24

First, yes, I have searched and, yes, I have read the same Apache document every one points to. :-) I think there is a bit of confusion and I think I know an answer, so let me lay out an example of what I thought was correct, follow it with what I think the answer is. Thanks. Oh, and I do know that some of the endChoice() lines are not strictly necessary and that Camel will figure it out, but I like the blocks to be cleanly delineated, unless there is some reason not to use them.

.choice()
    .when(X1)
        // do stuff
        .choice()
            .when(Y)
                //do more stuff
            .endChoice()  // close inner when block
        .end() // close inner choice block
    .endChoice()  // close first outer when
    .when(X2)
        // do other stuff
    .endChoice()  // close second outer when
.end() // close outer choice

So, my original look at the API, I thought that end() was for closing things like choice and split and that endChoice() was for closing choice options like when and otherwise. It looks more like the latter is actually an end() that returns a ChoiceDefinition. Which makes the name a little better.

But, if I take out the end() labeled 'close inner choice block', this means I carry on to the next line, an endChoice(). Does this then close the inner choice block? Given that, the when(X2) is still within the the when(X1) block. So, I think that I need to replace the end() with an endChoice() rather than removing it. So the result would look like:

.choice()
    .when(X1)
        // do stuff
        .choice()
            .when(Y)
                //do more stuff
            .endChoice()  // close inner when block
        .endChoice() // close inner choice block
    .endChoice()  // close first outer when
    .when(X2)
        // do other stuff
    .endChoice()  // close second outer when
.end() // close outer choice

So is this the way to handle this in Camel? Or is there a simpler way that I am just missing? Thanks for your time.

2 Answers 2

33

SHORT ANSWER: I will call myself on this so no one else has to, the answer is that you are doing it wrong and should not have nested choices.

LONG ANSWER: I inherited a complicated route builder and was trying to clean it up to make it clearer. But straightening and putting in either end() or endChoice() just broke things. And, yes, the above fix still broke things. I did not understand how Camel knew which block to go to. Research and trying to find good examples of nesting eventually drove home the fact that Camel is not really designed for nesting choices. It allows it, but due to limitations in Java, it does not do it well. So I tried removing my nested choices. While this would have been possible, it would have meant ugly redundant conditionals, like:

choice()
  .when(x and a)
    //do stuff xa
  .when(x not a)
    // do other x stuff
  .when(y and a)
    // do y stuff

Only mine would have had at least another level. Further thought and recalling things that I had read brought about the second bit of enlightenment. The whole point of Camel is directing routes. Each choice's when block should just be pointing the process to a route. It should not be thinking, processing, or anything. In the end, our group is going to be refactoring to remove most of the logic from the route builder to a bean. The design we will be working towards will be something simple:

   from(uri)
     .bean(class, method)  // do any processing
     .choice()
       .when(header("result").isEqualTo("A")
          .to(routeA)
       .endChoice()
       .when(header("result").isEqualTo("B")
          .to(routeB)
       .endChoice()
       .when(header("result").isEqualTo("C")
          .to(route)
       .endChoice()
      .end()

My advice to you is to avoid nesting choices. Particularly complicated ones. You might get it to work, but you will not be able to trust it when you have to make changes later. If you find yourself tempted to use nested choices, examine what you are trying to accomplish and decide if it really belongs in a route builder.

2
  • 3
    Good advice, I also try to not nest choices. Your colleagues or your future self will thank you ;). Commented Dec 22, 2016 at 10:06
  • I suggest to break down this kind of scenario into multiple routes where each route will contain its own single choice - when block. Commented Nov 14, 2019 at 6:44
14

Coming late in the game, but might help.

Nested choice definitions work just fine with Camel. Only your terminators are wrong:

  • .endChoice() --> to close a "when" predicate
  • .end() --> to close the entire "choice" block

I know, the syntax is a bit confusing.

So in your case:

.choice()
    .when(X1)
        // do stuff

        .choice()
            .when(Y)
                //do more stuff
            .endChoice() // close Y condition

        .end() // close inner choice block

    .endChoice() // close X1 condition

    .when(X2)
        // do other stuff
    .endChoice() // close X2 condition

    .otherwise()
        // default case
    .endChoice() // close default condition

.end()

In practice, you don't have to close all when predicates, only for multiple child routings. In my humble opinion, being super meticulous with the indentation helps a lot.

3
  • 2
    This is one of the many examples where Camel "syntax" could be much better :(
    – user272735
    Commented Sep 30, 2021 at 5:55
  • When I'm in an inner choice block (without .otherwise()) and want to close just that, I need to call .endChoice().end() and not just .end() for whatever reason, or it breaks the hierarchy. Not sure if it is always the case, but I don't really trust Camel in that regard Commented Sep 6, 2023 at 9:15
  • Agree @MarianKlühspies, closing with endChoice() is always allowed and safer anyway.
    – Alexis
    Commented Mar 6 at 18:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.