error handling/propagation in spring integration - java

I've read that if exception is thrown in the flow the first thing the framework will do is check message header for error-channel property. Is it always the case?
In my particular case I'm assigning a custom error-channel to a message header and yet the message seems to get propagated up the stream to the nearest error-handler/error-channel.
<int:chain id="buildAggregatedResponseChain" input-channel="aggregatedResultChannel"
output-channel="sendAggregatedChannel">
<int:header-enricher>
<int:error-channel ref="myErrorChannel"/>
</int:header-enricher>
<int:service-activator ref="service" method="doSomething"/>
</int:chain>
I explicitly throw an exception inside doSomething but the exception never ends up in myErrorChannel. Instead, it is "propagated" to the nearest ErrorHandler up the stream or to the error-channel specified up the stream for int-mail:imap-idle-channel-adapter(tried several different flows).
What do I miss? Maybe someone can outline the main principal of error handling/error propagation(for example when talking about several transactions, etc)? There is some information out there, but it is quite scattered and not systematic.

It depends on the upstream flow; if there's an async handoff the header is consulted; otherwise, the exception is thrown back to the inbound endpoint.
In general, I would advise against modifying framework headers such as errorChannel. Instead put an error-channel on the inbound endpoint (such as your imap idle adapter) and handle the errors on that flow.
Modifying the headers directly is rarely needed. If you wish to insert different error-handling mid-flow then you can insert a messaging gateway...
<int:service activator ... ref="gw" />
<int:gateway id="gw" default-request-channel="..."
error-channel="midFlowErrorChannel" />
If the downstream flow (from the gateway) returns no result on success, then be sure to add a default reply timeout of 0 (or use a custom service interface with a method that returns void).

Related

Spring Channel Interceptor - how to return null message without throwing an error

