How to ignore one exception in Camel while redelivering - java

I have to implement an error handler that uses the Camel Redelivery Policy over a business process which send a SOAP request and process its response. During the process part, a special exception (RetryException) could be thrown. This exception is caught (thanks to onException(RetryException.class)) on the error handler level.
That was the easy part.
Now I want to ignore exceptions that could be thrown by the cxf endpoint (in case of connection error per example) and process them.
So I try :
(1) One main route that has the onException clause with retry strategy
(2) One sub route that aggregates 2 routes (and has noErrorHandler) to be sure to replay the 2 routes and not only the processing one that throw the exception :
(3) The route which send the SOAP request
(4) The route which process the response and can throw the retryException.
In order to ignore the exception thrown by the cxf endpoint I implement the route (3) like that :
public void configure() {
from(ROUTE_NAME).
.handleFault() // To handle Soap fault
.onException(Fault.class)
.continued(true)
.end()
.to("cxf:[...]")
;
}
It works like a charm, the response processor perform some checks before throwing the RetryException... But the continued instruction throws away all the informations about redelivery that I previously had and Camel believe that this exception is the first one. So the route enters into a kind of retry forever loop.
Exchange headers before onException(Fault) :
Headers: {breadcrumbId=ID-ITEM-S28636-63117-1469800853403-0-1, CamelRedelivered=true, CamelRedeliveryCounter=1, CamelRedeliveryMaxCounter=2, operationName=[...]}
Exchange headers after onException(Fault) :
Headers: {breadcrumbId=ID-ITEM-S28636-63117-1469800853403-0-1, operationName=[...]}
Did you have any solution to ignore some sub-route exception without erasing the upper route retry strategy?

I guess you are connecting the routes via direct endpoint; if that's the case, I think your main issue is that you're marking Fault as continued which implies handled, which is most likely what's causing your redelivery headers to be cleared. You could try using seda or a message queue, just not the direct endpoint since it will basically link your routes into one.

Related

camel redeliver : How do I retry processing a message from a certain point back

I wanted to apply re-deliver and use Dead Letter Channel. So found this very useful apache-camel FAQ link. I followed the steps as mentioned in this website.
I added a little more logic, the code is available in github .
Basically, as per the FAQ, we have to split the routes, such that(as mentioned in the FAQ) the camel can handle any exception in the sub route.
Here is the code (Camel Route):
#Override
public void configure() throws Exception {
errorHandler(deadLetterChannel("mock:error")
.logExhaustedMessageBody(true)
.logExhausted(true)
.logRetryStackTrace(true)
.maximumRedeliveries(3)
.redeliveryDelay(1000)
.backOffMultiplier(2)
.useOriginalMessage()
.useExponentialBackOff());
from("direct:start")
.log("Dump incoming body: " + body())
.to("direct:sub")
.end();
from("direct:sub")
.errorHandler(noErrorHandler())
.process(new SubRouteProcessor())
.log("Dump incoming body: "+ body())
.process(new NewSubRouteProcessor())
.transform(body().append("Modified Data !"))
.to("mock:result");
}
I wrote a simple unit test, to trigger the exception, hence the redeliver code is executed.
Now the difficulty/problem: The expected behavior is to do only 3 redeliver attempts, with 1000 millis delay and ExponentialBackOff.
However, the Test runs forever, keeps on doing the re-delivery with exponential delay. If I remove/comment the 2 processor calls, the code does run fine, means on exception, it does only 3 retries.
Could you please help to understand,
1) what is wrong in this code/route?
2) what is causing code to run forever?
3) why removing these processors, it works,retry happens for
said number of times only?
I just want - when ever the "direct:sub" throws "exception", the control goes back to "direct:start", re-try processing message from a certain point.
Thank you !!
I checked out your code and simply by changing getOut to getIn in both of your processors, the test passes.
Edit: My suspicion from the beginning was that your processors were somehow overriding the headers Camel set in order to do a certain amount of redelivery. In your case:
CamelRedelivered=true, CamelRedeliveryCounter=1, CamelRedeliveryMaxCounter=3
The header CamelRedeliveryCounter was not incremented when you were setting the Body on the getOut exchange. In my mind, it should and this is a bug; but maybe it is working as designed? Maybe #claus-ibsen could help with that.
Hope this helps.
R.

