From 7381ee28cf5c686224e16bb95546b467ea50900e Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Sat, 27 Mar 2010 00:48:14 +0000 Subject: [PATCH] INT-789 --- ...alChannelInterceptorBeanPostProcessor.java | 198 +++++++++++++++++ .../GlobalChannelInterceptorChain.java | 54 +++++ .../xml/GlobalChannelInterceptorParser.java | 115 ++++++++++ .../xml/IntegrationNamespaceHandler.java | 1 + .../config/xml/spring-integration-2.0.xsd | 33 ++- .../GlobalChannelInterceptorTests-context.xml | 54 +++++ ...ChannelInterceptorTests-failed-context.xml | 17 ++ ...hannelInterceptorTests-ordered-context.xml | 40 ++++ ...nnelInterceptorTests-unordered-context.xml | 34 +++ .../GlobalChannelInterceptorTests.java | 209 ++++++++++++++++++ 10 files changed, 754 insertions(+), 1 deletion(-) create mode 100644 org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java create mode 100644 org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorChain.java create mode 100644 org.springframework.integration/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java create mode 100644 org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml create mode 100644 org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-failed-context.xml create mode 100644 org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-ordered-context.xml create mode 100644 org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-unordered-context.xml create mode 100644 org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java b/org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java new file mode 100644 index 0000000000..a0146556e7 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorBeanPostProcessor.java @@ -0,0 +1,198 @@ +/* + * Copyright 2002-2008 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.channel.interceptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.OrderComparator; +import org.springframework.integration.channel.AbstractMessageChannel; +import org.springframework.integration.channel.ChannelInterceptor; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +/** + * Will apply global interceptors to channels (). Since global interceptors + * could be Ordered or un-Ordered they will be sorted before merged with other interceptors in the channel. + * Sorting will only be done within the given interceptor chain which itself defines 'order' attribute + * essentially creating a group of ordered interceptors which are ordered internally and then these chain + * groups are also ordered. For example: + *
+ * channel-interceptor-chain channel-name-pattern="foo" order="5" - positive order value means AFTER local channel interceptors
+ * 			Ordered-global interceptor (4)
+ * 			Ordered-global interceptor (1)
+ * channel-interceptor-chain
+ * channel-interceptor-chain channel-name-pattern="foo" order="-1" - negative order value means AFTER local channel interceptors
+ * 			Ordered-global interceptor (3)
+ * 			Ordered-global interceptor (10)
+ * channel-interceptor-chain
+ * 
+ * channel id="foo"
+ * 		Ordered-in-channel interceptor (1)
+ * channel
+ * 
+ * will result in channel with the following interceptors
+ * Channel "foo"
+ * 		Ordered-global interceptor (3)
+ * 		Ordered-global interceptor (10)
+ * 		Ordered-in-channel interceptor (1)
+ * 		Ordered-global interceptor (1)
+ * 		Ordered-global interceptor (4)
+ * 
+ * + * @author Oleg Zhurakousky + * @since 2.0 + */ +final class GlobalChannelInterceptorBeanPostProcessor implements BeanPostProcessor, InitializingBean{ + private final static Log logger = LogFactory.getLog(GlobalChannelInterceptorBeanPostProcessor.class); + private final OrderComparator comparator = new OrderComparator(); + private List allAvailablePatters; + private List globalInterceptors; + + private List positiveOrderChains = new ArrayList(); + private List negativeOrderChains = new ArrayList(); + /** + * + * @param globalInterceptors + */ + GlobalChannelInterceptorBeanPostProcessor(List globalInterceptors){ + this.globalInterceptors = globalInterceptors; + } + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String) + */ + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String) + */ + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + if (channelPatternMatches(beanName)){ + Assert.isTrue(bean instanceof AbstractMessageChannel, "channel interceptors can only be added to " + + "AbstractMessageChannel. Current implementation is: " + bean.getClass()); + logger.debug("Applying global interceptors on channel '" + beanName + "'"); + this.mergeInterceptorsToChannel((AbstractMessageChannel) bean, beanName); + } + return bean; + } + /** + * + * @param channel + * @param channelName + */ + private void mergeInterceptorsToChannel(AbstractMessageChannel channel, String channelName){ + List tInt = null; + List interceptors = this.getExistingInterceptors(channel); + // POSITIVE + List tempPositiveInterceptorChains = new ArrayList(); + for (GlobalChannelInterceptorChain positiveOrderChain : positiveOrderChains) { + if (channelPatternMatches(channelName, positiveOrderChain.getPatterns())){ + tempPositiveInterceptorChains.add(positiveOrderChain); + } + } + // sort chain + Collections.sort(tempPositiveInterceptorChains, comparator); + + for (GlobalChannelInterceptorChain globalChannelInterceptorChain : tempPositiveInterceptorChains) { + tInt = globalChannelInterceptorChain.getInterceptors(); + // sort within the chain + Collections.sort(tInt, comparator); + interceptors.addAll(tInt); + } + // NEGATIVE + List tempNegativeInterceptorChains = new ArrayList(); + for (GlobalChannelInterceptorChain negativeOrderChain : negativeOrderChains) { + if (channelPatternMatches(channelName, negativeOrderChain.getPatterns())){ + tempNegativeInterceptorChains.add(negativeOrderChain); + } + } + // sort chains + Collections.sort(tempNegativeInterceptorChains, comparator); + + for (GlobalChannelInterceptorChain globalChannelInterceptorChain : tempNegativeInterceptorChains) { + tInt = globalChannelInterceptorChain.getInterceptors(); + // sort within the chain + Collections.sort(tInt, comparator); + interceptors.addAll(0, tInt); + } + } + /* + * + */ + private void filterPositiveNegativeOrderChains(){ + for (GlobalChannelInterceptorChain globalInterceptorChain : globalInterceptors) { + if (globalInterceptorChain.getOrder() < 0){ + negativeOrderChains.add(globalInterceptorChain); + } else { + positiveOrderChains.add(globalInterceptorChain); + } + } + } + /* + * + */ + @SuppressWarnings("unchecked") + private List getExistingInterceptors(AbstractMessageChannel channel){ + DirectFieldAccessor channelAccessor = new DirectFieldAccessor(channel); + Object iWrapper = channelAccessor.getPropertyValue("interceptors"); + DirectFieldAccessor iWrapperAccessor = new DirectFieldAccessor(iWrapper); + List interceptors = (List) iWrapperAccessor.getPropertyValue("interceptors"); + return interceptors; + } + /* + * + */ + private boolean channelPatternMatches(String beanName, String... patternsToMatch){ + String[] patterns = null; + if (patternsToMatch.length > 0){ + patterns = patternsToMatch; + } else { + patterns = allAvailablePatters.toArray(new String[]{}); + } + for (String channelPattern : patterns) { + Pattern p = Pattern.compile(channelPattern.trim()); + Matcher m = p.matcher(beanName); + if (m.find()){ + return true; + } + } + return false; + } + + @SuppressWarnings("unchecked") + public void afterPropertiesSet() throws Exception { + allAvailablePatters = new ArrayList(); + for (GlobalChannelInterceptorChain globalInterceptorchain : globalInterceptors) { + allAvailablePatters.addAll(CollectionUtils.arrayToList(globalInterceptorchain.getPatterns())); + } + this.filterPositiveNegativeOrderChains(); + logger.info("Initialized: '" + this.getClass().getSimpleName() + "' to apply global channel interceptors"); + } +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorChain.java b/org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorChain.java new file mode 100644 index 0000000000..3b375b87a0 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorChain.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2008 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.channel.interceptor; + +import java.util.List; + +import org.springframework.core.Ordered; +import org.springframework.integration.channel.ChannelInterceptor; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + */ +final class GlobalChannelInterceptorChain implements Ordered{ + private List interceptors; + private String[] patterns; + + private int order; + + public GlobalChannelInterceptorChain(List interceptors, String[] patterns, int order){ + this.interceptors = interceptors; + this.patterns = patterns; + this.order = order; + } + + List getInterceptors(){ + return interceptors; + } + + String[] getPatterns() { + return patterns; + } + + public String toString(){ + return interceptors.toString(); + } + + public int getOrder() { + return order; + } +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java new file mode 100644 index 0000000000..61b5a66918 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/GlobalChannelInterceptorParser.java @@ -0,0 +1,115 @@ +/* + * Copyright 2002-2008 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.xml; + +import java.util.List; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.RuntimeBeanReference; +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.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * Will parse 'channel-interceptor-chain' element + * + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class GlobalChannelInterceptorParser extends AbstractBeanDefinitionParser { + private static final String CONFIG_PACKAGE = IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.interceptor."; + private final ManagedList globalInterceptorChains = new ManagedList(); + private final String GLOBAL_NAME_PATTERN_ATTR = "channel-name-pattern"; + private final String REF_ATTR = "ref"; + private final String BEAN_ATTR = "bean"; + private final String ORDER_ATTR = "order"; + private final String INTERCEPTOR_CHAIN_CLASS = "GlobalChannelInterceptorChain"; + private final String GLOBAL_POST_PROCESSOR_CLASS = "GlobalChannelInterceptorBeanPostProcessor"; + + private boolean postProcessorCreated; + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext) + */ + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + this.createAndRegisterGlobalPostProcessorIfNeeded(parserContext); + + List interceptorElements = DomUtils.getChildElementsByTagName(element, new String[]{REF_ATTR, BEAN_ATTR}); + + BeanDefinitionBuilder globalChannelInterceptorBuilder = + BeanDefinitionBuilder.genericBeanDefinition(CONFIG_PACKAGE + INTERCEPTOR_CHAIN_CLASS); + String[] channelPatterns = element.getAttribute(GLOBAL_NAME_PATTERN_ATTR).split(","); + int order = this.getOrderAttribute(element); + + ManagedList adviceChain = new ManagedList(); + for (Element interceptorElement : interceptorElements) { + if (interceptorElement.getNodeName().equals(BEAN_ATTR)){ + BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); + BeanDefinitionHolder holder = delegate.parseBeanDefinitionElement(interceptorElement); + // needed for p: namespace + holder = delegate.decorateBeanDefinitionIfRequired(interceptorElement, holder); + parserContext.registerBeanComponent(new BeanComponentDefinition(holder)); + adviceChain.add(new RuntimeBeanReference(holder.getBeanName())); + } else if (interceptorElement.getNodeName().equals(REF_ATTR)) { + String ref = interceptorElement.getAttribute(BEAN_ATTR); + adviceChain.add(new RuntimeBeanReference(ref)); + } + } + globalChannelInterceptorBuilder.addConstructorArgValue(adviceChain); + globalChannelInterceptorBuilder.addConstructorArgValue(channelPatterns); + globalChannelInterceptorBuilder.addConstructorArgValue(order); + AbstractBeanDefinition interceptorChainDef = globalChannelInterceptorBuilder.getBeanDefinition(); + String interceptorChainName = + BeanDefinitionReaderUtils.generateBeanName(interceptorChainDef, parserContext.getRegistry()); + parserContext.registerBeanComponent(new BeanComponentDefinition(interceptorChainDef, interceptorChainName)); + globalInterceptorChains.add(new RuntimeBeanReference(interceptorChainName)); + return null; + } + /* + * + */ + private int getOrderAttribute(Element element){ + String sOrder = element.getAttribute(ORDER_ATTR); + if (StringUtils.hasText(sOrder)){ + return Integer.parseInt(sOrder); + } + return 0; + } + /* + * + */ + private void createAndRegisterGlobalPostProcessorIfNeeded(ParserContext parserContext){ + if (!postProcessorCreated){ + BeanDefinitionBuilder postProcessorBuilder = + BeanDefinitionBuilder.genericBeanDefinition(CONFIG_PACKAGE + GLOBAL_POST_PROCESSOR_CLASS); + BeanDefinition beanDef = postProcessorBuilder.getBeanDefinition(); + postProcessorBuilder.addConstructorArgValue(globalInterceptorChains); + String beanName = + BeanDefinitionReaderUtils.generateBeanName(beanDef, parserContext.getRegistry()); + parserContext.registerBeanComponent(new BeanComponentDefinition(beanDef, beanName)); + postProcessorCreated = true; + } + } +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java index 0f19c9dbdf..97cba5aef8 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/IntegrationNamespaceHandler.java @@ -56,6 +56,7 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan registerBeanDefinitionParser("annotation-config", new AnnotationConfigParser()); registerBeanDefinitionParser("application-event-multicaster", new ApplicationEventMulticasterParser()); registerBeanDefinitionParser("publisher", new PublisherParser()); + registerBeanDefinitionParser("channel-interceptor-chain", new GlobalChannelInterceptorParser()); } } diff --git a/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd b/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd index 03ad9bf922..b2a5fdde04 100644 --- a/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd +++ b/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd @@ -1653,5 +1653,36 @@ - + + + + + + + + + + + + + + [REQUIRED] Channel name(s) or patterns. To specify more then one channel use ','  + (e.g., channel-name-pattern="input*, foo, bar") + + + + + + + [OPTIONAL] Specifies the order in which these interceptors will be + added to the existing channel interceptors (if any). + Negative value (e.g., -2) will signify AFTER, but BEFORE the + the chain that might specify -1 (if any). Positive value (e.g., 2) + will signify BEFORE, but AFTER the chain that might specify 1 (if any). + + + + + + \ No newline at end of file diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml new file mode 100644 index 0000000000..270fc85b0d --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-context.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-failed-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-failed-context.xml new file mode 100644 index 0000000000..3915e8682f --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-failed-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-ordered-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-ordered-context.xml new file mode 100644 index 0000000000..9d7e396850 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-ordered-context.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-unordered-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-unordered-context.xml new file mode 100644 index 0000000000..bf92454584 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests-unordered-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java new file mode 100644 index 0000000000..5333db8de5 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/channel/interceptor/GlobalChannelInterceptorTests.java @@ -0,0 +1,209 @@ +/* + * Copyright 2002-2008 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.channel.interceptor; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.integration.channel.AbstractMessageChannel; +import org.springframework.integration.channel.ChannelInterceptor; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessageChannel; + +/** + * @author Oleg Zhurakousky + * @since 2.0 + */ +public class GlobalChannelInterceptorTests { + @SuppressWarnings("unchecked") + @Test + public void validateGlobalInterceptor(){ + ApplicationContext applicationContext = + new ClassPathXmlApplicationContext("GlobalChannelInterceptorTests-context.xml", GlobalChannelInterceptorTests.class); + Map channels = applicationContext.getBeansOfType(AbstractMessageChannel.class); + for (String channelName : channels.keySet()) { + AbstractMessageChannel channel = channels.get(channelName); + DirectFieldAccessor cAccessor = new DirectFieldAccessor(channel); + Object iList = cAccessor.getPropertyValue("interceptors"); + DirectFieldAccessor iAccessor = new DirectFieldAccessor(iList); + List interceptoList = (List) iAccessor.getPropertyValue("interceptors"); + if (channelName.equals("inputA")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 13); + Assert.assertEquals("four", inter[0].getTestIdentifier()); + Assert.assertEquals("seven", inter[1].getTestIdentifier()); + Assert.assertEquals("five", inter[2].getTestIdentifier()); + Assert.assertEquals("seven", inter[3].getTestIdentifier()); + Assert.assertEquals("eight", inter[4].getTestIdentifier()); + Assert.assertEquals("seven", inter[5].getTestIdentifier()); + Assert.assertEquals("one", inter[6].getTestIdentifier()); + Assert.assertEquals("seven", inter[7].getTestIdentifier()); + Assert.assertEquals("two", inter[8].getTestIdentifier()); + Assert.assertEquals("three", inter[9].getTestIdentifier()); + Assert.assertEquals("seven", inter[10].getTestIdentifier()); + Assert.assertEquals("six", inter[11].getTestIdentifier()); + Assert.assertEquals("seven", inter[12].getTestIdentifier()); + } + else + if (channelName.equals("inputB")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 11); + Assert.assertEquals("four", inter[0].getTestIdentifier()); + Assert.assertEquals("seven", inter[1].getTestIdentifier()); + Assert.assertEquals("five", inter[2].getTestIdentifier()); + Assert.assertEquals("seven", inter[3].getTestIdentifier()); + Assert.assertEquals("one", inter[4].getTestIdentifier()); + Assert.assertEquals("seven", inter[5].getTestIdentifier()); + Assert.assertEquals("two", inter[6].getTestIdentifier()); + Assert.assertEquals("three", inter[7].getTestIdentifier()); + Assert.assertEquals("seven", inter[8].getTestIdentifier()); + Assert.assertEquals("six", inter[9].getTestIdentifier()); + Assert.assertEquals("seven", inter[10].getTestIdentifier()); + } + else + if (channelName.equals("foo")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 6); + Assert.assertEquals("four", inter[0].getTestIdentifier()); + Assert.assertEquals("seven", inter[1].getTestIdentifier()); + Assert.assertEquals("five", inter[2].getTestIdentifier()); + Assert.assertEquals("seven", inter[3].getTestIdentifier()); + Assert.assertEquals("six", inter[4].getTestIdentifier()); + Assert.assertEquals("seven", inter[5].getTestIdentifier()); + } + else + if (channelName.equals("bar")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 2); + Assert.assertEquals("eight", inter[0].getTestIdentifier()); + Assert.assertEquals("seven", inter[1].getTestIdentifier()); + } + else + if (channelName.equals("baz")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 0); + } + } + } + /** + * Will test mix of Ordered and un-Ordered ChannelInterceptors + * Individual interceptors will only be sorted within groups they are defined. + * For example: interceptors defined inside of channels will be sorted according to Ordered implementation + * If global interceptors were added BEFORE (negative order) or AFTER (ppositive order) the global stack will be sorted + * and added before/after the existing stack + */ + @SuppressWarnings("unchecked") + @Test + public void validateGlobalInterceptorsOrdered(){ + ApplicationContext applicationContext = + new ClassPathXmlApplicationContext("GlobalChannelInterceptorTests-ordered-context.xml", GlobalChannelInterceptorTests.class); + Map channels = applicationContext.getBeansOfType(AbstractMessageChannel.class); + for (String channelName : channels.keySet()) { + AbstractMessageChannel channel = channels.get(channelName); + DirectFieldAccessor cAccessor = new DirectFieldAccessor(channel); + Object iList = cAccessor.getPropertyValue("interceptors"); + DirectFieldAccessor iAccessor = new DirectFieldAccessor(iList); + List interceptoList = (List) iAccessor.getPropertyValue("interceptors"); + if (channelName.equals("inputA")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 10); + Assert.assertEquals("ten", inter[0].getTestIdentifier()); + Assert.assertEquals("four", inter[1].getTestIdentifier()); + Assert.assertEquals("ten", inter[2].getTestIdentifier()); + Assert.assertEquals("eight", inter[3].getTestIdentifier()); + Assert.assertEquals("seven", inter[4].getTestIdentifier()); + Assert.assertEquals("five", inter[5].getTestIdentifier()); + Assert.assertEquals("ten", inter[6].getTestIdentifier()); + Assert.assertEquals("one", inter[7].getTestIdentifier()); + Assert.assertEquals("six", inter[8].getTestIdentifier()); + Assert.assertEquals("seven", inter[9].getTestIdentifier()); + } + + } + } + @SuppressWarnings("unchecked") + @Test + public void validateGlobalInterceptorsUnOrdered(){ + ApplicationContext applicationContext = + new ClassPathXmlApplicationContext("GlobalChannelInterceptorTests-unordered-context.xml", GlobalChannelInterceptorTests.class); + Map channels = applicationContext.getBeansOfType(AbstractMessageChannel.class); + for (String channelName : channels.keySet()) { + AbstractMessageChannel channel = channels.get(channelName); + DirectFieldAccessor cAccessor = new DirectFieldAccessor(channel); + Object iList = cAccessor.getPropertyValue("interceptors"); + DirectFieldAccessor iAccessor = new DirectFieldAccessor(iList); + List interceptoList = (List) iAccessor.getPropertyValue("interceptors"); + if (channelName.equals("inputA")){ + SampleInterceptor[] inter = interceptoList.toArray(new SampleInterceptor[]{}); + Assert.assertTrue(inter.length == 7); + Assert.assertEquals("eight", inter[0].getTestIdentifier()); + Assert.assertEquals("seven", inter[1].getTestIdentifier()); + Assert.assertEquals("five", inter[2].getTestIdentifier()); + Assert.assertEquals("six", inter[3].getTestIdentifier()); + Assert.assertEquals("seven", inter[4].getTestIdentifier()); + Assert.assertEquals("seven", inter[5].getTestIdentifier()); + Assert.assertEquals("one", inter[6].getTestIdentifier()); + } + } + } + /** + * Will test failure if 'channel-name-pattern' filter points to a valid + * bean which is not an AbstractMessageChannel + */ + @Test(expected=BeanCreationException.class) + public void failGlobalInterceptorConfig(){ + new ClassPathXmlApplicationContext("GlobalChannelInterceptorTests-failed-context.xml", GlobalChannelInterceptorTests.class); + } + + public static class SampleInterceptor implements ChannelInterceptor { + private String testIdentifier; + public String getTestIdentifier() { + return testIdentifier; + } + public void setTestIdentifier(String testIdentifier) { + this.testIdentifier = testIdentifier; + } + public Message postReceive(Message message, MessageChannel channel) { + return null; + } + public void postSend(Message message, MessageChannel channel, + boolean sent) { + } + public boolean preReceive(MessageChannel channel) { + return false; + } + public Message preSend(Message message, MessageChannel channel) { + return null; + } + } + public static class SampleOrderedInterceptor extends SampleInterceptor implements Ordered { + private int order; + public int getOrder() { + return order; + } + public void setOrder(int order) { + this.order = order; + } + } +}