Merge pull request #169 from ghillert/INT-2224
Enricher - Allow to send a sub-set of the payload see also: https://jira.springsource.org/browse/INT-2224
This commit is contained in:
@@ -1,115 +1,168 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="content-enricher"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Content Enricher</title>
|
||||
<title>Content Enricher</title>
|
||||
|
||||
<section id="content-enricher-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
At times you may have a requirement to enhance a request with more
|
||||
information than was provided by the target system. The
|
||||
<link href="http://www.eaipatterns.com/DataEnricher.html">Content Enricher</link>
|
||||
pattern describes various scenarios as well as the component
|
||||
(Enricher), which allows you to address such requirements.
|
||||
</para>
|
||||
<para>
|
||||
The Spring Integration <code>Core</code> module includes 2 enrichers:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>Header Enricher</listitem>
|
||||
<listitem>(Generic) Enricher</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Furthermore, several <emphasis>Adapter specific Header Enrichers</emphasis>
|
||||
are included as well:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>XPath Header Enricher (XML Module)</listitem>
|
||||
<listitem>Email Header Enricher (Mail Module)</listitem>
|
||||
<listitem>XMPP Header Enricher (XMPP Module)</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Please go to the adapter specific sections of this reference manual
|
||||
to learn more about those adapters.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="header-enricher">
|
||||
<title>Header Enricher</title>
|
||||
|
||||
<para>
|
||||
If you only need to add headers to a Message, and they are not
|
||||
dynamically determined by the Message content, then referencing a
|
||||
custom implementation of a Transformer may be overkill. For that reason,
|
||||
Spring Integration provides support for the <emphasis>Header Enricher</emphasis>
|
||||
pattern. It is exposed via the <code><header-enricher></code> element.
|
||||
</para>
|
||||
|
||||
<section id="content-enricher-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
At times you may have a requirement to enhance a request with more information than was
|
||||
provided by the target system. The <link href="http://www.eaipatterns.com/DataEnricher.html">Content Enricher</link> pattern
|
||||
describes various scenarios as well as the component (Enricher), which allows you to address such requirements.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="header-enricher">
|
||||
<title>Header Enricher</title>
|
||||
|
||||
<para>
|
||||
If you only need to add headers to a Message, and they are not dynamically determined by the Message content,
|
||||
then referencing a custom implementation of a Transformer may be overkill. For that reason,
|
||||
Spring Integration provides support for the <emphasis>Header Enricher</emphasis> pattern. It is exposed via
|
||||
the <code><header-enricher></code> element.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="foo" value="123"/>
|
||||
<int:header name="bar" ref="someBean"/>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
|
||||
</para>
|
||||
<para>
|
||||
The <emphasis>Header Enricher</emphasis> also provides helpful sub-elements to set well-known header names.
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<para>
|
||||
The <emphasis>Header Enricher</emphasis> also provides helpful sub-elements
|
||||
to set well-known header names.
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:error-channel ref="applicationErrorChannel"/>
|
||||
<int:reply-channel ref="quoteReplyChannel"/>
|
||||
<int:correlation-id value="123"/>
|
||||
<int:priority value="HIGHEST"/>
|
||||
<int:header name="bar" ref="someBean"/>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
|
||||
In the above configuration you can clearly see that for well-known headers such as <code>errorChannel</code>,
|
||||
<code>correlationId</code>, <code>priority</code>, <code>replyChannel</code>etc., instead of using generic
|
||||
<emphasis><header></emphasis> sub-elements where you would have to provide both header 'name' and 'value',
|
||||
you can use convenient sub-elements to set those values directly.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>POJO Support</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
Often a header value cannot be defined statically and has to be determined dynamically based on some content in the Message. That is why
|
||||
<emphasis>Header Enricher</emphasis> allows you to also specify a bean 'ref' and 'method' that will calculate the
|
||||
header value. Let's look at the following configuration:
|
||||
<para>
|
||||
In the above configuration you can clearly see that for well-known
|
||||
headers such as <code>errorChannel</code>, <code>correlationId</code>,
|
||||
<code>priority</code>, <code>replyChannel</code>etc., instead of
|
||||
using generic <emphasis><header></emphasis> sub-elements where
|
||||
you would have to provide both header 'name' and 'value', you can use
|
||||
convenient sub-elements to set those values directly.
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<para>
|
||||
<emphasis>POJO Support</emphasis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Often a header value cannot be defined statically and has to be
|
||||
determined dynamically based on some content in the Message. That is why
|
||||
<emphasis>Header Enricher</emphasis> allows you to also specify a bean
|
||||
reference using the <code>ref</code> and <code>method</code> attribute.
|
||||
The specified method will calculate the header value. Let's look at
|
||||
the following configuration:
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="foo" method="computeValue" ref="myBean"/>
|
||||
</int:header-enricher>
|
||||
|
||||
<bean id="myBean" class="foo.bar.MyBean"/>]]></programlisting>
|
||||
|
||||
<programlisting language="java"><![CDATA[public class MyBean {
|
||||
<programlisting language="java"><![CDATA[public class MyBean {
|
||||
public String computeValue(String payload){
|
||||
return payload.toUpperCase() + "_US";
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
You can also configure your POJO as inner bean
|
||||
|
||||
<para>
|
||||
You can also configure your POJO as inner bean:
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
|
||||
<int:header name="some_header">
|
||||
<bean class="org.MyEnricher"/>
|
||||
</int:header>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
<para>
|
||||
as well as point to a Groovy script:
|
||||
</para>
|
||||
|
||||
as well as point to a Groovy script
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="inputChannel" output-channel="outputChannel">
|
||||
<int:header name="some_header">
|
||||
<int-groovy:script location="org/SampleGroovyHeaderEnricher.groovy"/>
|
||||
</int:header>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>SpEL Support</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
In Spring Integration 2.0 we have introduced the convenience of the
|
||||
<link href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html">Spring Expression Language (SpEL)</link>
|
||||
to help configure many different components. The <emphasis>Header
|
||||
Enricher</emphasis> is one of them.
|
||||
|
||||
<para>
|
||||
<emphasis>SpEL Support</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
In Spring Integration 2.0 we have introduced the convenience of the
|
||||
<link href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html">Spring Expression Language (SpEL)</link>
|
||||
to help configure many different components. The <emphasis>Header Enricher</emphasis> is one of them.
|
||||
Looking again at the POJO example above, you can see that the computation
|
||||
logic to determine the header value is actually pretty simple. A natural
|
||||
question would be: "is there a simpler way to accomplish this?". That
|
||||
is where SpEL shows its true power.
|
||||
</para>
|
||||
|
||||
Looking again at the POJO example above, you can see that the computation logic to determine the header value is actually pretty simple.
|
||||
A natural question would be: "is there a simpler way to accomplish this?". That is where SpEL shows its true power.
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<programlisting language="xml"><![CDATA[<int:header-enricher input-channel="in" output-channel="out">
|
||||
<int:header name="foo" expression="payload.toUpperCase() + '_US'"/>
|
||||
</int:header-enricher>]]></programlisting>
|
||||
|
||||
As you can see, by using SpEL for such simple cases, we no longer have to provide a separate class and configure
|
||||
it in the application context. All we need is the <emphasis>expression</emphasis> attribute configured with a valid
|
||||
SpEL expression. The 'payload' and 'headers' variables are bound to the SpEL Evaluation Context,
|
||||
giving you full access to the incoming Message.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Adapter specific Header Enrichers</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
As you go through the manual, you will see that as an added convenience,
|
||||
Spring Integration also provides adapter specific Header Enrichers (e.g., MAIL, XMPP, etc.)
|
||||
</para>
|
||||
</section>
|
||||
<para>
|
||||
As you can see, by using SpEL for such simple cases, we no longer have
|
||||
to provide a separate class and configure it in the application context.
|
||||
All we need is the <emphasis>expression</emphasis> attribute configured
|
||||
with a valid SpEL expression. The 'payload' and 'headers' variables
|
||||
are bound to the SpEL Evaluation Context, giving you full access to
|
||||
the incoming Message.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="generic-enricher">
|
||||
<title>(Generic) Enricher</title>
|
||||
<para></para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[<int:enricher request-channel=""
|
||||
auto-startup=""
|
||||
id=""
|
||||
input-channel=""
|
||||
order=""
|
||||
output-channel=""
|
||||
reply-channel=""
|
||||
send-timeout=""
|
||||
should-clone-payload="true">
|
||||
<int:poller></int:poller>
|
||||
<int:property name="" expression=""/>
|
||||
<int:property name="" value=""/>
|
||||
</int:enricher>]]></programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Parser for the 'enricher' element.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.1
|
||||
*/
|
||||
@@ -75,7 +75,17 @@ public class EnricherParser extends AbstractConsumerEndpointParser {
|
||||
}
|
||||
builder.addPropertyValue("propertyExpressions", propertyExpressions);
|
||||
}
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "should-clone-payload");
|
||||
|
||||
String requestPayloadExpression = element.getAttribute("request-payload-expression");
|
||||
|
||||
if (StringUtils.hasText(requestPayloadExpression)) {
|
||||
BeanDefinitionBuilder expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
|
||||
expressionBuilder.addConstructorArgValue(requestPayloadExpression);
|
||||
builder.addPropertyValue("requestPayloadExpression", expressionBuilder.getBeanDefinition());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -53,6 +54,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
|
||||
|
||||
private volatile boolean shouldClonePayload = false;
|
||||
|
||||
private Expression requestPayloadExpression;
|
||||
|
||||
/**
|
||||
* Create a Content Enricher with the given request channel. An anonymous reply channel
|
||||
@@ -94,6 +96,34 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By default the original message's payload will be used as the actual payload
|
||||
* that will be send to the request-channel.
|
||||
*
|
||||
* By providing a SpEL expression as value for this setter, a subset of the
|
||||
* original payload, a header value or any other resolvable SpEL expression
|
||||
* can be used as the basis for the payload, that will be send to the
|
||||
* request-channel.
|
||||
*
|
||||
* For the Expression evaluation the full message is available as the <b>root object</b>.
|
||||
*
|
||||
* For instance the following SpEL expressions (among others) are possible:
|
||||
*
|
||||
* <ul>
|
||||
* <li>payload.foo</li>
|
||||
* <li>headers.foobar</li>
|
||||
* <li>new java.util.Date()</li>
|
||||
* <li>'foo' + 'bar'</li>
|
||||
* </ul>
|
||||
*
|
||||
* If more sophisticated logic is required (e.g. changing the message
|
||||
* headers etc.) please use additional downstream transformers.
|
||||
*
|
||||
*/
|
||||
public void setRequestPayloadExpression(Expression requestPayloadExpression) {
|
||||
this.requestPayloadExpression = requestPayloadExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to clone payload objects to create the target object.
|
||||
* This is only applicable for payload types that implement Cloneable.
|
||||
@@ -110,23 +140,46 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Object targetPayload = requestMessage.getPayload();
|
||||
if (targetPayload instanceof Cloneable && this.shouldClonePayload) {
|
||||
|
||||
final Object requestPayload = requestMessage.getPayload();
|
||||
|
||||
final Object targetPayload;
|
||||
|
||||
if (requestPayload instanceof Cloneable && this.shouldClonePayload) {
|
||||
try {
|
||||
Method cloneMethod = targetPayload.getClass().getMethod("clone", new Class<?>[0]);
|
||||
targetPayload = ReflectionUtils.invokeMethod(cloneMethod, targetPayload);
|
||||
Method cloneMethod = requestPayload.getClass().getMethod("clone", new Class<?>[0]);
|
||||
targetPayload = ReflectionUtils.invokeMethod(cloneMethod, requestPayload);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessageHandlingException(requestMessage, "Failed to clone payload object", e);
|
||||
}
|
||||
} else {
|
||||
targetPayload = requestPayload;
|
||||
}
|
||||
Message<?> replyMessage = this.gateway.sendAndReceiveMessage(requestMessage);
|
||||
|
||||
final Message<?> actualRequestMessage;
|
||||
|
||||
if (this.requestPayloadExpression==null) {
|
||||
|
||||
actualRequestMessage = requestMessage;
|
||||
|
||||
} else {
|
||||
|
||||
final Object requestMessagePayload = this.requestPayloadExpression.getValue(this.evaluationContext, requestMessage);
|
||||
actualRequestMessage = MessageBuilder.withPayload(requestMessagePayload)
|
||||
.copyHeaders(requestMessage.getHeaders())
|
||||
.build();
|
||||
}
|
||||
|
||||
final Message<?> replyMessage = this.gateway.sendAndReceiveMessage(actualRequestMessage);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return targetPayload;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Contains core-implementation of various Transformers which includes Enrichers
|
||||
* and Filters.
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
*/
|
||||
package org.springframework.integration.transformer;
|
||||
@@ -1052,6 +1052,32 @@ endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="request-payload-expression" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
By default the original message's payload will be used as payload
|
||||
that will be send to the request-channel. By specifying a SpEL expression
|
||||
as value for the 'request-payload-expression' attribute, a
|
||||
subset of the original payload, a header value or any other
|
||||
resolvable SpEL expression can be used as the basis for the payload,
|
||||
that will be send to the request-channel.
|
||||
|
||||
For the Expression evaluation the full message is available
|
||||
as the 'root object'.
|
||||
|
||||
For instance the following SpEL expressions (among others)
|
||||
are possible:
|
||||
|
||||
- payload.foo
|
||||
- headers.foobar
|
||||
- new java.util.Date()
|
||||
- 'foo' + 'bar'
|
||||
|
||||
If more sophisticated logic is required (e.g. changing the
|
||||
message headers etc.) please use additional downstream transformers.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="propertySubElementType">
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -63,6 +64,8 @@ public class EnricherParserTests {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(enricher);
|
||||
assertEquals(context.getBean("output"), accessor.getPropertyValue("outputChannel"));
|
||||
assertEquals(true, accessor.getPropertyValue("shouldClonePayload"));
|
||||
assertNull(accessor.getPropertyValue("requestPayloadExpression"));
|
||||
|
||||
Map<Expression, Expression> propertyExpressions = (Map<Expression, Expression>) accessor.getPropertyValue("propertyExpressions");
|
||||
for (Map.Entry<Expression, Expression> e : propertyExpressions.entrySet()) {
|
||||
if ("name".equals(e.getKey().getExpressionString())) {
|
||||
@@ -98,7 +101,7 @@ public class EnricherParserTests {
|
||||
|
||||
|
||||
private static class Source {
|
||||
|
||||
|
||||
private final String sourceName;
|
||||
|
||||
Source(String sourceName) {
|
||||
@@ -113,23 +116,23 @@ public class EnricherParserTests {
|
||||
|
||||
|
||||
public static class Target implements Cloneable {
|
||||
|
||||
|
||||
private volatile String name;
|
||||
|
||||
|
||||
private volatile int age;
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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="input"/>
|
||||
|
||||
<channel id="output">
|
||||
<queue />
|
||||
</channel>
|
||||
|
||||
<channel id="requests"/>
|
||||
|
||||
<enricher id="enricher" input-channel="input" request-channel="requests"
|
||||
output-channel="output" order="99"
|
||||
request-payload-expression="payload.age">
|
||||
<property name="name" expression="'Name as SpEL'"/>
|
||||
<property name="age" expression="payload.sourceName"/>
|
||||
</enricher>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.assertTrue;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.ContentEnricher;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 2.1
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class EnricherParserWithRequestPayloadExpressionTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configurationCheck() {
|
||||
Object endpoint = context.getBean("enricher");
|
||||
assertEquals(EventDrivenConsumer.class, endpoint.getClass());
|
||||
Object handler = TestUtils.getPropertyValue(endpoint, "handler");
|
||||
assertEquals(ContentEnricher.class, handler.getClass());
|
||||
ContentEnricher enricher = (ContentEnricher) handler;
|
||||
assertEquals(99, enricher.getOrder());
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(enricher);
|
||||
assertEquals(context.getBean("output"), accessor.getPropertyValue("outputChannel"));
|
||||
assertEquals(false, accessor.getPropertyValue("shouldClonePayload"));
|
||||
|
||||
Expression requestPayloadExpression = (Expression) accessor.getPropertyValue("requestPayloadExpression");
|
||||
assertEquals("payload.age", requestPayloadExpression.getExpressionString());
|
||||
|
||||
Map<Expression, Expression> propertyExpressions = (Map<Expression, Expression>) accessor.getPropertyValue("propertyExpressions");
|
||||
for (Map.Entry<Expression, Expression> e : propertyExpressions.entrySet()) {
|
||||
if ("name".equals(e.getKey().getExpressionString())) {
|
||||
assertEquals("'Name as SpEL'", e.getValue().getExpressionString());
|
||||
}
|
||||
else if ("age".equals(e.getKey().getExpressionString())) {
|
||||
assertEquals("payload.sourceName", e.getValue().getExpressionString());
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("expected 'name' and 'age' only, not: " + e.getKey().getExpressionString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void integrationTest() {
|
||||
SubscribableChannel requests = context.getBean("requests", SubscribableChannel.class);
|
||||
requests.subscribe(new AbstractReplyProducingMessageHandler() {
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
|
||||
assertTrue("Expected the payload of the requestMessage to be a String",
|
||||
requestMessage.getPayload() instanceof Integer);
|
||||
|
||||
Integer payload = (Integer) requestMessage.getPayload();
|
||||
assertEquals("Expected value: 99", Integer.valueOf(99), payload);
|
||||
|
||||
return new Source(String.valueOf(payload));
|
||||
}
|
||||
});
|
||||
|
||||
Target original = new Target();
|
||||
original.setAge(99);
|
||||
|
||||
Message<?> request = MessageBuilder.withPayload(original).build();
|
||||
context.getBean("input", MessageChannel.class).send(request);
|
||||
Message<?> reply = context.getBean("output", PollableChannel.class).receive(0);
|
||||
Target enriched = (Target) reply.getPayload();
|
||||
assertEquals("Name as SpEL", enriched.getName());
|
||||
assertEquals(99, enriched.getAge());
|
||||
assertSame(original, enriched);
|
||||
}
|
||||
|
||||
|
||||
private static class Source {
|
||||
|
||||
private final String sourceName;
|
||||
|
||||
Source(String sourceName) {
|
||||
this.sourceName = sourceName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String getSourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Target implements Cloneable {
|
||||
|
||||
private volatile String name;
|
||||
|
||||
private volatile int age;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
Target copy = new Target();
|
||||
copy.setName(this.name);
|
||||
copy.setAge(this.age);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user