Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback to the old Env name matching when case-sensitive env name matching does not return results #1051

Merged
merged 1 commit into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 115 additions & 23 deletions implementation/src/main/java/io/smallrye/config/EnvConfigSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -59,8 +61,7 @@ public class EnvConfigSource extends AbstractConfigSource {

private static final int DEFAULT_ORDINAL = 300;

private final Map<EnvProperty, String> properties;
private final Set<String> names;
private final EnvVars envVars;

protected EnvConfigSource() {
this(DEFAULT_ORDINAL);
Expand All @@ -72,38 +73,35 @@ protected EnvConfigSource(final int ordinal) {

public EnvConfigSource(final Map<String, String> 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<String, String> 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<String, String> getProperties() {
Map<String, String> properties = new HashMap<>(this.properties.size());
for (Map.Entry<EnvProperty, String> entry : this.properties.entrySet()) {
properties.put(entry.getKey().getName(), entry.getValue());
Map<String, String> properties = new HashMap<>();
for (Map.Entry<EnvName, EnvEntry> 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<String> 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));
}

/**
Expand Down Expand Up @@ -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<EnvName, EnvEntry> env;
private final Set<String> names;

public EnvVars(final Map<String, String> properties) {
this.env = new HashMap<>(properties.size());
this.names = new HashSet<>(properties.size() * 2);
for (Map.Entry<String, String> 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<EnvName, EnvEntry> getEnv() {
return env;
}

public Set<String> 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;
}
Expand All @@ -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);
}

Expand Down Expand Up @@ -199,7 +252,8 @@ public int hashCode() {
case '/':
continue;
}
h = 31 * h + c;
h = 31 * h + toLowerCase(c);
// h = 31 * h + c;
}
return h;
}
Expand Down Expand Up @@ -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--;
Expand All @@ -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<String, String> 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<String, String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2018 Red Hat inc.
Expand Down Expand Up @@ -166,33 +166,49 @@ 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")));
assertTrue(envSourceEquals("FOO_BAR", new String("FOO.BAR")));
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]")));
Expand All @@ -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")));
}

Expand All @@ -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"));
}
Expand Down
Loading