RKuser14521
RKuser14521

Reputation: 23

Handle exceptions/errors other than MessagingException ie.., other error/exception which are not wrapped as MessagingException in spring-integration

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

Answers (1)

Artem Bilan
Artem Bilan

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

Related Questions