INT-3326 Add GlobalChannelInterceptor Annotation
JIRA: https://jira.spring.io/browse/INT-3326 INT-3326: Add parent-child test INT-3326: Fix `ClassCastException` for `GCII` INT-3326: Rebase and Polishing INT-3326: Fix `GCII` for `getBeanNamesForType` INT-3326: Add `GlobalChannelInterceptor` annotation JIRA: https://jira.spring.io/browse/INT-3326 INT-3326: Add parent-child test INT-3326: Fix `ClassCastException` for `GCII` INT-3326: Rebase and Polishing INT-3326: Fix `GCII` for `getBeanNamesForType` INT-3326 Polishing Doc, JavaDocs Also fix a typo in the bean name for the CI processor. INT-3326: Polishing Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
42a13c830d
commit
6d6bef58b2
@@ -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.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* {@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<String, Object> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
|
||||
@@ -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<RuntimeBeanReference> globalInterceptors = new ManagedList<RuntimeBeanReference>();
|
||||
@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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,10 +8,16 @@
|
||||
|
||||
<message-history tracked-components="publishedChannel,input,*AnnotationTestService*"/>
|
||||
|
||||
<annotation-config default-publisher-channel="publishedChannel"/>
|
||||
<channel-interceptor pattern="none">
|
||||
<annotation-config default-publisher-channel="publishedChannel"/>
|
||||
|
||||
<channel-interceptor pattern="none">
|
||||
<wire-tap channel="bar" />
|
||||
</channel-interceptor>
|
||||
|
||||
<service-activator input-channel="bar" expression="'foo'" />
|
||||
|
||||
<channel-interceptor>
|
||||
<beans:bean class="org.springframework.integration.configuration.EnableIntegrationTests$TestChannelInterceptor"/>
|
||||
</channel-interceptor>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -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<ChannelInterceptor> ciFactoryBean() {
|
||||
return new AbstractFactoryBean<ChannelInterceptor>() {
|
||||
|
||||
@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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -359,6 +359,15 @@
|
||||
for the default channel.
|
||||
See <xref linkend="publisher-annotation"/> for more information.
|
||||
</para>
|
||||
<para>
|
||||
The <classname>@GlobalChannelInterceptor</classname> annotation has been introduced to
|
||||
mark <interfacename>ChannelInterceptor</interfacename> beans for global channel interception.
|
||||
This annotation is an analogue of the <code><int:channel-interceptor></code> xml element
|
||||
(see <xref linkend="global-channel-configuration-interceptors"/>). <code>@GlobalChannelInterceptor</code> annotations
|
||||
can be placed at the class level (with a <classname>@Component</classname> stereotype annotation), or
|
||||
on <classname>@Bean</classname> methods within <classname>@Configuration</classname> classes. In either case,
|
||||
the bean <emphasis role="bold">must</emphasis> be a <interfacename>ChannelInterceptor</interfacename>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
||||
@@ -72,6 +72,14 @@
|
||||
>Spring Boot - AutoConfigure</ulink>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-global-channel-interceptor">
|
||||
<title>@GlobalChannelInterceptor</title>
|
||||
<para>
|
||||
As well as the <code>@EnableIntegration</code> annotation mentioned above,
|
||||
the <code>@GlobalChannelInterceptor</code> annotation has bean introduced.
|
||||
For more information, see <xref linkend="enable-integration"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-enable-publisher">
|
||||
<title>@EnablePublisher</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user