From 02a82d6eaac27374b9f4ed10d313c778023bf109 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 27 Aug 2013 08:36:08 -0400 Subject: [PATCH] INT-3115 One EvaluationContext FB Per Context Move definition of the IntegrationEvaluationContextFactoryBean from DefaultConfiguringBeanFactoryPostProcessor to AbstractIntegrationNamespaceHandler. We need a separate eval contexts for each app context so that child contexts get the correct BeanResolver. Add Test For IECFB and Parent Context --- .../AbstractIntegrationNamespaceHandler.java | 42 ++++++++- ...ltConfiguringBeanFactoryPostProcessor.java | 19 +---- ...aluationContextAwareBeanPostProcessor.java | 9 +- .../expression/ChildContext-context.xml | 20 +++++ .../expression/ParentContext-context.xml | 22 +++++ .../expression/ParentContextTests.java | 85 +++++++++++++++++++ 6 files changed, 173 insertions(+), 24 deletions(-) create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java index 1b269621ea..57985316a0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java @@ -16,21 +16,28 @@ package org.springframework.integration.config.xml; +import static org.springframework.integration.context.IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.ManagedSet; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionDecorator; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.NamespaceHandler; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; import org.springframework.integration.config.xml.ChannelInitializer.AutoCreateCandidatesCollector; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.expression.IntegrationEvaluationContextAwareBeanPostProcessor; import org.springframework.util.StringUtils; -import org.w3c.dom.Element; -import org.w3c.dom.Node; /** * Base class for NamespaceHandlers that registers a BeanFactoryPostProcessor @@ -59,6 +66,7 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa public final BeanDefinition parse(Element element, ParserContext parserContext) { this.verifySchemaVersion(element, parserContext); this.registerImplicitChannelCreator(parserContext); + this.registerIntegrationEvaluationContext(parserContext); this.registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(parserContext); return this.delegate.parse(element, parserContext); } @@ -90,8 +98,8 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa } // ChannelInitializer$AutoCreateCandidatesCollector if (parserContext.getRegistry() instanceof ListableBeanFactory) { - // unlike DefaultConfiguringBeanFactoryPostProcessor we need one of these per registry - // therefore we need to call containsBeanDefinition(..) which does not consider parent registry + // unlike DefaultConfiguringBeanFactoryPostProcessor, we need one of these per registry + // therefore we need to call containsBeanDefinition(..) which does not consider the parent registry alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()). containsBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); } @@ -107,6 +115,32 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa } } + private void registerIntegrationEvaluationContext(ParserContext parserContext) { + boolean alreadyRegistered = false; + if (parserContext.getRegistry() instanceof ListableBeanFactory) { + // unlike DefaultConfiguringBeanFactoryPostProcessor, we need one of these per registry + // therefore we need to call containsBeanDefinition(..) which does not consider the parent registry + alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBeanDefinition( + INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); + } + else { + alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); + } + if (!alreadyRegistered) { + BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder + .genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class); + integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + BeanDefinitionHolder integrationEvaluationContextHolder = new BeanDefinitionHolder( + integrationEvaluationContextBuilder.getBeanDefinition(), + IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, + parserContext.getRegistry()); + RootBeanDefinition integrationEvalContextBPP = new RootBeanDefinition( + IntegrationEvaluationContextAwareBeanPostProcessor.class); + BeanDefinitionReaderUtils.registerWithGeneratedName(integrationEvalContextBPP, parserContext.getRegistry()); + } + } + private void registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(ParserContext parserContext) { boolean alreadyRegistered = false; if (parserContext.getRegistry() instanceof ListableBeanFactory) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessor.java index 96506e9b7b..92a3f211d2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessor.java @@ -32,9 +32,7 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.integration.channel.NullChannel; -import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; import org.springframework.integration.context.IntegrationContextUtils; -import org.springframework.integration.expression.IntegrationEvaluationContextAwareBeanPostProcessor; /** * A {@link BeanFactoryPostProcessor} implementation that provides default beans for the error handling and task @@ -44,13 +42,14 @@ import org.springframework.integration.expression.IntegrationEvaluationContextAw * @author Mark Fisher * @author Oleg Zhurakousky * @author Artem Bilan + * @author Gary Russell */ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProcessor { private static final String ERROR_LOGGER_BEAN_NAME = "_org.springframework.integration.errorLogger"; - private Log logger = LogFactory.getLog(this.getClass()); + private final Log logger = LogFactory.getLog(this.getClass()); public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { @@ -64,10 +63,6 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce this.registerTaskScheduler(registry); } this.registerIdGeneratorConfigurer(registry); - if (!beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) { - this.registerIntegrationEvaluationContext(registry); - beanFactory.addBeanPostProcessor(new IntegrationEvaluationContextAwareBeanPostProcessor(beanFactory)); - } } else if (logger.isWarnEnabled()) { logger.warn("BeanFactory is not a BeanDefinitionRegistry. The default '" @@ -78,16 +73,6 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce } } - private void registerIntegrationEvaluationContext(BeanDefinitionRegistry registry) { - BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationEvaluationContextFactoryBean.class); - integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - BeanDefinitionHolder integrationEvaluationContextHolder = new BeanDefinitionHolder( - integrationEvaluationContextBuilder.getBeanDefinition(), - IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, registry); - } - private void registerIdGeneratorConfigurer(BeanDefinitionRegistry registry) { String listenerClassName = "org.springframework.integration.config.IdGeneratorConfigurer"; String[] definitionNames = registry.getBeanDefinitionNames(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAwareBeanPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAwareBeanPostProcessor.java index c7b41c98f7..e93451f0e1 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAwareBeanPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAwareBeanPostProcessor.java @@ -18,19 +18,22 @@ package org.springframework.integration.expression; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.context.IntegrationContextUtils; /** * @author Artem Bilan + * @author Gary Russell * @since 3.0 */ -public class IntegrationEvaluationContextAwareBeanPostProcessor implements BeanPostProcessor { +public class IntegrationEvaluationContextAwareBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { - private final BeanFactory beanFactory; + private volatile BeanFactory beanFactory; - public IntegrationEvaluationContextAwareBeanPostProcessor(BeanFactory beanFactory) { + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml new file mode 100644 index 0000000000..a4c0aee2bd --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ChildContext-context.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml new file mode 100644 index 0000000000..8f0c3e9086 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContext-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java new file mode 100644 index 0000000000..455496787c --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ParentContextTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2013 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.expression; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.expression.EvaluationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.test.util.TestUtils; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class ParentContextTests { + + private static final List evalContexts = new ArrayList(); + + /** + * Verifies that beans in hierarchical contexts get an evaluation context that has the proper + * BeanResolver. Verifies that the two Foos in the parent context get an evaluation context + * with the same bean resolver. Verifies that the one Foo in the child context gets a different + * bean resolver. Verifies that bean references in SpEL expressions to beans in the child + * and parent contexts work. + */ + @Test + public void testSpelBeanReferencesInChildAndParent() { + ApplicationContext parent = new ClassPathXmlApplicationContext("ParentContext-context.xml", this.getClass()); + assertEquals(2, evalContexts.size()); + ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(parent); + child.setConfigLocation("org/springframework/integration/expression/ChildContext-context.xml"); + child.refresh(); + assertEquals(3, evalContexts.size()); + assertSame(evalContexts.get(0).getBeanResolver(), evalContexts.get(1).getBeanResolver()); + assertNotSame(evalContexts.get(1).getBeanResolver(), evalContexts.get(2).getBeanResolver()); + assertSame(parent, TestUtils.getPropertyValue(evalContexts.get(0).getBeanResolver(), "beanFactory")); + assertSame(child, TestUtils.getPropertyValue(evalContexts.get(2).getBeanResolver(), "beanFactory")); + + // Test transformer expressions + child.getBean("input", MessageChannel.class).send(new GenericMessage("baz")); + Message out = child.getBean("output", QueueChannel.class).receive(0); + assertNotNull(out); + assertEquals("foobar", out.getPayload()); + child.getBean("parentIn", MessageChannel.class).send(new GenericMessage("bar")); + out = child.getBean("parentOut", QueueChannel.class).receive(0); + assertNotNull(out); + assertEquals("foo", out.getPayload()); + } + + public static class Foo implements IntegrationEvaluationContextAware { + + @Override + public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) { + evalContexts.add(evaluationContext); + } + + } +}