diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.java index ed21da44da..af245141fa 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.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. @@ -23,6 +23,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Properties; import java.util.Set; import org.apache.commons.logging.Log; @@ -33,10 +34,13 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.OrderComparator; import org.springframework.integration.channel.ChannelInterceptorAware; import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper; import org.springframework.integration.channel.interceptor.VetoCapableInterceptor; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.context.IntegrationProperties; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -44,7 +48,8 @@ import org.springframework.util.PatternMatchUtils; import org.springframework.util.StringUtils; /** - * Will apply global interceptors to channels (<channel-interceptor>). + * This class applies global interceptors ({@code } or {@code @GlobalChannelInterceptor}) + * to message channels beans. * * @author Oleg Zhurakousky * @author Mark Fisher @@ -52,7 +57,8 @@ import org.springframework.util.StringUtils; * @author Gary Russell * @since 2.0 */ -final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, SmartInitializingSingleton { +public final class GlobalChannelInterceptorProcessor + implements BeanFactoryAware, SmartInitializingSingleton, BeanPostProcessor { private static final Log logger = LogFactory.getLog(GlobalChannelInterceptorProcessor.class); @@ -67,6 +73,8 @@ final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, Smart private ListableBeanFactory beanFactory; + private volatile boolean singletonsInstantiated; + @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { Assert.isInstanceOf(ListableBeanFactory.class, beanFactory); @@ -95,12 +103,34 @@ final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, Smart addMatchingInterceptors(entry.getValue(), entry.getKey()); } } + + // TODO Remove this logic in 5.1 + Properties integrationProperties = IntegrationContextUtils.getIntegrationProperties(this.beanFactory); + + this.singletonsInstantiated = + Boolean.parseBoolean(integrationProperties.getProperty( + IntegrationProperties.POST_PROCESS_DYNAMIC_BEANS)); + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (this.singletonsInstantiated && bean instanceof ChannelInterceptorAware) { + addMatchingInterceptors((ChannelInterceptorAware) bean, beanName); + } + return bean; } /** - * Adds any interceptor whose pattern matches against the channel's name. + * Add any interceptor whose pattern matches against the channel's name. + * @param channel the message channel to add interceptors. + * @param beanName the message channel bean name to match the pattern. */ - private void addMatchingInterceptors(ChannelInterceptorAware channel, String beanName) { + public void addMatchingInterceptors(ChannelInterceptorAware channel, String beanName) { if (logger.isDebugEnabled()) { logger.debug("Applying global interceptors on channel '" + beanName + "'"); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java index 383dc40032..148e293819 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 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. @@ -87,6 +87,12 @@ public final class IntegrationProperties { */ public static final String ENDPOINTS_NO_AUTO_STARTUP = INTEGRATION_PROPERTIES_PREFIX + "endpoints.noAutoStartup"; + /** + * Whether {@link org.springframework.beans.factory.config.BeanPostProcessor}s should process beans registered at runtime. + * Will be removed in 5.1. + */ + public static final String POST_PROCESS_DYNAMIC_BEANS = INTEGRATION_PROPERTIES_PREFIX + "postProcessDynamicBeans"; + private static Properties defaults; diff --git a/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties b/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties index 5ad42a3a9b..c82449cfc4 100644 --- a/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties +++ b/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties @@ -8,3 +8,4 @@ spring.integration.messagingGateway.convertReceiveMessage=false # Defaults to MessageHeaders.ID and MessageHeaders.TIMESTAMP spring.integration.readOnly.headers= spring.integration.endpoints.noAutoStartup= +spring.integration.postProcessDynamicBeans=false diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml index c3b4ce461f..c6e171739b 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml @@ -1,12 +1,18 @@ + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> + + + true + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java index 42c253a668..7b3ef90c95 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.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,6 +16,10 @@ package org.springframework.integration.channel.interceptor; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -28,9 +32,11 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.ApplicationContext; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.integration.channel.ChannelInterceptorAware; +import org.springframework.integration.channel.DirectChannel; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.ChannelInterceptor; @@ -49,7 +55,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; public class GlobalChannelInterceptorTests { @Autowired - ApplicationContext applicationContext; + ConfigurableApplicationContext applicationContext; @Autowired @Qualifier("inputC") @@ -126,6 +132,18 @@ public class GlobalChannelInterceptorTests { Assert.assertTrue(interceptorNames.contains("interceptor-eleven")); } + @Test + public void testDynamicMessageChannelBeanWithAutoGlobalChannelInterceptor() { + DirectChannel testChannel = new DirectChannel(); + ConfigurableListableBeanFactory beanFactory = this.applicationContext.getBeanFactory(); + beanFactory.initializeBean(testChannel, "testChannel"); + + List channelInterceptors = testChannel.getChannelInterceptors(); + + assertEquals(2, channelInterceptors.size()); + assertThat(channelInterceptors.get(0), instanceOf(SampleInterceptor.class)); + assertThat(channelInterceptors.get(0), instanceOf(SampleInterceptor.class)); + } public static class SampleInterceptor extends ChannelInterceptorAdapter { @@ -139,17 +157,21 @@ public class GlobalChannelInterceptorTests { this.testIdentifier = testIdentifier; } + @Override public Message postReceive(Message message, MessageChannel channel) { return null; } + @Override public void postSend(Message message, MessageChannel channel, boolean sent) { } + @Override public boolean preReceive(MessageChannel channel) { return false; } + @Override public Message preSend(Message message, MessageChannel channel) { return null; } diff --git a/src/reference/asciidoc/channel.adoc b/src/reference/asciidoc/channel.adoc index 27a8a41b17..9c8a05b690 100644 --- a/src/reference/asciidoc/channel.adoc +++ b/src/reference/asciidoc/channel.adoc @@ -723,6 +723,9 @@ To inject a global interceptor _BEFORE_ the existing interceptors, use a negativ NOTE: Note that both the `order` and `pattern` attributes are optional. The default value for `order` will be 0 and for `pattern`, the default is '*' (to match all channels). +Starting with _version 4.3.15_, you can configure a property `spring.integration.postProcessDynamicBeans = true` to apply any global interceptors to dynamically created `MessageChannel` beans. +See <> for more information. + [[channel-wiretap]] ===== Wire Tap diff --git a/src/reference/asciidoc/configuration.adoc b/src/reference/asciidoc/configuration.adoc index 572b643f31..6ad1c17d18 100644 --- a/src/reference/asciidoc/configuration.adoc +++ b/src/reference/asciidoc/configuration.adoc @@ -208,6 +208,7 @@ spring.integration.messagingTemplate.throwExceptionOnLateReply=false <5> spring.integration.messagingAnnotations.require.componentAnnotation=false <6> spring.integration.readOnly.headers= <7> spring.integration.endpoints.noAutoStartup= <8> +spring.integration.postProcessDynamicBeans=false <9> ---- <1> When true, `input-channel` s will be automatically declared as `DirectChannel` s when not explicitly found in the @@ -237,6 +238,10 @@ These endpoints can be started later manually by their bean name via `Control Bu The effect of this global property can be explicitly overridden by specifying `auto-startup` XML or `autoStartup` annotation attribute, or via call to the `AbstractEndpoint.setAutoStartup()` in bean definition. _Since version 4.3.12_ +<9> A boolean flag to indicate that `BeanPostProcessor` s should post-process beans registered at runtime, e.g. message channels created via `IntegrationFlowContext` can be supplied with global channel interceptors. +_Since version 4.3.15_ + + These properties can be overridden by adding a file `/META-INF/spring.integration.properties` to the classpath. It is not necessary to provide all the properties, just those that you want to override. @@ -429,7 +434,7 @@ With this endpoint using the default poller: public class AnnotationService { @Transformer(inputChannel = "aPollableChannel", outputChannel = "output" - poller = @Poller("myPoller") + poller = @Poller("myPoller")) public String handle(String payload) { ... }