Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add capability to lookup environment variables in configuration files
- Loading branch information
Showing
12 changed files
with
367 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
...rd-configuration/src/main/java/io/dropwizard/configuration/EnvironmentVariableLookup.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package io.dropwizard.configuration; | ||
|
||
import org.apache.commons.lang3.text.StrLookup; | ||
|
||
/** | ||
* A custom {@link org.apache.commons.lang3.text.StrLookup} implementation using environment variables as lookup source. | ||
*/ | ||
public class EnvironmentVariableLookup extends StrLookup { | ||
private final boolean strict; | ||
|
||
/** | ||
* Create a new instance with strict behavior. | ||
*/ | ||
public EnvironmentVariableLookup() { | ||
this(true); | ||
} | ||
|
||
/** | ||
* Create a new instance. | ||
* | ||
* @param strict {@code true} if looking up undefined environment variables should throw a | ||
* {@link UndefinedEnvironmentVariableException}, {@code false} otherwise. | ||
* @throws UndefinedEnvironmentVariableException if the environment variable doesn't exist and strict behavior | ||
* is enabled. | ||
*/ | ||
public EnvironmentVariableLookup(boolean strict) { | ||
this.strict = strict; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws UndefinedEnvironmentVariableException if the environment variable doesn't exist and strict behavior | ||
* is enabled. | ||
*/ | ||
@Override | ||
public String lookup(String key) { | ||
String value = System.getenv(key); | ||
|
||
if (value == null && strict) { | ||
throw new UndefinedEnvironmentVariableException("The environment variable '" + key | ||
+ "' is not defined; could not substitute the expression '${" | ||
+ key + "}'."); | ||
} | ||
|
||
return value; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...nfiguration/src/main/java/io/dropwizard/configuration/EnvironmentVariableSubstitutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.dropwizard.configuration; | ||
|
||
import org.apache.commons.lang3.text.StrSubstitutor; | ||
|
||
/** | ||
* A custom {@link StrSubstitutor} using environment variables as lookup source. | ||
*/ | ||
public class EnvironmentVariableSubstitutor extends StrSubstitutor { | ||
public EnvironmentVariableSubstitutor() { | ||
this(true, false); | ||
} | ||
|
||
public EnvironmentVariableSubstitutor(boolean strict) { | ||
this(strict, false); | ||
} | ||
|
||
/** | ||
* @param strict {@code true} if looking up undefined environment variables should throw a | ||
* {@link UndefinedEnvironmentVariableException}, {@code false} otherwise. | ||
* @param substitutionInVariables a flag whether substitution is done in variable names. | ||
* @see io.dropwizard.configuration.EnvironmentVariableLookup#EnvironmentVariableLookup(boolean) | ||
* @see org.apache.commons.lang3.text.StrSubstitutor#setEnableSubstitutionInVariables(boolean) | ||
*/ | ||
public EnvironmentVariableSubstitutor(boolean strict, boolean substitutionInVariables) { | ||
super(new EnvironmentVariableLookup(strict)); | ||
this.setEnableSubstitutionInVariables(substitutionInVariables); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...d-configuration/src/main/java/io/dropwizard/configuration/SubstitutingSourceProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package io.dropwizard.configuration; | ||
|
||
import com.google.common.io.ByteStreams; | ||
import org.apache.commons.lang3.text.StrSubstitutor; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
/** | ||
* A delegating {@link ConfigurationSourceProvider} which replaces variables in the underlying configuration | ||
* source according to the rules of a custom {@link org.apache.commons.lang3.text.StrSubstitutor}. | ||
*/ | ||
public class SubstitutingSourceProvider implements ConfigurationSourceProvider { | ||
private final ConfigurationSourceProvider delegate; | ||
private final StrSubstitutor substitutor; | ||
|
||
/** | ||
* Create a new instance. | ||
* | ||
* @param delegate The underlying {@link io.dropwizard.configuration.ConfigurationSourceProvider}. | ||
* @param substitutor The custom {@link org.apache.commons.lang3.text.StrSubstitutor} implementation. | ||
*/ | ||
public SubstitutingSourceProvider(ConfigurationSourceProvider delegate, StrSubstitutor substitutor) { | ||
this.delegate = checkNotNull(delegate); | ||
this.substitutor = checkNotNull(substitutor); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@Override | ||
public InputStream open(String path) throws IOException { | ||
String config = new String(ByteStreams.toByteArray(delegate.open(path)), StandardCharsets.UTF_8); | ||
String substituted = substitutor.replace(config); | ||
|
||
return new ByteArrayInputStream(substituted.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...tion/src/main/java/io/dropwizard/configuration/UndefinedEnvironmentVariableException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.dropwizard.configuration; | ||
|
||
public class UndefinedEnvironmentVariableException extends RuntimeException { | ||
public UndefinedEnvironmentVariableException(String errorMessage) { | ||
super(errorMessage); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...onfiguration/src/test/java/io/dropwizard/configuration/EnvironmentVariableLookupTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package io.dropwizard.configuration; | ||
|
||
import org.junit.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.hamcrest.core.IsNull.nullValue; | ||
import static org.junit.Assume.assumeThat; | ||
|
||
public class EnvironmentVariableLookupTest { | ||
@Test(expected = UndefinedEnvironmentVariableException.class) | ||
public void defaultConstructorEnablesStrict() { | ||
assumeThat(System.getenv("nope"), nullValue()); | ||
|
||
EnvironmentVariableLookup lookup = new EnvironmentVariableLookup(); | ||
lookup.lookup("nope"); | ||
} | ||
|
||
@Test | ||
public void lookupReplacesWithEnvironmentVariables() { | ||
EnvironmentVariableLookup lookup = new EnvironmentVariableLookup(false); | ||
|
||
// Let's hope this doesn't break on Windows | ||
assertThat(lookup.lookup("TEST")).isEqualTo(System.getenv("TEST")); | ||
assertThat(lookup.lookup("nope")).isNull(); | ||
} | ||
|
||
@Test(expected = UndefinedEnvironmentVariableException.class) | ||
public void lookupThrowsExceptionInStrictMode() { | ||
assumeThat(System.getenv("nope"), nullValue()); | ||
|
||
EnvironmentVariableLookup lookup = new EnvironmentVariableLookup(true); | ||
lookup.lookup("nope"); | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
...uration/src/test/java/io/dropwizard/configuration/EnvironmentVariableSubstitutorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package io.dropwizard.configuration; | ||
|
||
import org.junit.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.hamcrest.core.IsNull.nullValue; | ||
import static org.junit.Assume.assumeThat; | ||
|
||
public class EnvironmentVariableSubstitutorTest { | ||
@Test | ||
public void defaultConstructorDisablesSubstitutionInVariables() { | ||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(); | ||
assertThat(substitutor.isEnableSubstitutionInVariables()).isFalse(); | ||
} | ||
|
||
@Test(expected = UndefinedEnvironmentVariableException.class) | ||
public void defaultConstructorEnablesStrict() { | ||
assumeThat(System.getenv("DOES_NOT_EXIST"), nullValue()); | ||
|
||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(); | ||
substitutor.replace("${DOES_NOT_EXIST}"); | ||
} | ||
|
||
@Test | ||
public void constructorEnablesSubstitutionInVariables() { | ||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(true, true); | ||
assertThat(substitutor.isEnableSubstitutionInVariables()).isTrue(); | ||
} | ||
|
||
@Test | ||
public void substitutorUsesEnvironmentVariableLookup() { | ||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(); | ||
assertThat(substitutor.getVariableResolver()).isInstanceOf(EnvironmentVariableLookup.class); | ||
} | ||
|
||
@Test | ||
public void substitutorReplacesWithEnvironmentVariables() { | ||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(false); | ||
|
||
assertThat(substitutor.replace("${TEST}")).isEqualTo(System.getenv("TEST")); | ||
assertThat(substitutor.replace("no replacement")).isEqualTo("no replacement"); | ||
assertThat(substitutor.replace("${DOES_NOT_EXIST}")).isEqualTo("${DOES_NOT_EXIST}"); | ||
assertThat(substitutor.replace("${DOES_NOT_EXIST:-default}")).isEqualTo("default"); | ||
} | ||
|
||
@Test(expected = UndefinedEnvironmentVariableException.class) | ||
public void substitutorThrowsExceptionInStrictMode() { | ||
assumeThat(System.getenv("DOES_NOT_EXIST"), nullValue()); | ||
|
||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(true); | ||
substitutor.replace("${DOES_NOT_EXIST}"); | ||
} | ||
|
||
@Test | ||
public void substitutorReplacesRecursively() { | ||
EnvironmentVariableSubstitutor substitutor = new EnvironmentVariableSubstitutor(false, true); | ||
|
||
assertThat(substitutor.replace("$${${TEST}}")).isEqualTo("${test_value}"); | ||
assertThat(substitutor.replace("${TEST${TEST_SUFFIX}}")).isEqualTo(System.getenv("TEST2")); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...nfiguration/src/test/java/io/dropwizard/configuration/SubstitutingSourceProviderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package io.dropwizard.configuration; | ||
|
||
import com.google.common.io.ByteStreams; | ||
import org.apache.commons.lang3.text.StrLookup; | ||
import org.apache.commons.lang3.text.StrSubstitutor; | ||
import org.junit.Test; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class SubstitutingSourceProviderTest { | ||
@Test | ||
public void shouldSubstituteCorrectly() throws IOException { | ||
StrLookup dummyLookup = new StrLookup() { | ||
@Override | ||
public String lookup(String key) { | ||
return "baz"; | ||
} | ||
}; | ||
SubstitutingSourceProvider provider = new SubstitutingSourceProvider(new DummySourceProvider(), new StrSubstitutor(dummyLookup)); | ||
String results = new String(ByteStreams.toByteArray(provider.open("foo: ${bar}")), StandardCharsets.UTF_8); | ||
|
||
assertThat(results).isEqualTo("foo: baz"); | ||
} | ||
|
||
@Test | ||
public void shouldSubstituteOnlyExistingVariables() throws IOException { | ||
StrLookup dummyLookup = new StrLookup() { | ||
@Override | ||
public String lookup(String key) { | ||
return null; | ||
} | ||
}; | ||
SubstitutingSourceProvider provider = new SubstitutingSourceProvider(new DummySourceProvider(), new StrSubstitutor(dummyLookup)); | ||
String results = new String(ByteStreams.toByteArray(provider.open("foo: ${bar}")), StandardCharsets.UTF_8); | ||
|
||
assertThat(results).isEqualTo("foo: ${bar}"); | ||
} | ||
|
||
@Test | ||
public void shouldSubstituteWithDefaultValue() throws IOException { | ||
StrLookup dummyLookup = new StrLookup() { | ||
@Override | ||
public String lookup(String key) { | ||
return null; | ||
} | ||
}; | ||
SubstitutingSourceProvider provider = new SubstitutingSourceProvider(new DummySourceProvider(), new StrSubstitutor(dummyLookup)); | ||
String results = new String(ByteStreams.toByteArray(provider.open("foo: ${bar:-default}")), StandardCharsets.UTF_8); | ||
|
||
assertThat(results).isEqualTo("foo: default"); | ||
} | ||
|
||
private static class DummySourceProvider implements ConfigurationSourceProvider { | ||
@Override | ||
public InputStream open(String s) throws IOException { | ||
return new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
} | ||
} |
Oops, something went wrong.