diff --git a/implementation/src/main/java/io/smallrye/config/EnvConfigSource.java b/implementation/src/main/java/io/smallrye/config/EnvConfigSource.java index 9076873eb..16baebe59 100644 --- a/implementation/src/main/java/io/smallrye/config/EnvConfigSource.java +++ b/implementation/src/main/java/io/smallrye/config/EnvConfigSource.java @@ -17,6 +17,9 @@ import static io.smallrye.config.common.utils.ConfigSourceUtil.CONFIG_ORDINAL_KEY; import static io.smallrye.config.common.utils.StringUtil.isNumeric; +import static io.smallrye.config.common.utils.StringUtil.replaceNonAlphanumericByUnderscores; +import static io.smallrye.config.common.utils.StringUtil.toLowerCaseAndDotted; +import static java.lang.Character.toLowerCase; import static java.security.AccessController.doPrivileged; import java.io.Serializable; @@ -27,7 +30,6 @@ import java.util.Set; import io.smallrye.config.common.AbstractConfigSource; -import io.smallrye.config.common.utils.StringUtil; /** * A {@link org.eclipse.microprofile.config.spi.ConfigSource} to access Environment Variables following the mapping @@ -59,8 +61,7 @@ public class EnvConfigSource extends AbstractConfigSource { private static final int DEFAULT_ORDINAL = 300; - private final Map properties; - private final Set names; + private final EnvVars envVars; protected EnvConfigSource() { this(DEFAULT_ORDINAL); @@ -72,38 +73,35 @@ protected EnvConfigSource(final int ordinal) { public EnvConfigSource(final Map properties, final int ordinal) { super("EnvConfigSource", getEnvOrdinal(properties, ordinal)); - this.properties = new HashMap<>(properties.size()); - this.names = new HashSet<>(properties.size() * 2); - for (Map.Entry entry : properties.entrySet()) { - this.properties.put(new EnvProperty(entry.getKey()), entry.getValue()); - this.names.add(entry.getKey()); - String keyLowerCaseAndDotted = StringUtil.toLowerCaseAndDotted(entry.getKey()); - this.properties.putIfAbsent(new EnvProperty(keyLowerCaseAndDotted), entry.getValue()); - this.names.add(keyLowerCaseAndDotted); - } + this.envVars = new EnvVars(properties); } @Override public Map getProperties() { - Map properties = new HashMap<>(this.properties.size()); - for (Map.Entry entry : this.properties.entrySet()) { - properties.put(entry.getKey().getName(), entry.getValue()); + Map properties = new HashMap<>(); + for (Map.Entry entry : envVars.getEnv().entrySet()) { + EnvEntry entryValue = entry.getValue(); + if (entryValue.getEntries() != null) { + properties.putAll(entryValue.getEntries()); + } else { + properties.put(entryValue.getName(), entryValue.getValue()); + } } return properties; } @Override public Set getPropertyNames() { - return names; + return envVars.getNames(); } @Override public String getValue(final String propertyName) { - return this.properties.get(new EnvProperty(propertyName)); + return envVars.get(propertyName); } boolean hasPropertyName(final String propertyName) { - return properties.containsKey(new EnvProperty(propertyName)); + return envVars.getEnv().containsKey(new EnvName(propertyName)); } /** @@ -143,12 +141,67 @@ Object readResolve() { } } - static final class EnvProperty implements Serializable { + static final class EnvVars implements Serializable { + private static final long serialVersionUID = -56318356411229247L; + + private final Map env; + private final Set names; + + public EnvVars(final Map properties) { + this.env = new HashMap<>(properties.size()); + this.names = new HashSet<>(properties.size() * 2); + for (Map.Entry entry : properties.entrySet()) { + EnvName envName = new EnvName(entry.getKey()); + EnvEntry envEntry = env.get(envName); + if (envEntry == null) { + env.put(envName, new EnvEntry(entry.getKey(), entry.getValue())); + } else { + envEntry.add(entry.getKey(), entry.getValue()); + } + this.names.add(entry.getKey()); + this.names.add(toLowerCaseAndDotted(entry.getKey())); + } + } + + public String get(final String propertyName) { + EnvEntry envEntry = env.get(new EnvName(propertyName)); + if (envEntry != null) { + String value = envEntry.get(); + if (value != null) { + return value; + } + + value = envEntry.getEntries().get(propertyName); + if (value != null) { + return value; + } + + String envName = replaceNonAlphanumericByUnderscores(propertyName); + value = envEntry.getEntries().get(envName); + if (value != null) { + return value; + } + + return envEntry.envEntries.get(envName.toUpperCase()); + } + return null; + } + + public Map getEnv() { + return env; + } + + public Set getNames() { + return names; + } + } + + static final class EnvName implements Serializable { private static final long serialVersionUID = -2679716955093904512L; private final String name; - public EnvProperty(final String name) { + public EnvName(final String name) { assert name != null; this.name = name; } @@ -165,7 +218,7 @@ public boolean equals(final Object o) { if (o == null || getClass() != o.getClass()) { return false; } - final EnvProperty that = (EnvProperty) o; + final EnvName that = (EnvName) o; return equals(this.name, that.name); } @@ -199,7 +252,8 @@ public int hashCode() { case '/': continue; } - h = 31 * h + c; + h = 31 * h + toLowerCase(c); + // h = 31 * h + c; } return h; } @@ -288,7 +342,8 @@ && isNumeric(name, matchPosition - range, matchPosition)) { return false; } } - } else if (o != n) { + // } else if (o != n) { + } else if (toLowerCase(o) != toLowerCase(n)) { return false; } matchPosition--; @@ -297,4 +352,41 @@ && isNumeric(name, matchPosition - range, matchPosition)) { return matchPosition <= 0; } } + + static final class EnvEntry implements Serializable { + private static final long serialVersionUID = -8786927401082731020L; + + private final String name; + private final String value; + private Map envEntries; + + EnvEntry(final String name, final String value) { + this.name = name; + this.value = value; + } + + String getName() { + return name; + } + + String getValue() { + return value; + } + + String get() { + return envEntries == null ? value : null; + } + + Map getEntries() { + return envEntries; + } + + void add(String name, String value) { + if (envEntries == null) { + envEntries = new HashMap<>(); + envEntries.put(this.name, this.value); + } + envEntries.put(name, value); + } + } } diff --git a/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java b/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java index ad0912d39..ebef0d3ef 100644 --- a/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java +++ b/implementation/src/test/java/io/smallrye/config/EnvConfigSourceTest.java @@ -37,7 +37,7 @@ import org.eclipse.microprofile.config.spi.ConfigSource; import org.junit.jupiter.api.Test; -import io.smallrye.config.EnvConfigSource.EnvProperty; +import io.smallrye.config.EnvConfigSource.EnvName; /** * @author Jeff Mesnil (c) 2018 Red Hat inc. @@ -166,15 +166,15 @@ void map() { @Test void envEquals() { - assertTrue(EnvProperty.equals("", new String(""))); - assertTrue(EnvProperty.equals(" ", new String(" "))); - assertFalse(EnvProperty.equals(" ", new String("foo.bar"))); - assertFalse(EnvProperty.equals(" ", new String("FOO_BAR"))); - assertFalse(EnvProperty.equals("foo.bar", new String(""))); - assertFalse(EnvProperty.equals("FOO_BAR", new String(""))); + assertTrue(EnvName.equals("", new String(""))); + assertTrue(EnvName.equals(" ", new String(" "))); + assertFalse(EnvName.equals(" ", new String("foo.bar"))); + assertFalse(EnvName.equals(" ", new String("FOO_BAR"))); + assertFalse(EnvName.equals("foo.bar", new String(""))); + assertFalse(EnvName.equals("FOO_BAR", new String(""))); - assertFalse(EnvProperty.equals("BAR", new String("foo.bar"))); - assertFalse(EnvProperty.equals("foo.bar", new String("BAR"))); + assertFalse(EnvName.equals("BAR", new String("foo.bar"))); + assertFalse(EnvName.equals("foo.bar", new String("BAR"))); assertTrue(envSourceEquals("FOO_BAR", new String("FOO_BAR"))); assertTrue(envSourceEquals("FOO_BAR", new String("foo.bar"))); @@ -182,17 +182,33 @@ void envEquals() { assertTrue(envSourceEquals("FOO_BAR", new String("foo-bar"))); assertTrue(envSourceEquals("FOO_BAR", new String("foo_bar"))); - assertTrue(EnvProperty.equals("foo.bar", new String("foo.bar"))); - assertTrue(EnvProperty.equals("foo-bar", new String("foo-bar"))); - assertTrue(EnvProperty.equals("FOO.BAR", new String("FOO_BAR"))); + assertTrue(EnvName.equals("foo.bar", new String("foo.bar"))); + assertTrue(EnvName.equals("foo-bar", new String("foo-bar"))); + assertTrue(EnvName.equals("foo.bar", new String("FOO_BAR"))); + assertTrue(EnvName.equals("FOO.BAR", new String("FOO_BAR"))); + assertTrue(EnvName.equals("foo-bar", new String("FOO_BAR"))); + assertTrue(EnvName.equals("foo_bar", new String("FOO_BAR"))); + assertTrue(EnvName.equals("FOO__BAR__BAZ", new String("foo.\"bar\".baz"))); + assertTrue(EnvName.equals("foo.\"bar\".baz", new String("FOO__BAR__BAZ"))); assertTrue(envSourceEquals("FOO__BAR__BAZ", new String("foo.\"bar\".baz"))); + assertTrue(EnvName.equals("FOO__BAR__BAZ_0__Z_0_", new String("foo.\"bar\".baz[0].z[0]"))); assertTrue(envSourceEquals("FOO__BAR__BAZ_0__Z_0_", new String("foo.\"bar\".baz[0].z[0]"))); + assertTrue(EnvName.equals("_DEV_FOO_BAR", new String("%dev.foo.bar"))); + assertTrue(EnvName.equals("%dev.foo.bar", new String("_DEV_FOO_BAR"))); assertTrue(envSourceEquals("_DEV_FOO_BAR", new String("%dev.foo.bar"))); + assertTrue(EnvName.equals("_ENV_SMALLRYE_MP_CONFIG_PROP", new String("_ENV_SMALLRYE_MP_CONFIG_PROP"))); + assertTrue(EnvName.equals("%env.smallrye.mp.config.prop", new String("%env.smallrye.mp.config.prop"))); + assertTrue(EnvName.equals("_ENV_SMALLRYE_MP_CONFIG_PROP", new String("%env.smallrye.mp.config.prop"))); + assertTrue(EnvName.equals("%env.smallrye.mp.config.prop", new String("_ENV_SMALLRYE_MP_CONFIG_PROP"))); assertTrue(envSourceEquals("%env.smallrye.mp.config.prop", new String("%env.smallrye.mp.config.prop"))); assertTrue(envSourceEquals("_ENV_SMALLRYE_MP_CONFIG_PROP", new String("%env.smallrye.mp.config.prop"))); + assertTrue(EnvName.equals("indexed[0]", new String("indexed[0]"))); + assertTrue(EnvName.equals("INDEXED_0_", new String("INDEXED_0_"))); + assertTrue(EnvName.equals("indexed[0]", new String("INDEXED_0_"))); + assertTrue(EnvName.equals("INDEXED_0_", new String("indexed[0]"))); assertTrue(envSourceEquals("indexed[0]", new String("indexed[0]"))); assertTrue(envSourceEquals("INDEXED_0_", new String("INDEXED_0_"))); assertTrue(envSourceEquals("INDEXED_0_", new String("indexed[0]"))); @@ -201,14 +217,22 @@ void envEquals() { assertTrue(envSourceEquals("foo.bar[0].indexed[0]", new String("foo.bar[0].indexed[0]"))); assertTrue(envSourceEquals("FOO_BAR_0__INDEXED_0_", new String("foo.bar[0].indexed[0]"))); + assertTrue(EnvName.equals("env.\"quoted.key\".value", new String("env.\"quoted.key\".value"))); + assertTrue(EnvName.equals("ENV__QUOTED_KEY__VALUE", new String("ENV__QUOTED_KEY__VALUE"))); + assertTrue(EnvName.equals("ENV__QUOTED_KEY__VALUE", new String("env.\"quoted.key\".value"))); + assertTrue(EnvName.equals("env.\"quoted.key\".value", new String("ENV__QUOTED_KEY__VALUE"))); + assertTrue(EnvName.equals("env.\"quoted.key\".value", new String("env.\"quoted-key\".value"))); + assertTrue(EnvName.equals("env.\"quoted-key\".value", new String("env.\"quoted.key\".value"))); assertTrue(envSourceEquals("env.\"quoted.key\".value", new String("env.\"quoted.key\".value"))); assertTrue(envSourceEquals("ENV__QUOTED_KEY__VALUE", new String("ENV__QUOTED_KEY__VALUE"))); assertTrue(envSourceEquals("ENV__QUOTED_KEY__VALUE", new String("env.\"quoted.key\".value"))); assertTrue(envSourceEquals("env.\"quoted.key\".value", new String("env.\"quoted-key\".value"))); assertTrue(envSourceEquals("env.\"quoted-key\".value", new String("env.\"quoted.key\".value"))); - assertTrue(envSourceEquals("TEST_LANGUAGE__DE_ETR__", new String("test.language.\"de.etr\""))); - assertTrue(EnvProperty.equals("smallrye/mp/config/prop", new String("smallrye/mp/config/prop"))); + assertTrue(EnvName.equals("TEST_LANGUAGE__DE_ETR__", new String("test.language.\"de.etr\""))); + assertTrue(EnvName.equals("test.language.\"de.etr\"", new String("TEST_LANGUAGE__DE_ETR__"))); + assertEquals(new EnvName("TEST_LANGUAGE__DE_ETR_").hashCode(), new EnvName("test.language.\"de.etr\"").hashCode()); + assertTrue(envSourceEquals("SMALLRYE_MP_CONFIG_PROP", new String("smallrye/mp/config/prop"))); } @@ -229,12 +253,12 @@ void sameSemanticMeaning() { @Test void sameNames() { + assertTrue(envSourceEquals("FOOBAR", "foobar")); + assertTrue(envSourceEquals("FOOBAR", "fooBar")); + EnvConfigSource envConfigSource = new EnvConfigSource( Map.of("my_string_property", "lower", "MY_STRING_PROPERTY", "upper"), 100); - assertFalse(EnvProperty.equals("MY_STRING_PROPERTY", "my_string_property")); - assertFalse(EnvProperty.equals("my_string_property", "MY_STRING_PROPERTY")); - assertTrue(EnvProperty.equals("my_string_property", "my_string_property")); - assertTrue(EnvProperty.equals("MY_STRING_PROPERTY", "MY_STRING_PROPERTY")); + assertEquals(2, envConfigSource.getProperties().size()); assertEquals("lower", envConfigSource.getValue("my_string_property")); assertEquals("upper", envConfigSource.getValue("MY_STRING_PROPERTY")); }