Commit 1ec107ab authored by Oleg Zhurakousky's avatar Oleg Zhurakousky
Browse files

GH-474 Add TypeResolver library and simplify type discovery

For complex cases where deep hierarchies are used there was still an issue with the fix in #473.
By adding TypeResolver library we essentially simplify our discovery process

Resolves #474
parent 73c8f9c1
Showing with 52 additions and 47 deletions
+52 -47
......@@ -16,6 +16,11 @@
</parent>
<dependencies>
<dependency>
<groupId>net.jodah</groupId>
<artifactId>typetools</artifactId>
<version>0.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
......
......@@ -254,6 +254,9 @@ public class BeanFactoryAwareFunctionRegistry
}
private Type discoverFunctionType(Object function, String... names) {
if (function instanceof RoutingFunction) {
return FunctionType.of(FunctionContextUtils.findType(applicationContext.getBeanFactory(), names)).getType();
}
boolean beanDefinitionExists = false;
for (int i = 0; i < names.length && !beanDefinitionExists; i++) {
beanDefinitionExists = this.applicationContext.getBeanFactory().containsBeanDefinition(names[i]);
......@@ -267,9 +270,15 @@ public class BeanFactoryAwareFunctionRegistry
logger.info("BeanDefinition for function name(s) '" + Arrays.asList(names) +
"' can not be located. FunctionType will be based on " + function.getClass());
}
return beanDefinitionExists
? FunctionType.of(FunctionContextUtils.findType(applicationContext.getBeanFactory(), names)).getType()
: new FunctionType(function.getClass()).getType();
Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(function.getClass());
if (beanDefinitionExists) {
Type t = FunctionTypeUtils.getImmediateGenericType(type, 0);
if (t == null || t == Object.class) {
type = FunctionType.of(FunctionContextUtils.findType(this.applicationContext.getBeanFactory(), names)).getType();
}
}
return type;
}
private String discoverDefaultDefinitionIfNecessary(String definition) {
......
......@@ -30,6 +30,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import net.jodah.typetools.TypeResolver;
import org.reactivestreams.Publisher;
import reactor.util.function.Tuple2;
......@@ -118,29 +119,18 @@ public final class FunctionTypeUtils {
}
}
@SuppressWarnings("unchecked")
public static Type discoverFunctionTypeFromClass(Class<?> functionalClass) {
Assert.isTrue(isFunctional(functionalClass), "Type must be one of Supplier, Function or Consumer");
Type[] generics = functionalClass.getGenericInterfaces();
if (ObjectUtils.isEmpty(generics)) {
return functionalClass;
if (Function.class.isAssignableFrom(functionalClass)) {
return TypeResolver.reify(Function.class, (Class<Function<?, ?>>) functionalClass);
}
else {
for (Type generic : generics) {
if (generic instanceof ParameterizedType) {
Class<?> rawClsss = (Class<?>) ((ParameterizedType) generic).getRawType();
if (Function.class.isAssignableFrom(rawClsss)
|| Consumer.class.isAssignableFrom(rawClsss)
|| Supplier.class.isAssignableFrom(rawClsss)) {
return generic;
}
else {
return discoverFunctionTypeFromClass(rawClsss);
}
}
else {
return discoverFunctionTypeFromClass((Class<?>) generic);
}
}
else if (Consumer.class.isAssignableFrom(functionalClass)) {
return TypeResolver.reify(Consumer.class, (Class<Consumer<?>>) functionalClass);
}
else if (Supplier.class.isAssignableFrom(functionalClass)) {
return TypeResolver.reify(Supplier.class, (Class<Supplier<?>>) functionalClass);
}
return null;
}
......
......@@ -41,6 +41,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.cloud.function.context.catalog.BeanFactoryAwareFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtilsTests.ReactiveFunction;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -356,7 +357,6 @@ public class BeanFactoryAwareFunctionRegistryTests {
assertThat(result.getPayload()).isEqualTo("\"b2xsZWg=\"".getBytes());
}
@SuppressWarnings("rawtypes")
@Test
public void testMultipleValuesInOutputHandling() throws Exception {
FunctionCatalog catalog = this.configureCatalog(CollectionOutConfiguration.class);
......@@ -421,6 +421,29 @@ public class BeanFactoryAwareFunctionRegistryTests {
assertThat(dateResult.getHeaders().get("accept")).isNull();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testWithComplexHierarchyAndTypeConversion() {
FunctionCatalog catalog = this.configureCatalog(ReactiveFunctionImpl.class);
Function<Object, Flux> f = catalog.lookup("");
assertThat(f.apply(new GenericMessage("23")).blockFirst()).isEqualTo(23);
assertThat(f.apply(Flux.just("25")).blockFirst()).isEqualTo(25);
assertThat(f.apply(Flux.just(25)).blockFirst()).isEqualTo(25);
}
public interface ReactiveFunction<S, T> extends Function<Flux<S>, Flux<T>> {
}
@Component
@EnableAutoConfiguration
public static class ReactiveFunctionImpl implements ReactiveFunction<String, Integer> {
@Override
public Flux<Integer> apply(Flux<String> inFlux) {
return inFlux.map(v -> Integer.parseInt(v));
}
}
@SuppressWarnings("unchecked")
@EnableAutoConfiguration
public static class CollectionOutConfiguration {
......
......@@ -132,33 +132,12 @@ public class FunctionTypeUtilsTests {
}
@Test
public void foo() {
public void testWithComplexHierarchy() {
FunctionType type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(ReactiveFunctionImpl.class));
assertThat(String.class).isAssignableFrom(type.getInputType());
assertThat(Integer.class).isAssignableFrom(type.getOutputType());
}
// @Test
// public void testInputTypeByIndex() throws Exception {
// Type functionType = getReturnType("function");
// Type inputType = FunctionTypeUtils.getInputType(functionType, 0);
// assertThat(inputType.getTypeName()).isEqualTo(String.class.getName());
// inputType = FunctionTypeUtils.getInputType(functionType, 1);
// assertThat(inputType.getTypeName()).isEqualTo(Integer.class.getName());
//
// functionType = getReturnType("multiInputOutputPublisherFunction");
// inputType = FunctionTypeUtils.getInputType(functionType, 0);
// System.out.println("Reactive: " + FunctionTypeUtils.isReactive(inputType));
// System.out.println("Reactive: " + FunctionTypeUtils.getPublisherType(inputType));
// System.out.println("Reactive: " + FunctionTypeUtils.getImmediateGenericType(inputType, 0));
// System.out.println(inputType);
//
// functionType = getReturnType("typelessFunction");
// inputType = FunctionTypeUtils.getInputType(functionType, 0);
// System.out.println(inputType);
// }
private static Function<String, Integer> function() {
return null;
}
......@@ -246,6 +225,5 @@ public class FunctionTypeUtilsTests {
public Flux<Integer> apply(Flux<String> inFlux) {
return inFlux.map(v -> Integer.parseInt(v));
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment