INT-4517: Refactor beans in IntegrationRegistrar (#2537)

* INT-4517: Refactor beans in IntegrationRegistrar

JIRA: https://jira.spring.io/browse/INT-4517

The `IntegrationRegistrar` populates beans into the application context
too early and when the `allowBeanDefinitionsOverride` is disabled we
end up with the `BeanDefinitionOverrideException` just because end-user
beans are applied later.

* Move most of the bean definitions which can be overridden in the target
application from the `IntegrationRegistrar` into the
`DefaultConfiguringBeanFactoryPostProcessor`
* Revise their registration logic to be sure that some of them are
registered only once in the parent context nad others are registered
in the child context as well
* Refactor a `DslIntegrationConfigurationInitializer` do not register
an `IntegrationFlowDefinition.ReplyProducerCleaner` in the child context
one more time: it doesn't bring any local store within the current
child context
* Introduce a `NoBeansOverrideAnnotationConfigContextLoader` in tests
to ensure that in some test-cases we disallow an
`allowBeanDefinitionsOverride` as it is now in Spring Boot since it is
enabled by default in the Spring Framework

* Revernt DslIntegrationConfigurationInitializer changes

Regsiter all the required infrastructure beans in all the application
contexts to avoid class loader issue for `static` store access
This commit is contained in:
Artem Bilan
2018-08-15 14:18:10 -04:00
committed by Gary Russell
parent 60db7bc159
commit 5e47a6a9c4
11 changed files with 511 additions and 524 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -16,156 +16,483 @@
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 java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
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.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.Lifecycle;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.integration.channel.DefaultHeaderChannelRegistry;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.integration.handler.support.CollectionArgumentResolver;
import org.springframework.integration.handler.support.HandlerMethodArgumentResolversHolder;
import org.springframework.integration.handler.support.MapArgumentResolver;
import org.springframework.integration.handler.support.PayloadExpressionArgumentResolver;
import org.springframework.integration.handler.support.PayloadsArgumentResolver;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.SmartLifecycleRoleController;
import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter;
import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter;
import org.springframework.integration.support.json.JacksonPresent;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.ClassUtils;
/**
* A {@link BeanFactoryPostProcessor} implementation that provides default beans for the error handling and task
* scheduling if those beans have not already been explicitly defined within the registry. It also registers a single
* null channel with the bean name "nullChannel".
* A {@link BeanFactoryPostProcessor} implementation that registers bean definitions
* for many infrastructure components with their default configurations.
* All of them can be overridden using particular bean names.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
*
* @see IntegrationContextUtils
*/
class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
private static final String ERROR_LOGGER_BEAN_NAME = "_org.springframework.integration.errorLogger";
private static final Log logger = LogFactory.getLog(DefaultConfiguringBeanFactoryPostProcessor.class);
private final static IntegrationConverterInitializer INTEGRATION_CONVERTER_INITIALIZER =
new IntegrationConverterInitializer();
private final Log logger = LogFactory.getLog(this.getClass());
private static final Set<Integer> registriesProcessed = new HashSet<>();
private ClassLoader classLoader;
private ConfigurableListableBeanFactory beanFactory;
private BeanDefinitionRegistry registry;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
this.registerNullChannel(registry);
if (!beanFactory.containsBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)) {
this.registerErrorChannel(registry);
}
if (!beanFactory.containsBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)) {
this.registerTaskScheduler(registry);
}
this.registerIdGeneratorConfigurer(registry);
this.beanFactory = beanFactory;
this.registry = (BeanDefinitionRegistry) beanFactory;
registerNullChannel();
registerErrorChannel();
registerIntegrationEvaluationContext();
registerTaskScheduler();
registerIdGeneratorConfigurer();
registerIntegrationProperties();
registerBuiltInBeans();
registerRoleController();
registerMessageBuilderFactory();
registerHeaderChannelRegistry();
registerGlobalChannelInterceptorProcessor();
registerDefaultDatatypeChannelMessageConverter();
registerArgumentResolverMessageConverter();
registerArgumentResolvers();
registerListCapableArgumentResolvers();
}
else if (this.logger.isWarnEnabled()) {
this.logger.warn("BeanFactory is not a BeanDefinitionRegistry. The default '"
+ IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME + "' and '"
+ IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME + "' cannot be configured."
+ " Also, any custom IdGenerator implementation configured in this BeanFactory"
+ " will not be recognized.");
else if (logger.isWarnEnabled()) {
logger.warn("BeanFactory is not a BeanDefinitionRegistry. " +
"The default Spring Integration infrastructure beans are not going to be registered");
}
}
private void registerInfrastructureBean(BeanDefinitionRegistry registry, String className) {
String[] definitionNames = registry.getBeanDefinitionNames();
for (String definitionName : definitionNames) {
BeanDefinition definition = registry.getBeanDefinition(definitionName);
if (className.equals(definition.getBeanClassName())) {
if (this.logger.isInfoEnabled()) {
this.logger.info(className + " is already registered and will be used");
}
return;
}
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(className);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
private void registerIdGeneratorConfigurer(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, "org.springframework.integration.config.IdGeneratorConfigurer");
}
/**
* Register a null channel in the given BeanDefinitionRegistry. The bean name is defined by the constant
* {@link IntegrationContextUtils#NULL_CHANNEL_BEAN_NAME}.
* Register a null channel in the application context.
* The bean name is defined by the constant {@link IntegrationContextUtils#NULL_CHANNEL_BEAN_NAME}.
*/
private void registerNullChannel(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME)) {
BeanDefinition nullChannelDefinition = registry.getBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
if (NullChannel.class.getName().equals(nullChannelDefinition.getBeanClassName())) {
return;
private void registerNullChannel() {
if (this.beanFactory.containsBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME)) {
BeanDefinition nullChannelDefinition = null;
if (this.beanFactory.containsBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME)) {
nullChannelDefinition =
this.beanFactory.getBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
}
else {
nullChannelDefinition =
((BeanDefinitionRegistry) this.beanFactory.getParentBeanFactory())
.getBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
}
if (!NullChannel.class.getName().equals(nullChannelDefinition.getBeanClassName())) {
throw new IllegalStateException("The bean name '" + IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME
+ "' is reserved.");
}
}
else {
RootBeanDefinition nullChannelDef = new RootBeanDefinition();
nullChannelDef.setBeanClassName(IntegrationConfigUtils.BASE_PACKAGE + ".channel.NullChannel");
BeanDefinitionHolder nullChannelHolder = new BeanDefinitionHolder(nullChannelDef,
IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(nullChannelHolder, registry);
this.registry.registerBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME,
new RootBeanDefinition(NullChannel.class));
}
}
/**
* Register an error channel in the given BeanDefinitionRegistry.
* Register an error channel in the application context.
* The bean name is defined by the constant {@link IntegrationContextUtils#ERROR_CHANNEL_BEAN_NAME}.
* Also a {@link IntegrationContextUtils#ERROR_LOGGER_BEAN_NAME} is registered as a subscriber for this
* error channel.
*/
private void registerErrorChannel(BeanDefinitionRegistry registry) {
if (this.logger.isInfoEnabled()) {
this.logger.info("No bean named '" + IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME +
"' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.");
private void registerErrorChannel() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)) {
if (logger.isInfoEnabled()) {
logger.info("No bean named '" + IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME +
"' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.");
}
this.registry.registerBeanDefinition(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME,
new RootBeanDefinition(PublishSubscribeChannel.class));
BeanDefinition loggingHandler =
BeanDefinitionBuilder.genericBeanDefinition(LoggingHandler.class)
.addConstructorArgValue("ERROR")
.getBeanDefinition();
String errorLoggerBeanName =
IntegrationContextUtils.ERROR_LOGGER_BEAN_NAME + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
this.registry.registerBeanDefinition(errorLoggerBeanName, loggingHandler);
BeanDefinitionBuilder loggingEndpointBuilder =
BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class)
.addPropertyReference("handler", errorLoggerBeanName)
.addPropertyValue("inputChannelName", IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME);
BeanComponentDefinition componentDefinition =
new BeanComponentDefinition(loggingEndpointBuilder.getBeanDefinition(),
IntegrationContextUtils.ERROR_LOGGER_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(componentDefinition, this.registry);
}
registry.registerBeanDefinition(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME,
new RootBeanDefinition(PublishSubscribeChannel.class));
BeanDefinition loggingHandler =
BeanDefinitionBuilder.genericBeanDefinition(LoggingHandler.class).addConstructorArgValue("ERROR")
.getBeanDefinition();
String errorLoggerBeanName = ERROR_LOGGER_BEAN_NAME + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
registry.registerBeanDefinition(errorLoggerBeanName, loggingHandler);
BeanDefinitionBuilder loggingEndpointBuilder =
BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class)
.addPropertyReference("handler", errorLoggerBeanName)
.addPropertyValue("inputChannelName", IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME);
BeanComponentDefinition componentDefinition =
new BeanComponentDefinition(loggingEndpointBuilder.getBeanDefinition(), ERROR_LOGGER_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(componentDefinition, registry);
}
/**
* Register a TaskScheduler in the given BeanDefinitionRegistry.
* Register {@link IntegrationEvaluationContextFactoryBean}
* and {@link IntegrationSimpleEvaluationContextFactoryBean} beans, if necessary.
*/
private void registerTaskScheduler(BeanDefinitionRegistry registry) {
if (this.logger.isInfoEnabled()) {
this.logger.info("No bean named '" + IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME +
"' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.");
}
BeanDefinition scheduler = BeanDefinitionBuilder.genericBeanDefinition(ThreadPoolTaskScheduler.class)
.addPropertyValue("poolSize", IntegrationProperties.getExpressionFor(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE))
.addPropertyValue("threadNamePrefix", "task-scheduler-")
.addPropertyValue("rejectedExecutionHandler", new CallerRunsPolicy())
.addPropertyValue("errorHandler", new RootBeanDefinition(MessagePublishingErrorHandler.class))
.getBeanDefinition();
private void registerIntegrationEvaluationContext() {
if (!this.registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) {
BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class);
integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME, scheduler);
BeanDefinitionHolder integrationEvaluationContextHolder =
new BeanDefinitionHolder(integrationEvaluationContextBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, this.registry);
}
if (!this.registry.containsBeanDefinition(
IntegrationContextUtils.INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME)) {
BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationSimpleEvaluationContextFactoryBean.class);
integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionHolder integrationEvaluationContextHolder =
new BeanDefinitionHolder(integrationEvaluationContextBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME);
BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, this.registry);
}
}
/**
* Register an {@link IdGeneratorConfigurer} in the application context.
*/
private void registerIdGeneratorConfigurer() {
Class<?> clazz = IdGeneratorConfigurer.class;
String className = clazz.getName();
String[] definitionNames = this.registry.getBeanDefinitionNames();
for (String definitionName : definitionNames) {
BeanDefinition definition = this.registry.getBeanDefinition(definitionName);
if (className.equals(definition.getBeanClassName())) {
if (logger.isInfoEnabled()) {
logger.info(className + " is already registered and will be used");
}
return;
}
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(clazz);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, this.registry);
}
/**
* Register a {@link ThreadPoolTaskScheduler} bean in the application context.
*/
private void registerTaskScheduler() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)) {
if (logger.isInfoEnabled()) {
logger.info("No bean named '" + IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME +
"' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.");
}
BeanDefinition scheduler = BeanDefinitionBuilder.genericBeanDefinition(ThreadPoolTaskScheduler.class)
.addPropertyValue("poolSize", IntegrationProperties
.getExpressionFor(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE))
.addPropertyValue("threadNamePrefix", "task-scheduler-")
.addPropertyValue("rejectedExecutionHandler", new CallerRunsPolicy())
.addPropertyValue("errorHandler", new RootBeanDefinition(MessagePublishingErrorHandler.class))
.getBeanDefinition();
this.registry.registerBeanDefinition(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME, scheduler);
}
}
/**
* Register an {@code integrationGlobalProperties} bean if necessary.
*/
private void registerIntegrationProperties() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME)) {
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<>(Arrays.asList(defaultResources));
resources.addAll(Arrays.asList(userResources));
BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PropertiesFactoryBean.class)
.addPropertyValue("locations", resources);
this.registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME,
integrationPropertiesBuilder.getBeanDefinition());
}
catch (IOException e) {
logger.warn("Cannot load 'spring.integration.properties' Resources.", e);
}
}
}
/**
* Register {@code jsonPath} and {@code xpath} SpEL-function beans, if necessary.
*/
private void registerBuiltInBeans() {
int registryId = System.identityHashCode(this.registry);
String jsonPathBeanName = "jsonPath";
if (!this.beanFactory.containsBean(jsonPathBeanName) && !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(this.registry, jsonPathBeanName,
IntegrationConfigUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate");
}
}
String xpathBeanName = "xpath";
if (!this.beanFactory.containsBean(xpathBeanName) && !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(this.registry, xpathBeanName,
IntegrationConfigUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate");
}
}
if (!this.beanFactory.containsBean(
IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME) &&
!registriesProcessed.contains(registryId) && JacksonPresent.isJackson2Present()) {
this.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(this.registry,
new RuntimeBeanReference(
IntegrationContextUtils.TO_STRING_FRIENDLY_JSON_NODE_TO_STRING_CONVERTER_BEAN_NAME));
}
registriesProcessed.add(registryId);
}
/**
* Register a {@link SmartLifecycleRoleController} if necessary.
*/
private void registerRoleController() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER)) {
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(SmartLifecycleRoleController.class);
builder.addConstructorArgValue(new ManagedList<String>());
builder.addConstructorArgValue(new ManagedList<Lifecycle>());
this.registry.registerBeanDefinition(
IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER, builder.getBeanDefinition());
}
}
/**
* Register a {@link DefaultMessageBuilderFactory} if necessary.
*/
private void registerMessageBuilderFactory() {
if (!this.beanFactory.containsBean(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME)) {
BeanDefinitionBuilder mbfBuilder = BeanDefinitionBuilder
.genericBeanDefinition(DefaultMessageBuilderFactory.class)
.addPropertyValue("readOnlyHeaders",
IntegrationProperties.getExpressionFor(IntegrationProperties.READ_ONLY_HEADERS));
this.registry.registerBeanDefinition(
IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
mbfBuilder.getBeanDefinition());
}
}
/**
* Register a {@link DefaultHeaderChannelRegistry} if necessary.
*/
private void registerHeaderChannelRegistry() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME)) {
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, this.registry);
}
}
/**
* Register a {@link GlobalChannelInterceptorProcessor} if necessary.
*/
private void registerGlobalChannelInterceptorProcessor() {
if (!this.registry.containsBeanDefinition(
IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME)) {
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(GlobalChannelInterceptorProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
this.registry.registerBeanDefinition(IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME,
builder.getBeanDefinition());
}
}
/**
* Register a {@link DefaultDatatypeChannelMessageConverter} bean if necessary.
*/
private void registerDefaultDatatypeChannelMessageConverter() {
if (!this.beanFactory.containsBean(
IntegrationContextUtils.INTEGRATION_DATATYPE_CHANNEL_MESSAGE_CONVERTER_BEAN_NAME)) {
BeanDefinitionBuilder converterBuilder = BeanDefinitionBuilder
.genericBeanDefinition(DefaultDatatypeChannelMessageConverter.class);
this.registry.registerBeanDefinition(
IntegrationContextUtils.INTEGRATION_DATATYPE_CHANNEL_MESSAGE_CONVERTER_BEAN_NAME,
converterBuilder.getBeanDefinition());
}
}
/**
* Register the default {@link CompositeMessageConverter} for argument resolvers
* during handler method invocation.
*/
private void registerArgumentResolverMessageConverter() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)) {
BeanDefinitionBuilder converterBuilder = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurableCompositeMessageConverter.class);
this.registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
converterBuilder.getBeanDefinition());
}
}
/**
* Register the default {@link HandlerMethodArgumentResolversHolder} for handler
* method invocation.
*/
private void registerArgumentResolvers() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.ARGUMENT_RESOLVERS_BEAN_NAME)) {
this.registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVERS_BEAN_NAME,
internalArgumentResolversBuilder(false));
}
}
/**
* Register the default {@link HandlerMethodArgumentResolversHolder} for handler
* method invocation for lists.
*/
private void registerListCapableArgumentResolvers() {
if (!this.beanFactory.containsBean(IntegrationContextUtils.LIST_ARGUMENT_RESOLVERS_BEAN_NAME)) {
this.registry.registerBeanDefinition(IntegrationContextUtils.LIST_ARGUMENT_RESOLVERS_BEAN_NAME,
internalArgumentResolversBuilder(true));
}
}
private static BeanDefinition internalArgumentResolversBuilder(boolean listCapable) {
ManagedList<BeanDefinition> resolvers = new ManagedList<>();
resolvers.add(new RootBeanDefinition(PayloadExpressionArgumentResolver.class));
resolvers.add(new RootBeanDefinition(PayloadsArgumentResolver.class));
resolvers.add(new RootBeanDefinition(MapArgumentResolver.class));
if (listCapable) {
resolvers.add(
BeanDefinitionBuilder.genericBeanDefinition(CollectionArgumentResolver.class)
.addConstructorArgValue(true)
.getBeanDefinition());
}
return BeanDefinitionBuilder.genericBeanDefinition(HandlerMethodArgumentResolversHolder.class)
.addConstructorArgValue(resolvers)
.getBeanDefinition();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2018 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.
@@ -20,16 +20,13 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
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.ManagedList;
import org.springframework.context.Lifecycle;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.support.SmartLifecycleRoleController;
/**
* Shared utility methods for Integration configuration.
*
* @author Artem Bilan
*
* @since 4.0
*/
public final class IntegrationConfigUtils {
@@ -43,6 +40,7 @@ public final class IntegrationConfigUtils {
public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className,
String methodSignature) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpelFunctionFactoryBean.class)
.addConstructorArgValue(className)
.addConstructorArgValue(methodSignature);
@@ -55,17 +53,6 @@ public final class IntegrationConfigUtils {
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
public static void registerRoleControllerDefinitionIfNecessary(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(
IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER)) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SmartLifecycleRoleController.class);
builder.addConstructorArgValue(new ManagedList<String>());
builder.addConstructorArgValue(new ManagedList<Lifecycle>());
registry.registerBeanDefinition(
IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER, builder.getBeanDefinition());
}
}
private IntegrationConfigUtils() {
}

