5 Replies Latest reply on Aug 18, 2011 2:14 AM by Claus Ibsen

    Mixing UnitOfWork/Synchronization and onException

    Robert Schettini Newbie

      Experienced Camel Users,

       

      I am a relatively new Camel user.  I have successfully setup the "happy case" where Camel (using transactions) is routing messages through multiple JMS queues.  Now it's time to add some error handling...

       

      I use a UnitOfWork to manage updates to the file system (almost exactly as described in "Camel in Action" chapter 9).  When the Camel route succeeds, I create a file.  When the route fails, the file is deleted.  The UnitOfWork operates as expected when exceptions are thrown from my Processors and caught by Camel. 

       

      My problems occur when I add onException() to my DSL to catch and handle any exceptions manually.  On an exception, I would like to redirect the errant message to an "error queue"

       

         onException(Exception.class).handled(true).to("myErrorQueue").end()

       

      By stating handled(true), Camel believes that the route was successful, and the onComplete() method is called on the elements of the UnitOfWork.  In my case, I don't want that to happen, I want the UnitOfWork to call onFailure().

       

      I tried using handled(false).  In this case, the onFailure() method was applied to the elements of the UnitOfWork, but Camel did not route the errant message to "myErrorQueue".  Instead, Camel retried processing the message 6 times (the default # of retries) and then moved the message to the DeadLetterQueue.

       

      So I'm stuck.  How can I combine onException() to handle the errors, but still have the UnitOfWork treat the exceptions as "failures"?

       

      Any assistance would be greatly appreciated.

        • 1. Re: Mixing UnitOfWork/Synchronization and onException
          Claus Ibsen Master

          If you use JMS then the JMS broker has built in support for error queues (dead letter channel). So its in fact better to use that, instead of having Camel sending the message back to the error queue. Then all that is handled within the broker.

           

          See chapter 9 in the Camel in Action book.

           

          The Apache ActiveMQ web site have a few details as well

          http://activemq.apache.org/message-redelivery-and-dlq-handling.html

           

          Then you dont need to use the onException at all.

          • 2. Re: Mixing UnitOfWork/Synchronization and onException
            Robert Schettini Newbie

            Thanks for the suggestion Claus.  I changed my code to route my failed message to a dead letter queue:

             

                errorHandler(deadLetterChannel("myErrorQueue").disableRedelivery().useOriginalMessage());

             

            This successfully routed the origianl message to my error queue BUT the UnitOfWork was again called with the onComplete() message.

             

            No matter what I try, it appears that the only way the onFailure() message will be called for the UnitOfWork is if Camel catches an Exception.  Any of the Camel features to manually handle an exception seem to prevent the UnitOfWork from failing. 

             

            Clearly I am missing something.  The UnitOfWork seems to be the right Camel construct for handling processing that needs to be commited or rolled back based on the success or failure of the Camel route.  But what are the conditions under which onFailure will be called?

            • 3. Re: Mixing UnitOfWork/Synchronization and onException
              Claus Ibsen Master

              The UnitOfWork is executed at the very end. After onException or any errorHandler which may have been invoked.

               

              I was actually suggesting to not use the Camel errorHandler at all, but rely on the JMS broker to use its dead letter queue feature. The JMS Broker can be configured to move failed messages to a selected dead letter queue in case a consumer cannot process it successfully. The JMS broker have similar options as Camel in terms of number of redelivery attempts, delays between redeliveries, etc. When you use that its more safer as its all handled within the JMS broker itself.

               

              If you let Camel do it, then its JMS -> Camel -> JMS.

               

               

              If you let the JMS broker use its dead letter queue, then you can keep the UnitOfWork logic, as the exchange will still fail at the end, so it causes a rollback on the transaction, which is propagated back to the JMS broker, so it knows the TX failed.

               

              That means the exchange will failed at the end, and the UnitOfWork will invoke the onFailed method, where you can delete the file.

               

              If doing this you do not need to use the onException, errorHandler etc at all.

              • 4. Re: Mixing UnitOfWork/Synchronization and onException
                Claus Ibsen Master

                In terms of if you want to use as your very first suggestion

                 

                onException(Exception.class).handled(true).to("myErrorQueue").end()

                 

                Then you can move the logic that you do with the UnitOfWork into this piece. Such as using a processor to delete the file.

                 

                onException(Exception.class).handled(true).process(new MyDeleteFileProcessor()).  to("myErrorQueue").end()

                 

                Then the deleting becomes part of this logic above.

                • 5. Re: Mixing UnitOfWork/Synchronization and onException
                  Claus Ibsen Master

                  If you look at this unit test example from the book

                  http://code.google.com/p/camelinaction/source/browse/trunk/chapter9/riderautoparts-partner/src/test/java/camelinaction/RiderAutoPartsPartnerTXTest.java

                   

                  Then the testNoConnectionToDatabase shows a situation where the message will fail all the time, and eventually be moved in the DLQ of the JMS broker. The default DLQ is named "ActiveMQ.DLQ". You can configure this to be another name, or to use a DLQ per queue using some sort of prefix/suffix syntax.