diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java index 8dfa5c9d8..e4655f352 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.context; +import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; @@ -29,6 +30,7 @@ import reactor.core.publisher.Flux; import org.springframework.core.ResolvableType; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.messaging.Message; +import org.springframework.util.ReflectionUtils; /** * @author Dave Syer @@ -66,6 +68,37 @@ public class FunctionType { this.message = messageType(); } + /* + * Experimental for now. Used (reflectively) in FunctionCreatorConfiguration to effectively + * map an existing FunctionType created by one class loader to another. + */ + @SuppressWarnings("unused") // it is used + private FunctionType(Object functionType) throws Exception { + Field field = ReflectionUtils.findField(functionType.getClass(), "type"); + field.setAccessible(true); + this.type = (Type) field.get(functionType); + + field = ReflectionUtils.findField(functionType.getClass(), "inputWrapper"); + field.setAccessible(true); + this.inputWrapper = (Class) field.get(functionType); + + field = ReflectionUtils.findField(functionType.getClass(), "outputWrapper"); + field.setAccessible(true); + this.outputWrapper = (Class) field.get(functionType); + + field = ReflectionUtils.findField(functionType.getClass(), "inputType"); + field.setAccessible(true); + this.inputType = (Class) field.get(functionType); + + field = ReflectionUtils.findField(functionType.getClass(), "outputType"); + field.setAccessible(true); + this.outputType = (Class) field.get(functionType); + + field = ReflectionUtils.findField(functionType.getClass(), "message"); + field.setAccessible(true); + this.message = (boolean) field.get(functionType); + } + public static boolean isWrapper(Type type) { if (type instanceof ParameterizedType) { type = ((ParameterizedType) type).getRawType(); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java index 2a223647e..5992dba2e 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/AbstractComposableFunctionRegistry.java @@ -67,8 +67,6 @@ import org.springframework.util.StringUtils; public abstract class AbstractComposableFunctionRegistry implements FunctionRegistry, FunctionInspector, ApplicationEventPublisherAware, EnvironmentAware { - private final Map suppliers = new ConcurrentHashMap<>(); - private final Map functions = new ConcurrentHashMap<>(); private final Map names = new ConcurrentHashMap<>(); @@ -113,7 +111,10 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi * @return immutable {@link Set} of available {@link Supplier} names. */ public Set getSupplierNames() { - return this.suppliers.keySet(); + return this.functions.entrySet().stream() + .filter(entry -> entry.getValue() instanceof Supplier) + .map(entry -> entry.getKey()) + .collect(Collectors.toSet()); } /** @@ -121,7 +122,10 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi * @return immutable {@link Set} of available {@link Function} names. */ public Set getFunctionNames() { - return this.functions.keySet(); + return this.functions.entrySet().stream() + .filter(entry -> !(entry.getValue() instanceof Supplier)) + .map(entry -> entry.getKey()) + .collect(Collectors.toSet()); } public boolean hasSuppliers() { @@ -140,7 +144,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi */ @Override public int size() { - return getSupplierNames().size() + getFunctionNames().size(); + return this.functions.size(); } public FunctionType getFunctionType(String name) { @@ -243,7 +247,7 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi protected void addSupplier(String name, Supplier supplier) { - this.suppliers.put(name, supplier); + this.functions.put(name, supplier); } protected void addFunction(String name, Function function) { @@ -325,16 +329,16 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi } private FunctionRegistration find(String name, boolean supplierFound) { - Object result = this.suppliers.get(name); - if (result == null) { - result = this.functions.get(name); - } + Object result = this.functions.get(name); +// if (result == null) { +// result = this.functions.get(name); +// } if (result == null && !StringUtils.hasText(name)) { - if (supplierFound && this.functions.size() == 1) { - result = this.functions.values().iterator().next(); + if (supplierFound && this.getFunctionNames().size() == 1) { + result = this.functions.get(this.getFunctionNames().iterator().next()); } - else if (!supplierFound && this.suppliers.size() == 1) { - result = this.suppliers.values().iterator().next(); + else if (!supplierFound && this.getSupplierNames().size() == 1) { + result = this.functions.get(this.getSupplierNames().iterator().next()); } } @@ -426,24 +430,9 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi } private Object doLookup(Class type, String name) { - Object function = null; - if (type == null) { - function = this.compose(name, this.functions); - if (function == null) { - function = this.compose(name, this.suppliers); - } - } - else if (Function.class.isAssignableFrom(type)) { - Object composed = this.compose(name, this.functions); - if (composed != null && Function.class.isAssignableFrom(composed.getClass())) { - function = composed; - } - } - else if (Supplier.class.isAssignableFrom(type)) { - Object composed = this.compose(name, this.suppliers); - if (composed != null && Supplier.class.isAssignableFrom(composed.getClass())) { - function = composed; - } + Object function = this.compose(name, this.functions); + if (function != null && type != null && !type.isAssignableFrom(function.getClass())) { + function = null; } return function; } diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java index ecb060543..ed62183d2 100644 --- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java +++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.cloud.function.deployer; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -574,6 +575,26 @@ class FunctionCreatorConfiguration { } } + private FunctionType createFunctionType(Object functionCatalog) { + FunctionType functionType = null; + try { + @SuppressWarnings("unchecked") + String name = ((Set) this.runner.evaluate("getNames(#type)", functionCatalog, "type", null)) + .stream().findFirst().orElse(null); + if (name != null) { + Object ft = this.runner.evaluate("getFunctionType(#name)", functionCatalog, "name", name); + Constructor ftConstructor = FunctionType.class.getDeclaredConstructor(Object.class); + ftConstructor.setAccessible(true); + functionType = ftConstructor.newInstance(ft); + } + } + catch (Exception e) { + throw new IllegalStateException("Failed to extract and map FunctionType", e); + + } + return functionType; + } + public void register(Object bean) { if (bean == null) { return; @@ -583,27 +604,34 @@ class FunctionCreatorConfiguration { FunctionProperties.functionName(this.counter.getAndIncrement())); if (this.runner != null) { if (this.runner.containsBean(FunctionInspector.class.getName())) { - Object inspector = this.runner + Object functionCatalog = this.runner .getBean(FunctionInspector.class.getName()); - Class input = (Class) this.runner.evaluate( - "getInputType(#function)", inspector, "function", bean); - FunctionType type = FunctionType.from(input); - Class output = findType("getOutputType", inspector, bean); - type = type.to(output); - if (((Boolean) this.runner.evaluate("isMessage(#function)", inspector, - "function", bean))) { - type = type.message(); + + FunctionType type = this.createFunctionType(functionCatalog); + if (type == null) { + Class input = (Class) this.runner.evaluate( + "getInputType(#function)", functionCatalog, "function", bean); + type = FunctionType.from(input); + Class output = findType("getOutputType", functionCatalog, bean); + type = type.to(output); + if (((Boolean) this.runner.evaluate("isMessage(#function)", functionCatalog, + "function", bean))) { + type = type.message(); + } + Class inputWrapper = findType("getInputWrapper", functionCatalog, bean); + if (FunctionType.isWrapper(inputWrapper)) { + type = type.wrap(inputWrapper); + } + Class outputWrapper = findType("getOutputWrapper", functionCatalog, + bean); + if (FunctionType.isWrapper(outputWrapper)) { + type = type.wrap(outputWrapper); + } } - Class inputWrapper = findType("getInputWrapper", inspector, bean); - if (FunctionType.isWrapper(inputWrapper)) { - type = type.wrap(inputWrapper); - } - Class outputWrapper = findType("getOutputWrapper", inspector, - bean); - if (FunctionType.isWrapper(outputWrapper)) { - type = type.wrap(outputWrapper); - } - registration.type(type.getType()); + registration.type(this.createFunctionType(functionCatalog)); + + + //registration.type(type.getType()); } } else {