diff --git a/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java b/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java index 3b153244..3558eba6 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/ConfigProxyFactory.java @@ -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; @@ -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); } @@ -391,8 +392,8 @@ protected MethodInvoker createScalarProperty(final Type type, final Strin }; } - protected MethodInvoker createParameterizedProperty(final Class returnType, final String propName, final String nameAnnot, Function next) { - LOG.debug("Creating parameterized property `{}` for type `{}`", propName, returnType); + protected MethodInvoker createParameterizedProperty(final Class returnType, final String prefix, final String nameAnnot, Function next) { + LOG.debug("Creating parameterized property `{}` for type `{}`", prefix + nameAnnot, returnType); return new MethodInvoker() { @Override public T invoke(Object[] args) { @@ -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}") @@ -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); diff --git a/archaius2-core/src/test/java/com/netflix/archaius/ProxyFactoryTest.java b/archaius2-core/src/test/java/com/netflix/archaius/ProxyFactoryTest.java index 070cb941..56fbedde 100644 --- a/archaius2-core/src/test/java/com/netflix/archaius/ProxyFactoryTest.java +++ b/archaius2-core/src/test/java/com/netflix/archaius/ProxyFactoryTest.java @@ -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) {