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

Update handling for parameterized methods via ConfigProxyFactory #680

Merged
merged 1 commit into from
Sep 29, 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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.netflix.archaius.api.annotations.DefaultValue;
import com.netflix.archaius.api.annotations.PropertyName;
import com.netflix.archaius.util.Maps;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.slf4j.Logger;
Expand Down Expand Up @@ -287,7 +288,7 @@ else if ("toString".equals(method.getName())) {
if (nameAnnot == null) {
throw new IllegalArgumentException("Missing @PropertyName annotation on " + m.getDeclaringClass().getName() + "#" + m.getName());
}
invoker = createParameterizedProperty(returnType, propName, nameAnnot.name(), defaultSupplier);
invoker = createParameterizedProperty(returnType, prefix, nameAnnot.name(), defaultSupplier);
} else {
invoker = createScalarProperty(m.getGenericReturnType(), propName, defaultSupplier);
}
Expand Down Expand Up @@ -391,8 +392,8 @@ protected <T> MethodInvoker<T> createScalarProperty(final Type type, final Strin
};
}

protected <T> MethodInvoker<T> createParameterizedProperty(final Class<T> returnType, final String propName, final String nameAnnot, Function<Object[], T> next) {
LOG.debug("Creating parameterized property `{}` for type `{}`", propName, returnType);
protected <T> MethodInvoker<T> createParameterizedProperty(final Class<T> returnType, final String prefix, final String nameAnnot, Function<Object[], T> next) {
LOG.debug("Creating parameterized property `{}` for type `{}`", prefix + nameAnnot, returnType);
return new MethodInvoker<T>() {
@Override
public T invoke(Object[] args) {
Expand All @@ -405,6 +406,13 @@ public T invoke(Object[] args) {
return next.apply(null);
}

// A previous version allowed the full name to be specified, even if the prefix was specified. So, for
// backwards compatibility, we allow both including or excluding the prefix for parameterized names.
String propName = nameAnnot;
if (!StringUtils.isBlank(prefix) && !nameAnnot.startsWith(prefix)) {
propName = prefix + nameAnnot;
}

// Determine the actual property name by replacing with arguments using the argument index
// to the method. For example,
// @PropertyName(name="foo.${1}.${0}")
Expand All @@ -415,7 +423,7 @@ public T invoke(Object[] args) {
for (int i = 0; i < args.length; i++) {
values.put(String.valueOf(i), args[i]);
}
String propName = new StrSubstitutor(values, "${", "}", '$').replace(nameAnnot);
propName = new StrSubstitutor(values, "${", "}", '$').replace(propName);
T result = getPropertyWithDefault(returnType, propName);
if (result == null) {
result = next.apply(args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,37 @@ public void testWithArguments() {
Assert.assertEquals("default", withArgs.getProperty("a", 2));
}

@Configuration(prefix = "foo.bar")
static interface WithArgumentsAndPrefix {
@PropertyName(name="baz.${0}.abc.${1}")
@DefaultValue("default")
String getPropertyWithoutPrefix(String part0, int part1);

// For backward compatibility, we need to accept PropertyNames that also include the prefix.
@PropertyName(name="foo.bar.baz.${0}.abc.${1}")
@DefaultValue("default")
String getPropertyWithPrefix(String part0, int part1);
}

@Test
public void testWithArgumentsAndPrefix() {
SettableConfig config = new DefaultSettableConfig();
config.setProperty("foo.bar.baz.a.abc.1", "value1");
config.setProperty("foo.bar.baz.b.abc.2", "value2");

PropertyFactory factory = DefaultPropertyFactory.from(config);
ConfigProxyFactory proxy = new ConfigProxyFactory(config, config.getDecoder(), factory);
WithArgumentsAndPrefix withArgs = proxy.newProxy(WithArgumentsAndPrefix.class);

Assert.assertEquals("value1", withArgs.getPropertyWithPrefix("a", 1));
Assert.assertEquals("value1", withArgs.getPropertyWithoutPrefix("a", 1));
Assert.assertEquals("value2", withArgs.getPropertyWithPrefix("b", 2));
Assert.assertEquals("value2", withArgs.getPropertyWithoutPrefix("b", 2));
Assert.assertEquals("default", withArgs.getPropertyWithPrefix("a", 2));
Assert.assertEquals("default", withArgs.getPropertyWithoutPrefix("a", 2));
}


public interface WithArgumentsAndDefaultMethod {
@PropertyName(name="${0}.abc.${1}")
default String getProperty(String part0, int part1) {
Expand Down