diff --git a/build.sbt b/build.sbt
index 9d78b6ae..15260557 100644
--- a/build.sbt
+++ b/build.sbt
@@ -85,7 +85,17 @@ lazy val configLib = Project("config", file("config"))
Test/ run / fork := true
//env vars for tests
- Test / envVars ++= Map("testList.0" -> "0", "testList.1" -> "1")
+ Test / envVars ++= Map("testList.0" -> "0",
+ "testList.1" -> "1",
+ "CONFIG_FORCE_b" -> "5",
+ "CONFIG_FORCE_testList_0" -> "10",
+ "CONFIG_FORCE_testList_1" -> "11",
+ "CONFIG_FORCE_42___a" -> "1",
+ "CONFIG_FORCE_a_b_c" -> "2",
+ "CONFIG_FORCE_a__c" -> "3",
+ "CONFIG_FORCE_a___c" -> "4",
+ "CONFIG_FORCE_akka_version" -> "foo",
+ "CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10")
OsgiKeys.exportPackage := Seq("com.typesafe.config", "com.typesafe.config.impl")
publish := sys.error("use publishSigned instead of plain publish")
diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java
index 86d995e3..8405520f 100644
--- a/config/src/main/java/com/typesafe/config/ConfigFactory.java
+++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java
@@ -36,6 +36,7 @@ import java.util.concurrent.Callable;
*/
public final class ConfigFactory {
private static final String STRATEGY_PROPERTY_NAME = "config.strategy";
+ private static final String OVERRIDE_WITH_ENV_PROPERTY_NAME = "config.override_with_env_vars";
private ConfigFactory() {
}
@@ -383,7 +384,11 @@ public final class ConfigFactory {
* @return the default override configuration
*/
public static Config defaultOverrides() {
- return systemProperties();
+ if (!getOverrideWithEnv()) {
+ return systemProperties();
+ } else {
+ return systemEnvironmentOverrides().withFallback(systemProperties());
+ }
}
/**
@@ -394,7 +399,7 @@ public final class ConfigFactory {
* @return the default override configuration
*/
public static Config defaultOverrides(ClassLoader loader) {
- return systemProperties();
+ return defaultOverrides();
}
/**
@@ -549,6 +554,50 @@ public final class ConfigFactory {
return ConfigImpl.systemPropertiesAsConfig();
}
+ /**
+ * Gets a Config
containing the system's environment variables
+ * used to override configuration keys.
+ * Environment variables taken in considerations are starting with
+ * {@code CONFIG_FORCE_}
+ *
+ *
+ * Environment variables are mangled in the following way after stripping the prefix "CONFIG_FORCE_":
+ *
+ *
+ * Env Var |
+ * Config |
+ *
+ *
+ * _ [1 underscore] |
+ * . [dot] |
+ *
+ *
+ * __ [2 underscore] |
+ * - [dash] |
+ *
+ *
+ * ___ [3 underscore] |
+ * _ [underscore] |
+ *
+ *
+ *
+ *
+ * A variable like: {@code CONFIG_FORCE_a_b__c___d}
+ * is translated to a config key: {@code a.b-c_d}
+ *
+ *
+ * This method can return a global immutable singleton, so it's preferred
+ * over parsing system properties yourself.
+ *
+ * {@link #defaultOverrides} will include the system system environment variables as
+ * overrides if `config.override_with_env_vars` is set to `true`.
+ *
+ * @return system environment variable overrides parsed into a Config
+ */
+ public static Config systemEnvironmentOverrides() {
+ return ConfigImpl.envVariablesOverridesAsConfig();
+ }
+
/**
* Gets a Config
containing the system's environment variables.
* This method can return a global immutable singleton.
@@ -1063,4 +1112,10 @@ public final class ConfigFactory {
return new DefaultConfigLoadingStrategy();
}
}
+
+ private static Boolean getOverrideWithEnv() {
+ String overrideWithEnv = System.getProperties().getProperty(OVERRIDE_WITH_ENV_PROPERTY_NAME);
+
+ return Boolean.parseBoolean(overrideWithEnv);
+ }
}
diff --git a/config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java b/config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java
deleted file mode 100644
index 0b094803..00000000
--- a/config/src/main/java/com/typesafe/config/EnvFirstConfigLoadingStrategy.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.typesafe.config;
-
-import java.util.Map;
-import java.util.HashMap;
-
-/**
- * Environment variables first config loading strategy. Able to load environment variables first and fallback to {@link DefaultConfigLoadingStrategy}
- *
- *
- * Environment variables are mangled in the following way after stripping the prefix:
- *
- *
- * Env Var |
- * Config |
- *
- *
- * _ [1 underscore] |
- * . [dot] |
- *
- *
- * __ [2 underscore] |
- * - [dash] |
- *
- *
- * ___ [3 underscore] |
- * _ [underscore] |
- *
- *
- *
- *
- * A variable like: {@code CONFIG_a_b__c___d}
- * is translated to a config key: {@code a.b-c_d}
- *
- *
- * The prefix may be altered by defining the VM property {@code config.env_var_prefix}
- */
-public class EnvFirstConfigLoadingStrategy extends DefaultConfigLoadingStrategy {
-
- protected static Map env = new HashMap(System.getenv());
-
- @Override
- public Config parseApplicationConfig(ConfigParseOptions parseOptions) {
- String envVarPrefix = System.getProperty("config.env_var_prefix");
- if (envVarPrefix == null) // fallback to default
- envVarPrefix = "CONFIG_";
-
- Map defaultsFromEnv = new HashMap();
- for (String key : env.keySet()) {
- if (key.startsWith(envVarPrefix)) {
- StringBuilder builder = new StringBuilder();
-
- String strippedPrefix = key.substring(envVarPrefix.length(), key.length());
-
- int underscores = 0;
- for (char c : strippedPrefix.toCharArray()) {
- if (c == '_') {
- underscores++;
- } else {
- switch (underscores) {
- case 1: builder.append('.');
- break;
- case 2: builder.append('-');
- break;
- case 3: builder.append('_');
- break;
- }
- underscores = 0;
- builder.append(c);
- }
- }
-
- String propertyKey = builder.toString();
- defaultsFromEnv.put(propertyKey, env.get(key));
- }
- }
-
- return ConfigFactory.parseMap(defaultsFromEnv).withFallback(super.parseApplicationConfig(parseOptions));
- }
-}
diff --git a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
index 9cf49913..f9057ede 100644
--- a/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
+++ b/config/src/main/java/com/typesafe/config/impl/ConfigImpl.java
@@ -32,6 +32,7 @@ import com.typesafe.config.impl.SimpleIncluder.NameSource;
* For use only by the {@link com.typesafe.config} package.
*/
public class ConfigImpl {
+ private static final String ENV_VAR_OVERRIDE_PREFIX = "CONFIG_FORCE_";
private static class LoaderCache {
private Config currentSystemProperties;
@@ -360,6 +361,57 @@ public class ConfigImpl {
EnvVariablesHolder.envVariables = loadEnvVariables();
}
+ private static AbstractConfigObject loadEnvVariablesOverrides() {
+ Map env = new HashMap(System.getenv());
+ Map result = new HashMap(System.getenv());
+ for (String key : env.keySet()) {
+ if (key.startsWith(ENV_VAR_OVERRIDE_PREFIX)) {
+ StringBuilder builder = new StringBuilder();
+
+ String strippedPrefix = key.substring(ENV_VAR_OVERRIDE_PREFIX.length(), key.length());
+
+ int underscores = 0;
+ for (char c : strippedPrefix.toCharArray()) {
+ if (c == '_') {
+ underscores++;
+ } else {
+ switch (underscores) {
+ case 1: builder.append('.');
+ break;
+ case 2: builder.append('-');
+ break;
+ case 3: builder.append('_');
+ break;
+ }
+ underscores = 0;
+ builder.append(c);
+ }
+ }
+
+ String propertyKey = builder.toString();
+ result.put(propertyKey, env.get(key));
+ }
+ }
+
+ return PropertiesParser.fromStringMap(newSimpleOrigin("env variables overrides"), result);
+ }
+
+ private static class EnvVariablesOverridesHolder {
+ static volatile AbstractConfigObject envVariables = loadEnvVariablesOverrides();
+ }
+
+ static AbstractConfigObject envVariablesOverridesAsConfigObject() {
+ try {
+ return EnvVariablesOverridesHolder.envVariables;
+ } catch (ExceptionInInitializerError e) {
+ throw ConfigImplUtil.extractInitializerError(e);
+ }
+ }
+
+ public static Config envVariablesOverridesAsConfig() {
+ return envVariablesOverridesAsConfigObject().toConfig();
+ }
+
public static Config defaultReference(final ClassLoader loader) {
return computeCachedConfig(loader, "defaultReference", new Callable() {
@Override
diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
index f5084c79..853aa249 100644
--- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
@@ -1092,15 +1092,7 @@ class ConfigTest extends TestUtils {
@Test
def testLoadWithEnvSubstitutions() {
- TestEnvFirstStrategy.putEnvVar("CONFIG_42___a", "1")
- TestEnvFirstStrategy.putEnvVar("CONFIG_a_b_c", "2")
- TestEnvFirstStrategy.putEnvVar("CONFIG_a__c", "3")
- TestEnvFirstStrategy.putEnvVar("CONFIG_a___c", "4")
-
- TestEnvFirstStrategy.putEnvVar("CONFIG_akka_version", "foo")
- TestEnvFirstStrategy.putEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size", "10")
-
- System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName)
+ System.setProperty("config.override_with_env_vars", "true")
try {
val loader02 = new TestClassLoader(this.getClass().getClassLoader(),
@@ -1125,15 +1117,7 @@ class ConfigTest extends TestUtils {
assertEquals("foo", conf04.getString("akka.version"))
assertEquals(10, conf04.getInt("akka.event-handler-dispatcher.max-pool-size"))
} finally {
- System.clearProperty("config.strategy")
-
- TestEnvFirstStrategy.removeEnvVar("CONFIG_42___a")
- TestEnvFirstStrategy.removeEnvVar("CONFIG_a_b_c")
- TestEnvFirstStrategy.removeEnvVar("CONFIG_a__c")
- TestEnvFirstStrategy.removeEnvVar("CONFIG_a___c")
-
- TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_version")
- TestEnvFirstStrategy.removeEnvVar("CONFIG_akka_event__handler__dispatcher_max__pool__size")
+ System.clearProperty("config.override_with_env_vars")
}
}
diff --git a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
index 5eed422a..4c72643e 100644
--- a/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/PublicApiTest.scala
@@ -654,24 +654,22 @@ class PublicApiTest extends TestUtils {
}
@Test
- def supportsEnvFirstConfigLoadingStrategy(): Unit = {
- assertEquals("config.strategy is not set", null, System.getProperty("config.strategy"))
+ def loadEnvironmentVariablesOverridesIfConfigured(): Unit = {
+ assertEquals("config.override_with_env_vars is not set", null, System.getProperty("config.override_with_env_vars"))
- TestEnvFirstStrategy.putEnvVar("CONFIG_a", "5")
- System.setProperty("config.strategy", classOf[EnvFirstConfigLoadingStrategy].getCanonicalName)
+ System.setProperty("config.override_with_env_vars", "true")
try {
- 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) {
+ val configB2 = withContextClassLoader(loaderB2) {
ConfigFactory.load()
}
- assertEquals(5, configA1.getInt("a"))
+ assertEquals(5, configB2.getInt("b"))
} finally {
- System.clearProperty("config.strategy")
- TestEnvFirstStrategy.removeEnvVar("CONFIG_a")
+ System.clearProperty("config.override_with_env_vars")
}
}
@@ -1168,10 +1166,3 @@ object TestStrategy {
def getIncovations() = invocations
def increment() = invocations += 1
}
-
-object TestEnvFirstStrategy extends EnvFirstConfigLoadingStrategy {
- def putEnvVar(key: String, value: String) =
- EnvFirstConfigLoadingStrategy.env.put(key, value)
- def removeEnvVar(key: String) =
- EnvFirstConfigLoadingStrategy.env.remove(key)
-}