Skip to content

Commit

Permalink
Merge pull request #695 from Netflix/interface-converters
Browse files Browse the repository at this point in the history
Allow conversion for interface types
  • Loading branch information
rgallardo-netflix authored Dec 8, 2023
2 parents def12e8 + 1c5e0d3 commit d8480f1
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.netflix.archaius.api.Property;
import com.netflix.archaius.api.PropertyFactory;
import com.netflix.archaius.api.PropertyRepository;
import com.netflix.archaius.api.TypeConverter;
import com.netflix.archaius.api.annotations.Configuration;
import com.netflix.archaius.api.annotations.DefaultValue;
import com.netflix.archaius.api.annotations.PropertyName;
Expand Down Expand Up @@ -296,9 +297,11 @@ private <T> MethodInvokerHolder buildInvokerForMethod(Class<T> type, String pref
// This object encapsulates the way to get the value for the current property.
final PropertyValueGetter propertyValueGetter;

if (!knownCollections.containsKey(returnType) && returnType.isInterface()) {
// Our return type is an interface but not a known collection. We treat it as a nested Config proxy
// interface and create a proxy with it, with the current property name as the initial prefix for nesting.
if (!knownCollections.containsKey(returnType)
&& returnType.isInterface()
&& !(decoder instanceof TypeConverter.Registry && ((TypeConverter.Registry) decoder).get(m.getGenericReturnType()).isPresent())) {
// Our return type is an interface but not a known collection and is also not a type our decoder can handle.
// We treat it as a nested Config proxy interface and create a proxy with it, with the current property name as the initial prefix for nesting.
propertyValueGetter = createInterfaceProperty(propName, newProxy(returnType, propName, immutable));

} else if (m.getParameterCount() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ public <T> T decode(Type type, String encoded) {
if (encoded == null) {
return null;
}
return (T) getOrCreateConverter(type).convert(encoded);
@SuppressWarnings("unchecked")
TypeConverter<T> converter = (TypeConverter<T>) getOrCreateConverter(type);
if (converter == null) {
throw new RuntimeException("No converter found for type '" + type + "'");
}
return converter.convert(encoded);
} catch (Exception e) {
throw new ParseException("Error decoding type `" + type.getTypeName() + "`", e);
}
Expand All @@ -76,7 +81,7 @@ private TypeConverter<?> getOrCreateConverter(Type type) {
if (converter == null) {
converter = resolve(type);
if (converter == null) {
throw new RuntimeException("No converter found for type '" + type + "'");
return null;
}
TypeConverter<?> existing = cache.putIfAbsent(type, converter);
if (existing != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,12 @@ public void testJavaMiscellaneous() throws DecoderException {
Assert.assertEquals(URI.create("https://netflix.com"), decoder.decode(URI.class, "https://netflix.com"));
Assert.assertEquals(Locale.ENGLISH, decoder.decode(Locale.class, "en"));
}

@Test
public void testTypeConverterRegistry() {
Assert.assertTrue(DefaultDecoder.INSTANCE.get(Instant.class).isPresent());

class Foo {}
Assert.assertFalse(DefaultDecoder.INSTANCE.get(Foo.class).isPresent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.annotation.Nullable;

import com.netflix.archaius.api.Decoder;
import com.netflix.archaius.api.TypeConverter;
import com.netflix.archaius.config.MapConfig;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
Expand Down Expand Up @@ -574,4 +579,49 @@ public void testLogExcessiveUse() {
}
factory.newProxy(WithArguments.class, "somePrefix"); // This one should not log, because it's a new prefix.
}

interface ConfigWithNestedInterface {
int intValue();

CustomObject customValue();

interface CustomObject {
String value();
}
}

@Test
public void testNestedInterfaceWithCustomDecoder() {
TypeConverter<ConfigWithNestedInterface.CustomObject> customObjectTypeConverter = value -> value::toUpperCase;

final class CustomDecoder implements Decoder, TypeConverter.Registry {
@Override
public <T> T decode(Class<T> type, String encoded) {
if (type.equals(ConfigWithNestedInterface.CustomObject.class)) {
@SuppressWarnings("unchecked")
T converted = (T) customObjectTypeConverter.convert(encoded);
return converted;
}
return DefaultDecoder.INSTANCE.decode(type, encoded);
}

@Override
public Optional<TypeConverter<?>> get(Type type) {
if (type.equals(ConfigWithNestedInterface.CustomObject.class)) {
return Optional.of(customObjectTypeConverter);
}
return DefaultDecoder.INSTANCE.get(type);
}
}
Config config = MapConfig.builder()
.put("intValue", "5")
.put("customValue", "blah")
.build();
config.setDecoder(new CustomDecoder());
ConfigProxyFactory proxyFactory = new ConfigProxyFactory(config, config.getDecoder(), DefaultPropertyFactory.from(config));

ConfigWithNestedInterface proxy = proxyFactory.newProxy(ConfigWithNestedInterface.class);
Assert.assertEquals(5, proxy.intValue());
Assert.assertEquals("BLAH", proxy.customValue().value());
}
}

0 comments on commit d8480f1

Please sign in to comment.