Apache Camel: How to transmit exceptions over a SEDA endpoint?

When I sent messages in a Camel Context Component to its endpoint, I have to wait for a response message with acknowledgement. If no response is received within timeout time, an exception shall be thrown back to the camel route.
I tried to implement it the following way:
I used a multicast to generate a timeout response while the original message is sent to the endpoint. The timeout response is delayed and if no response is received after this timeout, a timeout exception shall be thrown back on the route.
So I have the following route:
private final String internalRespUri = "direct:internal_resp";
private final String internalRespTimeout = "seda:internaltimeout";
#Override
public void configure() {
SendController send_controller = new SendController();
TimeoutResponse resp = new TimeoutResponse();
from(Endpoints.MESSAGE_IN.direct())
.errorHandler(noErrorHandler())
.routeId(Endpoints.MESSAGE_IN.atsm())
.log("Incoming message at segment in")
.process(send_controller)
.log("Message after send controller")
.multicast().parallelProcessing()
.log("After wiretap")
.to(internalRespTimeout, Endpoints.SEGMENT_OUT.direct());
from(internalRespTimeout)
.errorHandler(noErrorHandler())
.routeId(internalRespTimeout)
.log("begin response route")
.log("timeout response route")
.process(resp)
.log("modify message to response")
.delay(1000)
.log("after delay")
.to(internalRespUri);
from(Endpoints.SEGMENT_IN.seda())
.routeId(Endpoints.SEGMENT_IN.atsm())
.to(internalRespUri);
from(internalRespUri)
.errorHandler(noErrorHandler())
.routeId(internalRespUri)
.log("after response gathering point")
.choice()
.when(header(HeaderKeys.TYPE.key()).isEqualTo(UserMessageType.RESP.toString()))
.log("process responses")
.process(send_controller)
.otherwise()
.log("no response")
.to(Endpoints.MESSAGE_OUT.direct());
}
The problem is that the exception thrown in the SendController is not propagated over the SEDA endpoint internalRespTimeout.
If I use a direct endpoint instead it works, but then I have another problem:
The delay blocks the route while a received response message from endpoint Endpoints.SEGMENT_IN.seda() may not be transmitted.
Are SEDA endpoint generally not able to propagate exceptions?
How can I achieve a solution to my problem?
Thanks,
Sven
I have an idea:
Instead of throwing an exception, I possibly could use transactions for timeout.
Could this work?
I am currently not aware of a way to propagate and exception back over a SEDA endpoint in camel. The way the error handling works is based on channels between endpoints. When you use a SEDA endpoint the code will keep processing and not wait for the code since it will keep processing. I am having a bit of trouble understanding what you would like to accomplish, but I will list some similar alternatives you might be able to use.
-The first is to use a route level error handler in your SEDA based route and store the exception using a unique Id that you can lookup later.
-The second is to pass the data into a Java Bean where you have full control of what you are doing and could even consider something like using a Guava's Futures to run the code asynchronously while doing other tasks.
If you can explain what you are trying to accomplish a bit better I might be able to make a clearer suggestion.

Spring integration - decorate message on fail

