diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptor.java
new file mode 100644
index 0000000000..fa2a8ce81c
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.integration.config;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@link org.springframework.messaging.support.ChannelInterceptor} components with this
+ * annotation will be applied as global channel interceptors
+ * using the provided {@code patterns} to match channel names.
+ *
+ * The annotation can be used at the {@code class} level for {@link org.springframework.stereotype.Component} beans
+ * and on methods with {@link org.springframework.context.annotation.Bean}.
+ *
+ * @author Artem Bilan
+ * @since 4.0
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface GlobalChannelInterceptor {
+
+ /**
+ * An array of simple patterns against which channel names will be matched. Default is "*"
+ * (all channels). See {@link org.springframework.util.PatternMatchUtils#simpleMatch(String, String)}.
+ * @return The pattern.
+ */
+ String[] patterns() default "*";
+
+ /**
+ * The order of the interceptor. Interceptors with negative order values will be placed before any
+ * explicit interceptors on the channel; interceptors with positive order values will be
+ * placed after explicit interceptors.
+ * @return The order.
+ */
+ int order() default 0;
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java
new file mode 100644
index 0000000000..9ff505615c
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.integration.config;
+
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.MethodMetadata;
+import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper;
+import org.springframework.messaging.support.ChannelInterceptor;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * The {@link IntegrationConfigurationInitializer} to populate {@link GlobalChannelInterceptorWrapper}
+ * for {@link ChannelInterceptor}s marked with {@link GlobalChannelInterceptor} annotation.
+ *
+ * {@link org.springframework.context.annotation.Bean} methods are also processed.
+ *
+ * @author Artem Bilan
+ * @since 4.0
+ */
+public class GlobalChannelInterceptorInitializer implements IntegrationConfigurationInitializer {
+
+ @Override
+ public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
+
+ for (String beanName : registry.getBeanDefinitionNames()) {
+ BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
+ if (beanDefinition instanceof AnnotatedBeanDefinition) {
+ AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
+ Map annotationAttributes = metadata.getAnnotationAttributes(GlobalChannelInterceptor.class.getName());
+ if (CollectionUtils.isEmpty(annotationAttributes) && beanDefinition.getSource() instanceof MethodMetadata) {
+ MethodMetadata beanMethod = (MethodMetadata) beanDefinition.getSource();
+ annotationAttributes = beanMethod.getAnnotationAttributes(GlobalChannelInterceptor.class.getName());
+ }
+
+ if (!CollectionUtils.isEmpty(annotationAttributes)) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(GlobalChannelInterceptorWrapper.class)
+ .addConstructorArgReference(beanName)
+ .addPropertyValue("patterns", annotationAttributes.get("patterns"))
+ .addPropertyValue("order", annotationAttributes.get("order"));
+
+ BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), registry);
+ }
+ }
+ }
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.java
similarity index 96%
rename from spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorProcessor.java
rename to spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.java
index efbdaaddeb..9410aa0dcf 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorProcessor.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorProcessor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.integration.channel.interceptor;
+package org.springframework.integration.config;
import java.util.ArrayList;
import java.util.Collection;
@@ -35,6 +35,8 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.SmartLifecycle;
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.messaging.support.ChannelInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
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 155f2a49d1..8741e141f3 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
@@ -84,6 +84,7 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
this.registerIntegrationEvaluationContext(registry);
this.registerIntegrationProperties(registry);
this.registerHeaderChannelRegistry(registry);
+ this.registerGlobalChannelInterceptorProcessor(registry);
this.registerBuiltInBeans(registry);
this.registerDefaultConfiguringBeanFactoryPostProcessor(registry);
this.registerDefaultDatatypeChannelMessageConverter(registry);
@@ -285,6 +286,20 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean
}
}
+ /**
+ * Register a {@link GlobalChannelInterceptorProcessor} in the given {@link BeanDefinitionRegistry}, if necessary.
+ *
+ * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s.
+ */
+ private void registerGlobalChannelInterceptorProcessor(BeanDefinitionRegistry registry) {
+ if (!registry.containsBeanDefinition(IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME)) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(GlobalChannelInterceptorProcessor.class)
+ .setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+
+ registry.registerBeanDefinition(IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME, builder.getBeanDefinition());
+ }
+ }
+
/**
* Register {@link MessagingAnnotationPostProcessor} and {@link PublisherAnnotationBeanPostProcessor}, if necessary.
* Inject {@code defaultPublishedChannel} from provided {@link AnnotationMetadata}, if any.
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java
index 6ac44b532f..ba92cc4c85 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java
@@ -26,12 +26,9 @@ import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
-import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper;
-import org.springframework.integration.config.IntegrationConfigUtils;
-import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.util.xml.DomUtils;
/**
@@ -41,46 +38,33 @@ import org.springframework.util.xml.DomUtils;
* @author Mark Fisher
* @author David Turanski
* @author Gary Russell
+ * @author Artem Bilan
* @since 2.0
*/
public class GlobalChannelInterceptorParser extends AbstractBeanDefinitionParser {
- private static final String BASE_PACKAGE = IntegrationConfigUtils.BASE_PACKAGE + ".channel.interceptor.";
-
private static final String CHANNEL_NAME_PATTERN_ATTRIBUTE = "pattern";
private static final String REF_ATTRIBUTE = "ref";
- private static final String GLOBAL_INTERCEPTOR_PROCESSOR_CLASSNAME = "GlobalChannelInterceptorProcessor";
-
-
- private final ManagedList globalInterceptors = new ManagedList();
+ @Override
+ protected boolean shouldGenerateId() {
+ return true;
+ }
+ @Override
+ protected boolean shouldFireEvents() {
+ return false;
+ }
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
- this.createAndRegisterGlobalPostProcessorIfNecessary(parserContext);
BeanDefinitionBuilder globalChannelInterceptorBuilder = BeanDefinitionBuilder.genericBeanDefinition(GlobalChannelInterceptorWrapper.class);
Object childBeanDefinition = getBeanDefinitionBuilderConstructorValue(element, parserContext);
globalChannelInterceptorBuilder.addConstructorArgValue(childBeanDefinition);
IntegrationNamespaceUtils.setValueIfAttributeDefined(globalChannelInterceptorBuilder, element, "order");
- IntegrationNamespaceUtils.setValueIfAttributeDefined(
- globalChannelInterceptorBuilder, element, CHANNEL_NAME_PATTERN_ATTRIBUTE, "patterns");
- String beanName = BeanDefinitionReaderUtils.generateBeanName(
- globalChannelInterceptorBuilder.getBeanDefinition(), parserContext.getRegistry());
- parserContext.registerBeanComponent(new BeanComponentDefinition(globalChannelInterceptorBuilder.getBeanDefinition(), beanName));
- this.globalInterceptors.add(new RuntimeBeanReference(beanName));
- return null;
- }
-
- private void createAndRegisterGlobalPostProcessorIfNecessary(ParserContext parserContext) {
- if (!parserContext.getRegistry().containsBeanDefinition(IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME)) {
- BeanDefinitionBuilder processorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
- BASE_PACKAGE + GLOBAL_INTERCEPTOR_PROCESSOR_CLASSNAME);
- BeanDefinition beanDef = processorBuilder.getBeanDefinition();
- parserContext.registerBeanComponent(new BeanComponentDefinition(beanDef,
- IntegrationContextUtils.GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME));
- }
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(globalChannelInterceptorBuilder, element, CHANNEL_NAME_PATTERN_ATTRIBUTE, "patterns");
+ return globalChannelInterceptorBuilder.getBeanDefinition();
}
protected Object getBeanDefinitionBuilderConstructorValue(Element element, ParserContext parserContext) {
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 2e02516f5a..48ef2a27f2 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
@@ -82,7 +82,7 @@ public abstract class IntegrationContextUtils {
public static final String INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME = "messageBuilderFactory";
- public static final String GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME = "gloabelChannelInterceptorProcessor";
+ public static final String GLOBAL_CHANNEL_INTERCEPTOR_PROCESSOR_BEAN_NAME = "globalChannelInterceptorProcessor";
/**
* @param beanFactory BeanFactory for lookup, must not be null.
diff --git a/spring-integration-core/src/main/resources/META-INF/spring.factories b/spring-integration-core/src/main/resources/META-INF/spring.factories
index 48ce58c12c..b8ac0151ab 100644
--- a/spring-integration-core/src/main/resources/META-INF/spring.factories
+++ b/spring-integration-core/src/main/resources/META-INF/spring.factories
@@ -1,2 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.integration.config.boot.IntegrationAutoConfiguration
+org.springframework.integration.config.IntegrationConfigurationInitializer=\
+org.springframework.integration.config.GlobalChannelInterceptorInitializer
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml
index 65036f4a7c..af2d32b8f7 100644
--- a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml
+++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml
@@ -8,10 +8,16 @@
-
-
+
+
+
+
+
+
+
+
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 ee9c637150..67439cd56c 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
@@ -18,16 +18,24 @@ package org.springframework.integration.configuration;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.concurrent.atomic.AtomicInteger;
+
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.AbstractFactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -43,11 +51,15 @@ import org.springframework.integration.annotation.Payload;
import org.springframework.integration.annotation.Publisher;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.annotation.Transformer;
+import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.QueueChannel;
+import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.EnableMessageHistory;
import org.springframework.integration.config.EnablePublisher;
+import org.springframework.integration.config.GlobalChannelInterceptor;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.history.MessageHistoryConfigurer;
import org.springframework.integration.support.MessageBuilder;
@@ -55,6 +67,9 @@ import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
+import org.springframework.messaging.support.ChannelInterceptor;
+import org.springframework.messaging.support.ChannelInterceptorAdapter;
+import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -68,24 +83,41 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader;
@RunWith(SpringJUnit4ClassRunner.class)
public class EnableIntegrationTests {
+ @Autowired
+ private ApplicationContext context;
+
@Autowired
private MessageChannel input;
@Autowired
- private PollableChannel output;
+ private QueueChannel output;
@Autowired
private PollableChannel publishedChannel;
+ @Autowired
+ private PollableChannel wireTapChannel;
+
@Autowired
private MessageHistoryConfigurer configurer;
@Autowired
private TestGateway testGateway;
+ @Autowired
+ private TestChannelInterceptor testChannelInterceptor;
+
+ @Autowired
+ private AtomicInteger fbInterceptorCounter;
+
@Test
public void testAnnotatedServiceActivator() {
this.input.send(MessageBuilder.withPayload("Foo").build());
+
+ Message> interceptedMessage = this.wireTapChannel.receive(1000);
+ assertNotNull(interceptedMessage);
+ assertEquals("Foo", interceptedMessage.getPayload());
+
Message> receive = this.output.receive(1000);
assertNotNull(receive);
assertEquals("FOO", receive.getPayload());
@@ -107,19 +139,24 @@ public class EnableIntegrationTests {
assertThat(messageHistoryString, Matchers.not(Matchers.containsString("input")));
assertThat(messageHistoryString, Matchers.not(Matchers.containsString("output")));
assertThat(messageHistoryString, Matchers.containsString("publishedChannel"));
+
+ assertNull(this.wireTapChannel.receive(0));
+ assertThat(this.testChannelInterceptor.getInvoked(), Matchers.greaterThan(0));
+ assertThat(this.fbInterceptorCounter.get(), Matchers.greaterThan(0));
}
- @Test @DirtiesContext
+ @Test
+ @DirtiesContext
public void testChangePatterns() {
try {
- this.configurer.setComponentNamePatterns(new String[] {"*"});
+ this.configurer.setComponentNamePatterns(new String[]{"*"});
fail("ExpectedException");
}
catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("cannot be changed"));
}
this.configurer.stop();
- this.configurer.setComponentNamePatterns(new String[] {"*"});
+ this.configurer.setComponentNamePatterns(new String[]{"*"});
assertEquals("*", TestUtils.getPropertyValue(this.configurer, "componentNamePatterns", String[].class)[0]);
}
@@ -129,6 +166,32 @@ public class EnableIntegrationTests {
assertEquals(payload.toUpperCase(), this.testGateway.echo(payload));
}
+ @Test
+ public void testParentChildAnnotationConfiguration() {
+ AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
+ child.register(ChildConfiguration.class);
+ child.setParent(this.context);
+ child.refresh();
+ AbstractMessageChannel foo = child.getBean("foo", AbstractMessageChannel.class);
+ ChannelInterceptor baz = child.getBean("baz", ChannelInterceptor.class);
+ assertTrue(foo.getChannelInterceptors().contains(baz));
+ assertFalse(this.output.getChannelInterceptors().contains(baz));
+ child.close();
+ }
+
+ @Test
+ public void testParentChildAnnotationConfigurationFromAnotherPackage() {
+ AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
+ child.register(org.springframework.integration.configuration2.ChildConfiguration.class);
+ child.setParent(this.context);
+ child.refresh();
+ AbstractMessageChannel foo = child.getBean("foo", AbstractMessageChannel.class);
+ ChannelInterceptor baz = child.getBean("baz", ChannelInterceptor.class);
+ assertTrue(foo.getChannelInterceptors().contains(baz));
+ assertFalse(this.output.getChannelInterceptors().contains(baz));
+ child.close();
+ }
+
@Configuration
@ComponentScan
@IntegrationComponentScan
@@ -147,6 +210,65 @@ public class EnableIntegrationTests {
return new QueueChannel();
}
+ @Bean
+ public PollableChannel wireTapChannel() {
+ return new QueueChannel();
+ }
+
+
+ @Bean
+ @GlobalChannelInterceptor(patterns = "input")
+ public WireTap wireTap() {
+ return new WireTap(this.wireTapChannel());
+ }
+
+ @Bean
+ public AtomicInteger fbInterceptorCounter() {
+ return new AtomicInteger();
+ }
+
+ @Bean
+ @GlobalChannelInterceptor
+ public FactoryBean ciFactoryBean() {
+ return new AbstractFactoryBean() {
+
+ @Override
+ public Class> getObjectType() {
+ return ChannelInterceptor.class;
+ }
+
+ @Override
+ protected ChannelInterceptor createInstance() throws Exception {
+ return new ChannelInterceptorAdapter() {
+
+ @Override
+ public Message> preSend(Message> message, MessageChannel channel) {
+ fbInterceptorCounter().incrementAndGet();
+ return super.preSend(message, channel);
+ }
+ };
+ }
+ };
+ }
+
+ }
+
+ @Component
+ @GlobalChannelInterceptor
+ public static class TestChannelInterceptor extends ChannelInterceptorAdapter {
+
+ private final AtomicInteger invoked = new AtomicInteger();
+
+ @Override
+ public Message> preSend(Message> message, MessageChannel channel) {
+ this.invoked.incrementAndGet();
+ return message;
+ }
+
+ public Integer getInvoked() {
+ return invoked.get();
+ }
+
}
@Configuration
@@ -173,6 +295,24 @@ public class EnableIntegrationTests {
}
+ @Configuration
+ @EnableIntegration
+ public static class ChildConfiguration {
+
+ @Bean
+ public MessageChannel foo() {
+ return new DirectChannel();
+ }
+
+ @Bean
+ @GlobalChannelInterceptor(patterns = "*")
+ public WireTap baz() {
+ return new WireTap(new NullChannel());
+ }
+
+ }
+
+
@MessageEndpoint
public static class AnnotationTestService {
@@ -196,7 +336,7 @@ public class EnableIntegrationTests {
@MessagingGateway(defaultRequestChannel = "gatewayChannel", defaultHeaders = @GatewayHeader(name = "foo", value = "FOO"))
public static interface TestGateway {
- @Gateway(headers = @GatewayHeader(name = "calledMethod", expression="#gatewayMethod.name"))
+ @Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "#gatewayMethod.name"))
String echo(String payload);
}
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration2/ChildConfiguration.java b/spring-integration-core/src/test/java/org/springframework/integration/configuration2/ChildConfiguration.java
new file mode 100644
index 0000000000..aa2ba4449f
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration2/ChildConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 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.configuration2;
+
+/**
+ * @author Artem Bilan
+ * @since 4.0
+ */
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.channel.interceptor.WireTap;
+import org.springframework.integration.config.EnableIntegration;
+import org.springframework.integration.config.GlobalChannelInterceptor;
+import org.springframework.messaging.MessageChannel;
+
+@Configuration
+@EnableIntegration
+public class ChildConfiguration {
+
+ @Bean
+ public MessageChannel foo() {
+ return new DirectChannel();
+ }
+
+ @Bean
+ public MessageChannel bar() {
+ return new DirectChannel();
+ }
+
+ @Bean
+ @GlobalChannelInterceptor(patterns = "*")
+ public WireTap baz() {
+ return new WireTap(this.bar());
+ }
+
+}
diff --git a/src/reference/docbook/overview.xml b/src/reference/docbook/overview.xml
index c5ffbbd2d7..08383b64e7 100644
--- a/src/reference/docbook/overview.xml
+++ b/src/reference/docbook/overview.xml
@@ -359,6 +359,15 @@
for the default channel.
See for more information.
+
+ The @GlobalChannelInterceptor annotation has been introduced to
+ mark ChannelInterceptor beans for global channel interception.
+ This annotation is an analogue of the <int:channel-interceptor> xml element
+ (see ). @GlobalChannelInterceptor annotations
+ can be placed at the class level (with a @Component stereotype annotation), or
+ on @Bean methods within @Configuration classes. In either case,
+ the bean must be a ChannelInterceptor.
+
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml
index a68cd54b59..46f2e47032 100644
--- a/src/reference/docbook/whats-new.xml
+++ b/src/reference/docbook/whats-new.xml
@@ -72,6 +72,14 @@
>Spring Boot - AutoConfigure.
+
+ @GlobalChannelInterceptor
+
+ As well as the @EnableIntegration annotation mentioned above,
+ the @GlobalChannelInterceptor annotation has bean introduced.
+ For more information, see .
+
+