Apache Camel: General Tips

Daniel S. Blanco
3 min readApr 12, 2023

--

Lately I’ve been doing a lot of integrations with Apache Camel. I’ve collected some tips that can be useful. Some have come from exceptions during the development, others are configuration or lesser known items.

  • DataBase Components

There are 3 different components that help us to operate with databases: JDBC, JPA and SQL. But in each of them you can pass parameters to the queries in a different way.

With JDBC you can use headers as parameters, but you need to put before ‘:?’ characters and use the query parameter ‘useHeadersAsParameters’.

from("direct:jdbc01").routeId("jdbc01")
.setHeader("bookId").constant(1)
.setBody(constant("select * from BOOK where id = :?bookId"))
.to("jdbc:dataSource?useHeadersAsParameters=true&outputType=SelectOne&outputClass="+Libro.class.getName())
.marshal().json(JsonLibrary.Jackson);

With JPA you can pass the parameters through the header CamelJpaParameters, which content it’s a Map.

@NamedQuery(name = "selectByName", query = "select L from com.example.Libro L where L.nombre like :nombre")
public class Libro {
@Id
private Integer id;
private String nombre;
}
// …..
.setHeader("CamelJpaParameters", constant(new HashMap<String, Object>()))
//add key-value 'nombre' with header.value
.bean("libroHelper", "addParameter(${exchange}, 'nombre', ${header.nombre})")
.setProperty("namedQuery", constant("selectByName"))
.toD("jpa:"+Libro.class.getName()+"?namedQuery=${exchangeProperty.namedQuery}")

SQL is the easiest, it can be passed as headers or body attributes directly.

from("direct:sqlInsert2")
.to("sql:INSERT INTO BOOK (NAME, AUTHOR) VALUES (:#${body.name}, :#${body.author})");
  • HTTP4

It’s a key element to call external HTTP Server, but it always launchs a exception if receive a exception different than a 300+ status code. To avoid this, use the throwExceptionOnFailure query parameter.

And if you want to finish the execution, and generate a response. You can use the stop DSL Command.

.to("http4:{{external.host}}/services/GetBooksProxy?httpMethod=POST&throwExceptionOnFailure=false")
.filter(header(Exchange.HTTP_RESPONSE_CODE).regex("^[3–5][0–9]{2}$"))
.log("Error calling External server to GetBooksProxy")
.stop()
.end()

The result of the invocation will be a InputStream, to support reading it multiple times. But if you want to use xpath DSL command or jsonpath DSL command, you must be careful and pass to string the result firstly. Because otherwise it will raise an exception with the message: ‘No serializer found for class org.apache.camel.converter.stream.InputStreamCache and no properties discovered to create BeanSerializer’.

from("direct:slc").routeId("slc")
.to("http4:{{external.host}}/supplier/pra/private/staging?httpMethod=PUT&disableStreamCache=false")
.convertBodyTo(String.class)
.marshal().json(JsonLibrary.Jackson)
.filter().jsonpath("status == 'success'").log("sucessful");
  • SPLIT

When you split a body, you must keep in mind that it will be transformed to a LinkedHashMap. So, for example if you are splitting a JSON object, and you want to use jsonpath DSL Command, you will need to unmarshal again.

from("direct:stc").routeId("stc")
.to("http4:{{external.host}}/services/GetBooksProxy?httpMethod=POST&throwExceptionOnFailure=false")
.split().jsonpath("$.response[0].resultado").parallelProcessing()
.marshal().json(JsonLibrary.Jackson)
.setProperty("bookId").jsonpath("$.bookId")
.end()
.end();

As the with the split Java DSL command, the return of the SQL Component is a LinkedHashMap so you can access to fields with ${body[xxx]}

from("direct:rea").routeId("rea")
.to("sql:SELECT indexId, identification FROM TABLE_X")
.split().body().parallelProcessing()
.removeHeaders("Camel*")
.setHeader("Content-Type").constant("application/json")
.setHeader("identification").simple("${body[identification]}")
.to("http4:{{external.host}}/book/${header.identification}?httpMethod=GET")
.end()
.end();
  • Transaction

If you mark a route as transactional with the transacted DSL command, if the route generates an exception, the rollback will be automatic. But if you catch the exception, you will have to do the rollback automatically.

from("jmsTx:queue:transactedException01").routeId("transactedException01")
.transacted()
.doTry()
.to("http4:localhost:57020/fault?httpMethod=POST")
.endDoTry()
.doCatch(HttpHostConnectException.class)
.rollback()
.end();
  • XPath

In case you want to get the result of an xpath expression in a specific format, since by default it will return a native XML object. But you can generate a different result if you pass the class of your choice.

.setProperty("total").xpath("count(//n2:bookLists/n2:bookId)", Integer.class, getNamespaces())
.filter(exchangeProperty("total").isEqualTo(0))
.log("GetBooks_Result is empty")
.stop()
.end()

As you can see, a set of tips and tricks that can save you a little time in your daily development. I hope you found it useful.

--

--

No responses yet