I am trying to implement a process consisting of several webservice-calls, initiated by a JMS-message read by Spring-integration. Since there are no transactions across these WS-calls, I would like to keep track of how far my process has gone, so that steps that are already carried out are skipped when retrying message processing.
Example steps:
Retrieve A (get A.id)
Create new B for A (using A.id, getting B.id)
Create new C for B (using B.id, getting C.id)
Now, if the first attempt fails in step 3, I already have a created a B, and know it's id. So if I want to retry the message, it will skip the second step, and not leave me with an incomplete B.
So, to the question: Is it possible to decorate a JMS-message read by Spring-integration with additional header properties upon message processing failures? If so, how could I do this?
The way it works at the moment:
Message is read
Some exception is thrown
Message processing halts, and ActiveMQ places the message on DLQ
How I would like it to work:
Message is read
Some exception is thrown
The exception is handled, with the result of this handling being an extra header property added to the original message
ActiveMQ places the message on DLQ
One thing that might achieve this is the following:
Read the message
Start processing, wrapped in try-catch
On exception, get the extra information from the exception, create a new message based on the original one, add extra info to header and send it directly to the DLQ
Swallow the exception so the original message dissappears
This feels kinda hackish though, hopefully there is a more elegant solution.
It's hard to generalize without more information about your flow(s) but you could consider adding a custom request handler advice to decorate and/or re-route failed messages. See Adding Behavior to Endpoints.
As the other answer says, you can't modify the message but you can build a new one from it.
EDIT:
So, to the question: Is it possible to decorate a JMS-message read by Spring-integration with additional header properties upon message processing failures? If so, how could I do this?
Ahhh... now I think I know what you are asking; no, you can't "decorate" the existing message; you can republish it with additional headers instead of throwing an exception.
You can republish in the advice, or in the error flow.
It might seem like a "hack" to you, but the JMS API provides no mechanism to do what you want.
From the spring forum:
To place new header to the MessageHeaders you should use
MessageBuilder, because not only headers, but entire Message is
immutable.
return MessageBuilder.fromMessage(message).setHeader(updateflag, message.getHeaders().get("Lgg_Rid") == "ACK" ? "CONF" : "FAIL").build();
In an asynchronous context, errors will go to an error channel - either one you configure yourself and indicate in the message headers with errorChannel, or a global error channel if none is specified. See for more details here.

In Apache Camel, how can I receive an error if an endpoint doesn't exist?

We are using Camel fluent builders to set up a series of complex routes, in which we are using dynamic routing using the RecipientList functionality.
We've encountered issues where in some cases, the recipient list contains a messaging endpoint that doesn't exist (for example, something like seda:notThere).
A simple example is something like this:
from("seda:SomeSource")....to("seda:notThere");
How can I configure the route so that if the exchange tries to route to an endpoint that doesn't already exist, an error is thrown?
I'm using Camel 2.9.x, and I've already experimented with the Dead Letter Channel and various Error Handler implementations, with (seemingly) no errors or warnings logged.
The only logging I see indicates that Camel is (attempting to) send to the endpoint which doesn't exist:
2013-07-03 16:07:08,030|main|DEBUG|o.a.c.p.SendProcessor|>>>> Endpoint[seda://notThere] Exchange[Message: x.y.Z#293b9fae]
Thanks in advance!
All endpoints behave differently in this case.
If you attempt to write to a ftp server that does not exist, you certainly get an error (connection refused or otherwise)..
This is also true for a number of endpoints.
SEDA queues gets created if the do not exist and the message will be left there. So your route actually sends to "notThere" and the message will still be there until the application restarts or someone starts to consume messages from seda:notThere. This is the way seda queues are designed. If you set the size of the seda queue by to("seda:notThere?size=100"), then if there is noone reading (or reading slowly) you will get exceptions on message 101 and forward.
If you need to be sure some route is consuming your messages, use "direct" instead of "seda". You can even have some middle layer to use the features of seda with respect to staging and the features of direct knowing there is a consumer active (if sent from recipient list with perhaps user input (god forbid).
from("whatever").recipentList( ... ); // "direct:ep1" work, "direct:ep2" throws exception
from("direct:ep1").to("seda:ep1");
from("seda:ep1").doRealStagedStuffHere();

JMS Listener & Sender - Spring Framework

I want to understand a java program and need to modify which was developed using jms spring framework. Typically it has JMS receiver & sender, it receives a message from request queue and will invoke a job (another java program) once the job is completed the sender will send response to response queue. Have couple of questions which are below,
The request message is not deleted until response posted into response queue successfully. How its been achieved what is the logic behind it.
I want to write a functionality of writing response into flat file when sender fails to send message (by catching JMS exception). Once the sender queue is up and running i will read flat file and will send responses. The reason i need is because its involved in job processing could be in hours if job failed then input message will be read again by receiver. I want to avoid duplicate processing. Please suggest your ideas here.
Without seeing the configuration it's hard to answer these questions, but best guess is that #1 is because the app is using a transactional session. This means all updates on that session are not completed until the transaction is committed.
Just catch the exception and write the data; as long as the transaction commits (because you caught the exception) the input message will be removed.

Categories

Resources