Search code examples
apache-camelspring-camel

How to prevent Camel splitter from aggregating after processing?


I have a camel route set up in the following way:

List<SomeRouteInfo> routes = getRouteInfo(); //I crete the routes using the info in the list.
for ( SomeRouteInfo info : routes ){
    RouteDefinition routeDef = from(info.from());

    routeDef
        .errorHandler(someErrorHandler);
        .routeId(info.getId());

    if (info.someCondition()){
        routeDef
            .split().method(someSplitterBean)
            .process(processorBean1) //The message seems split here.
    }

    routeDef
        .process(processorBean2) //The message is no longer split here. I need it to be.
        .to(info.to())
}

Basically, some routes I want the splitter to be present on, on others I dont. The info for whether or not to split a message is not present in the message itself.

So, the problem is when I process in processorBean1 it seems like I have multiple messages. Unfortunately it seems like at processorBean2 I don't have the same. Instead I have the original message body back.

I wan't the split up messages to fail individually in processorBean2 on exception without knocking down everthing else. So, I want it to be seperate messages at processorBean2 for that reason. What can I do to accoplish that?


Solution

  • Yes, the Splitter EIP returns by default the original message. This is explained at "What the Splitter retuns" in the Camel docs.

    I assume that the Splitter is implicitly "closed" at the end of the routeDef configuration in the if block.

    Therefore, your route effectively looks like this:

    ...
    .routeId()
    .split().method(someSplitterBean)
        .process(processorBean1) // here you got splitted messages
    .end // end splitter; from here you got the original message (before split)
    .process(processorBean2) 
    .to(info.to())
    

    So, for your case you have mutliple possibilities:

    1: Move processorBean2 and .to(info.to()) inside the Splitter. I.e. directly below processorBean1. That way the splitter still returns the original message, but you don't use it anymore.

    .split().method(someSplitterBean)
        .process(processorBean1) // here you got splitted messages
        .process(processorBean2) 
        .to(info.to())
    .end // end splitter; from here you got the original message (before split)
    

    In your case you would need to make, starting from the splitter, the whole rest of the route conditional.

    2: You add your own aggregator strategy to return for example a collection that contains the splitted messages instead of the original. However, the splitter can only return one single message. So you can't continue with the individual splitted messages after the splitter. The only place where you get them is inside the splitter.

    .split(method(someSplitterBean), new MyStrategy())
    ...
    

    3: You divide the "split path" and the "non-split path" (condition in your route builder) in two separate sub-routes and just make the `.to([subroute]) conditional.

    routeDef
        .errorHandler(someErrorHandler);
        .routeId(info.getId());
    
    if (info.someCondition()){
        routeDef.to("direct:mySplitRoute")
    } else {
        routeDef.to("direct:myStandardRoute")
    }
    

    This way you got a bit of redundancy in the two routes but on the other hand you got a cleanly separated processing of the (obviously) different message types.