Apache Camel: Tips & Caveats (from the trenches)

While working with a new client on some Camel-based microservices, I’ve been trying my best to keep a list of caveats and potential issues that occasionally pop up. Camel’s integration patterns and components are extremely powerful and include many bells and whistles. But unfortunately, that flexibility can also get in the way…

Without further ado, here’s the running list.  I’ll come back and update these, periodically!

camel-http’s bridgeEndpoint=true

If a route consumes from restlet/servlet, then later calls a different web endpoint (using the “http:” or “https:” components), you have to include the “?bridgeEndpoint=true” query param on that call’s URI. Example: .to(“https://www.3riverdev.com?bridgeEndpoint=true”). Camel components frequently use headers to control actions, and http/https are no exception. When the route initially consumed from the web service endpoint, the exchange automatically set an Exchange.HTTP_URI header. If that header is still present when you later call a different endpoint, that header will override whatever URL you have defined at that step. The bridgeEndpoint switch effectively tells Camel to ignore any URIs set in headers/properties and instead use the explicitly-given URL for the call.

camel-http + Apache HttpClient + retry attempts

camel-http is based on Apache’s HttpClient, which by default provides its own retry handling. Adding redeliveries in Camel’s onException handling on top of that ends up multiplying the attempts!  More details are available in this post: camel-http, Apache HttpClient, and Retry Attempts

Property Placeholder Notation

I trip over this simple one all the time: ${…} notation is for Camel expressions (dynamic operations in a route, etc.), while {{…}} notation is solely for property placeholders. So, if you set configuration properties in Spring’s application.properties file, using them within the route must be through the {{…}} notation.

Stateful Processors/Beans

Processors/beans used in Camel routes should never be stateful.  Spring treats them as singleton services by default, so statefulness will cause logic collisions. Here’s a simple example I wrote up a while back: Apache Camel Processors Should NEVER Be Stateful

endChoice

When using .choice(), note that .endChoice() might be the most confusingly named element in all of Camel. .endChoice() ends the current clause within the choice, not the choice itself! So, the structure typically looks something like:

.choice()
  .when(...)
    ...
  .endChoice()
  .when(...)
    ...
  .endChoice()
  .otherwise()
    ...
.end()

So, the .endChoice() elements terminate each .when() clause, then the final .end() element terminates the entire .choice() block. It’s clunky for sure, but Camel is stretching the limits of what Java can provide as a DSL.
Forgetting an .endChoice() or .end() completely changes the logic in the route, treating everything below it as still a part of the conditional block. So, it’s good practice to always include them by default, even if technically not needed (single clause .choice(), etc.)

Unique URIs

Within a camel-context, all subroute endpoints must use unique URIs. Using generic ones, like “direct:exception-route”, can later cause issues as the application continues to grow — conflicts come up, especially when multiple developers are involved. Try to be as specific as possible from the beginning. Include the parent route’s URI, the route builder name, etc. in the subroute URI in order to guarantee uniqueness (ex: “direct:subscription-service-exception-route”).

Abstract/Parent RouteBuilder

Never define subroutes in an abstract/parent RouteBuilder class, then extend that class with multiple child RouteBuilders. That will cause Camel to try and create the parent’s routes multiple times, causing conflicts. It’s perfectly fine to define commonly used routes in some sort of “base” RouteBuilder, but that needs to be standalone and not inherited by others.