org.springframework.cloud.stream.config.BindingServiceConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.stream.config.BindingServiceConfiguration.java

Source

/*
 * Copyright 2015-2017 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
 *
 *      http://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.stream.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.stream.binder.BinderFactory;
import org.springframework.cloud.stream.binding.AbstractBindingTargetFactory;
import org.springframework.cloud.stream.binding.BinderAwareChannelResolver;
import org.springframework.cloud.stream.binding.BinderAwareRouterBeanPostProcessor;
import org.springframework.cloud.stream.binding.BindingService;
import org.springframework.cloud.stream.binding.CompositeMessageChannelConfigurer;
import org.springframework.cloud.stream.binding.ContextStartAfterRefreshListener;
import org.springframework.cloud.stream.binding.DynamicDestinationsBindable;
import org.springframework.cloud.stream.binding.InputBindingLifecycle;
import org.springframework.cloud.stream.binding.MessageChannelConfigurer;
import org.springframework.cloud.stream.binding.MessageConverterConfigurer;
import org.springframework.cloud.stream.binding.OutputBindingLifecycle;
import org.springframework.cloud.stream.binding.SingleBindingTargetBindable;
import org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor;
import org.springframework.cloud.stream.binding.SubscribableChannelBindingTargetFactory;
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.expression.PropertyAccessor;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.json.JsonPropertyAccessor;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.core.DestinationResolutionException;
import org.springframework.messaging.core.DestinationResolver;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.tuple.spel.TuplePropertyAccessor;
import org.springframework.util.CollectionUtils;

/**
 * Configuration class that provides necessary beans for {@link MessageChannel} binding.
 *
 * @author Dave Syer
 * @author David Turanski
 * @author Marius Bogoevici
 * @author Ilayaperumal Gopinathan
 * @author Gary Russell
 */
@Configuration
@EnableConfigurationProperties({ BindingServiceProperties.class, SpringIntegrationProperties.class })
public class BindingServiceConfiguration {

    public static final String STREAM_LISTENER_ANNOTATION_BEAN_POST_PROCESSOR_NAME = "streamListenerAnnotationBeanPostProcessor";

    private static final String ERROR_CHANNEL_NAME = "error";

    @Autowired(required = false)
    private ObjectMapper objectMapper;

    /**
     * User defined custom message converters
     */
    @Autowired(required = false)
    private List<MessageConverter> customMessageConverters;

    @Bean
    public static MessageHandlerMethodFactory messageHandlerMethodFactory(
            CompositeMessageConverterFactory compositeMessageConverterFactory) {
        DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
        messageHandlerMethodFactory
                .setMessageConverter(compositeMessageConverterFactory.getMessageConverterForAllRegistered());
        return messageHandlerMethodFactory;
    }

    @Bean(name = STREAM_LISTENER_ANNOTATION_BEAN_POST_PROCESSOR_NAME)
    public static StreamListenerAnnotationBeanPostProcessor streamListenerAnnotationBeanPostProcessor() {
        return new StreamListenerAnnotationBeanPostProcessor();
    }

    @Bean
    // This conditional is intentionally not in an autoconfig (usually a bad idea) because
    // it is used to detect a BindingService in the parent context (which we know
    // already exists).
    @ConditionalOnMissingBean(BindingService.class)
    public BindingService bindingService(BindingServiceProperties bindingServiceProperties,
            BinderFactory binderFactory) {
        return new BindingService(bindingServiceProperties, binderFactory);
    }

    @Bean
    public MessageConverterConfigurer messageConverterConfigurer(BindingServiceProperties bindingServiceProperties,
            CompositeMessageConverterFactory compositeMessageConverterFactory) {
        return new MessageConverterConfigurer(bindingServiceProperties, compositeMessageConverterFactory);
    }

    @Bean
    public SubscribableChannelBindingTargetFactory channelFactory(
            CompositeMessageChannelConfigurer compositeMessageChannelConfigurer) {
        return new SubscribableChannelBindingTargetFactory(compositeMessageChannelConfigurer);
    }

    @Bean
    public CompositeMessageChannelConfigurer compositeMessageChannelConfigurer(
            MessageConverterConfigurer messageConverterConfigurer) {
        List<MessageChannelConfigurer> configurerList = new ArrayList<>();
        configurerList.add(messageConverterConfigurer);
        return new CompositeMessageChannelConfigurer(configurerList);
    }

    @Bean
    @DependsOn("bindingService")
    public OutputBindingLifecycle outputBindingLifecycle() {
        return new OutputBindingLifecycle();
    }

    @Bean
    @DependsOn("bindingService")
    public InputBindingLifecycle inputBindingLifecycle() {
        return new InputBindingLifecycle();
    }

    @Bean
    @DependsOn("bindingService")
    public ContextStartAfterRefreshListener contextStartAfterRefreshListener() {
        return new ContextStartAfterRefreshListener();
    }

