org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.function.context.config.KotlinLambdaToFunctionAutoConfiguration.java

Source

/*
 * 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.config;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeansException;
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;

/**
 * Configuration class which defines the required infrastructure to bootstrap Kotlin
 * lambdas as invocable functions within the context of the framework.
 *
 * @author Oleg Zhurakousky
 * @since 2.0
 */
@Configuration
@ConditionalOnClass(name = "kotlin.jvm.functions.Function1")
class KotlinLambdaToFunctionAutoConfiguration {

    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * 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 could be cast to both java and
     * kotlin types (i.e., java Function<I,O> vs. kotlin Function1<I,O>)
     * @return the bean factory post processor
     */
    @Bean
    public BeanFactoryPostProcessor kotlinToFunctionTransformer() {
        return new BeanFactoryPostProcessor() {

            @Override
            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 = new FunctionType(
                                    FunctionContextUtils.findType(beanDefinitionName, beanFactory));
                            if (returnTypeName.equals("kotlin.jvm.functions.Function1")) {
                                if (Unit.class.isAssignableFrom(functionType.getOutputType())) {
                                    KotlinLambdaToFunctionAutoConfiguration.this.logger
                                            .debug("Transforming Kotlin lambda " + beanDefinitionName
                                                    + " to java Consumer");
                                    this.register(beanDefinitionName, beanDefinition, KotlinConsumer.class,
                                            (BeanDefinitionRegistry) beanFactory);
                                } else {
                                    KotlinLambdaToFunctionAutoConfiguration.this.logger
                                            .debug("Transforming Kotlin lambda " + beanDefinitionName
                                                    + " to java Function");
                                    this.register(beanDefinitionName, beanDefinition, KotlinFunction.class,
                                            (BeanDefinitionRegistry) beanFactory);
                                }
                            } else {
                                KotlinLambdaToFunctionAutoConfiguration.this.logger.debug(
                                        "Transforming Kotlin lambda " + beanDefinitionName + " to java Supplier");
                                this.register(beanDefinitionName, beanDefinition, KotlinSupplier.class,
                                        (BeanDefinitionRegistry) beanFactory);
                            }
                        }
                    }
                }
            }

            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);
            }
        };
    }

    /**
     * Wrapper for Kotlin lambda to be represented as both Java Function&lt;I,O&gt; as
     * well as Kotlin's Function1&lt;I,O&gt;.
     */
    private static final class KotlinFunction<I, O> implements Function<I, O>, Function1<I, O> {

        private final Function1<I, O> kotlinLambda;

        private KotlinFunction(Function1<I, O> kotlinLambda) {
            this.kotlinLambda = kotlinLambda;
        }

        @Override
        public O apply(I i) {
            return this.kotlinLambda.invoke(i);
        }

        @Override
        public O invoke(I i) {
            return this.apply(i);
        }

    }

    /**
     * Wrapper for Kotlin lambda to be represented as both Java Consumer&lt;I&gt; as well
     * as Kotlin's Function1&lt;I,Unit&gt;.
     */
    private static final class KotlinConsumer<I, U> implements Consumer<I>, Function1<I, U> {

        private final Function1<I, U> kotlinLambda;

        private KotlinConsumer(Function1<I, U> kotlinLambda) {
            this.kotlinLambda = kotlinLambda;
        }

        @Override
        public U invoke(I i) {
            return this.kotlinLambda.invoke(i);
        }

        @Override
        public void accept(I i) {
            this.kotlinLambda.invoke(i);
        }

    }

    /**
     * Wrapper for Kotlin lambda to be represented as both Java Supplier&lt;O&gt; as well
     * as Kotlin's Function0&lt;O&gt;.
     */
    private static final class KotlinSupplier<O> implements Supplier<O>, Function0<O> {

        private final Function0<O> kotlinLambda;

        private KotlinSupplier(Function0<O> kotlinLambda) {
            this.kotlinLambda = kotlinLambda;
        }

        @Override
        public O get() {
            return this.invoke();
        }

        @Override
        public O invoke() {
            return this.kotlinLambda.invoke();
        }

    }

}