INT-2405 fix MessageProcessor for <header-value>
1. MethodInvokingHeaderValueMessageProcessor => MessageProcessingHeaderValueMessageProcessor because now he uses MessageProcessor 2. polishing HeaderEnricherParserSupport 3. GroovyHeaderEnricherTests: add check to HeaderValueMessageProcessor for Groovy scripts. 4. build.gradle: add dependency to 'spring-integration-test' for 'spring-integration-groovy'. 5. Fix HeaderEnricherParserSupport like in INT-2188
This commit is contained in:
committed by
Oleg Zhurakousky
parent
ec003d8c05
commit
95f29507a7
@@ -224,6 +224,7 @@ project('spring-integration-groovy') {
|
||||
compile project(":spring-integration-core")
|
||||
compile "org.codehaus.groovy:groovy-all:1.7.5"
|
||||
compile "org.springframework:spring-context-support:$springVersion"
|
||||
testCompile project(":spring-integration-test")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.xml.DomUtils;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class HeaderEnricherParserSupport extends AbstractTransformerParser {
|
||||
@@ -116,33 +117,53 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
|
||||
String ref = headerElement.getAttribute("ref");
|
||||
String method = headerElement.getAttribute("method");
|
||||
String expression = headerElement.getAttribute("expression");
|
||||
|
||||
List<Element> subElements = DomUtils.getChildElements(headerElement);
|
||||
|
||||
BeanDefinition innerComponentDefinition = null;
|
||||
|
||||
Element beanElement = null;
|
||||
Element scriptElement = null;
|
||||
Element expressionElement = null;
|
||||
if (subElements != null && subElements.size() == 1) {
|
||||
Element beanElement = subElements.get(0);
|
||||
if ("expression".equals(beanElement.getNodeName())){
|
||||
expressionElement = beanElement;
|
||||
if (StringUtils.hasText(expression) && expressionElement != null) {
|
||||
parserContext.getReaderContext().error("The 'expression' attribute and sub-element are mutually exclusive", element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ("bean".equals(beanElement.getNodeName())){
|
||||
innerComponentDefinition = parserContext.getDelegate().parseBeanDefinitionElement(beanElement).getBeanDefinition();
|
||||
|
||||
List<Element> subElements = DomUtils.getChildElements(headerElement);
|
||||
if (!subElements.isEmpty()) {
|
||||
Element subElement = subElements.get(0);
|
||||
String subElementLocalName = subElement.getLocalName();
|
||||
if ("bean".equals(subElementLocalName)) {
|
||||
beanElement = subElement;
|
||||
}
|
||||
else {
|
||||
innerComponentDefinition = parserContext.getDelegate().parseCustomElement(beanElement);
|
||||
else if ("script".equals(subElementLocalName)) {
|
||||
scriptElement = subElement;
|
||||
}
|
||||
else if ("expression".equals(subElementLocalName)) {
|
||||
expressionElement = subElement;
|
||||
}
|
||||
if (beanElement == null && scriptElement == null && expressionElement == null) {
|
||||
parserContext.getReaderContext().error("Only 'bean', 'script' or 'expression' can be defined as a sub-element", element);
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(expression) && expressionElement != null) {
|
||||
parserContext.getReaderContext().error("The 'expression' attribute and sub-element are mutually exclusive", element);
|
||||
}
|
||||
|
||||
boolean isValue = StringUtils.hasText(value);
|
||||
boolean isRef = StringUtils.hasText(ref);
|
||||
boolean hasMethod = StringUtils.hasText(method);
|
||||
boolean isExpression = StringUtils.hasText(expression) || expressionElement != null;
|
||||
boolean isScript = scriptElement != null;
|
||||
|
||||
BeanDefinition innerComponentDefinition = null;
|
||||
|
||||
if (beanElement != null) {
|
||||
innerComponentDefinition = parserContext.getDelegate().parseBeanDefinitionElement(beanElement).getBeanDefinition();
|
||||
}
|
||||
else if (isScript) {
|
||||
innerComponentDefinition = parserContext.getDelegate().parseCustomElement(scriptElement);
|
||||
}
|
||||
|
||||
boolean isCustomBean = innerComponentDefinition != null;
|
||||
|
||||
|
||||
if (hasMethod && isScript) {
|
||||
parserContext.getReaderContext().error("The 'method' attribute cannot be used when a 'script' sub-element is defined", element);
|
||||
}
|
||||
|
||||
if (!(isValue ^ (isRef ^ (isExpression ^ isCustomBean)))) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Exactly one of the 'ref', 'value', 'expression' or inner bean is required.", element);
|
||||
@@ -178,17 +199,24 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
|
||||
}
|
||||
valueProcessorBuilder.addConstructorArgValue(headerType);
|
||||
}
|
||||
else if (isCustomBean){
|
||||
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MethodInvokingHeaderValueMessageProcessor");
|
||||
valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition);
|
||||
if (hasMethod){
|
||||
valueProcessorBuilder.addConstructorArgValue(method);
|
||||
}
|
||||
else if (isCustomBean) {
|
||||
if (StringUtils.hasText(headerElement.getAttribute("type"))) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'type' attribute cannot be used with an inner bean.", element);
|
||||
}
|
||||
if (hasMethod || isScript) {
|
||||
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MessageProcessingHeaderValueMessageProcessor");
|
||||
valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition);
|
||||
if (hasMethod) {
|
||||
valueProcessorBuilder.addConstructorArgValue(method);
|
||||
}
|
||||
}
|
||||
else {
|
||||
valueProcessorBuilder.addConstructorArgValue(null);
|
||||
}
|
||||
headers.put(headerName, valueProcessorBuilder.getBeanDefinition());
|
||||
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$StaticHeaderValueMessageProcessor");
|
||||
valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (StringUtils.hasText(headerElement.getAttribute("type"))) {
|
||||
@@ -197,7 +225,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
|
||||
}
|
||||
if (hasMethod) {
|
||||
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MethodInvokingHeaderValueMessageProcessor");
|
||||
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MessageProcessingHeaderValueMessageProcessor");
|
||||
valueProcessorBuilder.addConstructorArgReference(ref);
|
||||
valueProcessorBuilder.addConstructorArgValue(method);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* 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.
|
||||
@@ -23,6 +23,8 @@ 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;
|
||||
@@ -36,17 +38,18 @@ import org.springframework.integration.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* A Transformer that adds statically configured header values to a Message.
|
||||
* Accepts the boolean 'overwrite' property that specifies whether values
|
||||
* should be overwritten. By default, any existing header values for
|
||||
* a given key, will <em>not</em> be replaced.
|
||||
* Accepts the boolean 'overwrite' property that specifies whether values should
|
||||
* be overwritten. By default, any existing header values for a given key, will
|
||||
* <em>not</em> be replaced.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author David Turanski
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class HeaderEnricher implements Transformer {
|
||||
public class HeaderEnricher implements Transformer, BeanNameAware, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(HeaderEnricher.class);
|
||||
|
||||
|
||||
private final Map<String, ? extends HeaderValueMessageProcessor<?>> headersToAdd;
|
||||
|
||||
private volatile MessageProcessor<?> messageProcessor;
|
||||
@@ -55,6 +58,7 @@ public class HeaderEnricher implements Transformer {
|
||||
|
||||
private volatile boolean shouldSkipNulls = true;
|
||||
|
||||
private Object beanName;
|
||||
|
||||
public HeaderEnricher() {
|
||||
this(null);
|
||||
@@ -64,10 +68,10 @@ public class HeaderEnricher implements Transformer {
|
||||
* Create a HeaderEnricher with the given map of headers.
|
||||
*/
|
||||
public HeaderEnricher(Map<String, ? extends HeaderValueMessageProcessor<?>> headersToAdd) {
|
||||
this.headersToAdd = (headersToAdd != null) ? headersToAdd : new HashMap<String, HeaderValueMessageProcessor<Object>>();
|
||||
this.headersToAdd = (headersToAdd != null) ? headersToAdd
|
||||
: new HashMap<String, HeaderValueMessageProcessor<Object>>();
|
||||
}
|
||||
|
||||
|
||||
public <T> void setMessageProcessor(MessageProcessor<T> messageProcessor) {
|
||||
this.messageProcessor = messageProcessor;
|
||||
}
|
||||
@@ -77,9 +81,11 @@ public class HeaderEnricher implements Transformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether <code>null</code> values, such as might be returned from an expression evaluation,
|
||||
* should be skipped. The default value is <code>true</code>. Set this to <code>false</false> if a
|
||||
* <code>null</code> value should trigger <i>removal</i> of the corresponding header instead.
|
||||
* Specify whether <code>null</code> values, such as might be returned from
|
||||
* an expression evaluation, should be skipped. The default value is
|
||||
* <code>true</code>. Set this to <code>false</false> if a
|
||||
* <code>null</code> value should trigger <i>removal</i> of the
|
||||
* corresponding header instead.
|
||||
*/
|
||||
public void setShouldSkipNulls(boolean shouldSkipNulls) {
|
||||
this.shouldSkipNulls = shouldSkipNulls;
|
||||
@@ -92,20 +98,29 @@ public class HeaderEnricher implements Transformer {
|
||||
for (Map.Entry<String, ? extends HeaderValueMessageProcessor<?>> entry : this.headersToAdd.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
HeaderValueMessageProcessor<?> valueProcessor = entry.getValue();
|
||||
|
||||
Boolean shouldOverwrite = valueProcessor.isOverwrite();
|
||||
if (shouldOverwrite == null) {
|
||||
shouldOverwrite = this.defaultOverwrite;
|
||||
}
|
||||
Object value = valueProcessor.processMessage(message);
|
||||
if ((value != null && shouldOverwrite) || headerMap.get(key) == null || (value == null && !this.shouldSkipNulls)) {
|
||||
headerMap.put(key, value);
|
||||
|
||||
boolean headerDoesNotExist = headerMap.get(key) == null;
|
||||
|
||||
/**
|
||||
* Only evaluate value expression if necessary
|
||||
*/
|
||||
if (headerDoesNotExist || shouldOverwrite) {
|
||||
Object value = valueProcessor.processMessage(message);
|
||||
if (value != null || !this.shouldSkipNulls) {
|
||||
headerMap.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MessageBuilder.withPayload(message.getPayload()).copyHeaders(headerMap).build();
|
||||
}
|
||||
return MessageBuilder.withPayload(message.getPayload()).copyHeaders(headerMap).build();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MessagingException(message, "failed to transform message headers", e);
|
||||
}
|
||||
throw new MessagingException(message, "failed to transform message headers", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@@ -131,17 +146,16 @@ public class HeaderEnricher implements Transformer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// null indicates no explicit setting; use header-enricher's
|
||||
// 'default-overwrite' value
|
||||
private volatile Boolean overwrite = null;
|
||||
|
||||
public void setOverwrite(Boolean overwrite) {
|
||||
@@ -154,7 +168,6 @@ public class HeaderEnricher implements Transformer {
|
||||
|
||||
}
|
||||
|
||||
|
||||
static class StaticHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T> {
|
||||
|
||||
private final T value;
|
||||
@@ -168,24 +181,27 @@ public class HeaderEnricher implements Transformer {
|
||||
}
|
||||
}
|
||||
|
||||
static class ExpressionEvaluatingHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T>
|
||||
implements BeanFactoryAware {
|
||||
|
||||
static class ExpressionEvaluatingHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T> implements BeanFactoryAware {
|
||||
|
||||
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
|
||||
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.
|
||||
* 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.
|
||||
* 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);
|
||||
@@ -202,12 +218,51 @@ public class HeaderEnricher implements Transformer {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang
|
||||
* .String)
|
||||
*/
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
|
||||
static class MethodInvokingHeaderValueMessageProcessor extends AbstractHeaderValueMessageProcessor<Object> {
|
||||
}
|
||||
|
||||
private final MethodInvokingMessageProcessor<Object> targetProcessor;
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
boolean shouldOverwrite = this.defaultOverwrite;
|
||||
for (HeaderValueMessageProcessor<?> processor : this.headersToAdd.values()) {
|
||||
Boolean processerOverwrite = processor.isOverwrite();
|
||||
if (processerOverwrite != null) {
|
||||
shouldOverwrite |= processerOverwrite.booleanValue();
|
||||
}
|
||||
}
|
||||
if (!shouldOverwrite && !this.shouldSkipNulls) {
|
||||
logger.warn(this.beanName
|
||||
+ " is configured to not overwrite existing headers. 'shouldSkipNulls = false' will have no effect");
|
||||
}
|
||||
}
|
||||
|
||||
public MethodInvokingHeaderValueMessageProcessor(Object targetObject, String method) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<int:channel id="inputB"/>
|
||||
|
||||
<int:header-enricher input-channel="inputB" output-channel="outputB">
|
||||
<int:header-enricher id="headerEnricherWithInlineGroovyScript" input-channel="inputB" output-channel="outputB">
|
||||
<int:header name="TEST_HEADER">
|
||||
<int-groovy:script>
|
||||
<![CDATA[
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,6 +17,10 @@
|
||||
package org.springframework.integration.groovy.config;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -24,12 +28,18 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.groovy.GroovyScriptExecutingMessageProcessor;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.HeaderEnricher;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@@ -48,14 +58,26 @@ public class GroovyHeaderEnricherTests {
|
||||
@Autowired
|
||||
private QueueChannel outputB;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer headerEnricherWithInlineGroovyScript;
|
||||
|
||||
@Test
|
||||
public void referencedScript() throws Exception{
|
||||
inputA.send(new GenericMessage<String>("Hello"));
|
||||
assertEquals("groovy", outputA.receive(1000).getHeaders().get("TEST_HEADER"));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void inlineScript() throws Exception{
|
||||
Map<String, HeaderEnricher.HeaderValueMessageProcessor> headers =
|
||||
TestUtils.getPropertyValue(headerEnricherWithInlineGroovyScript, "handler.transformer.headersToAdd", Map.class);
|
||||
assertEquals(1, headers.size());
|
||||
HeaderEnricher.HeaderValueMessageProcessor headerValueMessageProcessor = headers.get("TEST_HEADER");
|
||||
assertThat(headerValueMessageProcessor.getClass().getName(), Matchers.containsString("HeaderEnricher$MessageProcessingHeaderValueMessageProcessor"));
|
||||
Object targetProcessor = TestUtils.getPropertyValue(headerValueMessageProcessor, "targetProcessor");
|
||||
assertEquals(GroovyScriptExecutingMessageProcessor.class, targetProcessor.getClass());
|
||||
|
||||
inputB.send(new GenericMessage<String>("Hello"));
|
||||
assertEquals("groovy", outputB.receive(1000).getHeaders().get("TEST_HEADER"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user