Reputation: 23
My code deliberately throws a SourceAssertionError. Earlier in 5.1.6.release of spring-integraton for any error , in RequestHandlerRetryAdvice, for any error, it would wrap it in MessagingException. In Below doInvoke() of RequestHandlerRetryAdvice,
@Override
protected Object doInvoke(final ExecutionCallback callback, Object target, final Message<?> message)
throws Exception {
RetryState retryState = null;
retryState = this.retryStateGenerator.determineRetryState(message);
messageHolder.set(message);
try {
return this.retryTemplate.execute(context -> callback.cloneAndExecute(), this.recoveryCallback, retryState);
}
catch (MessagingException e) {
if (e.getFailedMessage() == null) {
throw new MessagingException(message, "Failed to invoke handler", e);
}
throw e;
}
catch (Exception e) {
throw new MessagingException(message, "Failed to invoke handler", unwrapExceptionIfNecessary(e));
}
finally {
messageHolder.remove();
}
}
but now in 5.3.2.Release, its not wrapped in MEssagingException but rather thrown as THrowableHolderException as seen below
@Override
protected Object doInvoke(final ExecutionCallback callback, Object target, final Message<?> message) {
RetryState retryState = this.retryStateGenerator.determineRetryState(message);
MESSAGE_HOLDER.set(message);
try {
return this.retryTemplate.execute(context -> callback.cloneAndExecute(), this.recoveryCallback, retryState);
}
catch (MessagingException e) {
if (e.getFailedMessage() == null) {
throw new MessagingException(message, "Failed to invoke handler", e);
}
throw e;
}
catch (ThrowableHolderException e) { // NOSONAR catch and rethrow
throw e;
}
catch (Exception e) {
throw new ThrowableHolderException(e);
}
finally {
MESSAGE_HOLDER.remove();
}
}
Now after spring-integration upgrade my code flow goes in such a way that error(Not MessagingException) is not being routed to error-channel. The exception thrown by doInvoke() in RequestRetryHandlerAdvice is rejected before its routed to error channel because the logic required them to be of particular type i.e., if only the exception is MessaginException then its routed to error-channel. The flow exits with exception thrown by rethrowExceptionCauseIfPossible(..) in GatewayProxyFactoryBean. Before upgrade my error had been wrapped to MessagingException and flow reaches till handleSendAndReceiveError(..) in MessagingGatewaySupport where it usualy is routed to custom defined error-channel. This is not happening after upgrade to 5.3.2.Release as flow ends before itself
my spring configuration is very straightforward, I just have one custom error channel defined only to gateway.
<int:gateway id="gateway" error-channel="myErrorChannel". and defined a transformer for this error-channel
Now my question is how can I handle this error? is there anyway I could handle it by routing to any newly defined error-channel or can I do it to my already defined myErrorChannel?
EDIT1: Adding config file as Artem Bilan Requested
<bean id="myPreferences" class="package.MyPreferences" />
<bean id="myExceptionTransformer"
class="package.myExceptionTransformer" />
<int:channel id="myErrorChannel" />
<int:transformer input-channel="myErrorChannel"
ref="myExceptionTransformer" />
<bean id="Logger" class="package.Logger" />
<int:gateway id="gateway" error-channel="myErrorChannel"
service-interface="package.myClient"
default-request-channel="myRoutingChannel">
<int:method name="invokeAuthenticationRequest">
<int:header
name="#{T(name1)}"
value="#{T(value1)}" />
</int:method>
<int:method name="invokeProvRequest">
<int:header
name="#{T(name2)}"
value="#{T(value2)}" />
</int:method>
</int:gateway>
<int:publish-subscribe-channel id="myRoutingChannel" />
<int:header-value-router input-channel="myRoutingChannel"
header-name="#{T(package.AppContext).REQUEST_TYPE}">
<int:mapping
value="#{T(value1)}"
channel="authGatewayChannel" />
<int:mapping
value="#{T(value2)}"
channel="provGatewayChannel" />
</int:header-value-router>
<int-ws:request-handler-advice-chain>
<ref bean="provRetryAdvice" />
</int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>
<bean id="provRetryAdvice"
class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.FixedBackOffPolicy">
<property name="backOffPeriod" value="4000" />
</bean>
</property>
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="#{provisioningRetryCount.intValue()}" />
</bean>
</property>
</bean>
</property>
</bean>
And my Transformer is defined as
@Transformer
public Message<?> transformErrorToResponse(Message<?> exceptionMessage) {
Object exceptionMessagePayload = exceptionMessage.getPayload();
Throwable cause = ((MessagingException) exceptionMessagePayload).getCause();
newPayload = new myException(msg);
/*add headers*/
return errorMessage;
}
Upvotes: 2
Views: 554
Reputation: 121272
The "regression" has been done with this change: https://github.com/spring-projects/spring-integration/commit/b187bca36e2565e80e72e662c204a57c05aab11d.
So, yes, we don't throw a MessagingException
wrapper for regular exceptions any more to avoid bigger stack trace for nothing.
I'm not fully sure how your exception cannot reach that error channel. The code in the MessagingGatewaySupport
is like this:
try {
...
reply = this.messagingTemplate.sendAndReceive(channel, requestMessage);
...
}
catch (Exception ex) {
...
reply = ex;
...
}
if (reply instanceof Throwable || reply instanceof ErrorMessage) {
Throwable error =
reply instanceof ErrorMessage
? ((ErrorMessage) reply).getPayload()
: (Throwable) reply;
return handleSendAndReceiveError(object, requestMessage, error, shouldConvert);
}
Probably the logic in your error handler to check only for the MessagingException
in the received ErrorMessage
which is not the case any more...
May we see your configuration to be sure what is going on and maybe reproduce to lead you to the proper solution?
Upvotes: 1