Combined commit from Gary and Mark for INT-2451

Added bean resolver to ContentEnricher
INT-2451 Polishing - PR Review Comments

Disallow bean resolution in the name expression for content enricher
properties - the name expressions can only resolve to payload
properties. Value expressions can resolve to beans and bean properties.
This commit is contained in:
Mark Fisher
2012-02-22 18:36:47 -05:00
committed by Oleg Zhurakousky
parent 1d392e6ab2
commit a77e5dac5a
7 changed files with 186 additions and 7 deletions

View File

@@ -20,7 +20,9 @@ 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.Expression;
import org.springframework.expression.spel.SpelParserConfiguration;
@@ -43,6 +45,7 @@ import org.springframework.util.ReflectionUtils;
*
* @author Mark Fisher
* @author Gunnar Hillert
* @author Gary Russell
* @since 2.1
*/
public class ContentEnricher extends AbstractReplyProducingMessageHandler implements Lifecycle {
@@ -51,7 +54,9 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
private final SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
private final StandardEvaluationContext sourceEvaluationContext = new StandardEvaluationContext();
private final StandardEvaluationContext targetEvaluationContext = new StandardEvaluationContext();
private volatile boolean shouldClonePayload = false;
@@ -192,7 +197,12 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
this.gateway.afterPropertiesSet();
}
this.evaluationContext.addPropertyAccessor(new MapAccessor());
this.sourceEvaluationContext.addPropertyAccessor(new MapAccessor());
this.targetEvaluationContext.addPropertyAccessor(new MapAccessor());
BeanFactory beanFactory = this.getBeanFactory();
if (beanFactory != null) {
this.sourceEvaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
}
@Override
@@ -216,7 +226,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
actualRequestMessage = requestMessage;
}
else {
final Object requestMessagePayload = this.requestPayloadExpression.getValue(this.evaluationContext, requestMessage);
final Object requestMessagePayload = this.requestPayloadExpression.getValue(this.sourceEvaluationContext, requestMessage);
actualRequestMessage = MessageBuilder.withPayload(requestMessagePayload)
.copyHeaders(requestMessage.getHeaders()).build();
}
@@ -233,8 +243,8 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
for (Map.Entry<Expression, Expression> entry : this.propertyExpressions.entrySet()) {
Expression propertyExpression = entry.getKey();
Expression valueExpression = entry.getValue();
Object value = valueExpression.getValue(this.evaluationContext, replyMessage);
propertyExpression.setValue(this.evaluationContext, targetPayload, value);
Object value = valueExpression.getValue(this.sourceEvaluationContext, replyMessage);
propertyExpression.setValue(this.targetEvaluationContext, targetPayload, value);
}
return targetPayload;
}

View File

@@ -21,6 +21,11 @@
order="99" should-clone-payload="true" output-channel="output">
<property name="name" expression="payload.sourceName"/>
<property name="age" value="42"/>
<property name="gender" expression="@testBean"/>
</enricher>
<beans:bean id="testBean" class="java.lang.String">
<beans:constructor-arg value="male"/>
</beans:bean>
</beans:beans>

View File

@@ -77,8 +77,11 @@ public class EnricherParserTests {
else if ("age".equals(e.getKey().getExpressionString())) {
assertEquals("42", e.getValue().getExpressionString());
}
else if ("gender".equals(e.getKey().getExpressionString())) {
assertEquals("@testBean", e.getValue().getExpressionString());
}
else {
throw new IllegalStateException("expected 'name' and 'age' only, not: " + e.getKey().getExpressionString());
throw new IllegalStateException("expected 'name', 'age', and 'gender' only, not: " + e.getKey().getExpressionString());
}
}
}
@@ -123,6 +126,7 @@ public class EnricherParserTests {
Target enriched = (Target) reply.getPayload();
assertEquals("foo", enriched.getName());
assertEquals(42, enriched.getAge());
assertEquals("male", enriched.getGender());
assertNotSame(original, enriched);
}
@@ -146,6 +150,8 @@ public class EnricherParserTests {
private volatile int age;
private volatile String gender;
public String getName() {
return name;
}
@@ -162,6 +168,14 @@ public class EnricherParserTests {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Object clone() {
Target copy = new Target();
copy.setName(this.name);

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="beanResolveIn" />
<channel id="beanResolveOut">
<queue />
</channel>
<enricher id="enricher" input-channel="beanResolveIn" output-channel="beanResolveOut">
<property name="nested.value" expression="@someBean.someOtherProperty" />
</enricher>
<beans:bean id="someBean" class="org.springframework.integration.config.xml.EnricherParserTests3$SomeBean">
<beans:constructor-arg value="baz" />
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="beanResolveIn" />
<channel id="beanResolveOut">
<queue />
</channel>
<enricher id="enricher" input-channel="beanResolveIn" output-channel="beanResolveOut">
<property name="@someBean.nested.value" value="qux" />
</enricher>
<beans:bean id="someBean" class="org.springframework.integration.config.xml.EnricherParserTests3$SomeBean">
<beans:constructor-arg value="baz" />
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;
/**
* @author Gary Russell
*
* @since 2.1.1
*/
public class EnricherParserTests3 {
@Test
public void testSourceBeanResolver() {
ApplicationContext context = new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-context.xml", this.getClass());
MessageChannel beanResolveIn = context.getBean("beanResolveIn", MessageChannel.class);
PollableChannel beanResolveOut = context.getBean("beanResolveOut", PollableChannel.class);
SomeBean payload = new SomeBean("foo");
assertEquals("foo", payload.getNested().getValue());
beanResolveIn.send(new GenericMessage<SomeBean>(payload));
@SuppressWarnings("unchecked")
Message<SomeBean> out = (Message<SomeBean>) beanResolveOut.receive();
assertSame(payload, out.getPayload());
assertEquals("bar", out.getPayload().getNested().getValue());
}
@Test
public void testTargetBeanResolver() {
ApplicationContext context = new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml", this.getClass());
MessageChannel beanResolveIn = context.getBean("beanResolveIn", MessageChannel.class);
SomeBean payload = new SomeBean("foo");
assertEquals("foo", payload.getNested().getValue());
try {
beanResolveIn.send(new GenericMessage<SomeBean>(payload));
fail("Expected SpEL Exception");
}
catch (MessageHandlingException e) {
assertTrue(e.getCause() instanceof SpelEvaluationException);
}
}
public static class SomeBean {
private Nested nested = new Nested();
public SomeBean(String someProperty) {
this.nested.setValue(someProperty);
}
public Nested getNested() {
return nested;
}
public String getSomeOtherProperty() {
return "bar";
}
public class Nested {
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}
}

View File

@@ -349,7 +349,10 @@
as well. The former for a literal value to set, and the
latter for a SpEL expression to be evaluated. The root
object of the evaluation context is the Message that was
returned from the flow initiated by this enricher.
returned from the flow initiated by this enricher, the
input Message if there is no request channel, or the
application context (using the '@&lt;beanName&gt;.&lt;beanProperty&gt;'
SpEL syntax).
</para>
</callout>
</calloutlist>