View File

@@ -16,51 +16,19 @@
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.ManagedList;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
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.handler.support.CollectionArgumentResolver;
import org.springframework.integration.handler.support.HandlerMethodArgumentResolversHolder;
import org.springframework.integration.handler.support.MapArgumentResolver;
import org.springframework.integration.handler.support.PayloadExpressionArgumentResolver;
import org.springframework.integration.handler.support.PayloadsArgumentResolver;
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.json.JacksonPresent;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.util.ClassUtils;
/**
* {@link ImportBeanDefinitionRegistrar} implementation that configures integration infrastructure.
@@ -70,21 +38,7 @@ import org.springframework.util.ClassUtils;
*
* @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<>();
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Invoked by the framework when an &#64;EnableIntegration annotation is encountered.
@@ -97,22 +51,11 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
BeanDefinitionRegistry registry) {
registerImplicitChannelCreator(registry);
registerIntegrationConfigurationBeanFactoryPostProcessor(registry);
registerIntegrationEvaluationContext(registry);
registerIntegrationProperties(registry);
registerHeaderChannelRegistry(registry);
registerGlobalChannelInterceptorProcessor(registry);
registerBuiltInBeans(registry);
registerDefaultConfiguringBeanFactoryPostProcessor(registry);
registerDefaultDatatypeChannelMessageConverter(registry);
registerArgumentResolverMessageConverter(registry);
registerArgumentResolvers(registry);
registerListCapableArgumentResolvers(registry);
registerIntegrationConfigurationBeanFactoryPostProcessor(registry);
if (importingClassMetadata != null) {
registerMessagingAnnotationPostProcessors(importingClassMetadata, registry);
}
registerMessageBuilderFactory(registry);
IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry);
}
/**
@@ -146,182 +89,12 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
}
}
/**
* 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);
}
if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_SIMPLE_EVALUATION_CONTEXT_BEAN_NAME)) {
BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationSimpleEvaluationContextFactoryBean.class);
integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionHolder integrationEvaluationContextHolder =
new BeanDefinitionHolder(integrationEvaluationContextBuilder.getBeanDefinition(),
IntegrationContextUtils.INTEGRATION_SIMPLE_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) && JacksonPresent.isJackson2Present()) {
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) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME)) {
BeanDefinitionBuilder postProcessorBuilder =
BeanDefinitionBuilder.genericBeanDefinition(DefaultConfiguringBeanFactoryPostProcessor.class);
BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder(
@@ -332,46 +105,18 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
}
/**
* Register a {@link DefaultHeaderChannelRegistry} in the given {@link BeanDefinitionRegistry}, if necessary.
* @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s.
* Register {@link IntegrationConfigurationBeanFactoryPostProcessor}
* to process the external Integration infrastructure.
*/
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);
}
}
private void registerIntegrationConfigurationBeanFactoryPostProcessor(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(
IntegrationContextUtils.INTEGRATION_CONFIGURATION_POST_PROCESSOR_BEAN_NAME)) {
/**
* 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());
BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder
.genericBeanDefinition(IntegrationConfigurationBeanFactoryPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_CONFIGURATION_POST_PROCESSOR_BEAN_NAME,
postProcessorBuilder.getBeanDefinition());
}
}
@@ -397,122 +142,4 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
}
}
/**
* 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 converterBuilder = BeanDefinitionBuilder
.genericBeanDefinition(ConfigurableCompositeMessageConverter.class);
registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME,
converterBuilder.getBeanDefinition());
}
}
/**
* Register the default {@link HandlerMethodArgumentResolversHolder} for handler
* method invocation.
* @param registry the registry.
*/
private void registerArgumentResolvers(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVERS_BEAN_NAME)) {
registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVERS_BEAN_NAME,
internalArgumentResolversBuilder(false));
}
}
/**
* Register the default {@link HandlerMethodArgumentResolversHolder} for handler
* method invocation for lists.
* @param registry the registry.
*/
private void registerListCapableArgumentResolvers(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(IntegrationContextUtils.LIST_ARGUMENT_RESOLVERS_BEAN_NAME)) {
registry.registerBeanDefinition(IntegrationContextUtils.LIST_ARGUMENT_RESOLVERS_BEAN_NAME,
internalArgumentResolversBuilder(true));
}
}
private BeanDefinition internalArgumentResolversBuilder(boolean listCapable) {
ManagedList<BeanDefinition> resolvers = new ManagedList<>();
resolvers.add(new RootBeanDefinition(PayloadExpressionArgumentResolver.class));
resolvers.add(new RootBeanDefinition(PayloadsArgumentResolver.class));
resolvers.add(new RootBeanDefinition(MapArgumentResolver.class));
if (listCapable) {
resolvers.add(
BeanDefinitionBuilder.genericBeanDefinition(CollectionArgumentResolver.class)
.addConstructorArgValue(true)
.getBeanDefinition());
}
return BeanDefinitionBuilder.genericBeanDefinition(HandlerMethodArgumentResolversHolder.class)
.addConstructorArgValue(resolvers)
.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());
}
}
}

