INT-3027: Headers Enrichment for <enricher>

* Add `<header>` sub-element to `<enricher>`
* Refactoring of `EnricherParser`
* Add Headers Enrichment logic to `ContentEnricher`
* Add tests
* What's new and Content Enricher's `<header>` note

JIRA: https://jira.springsource.org/browse/INT-3027

INT-3027: Addressing PR comments

INT-3027: add NPE asserts to map properties

INT-3027: add overwrite & type to enricher header

Move `HeaderValueMessageProcessor` hierarchy to 'transformer.support' package

INT-3027: Make header's overwrite=true by default

Doc Polishing
This commit is contained in:
Artem Bilan
2013-09-16 18:51:36 +03:00
committed by Gary Russell
parent 7c0c9e92fc
commit 7517963be2
17 changed files with 433 additions and 162 deletions

View File

@@ -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,10 @@ import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.transformer.ContentEnricher;
import org.springframework.util.CollectionUtils;
@@ -34,6 +34,7 @@ import org.springframework.util.xml.DomUtils;
* Parser for the 'enricher' element.
*
* @author Mark Fisher
* @author Artem Bilan
* @since 2.1
*/
public class EnricherParser extends AbstractConsumerEndpointParser {
@@ -47,34 +48,34 @@ public class EnricherParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
List<Element> propertyElements = DomUtils.getChildElementsByTagName(element, "property");
if (!CollectionUtils.isEmpty(propertyElements)) {
ManagedMap<String, Object> propertyExpressions = new ManagedMap<String, Object>();
for (Element propertyElement : propertyElements) {
String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
String expression = propertyElement.getAttribute("expression");
if (StringUtils.hasText(value) && StringUtils.hasText(expression)) {
parserContext.getReaderContext().error("The 'value' and 'expression' attributes are mutually exclusive on " +
"an <enricher> element's <property> sub-element.", parserContext.extractSource(propertyElement));
}
if (StringUtils.hasText(value)) {
BeanDefinitionBuilder expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(LiteralExpression.class);
expressionBuilder.addConstructorArgValue(value);
propertyExpressions.put(name, expressionBuilder.getBeanDefinition());
}
else if (StringUtils.hasText(expression)) {
BeanDefinitionBuilder expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
expressionBuilder.addConstructorArgValue(expression);
propertyExpressions.put(name, expressionBuilder.getBeanDefinition());
}
else {
parserContext.getReaderContext().error("Exactly one of 'value' or 'expression' attributes must be provided on " +
"an <enricher> element's <property> sub-element.", parserContext.extractSource(propertyElement));
}
List<Element> subElements = DomUtils.getChildElementsByTagName(element, "property");
if (!CollectionUtils.isEmpty(subElements)) {
ManagedMap<String, Object> expressions = new ManagedMap<String, Object>();
for (Element subElement : subElements) {
String name = subElement.getAttribute("name");
BeanDefinition beanDefinition = IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("value",
"expression", parserContext, subElement, true);
expressions.put(name, beanDefinition);
}
builder.addPropertyValue("propertyExpressions", propertyExpressions);
builder.addPropertyValue("propertyExpressions", expressions);
}
subElements = DomUtils.getChildElementsByTagName(element, "header");
if (!CollectionUtils.isEmpty(subElements)) {
ManagedMap<String, Object> expressions = new ManagedMap<String, Object>();
for (Element subElement : subElements) {
String name = subElement.getAttribute("name");
BeanDefinition expressionDefinition = IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("value",
"expression", parserContext, subElement, true);
BeanDefinitionBuilder valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgValue(expressionDefinition)
.addConstructorArgValue(subElement.getAttribute("type"));
IntegrationNamespaceUtils.setValueIfAttributeDefined(valueProcessorBuilder, subElement, "overwrite");
expressions.put(name, valueProcessorBuilder.getBeanDefinition());
}
builder.addPropertyValue("headerExpressions", expressions);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "should-clone-payload");

View File

@@ -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.
@@ -179,7 +179,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
Object headerValue = (headerType != null) ?
new TypedStringValue(value, headerType) : value;
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$StaticHeaderValueMessageProcessor");
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.StaticHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgValue(headerValue);
}
else if (isExpression) {
@@ -188,7 +188,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
"The 'method' attribute cannot be used with the 'expression' attribute.", element);
}
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$ExpressionEvaluatingHeaderValueMessageProcessor");
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor");
if (expressionElement != null) {
BeanDefinitionBuilder dynamicExpressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DynamicExpression.class);
dynamicExpressionBuilder.addConstructorArgValue(expressionElement.getAttribute("key"));
@@ -207,7 +207,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
}
if (hasMethod || isScript) {
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MessageProcessingHeaderValueMessageProcessor");
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.MessageProcessingHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition);
if (hasMethod) {
valueProcessorBuilder.addConstructorArgValue(method);
@@ -215,7 +215,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
}
else {
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$StaticHeaderValueMessageProcessor");
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.StaticHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition);
}
}
@@ -226,13 +226,13 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
}
if (hasMethod) {
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MessageProcessingHeaderValueMessageProcessor");
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.MessageProcessingHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgReference(ref);
valueProcessorBuilder.addConstructorArgValue(method);
}
else {
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$StaticHeaderValueMessageProcessor");
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.support.StaticHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgReference(ref);
}
}