The context: I have a message channel that does a post request to a service A and 2 interceptors - one that does some processing and another one that sends to another service B.
The problem: Where to add a feature toggle such that I can stop sending to A but still send to B? (also 1st interceptor does some processing that is needed before sending to B)
My approach was to add the feature toggle in the 2nd interceptor and return a null message object so that the messageChannel.send won't be executed. This works but also throws an error which is not desirable.
According to spring's message channel documentation:
The preSend of an interceptor is invoked before the Message is
actually sent to the channel. This allows for modification of the
Message if necessary. If this method returns {#code null} then the
actual send invocation will not occur.
Is there a way to silently kill the sending process or would there be a better approach?
Instead of using a ChannelInterceptor, try a Filter
Message filters are used to decide whether a Message should be passed along or dropped based on some criteria, such as a message header value or message content itself.
With a Filter you can specify your own implementation of a MessageSelector, which when returning false, will cause the Filter to discard the message.
package exampleCode;
import org.springframework.integration.core.MessageSelector;
import org.springframework.messaging.Message;
public class MySelector implements MessageSelector {
#Override
public boolean accept(Message<?> message) {
// If the message should continue through the filter, return true
// If the message should be blocked from continuing, return false
}
}
Then you can configure your selector to be used by a filter
<bean id="MySelector" class="exampleCode.MySelector" />
<int:filter input-channel="someChannel" ref="MySelector"
output-channel="someOtherChannel" />
Prior to version 3.0, invoking the send method on a PublishSubscribeChannel that had no subscribers returned false. When used in conjunction with a MessagingTemplate, a MessageDeliveryException was thrown. Starting with version 3.0, the behavior has changed such that a send is always considered successful if at least the minimum subscribers are present (and successfully handle the message). This behavior can be modified by setting the minSubscribers property, which defaults to 0.
Reference: https://docs.spring.io/spring-integration/docs/5.0.5.RELEASE/reference/html/messaging-channels-section.html
Also, I think you can implement Conditional Variables with Spring using #Conditional for the interceptors to execute for a given condition.
Reference: https://javapapers.com/spring/spring-conditional-annotation/#:~:text=Spring%20Boot%20module%20makes%20heavy,variables%2C%20irrespective%20of%20its%20value.

Add custom attributes to logging-channel-adapter Spring Integration

I have the following configuration:
<int:channel id="responseXmlChannel">
<int:interceptors>
<int:wire-tap channel="responseXmlLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="responseXmlLogger"
logger-name="test.SyncResponseLogger" level="DEBUG"
expression="'message received, headers:' + headers + ' payload:' + payload"/>
I am trying to add some session attributes to be logged. Current user for example.
I was taking a look at the header-enricher approach, but I could not find a good example of adding session attributes to the header using it.
Is there a way to do what I am trying to do? Is there other approach besides using the headers to add custom attributes to be logged?
My goal is to have in the logs something like
current user: $UserName. Payload: $Payload
OK. So, what is your question then? How to use header-enricher? I'm not sure what is your "session" object to be precise with the answer, but header-enricher can be configured to call any arbitrary method on beans. See its docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#header-enricher.
The other approach is too deal with ThreadLocal. That's exactly what the RequestContextHolder in Spring Web does for RequestAttributes with its HTTP session access. Although you need to keep in mind that moving to a different thread with queue or executor channels in between will lose the current thread context and you won't have access to that ThreadLocal. So, headers in the particular message is not so bad choice to do.

How to close spring integration channel?

I have project with Spring Integration and input/output channels. The problem is in the last piece of config
<int:transformer id="testTransformer" input-channel="inChannel" method="processor"
output-channel="outChannel">
<bean class="someClass"/>
</int:transformer>
It gives me an error
failure occurred in gateway sendAndReceive: No reply produced by handler 'testTransformer', and its 'requiresReply' property is set to true
I understand that it's about my outputChannel is not initialized, so how correctly make program finished?
No reply produced by handler 'testTransformer', and its 'requiresReply' property is set to true
That means that the transformer component is strictly request-reply and it definitely has to return something.
That's no the problem of your configuration, but your logic in the someClass.processor() method. Reconsider it to return anything valuable instead of null.
If null is possible by your logic and you manage it somehow in your flow, consider to switch to the service-activator. This one isn't so strict for its reply and allow to return null. But at the same time you should bear in mind that the flow stops exactly here. The null is invalid for messaging and is an indicator do stop processing because nothing what is payload any more.

Spring Integration - Exceptions and retries

My setup:
I have a message daemon using ActiveMQ which will consume JSON messages.
The publisher of JSON messages adds type header with value, for ex, com.example.Foo which is the type of the json message. I use this to transform json to pojo.
Spring config:
Once the message is received, these are the steps it goes through:
1. Transformer: Transforms json to pojo
2. Payload type router: Based on the type of the pojo, routes the pojo to appropriate service activator.
3. Service activator: Process the message.
<int:chain input-channel="transformerChannel">
<int:transformer id="jsonToPojoTransformer" ref="JsonToPojoTransformer" method="transform" />
<int:payload-type-router default-output-channel="defaultChannel">
<int:mapping type="com.example.Foo" channel="fooHandlerChannel"/>
<int:mapping type="com.example.Bar" channel="barHandlerChannel"/>
</int:payload-type-router>
</int:chain>
<int:service-activator input-channel="fooHandlerChannel" ref="fooHandler" method="onMessage"/>
<int:service-activator input-channel="barHandlerChannel" ref="barHandler" method="onMessage"/>
Service activator definition:
public class FooHandler {
public void onMessage(Foo foo) {...}
}
Problem:
I want to know how to access the message headers in the service activator. It seems like the service activator does not have access to the message headers since the transformer is returning a pojo.
Lets say the service activator is unable to call a down stream rest service for whatever reason. I want to skip processing this message now and I want to retry this message later. Or lets say there was an exception in processing this message. I want to retry processing this message after some delay. How do I accomplish this?
--edit--
Removed details to reduce question size as per Artem's comment.
Please, try do not make so long topics here in SO. It's hard to answer particular question if there are to many of them.
Absolutely not clear why you can't get access to header from your service activator method. You can accept the whole Message<>, and call its getHeaders(). You can use#Headersannotation on theMaparg to get headers from the message. You can use#Header` annotation to extract exactly particular header from the message.
Even if your transformer method returns just a POJO that doesn't mean that it isn't wrapped to the Message with the headers from requestMessage. If you need to return specific header alongside with your POJO, you should create Message yourself, using MessageBuilder and don't forget to copy requestMessage headers, just because transformer doesn't copy request headers if the whole message is returned.
You have to support TX on your JMS consumer, so that RuntimeException will lead to the rollback and, therefore, redelivery eventually. And you should ensure that all the flow is performed in the same thread. Otherwise TX is committed and message is acked on the broker. The same happens when you don't have transactions.

Spring Integration HTTP Inbound Gateway Request Overlap

I have an HTTP Inbound Gateway in my Integration Application, which I will call during some save operation. It's like this. If I have one product, I will call the API once, and if I have more than once, then I will call multiple times. The problem is, for single invoke, SI works just fine. But for multiple calls, request and response get messed up. I thought Spring Integration Channels are just like MQ's, but it is not?
Let me explain this. Let's say I have 2 products. First, I invoke SI for Product A and then for B. Response of A got mapped to request B! It happens all the time. I don't want to use some dirty hacks like wait for the first response to come and invoke again. This means the system has to wait for a long time. I guess we can do it in Spring Integration using task executor, but with all the basic samples out there, I can't find the right one. So please help me find out how can I fix this issue!
My Configuration is :
<int:channel id="n2iMotorCNInvokeRequest" />
<int:channel id="n2iMotorCNInvokeResponse" />
<int:channel id="n2iInvoketransformerOut" />
<int:channel id="n2iInvokeobjTransformerOut" />
<int:channel id="n2iInvokegatewayOut" />
<int-http:inbound-gateway id="i2nInvokeFromPOS"
supported-methods="GET"
request-channel="i2nInvokeRequest"
reply-channel="i2nInvokeResponse"
path="/postProduct/{Id}"
mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"
reply-timeout="50000">
<int-http:header name="Id" expression="#pathVariables.Id"/>
</int-http:inbound-gateway>
<int:service-activator id="InvokeActivator"
input-channel="i2nInvokeRequest"
output-channel="i2nInvokeResponse"
ref="apiService"
method="getProductId"
requires-reply="true"
send-timeout="60000"/>
<int:transformer input-channel="i2nInvokeResponse"
ref="apiTransformer"
method="retrieveProductJson"
output-channel="n2iInvokeRequest"/>
<int-http:outbound-gateway request-channel="n2iInvokeRequest" reply-channel="n2iInvoketransformerOut"
url="http://10.xx.xx.xx/api/index.php" http-method="POST"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<int:service-activator
input-channel="n2iInvoketransformerOut"
output-channel="n2iInvokeobjTransformerOut"
ref="apiService"
method="productResponse"
requires-reply="true"
send-timeout="60000"/>
The i2nInvokeFromPOS gateway is what we call from Web Application which is where all the products will be created. This Integration API will fetch that data, and post it to the backend system so that it will get updated to the other POS locations too!
Steps :
I will send the productId to i2nInvokeFromPOS.
apiTransformer -> retrieveProductJson() method will fetch the product details from DB based on the ID
Send the Request JSON to Backend system using http:outbound-gateway
Get the response from Backend and update the product status as uploaded in DB. Happens in apiService -> productResponse()
Once the response for A is received, all I'm getting is HTTP 500 Error for the Request B! But the Backend API is just fine.
The framework is completely thread-safe - if you are seeing cross-talk between different requests/responses then one (or more) of your components that the framework is invoking is not thread-safe.
You can't keep state in fields in, for example, code invoked from a service activator.

Categories

Resources