INT-4092: Add errorChannel Header with Void Return

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

Polishing

Polishing

Polish of Polishing
This commit is contained in:
Gary Russell
2016-08-19 12:14:03 -04:00
committed by Artem Bilan
parent 52904c7106
commit 9f40d896a2
5 changed files with 37 additions and 1 deletions

View File

@@ -90,6 +90,8 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
private final Map<String, Expression> globalHeaderExpressions;
private final Map<String, Object> headers;
private final List<MethodParameter> parameterList;
private final MethodArgsMessageMapper argsMapper;
@@ -115,9 +117,17 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
GatewayMethodInboundMessageMapper(Method method, Map<String, Expression> headerExpressions,
Map<String, Expression> globalHeaderExpressions, MethodArgsMessageMapper mapper,
MessageBuilderFactory messageBuilderFactory) {
this(method, headerExpressions, globalHeaderExpressions, null, mapper, messageBuilderFactory);
}
GatewayMethodInboundMessageMapper(Method method, Map<String, Expression> headerExpressions,
Map<String, Expression> globalHeaderExpressions, Map<String, Object> headers,
MethodArgsMessageMapper mapper,
MessageBuilderFactory messageBuilderFactory) {
Assert.notNull(method, "method must not be null");
this.method = method;
this.headerExpressions = headerExpressions;
this.headers = headers;
this.globalHeaderExpressions = globalHeaderExpressions;
this.parameterList = getMethodParameterList(method);
this.payloadExpression = parsePayloadExpression(method);
@@ -354,6 +364,9 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
GatewayMethodInboundMessageMapper.this.globalHeaderExpressions);
builder.copyHeadersIfAbsent(evaluatedHeaders);
}
if (GatewayMethodInboundMessageMapper.this.headers != null) {
builder.copyHeadersIfAbsent(GatewayMethodInboundMessageMapper.this.headers);
}
return builder.build();
}

View File

@@ -54,6 +54,7 @@ import org.springframework.integration.support.management.TrackableComponent;
import org.springframework.integration.support.utils.IntegrationUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.core.DestinationResolver;
import org.springframework.messaging.handler.annotation.Payload;
@@ -549,10 +550,17 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
}
}
}
Map<String, Object> headers = null;
// We don't want to eagerly resolve the error channel here
Object errorChannel = this.errorChannel == null ? this.errorChannelName : this.errorChannel;
if (errorChannel != null && method.getReturnType().equals(void.class)) {
headers = new HashMap<>();
headers.put(MessageHeaders.ERROR_CHANNEL, errorChannel);
}
GatewayMethodInboundMessageMapper messageMapper = new GatewayMethodInboundMessageMapper(method,
headerExpressions,
this.globalMethodMetadata != null ? this.globalMethodMetadata.getHeaderExpressions() : null,
this.argsMapper, this.getMessageBuilderFactory());
headers, this.argsMapper, this.getMessageBuilderFactory());
if (StringUtils.hasText(payloadExpression)) {
messageMapper.setPayloadExpression(payloadExpression);
}

View File

@@ -74,6 +74,7 @@ import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
@@ -133,6 +134,7 @@ public class GatewayInterfaceTests {
equalTo("public abstract void org.springframework.integration.gateway.GatewayInterfaceTests$Foo.foo(java.lang.String)"));
assertThat((Method) message.getHeaders().get("object"), equalTo(fooMethod));
assertThat((String) message.getPayload(), equalTo("hello"));
assertThat(new MessageHeaderAccessor(message).getErrorChannel(), equalTo("errorChannel"));
called.set(true);
}
};

View File

@@ -353,6 +353,12 @@ If you provide a one-way flow, then nothing would be sent back to the caller.
In the case that you want to completely suppress Exceptions, you can provide a reference to the global "nullChannel" (essentially a /dev/null approach).
Finally, as mentioned above, if no "error-channel" is defined at all, then the Exceptions will propagate as usual.
Starting with _version 5.0_, when using a gateway method with a `void` return type (one-way flow), the `error-channel` reference (if provided) is populated in the standard `errorChannel` header of each message sent.
This allows a downstream async flow, based on the standard `ExecutorChannel` configuration (or a `QueueChannel`), to override a default global `errorChannel` exceptions sending behavior.
Previously you had to specify an `errorChannel` header manually via `@GatewayHeader` annotation or `<header>` sub-element.
The `error-channel` property was ignored for `void` methods with an asynchronous flow; error messages were sent to the default `errorChannel` instead.
IMPORTANT: Exposing the messaging system via simple POJI Gateways obviously provides benefits, but "hiding" the reality of the underlying messaging system does come at a price so there are certain things you should consider.
We want our Java method to return as quickly as possible and not hang for an indefinite amount of time while the caller is waiting on it to return (void, return value, or a thrown Exception).
When regular methods are used as a proxies in front of the Messaging system, we have to take into account the potentially asynchronous nature of the underlying messaging.

View File

@@ -21,3 +21,10 @@ Previously, Spring Integration JMS XML configuration used a default bean name `c
It has now been renamed to `jmsConnectionFactory`, which is the bean name used by Spring Boot to auto-configure the JMS Connection Factory bean.
If your application is relying on the previous behavior, rename your `connectionFactory` bean to `jmsConnectionFactory`, or specifically configure your components to use your bean using its current name.
===== Gateway Changes
The gateway now correctly sets the `errorChannel` header when the gateway method has a `void` return type and an error channel is provided.
Previously, the header was not populated.
This had the effect that synchronous downstream flows (running on the calling thread) would send the exception to the configured channel but an exception on an async downstream flow would be sent to the default `errorChannel` instead.
See <<gateway-error-handling>> for more information.