Route Configuration

Camel 3.12 introduces route configuration which is used for separating configurations from the routes. This can be used in situations such as configuring different error handling across a set of routes. In previous versions of Camel this was more cumbersome to do, as you would either have to copy the same configuration to a set of routes or rely on global error handling configuration.

Now you can configure a number of route configurations, and then specify on each route which configuration to use (you can use match by ids, wildcards, and regular expression).

The route configuration is supported by all DSL’s, so useable by: Java, XML, Groovy, Kotlin and so forth.

In the route configuration you can setup common strategies for:

Route Configuration Builder in Java DSL

With Java DSL you can use RouteConfigurationBuilder to specify the configuration as shown below. The builder is similar to RouteBuilder so its use is familiar.

public class MyJavaErrorHandler extends RouteConfigurationBuilder {

    @Override
    public void configuration() throws Exception {
        routeConfiguration("javaError")
            .onException(Exception.class).handled(true)
            .log("Java WARN: ${exception.message}");
    }
}
The RouteConfigurationBuilder uses configuration as the method where the configuration is coded. This is on purpose not to use the configure method which the regular Java DSL RouteBuilder uses for coding Camel routes.

In the example above, then there is only one route configuration which has been assigned the ID javaError. This ID allows us to refer to this configuration later when you want to assign which routes are using the configuration.

This configuration is a basic configuration that just catches and handles all exceptions and logs a WARN message.

Assigning route configurations to routes

To use this configuration in your routes, then you can assign it with routeConfigurationId as shown:

public class MyJavaRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("timer:java?period=2s")
            // refer to the route configuration by the id to use for this route
            .routeConfigurationId("javaError")
            .setBody(method(MyJavaRouteBuilder.class, "randomNumber"))
            .log("Random number ${body}")
            .filter(simple("${body} < 30"))
                .throwException(new IllegalArgumentException("The number is too low"));
    }

    public static int randomNumber() {
        return new Random().nextInt(100);
    }
}

In the routeConfigurationId the configuration to use is specified by the ID, eg javaError.

Multiple configurations can be assigned (separated by comma), such as:

.routeConfigurationId("javaError,myAudit")

The route configurations supports matching by:

  • exact ID name. This is the sample we have seen above.

  • wildcard

  • regular expression.

Wildcards are text ending with a *; they are matched when the configuration ID starts with the specified text followed by any characters. For instance, you can do:

.routeConfigurationId("java*,myAudit")

Here we use wildcard in java* which means any configuration whose ID starts with java is a match.

Match by regular expression is just like match by wildcard but using regex instead.

.routeConfigurationId(".*error.*")

Here we want to match any configuration whose ID contains error.

Adding route configurations to CamelContext

Because a RouteConfigurationBuilder is also a RouteBuilder then you add route configurations the same way for RouteBuilder such as using the API on CamelContext

CamelContext context = ...
// add the route configuration
context.addRoutes(new MyJavaErrorHandler());
// add the regular route
context.addRoutes(new MyJavaRouteBuilder());

If you use Spring Boot, then your Camel routes and route configurations can be auto-discovered by the spring boot component scanning. This requires adding the @Component annotation to the class.

Route configuration with Endpoint DSL

The Endpoint DSL can also be used for route configurations. This requires adding camel-endpointdsl to the classpath, and then using org.apache.camel.builder.endpoint.EndpointRouteConfigurationBuilder, which offers the type safe DSL for Camel endpoints.

Default route configurations

Route configurations are either given an explicit unique ID, or the configuration is nameless. A nameless configuration is used as default/fallback configuration, for routes which have NOT been explicitly assigned route configurations.

Suppose you have one nameless configuration and another named retryError:

public class MyJavaErrorHandler extends RouteConfigurationBuilder {

    @Override
    public void configuration() throws Exception {
        routeConfiguration()
            .onException(Exception.class).handled(true)
            .log("WARN: ${exception.message}");

        routeConfiguration("retryError")
            .onException(Exception.class).maximumRedeliveries(5);
    }
}

And the following two routes:

   from("file:cheese").routeId("cheese")
        .to("kafka:cheese");

   from("file:beer").routeId("beer")
        .routeConfigurationId("retryError")
        .to("jms:beer");

In the example above, the cheese route has no route configurations assigned, so the route will use the default configuration, which in case of an exception will log a warning.

The beer route on the other hand has the route configuration retryError assigned, and this configuration will in case of an exception retry up to 5 times and then if still an error then fail and rollback.

If you add more routes, then those routes can also be assigned the retryError configuration if they should also retry in case of error.

Route Configuration with Error Handler

Each route configuration can also have a specific error handler configured, as shown below:

public class MyJavaErrorHandler extends RouteConfigurationBuilder {

    @Override
    public void configuration() throws Exception {
        routeConfiguration()
            .errorHandler(deadLetterChannel("mock:dead"));

        routeConfiguration("retryError")
            .onException(Exception.class).maximumRedeliveries(5);
    }
}