View File

@@ -34,6 +34,7 @@ import org.springframework.integration.expression.IntegrationEvaluationContextAw
import org.springframework.integration.gateway.MessagingGatewaySupport;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.transformer.support.HeaderValueMessageProcessor;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
@@ -51,7 +52,9 @@ import org.springframework.util.ReflectionUtils;
*/
public class ContentEnricher extends AbstractReplyProducingMessageHandler implements Lifecycle, IntegrationEvaluationContextAware {
private final Map<Expression, Expression> propertyExpressions = new HashMap<Expression, Expression>();
private volatile Map<Expression, Expression> propertyExpressions = new HashMap<Expression, Expression>();
private volatile Map<String, HeaderValueMessageProcessor<?>> headerExpressions = new HashMap<String, HeaderValueMessageProcessor<?>>();
private final SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
@@ -80,16 +83,28 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
*/
public void setPropertyExpressions(Map<String, Expression> propertyExpressions) {
Assert.notEmpty(propertyExpressions, "propertyExpressions must not be empty");
synchronized (this.propertyExpressions) {
this.propertyExpressions.clear();
for (Map.Entry<String, Expression> entry : propertyExpressions.entrySet()) {
String key = entry.getKey();
Expression value = entry.getValue();
Assert.notNull(key, "propertyExpressions key must not be null");
Assert.notNull(value, "propertyExpressions value must not be null");
this.propertyExpressions.put(parser.parseExpression(key), value);
}
Assert.noNullElements(propertyExpressions.keySet().toArray(), "propertyExpressions keys must not be empty");
Assert.noNullElements(propertyExpressions.values().toArray(), "propertyExpressions values must not be empty");
Map<Expression, Expression> localMap = new HashMap<Expression, Expression>(propertyExpressions.size());
for (Map.Entry<String, Expression> entry : propertyExpressions.entrySet()) {
String key = entry.getKey();
Expression value = entry.getValue();
localMap.put(parser.parseExpression(key), value);
}
this.propertyExpressions = localMap;
}
/**
* Provide the map of {@link HeaderValueMessageProcessor} to evaluate when enriching
* the target MessageHeaders.
* The keys should simply be header names, and the values should be Expressions
* that will evaluate against the reply Message as the root object.
*/
public void setHeaderExpressions(Map<String, HeaderValueMessageProcessor<?>> headerExpressions) {
Assert.notEmpty(headerExpressions, "headerExpressions must not be empty");
Assert.noNullElements(headerExpressions.keySet().toArray(), "headerExpressions keys must not be empty");
Assert.noNullElements(headerExpressions.values().toArray(), "headerExpressions values must not be empty");
this.headerExpressions = new HashMap<String, HeaderValueMessageProcessor<?>>(headerExpressions);
}
/**
@@ -221,7 +236,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
final Object targetPayload;
if (requestPayload instanceof Cloneable && this.shouldClonePayload) {
try {
Method cloneMethod = requestPayload.getClass().getMethod("clone", new Class<?>[0]);
Method cloneMethod = requestPayload.getClass().getMethod("clone");
targetPayload = ReflectionUtils.invokeMethod(cloneMethod, requestPayload);
}
catch (Exception e) {
@@ -256,7 +271,24 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
Object value = valueExpression.getValue(this.sourceEvaluationContext, replyMessage);
propertyExpression.setValue(this.targetEvaluationContext, targetPayload, value);
}
return targetPayload;
if (this.headerExpressions.isEmpty()) {
return targetPayload;
}
else {
Map<String, Object> targetHeaders = new HashMap<String, Object>(this.headerExpressions.size());
for (Map.Entry<String, HeaderValueMessageProcessor<?>> entry : this.headerExpressions.entrySet()) {
String header = entry.getKey();
HeaderValueMessageProcessor<?> valueProcessor = entry.getValue();
Boolean overwrite = valueProcessor.isOverwrite();
overwrite = overwrite != null ? overwrite : true;
if (overwrite || !requestMessage.getHeaders().containsKey(header)) {
Object value = valueProcessor.processMessage(replyMessage);
targetHeaders.put(header, value);
}
}
return MessageBuilder.withPayload(targetPayload).copyHeaders(targetHeaders).build();
}
}
/**

View File

@@ -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.
@@ -21,20 +21,14 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.expression.Expression;
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.MessagingException;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.handler.MethodInvokingMessageProcessor;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.transformer.support.HeaderValueMessageProcessor;
/**
* A Transformer that adds statically configured header values to a Message.
@@ -146,78 +140,6 @@ public class HeaderEnricher implements Transformer, BeanNameAware, InitializingB
}
}
public static interface HeaderValueMessageProcessor<T> extends MessageProcessor<T> {
Boolean isOverwrite();
}
static abstract class AbstractHeaderValueMessageProcessor<T> implements HeaderValueMessageProcessor<T> {
// null indicates no explicit setting; use header-enricher's
// 'default-overwrite' value
private volatile Boolean overwrite = null;
public void setOverwrite(Boolean overwrite) {
this.overwrite = overwrite;
}
public Boolean isOverwrite() {
return this.overwrite;
}
}
static class StaticHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T> {
private final T value;
public StaticHeaderValueMessageProcessor(T value) {
this.value = value;
}
public T processMessage(Message<?> message) {
return this.value;
}
}
static class ExpressionEvaluatingHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T>
implements BeanFactoryAware {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(
true, true));
private final ExpressionEvaluatingMessageProcessor<T> targetProcessor;
/**
* Create a header value processor for the given Expression and the
* expected type of the expression evaluation result. The expectedType
* may be null if unknown.
*/
public ExpressionEvaluatingHeaderValueMessageProcessor(Expression expression, Class<T> expectedType) {
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expression, expectedType);
}
/**
* Create a header value processor for the given expression string and
* the expected type of the expression evaluation result. The
* expectedType may be null if unknown.
*/
public ExpressionEvaluatingHeaderValueMessageProcessor(String expressionString, Class<T> expectedType) {
Expression expression = expressionParser.parseExpression(expressionString);
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expression, expectedType);
}
public void setBeanFactory(BeanFactory beanFactory) {
this.targetProcessor.setBeanFactory(beanFactory);
}
public T processMessage(Message<?> message) {
return this.targetProcessor.processMessage(message);
}
}
/*
* (non-Javadoc)
*
@@ -241,7 +163,7 @@ public class HeaderEnricher implements Transformer, BeanNameAware, InitializingB
for (HeaderValueMessageProcessor<?> processor : this.headersToAdd.values()) {
Boolean processerOverwrite = processor.isOverwrite();
if (processerOverwrite != null) {
shouldOverwrite |= processerOverwrite.booleanValue();
shouldOverwrite |= processerOverwrite;
}
}
if (!shouldOverwrite && !this.shouldSkipNulls) {
@@ -250,26 +172,4 @@ public class HeaderEnricher implements Transformer, BeanNameAware, InitializingB
}
}
static class MessageProcessingHeaderValueMessageProcessor extends AbstractHeaderValueMessageProcessor<Object> {
private final MessageProcessor<?> targetProcessor;
public <T> MessageProcessingHeaderValueMessageProcessor(MessageProcessor<T> targetProcessor) {
this.targetProcessor = targetProcessor;
}
public MessageProcessingHeaderValueMessageProcessor(Object targetObject) {
this(targetObject, null);
}
public MessageProcessingHeaderValueMessageProcessor(Object targetObject, String method) {
this.targetProcessor = new MethodInvokingMessageProcessor<Object>(targetObject, method);
}
public Object processMessage(Message<?> message) {
return this.targetProcessor.processMessage(message);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.transformer.support;
/**
* @author Mark Fisher
* @author Artem Bilan
* @since 3.0
*/
abstract class AbstractHeaderValueMessageProcessor<T> implements HeaderValueMessageProcessor<T> {
// null indicates no explicit setting
private volatile Boolean overwrite = null;
public void setOverwrite(Boolean overwrite) {
this.overwrite = overwrite;
}
public Boolean isOverwrite() {
return this.overwrite;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.transformer.support;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.expression.Expression;
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.handler.ExpressionEvaluatingMessageProcessor;
/**
* @author Mark Fisher
* @author Artem Bilan
* @since 3.0
*/
class ExpressionEvaluatingHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T>
implements BeanFactoryAware {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(
true, true));
private final ExpressionEvaluatingMessageProcessor<T> targetProcessor;
/**
* Create a header value processor for the given Expression and the
* expected type of the expression evaluation result. The expectedType
* may be null if unknown.
*/
public ExpressionEvaluatingHeaderValueMessageProcessor(Expression expression, Class<T> expectedType) {
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expression, expectedType);
}
/**
* Create a header value processor for the given expression string and
* the expected type of the expression evaluation result. The
* expectedType may be null if unknown.
*/
public ExpressionEvaluatingHeaderValueMessageProcessor(String expressionString, Class<T> expectedType) {
Expression expression = expressionParser.parseExpression(expressionString);
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expression, expectedType);
}
public void setBeanFactory(BeanFactory beanFactory) {
this.targetProcessor.setBeanFactory(beanFactory);
}
public T processMessage(Message<?> message) {
return this.targetProcessor.processMessage(message);
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.transformer.support;
import org.springframework.integration.handler.MessageProcessor;
/**
* @author Mark Fisher
* @author Artem Bilan
* @since 3.0
*/
public interface HeaderValueMessageProcessor<T> extends MessageProcessor<T> {
Boolean isOverwrite();
}

View File

@@ -0,0 +1,48 @@
/*
* 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.transformer.support;
import org.springframework.integration.Message;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.handler.MethodInvokingMessageProcessor;
/**
* @author Mark Fisher
* @author Artem Bilan
* @since 3.0
*/
class MessageProcessingHeaderValueMessageProcessor extends AbstractHeaderValueMessageProcessor<Object> {
private final MessageProcessor<?> targetProcessor;
public <T> MessageProcessingHeaderValueMessageProcessor(MessageProcessor<T> targetProcessor) {
this.targetProcessor = targetProcessor;
}
public MessageProcessingHeaderValueMessageProcessor(Object targetObject) {
this(targetObject, null);
}
public MessageProcessingHeaderValueMessageProcessor(Object targetObject, String method) {
this.targetProcessor = new MethodInvokingMessageProcessor<Object>(targetObject, method);
}
public Object processMessage(Message<?> message) {
return this.targetProcessor.processMessage(message);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.transformer.support;
import org.springframework.integration.Message;
/**
* @author Mark Fisher
* @author Artem Bilan
* @since 3.0
*/
class StaticHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T> {
private final T value;
public StaticHeaderValueMessageProcessor(T value) {
this.value = value;
}
public T processMessage(Message<?> message) {
return this.value;
}
}

View File

@@ -0,0 +1,7 @@
/**
* Contains support classes for Transformers.
*
* @since 3.0
*
*/
package org.springframework.integration.transformer.support;

View File

@@ -1229,6 +1229,42 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="header" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Each header sub-element provides the name of a message header (via the required 'name' attribute).
Exactly one of the 'value' or 'expression' attributes must be provided 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.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType >
<xsd:complexContent>
<xsd:extension base="propertySubElementType">
<xsd:attribute name="overwrite" default="true">
<xsd:annotation>
<xsd:documentation>
Boolean value to indicate whether this header value should overwrite an
existing header value. Unlike the Header Enricher, this attribute is 'true'
by default, similar to the 'property' attribute.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="type" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class">
The fully qualified class name of the header value's expected type.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="request-handler-advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="request-channel" type="xsd:string" use="optional">

View File

@@ -15,19 +15,28 @@
<channel id="requests"/>
<enricher id="enricher" input-channel="input"
<enricher id="enricher" input-channel="input"
request-channel="requests" request-timeout="1234"
reply-timeout="9876"
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"/>
<header name="foo" value="bar"/>
<header name="testBean" expression="@testBean"/>
<header name="sourceName" expression="payload.sourceName"/>
<header name="notOverwrite" expression="payload.sourceName" overwrite="false"/>
<request-handler-advice-chain>
<beans:bean class="org.springframework.integration.config.xml.EnricherParserTests$FooAdvice" />
</request-handler-advice-chain>
</enricher>
<enricher input-channel="input2" output-channel="output">
<header name="foo" expression="new java.util.Date()" type="int"/>
</enricher>
<beans:bean id="testBean" class="java.lang.String">
<beans:constructor-arg value="male"/>
</beans:bean>

View File

@@ -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.
@@ -19,23 +19,30 @@ package org.springframework.integration.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.TypeMismatchException;
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.MessageHandlingException;
import org.springframework.integration.MessageHeaders;
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.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.transformer.ContentEnricher;
@@ -46,6 +53,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Mark Fisher
* @author Gunnar Hillert
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.1
*/
@@ -124,7 +132,10 @@ public class EnricherParserTests {
}
});
Target original = new Target();
Message<?> request = MessageBuilder.withPayload(original).build();
Message<?> request = MessageBuilder.withPayload(original)
.setHeader("sourceName", "test")
.setHeader("notOverwrite", "test")
.build();
context.getBean("input", MessageChannel.class).send(request);
Message<?> reply = context.getBean("output", PollableChannel.class).receive(0);
Target enriched = (Target) reply.getPayload();
@@ -133,6 +144,26 @@ public class EnricherParserTests {
assertEquals("male", enriched.getGender());
assertNotSame(original, enriched);
assertEquals(1, adviceCalled);
MessageHeaders headers = reply.getHeaders();
assertEquals("bar", headers.get("foo"));
assertEquals("male", headers.get("testBean"));
assertEquals("foo", headers.get("sourceName"));
assertEquals("test", headers.get("notOverwrite"));
}
@Test
public void testInt3027WrongHeaderType() {
MessageChannel input = context.getBean("input2", MessageChannel.class);
try {
input.send(new GenericMessage<Object>("test"));
}
catch (Exception e) {
assertThat(e, Matchers.instanceOf(MessageHandlingException.class));
assertThat(e.getCause(), Matchers.instanceOf(TypeMismatchException.class));
assertThat(e.getCause().getMessage(),
Matchers.startsWith("Failed to convert value of type 'java.util.Date' to required type 'int'"));
}
}
private static class Source {