/*
* 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 @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());
}
}
}