    @Bean
    public BinderAwareChannelResolver binderAwareChannelResolver(BindingService bindingService,
            AbstractBindingTargetFactory<? extends MessageChannel> bindingTargetFactory,
            DynamicDestinationsBindable dynamicDestinationsBindable) {
        return new BinderAwareChannelResolver(bindingService, bindingTargetFactory, dynamicDestinationsBindable);
    }

    @Bean
    @ConditionalOnProperty("spring.cloud.stream.bindings." + ERROR_CHANNEL_NAME + ".destination")
    public SingleBindingTargetBindable<MessageChannel> errorChannelBindable(
            @Qualifier(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) PublishSubscribeChannel errorChannel) {
        return new SingleBindingTargetBindable<MessageChannel>(ERROR_CHANNEL_NAME, errorChannel);
    }

    @Bean
    public DynamicDestinationsBindable dynamicDestinationsBindable() {
        return new DynamicDestinationsBindable();
    }

    @Bean
    public CompositeMessageConverterFactory compositeMessageConverterFactory() {
        List<MessageConverter> messageConverters = new ArrayList<>();
        if (!CollectionUtils.isEmpty(this.customMessageConverters)) {
            messageConverters.addAll(Collections.unmodifiableCollection(this.customMessageConverters));
        }
        return new CompositeMessageConverterFactory(messageConverters, this.objectMapper);
    }

    @Bean
    // provided for backwards compatibility scenarios
    public ChannelBindingServiceProperties channelBindingServiceProperties(
            BindingServiceProperties bindingServiceProperties) {
        return new ChannelBindingServiceProperties(bindingServiceProperties);
    }

    // IMPORTANT: Nested class to avoid instantiating all of the above early
    @Configuration
    protected static class PostProcessorConfiguration {

        private BinderAwareChannelResolver binderAwareChannelResolver;

        /**
         * Adds property accessors for use in SpEL expression evaluation
         */
        @Bean
        public static BeanPostProcessor propertyAccessorBeanPostProcessor() {
            final Map<String, PropertyAccessor> accessors = new HashMap<>();
            accessors.put("tuplePropertyAccessor", new TuplePropertyAccessor());
            accessors.put("jsonPropertyAccessor", new JsonPropertyAccessor());
            return new BeanPostProcessor() {

                @Override
                public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                    if (IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME.equals(beanName)) {
                        IntegrationEvaluationContextFactoryBean factoryBean = (IntegrationEvaluationContextFactoryBean) bean;
                        Map<String, PropertyAccessor> factoryBeanAccessors = factoryBean.getPropertyAccessors();
                        for (Map.Entry<String, PropertyAccessor> entry : accessors.entrySet()) {
                            if (!factoryBeanAccessors.containsKey(entry.getKey())) {
                                factoryBeanAccessors.put(entry.getKey(), entry.getValue());
                            }
                        }
                        factoryBean.setPropertyAccessors(factoryBeanAccessors);
                    }
                    return bean;
                }

                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    return bean;
                }
            };
        }

        @Bean
        @ConditionalOnMissingBean(BinderAwareRouterBeanPostProcessor.class)
        public BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(
                final ConfigurableListableBeanFactory beanFactory) {
            // IMPORTANT: Lazy delegate to avoid instantiating all of the above early
            return new BinderAwareRouterBeanPostProcessor(new DestinationResolver<MessageChannel>() {

                @Override
                public MessageChannel resolveDestination(String name) throws DestinationResolutionException {
                    if (PostProcessorConfiguration.this.binderAwareChannelResolver == null) {
                        PostProcessorConfiguration.this.binderAwareChannelResolver = BeanFactoryUtils
                                .beanOfType(beanFactory, BinderAwareChannelResolver.class);
                    }
                    return PostProcessorConfiguration.this.binderAwareChannelResolver.resolveDestination(name);
                }

            });
        }

        @Bean
        public static BeanPostProcessor messageHandlerHeaderPropagationBeanPostProcessor(
                final SpringIntegrationProperties springIntegrationProperties) {
            return new BeanPostProcessor() {

                @Override
                public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                    // TODO: Filter out beans created by SCSt (not currently necessary)
                    Class<?> beanClass = AopUtils.isAopProxy(bean) ? AopUtils.getTargetClass(bean)
                            : bean.getClass();
                    if (AbstractReplyProducingMessageHandler.class.isAssignableFrom(beanClass)) {
                        AbstractReplyProducingMessageHandler messageHandler = (AbstractReplyProducingMessageHandler) bean;
                        messageHandler.addNotPropagatedHeaders(
                                springIntegrationProperties.getMessageHandlerNotPropagatedHeaders());
                    }
                    return bean;
                }

                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    return bean;
                }

            };
        }

    }

}