From 7f008b58c259d74debaa699617afd9ab91ba8c80 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 21 Aug 2013 15:05:21 -0400 Subject: [PATCH] INT-1639 Externalize SpEL Evaluation Context Allow modification of context property accessors and functions. Polishing - Fix tests to ensure the EvaluationContextFactoryBean is available when necessary. Polishing - Use Utility for ALL EvaluationContexts Add Test Showing Custom Function See SpelTransformerIntegrationTests-context.xml Polishing Polishing -fix Remote Sync Polishing - Emit WARN if No BeanFactory Polishing - Do Not Override ConversionService Kludge to Prevent Warning When No BeanFactory Expressions for method invoking in the MessagingMethodInvokerHelper don't need a BeanFactory - suppress the warning. Polishing - PR Comments - Invert Boolean --- .../amqp/config/OutboundGatewayTests.java | 22 ++- .../aop/MessagePublishingInterceptor.java | 21 ++- .../aop/PublisherAnnotationAdvisor.java | 1 + ...tegrationEvaluationContextFactoryBean.java | 134 ++++++++++++++++++ ...ltConfiguringBeanFactoryPostProcessor.java | 25 +++- .../context/IntegrationContextUtils.java | 11 ++ .../context/IntegrationObjectSupport.java | 2 +- .../ExpressionMessageProducerSupport.java | 24 +++- .../expression/ExpressionUtils.java | 84 +++++------ .../IntegrationEvaluationContextAware.java | 29 ++++ ...aluationContextAwareBeanPostProcessor.java | 50 +++++++ .../GatewayMethodInboundMessageMapper.java | 18 +-- .../integration/handler/DelayHandler.java | 7 +- .../integration/handler/LoggingHandler.java | 16 ++- ...ressionEvaluatingRequestHandlerAdvice.java | 9 +- .../SpelExpressionRetryStateGenerator.java | 7 +- ...ngTransactionSynchronizationProcessor.java | 11 +- .../transformer/ContentEnricher.java | 56 ++++---- .../util/AbstractExpressionEvaluator.java | 70 +++++---- .../util/MessagingMethodInvokerHelper.java | 5 +- .../aop/PublisherExpressionTests.java | 20 ++- .../xml/LoggingChannelAdapterParserTests.java | 5 +- .../expression/ExpressionUtilsTests.java | 96 +++++++++++++ .../GatewayProxyMessageMappingTests.java | 14 +- ...essionEvaluatingMessageProcessorTests.java | 31 +++- .../transformer/ContentEnricherTests.java | 99 +++++++------ ...pelTransformerIntegrationTests-context.xml | 27 +++- .../SpelTransformerIntegrationTests.java | 94 ++++++++++++ ...ionEventListeningMessageProducerTests.java | 20 ++- .../file/FileWritingMessageHandler.java | 14 +- .../AbstractInboundFileSynchronizer.java | 17 ++- ...boundChannelAdapterParserTests-context.xml | 16 ++- ...FileOutboundChannelAdapterParserTests.java | 17 ++- ...boundChannelAdapterParserTests-context.xml | 6 +- .../FtpInboundChannelAdapterParserTests.java | 17 +++ ...oundRemoteFileSystemSynchronizerTests.java | 25 ++-- .../ContinuousQueryMessageProducerTests.java | 14 +- .../HttpRequestHandlingEndpointSupport.java | 11 +- .../HttpRequestExecutingMessageHandler.java | 14 +- ...tpRequestExecutingMessageHandlerTests.java | 45 +++++- .../outbound/UriVariableExpressionTests.java | 1 + .../integration/jdbc/StoredProcExecutor.java | 14 +- .../mail/AbstractMailReceiver.java | 7 +- .../mail/config/MailReceiverFactoryBean.java | 15 +- .../InboundChannelAdapterParserTests.java | 4 +- .../mongodb/inbound/MongoDbMessageSource.java | 7 +- .../MongoDbStoringMessageHandler.java | 7 +- .../inbound/RedisStoreMessageSource.java | 7 +- .../RedisStoreWritingMessageHandler.java | 8 +- ...oundRemoteFileSystemSynchronizerTests.java | 37 ++--- .../ws/AbstractWebServiceOutboundGateway.java | 7 +- .../transformer/XsltPayloadTransformer.java | 14 +- .../XsltPayloadTransformerParserTests.java | 11 +- 53 files changed, 983 insertions(+), 360 deletions(-) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAware.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAwareBeanPostProcessor.java create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/expression/ExpressionUtilsTests.java diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java index 02e65a9051..e2387a9b4c 100644 --- a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java @@ -22,15 +22,20 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; + import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; +import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; +import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.handler.MessageProcessor; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.test.util.TestUtils; @@ -60,20 +65,27 @@ public class OutboundGatewayTests { @SuppressWarnings("unchecked") @Test - public void testExpressionsBeanResolver() { - BeanFactory bf = mock(BeanFactory.class); + public void testExpressionsBeanResolver() throws Exception { + ApplicationContext context = mock(ApplicationContext.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return invocation.getArguments()[0] + "bar"; } - }).when(bf).getBean(anyString()); + }).when(context).getBean(anyString()); + when(context.containsBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)).thenReturn(true); + IntegrationEvaluationContextFactoryBean integrationEvaluationContextFactoryBean = new IntegrationEvaluationContextFactoryBean(); + integrationEvaluationContextFactoryBean.setApplicationContext(context); + integrationEvaluationContextFactoryBean.afterPropertiesSet(); + StandardEvaluationContext evalContext = integrationEvaluationContextFactoryBean.getObject(); + when(context.getBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, StandardEvaluationContext.class)) + .thenReturn(evalContext); RabbitTemplate template = mock(RabbitTemplate.class); AmqpOutboundEndpoint endpoint = new AmqpOutboundEndpoint(template); endpoint.setRoutingKeyExpression("@foo"); endpoint.setExchangeNameExpression("@bar"); endpoint.setConfirmCorrelationExpression("@baz"); - endpoint.setBeanFactory(bf); + endpoint.setBeanFactory(context); endpoint.afterPropertiesSet(); Message message = new GenericMessage("Hello, world!"); assertEquals("foobar", TestUtils.getPropertyValue(endpoint, "routingKeyGenerator", MessageProcessor.class) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java b/spring-integration-core/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java index fc1cf8db6b..ddc02255df 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-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. @@ -24,7 +24,9 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; -import org.springframework.context.expression.MapAccessor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.expression.EvaluationException; @@ -37,6 +39,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.core.MessagingTemplate; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.support.channel.ChannelResolver; import org.springframework.util.Assert; @@ -47,11 +50,11 @@ import org.springframework.util.StringUtils; * payload of the published Message can be derived from arguments or any return * value or exception resulting from the method invocation. That mapping is the * responsibility of the EL expression provided by the {@link PublisherMetadataSource}. - * + * * @author Mark Fisher * @since 2.0 */ -public class MessagePublishingInterceptor implements MethodInterceptor { +public class MessagePublishingInterceptor implements MethodInterceptor, BeanFactoryAware { private final MessagingTemplate messagingTemplate = new MessagingTemplate(); @@ -61,6 +64,8 @@ public class MessagePublishingInterceptor implements MethodInterceptor { private volatile ChannelResolver channelResolver; + private volatile BeanFactory beanFactory; + private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); @@ -83,10 +88,14 @@ public class MessagePublishingInterceptor implements MethodInterceptor { this.channelResolver = channelResolver; } + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + public final Object invoke(final MethodInvocation invocation) throws Throwable { Assert.notNull(this.metadataSource, "PublisherMetadataSource is required."); - final StandardEvaluationContext context = new StandardEvaluationContext(); - context.addPropertyAccessor(new MapAccessor()); + final StandardEvaluationContext context = ExpressionUtils.createStandardEvaluationContext(this.beanFactory); Class targetClass = AopUtils.getTargetClass(invocation.getThis()); final Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); String[] argumentNames = this.resolveArgumentNames(method); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aop/PublisherAnnotationAdvisor.java b/spring-integration-core/src/main/java/org/springframework/integration/aop/PublisherAnnotationAdvisor.java index 1a05f742fb..35b454afcc 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/aop/PublisherAnnotationAdvisor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/aop/PublisherAnnotationAdvisor.java @@ -74,6 +74,7 @@ public class PublisherAnnotationAdvisor extends AbstractPointcutAdvisor implemen public void setBeanFactory(BeanFactory beanFactory) { this.interceptor.setChannelResolver(new BeanFactoryChannelResolver(beanFactory)); + this.interceptor.setBeanFactory(beanFactory); } public Advice getAdvice() { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java new file mode 100644 index 0000000000..d1f7b0b510 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java @@ -0,0 +1,134 @@ +/* + * 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.config; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.context.expression.MapAccessor; +import org.springframework.core.convert.ConversionService; +import org.springframework.expression.BeanResolver; +import org.springframework.expression.PropertyAccessor; +import org.springframework.expression.TypeConverter; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.expression.spel.support.StandardTypeConverter; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.util.Assert; + +/** + * @author Artem Bilan + * @author Gary Russell + * @since 3.0 + */ +public class IntegrationEvaluationContextFactoryBean implements FactoryBean, + ApplicationContextAware, BeanFactoryAware, InitializingBean { + + private volatile List propertyAccessors = new ArrayList(); + + private TypeConverter typeConverter = new StandardTypeConverter(); + + private volatile Map functions = new LinkedHashMap(); + + private ApplicationContext applicationContext; + + private BeanFactory beanFactory; + + private BeanResolver beanResolver; + + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + public void afterPropertiesSet() throws Exception { + this.beanResolver = new BeanFactoryResolver(this.applicationContext != null ? this.applicationContext : this.beanFactory); + this.loadDefaultPropertyAccessors(this.propertyAccessors); + if (this.applicationContext != null) { + ConversionService conversionService = IntegrationContextUtils.getConversionService(this.applicationContext); + if (conversionService != null) { + this.typeConverter = new StandardTypeConverter(conversionService); + } + } + } + + public void setPropertyAccessors(PropertyAccessor... accessors) { + Assert.noNullElements(accessors, "Cannot have null elements in accessors"); + List propertyAccessors = new ArrayList(); + loadDefaultPropertyAccessors(propertyAccessors); + for (PropertyAccessor accessor : accessors) { + propertyAccessors.add(accessor); + } + this.propertyAccessors = propertyAccessors; + } + + private void loadDefaultPropertyAccessors(List propertyAccessors) { + propertyAccessors.add(new MapAccessor()); + } + + public void setFunctions(Map functionsArg) { + Map functions = new LinkedHashMap(); + for (Entry function : functionsArg.entrySet()) { + Assert.notNull(function.getValue(), "Method cannot be null"); + functions.put(function.getKey(), function.getValue()); + } + this.functions = functions; + } + + @Override + public StandardEvaluationContext getObject() throws Exception { + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + for (PropertyAccessor propertyAccessor : this.propertyAccessors) { + evaluationContext.addPropertyAccessor(propertyAccessor); + } + evaluationContext.setBeanResolver(this.beanResolver); + evaluationContext.setTypeConverter(this.typeConverter); + for (Entry functionEntry : this.functions.entrySet()) { + evaluationContext.registerFunction(functionEntry.getKey(), functionEntry.getValue()); + } + return evaluationContext; + } + + @Override + public Class getObjectType() { + return StandardEvaluationContext.class; + } + + @Override + public boolean isSingleton() { + return false; + } + +} 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 b23c6e0b88..96506e9b7b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-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. @@ -32,15 +32,18 @@ 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 * scheduling if those beans have not already been explicitly defined within the registry. It also registers a single * null channel with the bean name "nullChannel". - * + * * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan */ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @@ -61,6 +64,10 @@ 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 '" @@ -71,6 +78,16 @@ 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(); @@ -113,7 +130,7 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce } /** - * Register an error channel in the given BeanDefinitionRegistry. + * Register an error channel in the given BeanDefinitionRegistry. */ private void registerErrorChannel(BeanDefinitionRegistry registry) { if (logger.isInfoEnabled()) { @@ -139,7 +156,7 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce } /** - * Register a TaskScheduler in the given BeanDefinitionRegistry. + * Register a TaskScheduler in the given BeanDefinitionRegistry. */ private void registerTaskScheduler(BeanDefinitionRegistry registry) { if (logger.isInfoEnabled()) { 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 56bc5b06de..184513990f 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 @@ -18,6 +18,7 @@ package org.springframework.integration.context; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.convert.ConversionService; +import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.MessageChannel; import org.springframework.integration.store.metadata.MetadataStore; import org.springframework.scheduling.TaskScheduler; @@ -28,6 +29,7 @@ import org.springframework.util.Assert; * * @author Mark Fisher * @author Josh Long + * @author Artem Bilan */ public abstract class IntegrationContextUtils { @@ -41,6 +43,7 @@ public abstract class IntegrationContextUtils { public static final String INTEGRATION_CONVERSION_SERVICE_BEAN_NAME = "integrationConversionService"; + public static final String INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME = "integrationEvaluationContext"; /** * Return the {@link MetadataStore} bean whose name is "metadataStore". @@ -85,6 +88,14 @@ public abstract class IntegrationContextUtils { return getBeanOfType(beanFactory, INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, ConversionService.class); } + /** + * Return the instance of {@link StandardEvaluationContext} bean whose name is "integrationEvaluationContext" . + * @param beanFactory BeanFactory for lookup, must not be null. + */ + public static StandardEvaluationContext getEvaluationContext(BeanFactory beanFactory) { + return getBeanOfType(beanFactory, INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, StandardEvaluationContext.class); + } + private static T getBeanOfType(BeanFactory beanFactory, String beanName, Class type) { Assert.notNull(beanFactory, "BeanFactory must not be null"); if (!beanFactory.containsBean(beanName)) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java index 3852267b36..f3f7756e60 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java @@ -140,7 +140,7 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo this.taskScheduler = taskScheduler; } - protected final ConversionService getConversionService() { + public final ConversionService getConversionService() { if (this.conversionService == null && this.beanFactory != null) { synchronized (this) { if (this.conversionService == null) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ExpressionMessageProducerSupport.java b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ExpressionMessageProducerSupport.java index 8c26c17947..a493786451 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ExpressionMessageProducerSupport.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/ExpressionMessageProducerSupport.java @@ -12,9 +12,11 @@ */ package org.springframework.integration.endpoint; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.expression.ExpressionUtils; +import org.springframework.integration.expression.IntegrationEvaluationContextAware; /** * A {@link MessageProducerSupport} sub-class that provides {@linkplain #payloadExpression} @@ -22,15 +24,18 @@ import org.springframework.integration.endpoint.MessageProducerSupport; * * @author David Turanski * @author Artem Bilan + * @author Gary Russell * @since 2.1 * */ -public abstract class ExpressionMessageProducerSupport extends MessageProducerSupport { +public abstract class ExpressionMessageProducerSupport extends MessageProducerSupport implements IntegrationEvaluationContextAware { private final SpelExpressionParser parser = new SpelExpressionParser(); private volatile Expression payloadExpression; + private volatile EvaluationContext evaluationContext; + public void setPayloadExpression(String payloadExpression) { if (payloadExpression == null) { this.payloadExpression = null; @@ -40,10 +45,23 @@ public abstract class ExpressionMessageProducerSupport extends MessageProducerSu } } + @Override + public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) { + this.evaluationContext = evaluationContext; + } + + @Override + protected void onInit() { + super.onInit(); + if (this.evaluationContext == null) { + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); + } + } + protected Object evaluatePayloadExpression(Object payload){ Object evaluationResult = payload; if (payloadExpression != null) { - evaluationResult = payloadExpression.getValue(payload); + evaluationResult = payloadExpression.getValue(this.evaluationContext, payload); } return evaluationResult; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java index 2e0be55e57..a6ab8b166c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/expression/ExpressionUtils.java @@ -16,11 +16,12 @@ package org.springframework.integration.expression; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.BeanFactory; -import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; -import org.springframework.expression.BeanResolver; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.integration.context.IntegrationContextUtils; @@ -36,26 +37,7 @@ import org.springframework.integration.context.IntegrationContextUtils; */ public abstract class ExpressionUtils { - /** - * Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its - * property accessor property. - * @return the evaluation context. - */ - public static StandardEvaluationContext createStandardEvaluationContext() { - return createStandardEvaluationContext(null, null); - } - - /** - * Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its - * property accessor property and the supplied {@link BeanResolver} in its - * beanResolver property. - * @param beanResolver the bean resolver. - * @return the evaluation context. - */ - public static StandardEvaluationContext createStandardEvaluationContext(BeanResolver beanResolver) { - return createStandardEvaluationContext(beanResolver, null); - } - + private static final Log logger = LogFactory.getLog(ExpressionUtils.class); /** * Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its * property accessor property and the supplied {@link ConversionService} in its @@ -63,26 +45,9 @@ public abstract class ExpressionUtils { * @param conversionService the conversion service. * @return the evaluation context. */ - public static StandardEvaluationContext createStandardEvaluationContext(ConversionService conversionService) { - return createStandardEvaluationContext(null, conversionService); - } - - /** - * Create a {@link StandardEvaluationContext} with a {@link MapAccessor} in its - * property accessor property, the supplied {@link BeanResolver} in its - * beanResolver property, and the supplied {@link ConversionService} in its - * conversionService property. - * @param beanResolver the bean resolver. - * @param conversionService the conversion service. - * @return the evaluation context. - */ - public static StandardEvaluationContext createStandardEvaluationContext(BeanResolver beanResolver, - ConversionService conversionService) { + private static StandardEvaluationContext createStandardEvaluationContext(ConversionService conversionService) { StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); evaluationContext.addPropertyAccessor(new MapAccessor()); - if (beanResolver != null) { - evaluationContext.setBeanResolver(beanResolver); - } if (conversionService != null) { evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService)); } @@ -90,14 +55,39 @@ public abstract class ExpressionUtils { } /** - * Creates {@link BeanFactoryResolver}, extracts {@link ConversionService} and delegates to - * {@link #createStandardEvaluationContext(BeanResolver, ConversionService)} - * - * @param beanFactory the beanFactory. - * @return the evaluation context. + * Used to create a context with no BeanFactory, usually in tests. + * @return The evaluation context. + */ + public static StandardEvaluationContext createStandardEvaluationContext() { + return doCreateContext(null); + } + + /** + * Obtains the context from the beanFactory if not null; emits a warning if the beanFactory + * is null. + * @param beanFactory + * @return The evaluation context. */ public static StandardEvaluationContext createStandardEvaluationContext(BeanFactory beanFactory) { - return createStandardEvaluationContext(new BeanFactoryResolver(beanFactory), - IntegrationContextUtils.getConversionService(beanFactory)); + if (beanFactory == null) { + logger.warn("Creating EvaluationContext with no beanFactory", new RuntimeException("No beanfactory")); + } + return doCreateContext(beanFactory); } + + private static StandardEvaluationContext doCreateContext(BeanFactory beanFactory) { + ConversionService conversionService = null; + StandardEvaluationContext evaluationContext = null; + if (beanFactory != null) { + evaluationContext = IntegrationContextUtils.getEvaluationContext(beanFactory); + } + if (evaluationContext == null) { + if (beanFactory != null) { + conversionService = IntegrationContextUtils.getConversionService(beanFactory); + } + evaluationContext = createStandardEvaluationContext(conversionService); + } + return evaluationContext; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAware.java b/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAware.java new file mode 100644 index 0000000000..8c7663fced --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAware.java @@ -0,0 +1,29 @@ +/* + * 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 org.springframework.expression.EvaluationContext; + +/** + * @author Artem Bilan + * @since 3.0 + */ +public interface IntegrationEvaluationContextAware { + + void setIntegrationEvaluationContext(EvaluationContext evaluationContext); + +} 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 new file mode 100644 index 0000000000..c7b41c98f7 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/expression/IntegrationEvaluationContextAwareBeanPostProcessor.java @@ -0,0 +1,50 @@ +/* + * 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 org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.context.IntegrationContextUtils; + +/** + * @author Artem Bilan + * @since 3.0 + */ +public class IntegrationEvaluationContextAwareBeanPostProcessor implements BeanPostProcessor { + + private final BeanFactory beanFactory; + + public IntegrationEvaluationContextAwareBeanPostProcessor(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof IntegrationEvaluationContextAware) { + StandardEvaluationContext evaluationContext = IntegrationContextUtils.getEvaluationContext(this.beanFactory); + ((IntegrationEvaluationContextAware) bean).setIntegrationEvaluationContext(evaluationContext); + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayMethodInboundMessageMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayMethodInboundMessageMapper.java index e49298b0e4..074cb4c675 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayMethodInboundMessageMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayMethodInboundMessageMapper.java @@ -28,11 +28,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.expression.BeanResolver; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -42,6 +40,7 @@ import org.springframework.integration.MessagingException; import org.springframework.integration.annotation.Header; import org.springframework.integration.annotation.Headers; import org.springframework.integration.annotation.Payload; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.mapping.InboundMessageMapper; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; @@ -87,9 +86,9 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper parameterPayloadExpressions = new HashMap(); - private final StandardEvaluationContext staticEvaluationContext = new StandardEvaluationContext(); + private volatile StandardEvaluationContext payloadExpressionEvaluationContext; - private volatile BeanResolver beanResolver; + private volatile BeanFactory beanFactory; public GatewayMethodInboundMessageMapper(Method method) { @@ -111,8 +110,8 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper message) throws Exception { switch (this.level) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java index 9d19ea4c17..c00647cd76 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java @@ -15,7 +15,6 @@ */ package org.springframework.integration.handler.advice; -import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -172,13 +171,7 @@ public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHan } protected StandardEvaluationContext createEvaluationContext(){ - if (this.getBeanFactory() != null) { - return ExpressionUtils.createStandardEvaluationContext(new BeanFactoryResolver(this.getBeanFactory()), - this.getConversionService()); - } - else { - return ExpressionUtils.createStandardEvaluationContext(this.getConversionService()); - } + return ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); } /** diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/SpelExpressionRetryStateGenerator.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/SpelExpressionRetryStateGenerator.java index 5b291a4dd0..a6b447c4c0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/SpelExpressionRetryStateGenerator.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/advice/SpelExpressionRetryStateGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-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. @@ -19,7 +19,6 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.classify.Classifier; -import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -37,7 +36,7 @@ import org.springframework.util.Assert; */ public class SpelExpressionRetryStateGenerator implements RetryStateGenerator, BeanFactoryAware { - private final StandardEvaluationContext evaluationContext; + private volatile StandardEvaluationContext evaluationContext; private final Expression keyExpression; @@ -62,7 +61,7 @@ public class SpelExpressionRetryStateGenerator implements RetryStateGenerator, B } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory); } public void setClassifier(Classifier classifier) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transaction/ExpressionEvaluatingTransactionSynchronizationProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/transaction/ExpressionEvaluatingTransactionSynchronizationProcessor.java index 5c358401a6..4a1101f747 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transaction/ExpressionEvaluatingTransactionSynchronizationProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transaction/ExpressionEvaluatingTransactionSynchronizationProcessor.java @@ -14,7 +14,6 @@ package org.springframework.integration.transaction; import java.util.Map.Entry; -import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; @@ -180,13 +179,7 @@ public class ExpressionEvaluatingTransactionSynchronizationProcessor extends Int return evaluationContextToUse; } - protected StandardEvaluationContext createEvaluationContext(){ - if (this.getBeanFactory() != null) { - return ExpressionUtils.createStandardEvaluationContext(new BeanFactoryResolver(this.getBeanFactory()), - this.getConversionService()); - } - else { - return ExpressionUtils.createStandardEvaluationContext(this.getConversionService()); - } + protected StandardEvaluationContext createEvaluationContext() { + return ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java index 749212b4d9..fa81fbb07d 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/ContentEnricher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-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. @@ -20,10 +20,8 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; -import org.springframework.beans.factory.BeanFactory; import org.springframework.context.Lifecycle; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; @@ -31,6 +29,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.expression.ExpressionUtils; +import org.springframework.integration.expression.IntegrationEvaluationContextAware; import org.springframework.integration.gateway.MessagingGatewaySupport; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.support.MessageBuilder; @@ -46,17 +46,18 @@ import org.springframework.util.ReflectionUtils; * @author Mark Fisher * @author Gunnar Hillert * @author Gary Russell + * @author Artem Bilan * @since 2.1 */ -public class ContentEnricher extends AbstractReplyProducingMessageHandler implements Lifecycle { +public class ContentEnricher extends AbstractReplyProducingMessageHandler implements Lifecycle, IntegrationEvaluationContextAware { private final Map propertyExpressions = new HashMap(); private final SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); - private final StandardEvaluationContext sourceEvaluationContext = new StandardEvaluationContext(); + private EvaluationContext sourceEvaluationContext; - private final StandardEvaluationContext targetEvaluationContext = new StandardEvaluationContext(); + private EvaluationContext targetEvaluationContext; private volatile boolean shouldClonePayload = false; @@ -69,7 +70,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem private volatile Gateway gateway = null; private volatile Long requestTimeout; - + private volatile Long replyTimeout; /** @@ -111,20 +112,20 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem } /** - * Set the timeout value for sending request messages. If not explicitly + * Set the timeout value for sending request messages. If not explicitly * configured, the default is one second. - * + * * @param requestTimeout the timeout value in milliseconds. Must not be null. */ public void setRequestTimeout(Long requestTimeout) { Assert.notNull(requestTimeout, "requestTimeout must not be null"); this.requestTimeout = requestTimeout; } - + /** - * Set the timeout value for receiving reply messages. If not explicitly + * Set the timeout value for receiving reply messages. If not explicitly * configured, the default is one second. - * + * * @param replyTimeout the timeout value in milliseconds. Must not be null. */ public void setReplyTimeout(Long replyTimeout) { @@ -168,6 +169,10 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem this.shouldClonePayload = shouldClonePayload; } + @Override + public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) { + this.sourceEvaluationContext = evaluationContext; + } /** * Initializes the Content Enricher. Will instantiate an internal Gateway if @@ -182,29 +187,34 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem if (this.requestChannel != null) { this.gateway = new Gateway(); this.gateway.setRequestChannel(requestChannel); - + if (this.requestTimeout != null) { this.gateway.setRequestTimeout(this.requestTimeout); } - + if (this.replyTimeout != null) { this.gateway.setReplyTimeout(this.replyTimeout); } - + if (replyChannel != null) { this.gateway.setReplyChannel(replyChannel); } - + this.gateway.afterPropertiesSet(); } - this.sourceEvaluationContext.addPropertyAccessor(new MapAccessor()); - this.targetEvaluationContext.addPropertyAccessor(new MapAccessor()); - BeanFactory beanFactory = this.getBeanFactory(); - if (beanFactory != null) { - this.sourceEvaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); + + if (this.sourceEvaluationContext == null) { + this.sourceEvaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); } + + StandardEvaluationContext targetContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); + // bean resolution is NOT allowed for the target of the enrichment + targetContext.setBeanResolver(null); + this.targetEvaluationContext = targetContext; + } + @Override protected Object handleRequestMessage(Message requestMessage) { final Object requestPayload = requestMessage.getPayload(); @@ -249,7 +259,6 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem return targetPayload; } - /** * Lifecycle implementation. If no requestChannel is defined, this method * has no effect as in that case no Gateway is initialized. @@ -283,7 +292,6 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem } } - /** * Internal gateway implementation for request/reply handling. * Simply exposes the sendAndReceiveMessage method. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java b/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java index 544f1ec6d6..a833328e25 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/AbstractExpressionEvaluator.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 the original author or authors. - * + * Copyright 2002-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. @@ -18,10 +18,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.InitializingBean; import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; -import org.springframework.expression.BeanResolver; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; @@ -29,47 +28,41 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.expression.ExpressionUtils; /** * @author Mark Fisher * @author Dave Syer * @author Oleg Zhurakousky - * + * @author Artem Bilan + * @author Gary Russell + * * @since 2.0 */ -public abstract class AbstractExpressionEvaluator implements BeanFactoryAware { - +public abstract class AbstractExpressionEvaluator implements BeanFactoryAware, InitializingBean { + private final Log logger = LogFactory.getLog(this.getClass()); - private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile StandardEvaluationContext evaluationContext; private final ExpressionParser expressionParser = new SpelExpressionParser(); private final BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter(); - private volatile BeanResolver beanResolver; - - public AbstractExpressionEvaluator() { - this.evaluationContext.setTypeConverter(this.typeConverter); - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - } + private volatile BeanFactory beanFactory; /** * Specify a BeanFactory in order to enable resolution via @beanName in the expression. */ public void setBeanFactory(final BeanFactory beanFactory) { if (beanFactory != null) { + this.beanFactory = beanFactory; this.typeConverter.setBeanFactory(beanFactory); - if (beanResolver == null) { + if (this.evaluationContext != null && this.evaluationContext.getBeanResolver() == null) { this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); } } } - - public void setBeanResolver(BeanResolver beanResolver) { - this.beanResolver = beanResolver; - this.evaluationContext.setBeanResolver(beanResolver); - } public void setConversionService(ConversionService conversionService) { if (conversionService != null) { @@ -77,7 +70,32 @@ public abstract class AbstractExpressionEvaluator implements BeanFactoryAware { } } + @Override + public void afterPropertiesSet() throws Exception { + getEvaluationContext(); + } + protected StandardEvaluationContext getEvaluationContext() { + return this.getEvaluationContext(true); + } + + /** + * Emits a WARN log if the beanFactory field is null, unless the argument is false. + * @param beanFactoryRequired set to false to suppress the warning. + * @return The evaluation context. + */ + protected final StandardEvaluationContext getEvaluationContext(boolean beanFactoryRequired) { + if (this.evaluationContext == null) { + if (this.beanFactory == null && !beanFactoryRequired) { + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); + } + else { + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.beanFactory); + } + if (this.typeConverter != null) { + this.evaluationContext.setTypeConverter(this.typeConverter); + } + } return this.evaluationContext; } @@ -107,7 +125,7 @@ public abstract class AbstractExpressionEvaluator implements BeanFactoryAware { } protected T evaluateExpression(String expression, Object input, Class expectedType) { - return this.expressionParser.parseExpression(expression).getValue(this.evaluationContext, input, expectedType); + return this.expressionParser.parseExpression(expression).getValue(this.getEvaluationContext(), input, expectedType); } protected Object evaluateExpression(Expression expression, Object input) { @@ -115,15 +133,15 @@ public abstract class AbstractExpressionEvaluator implements BeanFactoryAware { } protected T evaluateExpression(Expression expression, Class expectedType) { - return expression.getValue(this.evaluationContext, expectedType); + return expression.getValue(this.getEvaluationContext(), expectedType); } protected Object evaluateExpression(Expression expression) { - return expression.getValue(this.evaluationContext); + return expression.getValue(this.getEvaluationContext()); } protected T evaluateExpression(Expression expression, Object input, Class expectedType) { - return expression.getValue(this.evaluationContext, input, expectedType); + return expression.getValue(this.getEvaluationContext(), input, expectedType); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java index 640cf5ff39..0067fab170 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java @@ -36,6 +36,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; @@ -158,7 +159,7 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator this.targetObject = targetObject; this.handlerMethods = Collections., HandlerMethod> singletonMap(handlerMethod.getTargetParameterType() .getObjectType(), handlerMethod); - this.prepareEvaluationContext(this.getEvaluationContext(), method, annotationType); + this.prepareEvaluationContext(this.getEvaluationContext(false), method, annotationType); this.setDisplayString(targetObject, method); } @@ -170,7 +171,7 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator this.targetObject = targetObject; this.requiresReply = expectedType != null; this.handlerMethods = this.findHandlerMethodsForTarget(targetObject, annotationType, methodName, requiresReply); - this.prepareEvaluationContext(this.getEvaluationContext(), methodName, annotationType); + this.prepareEvaluationContext(this.getEvaluationContext(false), methodName, annotationType); this.setDisplayString(targetObject, methodName); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/aop/PublisherExpressionTests.java b/spring-integration-core/src/test/java/org/springframework/integration/aop/PublisherExpressionTests.java index 6ff5303580..1106317967 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/aop/PublisherExpressionTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/aop/PublisherExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-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. @@ -24,14 +24,18 @@ import org.junit.Test; import org.springframework.aop.framework.ProxyFactory; import org.springframework.context.support.StaticApplicationContext; +import org.springframework.expression.EvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.annotation.Header; import org.springframework.integration.annotation.Payload; import org.springframework.integration.annotation.Publisher; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; +import org.springframework.integration.context.IntegrationContextUtils; /** * @author Mark Fisher + * @author Gary Russell * @since 2.0 */ public class PublisherExpressionTests { @@ -40,8 +44,14 @@ public class PublisherExpressionTests { @Before - public void setup() { + public void setup() throws Exception { context.registerSingleton("testChannel", QueueChannel.class); + IntegrationEvaluationContextFactoryBean factory = new IntegrationEvaluationContextFactoryBean(); + factory.setBeanFactory(context); + factory.afterPropertiesSet(); + EvaluationContext ec = factory.getObject(); + context.getBeanFactory().registerSingleton(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, ec); + context.getBeanFactory().registerSingleton("foo", "foo"); } @@ -56,8 +66,8 @@ public class PublisherExpressionTests { TestBean proxy = (TestBean) pf.getProxy(); proxy.test("123"); Message message = testChannel.receive(0); - assertNotNull(message); - assertEquals("hello", message.getPayload()); + assertNotNull(message); + assertEquals("hellofoo", message.getPayload()); assertEquals("123", message.getHeaders().get("foo")); } @@ -70,7 +80,7 @@ public class PublisherExpressionTests { static class TestBeanImpl implements TestBean { @Publisher - @Payload("#return") + @Payload("#return + @foo") public String test(@Header("foo") String foo) { return "hello"; } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/LoggingChannelAdapterParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/LoggingChannelAdapterParserTests.java index 8f15c933aa..5acff17e8f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/LoggingChannelAdapterParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/LoggingChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-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. @@ -17,11 +17,13 @@ package org.springframework.integration.config.xml; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -67,6 +69,7 @@ public class LoggingChannelAdapterParserTests { assertEquals(Ordered.LOWEST_PRECEDENCE, TestUtils.getPropertyValue(loggingHandler, "order")); assertEquals("INFO", TestUtils.getPropertyValue(loggingHandler, "level").toString()); assertEquals("payload.foo", TestUtils.getPropertyValue(loggingHandler, "expression.expression")); + assertNotNull(TestUtils.getPropertyValue(loggingHandler, "evaluationContext.beanResolver")); } @Test diff --git a/spring-integration-core/src/test/java/org/springframework/integration/expression/ExpressionUtilsTests.java b/spring-integration-core/src/test/java/org/springframework/integration/expression/ExpressionUtilsTests.java new file mode 100644 index 0000000000..010491f8b3 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/expression/ExpressionUtilsTests.java @@ -0,0 +1,96 @@ +/* + * 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.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import org.junit.Test; + +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.support.ConversionServiceFactoryBean; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.expression.TypeConverter; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class ExpressionUtilsTests { + + @Test + public void testEvaluationContext() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, + new RootBeanDefinition(ConversionServiceFactoryBean.class)); + context.refresh(); + StandardEvaluationContext evalContext = ExpressionUtils.createStandardEvaluationContext(context); + assertNotNull(evalContext.getBeanResolver()); + assertNotNull(evalContext.getTypeConverter()); + IntegrationEvaluationContextFactoryBean factory = context.getBean("&" + IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + IntegrationEvaluationContextFactoryBean.class); + assertSame(evalContext.getTypeConverter(), TestUtils.getPropertyValue(factory, "typeConverter")); + } + + + @Test + public void testEvaluationContextDefaultTypeConverter() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); + context.refresh(); + StandardEvaluationContext evalContext = ExpressionUtils.createStandardEvaluationContext(context); + assertNotNull(evalContext.getBeanResolver()); + TypeConverter typeConverter = evalContext.getTypeConverter(); + assertNotNull(typeConverter); + assertSame(TestUtils.getPropertyValue(typeConverter, "defaultConversionService"), TestUtils.getPropertyValue(typeConverter, "conversionService")); + } + + @Test + public void testEvaluationContextNoFactoryBean() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME, + new RootBeanDefinition(ConversionServiceFactoryBean.class)); + context.refresh(); + StandardEvaluationContext evalContext = ExpressionUtils.createStandardEvaluationContext(context); + assertNull(evalContext.getBeanResolver()); + TypeConverter typeConverter = evalContext.getTypeConverter(); + assertNotNull(typeConverter); + assertNotSame(TestUtils.getPropertyValue(typeConverter, "defaultConversionService"), + TestUtils.getPropertyValue(typeConverter, "conversionService")); + assertSame(context.getBean(IntegrationContextUtils.INTEGRATION_CONVERSION_SERVICE_BEAN_NAME), + TestUtils.getPropertyValue(typeConverter, "conversionService")); + } + + @Test + public void testEvaluationContextNoBeanFactory() { + StandardEvaluationContext evalContext = ExpressionUtils.createStandardEvaluationContext(); + assertNull(evalContext.getBeanResolver()); + TypeConverter typeConverter = evalContext.getTypeConverter(); + assertNotNull(typeConverter); + assertSame(TestUtils.getPropertyValue(typeConverter, "defaultConversionService"), + TestUtils.getPropertyValue(typeConverter, "conversionService")); + } +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayProxyMessageMappingTests.java b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayProxyMessageMappingTests.java index 100c865f25..be5a3bdd11 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayProxyMessageMappingTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayProxyMessageMappingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-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. @@ -34,9 +34,12 @@ import org.springframework.integration.annotation.Header; import org.springframework.integration.annotation.Headers; import org.springframework.integration.annotation.Payload; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; +import org.springframework.integration.context.IntegrationContextUtils; /** * @author Mark Fisher + * @author Gary Russell * @since 2.0 */ public class GatewayProxyMessageMappingTests { @@ -52,6 +55,11 @@ public class GatewayProxyMessageMappingTests { factoryBean.setServiceInterface(TestGateway.class); factoryBean.setDefaultRequestChannel(channel); factoryBean.setBeanName("testGateway"); + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); + context.refresh(); + factoryBean.setBeanFactory(context); factoryBean.afterPropertiesSet(); this.gateway = (TestGateway) factoryBean.getObject(); } @@ -136,6 +144,8 @@ public class GatewayProxyMessageMappingTests { gatewayDefinition.getPropertyValues().add("serviceInterface", TestGateway.class); context.registerBeanDefinition("testGateway", gatewayDefinition); context.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); context.refresh(); TestGateway gateway = context.getBean("testGateway", TestGateway.class); gateway.payloadAnnotationAtMethodLevelUsingBeanResolver("foo"); @@ -160,6 +170,8 @@ public class GatewayProxyMessageMappingTests { gatewayDefinition.getPropertyValues().add("serviceInterface", TestGateway.class); context.registerBeanDefinition("testGateway", gatewayDefinition); context.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); context.refresh(); TestGateway gateway = context.getBean("testGateway", TestGateway.class); gateway.payloadAnnotationWithExpressionUsingBeanResolver("foo"); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java index 668f0b7f10..4d6e067dd5 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java @@ -21,12 +21,13 @@ import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; import org.junit.Rule; import org.junit.Test; -import org.hamcrest.TypeSafeMatcher; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.StaticApplicationContext; @@ -38,6 +39,8 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.Message; +import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; +import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; @@ -69,7 +72,7 @@ public class ExpressionEvaluatingMessageProcessorTests { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test - public void testProcessMessageWithParameterCoercion() { + public void testProcessMessageWithParameterCoercion() throws Exception { @SuppressWarnings("unused") class TestTarget { public String stringify(int number) { @@ -78,6 +81,7 @@ public class ExpressionEvaluatingMessageProcessorTests { } Expression expression = expressionParser.parseExpression("#target.stringify(payload)"); ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression); + processor.afterPropertiesSet(); EvaluationContext evaluationContext = TestUtils.getPropertyValue(processor, "evaluationContext", EvaluationContext.class); evaluationContext.setVariable("target", new TestTarget()); assertEquals("2", processor.processMessage(new GenericMessage("2"))); @@ -85,7 +89,7 @@ public class ExpressionEvaluatingMessageProcessorTests { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test - public void testProcessMessageWithVoidResult() { + public void testProcessMessageWithVoidResult() throws Exception { @SuppressWarnings("unused") class TestTarget { public void ping(String input) { @@ -93,6 +97,7 @@ public class ExpressionEvaluatingMessageProcessorTests { } Expression expression = expressionParser.parseExpression("#target.ping(payload)"); ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression); + processor.afterPropertiesSet(); EvaluationContext evaluationContext = TestUtils.getPropertyValue(processor, "evaluationContext", EvaluationContext.class); evaluationContext.setVariable("target", new TestTarget()); assertEquals(null, processor.processMessage(new GenericMessage("2"))); @@ -100,7 +105,7 @@ public class ExpressionEvaluatingMessageProcessorTests { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test - public void testProcessMessageWithParameterCoercionToNonPrimitive() { + public void testProcessMessageWithParameterCoercionToNonPrimitive() throws Exception { class TestTarget { @SuppressWarnings("unused") public String find(Resource[] resources) { @@ -110,7 +115,13 @@ public class ExpressionEvaluatingMessageProcessorTests { } Expression expression = expressionParser.parseExpression("#target.find(payload)"); ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression); - processor.setBeanFactory(new GenericApplicationContext().getBeanFactory()); + ConfigurableListableBeanFactory beanFactory = new GenericApplicationContext().getBeanFactory(); + processor.setBeanFactory(beanFactory); + IntegrationEvaluationContextFactoryBean factoryBean = new IntegrationEvaluationContextFactoryBean(); + factoryBean.setBeanFactory(beanFactory); + beanFactory.registerSingleton(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + factoryBean.getObject()); + processor.afterPropertiesSet(); EvaluationContext evaluationContext = TestUtils.getPropertyValue(processor, "evaluationContext", EvaluationContext.class); evaluationContext.setVariable("target", new TestTarget()); String result = (String) processor.processMessage(new GenericMessage("classpath*:*.properties")); @@ -146,28 +157,34 @@ public class ExpressionEvaluatingMessageProcessorTests { @SuppressWarnings({ "rawtypes", "unchecked" }) @Test - public void testProcessMessageWithBeanAsMethodArgument() { + public void testProcessMessageWithBeanAsMethodArgument() throws Exception { StaticApplicationContext context = new StaticApplicationContext(); BeanDefinition beanDefinition = new RootBeanDefinition(String.class); beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("bar"); context.registerBeanDefinition("testString", beanDefinition); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); Expression expression = expressionParser.parseExpression("payload.concat(@testString)"); ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression); processor.setBeanFactory(context); + processor.afterPropertiesSet(); GenericMessage message = new GenericMessage("foo"); assertEquals("foobar", processor.processMessage(message)); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test - public void testProcessMessageWithMethodCallOnBean() { + public void testProcessMessageWithMethodCallOnBean() throws Exception { StaticApplicationContext context = new StaticApplicationContext(); BeanDefinition beanDefinition = new RootBeanDefinition(String.class); beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("bar"); + context.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, + new RootBeanDefinition(IntegrationEvaluationContextFactoryBean.class)); context.registerBeanDefinition("testString", beanDefinition); Expression expression = expressionParser.parseExpression("@testString.concat(payload)"); ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression); processor.setBeanFactory(context); + processor.afterPropertiesSet(); GenericMessage message = new GenericMessage("foo"); assertEquals("barfoo", processor.processMessage(message)); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java index ea742f52a6..63c7d3752e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/ContentEnricherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-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. @@ -29,6 +29,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; + import org.springframework.beans.factory.BeanFactory; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; @@ -52,28 +53,29 @@ import org.springframework.scheduling.support.PeriodicTrigger; /** * @author Mark Fisher * @author Gunnar Hillert - * + * @author Artem Bilan + * * @since 2.1 */ public class ContentEnricherTests { - private ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); - + private final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + @Before public void init() throws Exception { taskScheduler.setPoolSize(2); taskScheduler.afterPropertiesSet(); } - + /** - * In this test a {@link Target} message is passed into an {@link ContentEnricher}. - * The Enricher passes the message to a "request-channel" that is backed by a - * {@link QueueChannel}. The consumer of the "request-channel" takes a long - * time to execute, longer actually than the specified "replyTimeout" set on + * In this test a {@link Target} message is passed into a {@link ContentEnricher}. + * The Enricher passes the message to a "request-channel" that is backed by a + * {@link QueueChannel}. The consumer of the "request-channel" takes a long + * time to execute, longer actually than the specified "replyTimeout" set on * the {@link ContentEnricher}. - * + * * Due to the occurring replyTimeout, a Null replyMessage is returned and because - * "requiresReply" is set to "true" on the {@link ContentEnricher}, a + * "requiresReply" is set to "true" on the {@link ContentEnricher}, a * {@link ReplyRequiredException} is raised. */ @Test @@ -81,26 +83,26 @@ public class ContentEnricherTests { final long requestTimeout = 500L; final long replyTimeout = 700L; - + final DirectChannel replyChannel = new DirectChannel(); final QueueChannel requestChannel = new QueueChannel(1); - + final ContentEnricher enricher = new ContentEnricher(); enricher.setRequestChannel(requestChannel); enricher.setReplyChannel(replyChannel); - - enricher.setOutputChannel(new NullChannel()); + + enricher.setOutputChannel(new NullChannel()); enricher.setRequestTimeout(requestTimeout); enricher.setReplyTimeout(replyTimeout); - + final ExpressionFactoryBean expressionFactoryBean = new ExpressionFactoryBean("payload"); expressionFactoryBean.setSingleton(false); expressionFactoryBean.afterPropertiesSet(); - + final Map expressions = new HashMap(); expressions.put("name", new LiteralExpression("cartman")); expressions.put("child.name", expressionFactoryBean.getObject()); - + enricher.setPropertyExpressions(expressions); enricher.setRequiresReply(true); enricher.setBeanName("Enricher"); @@ -117,45 +119,45 @@ public class ContentEnricherTests { } return new Target("child"); } - + }; - + handler.afterPropertiesSet(); - + final PollingConsumer consumer = new PollingConsumer(requestChannel, handler); final TestErrorHandler errorHandler = new TestErrorHandler(); - + consumer.setTrigger(new PeriodicTrigger(0)); consumer.setErrorHandler(errorHandler); consumer.setTaskScheduler(taskScheduler); consumer.setBeanFactory(mock(BeanFactory.class)); consumer.afterPropertiesSet(); consumer.start(); - + final Target target = new Target("replace me"); Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); - + try { enricher.handleMessage(requestMessage); } catch (ReplyRequiredException e) { assertEquals("No reply produced by handler 'Enricher', and its 'requiresReply' property is set to true.", e.getMessage()); return; } - + fail("ReplyRequiredException expected."); } - + @Test public void requestChannelSendTimingOut() { - + final String requestChannelName = "Request_Channel"; final long requestTimeout = 200L; - + QueueChannel replyChannel = new QueueChannel(); QueueChannel requestChannel = new RendezvousChannel(); requestChannel.setBeanName(requestChannelName); - + ContentEnricher enricher = new ContentEnricher(); enricher.setRequestChannel(requestChannel); enricher.setRequestTimeout(requestTimeout); @@ -163,17 +165,17 @@ public class ContentEnricherTests { Target target = new Target("replace me"); Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); - + try { enricher.handleMessage(requestMessage); } catch (MessageDeliveryException e) { - assertEquals("failed to send message to channel '" + requestChannelName + assertEquals("failed to send message to channel '" + requestChannelName + "' within timeout: " + requestTimeout, e.getMessage()); return; } } - + @Test public void simpleProperty() { QueueChannel replyChannel = new QueueChannel(); @@ -203,52 +205,52 @@ public class ContentEnricherTests { @Test public void setReplyChannelWithoutRequestChannel() { - + QueueChannel replyChannel = new QueueChannel(); - + ContentEnricher enricher = new ContentEnricher(); enricher.setReplyChannel(replyChannel); - - try { + + try { enricher.afterPropertiesSet(); } catch (IllegalArgumentException e) { assertEquals("If the replyChannel is set, then the requestChannel must not be null", e.getMessage()); return; } - + fail("Expected an exception."); } - + @Test public void setNullReplyTimeout() { - + ContentEnricher enricher = new ContentEnricher(); - + try { enricher.setReplyTimeout(null); } catch (IllegalArgumentException e) { assertEquals("replyTimeout must not be null", e.getMessage()); return; } - + fail("Expected an exception."); } - + @Test public void setNullRequestTimeout() { - + ContentEnricher enricher = new ContentEnricher(); - + try { enricher.setRequestTimeout(null); } catch (IllegalArgumentException e) { assertEquals("requestTimeout must not be null", e.getMessage()); return; } - + fail("Expected an exception."); } - + @Test public void testSimplePropertyWithoutUsingRequestChannel() { QueueChannel replyChannel = new QueueChannel(); @@ -257,6 +259,7 @@ public class ContentEnricherTests { Map propertyExpressions = new HashMap(); propertyExpressions.put("name", parser.parseExpression("'just a static string'")); enricher.setPropertyExpressions(propertyExpressions); + enricher.afterPropertiesSet(); Target target = new Target("replace me"); Message requestMessage = MessageBuilder.withPayload(target).setReplyChannel(replyChannel).build(); enricher.handleMessage(requestMessage); @@ -490,6 +493,7 @@ public class ContentEnricherTests { this.child = child; } + @Override public Object clone() { Target clone = new Target(this.name); clone.setChild(this.child); @@ -531,9 +535,10 @@ public class ContentEnricherTests { this.name = name; } + @Override public Object clone() { throw new IllegalStateException("Cloning not possible"); } } -} \ No newline at end of file +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml index 8a622ee906..04fba18278 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml @@ -2,10 +2,10 @@ + xmlns:util="http://www.springframework.org/schema/util" + xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> @@ -21,4 +21,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java index 3747f20f01..4c10f192b7 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java @@ -17,7 +17,9 @@ package org.springframework.integration.transformer; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import org.hamcrest.Matchers; import org.junit.Test; @@ -25,14 +27,22 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.PropertyAccessor; +import org.springframework.expression.TypedValue; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; +import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.PollableChannel; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.handler.ReplyRequiredException; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.Assert; /** * @author Mark Fisher @@ -54,6 +64,12 @@ public class SpelTransformerIntegrationTests { @Autowired private MessageChannel transformerChainInput; + @Autowired @Qualifier("foo.handler") + private AbstractReplyProducingMessageHandler fooHandler; + + @Autowired @Qualifier("bar.handler") + private AbstractReplyProducingMessageHandler barHandler; + @Test public void simple() { @@ -81,12 +97,90 @@ public class SpelTransformerIntegrationTests { } } + @Test + public void testCustomAccessor() { + QueueChannel outputChannel = new QueueChannel(); + fooHandler.setOutputChannel(outputChannel); + Foo foo = new Foo("baz"); + fooHandler.handleMessage(new GenericMessage(foo)); + Message reply = outputChannel.receive(0); + assertNotNull(reply); + assertTrue(reply.getPayload() instanceof String); + assertEquals("baz", reply.getPayload()); + } + + @Test + public void testCustomFunction() { + QueueChannel outputChannel = new QueueChannel(); + barHandler.setOutputChannel(outputChannel); + barHandler.handleMessage(new GenericMessage("foo")); + Message reply = outputChannel.receive(0); + assertNotNull(reply); + assertEquals("bar", reply.getPayload()); + } static class TestBean { public String getFoo() { return "test"; } + } + public static class Foo { + private String bar; + + public Foo(String bar) { + this.bar = bar; + } + + public String obtainBar() { + return bar; + } + + public void updateBar(String bar) { + this.bar = bar; + } + + } + + public static class FooAccessor implements PropertyAccessor { + + @Override + public Class[] getSpecificTargetClasses() { + return new Class[] {Foo.class}; + } + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return "bar".equals(name); + } + + @Override + public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { + Assert.isInstanceOf(Foo.class, target); + return new TypedValue(((Foo) target).obtainBar(), TypeDescriptor.valueOf(String.class)); + } + + @Override + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return "bar".equals(name); + } + + @Override + public void write(EvaluationContext context, Object target, String name, Object newValue) + throws AccessException { + Assert.isInstanceOf(Foo.class, target); + Assert.isInstanceOf(String.class, newValue); + ((Foo) target).updateBar((String) newValue); + } + + } + + public static class BarFunction { + + public static String bar(Message message) { + return "bar"; + } + } } diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java index 972dfb8963..9f043cee10 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java @@ -39,6 +39,7 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextStartedEvent; import org.springframework.context.event.ContextStoppedEvent; +import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; @@ -138,9 +139,24 @@ public class ApplicationEventListeningMessageProducerTests { ApplicationEventListeningMessageProducer adapter = new ApplicationEventListeningMessageProducer(); adapter.setPayloadExpression("'received: ' + source"); adapter.setOutputChannel(channel); - adapter.start(); + + GenericApplicationContext ctx = TestUtils.createTestApplicationContext(); + ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory(); + beanFactory.registerSingleton(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME, + new SimpleApplicationEventMulticaster(beanFactory)); + + adapter.setBeanFactory(beanFactory); + beanFactory.registerSingleton("testListenerMessageProducer", adapter); + adapter.afterPropertiesSet(); + + ctx.refresh(); + Message message1 = channel.receive(0); - assertNull(message1); + // ContextRefreshedEvent + assertNotNull(message1); + assertTrue(message1.getPayload().toString() + .contains("org.springframework.integration.test.util.TestUtils$TestApplicationContext")); + adapter.onApplicationEvent(new TestApplicationEvent1()); adapter.onApplicationEvent(new TestApplicationEvent2()); Message message2 = channel.receive(20); diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java index eef25fd62d..48b785a9b0 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileWritingMessageHandler.java @@ -26,15 +26,13 @@ import java.nio.charset.Charset; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.context.expression.MapAccessor; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.file.support.FileExistsMode; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.support.MessageBuilder; @@ -84,7 +82,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand private volatile FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator(); - private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile StandardEvaluationContext evaluationContext; private final Expression destinationDirectoryExpression; @@ -220,13 +218,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand super.onInit(); - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - - final BeanFactory beanFactory = this.getBeanFactory(); - - if (beanFactory != null) { - this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); - } + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); if (this.destinationDirectoryExpression instanceof LiteralExpression) { final File directory = new File(this.destinationDirectoryExpression.getValue( diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizer.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizer.java index a58147d864..b2bcab056b 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizer.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizer.java @@ -27,10 +27,12 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.InitializingBean; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; -import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.MessagingException; +import org.springframework.integration.expression.IntegrationEvaluationContextAware; import org.springframework.integration.file.filters.FileListFilter; import org.springframework.integration.file.remote.session.Session; import org.springframework.integration.file.remote.session.SessionFactory; @@ -48,13 +50,15 @@ import org.springframework.util.ObjectUtils; * @author Josh Long * @author Mark Fisher * @author Oleg Zhurakousky + * @author Gary Russell * @since 2.0 */ -public abstract class AbstractInboundFileSynchronizer implements InboundFileSynchronizer, InitializingBean { +public abstract class AbstractInboundFileSynchronizer implements InboundFileSynchronizer, + InitializingBean, IntegrationEvaluationContextAware { protected final Log logger = LogFactory.getLog(this.getClass()); - private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile EvaluationContext evaluationContext; private volatile String remoteFileSeparator = "/"; @@ -86,6 +90,7 @@ public abstract class AbstractInboundFileSynchronizer implements InboundFileS */ private volatile boolean deleteRemoteFiles; + private volatile BeanFactory beanFactory; /** * Create a synchronizer with the {@link SessionFactory} used to acquire {@link Session} instances. @@ -125,8 +130,14 @@ public abstract class AbstractInboundFileSynchronizer implements InboundFileS this.deleteRemoteFiles = deleteRemoteFiles; } + @Override + public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) { + this.evaluationContext = evaluationContext; + } + public final void afterPropertiesSet() { Assert.notNull(this.remoteDirectory, "remoteDirectory must not be null"); + Assert.notNull(this.evaluationContext, "evaluationContext must not be null"); } protected final List filterFiles(F[] files) { diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml index 63265cb8dd..058ca13978 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests-context.xml @@ -45,21 +45,29 @@ directory="${java.io.tmpdir}"/> + directory-expression="@barString"> + + + + + + + + diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests.java index 4bc148c8df..7dc76045c5 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/FileOutboundChannelAdapterParserTests.java @@ -29,6 +29,7 @@ import java.nio.charset.Charset; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.expression.Expression; @@ -189,14 +190,14 @@ public class FileOutboundChannelAdapterParserTests { String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile)); assertEquals(expectedFileContent, actualFileContent); + assertEquals(4, adviceCalled); + testFile.delete(); } @Test public void adapterUsageWithFailMode() throws Exception{ - String expectedFileContent = "Initial File Content:String content:byte[] content:File content"; - - File testFile = new File("test/fileToAppend.txt"); + File testFile = new File("test/fileToFail.txt"); if (testFile.exists()){ testFile.delete(); } @@ -207,15 +208,12 @@ public class FileOutboundChannelAdapterParserTests { usageChannelWithFailMode.send(new GenericMessage("String content:")); } catch (MessagingException e) { - + assertTrue(e.getMessage().contains("The destination file already exists at")); + testFile.delete(); return; } Assert.fail("Was expecting an Exception to be thrown."); - - - String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile)); - assertEquals(expectedFileContent, actualFileContent); } @Test @@ -224,7 +222,7 @@ public class FileOutboundChannelAdapterParserTests { String expectedFileContent = "Initial File Content:"; - File testFile = new File("test/fileToAppend.txt"); + File testFile = new File("test/fileToIgnore.txt"); if (testFile.exists()){ testFile.delete(); } @@ -234,6 +232,7 @@ public class FileOutboundChannelAdapterParserTests { String actualFileContent = new String(FileCopyUtils.copyToByteArray(testFile)); assertEquals(expectedFileContent, actualFileContent); + testFile.delete(); } diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests-context.xml b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests-context.xml index b630450762..6d2732ec76 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests-context.xml +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests-context.xml @@ -24,7 +24,7 @@ filename-pattern="*.txt" local-directory="." remote-file-separator="" - local-filename-generator-expression="#this.toUpperCase() + '.a'" + local-filename-generator-expression="#this.toUpperCase() + '.a' + @fooString" comparator="comparator" temporary-file-suffix=".foo" local-filter="acceptAllFilter" @@ -34,6 +34,10 @@ + + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests.java index 0fefc196a3..1d1a62082a 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpInboundChannelAdapterParserTests.java @@ -24,10 +24,12 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; @@ -39,11 +41,14 @@ import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.file.filters.FileListFilter; import org.springframework.integration.file.remote.session.CachingSessionFactory; import org.springframework.integration.file.remote.session.Session; +import org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer; import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter; import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizer; import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizingMessageSource; import org.springframework.integration.ftp.session.DefaultFtpSessionFactory; import org.springframework.integration.test.util.TestUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.MethodCallback; /** * @author Oleg Zhurakousky @@ -82,6 +87,18 @@ public class FtpInboundChannelAdapterParserTests { assertTrue(DefaultFtpSessionFactory.class.isAssignableFrom(sessionFactory.getClass())); FileListFilter acceptAllFilter = ac.getBean("acceptAllFilter", FileListFilter.class); assertTrue(TestUtils.getPropertyValue(inbound, "fileSource.scanner.filter.fileFilters", Collection.class).contains(acceptAllFilter)); + final AtomicReference genMethod = new AtomicReference(); + ReflectionUtils.doWithMethods(AbstractInboundFileSynchronizer.class, new MethodCallback() { + + @Override + public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + if ("generateLocalFileName".equals(method.getName())) { + method.setAccessible(true); + genMethod.set(method); + } + } + }); + assertEquals("FOO.afoo", genMethod.get().invoke(fisync, "foo")); } @Test diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizerTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizerTests.java index abb7e4e6f6..96bc62aef5 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizerTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/inbound/FtpInboundRemoteFileSystemSynchronizerTests.java @@ -16,6 +16,17 @@ package org.springframework.integration.ftp.inbound; +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.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.File; import java.io.OutputStream; import java.util.ArrayList; @@ -32,23 +43,14 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.Message; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter; import org.springframework.integration.ftp.session.AbstractFtpSessionFactory; -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.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - /** * @author Oleg Zhurakousky * @author Gunnar Hillert + * @author Gary Russell * @since 2.0 */ public class FtpInboundRemoteFileSystemSynchronizerTests { @@ -80,6 +82,7 @@ public class FtpInboundRemoteFileSystemSynchronizerTests { synchronizer.setDeleteRemoteFiles(true); synchronizer.setRemoteDirectory("remote-test-dir"); synchronizer.setFilter(new FtpRegexPatternFileListFilter(".*\\.test$")); + synchronizer.setIntegrationEvaluationContext(ExpressionUtils.createStandardEvaluationContext()); ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); Expression expression = expressionParser.parseExpression("#this.toUpperCase() + '.a'"); diff --git a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/inbound/ContinuousQueryMessageProducerTests.java b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/inbound/ContinuousQueryMessageProducerTests.java index 7d6ffe9934..bb82529a08 100644 --- a/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/inbound/ContinuousQueryMessageProducerTests.java +++ b/spring-integration-gemfire/src/test/java/org/springframework/integration/gemfire/inbound/ContinuousQueryMessageProducerTests.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 the original author or authors. - * + * Copyright 2002-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. @@ -17,11 +17,13 @@ import static org.mockito.Mockito.mock; import org.junit.Before; import org.junit.Test; + import org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer; import org.springframework.integration.Message; import org.springframework.integration.MessagingException; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.expression.ExpressionUtils; import com.gemstone.gemfire.cache.Operation; import com.gemstone.gemfire.cache.query.CqEvent; @@ -30,6 +32,7 @@ import com.gemstone.gemfire.cache.query.internal.CqQueryImpl; /** * @author David Turanski + * @author Artem Bilan * @since 2.1 */ public class ContinuousQueryMessageProducerTests { @@ -45,6 +48,7 @@ public class ContinuousQueryMessageProducerTests { cqMessageProducer = new ContinuousQueryMessageProducer(queryListenerContainer, ""); DirectChannel outputChannel = new DirectChannel(); cqMessageProducer.setOutputChannel(outputChannel); + cqMessageProducer.setIntegrationEvaluationContext(ExpressionUtils.createStandardEvaluationContext()); handler = new CqMessageHandler(); outputChannel.subscribe(handler); } @@ -140,7 +144,7 @@ public class ContinuousQueryMessageProducerTests { /* * (non-Javadoc) - * + * * @see * org.springframework.integration.core.MessageHandler#handleMessage * (org.springframework.integration.Message) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index 7917675343..34aab2fd47 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -28,7 +28,6 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.http.HttpEntity; @@ -549,14 +548,8 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa return httpStatus; } - protected StandardEvaluationContext createEvaluationContext(){ - if (this.getBeanFactory() != null) { - return ExpressionUtils.createStandardEvaluationContext(new BeanFactoryResolver(this.getBeanFactory()), - this.getConversionService()); - } - else { - return ExpressionUtils.createStandardEvaluationContext(this.getConversionService()); - } + protected StandardEvaluationContext createEvaluationContext() { + return ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); } private void validateSupportedMethods() { diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java index db2f808ac1..9de441aaae 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java @@ -28,8 +28,6 @@ import java.util.Map; import javax.xml.transform.Source; import org.springframework.beans.factory.BeanFactory; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterRegistry; @@ -50,6 +48,7 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.MessagingException; import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.mapping.HeaderMapper; @@ -88,7 +87,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe private final RestTemplate restTemplate; - private final StandardEvaluationContext evaluationContext; + private volatile StandardEvaluationContext evaluationContext; private final Expression uriExpression; @@ -156,9 +155,6 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe Assert.notNull(uriExpression, "URI Expression is required"); this.restTemplate = (restTemplate == null ? new RestTemplate() : restTemplate); this.uriExpression = uriExpression; - StandardEvaluationContext sec = new StandardEvaluationContext(); - sec.addPropertyAccessor(new MapAccessor()); - this.evaluationContext = sec; } /** @@ -295,10 +291,8 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe @Override public void onInit() { super.onInit(); - BeanFactory beanFactory = this.getBeanFactory(); - if (beanFactory != null) { - this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); - } + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); + ConversionService conversionService = this.getConversionService(); if (conversionService == null){ conversionService = new GenericConversionService(); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java index 145978948e..5a85cc1c88 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandlerTests.java @@ -44,8 +44,10 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.junit.Test; import org.mockito.Mockito; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; @@ -90,6 +92,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put("a", "1"); form.put("b", "2"); @@ -122,6 +126,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put("a", new City("Philadelphia")); form.put("b", new City("Ambler")); @@ -153,6 +159,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put(1, new City("Philadelphia")); form.put(2, new City("Ambler")); @@ -185,6 +193,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put("a", new String[] { "1", "2", "3" }); form.put("b", "4"); @@ -230,6 +240,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put("a", new int[]{1,2,3}); form.put("b", "4"); @@ -277,6 +289,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put("a", new Object[]{null, 4, null}); form.put("b", "4"); @@ -318,6 +332,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); List list = new ArrayList(); list.add(null); @@ -363,6 +379,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); List list = new ArrayList(); list.add(null); @@ -403,6 +421,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); List listA = new ArrayList(); listA.add("1"); @@ -446,6 +466,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); List listA = new ArrayList(); listA.add(new City("Philadelphia")); @@ -489,6 +511,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Map form = new LinkedHashMap(); form.put("a", null); form.put("b", "foo"); @@ -524,6 +548,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); byte[] bytes = "Hello World".getBytes(); Message message = MessageBuilder.withPayload(bytes).build(); @@ -538,7 +564,7 @@ public class HttpRequestExecutingMessageHandlerTests { HttpEntity request = template.lastRequestEntity.get(); Object body = request.getBody(); assertTrue(body instanceof byte[]); - assertEquals("Hello World", new String((byte[])bytes)); + assertEquals("Hello World", new String(bytes)); assertEquals(MediaType.APPLICATION_OCTET_STREAM, request.getHeaders().getContentType()); } @@ -548,6 +574,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); + setBeanFactory(handler); + handler.afterPropertiesSet(); Message message = MessageBuilder.withPayload(mock(Source.class)).build(); Exception exception = null; @@ -573,6 +601,7 @@ public class HttpRequestExecutingMessageHandlerTests { new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.GET); handler.setExtractPayload(true); + setBeanFactory(handler); handler.afterPropertiesSet(); // should not see a warn message since 'setExtractPayload' is not set explicitly @@ -581,6 +610,7 @@ public class HttpRequestExecutingMessageHandlerTests { template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.GET); + setBeanFactory(handler); handler.afterPropertiesSet(); // should not see a warn message since HTTP method is not GET @@ -590,6 +620,7 @@ public class HttpRequestExecutingMessageHandlerTests { new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.POST); handler.setExtractPayload(true); + setBeanFactory(handler); handler.afterPropertiesSet(); } @@ -600,6 +631,8 @@ public class HttpRequestExecutingMessageHandlerTests { MockRestTemplate template = new MockRestTemplate(); new DirectFieldAccessor(handler).setPropertyValue("restTemplate", template); handler.setHttpMethod(HttpMethod.GET); + setBeanFactory(handler); + handler.afterPropertiesSet(); Message message = MessageBuilder.withPayload(mock(Source.class)).build(); Exception exception = null; @@ -705,6 +738,8 @@ public class HttpRequestExecutingMessageHandlerTests { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler( new SpelExpressionParser().parseExpression("headers['foo']"), restTemplate); + setBeanFactory(handler); + handler.afterPropertiesSet(); String theURL = "http://bar/baz?foo#bar"; Message message = MessageBuilder.withPayload("").setHeader("foo", theURL).build(); try { @@ -721,6 +756,8 @@ public class HttpRequestExecutingMessageHandlerTests { new SpelExpressionParser().parseExpression("'http://my.RabbitMQ.com/api/' + payload"), restTemplate); handler.setEncodeUri(false); + setBeanFactory(handler); + handler.afterPropertiesSet(); Message message = MessageBuilder.withPayload("queues/%2f/si.test.queue?foo#bar").build(); try { handler.handleRequestMessage(message); @@ -779,6 +816,7 @@ public class HttpRequestExecutingMessageHandlerTests { List> converters = new ArrayList>(); converters.add(new SerializingHttpMessageConverter()); handler.setMessageConverters(converters); + setBeanFactory(handler); handler.afterPropertiesSet(); RestTemplate restTemplate = TestUtils.getPropertyValue(handler, "restTemplate", RestTemplate.class); @@ -813,6 +851,7 @@ public class HttpRequestExecutingMessageHandlerTests { List> converters = new ArrayList>(); converters.add(new SerializingHttpMessageConverter()); handler.setMessageConverters(converters); + setBeanFactory(handler); handler.afterPropertiesSet(); RestTemplate restTemplate = TestUtils.getPropertyValue(handler, "restTemplate", RestTemplate.class); @@ -836,6 +875,10 @@ public class HttpRequestExecutingMessageHandlerTests { assertEquals("x-java-serialized-object", accept.get(0).getSubtype()); } + private void setBeanFactory(HttpRequestExecutingMessageHandler handler) { + handler.setBeanFactory(mock(BeanFactory.class)); + } + private HttpHeaders setUpMocksToCaptureSentHeaders(RestTemplate restTemplate) throws IOException { HttpHeaders headers = new HttpHeaders(); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java index bd2880b1f8..b974837838 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java @@ -52,6 +52,7 @@ public class UriVariableExpressionTests { throw new RuntimeException("intentional"); } }); + handler.afterPropertiesSet(); Message message = new GenericMessage("bar"); Exception exception = null; try { diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java index 47ddbe6655..e2f1e898db 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java @@ -26,14 +26,14 @@ import javax.sql.DataSource; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.expression.BeanFactoryResolver; -import org.springframework.context.expression.MapAccessor; import org.springframework.expression.BeanResolver; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageHeaders; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; @@ -60,13 +60,15 @@ import com.google.common.cache.LoadingCache; * * @author Gunnar Hillert * @author Artem Bilan + * @author Gary Russell * @since 2.1 * */ @ManagedResource public class StoredProcExecutor implements BeanFactoryAware, InitializingBean { - private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile EvaluationContext evaluationContext; + private volatile BeanFactory beanFactory = null; private volatile int jdbcCallOperationsCacheSize = 10; @@ -220,11 +222,7 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean { this.jdbcCallOperationsCache.put(storedProcedureName, simpleJdbcCall); } - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - - if (this.beanFactory != null) { - this.evaluationContext.setBeanResolver(new BeanFactoryResolver(this.beanFactory)); - } + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory); } diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java index 737d3ccde0..99dfbff6be 100755 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java @@ -33,10 +33,12 @@ import javax.mail.internet.MimeMessage; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.DisposableBean; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.context.IntegrationObjectSupport; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.util.Assert; /** @@ -75,7 +77,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl private volatile Authenticator javaMailAuthenticator; - private final StandardEvaluationContext context = new StandardEvaluationContext(); + private volatile StandardEvaluationContext evaluationContext; private volatile Expression selectorExpression; @@ -312,7 +314,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl for (int i = 0; i < messages.length; i++) { MimeMessage message = (MimeMessage) messages[i]; if (this.selectorExpression != null) { - if (this.selectorExpression.getValue(this.context, message, Boolean.class)){ + if (this.selectorExpression.getValue(this.evaluationContext, message, Boolean.class)) { filteredMessages.add(message); } else { @@ -381,6 +383,7 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl protected void onInit() throws Exception { super.onInit(); this.folderOpenMode = Folder.READ_WRITE; + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); this.initialized = true; } diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java index 36296fcd11..9083443fe6 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java @@ -24,6 +24,10 @@ import javax.mail.URLName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.expression.Expression; @@ -40,7 +44,7 @@ import org.springframework.util.StringUtils; * @author Oleg Zhurakousky * @since 1.0.3 */ -public class MailReceiverFactoryBean implements FactoryBean, DisposableBean { +public class MailReceiverFactoryBean implements FactoryBean, DisposableBean, BeanFactoryAware { protected final Log logger = LogFactory.getLog(this.getClass()); @@ -70,6 +74,7 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo private volatile SearchTermStrategy searchTermStrategy; + private volatile BeanFactory beanFactory; public void setStoreUri(String storeUri) { this.storeUri = storeUri; @@ -115,6 +120,11 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo this.searchTermStrategy = searchTermStrategy; } + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + public MailReceiver getObject() throws Exception { if (this.receiver == null) { this.receiver = this.createReceiver(); @@ -184,6 +194,9 @@ public class MailReceiverFactoryBean implements FactoryBean, Dispo else if (isImap) { ((ImapMailReceiver) receiver).setShouldMarkMessagesAsRead(this.shouldMarkMessagesAsRead); } + if (this.beanFactory != null) { + receiver.setBeanFactory(this.beanFactory); + } receiver.afterPropertiesSet(); return receiver; } diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java index ce84e30728..c6a5931ac8 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/InboundChannelAdapterParserTests.java @@ -28,6 +28,8 @@ import javax.mail.Authenticator; import org.junit.Test; import org.junit.runner.RunWith; +import org.xml.sax.SAXParseException; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; @@ -45,7 +47,6 @@ import org.springframework.integration.mail.SearchTermStrategy; import org.springframework.integration.test.util.TestUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.xml.sax.SAXParseException; /** * @author Mark Fisher @@ -132,6 +133,7 @@ public class InboundChannelAdapterParserTests { assertEquals(ImapMailReceiver.class, receiver.getClass()); Boolean value = (Boolean) new DirectFieldAccessor(receiver).getPropertyValue("shouldDeleteMessages"); assertTrue(value); + assertNotNull(TestUtils.getPropertyValue(receiver, "evaluationContext.beanResolver")); } @Test diff --git a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/inbound/MongoDbMessageSource.java b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/inbound/MongoDbMessageSource.java index 9e82689671..c8d8c5bfca 100644 --- a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/inbound/MongoDbMessageSource.java +++ b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/inbound/MongoDbMessageSource.java @@ -168,13 +168,8 @@ public class MongoDbMessageSource extends IntegrationObjectSupport @Override protected void onInit() throws Exception { - if (this.getBeanFactory() != null){ - this.evaluationContext = + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); - } - else { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); - } if (this.mongoTemplate == null){ this.mongoTemplate = new MongoTemplate(this.mongoDbFactory, this.mongoConverter); diff --git a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbStoringMessageHandler.java b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbStoringMessageHandler.java index 32b99b5b89..f111c35c97 100644 --- a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbStoringMessageHandler.java +++ b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbStoringMessageHandler.java @@ -99,13 +99,8 @@ public class MongoDbStoringMessageHandler extends AbstractMessageHandler { @Override protected void onInit() throws Exception { - if (this.getBeanFactory() != null) { - this.evaluationContext = + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); - } - else { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); - } if (this.mongoTemplate == null){ this.mongoTemplate = new MongoTemplate(this.mongoDbFactory, this.mongoConverter); } diff --git a/spring-integration-redis/src/main/java/org/springframework/integration/redis/inbound/RedisStoreMessageSource.java b/spring-integration-redis/src/main/java/org/springframework/integration/redis/inbound/RedisStoreMessageSource.java index 767ac08733..89b3a67f39 100644 --- a/spring-integration-redis/src/main/java/org/springframework/integration/redis/inbound/RedisStoreMessageSource.java +++ b/spring-integration-redis/src/main/java/org/springframework/integration/redis/inbound/RedisStoreMessageSource.java @@ -142,13 +142,8 @@ public class RedisStoreMessageSource extends IntegrationObjectSupport @Override protected void onInit() throws Exception { - if (this.getBeanFactory() != null){ - this.evaluationContext = + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); - } - else { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); - } } public RedisStore getResource() { diff --git a/spring-integration-redis/src/main/java/org/springframework/integration/redis/outbound/RedisStoreWritingMessageHandler.java b/spring-integration-redis/src/main/java/org/springframework/integration/redis/outbound/RedisStoreWritingMessageHandler.java index 118249e5b7..b277c6ac26 100644 --- a/spring-integration-redis/src/main/java/org/springframework/integration/redis/outbound/RedisStoreWritingMessageHandler.java +++ b/spring-integration-redis/src/main/java/org/springframework/integration/redis/outbound/RedisStoreWritingMessageHandler.java @@ -23,6 +23,7 @@ import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.BoundSetOperations; @@ -199,13 +200,8 @@ public class RedisStoreWritingMessageHandler extends AbstractMessageHandler { @Override protected void onInit() throws Exception { - if (this.getBeanFactory() != null) { - this.evaluationContext = + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); - } - else { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); - } Assert.state(!this.mapKeyExpressionExplicitlySet || (this.collectionType == CollectionType.MAP || this.collectionType == CollectionType.PROPERTIES), "'mapKeyExpression' can only be set for CollectionType.MAP or CollectionType.PROPERTIES"); diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizerTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizerTests.java index b807e3bcd5..dd8842efb7 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizerTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/inbound/SftpInboundRemoteFileSystemSynchronizerTests.java @@ -16,23 +16,6 @@ package org.springframework.integration.sftp.inbound; -import java.io.File; -import java.io.FileInputStream; -import java.util.Vector; - -import org.junit.After; -import org.junit.Test; - -import org.springframework.integration.Message; -import org.springframework.integration.file.remote.session.Session; -import org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter; -import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; -import org.springframework.integration.sftp.session.SftpTestSessionFactory; - -import com.jcraft.jsch.ChannelSftp; -import com.jcraft.jsch.ChannelSftp.LsEntry; -import com.jcraft.jsch.SftpATTRS; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -44,9 +27,28 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; +import java.io.FileInputStream; +import java.util.Vector; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.integration.Message; +import org.springframework.integration.expression.ExpressionUtils; +import org.springframework.integration.file.remote.session.Session; +import org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import org.springframework.integration.sftp.session.SftpTestSessionFactory; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.ChannelSftp.LsEntry; +import com.jcraft.jsch.SftpATTRS; + /** * @author Oleg Zhurakousky * @author Gunnar Hillert + * @author Gary Russell * @since 2.0 */ public class SftpInboundRemoteFileSystemSynchronizerTests { @@ -81,6 +83,7 @@ public class SftpInboundRemoteFileSystemSynchronizerTests { synchronizer.setDeleteRemoteFiles(true); synchronizer.setRemoteDirectory("remote-test-dir"); synchronizer.setFilter(new SftpRegexPatternFileListFilter(".*\\.test$")); + synchronizer.setIntegrationEvaluationContext(ExpressionUtils.createStandardEvaluationContext()); SftpInboundFileSynchronizingMessageSource ms = new SftpInboundFileSynchronizingMessageSource(synchronizer); diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java index 6fd436227b..d56cafd855 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java @@ -146,12 +146,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro @Override public void onInit() { super.onInit(); - if (this.getBeanFactory() != null) { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); - } - else { - this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); - } + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); Assert.state(this.destinationProvider == null || CollectionUtils.isEmpty(this.uriVariableExpressions), "uri variables are not supported when a DestinationProvider is supplied."); } diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java index 9423c4a71e..d586c858a6 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/transformer/XsltPayloadTransformer.java @@ -32,16 +32,15 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.w3c.dom.Document; -import org.springframework.context.expression.MapAccessor; import org.springframework.core.io.Resource; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageHeaders; import org.springframework.integration.MessagingException; +import org.springframework.integration.expression.ExpressionUtils; import org.springframework.integration.transformer.AbstractTransformer; import org.springframework.integration.xml.result.DomResultFactory; import org.springframework.integration.xml.result.ResultFactory; @@ -84,7 +83,7 @@ public class XsltPayloadTransformer extends AbstractTransformer { private final Templates templates; - private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile StandardEvaluationContext evaluationContext; private Map xslParameterMappings; @@ -120,7 +119,6 @@ public class XsltPayloadTransformer extends AbstractTransformer { public XsltPayloadTransformer(Templates templates, ResultTransformer resultTransformer) throws ParserConfigurationException { this.templates = templates; this.resultTransformer = resultTransformer; - this.evaluationContext.addPropertyAccessor(new MapAccessor()); } @@ -163,10 +161,18 @@ public class XsltPayloadTransformer extends AbstractTransformer { this.xsltParamHeaders = xsltParamHeaders; } + @Override public String getComponentType() { return "xml:xslt-transformer"; } + + @Override + protected void onInit() throws Exception { + super.onInit(); + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); + } + @Override protected Object doTransform(Message message) throws Exception { Transformer transformer = buildTransformer(message); diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests.java index c56cf11052..7856c390ea 100644 --- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests.java +++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/XsltPayloadTransformerParserTests.java @@ -17,6 +17,7 @@ package org.springframework.integration.xml.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import javax.xml.transform.dom.DOMResult; @@ -24,8 +25,6 @@ import javax.xml.transform.dom.DOMResult; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.springframework.integration.support.MessageBuilder; -import org.springframework.integration.xml.transformer.CustomTestResultFactory; import org.w3c.dom.Document; import org.springframework.context.ApplicationContext; @@ -34,7 +33,10 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.test.util.TestUtils; import org.springframework.integration.xml.config.StubResultFactory.StubStringResult; +import org.springframework.integration.xml.transformer.CustomTestResultFactory; import org.springframework.integration.xml.util.XmlTestUtil; import org.springframework.xml.transform.StringResult; @@ -45,13 +47,12 @@ import org.springframework.xml.transform.StringResult; */ public class XsltPayloadTransformerParserTests { - private String doc = "test"; + private final String doc = "test"; private ApplicationContext applicationContext; private PollableChannel output; - @Before public void setUp() { applicationContext = new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass()); @@ -68,6 +69,8 @@ public class XsltPayloadTransformerParserTests { assertTrue("Payload was not a DOMResult", result.getPayload() instanceof DOMResult); Document doc = (Document) ((DOMResult) result.getPayload()).getNode(); assertEquals("Wrong payload", "test", doc.getDocumentElement().getTextContent()); + assertNotNull(TestUtils.getPropertyValue(applicationContext.getBean("xsltTransformerWithResource.handler"), + "transformer.evaluationContext.beanResolver")); } @Test