How to close spring integration channel? - java

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.

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.

catch exception in camel

I have a route in camel which is as follows
errorHandler(deadLetterChannel("file:somelocation");
from("jms:queuwlocation").to(
"file:someLocation");
I have read that camel error handling comes when there's a processing between two nodes, such as Processor, Predicate etc. But what if I cannot consume the message, in my case the camel cannot connect to jms. How should I log this exception ?. As I am trying to use a deadLetterChannel to send the message to filesystem, but since I have not received the message there is nothing new on the file location. how should I encounter this type of situation?
The problem you are facing might be due to the JMS Connection Exception being thrown outside of the life cycle of your Camel exchange. Claus Ibsen refers this in "Camel in Action" as a chicken and egg situation:
You can picture this as a chicken and egg situation. Camel's error handler only applies
during routing of Exchanges (chicken), but the consumer needs successfully to create the
Exchange (hatch the egg). So if we want a chicken but only have an egg, what can we do?
The answer lies with extending the error handling boundaries to cover the entire Camel JMS Consumer:
As
a figure of speech we can tell Camel to treat the eggs as if they were chickens. This is done by
configuring the consumer to bridge its internal error handler with Camel's error handler.
For the JMS module, I suspect this involves playing around with the transferException property. Good luck with that!
Futhermore, I don't think using a deadletter channel is appropriate for your problem, since you don't gain anything from taking messages out of the queue when encountering a connectivity problem.
Typically connectivity problems are self fixing, e.g. a server was restarted, and can be solved by retrial. For your use case, a sensible redelivery strategy can do this. Luckily Camel is really good at this: https://camel.apache.org/redeliverypolicy.html.
I recommend an indefinite redelivery until connectivity commences. Something like this:
onException(SomeJmsTimeoutException.class, SomeJmsConnectivityException.class)
.useOriginalMessage()
.maximumRedeliveries(Integer.MAX_VALUE)
.retryAttemptedLogLevel(LoggingLevel.ERROR)
.logRetryStackTrace(true)
.redeliveryDelay(1000 * 60);
As far as I know the error handling does not come in play just between the nodes. It has a more complete scope than that. It also depends on the type of error you are facing.
How you should log the exception?
Well why don't you do it like this?
DeadLetterChannel defined and you refer to it on your CamelContext.
The deadletteruri refers to another route - call it errorhandler route.
In the errorhandler route you can set headers that contain errors and other information you want to set.
In the errorhandler route in your "to" send the error message to a file writing the headers with it like you would with a log file.

error handling/propagation in spring integration

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).

How to handle badly formatted messages on the consumer side when working with Spring Integration and RabbitMQ