In the example above, the nameless configuration has an error handler with a dead letter queue. And the route configuration with id retryError does not, and instead it will attempt to retry the failing message up till 5 times before giving up (exhausted). Because this route configuration does not have any error handler assigned, then Camel will use the default error handler.

Routes that have a local error handler defined, will always use this error handler, instead of the error handler from route configurations. A route can only have 1 error handler.

Route Configuration in XML

When using XML DSL then you can code your route configurations in XML files as shown below:

<routeConfiguration id="xmlError">
    <onException>
        <exception>java.lang.Exception</exception>
        <handled><constant>true</constant></handled>
        <log message="XML WARN: ${exception.message}"/>
    </onException>
</routeConfiguration>

And in the XML routes you can assign which configurations to use:

<route routeConfigurationId="xmlError">
    <from uri="timer:xml?period=5s"/>
    <log message="I am XML"/>
    <throwException exceptionType="java.lang.Exception" message="Some kind of XML error"/>
</route>

In this example the route is assigned the xmlError route configuration by the exact ID.

Route Configuration in YAML

When using YAML DSL then you can code your route configurations in YAML files as shown below:

- route-configuration:
    id: "yamlError"
    on-exception:
    - on-exception:
        handled:
          constant: "true"
        exception:
          - "java.lang.Exception"
        steps:
          - log:
              message: "YAML WARN ${exception.message}"

And in the YAML routes you can assign which configurations to use:

- route:
    # refer to the route configuration by the id to use for this route
    route-configuration-id: "yamlError"
    from:
      uri: "timer:yaml?period=3s"
      steps:
        - set-body:
            simple: "Timer fired ${header.CamelTimerCounter} times"
        - to:
            uri: "log:yaml"
            parameters:
              show-body-type: false
              show-exchange-pattern: false
        - throw-exception:
            exception-type: "java.lang.IllegalArgumentException"
            message: "Error from yaml"

In this example the route is assigned the yamlError route configuration by the exact ID.

Mixing DSLs

Routes and route configuration are not required to use the same language. For example, you can code route configurations in Java, and then use XML DSL for the routes, and they would work together.

Route Configuration in classic Spring/Blueprint XML

When using XML DSL with camel-spring-xml or camel-blueprint then you can code your route configurations in <routeConfigurationContext> snippets in separate XML files as shown below:

<routeConfigurationContext id="myConf" xmlns="http://camel.apache.org/schema/spring">
    <routeConfiguration id="xmlError">
        <onException>
            <exception>java.lang.Exception</exception>
            <handled><constant>true</constant></handled>
            <log message="XML WARN: ${exception.message}"/>
        </onException>
    </routeConfiguration>
</routeConfigurationContext>

Then from <camelContext> you can refer to these XML snippets by their ids:

<camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring">

    <!-- refer to the ID on the context that has the route configurations (see above) -->
    <routeConfigurationContextRef ref="myConf"/>

    <!-- routes can then assign which configuration to use -->
    <route routeConfigurationId="xmlError">
        <from uri="timer:xml?period=5s"/>
        <log message="I am XML"/>
        <throwException exceptionType="java.lang.Exception" message="Some kind of XML error"/>
    </route>
</camelContext>

In this example the route is assigned the xmlError route configuration by the exact ID.

Packaging route configurations in reusable JARs

You can package common route configurations into JARs which you can then use together with your Camel applications, by adding the JARs as dependencies to the classpath (such as in Maven pom.xml file).

This allows for example to use a common practice among your Camel applications.

Logging Summary

If you set startup-summary-level=verbose then Camel will log for each route which route configurations they have been assigned.

This option can be configured via Java API and also in application.properties for Camel on Spring Boot, Quarkus, and Camel standalone via camel-main

camelContext.setStartupSummaryLevel(StartupSummaryLevel.Verbose);

And with Spring Boot:

camel.spring-boot.startup-summary-level = verbose

And in Camel Main / Quarkus:

camel.main.startup-summary-level = verbose

Route Precondition

The route configurations can be included or not according to the result of a test expressed in simple language that is evaluated only once during the initialization phase.

In the next example, the route configuration is only included if the parameter activate has been set to true.

routeConfiguration().precondition("{{activate}}")
    .onException(IllegalArgumentException.class)
    .handled(true)
    .log("WARN: ${exception.message}");

And the same example using XML DSL:

<routeConfiguration precondition="{{activate}}">
    <onException>
        <exception>java.lang.IllegalArgumentException</exception>
        <handled>
            <constant>true</constant>
        </handled>
        <log message="XML WARN: ${exception.message}"/>
    </onException>
</routeConfiguration>

And in YAML DSL:

- route-configuration:
    precondition: "{{activate}}"
    on-exception:
    - on-exception:
        exception:
          - "java.lang.IllegalArgumentException"
        handled:
          constant: "true"
        steps:
          - log:
              message: "YAML WARN ${exception.message}"