INT-3943: AMQP Async Outbound Gateway

JIRA: https://jira.spring.io/browse/INT-3943

Initial commit.

Polishing; Address PR Comments; Docs

Doc Polishing

Async GW - Support requiresReply

Polishing.

Avoid extra `requiresReply` in the `AsyncAmqpOutboundGateway`
This commit is contained in:
Gary Russell
2016-03-01 15:54:53 -05:00
committed by Artem Bilan
parent 78aaa6dac6
commit d4e6615f82
16 changed files with 1357 additions and 444 deletions

View File

@@ -32,6 +32,7 @@ import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.core.DestinationResolutionException;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.concurrent.ListenableFuture;
@@ -82,10 +83,19 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
*
* @since 4.3
*/
protected void setAsyncReplySupported(boolean asyncReplySupported) {
protected final void setAsyncReplySupported(boolean asyncReplySupported) {
this.asyncReplySupported = asyncReplySupported;
}
/**
* @see #setAsyncReplySupported(boolean)
* @return true if this handler supports async replies.
* @since 4.3
*/
protected boolean getAsyncReplySupported() {
return this.asyncReplySupported;
}
@Override
protected void onInit() throws Exception {
super.onInit();
@@ -174,13 +184,18 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
@Override
public void onSuccess(Object result) {
Message<?> replyMessage = null;
try {
sendOutput(createOutputMessage(result, requestHeaders), theReplyChannel, false);
replyMessage = createOutputMessage(result, requestHeaders);
sendOutput(replyMessage, theReplyChannel, false);
}
catch (Exception e) {
Exception exceptionToLogAndSend = e;
if (!(e instanceof MessagingException)) {
exceptionToLogAndSend = new MessageHandlingException(requestMessage, e);
if (replyMessage != null) {
exceptionToLogAndSend = new MessagingException(replyMessage, exceptionToLogAndSend);
}
}
logger.error("Failed to send async reply: " + result.toString(), exceptionToLogAndSend);
onFailure(exceptionToLogAndSend);
@@ -200,7 +215,7 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
}
else {
try {
sendOutput(createOutputMessage(result, requestHeaders), errorChannel, true);
sendOutput(new ErrorMessage(result), errorChannel, true);
}
catch (Exception e) {
Exception exceptionToLog = e;
@@ -254,7 +269,7 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
}
}
private Message<?> createOutputMessage(Object output, MessageHeaders requestHeaders) {
protected Message<?> createOutputMessage(Object output, MessageHeaders requestHeaders) {
AbstractIntegrationMessageBuilder<?> builder = null;
if (output instanceof Message<?>) {
if (!this.shouldCopyRequestHeaders()) {
@@ -280,12 +295,12 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
* <code>null</code>, and it must be an instance of either String or {@link MessageChannel}.
* @param output the output object to send
* @param replyChannel the 'replyChannel' value from the original request
* @param isError - this is an error, use the replyChannel argument (must not be null), not
* @param useArgChannel - use the replyChannel argument (must not be null), not
* the configured output channel.
*/
private void sendOutput(Object output, Object replyChannel, boolean isError) {
protected void sendOutput(Object output, Object replyChannel, boolean useArgChannel) {
MessageChannel outputChannel = getOutputChannel();
if (!isError && outputChannel != null) {
if (!useArgChannel && outputChannel != null) {
replyChannel = outputChannel;
}
if (replyChannel == null) {

View File

@@ -57,6 +57,9 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
this.requiresReply = requiresReply;
}
protected boolean getRequiresReply() {
return requiresReply;
}
public void setAdviceChain(List<Advice> adviceChain) {
Assert.notNull(adviceChain, "adviceChain cannot be null");
@@ -104,11 +107,11 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
if (result != null) {
sendOutputs(result, message);
}
else if (this.requiresReply) {
else if (this.requiresReply && !getAsyncReplySupported()) {
throw new ReplyRequiredException(message, "No reply produced by handler '" +
getComponentName() + "', and its 'requiresReply' property is set to true.");
}
else if (logger.isDebugEnabled()) {
else if (!getAsyncReplySupported() && logger.isDebugEnabled()) {
logger.debug("handler '" + this + "' produced no reply for request Message: " + message);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2016 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.
@@ -22,15 +22,30 @@ import org.springframework.messaging.MessagingException;
/**
* Exception that indicates no reply message is produced by a handler
* that does have a value of true for the 'requiresReply' property.
*
*
* @author Mark Fisher
* @author Gary Russell
* @since 2.1
*/
@SuppressWarnings("serial")
public class ReplyRequiredException extends MessagingException {
/**
* @param failedMessage the failed message.
* @param description the description.
*/
public ReplyRequiredException(Message<?> failedMessage, String description) {
super(failedMessage, description);
}
/**
* @param failedMessage the failed message.
* @param description the description.
* @param t the root cause.
* @since 4.3
*/
public ReplyRequiredException(Message<?> failedMessage, String description, Throwable t) {
super(failedMessage, description, t);
}
}