I am currently working on a project involves consuming messages from RabbitMQ brocker. However, I am still new to Spring Integration, AMQP and RabbitMQ.
I have an issue with consuming malformed messages formats. When my consumer receives a malformed message it returns it back the queue then RabbitMQ sends it back which creates an endless cycle.
In Spring Integration documentation there are some configuration that can be implemented to that this kind of message are no returned back to the queue.
However I could not understand how to implement that.
What I want is to be able to configure some kind of bean that has a format like
class ExceptionHandler {
public void handle(Throwable e ) {
Logger.log("Some log ... we don't give a Sh** ... ") ;
}
}
I've checked section 3.9 Exception Handling
and 3.15.3 Message Listeners and the Asynchronous Case
but unfortunately I could not understand anything.
So, if you have an example code or a link to one send it I will be greateful.
Yes, that's is one of the correct solution - to throw AmqpRejectAndDontRequeueException, when you decide that the message should not be requeued.
There is also defaultRequeueRejected on the SimpleMessageListenerContainer, which is true by default.
You maybe should to take a look to the DLX/DLQ solution to not lose those malformed messages.
Please, share the StackTrace which bothers you.
There is such a code in the SimpleMessageListenerContainer:
catch (AmqpRejectAndDontRequeueException rejectEx) {
/*
* These will normally be wrapped by an LEFE if thrown by the
* listener, but we will also honor it if thrown by an
* error handler.
*/
}
After a lot of try-fails attempts I was able to handle the error. However I am struggling with harboring the exception log now. I don't understand why this is implemented this way. I was able to handle the log issue too.
It turns that there is another way to say that you don't want to return the message back it is with acknowledge-mode="NONE" attribute. Checkout 10.2 Inbound Channel Adapter section.This way you don't even need to throw that ugly exception.
< bean id="handler" class="MessagesErrorHandler"/>
< int-amqp:inbound-channel-adapter
error-handler="handler"
id="idActivityAdapter"
channel="channelName"
queue-names="activityQueue"
/>
import org.springframework.util.ErrorHandler;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
public class MessagesErrorHandler implements ErrorHandler {
#Override
public void handleError(Throwable throwable) {
System.out.println("YESSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS ERROR IS HANDLED !!!!");
throw new AmqpRejectAndDontRequeueException(throwable);// this very important
//so that message don't go back to the queue.
}
}
The AmqpRejectAndDontRequeueException is a signal to the container to reject and not requeue the message; by default, it requeues any exception.
Alternatively, you can manually wire up a SimpleMessageListenerContainer bean; set defaultRequeueRejected to false and add it to the adapter using the container attribute. Then, all exceptions will cause messages to be rejected and not requeued.
Also, instead of an error-handler, you can use an error-channel and throw the AmqpRejectAndDontRequeueException from the error flow.

JMS TextMessage, JMS_BEA_SELECT message selector, transacted session and acknowledge modes

My application is running under Tomcat with a number of Spring's DefaultMessageListenerContainer listening to a number of different JMS queues running under Oracle 11g Weblogic server.
DefaultMessageListenerContainer configuration is.. well.. default with sessionTransacted = false and sessionAcknowledgeMode = AUTO_ACKNOWLEDGE. The type of messages my application receives is javax.jms.TextMessage. The actual body of the message (message.getText()) is an XML string.
I was faced with a problem when a number of application instances (dev boxes, test boxes, etc) needed to be pointed to the same JMS server, so once message comes to the queue it is unknown which server will consume it (I believe the one that runs receive() method first). The problem is that any given application instance is only interested in messages dedicated to that particular application instance, so with a current configuration most of the messages get lost (consumed by other application instances and ignored in message processing business logic).
I have no control on JMS server implementation, but I can force it to set a particular XML element in the message body to an application instance specific value, so I can read it and decide what application instance should consume it.
The most natural way to do that would be setting messageSelector property on DefaultMessageListenerContainer so decision is made on JMS server which consumer should receive what message. I also learned about Weblogic specific JMS_BEA_SELECT message selector expression that works with XML message types. Unfortunately it doesn't seem to work with javax.jms.TextMessage messages with XML payload (or at least I couldn't make it to work). I was trying the following expression with no luck:
<property name="messageSelector" value="JMS_BEA_SELECT('xpath', '//Event/CorrelationID/text()') = 'MY_SELECTOR_TEST_3'"/>
According to this article the other options are:
Use a transacted session, then rollback the session so the message will go back to the queue and can be consumed by other application instance.
Use Session.CLIENT_ACKNOWLEDGE when creating a session, then recover the session so the message will go back to the queue and can be consumed by other application instance.
I understand that I need to set sessionTransacted and sessionAcknowledgeMode on DefaultMessageListenerContainer to non-default values (what values?) and then rollback the session in the message processor code (option 1) or don't call message.acknowledge() (option 2).
It looks like DefaultMessageListenerContainer controls message processing / session life cycle. How can I customise it?
Solution with rollbacks looks really weird.
Setting message selector should be enough. I didn't work with BEA JMS implementation but i guess you can take care with regular "SELECT" and selecting from header .
<property name="messageSelector" value="CorrelationID='MY_SELECTOR_TEST_3'/>
Do you work on both side of communication points (server,client) to control correlation id?

Categories

Resources