From 5e47a6a9c4ef33ad1c335351e9a0fade6cbf14a2 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 15 Aug 2018 14:18:10 -0400 Subject: [PATCH] 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 --- ...ltConfiguringBeanFactoryPostProcessor.java | 505 +++++++++++++++--- .../config/IntegrationConfigUtils.java | 19 +- .../config/IntegrationRegistrar.java | 399 +------------- .../AbstractIntegrationNamespaceHandler.java | 1 - .../config/xml/AnnotationConfigParser.java | 1 - .../context/IntegrationContextUtils.java | 2 + .../configuration/EnableIntegrationTests.java | 9 +- .../dsl/flows/IntegrationFlowTests.java | 25 +- .../dsl/manualflow/ManualFlowTests.java | 4 +- ...OverrideAnnotationConfigContextLoader.java | 34 ++ .../outbound/UriVariableExpressionTests.java | 36 +- 11 files changed, 511 insertions(+), 524 deletions(-) create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/util/NoBeansOverrideAnnotationConfigContextLoader.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java index ab6a340099..3ad0dea1a5 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java @@ -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 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 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()); + builder.addConstructorArgValue(new ManagedList()); + 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 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(); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java index 0d4f63e7d2..3682e6e3a5 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java @@ -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()); - builder.addConstructorArgValue(new ManagedList()); - registry.registerBeanDefinition( - IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER, builder.getBeanDefinition()); - } - } - private IntegrationConfigUtils() { } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java index d0b7ecf7fa..add0e542bb 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java @@ -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 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 @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 resources = new LinkedList(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 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()); - } - - } - } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java index 98dee457a7..b2dcfabcc6 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java @@ -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); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java index 51263e3d50..2887a2b989 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java @@ -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) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java index 8d0030eab3..1c8d25f137 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java @@ -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"; diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java index 6fcf6e87a4..4394789cf0 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java @@ -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) diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dsl/flows/IntegrationFlowTests.java b/spring-integration-core/src/test/java/org/springframework/integration/dsl/flows/IntegrationFlowTests.java index b5873a1f77..1bcd5722c3 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dsl/flows/IntegrationFlowTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/dsl/flows/IntegrationFlowTests.java @@ -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))) - .transform(p -> p.toUpperCase()) + .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"; } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dsl/manualflow/ManualFlowTests.java b/spring-integration-core/src/test/java/org/springframework/integration/dsl/manualflow/ManualFlowTests.java index 2389fd6afb..363d688391 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dsl/manualflow/ManualFlowTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/dsl/manualflow/ManualFlowTests.java @@ -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 { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/util/NoBeansOverrideAnnotationConfigContextLoader.java b/spring-integration-core/src/test/java/org/springframework/integration/util/NoBeansOverrideAnnotationConfigContextLoader.java new file mode 100644 index 0000000000..13bac8d32b --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/util/NoBeansOverrideAnnotationConfigContextLoader.java @@ -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); + } + +} diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java index 09d606242f..06a3fe9f45 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java @@ -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 uriHolder = new AtomicReference(); + public void testFromMessageWithExpressions() { + final AtomicReference 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("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 uriHolder = new AtomicReference(); + public void testFromMessageWithSuperfluousExpressionsInt3054() { + final AtomicReference uriHolder = new AtomicReference<>(); HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("http://test/{foo}"); SpelExpressionParser parser = new SpelExpressionParser(); - Map multipleExpressions = new HashMap(); + Map 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 uriHolder = new AtomicReference(); + public void testInt3055UriVariablesExpression() { + final AtomicReference 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 expressions = new HashMap(); + Map expressions = new HashMap<>(); expressions.put("foo", "bar"); Map expressionsMap = ExpressionEvalMap.from(expressions).usingSimpleCallback().build();