INT-3944: Async JMS Outbound Gateway
JIRA: https://jira.spring.io/browse/INT-3944 Polishing (PR comments) and Doc Polish Polishing - PR Comments Remove custom `async` boolean in favor of the now public setter. Add exception to `JmsException` hierarchy. INT-3944: Polishing * Rename `asyncReplySupported` just to `async`. As well as its getter and setter. Plus fix docs on the matter * Polishing for the `JmsOutboundGateway` according PR comments * Rework `JmsOutboundGateway` to return `AbstractIntegrationMessageBuilder` instead of `Message` since `AbstractMessageProducingHandler` rebuilds `Message` for a new one to copy headers from request * Add `getPayload()` and `getHeaders()` to the `AbstractIntegrationMessageBuilder` to make the `Routing Slip` users happy, when the `AbstractIntegrationMessageBuilder` is pushed to the `Routing Slip path` function * Fix race condition in the `ChatMessageListeningEndpointTests`: the `stanza` parsing is done in the different Thread. Doc Polishing (Routing Slip) Fix timing issue in the `LastModifiedFileListFilterTests`: `Thread.sleep()` not always reflects the reality. Switch to the "past simulation" via explicit `File.setLastModified()`
This commit is contained in:
committed by
Artem Bilan
parent
0fd72d2d18
commit
4bfcdb9dfa
@@ -84,7 +84,7 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
|
||||
|
||||
private DestinationResolver<MessageChannel> channelResolver;
|
||||
|
||||
private Boolean asyncReplySupported;
|
||||
private Boolean async;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
@@ -150,11 +150,11 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
|
||||
* namespace. It's not clear that other endpoints would benefit from async support,
|
||||
* but any subclass of {@link AbstractReplyProducingMessageHandler} can potentially
|
||||
* return a {@code ListenableFuture<?>}.
|
||||
* @param asyncReplySupported the asyncReplySupported to set.
|
||||
* @param async the async to set.
|
||||
* @since 4.3
|
||||
*/
|
||||
public void setAsyncReplySupported(Boolean asyncReplySupported) {
|
||||
this.asyncReplySupported = asyncReplySupported;
|
||||
public void setAsync(Boolean async) {
|
||||
this.async = async;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,10 +223,10 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
|
||||
+ (name == null ? "" : (", " + name)) + ".");
|
||||
}
|
||||
}
|
||||
if (this.asyncReplySupported != null) {
|
||||
if (this.async != null) {
|
||||
if (actualHandler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) actualHandler)
|
||||
.setAsyncReplySupported(this.asyncReplySupported);
|
||||
.setAsync(this.async);
|
||||
}
|
||||
}
|
||||
if (this.handler instanceof Orderable && this.order != null) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ServiceActivatorParser extends AbstractDelegatingConsumerEndpointPa
|
||||
|
||||
@Override
|
||||
void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "async", "asyncReplySupported");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "async");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
|
||||
private volatile String outputChannelName;
|
||||
|
||||
private volatile boolean asyncReplySupported;
|
||||
private volatile boolean async;
|
||||
|
||||
/**
|
||||
* Set the timeout for sending reply Messages.
|
||||
@@ -79,21 +79,20 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
* Allow async replies. If the handler reply is a {@link ListenableFuture} send
|
||||
* the output when it is satisfied rather than sending the future as the result.
|
||||
* Only subclasses that support this feature should set it.
|
||||
* @param asyncReplySupported true to allow.
|
||||
*
|
||||
* @param async true to allow.
|
||||
* @since 4.3
|
||||
*/
|
||||
public final void setAsyncReplySupported(boolean asyncReplySupported) {
|
||||
this.asyncReplySupported = asyncReplySupported;
|
||||
public final void setAsync(boolean async) {
|
||||
this.async = async;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setAsyncReplySupported(boolean)
|
||||
* @see #setAsync(boolean)
|
||||
* @return true if this handler supports async replies.
|
||||
* @since 4.3
|
||||
*/
|
||||
protected boolean getAsyncReplySupported() {
|
||||
return this.asyncReplySupported;
|
||||
protected boolean isAsync() {
|
||||
return this.async;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,7 +176,7 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
}
|
||||
}
|
||||
|
||||
if (this.asyncReplySupported && reply instanceof ListenableFuture<?>) {
|
||||
if (this.async && reply instanceof ListenableFuture<?>) {
|
||||
ListenableFuture<?> future = (ListenableFuture<?>) reply;
|
||||
final Object theReplyChannel = replyChannel;
|
||||
future.addCallback(new ListenableFutureCallback<Object>() {
|
||||
@@ -230,7 +229,7 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("The RoutingSlip 'path' can be of " +
|
||||
"String or RoutingSlipRouteStrategy type, but gotten: " + path);
|
||||
"String or RoutingSlipRouteStrategy type, but got: " + path.getClass());
|
||||
}
|
||||
|
||||
if (routingSlipPathValue instanceof MessageChannel) {
|
||||
|
||||
@@ -107,11 +107,11 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
|
||||
if (result != null) {
|
||||
sendOutputs(result, message);
|
||||
}
|
||||
else if (this.requiresReply && !getAsyncReplySupported()) {
|
||||
else if (this.requiresReply && !isAsync()) {
|
||||
throw new ReplyRequiredException(message, "No reply produced by handler '" +
|
||||
getComponentName() + "', and its 'requiresReply' property is set to true.");
|
||||
}
|
||||
else if (!getAsyncReplySupported() && logger.isDebugEnabled()) {
|
||||
else if (!isAsync() && logger.isDebugEnabled()) {
|
||||
logger.debug("handler '" + this + "' produced no reply for request Message: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ import org.springframework.messaging.Message;
|
||||
*/
|
||||
public interface RoutingSlipRouteStrategy {
|
||||
|
||||
/**
|
||||
* Get the next path for this routing slip.
|
||||
* @param requestMessage the request message.
|
||||
* @param reply the reply - depending on context, this may be a user-level domain
|
||||
* object, a {@link Message} or a {@code AbstractIntegrationMessageBuilder}.
|
||||
* @return a channel name or another {@link RoutingSlipRouteStrategy}.
|
||||
*/
|
||||
Object getNextPath(Message<?> requestMessage, Object reply);
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class AbstractIntegrationMessageBuilder<T> {
|
||||
|
||||
public abstract T getPayload();
|
||||
|
||||
public abstract Map<String, Object> getHeaders();
|
||||
|
||||
/**
|
||||
* Set the value for the given header name. If the provided value is <code>null</code>, the header will be removed.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.Assert;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Dave Syer
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public final class MessageBuilder<T> extends AbstractIntegrationMessageBuilder<T> {
|
||||
|
||||
@@ -62,6 +63,16 @@ public final class MessageBuilder<T> extends AbstractIntegrationMessageBuilder<T
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getPayload() {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getHeaders() {
|
||||
return this.headerAccessor.toMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for a new {@link Message} instance pre-populated with all of the headers copied from the
|
||||
* provided message. The payload of the provided Message will also be used as the payload for the new message.
|
||||
@@ -72,8 +83,7 @@ public final class MessageBuilder<T> extends AbstractIntegrationMessageBuilder<T
|
||||
*/
|
||||
public static <T> MessageBuilder<T> fromMessage(Message<T> message) {
|
||||
Assert.notNull(message, "message must not be null");
|
||||
MessageBuilder<T> builder = new MessageBuilder<T>(message.getPayload(), message);
|
||||
return builder;
|
||||
return new MessageBuilder<T>(message.getPayload(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +94,7 @@ public final class MessageBuilder<T> extends AbstractIntegrationMessageBuilder<T
|
||||
* @return A MessageBuilder.
|
||||
*/
|
||||
public static <T> MessageBuilder<T> withPayload(T payload) {
|
||||
MessageBuilder<T> builder = new MessageBuilder<T>(payload, null);
|
||||
return builder;
|
||||
return new MessageBuilder<T>(payload, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-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.
|
||||
@@ -30,6 +30,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
@@ -53,6 +54,16 @@ public class MutableMessageBuilder<T> extends AbstractIntegrationMessageBuilder<
|
||||
this.headers = this.mutableMessage.getRawHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getPayload() {
|
||||
return this.mutableMessage.getPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for a new {@link Message} instance pre-populated with all of the headers copied from the
|
||||
* provided message. The payload of the provided Message will also be used as the payload for the new message.
|
||||
|
||||
@@ -111,7 +111,7 @@ public class AsyncHandlerTests {
|
||||
}
|
||||
|
||||
};
|
||||
this.handler.setAsyncReplySupported(true);
|
||||
this.handler.setAsync(true);
|
||||
this.handler.setOutputChannel(this.output);
|
||||
this.handler.setBeanFactory(mock(BeanFactory.class));
|
||||
this.latch = new CountDownLatch(1);
|
||||
|
||||
Reference in New Issue
Block a user