-1

I have been working with Apache Camel for a while and I came across splitter functionality. And I was wondering why can't I stop the whole split process on some condition? After hours of googling I didn't find any info about it. The only thing I found was stopOnException() and stopOnAggregateException(). But what if I want to stop it on some specific exception or even on some condition depending on message data. Can somebody tell me how I can do that?

Let's consider this peace of code

from("timer://file-poll?period=5s")
    .onException(Exception.class)
        .log(LoggingLevel.ERROR, "Error message")
        .end()
    .to("direct:load-file")        // load csv
    .split(body()).delimiter("\n") // split file into lines
        .to("direct:process-line") // throws exceptions
    .end()
    .to("direct:save-result");

I poll file system and load a csv file with 1,000 lines.

Then I split the file into lines and process each line separately.

direct:process-line route can throw exceptions and if it throws LineProcessingException I need to skip the line and continue processing of next lines but if it throws any other exceptions I need to stop the splitter.

For instance: Processing of line 15th throws LineProcessingException and I skip this line and continue processing the rest 985 lines.

Processing of line 30th throws IOException so now I need to stop processing 970 lines that left and go to direct:save-result route.

I tried stopOnException() and stopOnAggregateException() but as I said above it stops splitter on any exceptions and I don't need to stop the splitter and just skip the line if LineProcessingException was thrown. I also tried calling stop() on a sub-route that is processing the line but it stops only the sub-route not the splitter.

Eugene M
  • 11
  • 2
  • What do you mean by stop? Could you add some code snippet with a walk through what you are trying to achieve and how have you tried to address it? – tmarwen Jun 30 '22 at 10:24
  • You stop the splitter by raising an exception and handling it either with `stopOnException()` or `stopOnAggregateException()` as you have already noted. Another approach could be to use `.filter()` to skip the bad data so it's not processed at all. – user272735 Jun 30 '22 at 13:57
  • Please provide enough code so others can better understand or reproduce the problem. – Community Jun 30 '22 at 15:17
  • 1
    @tmarwen @user272735 I added an example. As you can see I can't use `stopOnException()` and I think `filter()` is not a good idea because you still need to iterate through all the lines and what if we have millions of lines or we have time consuming code before `filter()` – Eugene M Jun 30 '22 at 19:53

1 Answers1

0

I'm still not convinced the filtering is a bad option here. If you can identify and ignore invalid data up-front (as your question suggests) filtering is very elegant.

Below you find two examples how to skip data that is known to be invalid and thus non-processable. This way you eliminate the need for one exception. Hopefully it gives you new ideas how to approach the real problem.

from("timer:mainRouteTrigger?repeatCount=1")
.routeId("MainRouteTrigger")
    // invalid value (BOOM)
    .setBody(constant("100\n200\n300\nBOOM\n400"))
    .to("direct:mainRoute")
.end()
;

from("direct:mainRoute")
.routeId("MainRoute")
    .log("MainRoute BEGINS: BODY: ${body}")
    .split(body().tokenize("\n"))
        .multicast()
            .to("direct:processRecord1")
            .to("direct:processRecord2")
        .end()
    .end()
    .log("MainRoute ENDS: BODY: ${body}")
.end()
;

from("direct:processRecord1")
.routeId("processRecord1")
    .filter(method(ValueFilter.class, "isValidValue"))
    .log("processing record ${body}")
.end()
;

from("direct:processRecord2")
.routeId("processRecord2")
    .process(e -> {
        String body = e.getMessage().getBody(String.class);
        if (ValueFilter.isValidValue(body)) {
            e.setProperty("PROCESSED", true);
            e.getMessage().setBody("processing record " + body);
        }
    })
    .choice()
        .when(simple("${exchangeProperty.PROCESSED}"))
            .log("${body}")
.end()
;
import org.apache.camel.Body;

public class ValueFilter {
    static public boolean isValidValue(@Body String body) {
        switch (body) {
            case "100":
            case "200":
            case "300":
            case "400":
                return true;
            default:
                return false;
        }
    }
}

When the example is run the following is correct output is logged:

MainRoute                      INFO  MainRoute BEGINS: BODY: 100
200
300
BOOM
400
processRecord1                 INFO  processing record 100
processRecord2                 INFO  processing record 100
processRecord1                 INFO  processing record 200
processRecord2                 INFO  processing record 200
processRecord1                 INFO  processing record 300
processRecord2                 INFO  processing record 300
processRecord1                 INFO  processing record 400
processRecord2                 INFO  processing record 400
MainRoute                      INFO  MainRoute ENDS: BODY: 100
200
300
BOOM
400
user272735
  • 10,473
  • 9
  • 65
  • 96