Added a 'replyTimeout' property to DefaultMessageEndpoint with a default value of 1000 milliseconds. When sending to either the 'defaultOutputChannelName' or the reply channel specified by the input MessageHeader's 'returnAddress', a timeout will now cause the reply Message to be wrapped within a MessageDeliveryException and passed to the endpoint's ErrorHandler. The default error handler publishes the exception as an ErrorMessage to the global error channel (INT-108).
This commit is contained in:
@@ -40,6 +40,7 @@ import org.springframework.integration.handler.MessageHandlerNotRunningException
|
||||
import org.springframework.integration.handler.MessageHandlerRejectedExecutionException;
|
||||
import org.springframework.integration.handler.ReplyHandler;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.integration.message.MessageHeader;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
@@ -72,6 +73,8 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
|
||||
|
||||
private final ReplyHandler replyHandler = new EndpointReplyHandler();
|
||||
|
||||
private volatile long replyTimeout = 1000;
|
||||
|
||||
private volatile String defaultOutputChannelName;
|
||||
|
||||
private volatile ChannelRegistry channelRegistry;
|
||||
@@ -146,12 +149,24 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
|
||||
return (this.errorHandler != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout in milliseconds to be enforced when this endpoint sends a
|
||||
* reply message. If the message is not sent successfully within the
|
||||
* allotted time, then it will be sent within a MessageDeliveryException to
|
||||
* the error handler instead. The default <code>replyTimeout</code> value
|
||||
* is 1000 milliseconds.
|
||||
*/
|
||||
public void setReplyTimeout(long replyTimeout) {
|
||||
this.replyTimeout = replyTimeout;
|
||||
}
|
||||
|
||||
public String getDefaultOutputChannelName() {
|
||||
return this.defaultOutputChannelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the channel to which this endpoint can send reply messages by default.
|
||||
* Set the name of the channel to which this endpoint should send reply
|
||||
* messages by default.
|
||||
*/
|
||||
public void setDefaultOutputChannelName(String defaultOutputChannelName) {
|
||||
this.defaultOutputChannelName = defaultOutputChannelName;
|
||||
@@ -170,9 +185,9 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
|
||||
int capacity = concurrencyPolicy.getQueueCapacity();
|
||||
BlockingQueue<Runnable> queue = (capacity < 1) ? new SynchronousQueue<Runnable>() :
|
||||
new ArrayBlockingQueue<Runnable>(capacity);
|
||||
ExecutorService executor = new ThreadPoolExecutor(concurrencyPolicy.getCoreSize(),
|
||||
concurrencyPolicy.getMaxSize(), concurrencyPolicy.getKeepAliveSeconds(),
|
||||
TimeUnit.SECONDS, queue);
|
||||
ExecutorService executor = new ThreadPoolExecutor(
|
||||
concurrencyPolicy.getCoreSize(), concurrencyPolicy.getMaxSize(),
|
||||
concurrencyPolicy.getKeepAliveSeconds(), TimeUnit.SECONDS, queue);
|
||||
this.handler = new ConcurrentHandler(this.handler, executor);
|
||||
}
|
||||
ConcurrentHandler concurrentHandler = (ConcurrentHandler) this.handler;
|
||||
@@ -232,7 +247,10 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("endpoint '" + this + "' sending to output channel '" + outputChannel + "', message: " + message);
|
||||
}
|
||||
outputChannel.send(message);
|
||||
if (!outputChannel.send(message, this.replyTimeout)) {
|
||||
this.errorHandler.handle(new MessageDeliveryException(message,
|
||||
"unable to send output message within alloted timeout of " + replyTimeout + " milliseconds"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
@@ -291,7 +309,10 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("endpoint '" + DefaultMessageEndpoint.this + "' replying to channel '" + replyChannel + "' with message: " + replyMessage);
|
||||
}
|
||||
replyChannel.send(replyMessage);
|
||||
if (!replyChannel.send(replyMessage, replyTimeout)) {
|
||||
errorHandler.handle(new MessageDeliveryException(replyMessage,
|
||||
"unable to send reply message within alloted timeout of " + replyTimeout + " milliseconds"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,8 +157,8 @@ public class MessageBusTests {
|
||||
bus.registerHandler("handler2", handler2, new Subscription(inputChannel));
|
||||
bus.start();
|
||||
inputChannel.send(new StringMessage(1, "testing"));
|
||||
Message<?> message1 = outputChannel1.receive(200);
|
||||
Message<?> message2 = outputChannel2.receive(200);
|
||||
Message<?> message1 = outputChannel1.receive(500);
|
||||
Message<?> message2 = outputChannel2.receive(500);
|
||||
bus.stop();
|
||||
assertTrue("both handlers should have received and replied to the message",
|
||||
(message1 != null && message2 != null));
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.integration.handler.MessageHandler;
|
||||
import org.springframework.integration.handler.MessageHandlerNotRunningException;
|
||||
import org.springframework.integration.handler.TestHandlers;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageDeliveryException;
|
||||
import org.springframework.integration.message.StringMessage;
|
||||
import org.springframework.integration.message.selector.MessageSelector;
|
||||
import org.springframework.integration.message.selector.MessageSelectorRejectedException;
|
||||
@@ -442,9 +443,102 @@ public class DefaultMessageEndpointTests {
|
||||
endpoint.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultOutputChannelTimeoutSendsToErrorHandler() {
|
||||
SimpleChannel output = new SimpleChannel(1);
|
||||
ChannelRegistry channelRegistry = new DefaultChannelRegistry();
|
||||
channelRegistry.registerChannel("output", output);
|
||||
DefaultMessageEndpoint endpoint = new DefaultMessageEndpoint(new MessageHandler() {
|
||||
public Message<?> handle(Message<?> message) {
|
||||
return message;
|
||||
}
|
||||
});
|
||||
endpoint.setDefaultOutputChannelName("output");
|
||||
endpoint.setChannelRegistry(channelRegistry);
|
||||
TestErrorHandler errorHandler = new TestErrorHandler();
|
||||
endpoint.setErrorHandler(errorHandler);
|
||||
endpoint.setReplyTimeout(0);
|
||||
endpoint.start();
|
||||
endpoint.handle(new StringMessage("test1"));
|
||||
assertNull(errorHandler.getLastError());
|
||||
endpoint.handle(new StringMessage("test2"));
|
||||
Throwable error = errorHandler.getLastError();
|
||||
assertNotNull(error);
|
||||
assertEquals(MessageDeliveryException.class, error.getClass());
|
||||
assertEquals("test2", ((MessageDeliveryException) error).getUndeliveredMessage().getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnAddressChannelTimeoutSendsToErrorHandler() {
|
||||
SimpleChannel replyChannel = new SimpleChannel(1);
|
||||
DefaultMessageEndpoint endpoint = new DefaultMessageEndpoint(new MessageHandler() {
|
||||
public Message<?> handle(Message<?> message) {
|
||||
return message;
|
||||
}
|
||||
});
|
||||
TestErrorHandler errorHandler = new TestErrorHandler();
|
||||
endpoint.setErrorHandler(errorHandler);
|
||||
endpoint.setReplyTimeout(0);
|
||||
endpoint.start();
|
||||
Message<?> message1 = new StringMessage("test1");
|
||||
message1.getHeader().setReturnAddress(replyChannel);
|
||||
endpoint.handle(message1);
|
||||
assertNull(errorHandler.getLastError());
|
||||
Message<?> message2 = new StringMessage("test2");
|
||||
message2.getHeader().setReturnAddress(replyChannel);
|
||||
endpoint.handle(message2);
|
||||
Throwable error = errorHandler.getLastError();
|
||||
assertNotNull(error);
|
||||
assertEquals(MessageDeliveryException.class, error.getClass());
|
||||
assertEquals(message2, ((MessageDeliveryException) error).getUndeliveredMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnAddressChannelNameTimeoutSendsToErrorHandler() {
|
||||
SimpleChannel replyChannel = new SimpleChannel(1);
|
||||
ChannelRegistry channelRegistry = new DefaultChannelRegistry();
|
||||
channelRegistry.registerChannel("replyChannel", replyChannel);
|
||||
DefaultMessageEndpoint endpoint = new DefaultMessageEndpoint(new MessageHandler() {
|
||||
public Message<?> handle(Message<?> message) {
|
||||
return message;
|
||||
}
|
||||
});
|
||||
endpoint.setChannelRegistry(channelRegistry);
|
||||
TestErrorHandler errorHandler = new TestErrorHandler();
|
||||
endpoint.setErrorHandler(errorHandler);
|
||||
endpoint.setReplyTimeout(10);
|
||||
endpoint.start();
|
||||
Message<?> message1 = new StringMessage("test1");
|
||||
message1.getHeader().setReturnAddress("replyChannel");
|
||||
endpoint.handle(message1);
|
||||
assertNull(errorHandler.getLastError());
|
||||
Message<?> message2 = new StringMessage("test2");
|
||||
message2.getHeader().setReturnAddress("replyChannel");
|
||||
endpoint.handle(message2);
|
||||
Throwable error = errorHandler.getLastError();
|
||||
assertNotNull(error);
|
||||
assertEquals(MessageDeliveryException.class, error.getClass());
|
||||
assertEquals(message2, ((MessageDeliveryException) error).getUndeliveredMessage());
|
||||
}
|
||||
|
||||
|
||||
private static ExecutorService createExecutor() {
|
||||
return new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
|
||||
}
|
||||
|
||||
|
||||
private static class TestErrorHandler implements ErrorHandler {
|
||||
|
||||
private volatile Throwable lastError;
|
||||
|
||||
|
||||
public void handle(Throwable t) {
|
||||
this.lastError = t;
|
||||
}
|
||||
|
||||
Throwable getLastError() {
|
||||
return this.lastError;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user