Remove FunctionType and dependencies on it
This commit is contained in:
@@ -141,7 +141,7 @@ public abstract class AbstractSpringFunctionAdapterInitializer<C> 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<C> 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);
|
||||
|
||||
@@ -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 <T> target type
|
||||
* @author Dave Syer
|
||||
@@ -63,7 +57,7 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
||||
|
||||
private T target;
|
||||
|
||||
private FunctionType type;
|
||||
private Type type;
|
||||
|
||||
/**
|
||||
* Creates instance of FunctionRegistration.
|
||||
@@ -97,7 +91,7 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
||||
this.names.addAll(names);
|
||||
}
|
||||
|
||||
public FunctionType getType() {
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@@ -111,25 +105,33 @@ public class FunctionRegistration<T> implements BeanNameAware {
|
||||
}
|
||||
|
||||
public FunctionRegistration<T> type(Type type) {
|
||||
return type(FunctionType.of(type));
|
||||
}
|
||||
|
||||
public FunctionRegistration<T> 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<T> implements BeanNameAware {
|
||||
*
|
||||
*/
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public <S> FunctionRegistration<S> wrap() {
|
||||
this.isFunctionSignatureSupported();
|
||||
FunctionRegistration<S> result;
|
||||
if (this.type == null) {
|
||||
result = (FunctionRegistration<S>) this;
|
||||
}
|
||||
else if (this.target instanceof RoutingFunction) {
|
||||
S target = (S) this.target;
|
||||
result = new FunctionRegistration<S>(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<S>(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 <S> FunctionRegistration<S> wrap() {
|
||||
// this.isFunctionSignatureSupported();
|
||||
// FunctionRegistration<S> result;
|
||||
// if (this.type == null) {
|
||||
// result = (FunctionRegistration<S>) this;
|
||||
// }
|
||||
// else if (this.target instanceof RoutingFunction) {
|
||||
// S target = (S) this.target;
|
||||
// result = new FunctionRegistration<S>(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<S>(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<T> implements BeanNameAware {
|
||||
}
|
||||
}
|
||||
|
||||
private void isFunctionSignatureSupported() {
|
||||
if (type != null) {
|
||||
Assert.isTrue(!(Mono.class.isAssignableFrom(this.type.getOutputWrapper())
|
||||
&& Mono.class.isAssignableFrom(this.type.getInputWrapper())),
|
||||
"Function<Mono, Mono> is not supported.");
|
||||
}
|
||||
}
|
||||
// private void isFunctionSignatureSupported() {
|
||||
// if (type != null) {
|
||||
// Assert.isTrue(!(Mono.class.isAssignableFrom(this.type.getOutputWrapper())
|
||||
// && Mono.class.isAssignableFrom(this.type.getInputWrapper())),
|
||||
// "Function<Mono, Mono> is not supported.");
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -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<WrapperDetector> 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<String>
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Person>, Message<String>> 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<Person>, Message<String>> 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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<FunctionRegistration<Function<Integer, String>>> {
|
||||
|
||||
@Override
|
||||
public FunctionRegistration<Function<Integer, String>> get() {
|
||||
return new FunctionRegistration<Function<Integer, String>>(
|
||||
new IntegerToString(), "ints");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SupplierOfIntegerToString
|
||||
implements Supplier<Function<Integer, String>> {
|
||||
|
||||
@Override
|
||||
public Function<Integer, String> get() {
|
||||
return new IntegerToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class IntegerToString implements Function<Integer, String> {
|
||||
|
||||
@Override
|
||||
public String apply(Integer t) {
|
||||
return "" + t;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class StringToMap implements Function<String, Map<String, Integer>> {
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> apply(String t) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FooToFoo implements Function<Foo, Bar> {
|
||||
|
||||
@Override
|
||||
public Bar apply(Foo t) {
|
||||
return new Bar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FluxToFlux implements Function<Flux<Foo>, Flux<Bar>> {
|
||||
|
||||
@Override
|
||||
public Flux<Bar> apply(Flux<Foo> t) {
|
||||
return t.map(f -> new Bar());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FluxMessageToFluxMessage
|
||||
implements Function<Flux<Message<Foo>>, Flux<Message<Bar>>> {
|
||||
|
||||
@Override
|
||||
public Flux<Message<Bar>> apply(Flux<Message<Foo>> 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<Tuple2<Flux<String>, Flux<Integer>>, Tuple3<Flux<String>, Flux<Integer>, Flux<Object>>> {
|
||||
|
||||
@Override
|
||||
public Tuple3<Flux<String>, Flux<Integer>, Flux<Object>> apply(Tuple2<Flux<String>, Flux<Integer>> t) {
|
||||
return 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<Object> 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<Object>(null) {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sourceAsMainClassProperty() {
|
||||
try {
|
||||
System.setProperty("MAIN_CLASS", FluxFunctionConfig.class.getName());
|
||||
this.initializer = new AbstractSpringFunctionAdapterInitializer<Object>() {
|
||||
|
||||
};
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("MAIN_CLASS");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void functionBean() {
|
||||
this.initializer = new AbstractSpringFunctionAdapterInitializer<Object>(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<Object>(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<Object>(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<Object>(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<Object>(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<Object>(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<Object>(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<Foo>, Flux<Bar>> function() {
|
||||
return flux -> flux.map(foo -> new Bar());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class FluxFunctionApp implements Function<Flux<Foo>, Flux<Bar>> {
|
||||
|
||||
@Override
|
||||
public Flux<Bar> apply(Flux<Foo> flux) {
|
||||
return flux.map(foo -> new Bar());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class FunctionRegistrar
|
||||
implements ApplicationContextInitializer<GenericApplicationContext> {
|
||||
|
||||
public Function<Flux<Foo>, Flux<Bar>> function() {
|
||||
return flux -> flux.map(foo -> new Bar());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(GenericApplicationContext context) {
|
||||
context.registerBean("function", FunctionRegistration.class,
|
||||
() -> new FunctionRegistration<Function<Flux<Foo>, Flux<Bar>>>(
|
||||
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<Foo, Bar> function() {
|
||||
return foo -> new Bar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
||||
protected static class NamedFunctionConfig {
|
||||
|
||||
@Bean
|
||||
public Function<Foo, Bar> other() {
|
||||
return foo -> new Bar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
||||
protected static class SupplierConfig {
|
||||
@Bean
|
||||
public Supplier<Bar> supplier() {
|
||||
return () -> {
|
||||
return new Bar();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ContextFunctionCatalogAutoConfiguration.class)
|
||||
protected static class ConsumerConfig {
|
||||
|
||||
@Bean
|
||||
public Consumer<Foo> consumer() {
|
||||
return foo -> {
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class Foo {
|
||||
|
||||
}
|
||||
|
||||
protected static class Bar {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Echo> 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<Echo> 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<Echo> 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<UpperCase> reg1 = new FunctionRegistration<>(
|
||||
new UpperCase(), "uppercase").type(FunctionType.of(UpperCase.class));
|
||||
new UpperCase(), "uppercase").type(UpperCase.class);
|
||||
catalog.register(reg1);
|
||||
//
|
||||
FunctionRegistration<UpperCaseMessage> reg2 = new FunctionRegistration<>(
|
||||
new UpperCaseMessage(), "uppercaseMessage").type(FunctionType.of(UpperCaseMessage.class));
|
||||
new UpperCaseMessage(), "uppercaseMessage").type(UpperCaseMessage.class);
|
||||
catalog.register(reg2);
|
||||
//
|
||||
FunctionRegistration<StringArrayFunction> reg3 = new FunctionRegistration<>(
|
||||
new StringArrayFunction(), "stringArray").type(FunctionType.of(StringArrayFunction.class));
|
||||
new StringArrayFunction(), "stringArray").type(StringArrayFunction.class);
|
||||
catalog.register(reg3);
|
||||
//
|
||||
FunctionRegistration<TypelessFunction> reg4 = new FunctionRegistration<>(
|
||||
new TypelessFunction(), "typeless").type(FunctionType.of(TypelessFunction.class));
|
||||
new TypelessFunction(), "typeless").type(TypelessFunction.class);
|
||||
catalog.register(reg4);
|
||||
//
|
||||
FunctionRegistration<ByteArrayFunction> reg5 = new FunctionRegistration<>(
|
||||
new ByteArrayFunction(), "typeless").type(FunctionType.of(ByteArrayFunction.class));
|
||||
new ByteArrayFunction(), "typeless").type(ByteArrayFunction.class);
|
||||
catalog.register(reg5);
|
||||
//
|
||||
FunctionRegistration<StringListFunction> reg6 = new FunctionRegistration<>(
|
||||
new StringListFunction(), "stringList").type(FunctionType.of(StringListFunction.class));
|
||||
new StringListFunction(), "stringList").type(StringListFunction.class);
|
||||
catalog.register(reg6);
|
||||
|
||||
Message<String> collectionMessage = MessageBuilder.withPayload("[\"ricky\", \"julien\", \"bubbles\"]").build();
|
||||
@@ -218,7 +217,7 @@ public class SimpleFunctionRegistryTests {
|
||||
|
||||
UpperCase function = new UpperCase();
|
||||
FunctionRegistration<UpperCase> 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<TestFunction> 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<TestFunction> 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<UpperCase> upperCaseRegistration = new FunctionRegistration<>(
|
||||
new UpperCase(), "uppercase").type(FunctionType.of(UpperCase.class));
|
||||
new UpperCase(), "uppercase").type(UpperCase.class);
|
||||
FunctionRegistration<Reverse> 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<String>, Flux<String>> 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<Words> wordsRegistration = new FunctionRegistration<>(
|
||||
new Words(), "words").type(FunctionType.of(Words.class));
|
||||
new Words(), "words").type(Words.class);
|
||||
FunctionRegistration<Reverse> 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<Words> wordsRegistration = new FunctionRegistration<>(
|
||||
new Words(), "words").type(FunctionType.of(Words.class));
|
||||
new Words(), "words").type(Words.class);
|
||||
FunctionRegistration<Reverse> 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<Words> wordsRegistration = new FunctionRegistration<>(
|
||||
new Words(), "words").type(FunctionType.of(Words.class));
|
||||
new Words(), "words").type(Words.class);
|
||||
FunctionRegistration<Reverse> 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<UpperCaseMessage> upperCaseRegistration = new FunctionRegistration<>(
|
||||
new UpperCaseMessage(), "uppercase")
|
||||
.type(FunctionType.of(UpperCaseMessage.class));
|
||||
.type(UpperCaseMessage.class);
|
||||
FunctionRegistration<ReverseMessage> 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<UpperCaseMessage> upperCaseRegistration = new FunctionRegistration<>(
|
||||
new UpperCaseMessage(), "uppercase")
|
||||
.type(FunctionType.of(UpperCaseMessage.class));
|
||||
.type(UpperCaseMessage.class);
|
||||
FunctionRegistration<Reverse> 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<ReactiveFunction> 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<HeaderEnricherFunction> 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<ReactiveMonoGreeter> 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);
|
||||
|
||||
@@ -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<String> 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
|
||||
|
||||
Reference in New Issue
Block a user