0
votes

I am trying to do the following using spring integration.

I would like to read from an input channel an incoming Message that goes throws a series of Steps and produces a final message.Each step can throw an Exception. This could be either due to a DB call or ExternalCall etc. Each step is considered as a service-activator. If all is well a successful a final Message is generated.I need to handle each Exception thrown by each Step as a separate case and then produce the appropriate final Message.

Using the below xml I have written a test code that passes when sucessfull but when an error occurs although I see a Final message generated in the postSend (sent=true) on channel 'bean 'finalChannel' log but the process does not return to the final queue.

Why does this happen?

<?xml version="1.0" encoding="UTF-8"?>
<context:component-scan base-package="com.demo.">
</context:component-scan>

<header-enricher input-channel="inputChannel"
    output-channel="enrichedChannel">
    <header name="initialMessage" expression="getPayload()" />
</header-enricher>
<!-- first-step -->
<service-activator input-channel="enrichedChannel"
    output-channel="firstStep" ref="validateOne" />
<gateway id="validateOne" default-request-channel="ch1"
    error-channel="errors1" />

<chain input-channel="ch1">
    <service-activator id="mockServiceOneActivator"
        ref="mockServiceOne" method="register" />
</chain>
<!-- enrich-header with payload to be available down the line -->
<header-enricher input-channel="firstStep"
    output-channel="secondStep">
    <header name="initialInfo" expression="getPayload()" />
</header-enricher>
<!-- second-step -->
<service-activator input-channel="secondStep"
    output-channel="enrichWithTwoChannel" ref="validateTwo" />
<gateway id="validateTwo" default-request-channel="ch2"
    error-channel="errors2" />
<chain input-channel="ch2">
    <service-activator id="mockServiceTwoActivator"
        ref="mockServiceTwo" method="callExternal" />
</chain>
<!-- enrich-header with payload to be available down the line -->
<header-enricher input-channel="enrichWithTwoChannel"
    output-channel="eligibilityCheck">
    <header name="serviceTwoInfo" expression="getPayload()" />
</header-enricher>
<!-- final-step -->
<service-activator input-channel="eligibilityCheck"
    output-channel="finalChannel" id="mockServiceFinalActivator"
    ref="mockServiceFinal" method="submit" />
<!-- error handling -->
<service-activator input-channel="errors1"
    output-channel="finalChannel" ref="traceErrorHandler"
    method="handleFailedTrace" />
<service-activator input-channel="errors2"
    output-channel="finalChannel" ref="traceErrorHandler2"
    method="handleFailedTrace" />

<channel id="finalChannel">
    <queue />
</channel>
<service-activator input-channel="errorChannel"
    ref="globalExceptionHandler" method="handleError" />

My handler code looks like this ..

@Component("traceErrorHandler")public class TraceErrorHandler {
public Message<FinalMessage> handleFailedTrace(Message<?> errorMessage) {
    MessagingException payload = (MessagingException) errorMessage.getPayload();
    InitialMessage im = (InitialMessage) payload.getFailedMessage().getHeaders().get("initialMessage");
    ServiceOneException error = (ServiceOneException) payload.getCause();
    FinalMessage v = new FinalMessage(im.getFrom(), im.getTo(), error.getErrorTemplate());
    Message<FinalMessage> finalMessage = MessageBuilder.withPayload(v).copyHeaders(payload.getFailedMessage().getHeaders()).build();
    return finalMessage;
}}

I am not sure if this is the correct approach in regards to error-handling. The initial chains are simple activators but further down the line there will be more logic. Should we I always use chains to handle separate error channels

  1. Even if its a single activator service called.
  2. Only when there are Multiple service-activator/transforms that encapsulate a single point of error?

EDIT 1 I added a request-handler-advice-chain reference a bean.

<int:service-activator
    id="serviceOneActivator" input-channel="enrichedChannel"
    ref="serviceOne" method="register" output-channel="firstStep">
    <int:request-handler-advice-chain>
        <ref bean="myclass" />
    </int:request-handler-advice-chain></int:service-activator>

The reference bean is at first a default definition in xml

<bean id="myclass"
    class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
    <property name="failureChannel" ref="finalChannel" />
    <property name="onFailureExpression" value="#payload"/>
    <property name="returnFailureExpressionResult" value="true" />
</bean>

if I add the <property name="onFailureExpression" value="#payload"/> property it fails with:

Cannot convert value of type 'java.lang.String' to required type 'org.springframework.expression.Expression' for property 'onFailureExpression' What i would like to do is convert the exception message to a final message object? but all expression i have added seem to fail on load.

1

1 Answers

0
votes

A chain with one component makes no sense.

A chain is syntactic sugar only, each component still stands alone at runtime.

If you want to handle errors for individual endpoints, consider using ExpressionEvaluatingRequestHandlerAdvices instead.

See https://docs.spring.io/spring-integration/docs/5.3.2.RELEASE/reference/html/messaging-endpoints.html#message-handler-advice-chain