From 8eb7e06e02d5e5118ac60a5416ada6fb73b34aba Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 31 Oct 2018 16:59:42 +0100 Subject: [PATCH] GH-227 Moved Kotlin transformation to BFPP resolves #227 --- .../cloud/function/context/FunctionType.java | 16 ++-- ...tlinLambdaToFunctionAutoConfiguration.java | 91 ++++++++++--------- ...FunctionCatalogAutoConfigurationTests.java | 2 +- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java index c3789796f..961dfec8e 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/FunctionType.java @@ -418,13 +418,15 @@ public class FunctionType { if (Supplier.class.isAssignableFrom(extractClass(type, ParamType.OUTPUT))) { Type product = extractType(type, ParamType.OUTPUT, 0); Class output = extractClass(product, ParamType.OUTPUT); - 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; + 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; diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java index f1e945460..d17417294 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java @@ -24,14 +24,17 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.cloud.function.context.FunctionType; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.MethodMetadata; import kotlin.Unit; import kotlin.jvm.functions.Function0; @@ -47,55 +50,57 @@ import kotlin.jvm.functions.Function1; */ @Configuration @ConditionalOnClass(name = "kotlin.jvm.functions.Function1") -class KotlinLambdaToFunctionAutoConfiguration implements BeanFactoryAware { +class KotlinLambdaToFunctionAutoConfiguration { protected final Log logger = LogFactory.getLog(getClass()); - private ConfigurableListableBeanFactory beanFactory; - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; - } - /** * Will transform all discovered Kotlin's Function1 and Function0 lambdas to java * Supplier, Function and Consumer, retaining the original Kotlin type - * characteristics. In other words the resulting bean coudl be cast to both java and + * characteristics. In other words the resulting bean could be cast to both java and * kotlin types (i.e., java Function vs. kotlin Function1) */ @Bean - public BeanPostProcessor kotlinPostProcessor() { - return new BeanPostProcessor() { + public BeanFactoryPostProcessor kotlinToFunctionTransformer() { + return new BeanFactoryPostProcessor() { + @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { - if (bean instanceof Function1) { - FunctionType functionType = FunctionContextUtils.findType(beanName, - beanFactory); - if (Unit.class.isAssignableFrom(functionType.getOutputType())) { - logger.debug("Transforming Kotlin lambda " + beanName - + " to java Consumer"); - @SuppressWarnings({ "rawtypes", "unchecked" }) - KotlinConsumer consumer = new KotlinConsumer((Function1) bean); - bean = consumer; - } - else { - logger.debug("Transforming Kotlin lambda " + beanName - + " to java Function"); - @SuppressWarnings({ "rawtypes", "unchecked" }) - KotlinFunction function = new KotlinFunction((Function1) bean); - bean = function; + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); + for (String beanDefinitionName : beanDefinitionNames) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName); + Object source = beanDefinition.getSource(); + if (source instanceof MethodMetadata) { + String returnTypeName = ((MethodMetadata)source).getReturnTypeName(); + if (returnTypeName.startsWith("kotlin.jvm.functions.Function")) { + FunctionType functionType = FunctionContextUtils.findType(beanDefinitionName, beanFactory); + if (returnTypeName.equals("kotlin.jvm.functions.Function1")) { + if (Unit.class.isAssignableFrom(functionType.getOutputType())) { + logger.debug("Transforming Kotlin lambda " + beanDefinitionName + " to java Consumer"); + this.register(beanDefinitionName, beanDefinition, KotlinConsumer.class, (BeanDefinitionRegistry) beanFactory); + } + else { + logger.debug("Transforming Kotlin lambda " + beanDefinitionName + " to java Function"); + this.register(beanDefinitionName, beanDefinition, KotlinFunction.class, (BeanDefinitionRegistry) beanFactory); + } + } + else { + logger.debug("Transforming Kotlin lambda " + beanDefinitionName + " to java Supplier"); + this.register(beanDefinitionName, beanDefinition, KotlinSupplier.class, (BeanDefinitionRegistry) beanFactory); + } + } } } - else if (bean instanceof Function0) { - logger.debug("Transforming Kotlin lambda " + beanName - + " to java Supplier"); - @SuppressWarnings({ "rawtypes", "unchecked" }) - KotlinSupplier supplier = new KotlinSupplier((Function0) bean); - bean = supplier; - } - return bean; + } + + private void register(String originalName, BeanDefinition originalDefinition, Class clazz, BeanDefinitionRegistry registry) { + RootBeanDefinition cbd = new RootBeanDefinition(clazz); + ConstructorArgumentValues ca = new ConstructorArgumentValues(); + ca.addGenericArgumentValue(originalDefinition); + cbd.setConstructorArgumentValues(ca); + registry.removeBeanDefinition(originalName); + registry.registerBeanDefinition(originalName, cbd); } }; } @@ -108,7 +113,7 @@ class KotlinLambdaToFunctionAutoConfiguration implements BeanFactoryAware { private final Function1 kotlinLambda; - KotlinFunction(Function1 kotlinLambda) { + private KotlinFunction(Function1 kotlinLambda) { this.kotlinLambda = kotlinLambda; } @@ -131,7 +136,7 @@ class KotlinLambdaToFunctionAutoConfiguration implements BeanFactoryAware { private final Function1 kotlinLambda; - KotlinConsumer(Function1 kotlinLambda) { + private KotlinConsumer(Function1 kotlinLambda) { this.kotlinLambda = kotlinLambda; } @@ -154,7 +159,7 @@ class KotlinLambdaToFunctionAutoConfiguration implements BeanFactoryAware { private final Function0 kotlinLambda; - KotlinSupplier(Function0 kotlinLambda) { + private KotlinSupplier(Function0 kotlinLambda) { this.kotlinLambda = kotlinLambda; } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java index f8545c386..14ee39072 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java @@ -397,7 +397,7 @@ public class ContextFunctionCatalogAutoConfigurationTests { assertThat(context.getBean("kotlinSupplier")).isInstanceOf(Supplier.class); assertThat(context.getBean("kotlinSupplier")).isInstanceOf(Function0.class); Supplier> supplier = catalog.lookup(Supplier.class, "kotlinSupplier"); - supplier.get().subscribe(System.out::println); + assertThat(supplier.get().blockFirst()).isEqualTo("Hello"); assertThat((Supplier)catalog.lookup(Supplier.class, "kotlinSupplier")) .isInstanceOf(Supplier.class); assertThat(inspector.getOutputType(catalog.lookup(Supplier.class, "kotlinSupplier")))