org.springframework.integration.config.IntegrationRegistrar.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.config.IntegrationRegistrar.java

Source

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

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor;
import org.springframework.integration.channel.DefaultHeaderChannelRegistry;
import org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter;
import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.util.ClassUtils;

/**
 * {@link ImportBeanDefinitionRegistrar} implementation that configures integration infrastructure.
 *
 * @author Artem Bilan
 * @author Gary Russell
 * @since 4.0
 */
public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {

    private static final Log logger = LogFactory.getLog(IntegrationRegistrar.class);

    private final static IntegrationConverterInitializer INTEGRATION_CONVERTER_INITIALIZER = new IntegrationConverterInitializer();

    private static final Set<Integer> registriesProcessed = new HashSet<Integer>();

    private ClassLoader classLoader;

    private volatile boolean jackson2Present;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        this.jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
                && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
    }

    /**
     * Invoked by the framework when an &#64;EnableIntegration annotation is encountered.
     * Also called with {@code null} {@code importingClassMetadata} from {@code AbstractIntegrationNamespaceHandler}
     * to register the same beans when using XML configuration. Also called by {@code AnnotationConfigParser}
     * to register the messaging annotation post processors (for {@code <int:annotation-config/>}).
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
            BeanDefinitionRegistry registry) {
        registerImplicitChannelCreator(registry);
        registerIntegrationConfigurationBeanFactoryPostProcessor(registry);
        registerIntegrationEvaluationContext(registry);
        registerIntegrationProperties(registry);
        registerHeaderChannelRegistry(registry);
        registerGlobalChannelInterceptorProcessor(registry);
        registerBuiltInBeans(registry);
        registerDefaultConfiguringBeanFactoryPostProcessor(registry);
        registerDefaultDatatypeChannelMessageConverter(registry);
        registerArgumentResolverMessageConverter(registry);
        if (importingClassMetadata != null) {
            registerMessagingAnnotationPostProcessors(importingClassMetadata, registry);
        }
        registerMessageBuilderFactory(registry);
        IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry);
    }

    /**
     * This method will auto-register a ChannelInitializer which could also be overridden by the user
     * by simply registering a ChannelInitializer {@code <bean>} with its {@code autoCreate} property
     * set to false to suppress channel creation.
     * It will also register a ChannelInitializer$AutoCreateCandidatesCollector
     * which simply collects candidate channel names.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerImplicitChannelCreator(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME)) {
            String channelsAutoCreateExpression = IntegrationProperties
                    .getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE);
            BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class)
                    .addPropertyValue("autoCreate", channelsAutoCreateExpression);
            BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(),
                    IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME);
            BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, registry);
        }

        if (!registry.containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {
            BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(ChannelInitializer.AutoCreateCandidatesCollector.class);
            channelRegistryBuilder.addConstructorArgValue(new ManagedSet<String>());
            channelRegistryBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //SPR-12761
            BeanDefinitionHolder channelRegistryHolder = new BeanDefinitionHolder(
                    channelRegistryBuilder.getBeanDefinition(),
                    IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
            BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, registry);
        }
    }

    /**
     * Register {@code integrationGlobalProperties} bean if necessary.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerIntegrationProperties(BeanDefinitionRegistry registry) {
        boolean alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry)
                    .containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME);
        } else {
            alreadyRegistered = registry
                    .isBeanNameInUse(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME);
        }
        if (!alreadyRegistered) {
            ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(this.classLoader);
            try {
                Resource[] defaultResources = resourceResolver
                        .getResources("classpath*:META-INF/spring.integration.default.properties");
                Resource[] userResources = resourceResolver
                        .getResources("classpath*:META-INF/spring.integration.properties");

                List<Resource> resources = new LinkedList<Resource>(Arrays.asList(defaultResources));
                resources.addAll(Arrays.asList(userResources));

                BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder
                        .genericBeanDefinition(PropertiesFactoryBean.class)
                        .addPropertyValue("locations", resources);

                registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME,
                        integrationPropertiesBuilder.getBeanDefinition());
            } catch (IOException e) {
                logger.warn("Cannot load 'spring.integration.properties' Resources.", e);
            }
        }
    }

    /**
     * Register {@link IntegrationEvaluationContextFactoryBean} bean, if necessary.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerIntegrationEvaluationContext(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) {
            BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class);
            integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

            BeanDefinitionHolder integrationEvaluationContextHolder = new BeanDefinitionHolder(
                    integrationEvaluationContextBuilder.getBeanDefinition(),
                    IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);

            BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, registry);
        }
    }

    /**
     * Register {@code jsonPath} and {@code xpath} SpEL-function beans, if necessary.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerBuiltInBeans(BeanDefinitionRegistry registry) {
        int registryId = System.identityHashCode(registry);

        String jsonPathBeanName = "jsonPath";
        boolean alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry).containsBean(jsonPathBeanName);
        } else {
            alreadyRegistered = registry.isBeanNameInUse(jsonPathBeanName);
        }
        if (!alreadyRegistered && !registriesProcessed.contains(registryId)) {
            Class<?> jsonPathClass = null;
            try {
                jsonPathClass = ClassUtils.forName("com.jayway.jsonpath.JsonPath", this.classLoader);
            } catch (ClassNotFoundException e) {
                logger.debug("The '#jsonPath' SpEL function cannot be registered: "
                        + "there is no jayway json-path.jar on the classpath.");
            }

            if (jsonPathClass != null) {
                try {
                    ClassUtils.forName("com.jayway.jsonpath.Predicate", this.classLoader);
                } catch (ClassNotFoundException e) {
                    jsonPathClass = null;
                    logger.warn("The '#jsonPath' SpEL function cannot be registered. "
                            + "An old json-path.jar version is detected in the classpath."
                            + "At least 1.2.0 is required; see version information at: "
                            + "https://github.com/jayway/JsonPath/releases", e);

                }
            }

            if (jsonPathClass != null) {
                IntegrationConfigUtils.registerSpelFunctionBean(registry, jsonPathBeanName,
                        IntegrationConfigUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate");
            }
        }

        alreadyRegistered = false;
        String xpathBeanName = "xpath";
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry).containsBean(xpathBeanName);
        } else {
            alreadyRegistered = registry.isBeanNameInUse(xpathBeanName);
        }
        if (!alreadyRegistered && !registriesProcessed.contains(registryId)) {
            Class<?> xpathClass = null;
            try {
                xpathClass = ClassUtils.forName(IntegrationConfigUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils",
                        this.classLoader);
            } catch (ClassNotFoundException e) {
                logger.debug("SpEL function '#xpath' isn't registered: "
                        + "there is no spring-integration-xml.jar on the classpath.");
            }

            if (xpathClass != null) {
                IntegrationConfigUtils.registerSpelFunctionBean(registry, xpathBeanName,
                        IntegrationConfigUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate");
            }
        }

        alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry).containsBean(
                    IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME);
        } else {
            alreadyRegistered = registry.isBeanNameInUse(
                    IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME);
        }

        if (!alreadyRegistered && !registriesProcessed.contains(registryId) && this.jackson2Present) {
            registry.registerBeanDefinition(
                    IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME,
                    BeanDefinitionBuilder.genericBeanDefinition(
                            IntegrationConfigUtils.BASE_PACKAGE + ".json.ToStringFriendlyJsonNodeToStringConverter")
                            .getBeanDefinition());
            INTEGRATION_CONVERTER_INITIALIZER.registerConverter(registry, new RuntimeBeanReference(
                    IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME));
        }

        registriesProcessed.add(registryId);
    }

    /**
     * Register {@code DefaultConfiguringBeanFactoryPostProcessor}, if necessary.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerDefaultConfiguringBeanFactoryPostProcessor(BeanDefinitionRegistry registry) {
        boolean alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry)
                    .containsBean(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
        } else {
            alreadyRegistered = registry
                    .isBeanNameInUse(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
        }
        if (!alreadyRegistered) {
            BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(DefaultConfiguringBeanFactoryPostProcessor.class);
            BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder(
                    postProcessorBuilder.getBeanDefinition(),
                    IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME);
            BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, registry);
        }
    }

    /**
     * Register a {@link DefaultHeaderChannelRegistry} in the given {@link BeanDefinitionRegistry}, if necessary.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerHeaderChannelRegistry(BeanDefinitionRegistry registry) {
        boolean alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry)
                    .containsBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
        } else {
            alreadyRegistered = registry
                    .isBeanNameInUse(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
        }
        if (!alreadyRegistered) {
            if (logger.isInfoEnabled()) {
                logger.info(
                        "No bean named '" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME
                                + "' has been explicitly defined. "
                                + "Therefore, a default DefaultHeaderChannelRegistry will be created.");
            }
            BeanDefinitionBuilder schedulerBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(DefaultHeaderChannelRegistry.class);
            BeanDefinitionHolder replyChannelRegistryComponent = new BeanDefinitionHolder(
                    schedulerBuilder.getBeanDefinition(),
                    IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME);
            BeanDefinitionReaderUtils.registerBeanDefinition(replyChannelRegistryComponent, registry);
        }
    }

    /**
     * Register a {@link GlobalChannelInterceptorProcessor} in the given {@link BeanDefinitionRegistry}, if necessary.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerGlobalChannelInterceptorProcessor(BeanDefinitionRegistry registry) {
        if (!registry
                .containsBeanDefinition(IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME)) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder
                    .genericBeanDefinition(GlobalChannelInterceptorProcessor.class)
                    .setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

            registry.registerBeanDefinition(IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME,
                    builder.getBeanDefinition());
        }
    }

    /**
     * Register {@link MessagingAnnotationPostProcessor} and {@link PublisherAnnotationBeanPostProcessor}, if necessary.
     * Inject {@code defaultPublishedChannel} from provided {@link AnnotationMetadata}, if any.
     * @param meta The {@link AnnotationMetadata} to get additional properties for {@link BeanDefinition}s.
     * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
     */
    private void registerMessagingAnnotationPostProcessors(AnnotationMetadata meta,
            BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME)) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder
                    .genericBeanDefinition(MessagingAnnotationPostProcessor.class)
                    .setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

            registry.registerBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME,
                    builder.getBeanDefinition());
        }

        new PublisherRegistrar().registerBeanDefinitions(meta, registry);
    }

    /**
     * Register {@link IntegrationConfigurationBeanFactoryPostProcessor}
     * to process the external Integration infrastructure.
     */
    private void registerIntegrationConfigurationBeanFactoryPostProcessor(BeanDefinitionRegistry registry) {
        if (!registry.containsBeanDefinition(
                IntegrationContextUtils.INTEGRATION_CONFIGURATION_POST_PROCESSOR_BEAN_NAME)) {
            BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(IntegrationConfigurationBeanFactoryPostProcessor.class)
                    .setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(
                    IntegrationContextUtils.INTEGRATION_CONFIGURATION_POST_PROCESSOR_BEAN_NAME,
                    postProcessorBuilder.getBeanDefinition());
        }
    }

    /**
     * Register the default datatype channel MessageConverter.
     * @param registry the registry.
     */
    private void registerDefaultDatatypeChannelMessageConverter(BeanDefinitionRegistry registry) {
        boolean alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry)
                    .containsBean(IntegrationContextUtils.INTEGRATION_DATATYPE_CHANNEL_MESSAGE_CONVERTER_BEAN_NAME);
        } else {
            alreadyRegistered = registry.isBeanNameInUse(
                    IntegrationContextUtils.INTEGRATION_DATATYPE_CHANNEL_MESSAGE_CONVERTER_BEAN_NAME);
        }
        if (!alreadyRegistered) {
            BeanDefinitionBuilder converterBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(DefaultDatatypeChannelMessageConverter.class);
            registry.registerBeanDefinition(
                    IntegrationContextUtils.INTEGRATION_DATATYPE_CHANNEL_MESSAGE_CONVERTER_BEAN_NAME,
                    converterBuilder.getBeanDefinition());
        }

    }

    /**
     * Register the default {@link CompositeMessageConverter} for argument resolvers
     * during handler method invocation.
     * @param registry the registry.
     */
    private void registerArgumentResolverMessageConverter(BeanDefinitionRegistry registry) {
        if (!registry
                .containsBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)) {
            BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(ConfigurableCompositeMessageConverter.class);
            registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
                    postProcessorBuilder.getBeanDefinition());
        }
    }

    private void registerMessageBuilderFactory(BeanDefinitionRegistry registry) {
        boolean alreadyRegistered = false;
        if (registry instanceof ListableBeanFactory) {
            alreadyRegistered = ((ListableBeanFactory) registry)
                    .containsBean(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME);
        } else {
            alreadyRegistered = registry
                    .isBeanNameInUse(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME);
        }
        if (!alreadyRegistered) {
            BeanDefinitionBuilder mbfBuilder = BeanDefinitionBuilder
                    .genericBeanDefinition(DefaultMessageBuilderFactory.class).addPropertyValue("readOnlyHeaders",
                            IntegrationProperties.getExpressionFor(IntegrationProperties.READ_ONLY_HEADERS));
            registry.registerBeanDefinition(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
                    mbfBuilder.getBeanDefinition());
        }

    }

}