Babel Camel Basics

The Babel Camel Basics part exposes which basic statement may be defined.

Messages

In Babel Camel, the base interface which models a message which passes through the route is called Message. A Message contains a payload called body. From Camel point of view, a Babel Message may be understood as the in Message of an Exchange with required methods to read and write the Exchange properties.

For more details, please have a look at the Camel Message Specification

Basics

A simple route without changing or routing the message. The output will be the same as the input.

import io.xtech.babel.camel.builder.RouteBuilder

val routeDef = new RouteBuilder {
  //sends what is received in the direct
  //  endpoint to the mock endpoint
  from("direct:input").to("mock:output")
}

The producer may set the exchange as InOnly by setting the second argument of to, to false. This would override the default behaviour of the producer.

import io.xtech.babel.camel.builder.RouteBuilder

val routeDef = new RouteBuilder {
  //the mock endpoint is set in InOnly Exchange Pattern
  from("direct:input").to("mock:output", false)
}

The producer may set the exchange as InOut by setting the second argument of to, to true. This would override the default behaviour of the producer.

import io.xtech.babel.camel.builder.RouteBuilder

val routeDef = new RouteBuilder {
  //the mock endpoint is set in InOut Exchange Pattern
  from("direct:input").to("mock:output", true)
}

Route Id

The routeId will give an id to the route, then this id can be used for the monitoring or during testing.

val routeBuilder = new RouteBuilder {
  from("direct:input").
    //the routeId of this route will be "bla"
    routeId("bla").
    //the routeId keyword needs to be at the beginning of the route
    //   (enforced by the Babel DSL)
    to("mock:output")
}

The routeId can not be specified as null nor an empty string.

val route = new RouteBuilder {
  from("direct:input").
    //a routeId may not be empty
    routeId("") must throwA[IllegalArgumentException]
}
val route = new RouteBuilder {
  from("direct:input").
    //a routeId may not be null
    routeId(null) must throwA[IllegalArgumentException]
}

Note

The routeId keyword is member of a set of keywords which should follow directly the from keyword or any keyword of this set.

As

A basic example with type transformation. The keyword as will coerce the type of the message passing within a route to a given type.

val routeDef = new RouteBuilder {
  //message bodies are converted to String if required
  from("direct:input").as[String]
    //the processBody concatenates received String with "4"
    .processBody(_ + "4")
    //sends the concatenated string to the mock endpoint
    .to("mock:output")
}

RequireAs

A basic example with type requirement. The requireAs will type the exchange body for the next keyword and will accept only a message with the given type.

import io.xtech.babel.camel.builder.RouteBuilder

val routeDef = new RouteBuilder {
  //Input Message bodies should be of type String
  //  or would throw an Exception
  from("direct:input").requireAs[String].
    processBody(_ + "4").to("mock:output")
}
val producer = camelContext.createProducerTemplate()
producer.sendBody("direct:input", 123) must throwA[CamelExecutionException]

The requiredAs lets you ensure you will always receive the expected body type. For example, the following may not work.

import io.xtech.babel.camel.builder.RouteBuilder

val routeDef = new RouteBuilder {
  //no input Message may satisfies both type constraints,
  //   thus any message sent would throw an Exception.
  from("direct:input").requireAs[String].requireAs[Int].
    to("mock:output")
}
val producer = camelContext.createProducerTemplate()
producer.sendBody("direct:input", "123") must throwA[CamelExecutionException]

Warning

Camel also provides tools to handle data type at runtime (which may be referred to as “runtime typing”). This may cause the regular typing to modify your data after the requireAs keyword depending on your ecosystem. Unfortunately, there is no way for Babel to prevent such variable behaviour.

Logging

With a log, you can log a defined string (which may use Camel Simple Expression Language) and define:

  • the Log level
  • the Log name
  • a marker for this Log event
import io.xtech.babel.camel.builder.RouteBuilder
import org.apache.camel.LoggingLevel

val routeBuilder = new RouteBuilder {
  from("direct:input")
    //logs to the Trace level message such as "received ID-3423 -> toto"
    .log(LoggingLevel.TRACE, "my.cool.toto", "foo", "received: ${id} -> ${body}")
    //logs to the Info level message such as "ID-3423 -> toto"
    .log(LoggingLevel.INFO, "${id} -> ${body}")
    .to("mock:output")
}

Sub routes

With a sub, you can define the following steps as part of a new route.

val routeBuilder = new RouteBuilder {
  from("direct:input").
    to("mock:before").
    //defines a new route called "subroute" which
    //   will send its incoming message to the next mock endpoint
    sub("subroute").
    to("mock:after")
}

This example would just create two routes:

  • the “master-route” from the “input” to the “before” to the “subroute”
  • the “subroute” from the “subroute” to the “after”

Thus, sub may be seen as an inline channel. The first goal of the sub route is to separate, at runtime, the two routes in a proper manner without requiring more than required code. The sub routes may also be interesting for example in Error Handling.

Route configuration

Callbacks may be added to a given route in order to manage its lifecycle such as :

  • onInit
  • onStart
  • onSuspend
  • onResume
  • onStop
  • onRemove
var success: Boolean = false
val routeBuilder = new RouteBuilder {
  from("direct:input").
    //As the route is initialing, the success variable is set to true
    onInit(route => success = true).
    to("mock:output")
}

Concerning the exchange lifecycle :

  • onExchangeBegin
  • onExchangeDone
var success: Boolean = false
val routeBuilder = new RouteBuilder {
  from("direct:input").
    //At each time an exchange reach the end of the route,
    //   the success variable is set to true
    onExchangeDone((exchange, route) => success = true).
    to("mock:output")
}

Moreover, you may prevent a route from being started automatically using the noAutoStartup keyword.

val routeBuilder = new RouteBuilder {
  from("direct:input").routeId("babel").
    //The route is told not starting with the Camel Context
    //   but wait until beeing started especially.
    noAutoStartup.
    to("mock:output")
}