INT-4365: Improve notPropagatedHeaders function
JIRA: https://jira.spring.io/browse/INT-4365 It is much useful to configure the `notPropagatedHeaders` as a set of patterns to match. In this case we can filter a group of headers with the common prefix or suffix * Allow to configure `AbstractMessageProducingHandler.setNotPropagatedHeaders` as simple patterns; the `*` means filter all - not copy request headers at all - similar to `transformer` behavior * Add `ConsumerEndpointSpec.notPropagatedHeaders()` for Java DSL * Add `not-propagated-headers` to the `<service-activator>` Address PR comments; some other improvements * Fix `ConsumerEndpointSpec#notPropagatedHeaders()` log message * Improve `AbstractMessageProducingHandler.notPropagatedHeaders() logic so any `*` in the set of patterns eliminates all others since it has a highest priority * Expose `requires-reply` for the `<transformer>` as a `true` by default * Refactor a bit `AbstractStandardMessageHandlerFactoryBean` hierarchy to avoid duplicated code * Fix `message.adoc` * Add `noHeadersPropagation` flag to the `AbstractMessageProducingHandler` * Rework logic in the `updateNotPropagatedHeaders()` to store the array of patterns instead of `Set` to avoid extra operation on each message * Combine `noHeadersPropagation` with the `shouldCopyRequestHeaders()` in the `createOutputMessage()` for logic to determine if we should start the copy-headers procedure at all * Revert `AbstractMessageProducingHandler.selectiveHeaderPropagation` * Optimize `AmqpOutboundGatewayParserTests` performance from 3 secs to 0.5 Fix more NPEs in the `AbstractMessageProducingHandler`
This commit is contained in:
committed by
Gary Russell
parent
7c701ca5e6
commit
90c46f5a79
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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,7 +23,6 @@ import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
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.handler.AbstractMessageProducingHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
@@ -44,10 +43,11 @@ import org.springframework.util.StringUtils;
|
||||
public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
extends AbstractSimpleMessageHandlerFactoryBean<MessageHandler> {
|
||||
|
||||
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true,
|
||||
true));
|
||||
private static final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||
|
||||
private static final Set<MessageHandler> referencedReplyProducers = new HashSet<MessageHandler>();
|
||||
private static final Set<MessageHandler> referencedReplyProducers = new HashSet<>();
|
||||
|
||||
private volatile Boolean requiresReply;
|
||||
|
||||
private volatile Object targetObject;
|
||||
|
||||
@@ -55,6 +55,8 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
private volatile Expression expression;
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
/**
|
||||
* Set the target POJO for the message handler.
|
||||
* @param targetObject the target object.
|
||||
@@ -87,6 +89,18 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
public void setRequiresReply(Boolean requiresReply) {
|
||||
this.requiresReply = requiresReply;
|
||||
}
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public Long getSendTimeout() {
|
||||
return this.sendTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler() {
|
||||
MessageHandler handler;
|
||||
@@ -100,8 +114,8 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
AbstractMessageProducingHandler actualHandler = this.extractTypeIfPossible(this.targetObject,
|
||||
AbstractMessageProducingHandler.class);
|
||||
boolean targetIsDirectReplyProducingHandler = actualHandler != null
|
||||
&& this.canBeUsedDirect(actualHandler) // give subclasses a say
|
||||
&& this.methodIsHandleMessageOrEmpty(this.targetMethodName);
|
||||
&& canBeUsedDirect(actualHandler) // give subclasses a say
|
||||
&& methodIsHandleMessageOrEmpty(this.targetMethodName);
|
||||
if (this.targetObject instanceof MessageProcessor<?>) {
|
||||
handler = this.createMessageProcessingHandler((MessageProcessor<?>) this.targetObject);
|
||||
}
|
||||
@@ -109,8 +123,8 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Wiring handler (" + this.targetObject + ") directly into endpoint");
|
||||
}
|
||||
this.checkReuse(actualHandler);
|
||||
this.postProcessReplyProducer(actualHandler);
|
||||
checkReuse(actualHandler);
|
||||
postProcessReplyProducer(actualHandler);
|
||||
handler = (MessageHandler) this.targetObject;
|
||||
}
|
||||
else {
|
||||
@@ -142,7 +156,7 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
private void checkReuse(AbstractMessageProducingHandler replyHandler) {
|
||||
Assert.isTrue(!referencedReplyProducers.contains(replyHandler),
|
||||
"An AbstractMessageProducingMessageHandler may only be referenced once (" +
|
||||
replyHandler.getComponentName() + ") - use scope=\"prototype\"");
|
||||
replyHandler.getComponentName() + ") - use scope=\"prototype\"");
|
||||
referencedReplyProducers.add(replyHandler);
|
||||
}
|
||||
|
||||
@@ -176,9 +190,6 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
}
|
||||
if (targetObject instanceof Advised) {
|
||||
TargetSource targetSource = ((Advised) targetObject).getTargetSource();
|
||||
if (targetSource == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return extractTypeIfPossible(targetSource.getTarget(), expectedType);
|
||||
}
|
||||
@@ -199,6 +210,21 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
}
|
||||
|
||||
protected void postProcessReplyProducer(AbstractMessageProducingHandler handler) {
|
||||
if (this.sendTimeout != null) {
|
||||
handler.setSendTimeout(this.sendTimeout);
|
||||
}
|
||||
|
||||
if (this.requiresReply != null) {
|
||||
if (handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) handler).setRequiresReply(this.requiresReply);
|
||||
}
|
||||
else {
|
||||
if (this.requiresReply && logger.isDebugEnabled()) {
|
||||
logger.debug("requires-reply can only be set to AbstractReplyProducingMessageHandler " +
|
||||
"or its subclass, " + handler.getComponentName() + " doesn't support it.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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,6 +34,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author David Liu
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FilterFactoryBean extends AbstractStandardMessageHandlerFactoryBean {
|
||||
@@ -42,8 +44,6 @@ public class FilterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
private volatile Boolean throwExceptionOnRejection;
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
private volatile Boolean discardWithinAdvice;
|
||||
|
||||
public void setDiscardChannel(MessageChannel discardChannel) {
|
||||
@@ -54,10 +54,6 @@ public class FilterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
this.throwExceptionOnRejection = throwExceptionOnRejection;
|
||||
}
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public void setDiscardWithinAdvice(boolean discardWithinAdvice) {
|
||||
this.discardWithinAdvice = discardWithinAdvice;
|
||||
}
|
||||
@@ -113,12 +109,12 @@ public class FilterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
@Override
|
||||
protected void postProcessReplyProducer(AbstractMessageProducingHandler handler) {
|
||||
if (this.sendTimeout != null) {
|
||||
handler.setSendTimeout(this.sendTimeout);
|
||||
}
|
||||
super.postProcessReplyProducer(handler);
|
||||
|
||||
if (!(handler instanceof MessageFilter)) {
|
||||
Assert.isNull(this.throwExceptionOnRejection, "Cannot set throwExceptionOnRejection if the referenced bean is "
|
||||
+ "an AbstractReplyProducingMessageHandler, but not a MessageFilter");
|
||||
Assert.isNull(this.throwExceptionOnRejection,
|
||||
"Cannot set throwExceptionOnRejection if the referenced bean is "
|
||||
+ "an AbstractReplyProducingMessageHandler, but not a MessageFilter");
|
||||
Assert.isNull(this.discardChannel, "Cannot set discardChannel if the referenced bean is "
|
||||
+ "an AbstractReplyProducingMessageHandler, but not a MessageFilter");
|
||||
Assert.isNull(this.discardWithinAdvice, "Cannot set discardWithinAdvice if the referenced bean is "
|
||||
@@ -137,8 +133,8 @@ public class FilterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
protected boolean canBeUsedDirect(AbstractMessageProducingHandler handler) {
|
||||
return handler instanceof MessageFilter
|
||||
|| (!(handler instanceof MessageSelector)
|
||||
&& this.discardChannel == null && this.throwExceptionOnRejection == null
|
||||
&& this.discardWithinAdvice == null);
|
||||
&& this.discardChannel == null && this.throwExceptionOnRejection == null
|
||||
&& this.discardWithinAdvice == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.StringUtils;
|
||||
* @author Dave Syer
|
||||
* @author Gary Russell
|
||||
* @author David Liu
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean {
|
||||
|
||||
@@ -47,8 +48,6 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
private volatile String defaultOutputChannelName;
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
private volatile Boolean resolutionRequired;
|
||||
|
||||
private volatile Boolean applySequence;
|
||||
@@ -63,10 +62,6 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
this.defaultOutputChannelName = defaultOutputChannelName;
|
||||
}
|
||||
|
||||
public void setSendTimeout(Long timeout) {
|
||||
this.sendTimeout = timeout;
|
||||
}
|
||||
|
||||
public void setResolutionRequired(Boolean resolutionRequired) {
|
||||
this.resolutionRequired = resolutionRequired;
|
||||
}
|
||||
@@ -124,8 +119,8 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
if (this.defaultOutputChannelName != null) {
|
||||
router.setDefaultOutputChannelName(this.defaultOutputChannelName);
|
||||
}
|
||||
if (this.sendTimeout != null) {
|
||||
router.setSendTimeout(this.sendTimeout);
|
||||
if (getSendTimeout() != null) {
|
||||
router.setSendTimeout(getSendTimeout());
|
||||
}
|
||||
if (this.applySequence != null) {
|
||||
router.setApplySequence(this.applySequence);
|
||||
@@ -155,7 +150,7 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
protected boolean noRouterAttributesProvided() {
|
||||
return this.channelMappings == null && this.defaultOutputChannel == null
|
||||
&& this.sendTimeout == null && this.resolutionRequired == null && this.applySequence == null
|
||||
&& getSendTimeout() == null && this.resolutionRequired == null && this.applySequence == null
|
||||
&& this.ignoreSendFailures == null;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.handler.AbstractMessageProducingHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper;
|
||||
@@ -38,16 +39,10 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class ServiceActivatorFactoryBean extends AbstractStandardMessageHandlerFactoryBean {
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
private String[] headers;
|
||||
|
||||
private volatile Boolean requiresReply;
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public void setRequiresReply(Boolean requiresReply) {
|
||||
this.requiresReply = requiresReply;
|
||||
public void setNotPropagatedHeaders(String... headers) {
|
||||
this.headers = Arrays.copyOf(headers, headers.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,19 +114,10 @@ public class ServiceActivatorFactoryBean extends AbstractStandardMessageHandlerF
|
||||
|
||||
@Override
|
||||
protected void postProcessReplyProducer(AbstractMessageProducingHandler handler) {
|
||||
if (this.sendTimeout != null) {
|
||||
handler.setSendTimeout(this.sendTimeout);
|
||||
}
|
||||
if (this.requiresReply != null) {
|
||||
if (handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) handler).setRequiresReply(this.requiresReply);
|
||||
}
|
||||
else {
|
||||
if (this.requiresReply && logger.isDebugEnabled()) {
|
||||
logger.debug("requires-reply can only be set to AbstractReplyProducingMessageHandler or its subclass, "
|
||||
+ handler.getComponentName() + " doesn't support it.");
|
||||
}
|
||||
}
|
||||
super.postProcessReplyProducer(handler);
|
||||
|
||||
if (this.headers != null) {
|
||||
handler.setNotPropagatedHeaders(this.headers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.integration.config;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.handler.AbstractMessageProducingHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.splitter.AbstractMessageSplitter;
|
||||
import org.springframework.integration.splitter.DefaultMessageSplitter;
|
||||
import org.springframework.integration.splitter.ExpressionEvaluatingSplitter;
|
||||
@@ -34,30 +33,14 @@ import org.springframework.util.StringUtils;
|
||||
* @author Iwein Fuld
|
||||
* @author Gary Russell
|
||||
* @author David Liu
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class SplitterFactoryBean extends AbstractStandardMessageHandlerFactoryBean {
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
private volatile Boolean requiresReply;
|
||||
|
||||
private volatile Boolean applySequence;
|
||||
|
||||
private volatile String delimiters;
|
||||
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public boolean isRequiresReply() {
|
||||
return this.requiresReply;
|
||||
}
|
||||
|
||||
public void setRequiresReply(boolean requiresReply) {
|
||||
this.requiresReply = requiresReply;
|
||||
}
|
||||
|
||||
public void setApplySequence(boolean applySequence) {
|
||||
this.applySequence = applySequence;
|
||||
}
|
||||
@@ -116,18 +99,8 @@ public class SplitterFactoryBean extends AbstractStandardMessageHandlerFactoryBe
|
||||
|
||||
@Override
|
||||
protected void postProcessReplyProducer(AbstractMessageProducingHandler handler) {
|
||||
if (this.sendTimeout != null) {
|
||||
handler.setSendTimeout(this.sendTimeout);
|
||||
}
|
||||
if (this.requiresReply != null) {
|
||||
if (handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) handler).setRequiresReply(this.requiresReply);
|
||||
}
|
||||
else if (this.requiresReply && logger.isDebugEnabled()) {
|
||||
logger.debug("requires-reply can only be set to AbstractReplyProducingMessageHandler or its subclass, "
|
||||
+ handler.getComponentName() + " doesn't support it.");
|
||||
}
|
||||
}
|
||||
super.postProcessReplyProducer(handler);
|
||||
|
||||
if (!(handler instanceof AbstractMessageSplitter)) {
|
||||
Assert.isNull(this.applySequence, "Cannot set applySequence if the referenced bean is "
|
||||
+ "an AbstractReplyProducingMessageHandler, but not an AbstractMessageSplitter");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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,13 +32,12 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author David Liu
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class TransformerFactoryBean extends AbstractStandardMessageHandlerFactoryBean {
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
|
||||
public void setSendTimeout(Long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
public TransformerFactoryBean() {
|
||||
setRequiresReply(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,13 +73,6 @@ public class TransformerFactoryBean extends AbstractStandardMessageHandlerFactor
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessReplyProducer(AbstractMessageProducingHandler handler) {
|
||||
if (this.sendTimeout != null) {
|
||||
handler.setSendTimeout(this.sendTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns true - any {@link AbstractMessageProducingHandler} can
|
||||
* be used directly.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -28,6 +28,7 @@ import org.springframework.integration.config.ServiceActivatorFactoryBean;
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artme Bilan
|
||||
*/
|
||||
public class ServiceActivatorParser extends AbstractDelegatingConsumerEndpointParser {
|
||||
|
||||
@@ -44,6 +45,7 @@ public class ServiceActivatorParser extends AbstractDelegatingConsumerEndpointPa
|
||||
@Override
|
||||
void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "async");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "not-propagated-headers");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
|
||||
((AbstractReplyProducingMessageHandler) this.handler).setRequiresReply(requiresReply);
|
||||
}
|
||||
else {
|
||||
logger.warn("'requiresReply' can be applied only for AbstractReplyProducingMessageHandler");
|
||||
this.logger.warn("'requiresReply' can be applied only for AbstractReplyProducingMessageHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
@@ -207,7 +207,7 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
|
||||
((AbstractMessageRouter) this.handler).setSendTimeout(sendTimeout);
|
||||
}
|
||||
else {
|
||||
logger.warn("'sendTimeout' can be applied only for AbstractMessageProducingHandler");
|
||||
this.logger.warn("'sendTimeout' can be applied only for AbstractMessageProducingHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
@@ -223,7 +223,7 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
|
||||
((AbstractMessageHandler) this.handler).setOrder(order);
|
||||
}
|
||||
else {
|
||||
logger.warn("'order' can be applied only for AbstractMessageHandler");
|
||||
this.logger.warn("'order' can be applied only for AbstractMessageHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
@@ -244,7 +244,26 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
|
||||
((AbstractMessageProducingHandler) this.handler).setAsync(async);
|
||||
}
|
||||
else {
|
||||
logger.warn("'async' can be applied only for AbstractMessageProducingHandler");
|
||||
this.logger.warn("'async' can be applied only for AbstractMessageProducingHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set header patterns ("xxx*", "*xxx", "*xxx*" or "xxx*yyy")
|
||||
* that will NOT be copied from the inbound message.
|
||||
* At least one pattern as "*" means do not copy headers at all.
|
||||
* @param headerPatterns the headers to not propagate from the inbound message.
|
||||
* @return the endpoint spec.
|
||||
* @see AbstractMessageProducingHandler#setNotPropagatedHeaders(String...)
|
||||
*/
|
||||
public S notPropagatedHeaders(String... headerPatterns) {
|
||||
assertHandler();
|
||||
if (this.handler instanceof AbstractMessageProducingHandler) {
|
||||
((AbstractMessageProducingHandler) this.handler).setNotPropagatedHeaders(headerPatterns);
|
||||
}
|
||||
else {
|
||||
this.logger.warn("'headerPatterns' can be applied only for AbstractMessageProducingHandler");
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.messaging.core.DestinationResolutionException;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureCallback;
|
||||
@@ -64,18 +65,20 @@ import reactor.core.publisher.Mono;
|
||||
public abstract class AbstractMessageProducingHandler extends AbstractMessageHandler
|
||||
implements MessageProducer, HeaderPropagationAware {
|
||||
|
||||
private final Set<String> notPropagatedHeaders = new HashSet<String>();
|
||||
|
||||
protected final MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
|
||||
private volatile MessageChannel outputChannel;
|
||||
private boolean async;
|
||||
|
||||
private volatile String outputChannelName;
|
||||
private String outputChannelName;
|
||||
|
||||
private volatile boolean async;
|
||||
private MessageChannel outputChannel;
|
||||
|
||||
private String[] notPropagatedHeaders;
|
||||
|
||||
private boolean selectiveHeaderPropagation;
|
||||
|
||||
private boolean noHeadersPropagation;
|
||||
|
||||
/**
|
||||
* Set the timeout for sending reply Messages.
|
||||
* @param sendTimeout The send timeout.
|
||||
@@ -115,10 +118,13 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
}
|
||||
|
||||
/**
|
||||
* Set headers that will NOT be copied from the inbound message if
|
||||
* Set header patterns ("xxx*", "*xxx", "*xxx*" or "xxx*yyy")
|
||||
* that will NOT be copied from the inbound message if
|
||||
* {@link #shouldCopyRequestHeaders() shouldCopyRequestHeaaders} is true.
|
||||
* At least one pattern as "*" means do not copy headers at all.
|
||||
* @param headers the headers to not propagate from the inbound message.
|
||||
* @since 4.3.10
|
||||
* @see org.springframework.util.PatternMatchUtils
|
||||
*/
|
||||
@Override
|
||||
public void setNotPropagatedHeaders(String... headers) {
|
||||
@@ -126,30 +132,48 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
}
|
||||
|
||||
private void updateNotPropagatedHeaders(String[] headers, boolean merge) {
|
||||
Set<String> headerPatterns = new HashSet<>();
|
||||
|
||||
if (merge) {
|
||||
headerPatterns.addAll(Arrays.asList(this.notPropagatedHeaders));
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(headers)) {
|
||||
Assert.noNullElements(headers, "null elements are not allowed in 'headers'");
|
||||
if (!merge) {
|
||||
this.notPropagatedHeaders.clear();
|
||||
}
|
||||
this.notPropagatedHeaders.addAll(Arrays.asList(headers));
|
||||
|
||||
headerPatterns.addAll(Arrays.asList(headers));
|
||||
|
||||
this.notPropagatedHeaders = headerPatterns.toArray(new String[headerPatterns.size()]);
|
||||
}
|
||||
this.selectiveHeaderPropagation = this.notPropagatedHeaders.size() > 0;
|
||||
|
||||
boolean hasAsterisk = headerPatterns.contains("*");
|
||||
|
||||
if (hasAsterisk) {
|
||||
this.notPropagatedHeaders = new String[] { "*" };
|
||||
this.noHeadersPropagation = true;
|
||||
}
|
||||
|
||||
this.selectiveHeaderPropagation = this.notPropagatedHeaders.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header names this handler doesn't propagate.
|
||||
* Get the header patterns this handler doesn't propagate.
|
||||
* @return an immutable {@link java.util.Collection} of headers that will not be
|
||||
* copied from the inbound message if {@link #shouldCopyRequestHeaders()} is true.
|
||||
* @since 4.3.10
|
||||
* @see #setNotPropagatedHeaders(String...)
|
||||
* @see org.springframework.util.PatternMatchUtils
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getNotPropagatedHeaders() {
|
||||
return Collections.unmodifiableSet(this.notPropagatedHeaders);
|
||||
return this.notPropagatedHeaders != null
|
||||
? Collections.unmodifiableSet(new HashSet<>(Arrays.asList(this.notPropagatedHeaders)))
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add headers that will NOT be copied from the inbound message if
|
||||
* Add header patterns ("xxx*", "*xxx", "*xxx*" or "xxx*yyy")
|
||||
* that will NOT be copied from the inbound message if
|
||||
* {@link #shouldCopyRequestHeaders()} is true, instead of overwriting the existing
|
||||
* set.
|
||||
* @param headers the headers to not propagate from the inbound message.
|
||||
@@ -344,7 +368,7 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
protected Message<?> createOutputMessage(Object output, MessageHeaders requestHeaders) {
|
||||
AbstractIntegrationMessageBuilder<?> builder = null;
|
||||
if (output instanceof Message<?>) {
|
||||
if (!this.shouldCopyRequestHeaders()) {
|
||||
if (this.noHeadersPropagation || !shouldCopyRequestHeaders()) {
|
||||
return (Message<?>) output;
|
||||
}
|
||||
builder = this.getMessageBuilderFactory().fromMessage((Message<?>) output);
|
||||
@@ -355,12 +379,13 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
else {
|
||||
builder = this.getMessageBuilderFactory().withPayload(output);
|
||||
}
|
||||
if (this.shouldCopyRequestHeaders()) {
|
||||
if (!this.noHeadersPropagation && shouldCopyRequestHeaders()) {
|
||||
if (this.selectiveHeaderPropagation) {
|
||||
Map<String, Object> headersToCopy = new HashMap<String, Object>(requestHeaders);
|
||||
for (String header : this.notPropagatedHeaders) {
|
||||
headersToCopy.remove(header);
|
||||
}
|
||||
Map<String, Object> headersToCopy = new HashMap<>(requestHeaders);
|
||||
|
||||
headersToCopy.entrySet()
|
||||
.removeIf(entry -> PatternMatchUtils.simpleMatch(this.notPropagatedHeaders, entry.getKey()));
|
||||
|
||||
builder.copyHeadersIfAbsent(headersToCopy);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1212,16 +1212,7 @@
|
||||
<xsd:complexType name="serviceActivatorType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
|
||||
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether the service method must return a non-null value. This value will be
|
||||
'false' by default, but if set to 'true', a ReplyRequiredException will be thrown when
|
||||
the underlying service method (or expression) returns a null value.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="async" type="xsd:string" use="optional">
|
||||
<xsd:attribute name="async">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
If the service method returns a ListenableFuture<?> and this flag is 'true', the calling
|
||||
@@ -1231,6 +1222,15 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="not-propagated-headers">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Header patterns ("xxx*", "*xxx", "*xxx*" or "xxx*yyy")
|
||||
that will NOT be copied from the inbound message.
|
||||
'*' means do not copy headers at all.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
@@ -3617,15 +3617,6 @@
|
||||
<xsd:complexType name="splitter-type">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
|
||||
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether the service method must return a non-null value. This value will be
|
||||
'false' by default, but if set to 'true', a ReplyRequiredException will be thrown when
|
||||
the underlying service method (or expression) returns a null value.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="apply-sequence" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -4190,6 +4181,20 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="requires-reply">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
If set to 'true', a reply must return a non-null value.
|
||||
By setting 'requires-reply' to 'true', a 'ReplyRequiredException'
|
||||
will be raised for null reply messages. If 'requires-reply' is set
|
||||
to false, those messages are silently dropped.
|
||||
This attribute defaults to 'true' for 'transformer'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @author Marius Bogoevici
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AbstractReplyProducingMessageHandlerTests {
|
||||
@@ -80,19 +81,20 @@ public class AbstractReplyProducingMessageHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testNotPropagate() {
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
return new GenericMessage<String>("world", Collections.singletonMap("bar", "RAB"));
|
||||
return new GenericMessage<>("world", Collections.singletonMap("bar", "RAB"));
|
||||
}
|
||||
|
||||
};
|
||||
assertThat(handler.getNotPropagatedHeaders(), emptyCollectionOf(String.class));
|
||||
handler.setNotPropagatedHeaders("foo", "bar");
|
||||
handler.setNotPropagatedHeaders("f*", "*r");
|
||||
handler.setOutputChannel(this.channel);
|
||||
assertThat(handler.getNotPropagatedHeaders(), containsInAnyOrder("foo", "bar"));
|
||||
assertThat(handler.getNotPropagatedHeaders(), containsInAnyOrder("f*", "*r"));
|
||||
ArgumentCaptor<Message<?>> captor = ArgumentCaptor.forClass(Message.class);
|
||||
willReturn(true).given(this.channel).send(captor.capture());
|
||||
handler.handleMessage(MessageBuilder.withPayload("hello")
|
||||
@@ -120,9 +122,9 @@ public class AbstractReplyProducingMessageHandlerTests {
|
||||
};
|
||||
assertThat(handler.getNotPropagatedHeaders(), emptyCollectionOf(String.class));
|
||||
handler.setNotPropagatedHeaders("foo");
|
||||
handler.addNotPropagatedHeaders("bar");
|
||||
handler.addNotPropagatedHeaders("b*r");
|
||||
handler.setOutputChannel(this.channel);
|
||||
assertThat(handler.getNotPropagatedHeaders(), containsInAnyOrder("foo", "bar"));
|
||||
assertThat(handler.getNotPropagatedHeaders(), containsInAnyOrder("foo", "b*r"));
|
||||
ArgumentCaptor<Message<?>> captor =
|
||||
(ArgumentCaptor<Message<?>>) (ArgumentCaptor<?>) ArgumentCaptor.forClass(Message.class);
|
||||
willReturn(true).given(this.channel).send(captor.capture());
|
||||
|
||||
@@ -38,7 +38,10 @@
|
||||
class="org.springframework.integration.handler.ServiceActivatorDefaultFrameworkMethodTests$TestMessageHandler"/>
|
||||
</service-activator>
|
||||
|
||||
<service-activator id="processorTestService" input-channel="processorTestInputChannel" ref="testMessageProcessor"/>
|
||||
<service-activator id="processorTestService"
|
||||
input-channel="processorTestInputChannel"
|
||||
ref="testMessageProcessor"
|
||||
not-propagated-headers="*"/>
|
||||
|
||||
<gateway id="gateway" default-request-channel="requestChannel" default-reply-channel="replyChannel"/>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.handler;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -27,6 +28,7 @@ import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.integration.test.matcher.HeaderMatcher.hasHeaderKey;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -189,18 +191,20 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
|
||||
this.handlerTestInputChannel.send(message);
|
||||
}
|
||||
|
||||
// INT-2399
|
||||
@Test
|
||||
public void testMessageProcessor() {
|
||||
Object processor = TestUtils.getPropertyValue(processorTestService, "handler.processor");
|
||||
assertSame(testMessageProcessor, processor);
|
||||
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("bar").setReplyChannel(replyChannel).build();
|
||||
Message<?> message = MessageBuilder.withPayload("bar")
|
||||
.setReplyChannel(replyChannel)
|
||||
.setHeader("foo", "foo")
|
||||
.build();
|
||||
this.processorTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("foo:bar", reply.getPayload());
|
||||
assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString());
|
||||
assertThat(reply, not(hasHeaderKey("foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
<beans:bean class="org.springframework.integration.transformer.TransformerContextTests$Bar"/>
|
||||
</transformer>
|
||||
|
||||
<transformer input-channel="directRef" output-channel="output" ref="trans" method="handleMessage"/>
|
||||
<transformer input-channel="directRef" output-channel="output" ref="trans" method="handleMessage"
|
||||
requires-reply="false"/>
|
||||
|
||||
<beans:bean id="trans" class="org.springframework.integration.transformer.TransformerContextTests$Bar"/>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.integration.transformer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -90,6 +91,9 @@ public class TransformerContextTests {
|
||||
assertFalse(this.testBean.isRunning());
|
||||
this.pojoTransformer.start();
|
||||
assertTrue(this.testBean.isRunning());
|
||||
|
||||
this.directRef.send(new GenericMessage<String>("bar"));
|
||||
assertNull(this.output.receive(0));
|
||||
}
|
||||
|
||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||
@@ -106,6 +110,9 @@ public class TransformerContextTests {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if ("bar".equals(requestMessage.getPayload())) {
|
||||
return null;
|
||||
}
|
||||
Exception e = new RuntimeException();
|
||||
StackTraceElement[] st = e.getStackTrace();
|
||||
return MessageBuilder.withPayload(requestMessage.getPayload().toString().toUpperCase())
|
||||
|
||||
Reference in New Issue
Block a user