catch exception in camel - java

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.

Related

In Camel #Produce produces what, and #Consume consumes what?

As an irrelevant poorly opinionated opinion, I find apache camel docs too presumptuous in presuming the reader has a background in camel already.
In normal terms, a web service provider is a producer, and a client of the service it produces is a consumer.
Let's look at http://camel.apache.org/pojo-producing.html.
Which says, there are two diff ways to send messages to Camel Endpoint.
#EndpointInjecct (uri ..) ProducerTemplate ...
Is it saying
Hey I;m an endpoint and this is my uri and a template to hit me with, so hit me
Or, hmm ... there is an endpoint with this uri somewhere out there beneath the clear blue skies, and here is the template I presume I'm gonna hit it with
???
Similarly, is #Produce, and ProducerTemplate
specifying I'm an endpoint that is going to get hit?
or specifying the template of a producer I am going to hit??
Similarly, #Consume,
Am I specifying I am a consumer?
Or that I am specifying how I am to be consumed?
And BTW, the uri in #Produce(uri) or EndpointInjection (uri)
am I sending to this uri?
or recceiving under this uri?
As per your doubts, below I'm trying to clarify:
#EndpointInject (uri=...) ProducerTemplate ...
This means rather your 2nd option:
there is an endpoint with this uri somewhere out there beneath the clear blue skies, and here is the template I presume I'm gonna hit it with
I.e., uses the Camel API, ProducerTemplate, to send the message to another endpoint, defined on the uri.
Regarding the #Produce and ProducerTemplate, the closest guess here would be
specifying the template of a producer I am going to hit
Although technically the endpoint specified to be hit on the uri is not to be mistaken with the producer in Camel terminology, it is simply an endpoint, which in this context would be called a consumer since it receives messages.
Analogously, regarding #Consume marks the method as a consumer method, i.e. the one that handles the endpoint incomming messages, endpoint specified by the uri.
Hope this is of some help.

Camel consume single message and stop, transacted

I am trying to use Camel to consume a single message from a JMS queue in a transacted manner. Specifically in a flow like this:
Wait until message is published on JMS queue
Try to consume and process the single message
If processing fails (exception occurs), rollback the consumption
If the processing passes, acknowledge and stop consuming anymore messages
Later in the application lifecycle, another process triggers consumption to start again from (1)
At first I tried to do this using a polling consumer, using the ConsumerTemplate, but I can't figure out if its possible to do this transactionally - it seems like the transaction is internal to the ConsumerTemplate so regardless of what I do the message is already acknowledged as consumed by the time the ConsumerTemplate returns.
Can I do this using the ConsumerTemplate? Can I do this using Camel and if so what is the best approach (Simple examples would be appreciated)?
I ended up using the pollEnrich dsl to achieve this. For example my route builder looks like:
from("direct:service-endpont").transacted("PROPOGATION_REQUIRED").setExchangePattern(ExchangePattern.InOut).pollEnrich("activemq:test-queue").bean(myHandler);
I use the direct endpoint as a service, sending a "request" message to the direct endpoint polls the jms queue for a single message (blocking if required). The transaction started extends to the pollEnrich so if, for example, myHandler bean fails then the message taken during the pollEnrich is not consumed and left on the queue.

update camel exchange body after exception and continue the route

Hope you have some spare minutes for my question. For the last couple of days I was reading about Camel and managed to setup everything up and running. Now, I have bumped on a tricky part :). Basically, I define a route using Java in runtime and put the route configuration in DB. Routes are working and messages are flowing from one side to another. But, when an exception occurs I would like to know where the exception has occurred (between which route endpoints), store the current exchange body (in-flight message that is useful for further processing) in the DB, update the message by the user and then retry the route execution from the point of exception (where it failed). Lets say that all route steps are idempotent.
My solution:
Make a Interceptor for the route
Granulate the route in as much as possible parts (each new step is a new route)
Between each step update the DB with the current and future step and current exchange body
If exception occurs, store the message in DB, update it using an editor
Send a message to a next route point (taken from DB) using ProducerTemplate
What do you think about this ? Is it doable or Camel cannot support me with this approach ?
Thank you for patience and your time.
Hope I was clear enough.
You can use Camel's tracer component. A detailed example meeting your needs is already available on camel's site : http://camel.apache.org/tracer-example.html
You should use onException() clause to tackle this. For eg :
public void configure() throws Exception{
//This is a global onException definition and will work for all routes defined in this calss
onException().process(new Processor(){
public void process(Exchang arg0){
Exception e = arg0.getProperty(Exchange.EXCEPTION_CAUGHT,Exception.class);
//get message and other properties that you are interested in
db.saveOrUpdate(/*Pass your custom object here*/);
}
}).handled(true);
from("direct:route1")
//some processing
.to("jms:route1");
from("direct:route2")
//some processing
.to("http://route2");
}
You might need to consult exact details at apache camel site, since i just wrote this code here.

Apache Camel - Multicast - Is there a 'null' or a similar endpoint in Camel?

Please excuse stupidity as this is my first Camel application
To respond to a web request, I am sourcing the content from two different sources.
I am, therefore, making a multicast request to two methods and parallelizing it.
The response is an marshalled JSON object (using camel-jackson)
All works fine.
public class RestToBeanRouter extends RouteBuilder{
#Override
public void configure() throws Exception {
from("cxfrs://bean://rsServer")
.multicast()
.parallelProcessing()
.aggregationStrategy(new CoreSearchResponseAggregator())
.beanRef("searchRestServiceImpl", "firstMethod")
.beanRef("searchRestServiceImpl", "secondMethod")
.end()
.marshal().json(JsonLibrary.Jackson)
.to("log://camelLogger?level=DEBUG");
}
Question :
The Multicast routing expects a to in the DSL. Currently, I am mapping this to a log endpoint. Is this fine?
Since I am not using the to and the last exchange of the Aggregator strategy is the one which is returned to the user, should my endpoint be configured to something else - like a null or something? (Ah, the stupidity kicks in)
For the benefit of SO visitors, copying the solution given in the Camel mailing list here :
by Robert Simmons Jr. MSc. - Lead Java Architect # EA
Author of: Hardcore Java (2003) and Maintainable Java (2012)
The aggregated exchange is the one that gets returned and how the
aggregated exchange is created depends on the aggregation strategy you use.
When a route stops either by calling stop or merely not routing anymore,
the exchange on the last part of the route could be considered a reply. In
most cases it will reply back to the caller (unless you set a reply-to
destination in a JMS based route or some other cases). In your case if all
you want to do is return the enriched exchange then you dont need any to()
call. Just stop after the marshal.

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