diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java index 8081c234c..e8b2eb1bb 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java @@ -44,7 +44,6 @@ import org.springframework.boot.WebApplicationType; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; @@ -152,7 +151,7 @@ public class FunctionInvoker { Type type = FunctionContextUtils. findType(functionDefinition, APPLICATION_CONTEXT.getBeanFactory()); - functionRegistration = functionRegistration.type(new FunctionType(type)); + functionRegistration = functionRegistration.type(type); ((FunctionRegistry) FUNCTION_CATALOG).register(functionRegistration); } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java index 8aea9e37f..95b64e5d2 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java @@ -141,7 +141,7 @@ public abstract class AbstractSpringFunctionAdapterInitializer implements Clo return FunctionTypeUtils.getRawType(FunctionTypeUtils.getGenericType(((FunctionInvocationWrapper) func).getInputType())); } if (functionRegistration != null) { - return functionRegistration.getType().getInputType(); + return FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(functionRegistration.getType())); } return Object.class; } @@ -287,7 +287,7 @@ public abstract class AbstractSpringFunctionAdapterInitializer implements Clo Type type = FunctionContextUtils. findType(name, this.context.getBeanFactory()); - this.functionRegistration = functionRegistration.type(new FunctionType(type)); + this.functionRegistration = functionRegistration.type(type); ((FunctionRegistry) this.catalog).register(functionRegistration); return this.catalog.lookup(name); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java index c579273dd..8be661d2f 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionRegistration.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,19 +27,13 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import net.jodah.typetools.TypeResolver; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - import org.springframework.beans.factory.BeanNameAware; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.cloud.function.context.config.RoutingFunction; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; - - /** * @param target type * @author Dave Syer @@ -63,7 +57,7 @@ public class FunctionRegistration implements BeanNameAware { private T target; - private FunctionType type; + private Type type; /** * Creates instance of FunctionRegistration. @@ -97,7 +91,7 @@ public class FunctionRegistration implements BeanNameAware { this.names.addAll(names); } - public FunctionType getType() { + public Type getType() { return this.type; } @@ -111,25 +105,33 @@ public class FunctionRegistration implements BeanNameAware { } public FunctionRegistration type(Type type) { - return type(FunctionType.of(type)); - } - - public FunctionRegistration type(FunctionType type) { - - Type t = FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); - if (t == null) { // only valid for Kafka Stream KStream[] return type. + Type discoveredFunctionType = FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); + if (discoveredFunctionType == null) { // only valid for Kafka Stream KStream[] return type. return null; } - FunctionType discoveredFunctionType = FunctionType.of(t); - Class inputType = TypeResolver.resolveRawClass(discoveredFunctionType.getInputType(), null); - Class outputType = TypeResolver.resolveRawClass(discoveredFunctionType.getOutputType(), null); - - if (!(inputType.isAssignableFrom(TypeResolver.resolveRawClass(type.getInputType(), null)) - && outputType.isAssignableFrom(TypeResolver.resolveRawClass(type.getOutputType(), null)))) { - throw new IllegalStateException("Discovered function type does not match provided function type. Discovered: " - + discoveredFunctionType + "; Provided: " + type); - } this.type = type; + + Class inputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(discoveredFunctionType)); + Class outputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(discoveredFunctionType)); + + if (inputType != null && inputType != Object.class && outputType != null && outputType != Object.class) { + Assert.isTrue((inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))) + && outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type)))), + "Discovered function type does not match provided function type. Discovered: " + + discoveredFunctionType + "; Provided: " + type); + } + else if (inputType == null && outputType != Object.class) { + Assert.isTrue(outputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(type))), + "Discovered function type does not match provided function type. Discovered: " + + discoveredFunctionType + "; Provided: " + type); + } + else if (outputType == null && inputType != Object.class) { + Assert.isTrue(inputType.isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))), + "Discovered function type does not match provided function type. Discovered: " + + discoveredFunctionType + "; Provided: " + type); + } + + return this; } @@ -167,30 +169,30 @@ public class FunctionRegistration implements BeanNameAware { * */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public FunctionRegistration wrap() { - this.isFunctionSignatureSupported(); - FunctionRegistration result; - if (this.type == null) { - result = (FunctionRegistration) this; - } - else if (this.target instanceof RoutingFunction) { - S target = (S) this.target; - result = new FunctionRegistration(target); - result.type(this.type.getType()); - result = result.target(target).names(this.names) - .type(result.type.wrap(Flux.class)).properties(this.properties); - } - else { - S target = (S) this.target; - result = new FunctionRegistration(target); - result.type(this.type.getType()); - result = result.target(target).names(this.names) - .type(result.type.wrap(Flux.class)).properties(this.properties); - } - - return result; - } +// @SuppressWarnings({ "unchecked", "rawtypes" }) +// public FunctionRegistration wrap() { +// this.isFunctionSignatureSupported(); +// FunctionRegistration result; +// if (this.type == null) { +// result = (FunctionRegistration) this; +// } +// else if (this.target instanceof RoutingFunction) { +// S target = (S) this.target; +// result = new FunctionRegistration(target); +// result.type(this.type.getType()); +// result = result.target(target).names(this.names) +// .type(result.type.wrap(Flux.class)).properties(this.properties); +// } +// else { +// S target = (S) this.target; +// result = new FunctionRegistration(target); +// result.type(this.type.getType()); +// result = result.target(target).names(this.names) +// .type(result.type.wrap(Flux.class)).properties(this.properties); +// } +// +// return result; +// } @Override public void setBeanName(String name) { @@ -199,12 +201,12 @@ public class FunctionRegistration implements BeanNameAware { } } - private void isFunctionSignatureSupported() { - if (type != null) { - Assert.isTrue(!(Mono.class.isAssignableFrom(this.type.getOutputWrapper()) - && Mono.class.isAssignableFrom(this.type.getInputWrapper())), - "Function is not supported."); - } - } +// private void isFunctionSignatureSupported() { +// if (type != null) { +// Assert.isTrue(!(Mono.class.isAssignableFrom(this.type.getOutputWrapper()) +// && Mono.class.isAssignableFrom(this.type.getInputWrapper())), +// "Function is not supported."); +// } +// } } 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 deleted file mode 100644 index 9d9723361..000000000 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java +++ /dev/null @@ -1,549 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -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.ObjectUtils; -import org.springframework.util.ReflectionUtils; - -/** - * @author Dave Syer - * @author Oleg Zhurakousky - * - */ -public class FunctionType { - - /** - * Unclassified function types. - */ - public static FunctionType UNCLASSIFIED = new FunctionType(ResolvableType - .forClassWithGenerics(Function.class, Object.class, Object.class).getType()); - - private static List transformers; - - private Type type; - - private Class inputType; - - private Class outputType; - - private Class inputWrapper; - - private Class outputWrapper; - - private boolean message; - - public FunctionType(Type type) { - this.type = functionType(type); - this.inputWrapper = findType(ParamType.INPUT_WRAPPER); - this.outputWrapper = findType(ParamType.OUTPUT_WRAPPER); - this.inputType = findType(ParamType.INPUT); - this.outputType = findType(ParamType.OUTPUT); - this.message = messageType(); - resetType(); - } - - /* - * 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[] fields = functionType.getClass().getDeclaredFields(); - for (Field field : fields) { - if (!Modifier.isStatic(field.getModifiers())) { - field.setAccessible(true); - Field thisField = ReflectionUtils.findField(this.getClass(), field.getName()); - thisField.setAccessible(true); - thisField.set(this, field.get(functionType)); - } - } - } - - public static boolean isWrapper(Type type) { - if (type instanceof ParameterizedType) { - type = ((ParameterizedType) type).getRawType(); - } - if (transformers == null) { - transformers = new ArrayList<>(); - transformers.addAll( - SpringFactoriesLoader.loadFactories(WrapperDetector.class, null)); - } - for (WrapperDetector transformer : transformers) { - if (transformer.isWrapper(type)) { - return true; - } - } - return false; - } - - public static FunctionType of(Type function) { - FunctionType ft = new FunctionType(function); - if (!ft.isWrapper() && !(ft.type instanceof ParameterizedType)) { - Type[] genericInterfaces = ((Class) function).getGenericInterfaces(); - if (!ObjectUtils.isEmpty(genericInterfaces)) { - ft.type = genericInterfaces[0]; - } - } - return ft; - } - - public static FunctionType from(Class input) { - return new FunctionType(ResolvableType - .forClassWithGenerics(Function.class, input, Object.class).getType()); - } - - public static FunctionType supplier(Class input) { - return new FunctionType( - ResolvableType.forClassWithGenerics(Supplier.class, input).getType()); - } - - public static FunctionType consumer(Class input) { - return new FunctionType( - ResolvableType.forClassWithGenerics(Consumer.class, input).getType()); - } - - public static FunctionType compose(FunctionType input, FunctionType output) { - ResolvableType inputGeneric = input(input); - ResolvableType outputGeneric = output(output); - if (!isWrapper(outputGeneric.getType())) { - ResolvableType inputOutput = output(input); - if (isWrapper(inputOutput.getType())) { - outputGeneric = wrap(input, - extractClass(inputOutput.getType(), ParamType.OUTPUT_WRAPPER), - extractClass(outputGeneric.getType(), ParamType.OUTPUT)); - } - } - return new FunctionType(ResolvableType - .forClassWithGenerics(Function.class, inputGeneric, outputGeneric) - .getType()); - } - - public Type getType() { - return this.type; - } - - public Class getInputWrapper() { - return this.inputWrapper; - } - - public Class getOutputWrapper() { - return this.outputWrapper; - } - - public Class getInputType() { - return this.inputType; - } - - public Class getOutputType() { - return this.outputType; - } - - public boolean isMessage() { - return this.message; - } - - public boolean isWrapper() { - return isWrapper(getInputWrapper()) || isWrapper(getOutputWrapper()); - } - - public FunctionType to(Class output) { - ResolvableType inputGeneric = input(this); - ResolvableType outputGeneric = output(output); - return new FunctionType(ResolvableType - .forClassWithGenerics(Function.class, inputGeneric, outputGeneric) - .getType()); - } - - public FunctionType message() { - if (isMessage()) { - return this; - } - ResolvableType inputGeneric = message(getInputType()); - ResolvableType outputGeneric = message(getOutputType()); - if (isWrapper(getInputWrapper())) { - inputGeneric = ResolvableType.forClassWithGenerics(getInputWrapper(), - inputGeneric); - outputGeneric = ResolvableType.forClassWithGenerics(getInputWrapper(), - outputGeneric); - } - return new FunctionType(ResolvableType - .forClassWithGenerics(Function.class, inputGeneric, outputGeneric) - .getType()); - } - - public FunctionType wrap(Class input, Class output) { - if (!isWrapper(input) && !isWrapper(output)) { - return this; - } - else if (isWrapper(input) && isWrapper(output)) { - if (input.isAssignableFrom(getInputWrapper()) - && output.isAssignableFrom(getOutputWrapper())) { - return this; - } - return new FunctionType(ResolvableType.forClassWithGenerics(Function.class, - wrapper(input, getInputType()), wrapper(output, getOutputType())) - .getType()); - } - else { - throw new IllegalArgumentException("Both wrapper types must be wrappers in (" - + input + ", " + output + ")"); - } - } - - public FunctionType wrap(Class wrapper) { - return wrap(wrapper, wrapper); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((this.inputType == null) ? 0 : this.inputType.toString().hashCode()); - result = prime * result + ((this.inputWrapper == null) ? 0 - : this.inputWrapper.toString().hashCode()); - result = prime * result + (this.message ? 1231 : 1237); - result = prime * result - + ((this.outputType == null) ? 0 : this.outputType.toString().hashCode()); - result = prime * result + ((this.outputWrapper == null) ? 0 - : this.outputWrapper.toString().hashCode()); - return result; - } - - public String toString() { - if (this.inputType == Void.class) { - return this.type.toString() + ", which is effectively a Supplier<" - + this.outputType + ">"; - } - else if (this.outputType == Void.class) { - return this.type.toString() + ", which is effectively a Consumer<" - + this.inputType + ">"; - } - return this.type.toString(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - FunctionType other = (FunctionType) obj; - if (this.inputType == null) { - if (other.inputType != null) { - return false; - } - } - else if (!this.inputType.toString().equals(other.inputType.toString())) { - return false; - } - if (this.inputWrapper == null) { - if (other.inputWrapper != null) { - return false; - } - } - else if (!this.inputWrapper.toString().equals(other.inputWrapper.toString())) { - return false; - } - if (this.message != other.message) { - return false; - } - if (this.outputType == null) { - if (other.outputType != null) { - return false; - } - } - else if (!this.outputType.toString().equals(other.outputType.toString())) { - return false; - } - if (this.outputWrapper == null) { - if (other.outputWrapper != null) { - return false; - } - } - else if (!this.outputWrapper.toString().equals(other.outputWrapper.toString())) { - return false; - } - return true; - } - - private static ResolvableType wrap(FunctionType input, Class wrapper, - Class type) { - return input.isMessage() ? wrap(wrapper, message(type)) - : ResolvableType.forClassWithGenerics(wrapper, type); - } - - private static ResolvableType wrap(Class wrapper, ResolvableType type) { - return ResolvableType.forClassWithGenerics(wrapper, type); - } - - private static ResolvableType message(Class type) { - return ResolvableType.forClassWithGenerics(Message.class, type); - } - - private static ResolvableType input(FunctionType type) { - return type.input(type.getInputType()); - } - - private static ResolvableType output(FunctionType type) { - return type.output(type.getOutputType()); - } - - private static Class extractClass(Type param, ParamType paramType) { - if (param instanceof ParameterizedType) { - ParameterizedType concrete = (ParameterizedType) param; - param = concrete.getRawType(); - } - if (param == null) { - // Last ditch attempt to guess: Flux - if (paramType.isWrapper()) { - param = Flux.class; - } - else { - param = String.class; - } - } - Class result = param instanceof Class ? (Class) param : null; - // TODO: cache result - return result; - } - - private ResolvableType wrapper(Class wrapper, Class type) { - return wrap(this, wrapper, type); - } - - private ResolvableType output(Class type) { - ResolvableType generic; - ResolvableType raw = ResolvableType.forClass(type); - if (isMessage()) { - raw = ResolvableType.forClassWithGenerics(Message.class, raw); - } - if (FunctionType.isWrapper(getOutputWrapper())) { - generic = ResolvableType.forClassWithGenerics(getOutputWrapper(), raw); - } - else { - generic = raw; - } - return generic; - } - - private ResolvableType input(Class type) { - ResolvableType generic; - ResolvableType raw = ResolvableType.forClass(type); - if (isMessage()) { - raw = ResolvableType.forClassWithGenerics(Message.class, raw); - } - if (FunctionType.isWrapper(getInputWrapper())) { - generic = ResolvableType.forClassWithGenerics(getInputWrapper(), raw); - } - else { - generic = raw; - } - return generic; - } - - private Class findType(ParamType paramType) { - int index = paramType.isOutput() ? 1 : 0; - Type type = this.type; - if (Supplier.class.isAssignableFrom(extractClass(this.type, null))) { - if (paramType.isInput()) { - return Void.class; - } - } - boolean found = false; - while (!found && type instanceof Class && type != Object.class) { - Class clz = (Class) type; - for (Type iface : clz.getGenericInterfaces()) { - if (iface.getTypeName().startsWith("java.util.function")) { - type = iface; - found = true; - break; - } - } - if (!found) { - type = clz.getSuperclass(); - } - } - Type param = extractType(type, paramType, index); - if (param != null) { - Class result = extractClass(param, paramType); - if (result != null) { - return result; - } - } - return Object.class; - } - - private void resetType() { - if (!this.type.getTypeName().contains("EnhancerBySpringCGLIB")) { - return; - } - Type type = this.type; - - boolean found = false; - while (!found && type instanceof Class && type != Object.class) { - Class clz = (Class) type; - for (Type iface : clz.getGenericInterfaces()) { - if (iface.getTypeName().startsWith("java.util.function")) { - type = iface; - found = true; - break; - } - } - if (!found) { - type = clz.getSuperclass(); - } - } - this.type = type; - } - - private Type extractType(Type type, ParamType paramType, int index) { - Type param; - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - if (parameterizedType.getActualTypeArguments().length == 1) { - if (isVoid(parameterizedType, paramType)) { - return Void.class; - } - // There's only one - index = 0; - } - Type typeArgumentAtIndex = parameterizedType.getActualTypeArguments()[index]; - if (typeArgumentAtIndex instanceof ParameterizedType - && !paramType.isWrapper()) { - if (FunctionType.isWrapper( - ((ParameterizedType) typeArgumentAtIndex).getRawType())) { - param = ((ParameterizedType) typeArgumentAtIndex) - .getActualTypeArguments()[0]; - param = extractNestedType(paramType, param); - } - else { - param = extractNestedType(paramType, typeArgumentAtIndex); - } - } - else { - param = extractNestedType(paramType, typeArgumentAtIndex); - } - } - else { - if (type != null) { - Type[] interfaces = ((Class) type).getGenericInterfaces(); - for (Type ifc : interfaces) { - Type value = extractType(ifc, paramType, index); - if (value != Object.class) { - return value; - } - } - } - param = Object.class; - } - return param; - } - - private boolean isVoid(ParameterizedType parameterizedType, ParamType paramType) { - Class rawType = extractClass(parameterizedType.getRawType(), paramType); - if (Consumer.class.isAssignableFrom(rawType) && paramType.isOutput()) { - return true; - } - if (Supplier.class.isAssignableFrom(rawType) && paramType.isInput()) { - return true; - } - return false; - } - - private Type extractNestedType(ParamType paramType, Type param) { - if (!paramType.isInnerWrapper() && param instanceof ParameterizedType) { - if (((ParameterizedType) param).getRawType().getTypeName() - .startsWith(Message.class.getName())) { - param = ((ParameterizedType) param).getActualTypeArguments()[0]; - } - } - return param; - } - - private Type functionType(Type type) { - if (Supplier.class.isAssignableFrom(extractClass(type, ParamType.OUTPUT))) { - Type product = extractType(type, ParamType.OUTPUT, 0); - Class output = extractClass(product, ParamType.OUTPUT); - if (output != null) { - if (FunctionRegistration.class.isAssignableFrom(output)) { - type = extractType(product, ParamType.OUTPUT, 0); - } - else if (Function.class.isAssignableFrom(output) - || Supplier.class.isAssignableFrom(output) - || Consumer.class.isAssignableFrom(output)) { - type = product; - } - } - } - return type; - } - - private boolean messageType() { - Class inputType = findType(ParamType.INPUT_INNER_WRAPPER); - Class outputType = findType(ParamType.OUTPUT_INNER_WRAPPER); - return inputType.getName().startsWith(Message.class.getName()) - || Message.class.isAssignableFrom(inputType) - || outputType.getName().startsWith(Message.class.getName()) - || Message.class.isAssignableFrom(outputType); - } - - enum ParamType { - - INPUT, OUTPUT, INPUT_WRAPPER, OUTPUT_WRAPPER, INPUT_INNER_WRAPPER, OUTPUT_INNER_WRAPPER; - - public boolean isOutput() { - return this == OUTPUT || this == OUTPUT_WRAPPER - || this == OUTPUT_INNER_WRAPPER; - } - - public boolean isInput() { - return this == INPUT || this == INPUT_WRAPPER || this == INPUT_INNER_WRAPPER; - } - - public boolean isWrapper() { - return this == OUTPUT_WRAPPER || this == INPUT_WRAPPER; - } - - public boolean isInnerWrapper() { - return this == OUTPUT_INNER_WRAPPER || this == INPUT_INNER_WRAPPER; - } - - } - -} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java index eebf2f0d2..04365495a 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionalSpringApplication.java @@ -153,7 +153,7 @@ public class FunctionalSpringApplication context.registerBean("function", FunctionRegistration.class, () -> new FunctionRegistration<>( handler(context, function, functionType)) - .type(FunctionType.of(functionType))); + .type(functionType)); } private Object handler(GenericApplicationContext generic, Object handler, diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeConversionHelper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeConversionHelper.java index 67e871b8e..f5b0a4840 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeConversionHelper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeConversionHelper.java @@ -71,12 +71,12 @@ class FunctionTypeConversionHelper { this.conversionService = conversionService; this.messageConverter = messageConverter; this.functionRegistration = functionRegistration; - if ((this.functionRegistration.getType().getType()) instanceof ParameterizedType) { - this.functionArgumentTypes = ((ParameterizedType) this.functionRegistration.getType().getType()) + if ((this.functionRegistration.getType()) instanceof ParameterizedType) { + this.functionArgumentTypes = ((ParameterizedType) this.functionRegistration.getType()) .getActualTypeArguments(); } else { - this.functionArgumentTypes = new Type[] { this.functionRegistration.getType().getInputType() }; + this.functionArgumentTypes = new Type[] { FunctionTypeUtils.getInputType(this.functionRegistration.getType()) }; } } @@ -227,7 +227,7 @@ class FunctionTypeConversionHelper { } } else { - Assert.isTrue(!Publisher.class.isAssignableFrom(this.functionRegistration.getType().getInputWrapper()), + Assert.isTrue(!FunctionTypeUtils.isPublisher(FunctionTypeUtils.getInputType(this.functionRegistration.getType())), "Invoking reactive function as imperative is not allowed. Function name(s): " + this.functionRegistration.getNames()); incoming = this.doConvertArgument(incoming, targetType, actualType); @@ -244,7 +244,7 @@ class FunctionTypeConversionHelper { : Flux.from((Publisher) incoming).map(value -> this.messageConverter.toMessage(value, headers)); } else { - Assert.isTrue(!Publisher.class.isAssignableFrom(this.functionRegistration.getType().getInputWrapper()), + Assert.isTrue(!FunctionTypeUtils.isPublisher(FunctionTypeUtils.getInputType(this.functionRegistration.getType())), "Invoking reactive function as imperative is not allowed. Function name(s): " + this.functionRegistration.getNames()); incoming = this.messageConverter.toMessage(incoming, headers); @@ -296,7 +296,7 @@ class FunctionTypeConversionHelper { incomingValue = incomingMessage; } else { - incomingValue = this.messageConverter.fromMessage((Message) incomingMessage, targetType); + incomingValue = this.messageConverter.fromMessage(incomingMessage, targetType); } } return incomingValue; diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java index be0b9264e..0574ae627 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java @@ -41,7 +41,6 @@ import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.config.FunctionContextUtils; import org.springframework.cloud.function.context.config.RoutingFunction; import org.springframework.context.support.GenericApplicationContext; @@ -68,6 +67,21 @@ public final class FunctionTypeUtils { } + public static Type functionType(Type input, Type output) { + return ResolvableType.forClassWithGenerics(Function.class, + ResolvableType.forType(input), ResolvableType.forType(output)).getType(); + } + + public static Type consumerType(Type input) { + return ResolvableType.forClassWithGenerics(Consumer.class, + ResolvableType.forType(input)).getType(); + } + + public static Type supplierType(Type output) { + return ResolvableType.forClassWithGenerics(Supplier.class, + ResolvableType.forType(output)).getType(); + } + /** * Will return 'true' if the provided type is a {@link Collection} type. * This also includes collections wrapped in {@link Message}. For example, @@ -176,6 +190,37 @@ public final class FunctionTypeUtils { return null; } + /** + * Discovers the function {@link Type} based on the signature of a factory method. + * For example, given the following method {@code Function, Message> uppercase()} of + * class Foo - {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} + * + * @param clazz instance of Class containing the factory method + * @param methodName factory method name + * @return type of the function + */ + public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, String methodName) { + return discoverFunctionTypeFromFunctionFactoryMethod(ReflectionUtils.findMethod(clazz, methodName)); + } + + /** + * Discovers the function {@link Type} based on the signature of a factory method. + * For example, given the following method {@code Function, Message> uppercase()} of + * class Foo - {@code Type type = discoverFunctionTypeFromFunctionFactoryMethod(Foo.class, "uppercase");} + * + * @param method factory method + * @return type of the function + */ + public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) { + return method.getGenericReturnType(); + } + + /** + * Unlike {@link #discoverFunctionTypeFromFunctionFactoryMethod(Class, String)}, this method discovers function + * type from the well known method of Function(apply), Supplier(get) or Consumer(accept). + * @param functionMethod functional method + * @return type of the function + */ public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod) { Assert.isTrue( functionMethod.getName().equals("apply") || @@ -223,6 +268,26 @@ public final class FunctionTypeUtils { return outputCount; } + /** + * In the event the input type is {@link ParameterizedType} this method returns its generic type. + * @param functionType instance of function type + * @return generic type or input type + */ + public static Type getComponentTypeOfInputType(Type functionType) { + Type inputType = getInputType(functionType); + return getImmediateGenericType(inputType, 0); + } + + /** + * In the event the output type is {@link ParameterizedType} this method returns its generic type. + * @param functionType instance of function type + * @return generic type or output type + */ + public static Type getComponentTypeOfOutputType(Type functionType) { + Type inputType = getOutputType(functionType); + return getImmediateGenericType(inputType, 0); + } + /** * Returns input type of function type that represents Function or Consumer. * @param functionType the Type of Function or Consumer @@ -253,15 +318,15 @@ public final class FunctionTypeUtils { @SuppressWarnings("rawtypes") public static Type discoverFunctionType(Object function, String functionName, GenericApplicationContext applicationContext) { if (function instanceof RoutingFunction) { - return FunctionType.of(FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionName)).getType(); + return FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionName); } else if (function instanceof FunctionRegistration) { - return ((FunctionRegistration) function).getType().getType(); + return ((FunctionRegistration) function).getType(); } if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { // for Kotlin primarily FunctionRegistration fr = applicationContext .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); - return fr.getType().getType(); + return fr.getType(); } boolean beanDefinitionExists = false; @@ -277,13 +342,13 @@ public final class FunctionTypeUtils { if (beanDefinitionExists) { Type t = FunctionTypeUtils.getImmediateGenericType(type, 0); if (t == null || t == Object.class) { - type = FunctionType.of(FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionBeanDefinitionName)).getType(); + type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), functionBeanDefinitionName); } } else if (!(type instanceof ParameterizedType)) { String beanDefinitionName = discoverBeanDefinitionNameByQualifier(applicationContext.getBeanFactory(), functionName); if (StringUtils.hasText(beanDefinitionName)) { - type = FunctionType.of(FunctionContextUtils.findType(applicationContext.getBeanFactory(), beanDefinitionName)).getType(); + type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), beanDefinitionName); } } return type; diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java index 2e22f46c4..a6e13f4f4 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java @@ -262,7 +262,7 @@ public class SimpleFunctionRegistry implements FunctionRegistry { .findFirst() .orElseGet(() -> null); FunctionInvocationWrapper function = functionRegistration != null - ? this.invocationWrapperInstance(functionName, functionRegistration.getTarget(), functionRegistration.getType().getType()) + ? this.invocationWrapperInstance(functionName, functionRegistration.getTarget(), functionRegistration.getType()) : null; if (functionRegistration != null && functionRegistration.getProperties().containsKey("singleton")) { try { diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/FunctionTypeTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/FunctionTypeTests.java deleted file mode 100644 index 94b0abb81..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/FunctionTypeTests.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuple3; - -import org.springframework.core.ResolvableType; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Dave Syer - * - */ -public class FunctionTypeTests { - - @Test - public void functionWithTuples() { - FunctionType functionType = FunctionType.of(MyFunction.class); - assertThat(functionType.getType()).isInstanceOf(ParameterizedType.class); - } - - @Test - public void plainFunction() { - FunctionType function = new FunctionType(IntegerToString.class); - assertThat(function.getInputType()).isEqualTo(Integer.class); - assertThat(function.getOutputType()).isEqualTo(String.class); - assertThat(function.getInputWrapper()).isEqualTo(Integer.class); - assertThat(function.getOutputWrapper()).isEqualTo(String.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void supplierOfRegistration() { - FunctionType function = new FunctionType( - SupplierOfRegistrationOfIntegerToString.class); - assertThat(function.getInputType()).isEqualTo(Integer.class); - assertThat(function.getOutputType()).isEqualTo(String.class); - assertThat(function.getInputWrapper()).isEqualTo(Integer.class); - assertThat(function.getOutputWrapper()).isEqualTo(String.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void supplier() { - FunctionType function = new FunctionType(SupplierOfIntegerToString.class); - assertThat(function.getInputType()).isEqualTo(Integer.class); - assertThat(function.getOutputType()).isEqualTo(String.class); - assertThat(function.getInputWrapper()).isEqualTo(Integer.class); - assertThat(function.getOutputWrapper()).isEqualTo(String.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void genericFunction() { - FunctionType function = new FunctionType(StringToMap.class); - assertThat(function.getInputType()).isEqualTo(String.class); - assertThat(function.getOutputType()).isEqualTo(Map.class); - assertThat(function.getInputWrapper()).isEqualTo(String.class); - assertThat(function.getOutputWrapper()).isEqualTo(Map.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void pojoFunction() { - FunctionType function = new FunctionType(FooToFoo.class); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Bar.class); - assertThat(function.getInputWrapper()).isEqualTo(Foo.class); - assertThat(function.getOutputWrapper()).isEqualTo(Bar.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void fluxFunction() { - FunctionType function = new FunctionType(FluxToFlux.class); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Bar.class); - assertThat(function.getInputWrapper()).isEqualTo(Flux.class); - assertThat(function.getOutputWrapper()).isEqualTo(Flux.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void fluxMessageFunction() { - FunctionType function = new FunctionType(FluxMessageToFluxMessage.class); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Bar.class); - assertThat(function.getInputWrapper()).isEqualTo(Flux.class); - assertThat(function.getOutputWrapper()).isEqualTo(Flux.class); - assertThat(function.isMessage()).isEqualTo(true); - } - - @Test - public void plainFunctionFromType() { - Type type = ResolvableType - .forClassWithGenerics(Function.class, Integer.class, String.class) - .getType(); - FunctionType function = new FunctionType(type); - assertThat(function.getInputType()).isEqualTo(Integer.class); - assertThat(function.getOutputType()).isEqualTo(String.class); - assertThat(function.getInputWrapper()).isEqualTo(Integer.class); - assertThat(function.getOutputWrapper()).isEqualTo(String.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void pojoConsumerFromType() { - Type type = ResolvableType.forClassWithGenerics(Consumer.class, Foo.class) - .getType(); - FunctionType function = new FunctionType(type); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Void.class); - assertThat(function.getInputWrapper()).isEqualTo(Foo.class); - assertThat(function.getOutputWrapper()).isEqualTo(Void.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void pojoSupplierFromType() { - Type type = ResolvableType.forClassWithGenerics(Supplier.class, Foo.class) - .getType(); - FunctionType function = new FunctionType(type); - assertThat(function.getInputType()).isEqualTo(Void.class); - assertThat(function.getOutputType()).isEqualTo(Foo.class); - assertThat(function.getInputWrapper()).isEqualTo(Void.class); - assertThat(function.getOutputWrapper()).isEqualTo(Foo.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void pojoSupplierFrom() { - FunctionType function = new FunctionType(Supplier.class).to(Foo.class); - assertThat(function.getInputType()).isEqualTo(Void.class); - assertThat(function.getOutputType()).isEqualTo(Foo.class); - assertThat(function.getInputWrapper()).isEqualTo(Void.class); - assertThat(function.getOutputWrapper()).isEqualTo(Foo.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void pojoSupplier() { - FunctionType function = FunctionType.supplier(Foo.class); - assertThat(function.getInputType()).isEqualTo(Void.class); - assertThat(function.getOutputType()).isEqualTo(Foo.class); - assertThat(function.getInputWrapper()).isEqualTo(Void.class); - assertThat(function.getOutputWrapper()).isEqualTo(Foo.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void pojoConsumer() { - FunctionType function = FunctionType.consumer(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Void.class); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputWrapper()).isEqualTo(Void.class); - assertThat(function.getInputWrapper()).isEqualTo(Foo.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void plainFunctionFromApi() { - FunctionType function = FunctionType.from(Integer.class).to(String.class); - assertThat(function.getInputType()).isEqualTo(Integer.class); - assertThat(function.getOutputType()).isEqualTo(String.class); - assertThat(function.getInputWrapper()).isEqualTo(Integer.class); - assertThat(function.getOutputWrapper()).isEqualTo(String.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void fluxMessageFunctionFromType() { - Type type = ResolvableType - .forClassWithGenerics(Function.class, - ResolvableType.forClassWithGenerics( - Flux.class, - ResolvableType.forClassWithGenerics(Message.class, - Foo.class)), - ResolvableType.forClassWithGenerics(Flux.class, ResolvableType - .forClassWithGenerics(Message.class, Bar.class))) - .getType(); - FunctionType function = new FunctionType(type); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Bar.class); - assertThat(function.getInputWrapper()).isEqualTo(Flux.class); - assertThat(function.getOutputWrapper()).isEqualTo(Flux.class); - assertThat(function.isMessage()).isEqualTo(true); - } - - @Test - public void fluxMessageFunctionFromApi() { - FunctionType function = FunctionType.from(Foo.class).to(Bar.class).message() - .wrap(Flux.class); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(Bar.class); - assertThat(function.getInputWrapper()).isEqualTo(Flux.class); - assertThat(function.getOutputWrapper()).isEqualTo(Flux.class); - assertThat(function.isMessage()).isEqualTo(true); - } - - @Test - public void compose() { - FunctionType input = FunctionType.from(Foo.class).to(Bar.class).wrap(Flux.class); - FunctionType output = FunctionType.from(Bar.class).to(String.class); - FunctionType function = FunctionType.compose(input, output); - assertThat(function.getInputType()).isEqualTo(Foo.class); - assertThat(function.getOutputType()).isEqualTo(String.class); - assertThat(function.getInputWrapper()).isEqualTo(Flux.class); - assertThat(function.getOutputWrapper()).isEqualTo(Flux.class); - assertThat(function.isMessage()).isEqualTo(false); - } - - @Test - public void idempotentMessage() { - FunctionType function = FunctionType.from(Foo.class).to(Bar.class).message() - .wrap(Flux.class); - assertThat(function).isSameAs(function.message()); - } - - @Test - public void idempotentWrapper() { - FunctionType function = FunctionType.from(Foo.class).to(Bar.class).message() - .wrap(Flux.class); - assertThat(function).isSameAs(function.wrap(Flux.class)); - } - - @Test - public void nonWrapper() { - FunctionType function = FunctionType.from(Foo.class).to(Bar.class); - assertThat(function).isSameAs(function.wrap(Object.class)); - } - - private static class SupplierOfRegistrationOfIntegerToString - implements Supplier>> { - - @Override - public FunctionRegistration> get() { - return new FunctionRegistration>( - new IntegerToString(), "ints"); - } - - } - - private static class SupplierOfIntegerToString - implements Supplier> { - - @Override - public Function get() { - return new IntegerToString(); - } - - } - - private static class IntegerToString implements Function { - - @Override - public String apply(Integer t) { - return "" + t; - } - - } - - private static class StringToMap implements Function> { - - @Override - public Map apply(String t) { - return Collections.emptyMap(); - } - - } - - private static class FooToFoo implements Function { - - @Override - public Bar apply(Foo t) { - return new Bar(); - } - - } - - private static class FluxToFlux implements Function, Flux> { - - @Override - public Flux apply(Flux t) { - return t.map(f -> new Bar()); - } - - } - - private static class FluxMessageToFluxMessage - implements Function>, Flux>> { - - @Override - public Flux> apply(Flux> t) { - return t.map(f -> MessageBuilder.withPayload(new Bar()) - .copyHeadersIfAbsent(f.getHeaders()).build()); - } - - } - - private static class Foo { - - } - - private static class Bar { - - } - - private static class MyFunction - implements Function, Flux>, Tuple3, Flux, Flux>> { - - @Override - public Tuple3, Flux, Flux> apply(Tuple2, Flux> t) { - return null; - } - -} - -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java deleted file mode 100644 index 66e257ec9..000000000 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2019-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.function.context; - -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.support.GenericApplicationContext; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * - * @author Oleg Zhurakousky - * - */ -public class SpringFunctionAdapterInitializerTests { - - private AbstractSpringFunctionAdapterInitializer initializer; - - @AfterEach - public void after() { - System.clearProperty("function.name"); - if (this.initializer != null) { - this.initializer.close(); - } - } - - @Test - public void nullSource() { - Assertions.assertThrows(IllegalArgumentException.class, () -> - this.initializer = new AbstractSpringFunctionAdapterInitializer(null) { - - }); - } - - @Test - public void sourceAsMainClassProperty() { - try { - System.setProperty("MAIN_CLASS", FluxFunctionConfig.class.getName()); - this.initializer = new AbstractSpringFunctionAdapterInitializer() { - - }; - } - finally { - System.clearProperty("MAIN_CLASS"); - } - } - - @Test - public void functionBean() { - this.initializer = new AbstractSpringFunctionAdapterInitializer(FluxFunctionConfig.class) { - - }; - this.initializer.initialize(null); - Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - - @Test - public void functionApp() { - this.initializer = new AbstractSpringFunctionAdapterInitializer(FluxFunctionApp.class) { - - }; - this.initializer.initialize(null); - Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); - Object o = result.blockFirst(); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - - @Test - public void functionCatalog() { - this.initializer = new AbstractSpringFunctionAdapterInitializer(FunctionConfig.class) { - - }; - this.initializer.initialize(null); - Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - - @Test - @Disabled // related to boot 2.1 no bean override change - public void functionRegistrar() { - this.initializer = new AbstractSpringFunctionAdapterInitializer(FunctionRegistrar.class) { - - }; - this.initializer.initialize(null); - Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - - @Test - public void namedFunctionCatalog() { - this.initializer = new AbstractSpringFunctionAdapterInitializer(NamedFunctionConfig.class) { - - }; - System.setProperty("function.name", "other"); - this.initializer.initialize(null); - Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - - @Test - public void consumerCatalog() { - this.initializer = new AbstractSpringFunctionAdapterInitializer(ConsumerConfig.class) { - - }; - this.initializer.initialize(null); - Flux result = Flux.from(this.initializer.apply(Flux.just(new Foo()))); - assertThat(result.toStream().collect(Collectors.toList())).isEmpty(); - } - - @Test - public void supplierCatalog() { - initializer = new AbstractSpringFunctionAdapterInitializer(SupplierConfig.class) { - - }; - initializer.initialize(null); - Flux result = Flux.from(initializer.apply(null)); - assertThat(result.blockFirst()).isInstanceOf(Bar.class); - } - - @Configuration - protected static class FluxFunctionConfig { - - @Bean - public Function, Flux> function() { - return flux -> flux.map(foo -> new Bar()); - } - - } - - protected static class FluxFunctionApp implements Function, Flux> { - - @Override - public Flux apply(Flux flux) { - return flux.map(foo -> new Bar()); - } - - } - - protected static class FunctionRegistrar - implements ApplicationContextInitializer { - - public Function, Flux> function() { - return flux -> flux.map(foo -> new Bar()); - } - - @Override - public void initialize(GenericApplicationContext context) { - context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration, Flux>>( - function()).name("function") - .type(FunctionType.from(Foo.class).to(Bar.class) - .wrap(Flux.class).getType())); - } - - } - - @Configuration - @Import(ContextFunctionCatalogAutoConfiguration.class) - protected static class FunctionConfig { - - @Bean - public Function function() { - return foo -> new Bar(); - } - - } - - @Configuration - @Import(ContextFunctionCatalogAutoConfiguration.class) - protected static class NamedFunctionConfig { - - @Bean - public Function other() { - return foo -> new Bar(); - } - - } - - @Configuration - @Import(ContextFunctionCatalogAutoConfiguration.class) - protected static class SupplierConfig { - @Bean - public Supplier supplier() { - return () -> { - return new Bar(); - }; - } - } - - @Configuration - @Import(ContextFunctionCatalogAutoConfiguration.class) - protected static class ConsumerConfig { - - @Bean - public Consumer consumer() { - return foo -> { - }; - } - - } - - protected static class Foo { - - } - - protected static class Bar { - - } -} diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java index 91bd06857..d750df4b4 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java @@ -52,7 +52,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; import org.springframework.cloud.function.json.JsonMapper; import org.springframework.context.ApplicationContext; @@ -498,30 +497,35 @@ public class BeanFactoryAwareFunctionRegistryTests { assertThat(func).isNull(); FunctionRegistry registry = (FunctionRegistry) catalog; try { - FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "a").type(FunctionType.from(Integer.class).to(String.class)); + FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "a") + .type(FunctionTypeUtils.functionType(Integer.class, String.class)); registry.register(registration); fail(); } - catch (IllegalStateException e) { + catch (IllegalArgumentException e) { // good as we expect it to fail } // try { - FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "b").type(FunctionType.from(String.class).to(Integer.class)); + FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "b") + .type(FunctionTypeUtils.functionType(String.class, Integer.class)); registry.register(registration); fail(); } - catch (IllegalStateException e) { + catch (IllegalArgumentException e) { // good as we expect it to fail } // - FunctionRegistration c = new FunctionRegistration(new MyFunction(), "c").type(FunctionType.from(String.class).to(String.class)); + FunctionRegistration c = new FunctionRegistration(new MyFunction(), "c") + .type(FunctionTypeUtils.functionType(String.class, String.class)); registry.register(c); // - FunctionRegistration d = new FunctionRegistration(new RawFunction(), "d").type(FunctionType.from(Person.class).to(String.class)); + FunctionRegistration d = new FunctionRegistration(new RawFunction(), "d") + .type(FunctionTypeUtils.functionType(Person.class, String.class)); registry.register(d); // - FunctionRegistration e = new FunctionRegistration(new RawFunction(), "e").type(FunctionType.from(Object.class).to(Object.class)); + FunctionRegistration e = new FunctionRegistration(new RawFunction(), "e") + .type(FunctionTypeUtils.functionType(Object.class, Object.class)); registry.register(e); } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java index 6f0285422..1f96e0ab1 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtilsTests.java @@ -31,7 +31,6 @@ import reactor.core.publisher.Mono; import reactor.util.function.Tuple2; import reactor.util.function.Tuple3; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.core.ParameterizedTypeReference; import org.springframework.messaging.Message; @@ -114,29 +113,29 @@ public class FunctionTypeUtilsTests { @Test public void testFunctionTypeByClassDiscovery() { - FunctionType type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(Function.class)); - assertThat(type.getInputType()).isAssignableFrom(Object.class); + Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(Function.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(type))).isAssignableFrom(Object.class); - type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MessageFunction.class)); - assertThat(type.getInputType()).isAssignableFrom(String.class); - assertThat(type.getOutputType()).isAssignableFrom(String.class); + type = FunctionTypeUtils.discoverFunctionTypeFromClass(MessageFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))).isAssignableFrom(String.class); - type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageFunction.class)); - assertThat(type.getInputType()).isAssignableFrom(String.class); - assertThat(type.getOutputType()).isAssignableFrom(String.class); + type = FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageFunction.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))).isAssignableFrom(String.class); - type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MessageConsumer.class)); - assertThat(type.getInputType()).isAssignableFrom(String.class); + type = FunctionTypeUtils.discoverFunctionTypeFromClass(MessageConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); - type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageConsumer.class)); - assertThat(type.getInputType()).isAssignableFrom(String.class); + type = FunctionTypeUtils.discoverFunctionTypeFromClass(MyMessageConsumer.class); + assertThat(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))).isAssignableFrom(String.class); } @Test public void testWithComplexHierarchy() { - FunctionType type = FunctionType.of(FunctionTypeUtils.discoverFunctionTypeFromClass(ReactiveFunctionImpl.class)); - assertThat(String.class).isAssignableFrom(type.getInputType()); - assertThat(Integer.class).isAssignableFrom(type.getOutputType()); + Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(ReactiveFunctionImpl.class); + assertThat(String.class).isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfInputType(type))); + assertThat(Integer.class).isAssignableFrom(FunctionTypeUtils.getRawType(FunctionTypeUtils.getComponentTypeOfOutputType(type))); } @Test diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java index 9f5840c50..517b08dfa 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java @@ -40,7 +40,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.FunctionRegistry; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.HybridFunctionalRegistrationTests.UppercaseFunction; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; import org.springframework.cloud.function.context.config.JsonMessageConverter; @@ -91,7 +90,7 @@ public class SimpleFunctionRegistryTests { public void testCachingOfFunction() { Echo function = new Echo(); FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(FunctionType.of(Echo.class)); + function, "echo").type(Echo.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -107,7 +106,7 @@ public class SimpleFunctionRegistryTests { public void testNoCachingOfFunction() { Echo function = new Echo(); FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(FunctionType.of(Echo.class)); + function, "echo").type(Echo.class); registration.getProperties().put("singleton", "false"); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); @@ -124,7 +123,7 @@ public class SimpleFunctionRegistryTests { public void testSCF640() { Echo function = new Echo(); FunctionRegistration registration = new FunctionRegistration<>( - function, "echo").type(FunctionType.of(Echo.class)); + function, "echo").type(Echo.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -142,27 +141,27 @@ public class SimpleFunctionRegistryTests { new JacksonMapper(new ObjectMapper())); FunctionRegistration reg1 = new FunctionRegistration<>( - new UpperCase(), "uppercase").type(FunctionType.of(UpperCase.class)); + new UpperCase(), "uppercase").type(UpperCase.class); catalog.register(reg1); // FunctionRegistration reg2 = new FunctionRegistration<>( - new UpperCaseMessage(), "uppercaseMessage").type(FunctionType.of(UpperCaseMessage.class)); + new UpperCaseMessage(), "uppercaseMessage").type(UpperCaseMessage.class); catalog.register(reg2); // FunctionRegistration reg3 = new FunctionRegistration<>( - new StringArrayFunction(), "stringArray").type(FunctionType.of(StringArrayFunction.class)); + new StringArrayFunction(), "stringArray").type(StringArrayFunction.class); catalog.register(reg3); // FunctionRegistration reg4 = new FunctionRegistration<>( - new TypelessFunction(), "typeless").type(FunctionType.of(TypelessFunction.class)); + new TypelessFunction(), "typeless").type(TypelessFunction.class); catalog.register(reg4); // FunctionRegistration reg5 = new FunctionRegistration<>( - new ByteArrayFunction(), "typeless").type(FunctionType.of(ByteArrayFunction.class)); + new ByteArrayFunction(), "typeless").type(ByteArrayFunction.class); catalog.register(reg5); // FunctionRegistration reg6 = new FunctionRegistration<>( - new StringListFunction(), "stringList").type(FunctionType.of(StringListFunction.class)); + new StringListFunction(), "stringList").type(StringListFunction.class); catalog.register(reg6); Message collectionMessage = MessageBuilder.withPayload("[\"ricky\", \"julien\", \"bubbles\"]").build(); @@ -218,7 +217,7 @@ public class SimpleFunctionRegistryTests { UpperCase function = new UpperCase(); FunctionRegistration registration = new FunctionRegistration<>( - function, "foo").type(FunctionType.of(UppercaseFunction.class)); + function, "foo").type(UppercaseFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -237,7 +236,7 @@ public class SimpleFunctionRegistryTests { TestFunction function = new TestFunction(); FunctionRegistration registration = new FunctionRegistration<>( - function, "foo").type(FunctionType.of(TestFunction.class)); + function, "foo").type(TestFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -246,7 +245,7 @@ public class SimpleFunctionRegistryTests { FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello"); assertThat(lookedUpFunction).isNotNull(); // because we only have one and can look it up with any name FunctionRegistration registration2 = new FunctionRegistration<>( - function, "foo2").type(FunctionType.of(TestFunction.class)); + function, "foo2").type(TestFunction.class); catalog.register(registration2); lookedUpFunction = catalog.lookup("hello"); assertThat(lookedUpFunction).isNull(); @@ -257,9 +256,9 @@ public class SimpleFunctionRegistryTests { @Test public void testFunctionComposition() { FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( - new UpperCase(), "uppercase").type(FunctionType.of(UpperCase.class)); + new UpperCase(), "uppercase").type(UpperCase.class); FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(FunctionType.of(Reverse.class)); + new Reverse(), "reverse").type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(upperCaseRegistration); @@ -268,23 +267,15 @@ public class SimpleFunctionRegistryTests { Function, Flux> lookedUpFunction = catalog .lookup("uppercase|reverse"); assertThat(lookedUpFunction).isNotNull(); - - Flux flux = lookedUpFunction.apply(Flux.just("star")); - flux.subscribe(v -> { - System.out.println(v); - }); - -// assertThat(lookedUpFunction.apply(Flux.just("star")).blockFirst()) -// .isEqualTo("RATS"); } @Test @Disabled public void testFunctionCompositionImplicit() { FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(FunctionType.of(Words.class)); + new Words(), "words").type(Words.class); FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(FunctionType.of(Reverse.class)); + new Reverse(), "reverse").type(Reverse.class); FunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(wordsRegistration); @@ -301,9 +292,9 @@ public class SimpleFunctionRegistryTests { @Disabled public void testFunctionCompletelyImplicitComposition() { FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(FunctionType.of(Words.class)); + new Words(), "words").type(Words.class); FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(FunctionType.of(Reverse.class)); + new Reverse(), "reverse").type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(wordsRegistration); @@ -319,9 +310,9 @@ public class SimpleFunctionRegistryTests { @Test public void testFunctionCompositionExplicit() { FunctionRegistration wordsRegistration = new FunctionRegistration<>( - new Words(), "words").type(FunctionType.of(Words.class)); + new Words(), "words").type(Words.class); FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(FunctionType.of(Reverse.class)); + new Reverse(), "reverse").type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(wordsRegistration); @@ -337,10 +328,10 @@ public class SimpleFunctionRegistryTests { public void testFunctionCompositionWithMessages() { FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( new UpperCaseMessage(), "uppercase") - .type(FunctionType.of(UpperCaseMessage.class)); + .type(UpperCaseMessage.class); FunctionRegistration reverseRegistration = new FunctionRegistration<>( new ReverseMessage(), "reverse") - .type(FunctionType.of(ReverseMessage.class)); + .type(ReverseMessage.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(upperCaseRegistration); @@ -359,9 +350,9 @@ public class SimpleFunctionRegistryTests { public void testFunctionCompositionMixedMessages() { FunctionRegistration upperCaseRegistration = new FunctionRegistration<>( new UpperCaseMessage(), "uppercase") - .type(FunctionType.of(UpperCaseMessage.class)); + .type(UpperCaseMessage.class); FunctionRegistration reverseRegistration = new FunctionRegistration<>( - new Reverse(), "reverse").type(FunctionType.of(Reverse.class)); + new Reverse(), "reverse").type(Reverse.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(upperCaseRegistration); @@ -379,7 +370,7 @@ public class SimpleFunctionRegistryTests { @Test public void testReactiveFunctionMessages() { FunctionRegistration registration = new FunctionRegistration<>(new ReactiveFunction(), "reactive") - .type(FunctionType.of(ReactiveFunction.class)); + .type(ReactiveFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); @@ -407,6 +398,7 @@ public class SimpleFunctionRegistryTests { assertThat(result).isEqualTo("Jim Lahey"); } + @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void lookup() { SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, @@ -416,7 +408,7 @@ public class SimpleFunctionRegistryTests { Function userFunction = uppercase(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionType.from(String.class).to(String.class)); + .type(FunctionTypeUtils.functionType(String.class, String.class)); functionRegistry.register(functionRegistration); function = functionRegistry.lookup("uppercase"); @@ -424,20 +416,21 @@ public class SimpleFunctionRegistryTests { } + @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void lookupDefaultName() { SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); Function userFunction = uppercase(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionType.from(String.class).to(String.class)); + .type(FunctionTypeUtils.functionType(String.class, String.class)); functionRegistry.register(functionRegistration); FunctionInvocationWrapper function = functionRegistry.lookup(""); assertThat(function).isNotNull(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void lookupWithCompositionFunctionAndConsumer() { SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, @@ -445,7 +438,7 @@ public class SimpleFunctionRegistryTests { Object userFunction = uppercase(); FunctionRegistration functionRegistration = new FunctionRegistration(userFunction, "uppercase") - .type(FunctionType.from(String.class).to(String.class)); + .type(FunctionTypeUtils.functionType(String.class, String.class)); functionRegistry.register(functionRegistration); userFunction = consumer(); @@ -458,6 +451,7 @@ public class SimpleFunctionRegistryTests { functionWrapper.apply("123"); } + @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void lookupWithReactiveConsumer() { SimpleFunctionRegistry functionRegistry = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, @@ -474,12 +468,11 @@ public class SimpleFunctionRegistryTests { functionWrapper.apply("123"); } - @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void testHeaderEnricherFunction() { FunctionRegistration registration = new FunctionRegistration<>(new HeaderEnricherFunction(), "headerEnricher") - .type(FunctionType.of(HeaderEnricherFunction.class)); + .type(HeaderEnricherFunction.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); @@ -493,7 +486,7 @@ public class SimpleFunctionRegistryTests { @Test public void testReactiveMonoSupplier() { FunctionRegistration registration = new FunctionRegistration<>(new ReactiveMonoGreeter(), - "greeter").type(FunctionType.of(ReactiveMonoGreeter.class)); + "greeter").type(ReactiveMonoGreeter.class); SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter, new JacksonMapper(new ObjectMapper())); catalog.register(registration); diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java index af23aa560..3b3075a73 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java @@ -39,7 +39,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.context.scan.TestFunction; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.annotation.Bean; @@ -245,17 +245,18 @@ public class ContextFunctionCatalogInitializerTests { private List list = new ArrayList<>(); + @Override public void initialize(GenericApplicationContext context) { + context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type( - FunctionType.from(Person.class).to(Person.class).getType())); + () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(Person.class, Person.class))); context.registerBean("supplier", FunctionRegistration.class, () -> new FunctionRegistration<>(supplier()) - .type(FunctionType.supplier(String.class).getType())); + .type(FunctionTypeUtils.supplierType(String.class))); context.registerBean("consumer", FunctionRegistration.class, () -> new FunctionRegistration<>(consumer()) - .type(FunctionType.consumer(String.class).getType())); + .type(FunctionTypeUtils.consumerType(String.class))); context.registerBean(SimpleConfiguration.class, () -> this); } @@ -296,8 +297,7 @@ public class ContextFunctionCatalogInitializerTests { @Override public void initialize(GenericApplicationContext context) { context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type( - FunctionType.from(String.class).to(String.class).getType())); + () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(String.class, String.class))); context.registerBean(PropertiesConfiguration.class, () -> this); } @@ -317,8 +317,7 @@ public class ContextFunctionCatalogInitializerTests { @Override public void initialize(GenericApplicationContext context) { context.registerBean("function", FunctionRegistration.class, - () -> new FunctionRegistration<>(function()).type( - FunctionType.from(String.class).to(String.class).getType())); + () -> new FunctionRegistration<>(function()).type(FunctionTypeUtils.functionType(String.class, String.class))); context.registerBean(ValueConfiguration.class, () -> this); } @@ -355,8 +354,7 @@ public class ContextFunctionCatalogInitializerTests { context.registerBean(String.class, () -> value()); context.registerBean("foos", FunctionRegistration.class, () -> new FunctionRegistration<>(foos(context.getBean(String.class))) - .type(FunctionType.from(String.class).to(Foo.class) - .getType())); + .type(FunctionTypeUtils.functionType(String.class, Foo.class))); } @Bean diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java index 34ff8b4d4..477300d38 100644 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java +++ b/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java @@ -4,7 +4,7 @@ import java.util.function.Function; import org.springframework.boot.SpringBootConfiguration; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.support.GenericApplicationContext; @@ -24,7 +24,6 @@ public class FunctionConfiguration implements ApplicationContextInitializer function = (str) -> str + str.toUpperCase(); context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(function).type( - FunctionType.from(String.class).to(String.class))); + () -> new FunctionRegistration<>(function).type(FunctionTypeUtils.functionType(String.class, String.class))); } } diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java b/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java index 730053793..3d6ed1751 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java +++ b/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java @@ -7,8 +7,8 @@ import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.FunctionalSpringApplication; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.support.GenericApplicationContext; @@ -32,7 +32,6 @@ public class LambdaApplication @Override public void initialize(GenericApplicationContext context) { context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()).type( - FunctionType.from(String.class).to(String.class))); + () -> new FunctionRegistration<>(uppercase()).type(FunctionTypeUtils.functionType(String.class, String.class))); } } diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java b/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java index d41f097c4..fd7b20ea4 100644 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java +++ b/spring-cloud-function-samples/function-sample-supplier-exporter/src/main/java/com/example/demo/DemoApplication.java @@ -4,8 +4,8 @@ import java.util.function.Function; import org.springframework.boot.SpringBootConfiguration; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.FunctionalSpringApplication; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.support.GenericApplicationContext; @@ -21,7 +21,7 @@ public class DemoApplication public void initialize(GenericApplicationContext context) { context.registerBean("foobar", FunctionRegistration.class, () -> new FunctionRegistration<>(new Foobar()) - .type(FunctionType.from(Foo.class).to(Foo.class))); + .type(FunctionTypeUtils.functionType(Foo.class, Foo.class))); } } diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java index 92f112953..9d65de859 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/source/FunctionExporterAutoConfiguration.java @@ -30,7 +30,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.web.source.FunctionExporterAutoConfiguration.SourceActiveCondition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; @@ -70,10 +70,11 @@ public class FunctionExporterAutoConfiguration { FunctionRegistration>> registration = new FunctionRegistration<>(supplier); Type rawType = ResolvableType.forClassWithGenerics(Supplier.class, this.props.getSource().getType()).getType(); // FunctionType functionType = FunctionType.supplier(this.props.getSource().getType()).wrap(Flux.class); - FunctionType type = FunctionType.of(rawType); - if (this.props.getSource().isIncludeHeaders()) { -// type = type.message(); - } +// FunctionType type = FunctionType.of(rawType); +// if (this.props.getSource().isIncludeHeaders()) { +//// type = type.message(); +// } + Type type = FunctionTypeUtils.discoverFunctionTypeFromClass(HttpSupplier.class); registration = registration.type(type); return registration; } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java index a75d0806d..de952b7f4 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java @@ -16,6 +16,8 @@ package org.springframework.cloud.function.test; +import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -29,7 +31,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.cloud.function.context.test.FunctionalSpringBootTest; import org.springframework.cloud.function.test.FunctionalExporterTests.ApplicationConfiguration; import org.springframework.cloud.function.web.source.SupplierExporter; @@ -38,6 +40,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; +import org.springframework.util.ReflectionUtils; import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -114,8 +117,16 @@ public class FunctionalExporterTests { @Override public void initialize(GenericApplicationContext context) { context.registerBean("uppercase", FunctionRegistration.class, - () -> new FunctionRegistration<>(uppercase()).type( - FunctionType.from(Person.class).to(String.class).message())); + () -> new FunctionRegistration<>(uppercase()) + .type(FunctionTypeUtils.discoverFunctionTypeFromFunctionFactoryMethod(this.getClass(), "uppercase"))); + } + + public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class clazz, String methodName) { + return discoverFunctionTypeFromFunctionFactoryMethod(ReflectionUtils.findMethod(clazz, methodName)); + } + + public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) { + return method.getGenericReturnType(); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java index cc6295ff0..3359a742b 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2019 the original author or authors. + * Copyright 2019-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.cloud.function.context.FunctionRegistration; -import org.springframework.cloud.function.context.FunctionType; import org.springframework.cloud.function.context.FunctionalSpringApplication; +import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.support.GenericApplicationContext; import org.springframework.http.HttpStatus; @@ -139,18 +139,19 @@ public class FunctionEndpointInitializerTests { @Override public void initialize(GenericApplicationContext applicationContext) { + applicationContext.registerBean("uppercase", FunctionRegistration.class, () -> new FunctionRegistration<>(uppercase()) - .type(FunctionType.from(String.class).to(String.class))); + .type(FunctionTypeUtils.functionType(String.class, String.class))); applicationContext.registerBean("reverse", FunctionRegistration.class, () -> new FunctionRegistration<>(reverse()) - .type(FunctionType.from(String.class).to(String.class))); + .type(FunctionTypeUtils.functionType(String.class, String.class))); applicationContext.registerBean("lowercase", FunctionRegistration.class, () -> new FunctionRegistration<>(lowercase()) - .type(FunctionType.from(String.class).to(String.class))); + .type(FunctionTypeUtils.functionType(String.class, String.class))); applicationContext.registerBean("supplier", FunctionRegistration.class, () -> new FunctionRegistration<>(supplier()) - .type(FunctionType.supplier(String.class))); + .type(FunctionTypeUtils.supplierType(String.class))); } }