View File

@@ -57,7 +57,6 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
if (!this.initialized.getAndSet(true)) {
verifySchemaVersion(element, parserContext);
IntegrationRegistrar integrationRegistrar = new IntegrationRegistrar();
integrationRegistrar.setBeanClassLoader(parserContext.getReaderContext().getBeanClassLoader());
integrationRegistrar.registerBeanDefinitions(null, parserContext.getRegistry());
}
return this.delegate.parse(element, parserContext);

View File

@@ -41,7 +41,6 @@ public class AnnotationConfigParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(final Element element, ParserContext parserContext) {
IntegrationRegistrar integrationRegistrar = new IntegrationRegistrar();
integrationRegistrar.setBeanClassLoader(parserContext.getReaderContext().getBeanClassLoader());
StandardAnnotationMetadata importingClassMetadata =
new StandardAnnotationMetadata(Object.class) {

View File

@@ -41,6 +41,8 @@ public abstract class IntegrationContextUtils {
public static final String ERROR_CHANNEL_BEAN_NAME = "errorChannel";
public static final String ERROR_LOGGER_BEAN_NAME = "_org.springframework.integration.errorLogger";
public static final String NULL_CHANNEL_BEAN_NAME = "nullChannel";
public static final String METADATA_STORE_BEAN_NAME = "metadataStore";

View File

@@ -118,6 +118,7 @@ import org.springframework.integration.support.MutableMessageBuilder;
import org.springframework.integration.support.SmartLifecycleRoleController;
import org.springframework.integration.test.util.OnlyOnceTrigger;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.util.NoBeansOverrideAnnotationConfigContextLoader;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
@@ -136,7 +137,6 @@ import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
@@ -148,7 +148,7 @@ import reactor.core.publisher.Mono;
* @author Gary Russell
* @since 4.0
*/
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,
@ContextConfiguration(loader = NoBeansOverrideAnnotationConfigContextLoader.class,
classes = { EnableIntegrationTests.ContextConfiguration.class,
EnableIntegrationTests.ContextConfiguration2.class })
@RunWith(SpringJUnit4ClassRunner.class)
@@ -961,6 +961,7 @@ public class EnableIntegrationTests {
public Integer getInvoked() {
return invoked.get();
}
}
@Configuration
@@ -1443,6 +1444,7 @@ public class EnableIntegrationTests {
String phase() default "";
Poller[] poller() default { };
}
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@@ -1461,6 +1463,7 @@ public class EnableIntegrationTests {
String phase() default "";
Poller[] poller() default { };
}
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@@ -1566,6 +1569,7 @@ public class EnableIntegrationTests {
String phase() default "";
Poller[] poller() default { };
}
@Target(ElementType.METHOD)
@@ -1614,6 +1618,7 @@ public class EnableIntegrationTests {
public @interface MyBridgeFrom {
String value() default "";
}
@Target(ElementType.METHOD)

View File

@@ -75,6 +75,7 @@ import org.springframework.integration.store.SimpleMessageStore;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.MutableMessageBuilder;
import org.springframework.integration.transformer.PayloadSerializingTransformer;
import org.springframework.integration.util.NoBeansOverrideAnnotationConfigContextLoader;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
@@ -91,6 +92,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
@@ -101,6 +103,7 @@ import org.springframework.test.context.junit4.SpringRunner;
*
* @since 5.0
*/
@ContextConfiguration(loader = NoBeansOverrideAnnotationConfigContextLoader.class)
@RunWith(SpringRunner.class)
@DirtiesContext
public class IntegrationFlowTests {
@@ -482,11 +485,13 @@ public class IntegrationFlowTests {
public interface ControlBusGateway {
void send(String command);
}
@Configuration
@EnableIntegration
public static class SupplierContextConfiguration1 {
@Bean
public IntegrationFlow supplierFlow() {
return IntegrationFlows.from(() -> "foo")
@@ -512,15 +517,17 @@ public class IntegrationFlowTests {
public MessageChannel suppliedChannel() {
return MessageChannels.queue(10).get();
}
}
@Configuration
@EnableIntegration
public static class SupplierContextConfiguration2 {
@Bean
public IntegrationFlow supplierFlow2() {
return IntegrationFlows.from(() -> "foo", c -> c.poller(Pollers.fixedDelay(100).maxMessagesPerPoll(1)))
.<String, String>transform(p -> p.toUpperCase())
.<String, String>transform(String::toUpperCase)
.channel("suppliedChannel2")
.get();
}
@@ -529,6 +536,7 @@ public class IntegrationFlowTests {
public MessageChannel suppliedChannel2() {
return MessageChannels.queue(10).get();
}
}
@Configuration
@@ -542,18 +550,6 @@ public class IntegrationFlowTests {
.get();
}
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata poller() {
return Pollers.fixedRate(500).get();
}
@Bean(name = IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(100);
return threadPoolTaskScheduler;
}
@Bean
public MessageChannel inputChannel() {
return MessageChannels.direct().get();
@@ -690,6 +686,7 @@ public class IntegrationFlowTests {
public void handle(Object payload) {
assertEquals(100, payload);
}
}
@Configuration
@@ -856,6 +853,7 @@ public class IntegrationFlowTests {
protected Object handleRequestMessage(Message<?> requestMessage) {
return "Hello " + this.worldService.world() + " and " + requestMessage.getPayload();
}
}
@Service
@@ -864,6 +862,7 @@ public class IntegrationFlowTests {
public String world() {
return "World";
}
}

View File

@@ -82,6 +82,7 @@ import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.support.SmartLifecycleRoleController;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.transformer.MessageTransformingHandler;
import org.springframework.integration.util.NoBeansOverrideAnnotationConfigContextLoader;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
@@ -102,7 +103,8 @@ import reactor.core.publisher.Flux;
*
* @since 5.0
*/
@ContextConfiguration(classes = ManualFlowTests.RootConfiguration.class)
@ContextConfiguration(loader = NoBeansOverrideAnnotationConfigContextLoader.class,
classes = ManualFlowTests.RootConfiguration.class)
@RunWith(SpringRunner.class)
@DirtiesContext
public class ManualFlowTests {

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2018 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.util;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
/**
* @author Artem Bilan
*
* @since 5.1
*/
public class NoBeansOverrideAnnotationConfigContextLoader extends AnnotationConfigContextLoader {
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
beanFactory.setAllowBeanDefinitionOverriding(false);
}
}

View File

@@ -22,7 +22,6 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
@@ -58,21 +57,23 @@ import org.springframework.messaging.support.GenericMessage;
public class UriVariableExpressionTests {
@Test
public void testFromMessageWithExpressions() throws Exception {
final AtomicReference<URI> uriHolder = new AtomicReference<URI>();
public void testFromMessageWithExpressions() {
final AtomicReference<URI> uriHolder = new AtomicReference<>();
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://test/{foo}");
SpelExpressionParser parser = new SpelExpressionParser();
handler.setUriVariableExpressions(Collections.singletonMap("foo", parser.parseExpression("payload")));
handler.setRequestFactory(new SimpleClientHttpRequestFactory() {
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
uriHolder.set(uri);
throw new RuntimeException("intentional");
}
});
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<?> message = new GenericMessage<Object>("bar");
Message<?> message = new GenericMessage<>("bar");
try {
handler.handleMessage(message);
fail("Exception expected.");
@@ -83,22 +84,26 @@ public class UriVariableExpressionTests {
assertEquals("http://test/bar", uriHolder.get().toString());
}
/** Test for INT-3054: Do not break if there are extra uri variables defined in the http outbound gateway. */
/**
* Test for INT-3054: Do not break if there are extra uri variables defined in the http outbound gateway.
*/
@Test
public void testFromMessageWithSuperfluousExpressionsInt3054() throws Exception {
final AtomicReference<URI> uriHolder = new AtomicReference<URI>();
public void testFromMessageWithSuperfluousExpressionsInt3054() {
final AtomicReference<URI> uriHolder = new AtomicReference<>();
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://test/{foo}");
SpelExpressionParser parser = new SpelExpressionParser();
Map<String, Expression> multipleExpressions = new HashMap<String, Expression>();
Map<String, Expression> multipleExpressions = new HashMap<>();
multipleExpressions.put("foo", parser.parseExpression("payload"));
multipleExpressions.put("extra-to-be-ignored", parser.parseExpression("headers.extra"));
handler.setUriVariableExpressions(multipleExpressions);
handler.setRequestFactory(new SimpleClientHttpRequestFactory() {
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
uriHolder.set(uri);
throw new RuntimeException("intentional");
}
});
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
@@ -113,28 +118,29 @@ public class UriVariableExpressionTests {
}
@Test
public void testInt3055UriVariablesExpression() throws Exception {
final AtomicReference<URI> uriHolder = new AtomicReference<URI>();
public void testInt3055UriVariablesExpression() {
final AtomicReference<URI> uriHolder = new AtomicReference<>();
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://test/{foo}");
handler.setRequestFactory(new SimpleClientHttpRequestFactory() {
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
uriHolder.set(uri);
throw new RuntimeException("intentional");
}
});
AbstractApplicationContext context = TestUtils.createTestApplicationContext();
IntegrationRegistrar registrar = new IntegrationRegistrar();
registrar.setBeanClassLoader(context.getClassLoader());
registrar.registerBeanDefinitions(null, (BeanDefinitionRegistry) context.getBeanFactory());
context.refresh();
handler.setBeanFactory(context);
handler.setUriVariablesExpression(new SpelExpressionParser().parseExpression("headers.uriVariables"));
handler.afterPropertiesSet();
Map<String, Object> expressions = new HashMap<String, Object>();
Map<String, Object> expressions = new HashMap<>();
expressions.put("foo", "bar");
Map<String, ?> expressionsMap = ExpressionEvalMap.from(expressions).usingSimpleCallback().build();