LearnToLive
LearnToLive

Reputation: 511

Original Message within the body of camel Exchange is lost

I have a camel route as follows which is transacted.

 from("jms:queue:start")
     .transacted()
     .bean(new FirstDummyBean(), "setBodyToHello")
     .bean(new SecondDummyBean(), "setBodyToWorld")
     .to("jms:queue:end")

The bean methods due as their name suggests, set body to "Hello" and "World" respectively.

I also have a onException clause setup as well as follows:

onException(Exception.class)
    .useOriginalMessage()
    .handled(true)
    .to("jms:queue:deadletter")
    .markRollbackOnlyLast(); 

Assume, I drop a message on queue "start" with body as "test message". After successfully processing in FirstDummyBean, I throw a RuntimeException in SecondDummyBean. I was expecting to the see the actual message or (the original message contents intact ie "test message") being sent to my dead letter queue.

However the contents of the message on deadletter queue are "Hello".

Why is this happening?..

I am using apache camel 2.10.0.

Also can anyone provide more information on how I can use both errorhandler and onexception clause together. The document says :

If you have marked a route as transacted using the transacted DSL then Camel will automatic use a TransactionErrorHandler. It will try to lookup the global/per route configured error handler and use it if its a TransactionErrorHandlerBuilder
instance. If not Camel will automatic create a temporary TransactionErrorHandler that overrules the default error handler. This is convention over configuration.

Example of how to use transactionerrorhandler with JavaDSL would be great.

Upvotes: 0

Views: 8264

Answers (1)

Richard Miskin
Richard Miskin

Reputation: 1260

I've seen this in non-transaction examples and it appears that useOriginalMessage() does use the original exchange, but if you've modified any objects that this references then you still get the modifications. It doesn't appear that useOriginalMessage goes back to the queue to get the original data.

Example code to show problem

The code below includes a set of route to demonstrate the problem. The timed route sends an ArrayList containing the String "Test message" to a queue read by a second route. This second route passes the message to ModifyBody which changes the content of the list. Next the message goes to TriggerException with throws a RuntimeException. This is handled by the onException route, which despite using useOriginalMessage is passed the updated body.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.camel.spring.SpringRouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class TimedRoute extends SpringRouteBuilder {

    private static final String QUEUE = "jms:a.queue";
    private static final String QUEUE2 = "jms:another.queue";
    // The message that will be sent on the route
    private static final ArrayList<String> payLoad = new ArrayList<String>(
            Arrays.asList("test message"));

    public static class ModifyBody {
        public List<String> modify(final List<String> list) {
            final List<String> returnList = list;
            returnList.clear();
            returnList.add("Hello");
            return returnList;
        }
    }

    public static class TriggerException {
        public List<String> trigger(final List<String> list) {
            throw new RuntimeException("Deliberate");
        }
    }

    @Override
    public void configure() throws Exception {
        //@formatter:off
        onException(Exception.class)
             .useOriginalMessage()
             .log("Exception: ${body}")
             .handled(true)
             .setHeader("exception", constant("exception"))
             .to(QUEUE2);


        // Timed route to send the original message
        from("timer://foo?period=60000")
           .setBody().constant(payLoad)
           .to(QUEUE);

        // Initial processing route - this modifies the body.
        from(QUEUE)
            .log("queue ${body}")
            .bean(new ModifyBody())
            .log("after firstDummyBean: ${body}")
            .bean(new TriggerException())
            .stop();

        // Messages are send here by the exception handler.
        from(QUEUE2)
            .log("queue2: ${body}")
            .stop();
        //@formatter:on
    }
}

Workaround

If you replace ModifyBody with the code below then the original message is seen in the exception handling route.

public static class ModifyBody {
    public List<String> modify(final List<String> list) {
        final List<String> returnList = new ArrayList<String>(list);
        returnList.clear();
        returnList.add("Hello");
        return returnList;
    }
}

By changing the body to a new list the original Exchange can be left unmodified.

Doing a general solution is awkward as the mechanism for copying will depend on the objects that you have in flight. You might find that you can extend the RouteBuilder class to give yourself some custom DSL that copies your objects.

Upvotes: 2

Related Questions