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 8be661d2f..93081840c 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 @@ -31,6 +31,8 @@ import reactor.core.publisher.Flux; import org.springframework.beans.factory.BeanNameAware; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; +import org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration; +import org.springframework.core.KotlinDetector; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -105,11 +107,14 @@ public class FunctionRegistration implements BeanNameAware { } public FunctionRegistration type(Type type) { + this.type = type; + if (KotlinDetector.isKotlinPresent() && this.target instanceof KotlinLambdaToFunctionAutoConfiguration.KotlinFunctionWrapper) { + return this; + } Type discoveredFunctionType = FunctionTypeUtils.discoverFunctionTypeFromClass(this.target.getClass()); if (discoveredFunctionType == null) { // only valid for Kafka Stream KStream[] return type. return null; } - this.type = type; Class inputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getInputType(discoveredFunctionType)); Class outputType = FunctionTypeUtils.getRawType(FunctionTypeUtils.getOutputType(discoveredFunctionType)); @@ -169,44 +174,10 @@ 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; -// } - @Override public void setBeanName(String name) { if (CollectionUtils.isEmpty(this.names)) { this.name(name); } } - -// 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/catalog/FunctionTypeUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java index cd4724185..24e181e12 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 @@ -32,6 +32,8 @@ import java.util.function.Supplier; import java.util.stream.Stream; import com.fasterxml.jackson.databind.JsonNode; +import kotlin.jvm.functions.Function0; +import kotlin.jvm.functions.Function1; import net.jodah.typetools.TypeResolver; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,6 +48,7 @@ import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry import org.springframework.cloud.function.context.config.FunctionContextUtils; import org.springframework.cloud.function.context.config.RoutingFunction; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.KotlinDetector; import org.springframework.core.ResolvableType; import org.springframework.messaging.Message; import org.springframework.util.Assert; @@ -187,6 +190,14 @@ public final class FunctionTypeUtils { @SuppressWarnings("unchecked") public static Type discoverFunctionTypeFromClass(Class functionalClass) { + if (KotlinDetector.isKotlinPresent()) { + if (Function1.class.isAssignableFrom(functionalClass)) { + return TypeResolver.reify(Function1.class, (Class>) functionalClass); + } + else if (Function0.class.isAssignableFrom(functionalClass)) { + return TypeResolver.reify(Function0.class, (Class>) functionalClass); + } + } if (Function.class.isAssignableFrom(functionalClass)) { for (Type superInterface : functionalClass.getGenericInterfaces()) { if (superInterface != null && !superInterface.equals(Object.class)) { @@ -203,7 +214,7 @@ public final class FunctionTypeUtils { else if (Supplier.class.isAssignableFrom(functionalClass)) { return TypeResolver.reify(Supplier.class, (Class>) functionalClass); } - return null; + return TypeResolver.reify(functionalClass); } /** diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java index ee37442d5..5954bf4c8 100644 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java +++ b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java @@ -56,7 +56,8 @@ public class ContextFunctionCatalogAutoConfigurationKotlinTests { public void typeDiscoveryTests() { create(new Class[] { KotlinLambdasConfiguration.class, SimpleConfiguration.class, - KotlinComponentFunction.class}); + KotlinComponentFunction.class, + ComponentUppercase.class}); FunctionCatalog functionCatalog = this.context.getBean(FunctionCatalog.class); @@ -89,12 +90,19 @@ public class ContextFunctionCatalogAutoConfigurationKotlinTests { assertThat(kotlinListPojoFunction.getInputType().getTypeName()).isEqualTo("java.util.List"); assertThat(kotlinListPojoFunction.getOutputType()).isEqualTo(String.class); -// function = this.context.getBean("kotlinListPojoFunction"); -// functionType = (ParameterizedType) FunctionTypeUtils.discoverFunctionType(function, "kotlinListPojoFunction", this.context); -// assertThat(functionType.getRawType().getTypeName()).isEqualTo(Function.class.getName()); -// assertThat(functionType.getActualTypeArguments().length).isEqualTo(2); -// assertThat(functionType.getActualTypeArguments()[0].getTypeName()).isEqualTo("java.util.List"); -// assertThat(functionType.getActualTypeArguments()[1].getTypeName()).isEqualTo(String.class.getName()); + FunctionInvocationWrapper componentUppercase = functionCatalog.lookup("componentUppercase"); + assertThat(componentUppercase.isFunction()).isTrue(); + assertThat(componentUppercase.getInputType()).isEqualTo(String.class); + assertThat(componentUppercase.getOutputType()).isEqualTo(String.class); + + assertThat(componentUppercase.apply("hello")).isEqualTo("HELLO"); + + FunctionInvocationWrapper uppercaseBean = functionCatalog.lookup("uppercase"); + assertThat(uppercaseBean.isFunction()).isTrue(); + assertThat(uppercaseBean.getInputType()).isEqualTo(String.class); + assertThat(uppercaseBean.getOutputType()).isEqualTo(String.class); + + assertThat(uppercaseBean.apply("hello")).isEqualTo("HELLO"); } @Test diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/ComponentUppercase.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/ComponentUppercase.kt new file mode 100644 index 000000000..574b6aa48 --- /dev/null +++ b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/ComponentUppercase.kt @@ -0,0 +1,10 @@ +package org.springframework.cloud.function.kotlin + +import org.springframework.stereotype.Component + +@Component +class ComponentUppercase : (String) -> String { + override fun invoke(p1: String): String { + return p1.uppercase() + } +} diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt index 9e85d664b..ef4583d39 100644 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt +++ b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/KotlinLambdasConfiguration.kt @@ -29,6 +29,9 @@ import java.util.List @EnableAutoConfiguration @Configuration class KotlinLambdasConfiguration { + + @Bean + fun uppercase(): Function = KotlinComponentFunction() @Bean fun kotlinFunction(): (String) -> String { return { it.toUpperCase() }