mirror of
https://github.com/lightbend/config.git
synced 2025-02-23 17:50:30 +08:00
Do not cache default config, and use context class loader to load it
So we get any reference.conf from the context class loader. This does NOT fix loading non-default configs, we need new API to allow passing in a class loader for that. It also makes things a bit less efficient since it no longer caches; in the future we could do a per-class-loader cache.
This commit is contained in:
parent
e2c0979422
commit
9733578ebb
@ -11,7 +11,6 @@ import java.util.Map;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import com.typesafe.config.impl.ConfigImpl;
|
import com.typesafe.config.impl.ConfigImpl;
|
||||||
import com.typesafe.config.impl.ConfigImplUtil;
|
|
||||||
import com.typesafe.config.impl.Parseable;
|
import com.typesafe.config.impl.Parseable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,49 +102,43 @@ public final class ConfigFactory {
|
|||||||
.resolve(resolveOptions);
|
.resolve(resolveOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DefaultConfigHolder {
|
private static Config loadDefaultConfig() {
|
||||||
|
int specified = 0;
|
||||||
|
|
||||||
private static Config loadDefaultConfig() {
|
// override application.conf with config.file, config.resource,
|
||||||
int specified = 0;
|
// config.url if requested.
|
||||||
|
String resource = System.getProperty("config.resource");
|
||||||
|
if (resource != null)
|
||||||
|
specified += 1;
|
||||||
|
String file = System.getProperty("config.file");
|
||||||
|
if (file != null)
|
||||||
|
specified += 1;
|
||||||
|
String url = System.getProperty("config.url");
|
||||||
|
if (url != null)
|
||||||
|
specified += 1;
|
||||||
|
|
||||||
// override application.conf with config.file, config.resource,
|
if (specified == 0) {
|
||||||
// config.url if requested.
|
return load("application");
|
||||||
String resource = System.getProperty("config.resource");
|
} else if (specified > 1) {
|
||||||
if (resource != null)
|
throw new ConfigException.Generic("You set more than one of config.file='" + file
|
||||||
specified += 1;
|
+ "', config.url='" + url + "', config.resource='" + resource
|
||||||
String file = System.getProperty("config.file");
|
+ "'; don't know which one to use!");
|
||||||
if (file != null)
|
} else {
|
||||||
specified += 1;
|
if (resource != null) {
|
||||||
String url = System.getProperty("config.url");
|
// this deliberately does not parseResourcesAnySyntax; if
|
||||||
if (url != null)
|
// people want that they can use an include statement.
|
||||||
specified += 1;
|
return load(parseResources(ConfigFactory.class, resource));
|
||||||
|
} else if (file != null) {
|
||||||
if (specified == 0) {
|
return load(parseFile(new File(file)));
|
||||||
return load("application");
|
|
||||||
} else if (specified > 1) {
|
|
||||||
throw new ConfigException.Generic("You set more than one of config.file='" + file
|
|
||||||
+ "', config.url='" + url + "', config.resource='" + resource
|
|
||||||
+ "'; don't know which one to use!");
|
|
||||||
} else {
|
} else {
|
||||||
if (resource != null) {
|
try {
|
||||||
// this deliberately does not parseResourcesAnySyntax; if
|
return load(parseURL(new URL(url)));
|
||||||
// people want that they can use an include statement.
|
} catch (MalformedURLException e) {
|
||||||
return load(parseResources(ConfigFactory.class, resource));
|
throw new ConfigException.Generic("Bad URL in config.url system property: '"
|
||||||
} else if (file != null) {
|
+ url + "': " + e.getMessage(), e);
|
||||||
return load(parseFile(new File(file)));
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return load(parseURL(new URL(url)));
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
throw new ConfigException.Generic(
|
|
||||||
"Bad URL in config.url system property: '" + url + "': "
|
|
||||||
+ e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Config defaultConfig = loadDefaultConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,11 +169,7 @@ public final class ConfigFactory {
|
|||||||
* @return configuration for an application
|
* @return configuration for an application
|
||||||
*/
|
*/
|
||||||
public static Config load() {
|
public static Config load() {
|
||||||
try {
|
return loadDefaultConfig();
|
||||||
return DefaultConfigHolder.defaultConfig;
|
|
||||||
} catch (ExceptionInInitializerError e) {
|
|
||||||
throw ConfigImplUtil.extractInitializerError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,12 +290,12 @@ public final class ConfigFactory {
|
|||||||
* string values. If you have both "a=foo" and "a.b=bar" in your properties
|
* string values. If you have both "a=foo" and "a.b=bar" in your properties
|
||||||
* file, so "a" is both the object containing "b" and the string "foo", then
|
* file, so "a" is both the object containing "b" and the string "foo", then
|
||||||
* the string value is dropped.
|
* the string value is dropped.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* If you want to have <code>System.getProperties()</code> as a
|
* If you want to have <code>System.getProperties()</code> as a
|
||||||
* ConfigObject, it's better to use the {@link #systemProperties()} method
|
* ConfigObject, it's better to use the {@link #systemProperties()} method
|
||||||
* which returns a cached global singleton.
|
* which returns a cached global singleton.
|
||||||
*
|
*
|
||||||
* @param properties
|
* @param properties
|
||||||
* a Java Properties object
|
* a Java Properties object
|
||||||
* @param options
|
* @param options
|
||||||
|
@ -397,21 +397,12 @@ public class ConfigImpl {
|
|||||||
return envVariablesAsConfigObject().toConfig();
|
return envVariablesAsConfigObject().toConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReferenceHolder {
|
|
||||||
private static final Config unresolvedResources = Parseable
|
|
||||||
.newResources(ConfigImpl.class, "/reference.conf", ConfigParseOptions.defaults())
|
|
||||||
.parse().toConfig();
|
|
||||||
static final Config referenceConfig = systemPropertiesAsConfig().withFallback(
|
|
||||||
unresolvedResources).resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
|
||||||
public static Config defaultReference() {
|
public static Config defaultReference() {
|
||||||
try {
|
Config unresolvedResources = Parseable
|
||||||
return ReferenceHolder.referenceConfig;
|
.newResources(Thread.currentThread().getContextClassLoader(), "reference.conf",
|
||||||
} catch (ExceptionInInitializerError e) {
|
ConfigParseOptions.defaults()).parse().toConfig();
|
||||||
throw ConfigImplUtil.extractInitializerError(e);
|
return systemPropertiesAsConfig().withFallback(unresolvedResources).resolve();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DebugHolder {
|
private static class DebugHolder {
|
||||||
|
1
config/src/test/resources/a_1.conf
Normal file
1
config/src/test/resources/a_1.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
a=1
|
1
config/src/test/resources/b_2.conf
Normal file
1
config/src/test/resources/b_2.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
b=2
|
@ -464,4 +464,28 @@ class PublicApiTest extends TestUtils {
|
|||||||
assertEquals("\"a\"", ConfigUtil.quoteString("a"))
|
assertEquals("\"a\"", ConfigUtil.quoteString("a"))
|
||||||
assertEquals("\"\\n\"", ConfigUtil.quoteString("\n"))
|
assertEquals("\"\\n\"", ConfigUtil.quoteString("\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def usesContextClassLoader() {
|
||||||
|
val loaderA1 = new TestClassLoader(this.getClass().getClassLoader(),
|
||||||
|
Map("reference.conf" -> resourceFile("a_1.conf").toURI.toURL()))
|
||||||
|
val loaderB2 = new TestClassLoader(this.getClass().getClassLoader(),
|
||||||
|
Map("reference.conf" -> resourceFile("b_2.conf").toURI.toURL()))
|
||||||
|
|
||||||
|
val configA1 = withContextClassLoader(loaderA1) {
|
||||||
|
ConfigFactory.load()
|
||||||
|
}
|
||||||
|
assertEquals(1, configA1.getInt("a"))
|
||||||
|
assertFalse("no b", configA1.hasPath("b"))
|
||||||
|
|
||||||
|
val configB2 = withContextClassLoader(loaderB2) {
|
||||||
|
ConfigFactory.load()
|
||||||
|
}
|
||||||
|
assertEquals(2, configB2.getInt("b"))
|
||||||
|
assertFalse("no a", configB2.hasPath("a"))
|
||||||
|
|
||||||
|
val configPlain = ConfigFactory.load()
|
||||||
|
assertFalse("no a", configPlain.hasPath("a"))
|
||||||
|
assertFalse("no b", configPlain.hasPath("b"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@ import java.io.ByteArrayInputStream
|
|||||||
import java.io.ObjectInputStream
|
import java.io.ObjectInputStream
|
||||||
import org.apache.commons.codec.binary.Hex
|
import org.apache.commons.codec.binary.Hex
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
abstract trait TestUtils {
|
abstract trait TestUtils {
|
||||||
protected def intercept[E <: Throwable: Manifest](block: => Unit): E = {
|
protected def intercept[E <: Throwable: Manifest](block: => Unit): E = {
|
||||||
@ -547,4 +550,30 @@ abstract trait TestUtils {
|
|||||||
protected def resourceFile(filename: String) = {
|
protected def resourceFile(filename: String) = {
|
||||||
new File(resourceDir, filename)
|
new File(resourceDir, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class TestClassLoader(parent: ClassLoader, val additions: Map[String, URL]) extends ClassLoader(parent) {
|
||||||
|
override def findResources(name: String) = {
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
val other = super.findResources(name).asScala
|
||||||
|
additions.get(name).map({ url => Iterator(url) ++ other }).getOrElse(other).asJavaEnumeration
|
||||||
|
}
|
||||||
|
override def findResource(name: String) = {
|
||||||
|
additions.get(name).getOrElse(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def withContextClassLoader[T](loader: ClassLoader)(body: => T): T = {
|
||||||
|
val executor = Executors.newSingleThreadExecutor()
|
||||||
|
val f = executor.submit(new Callable[T] {
|
||||||
|
override def call(): T = {
|
||||||
|
val t = Thread.currentThread()
|
||||||
|
val old = t.getContextClassLoader()
|
||||||
|
t.setContextClassLoader(loader)
|
||||||
|
val result = body
|
||||||
|
t.setContextClassLoader(old)
|
||||||
|
result
|
||||||
|
}
|
||||||
|
})
|
||||||
|
f.get
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user