ibsen3_00

By Claus Ibsen and Jonathan Anstey

In this article, excerpted from Camel in Action, we will take a look at an EIP that supports dynamic routing, the Routing Slip pattern.

There are times when you need to route messages dynamically. For example, you may have an architecture that requires incoming messages to undergo a sequence of processing steps and business rule validations. Because the steps and validations vary widely, you can implement each step as a separate filter. The filter acts as a dynamic model to apply the business rule and validations.

This architecture could be implemented using the Pipes and Filters EIP together with the Filter EIP. But as often happens with EIPs, there’s a better way, known as the Routing Slip EIP. The Routing Slip acts as a dynamic router that dictates the next step a message should undergo. Figure 1 shows this principle.


ibsen3_01

Figure 1 The incoming message has a slip attached that specifies the sequence of the processing steps. The Routing Slip EIP reads the slip and routes the message to the next endpoint in the list.


The Camel Routing Slip EIP requires a preexisting header or Expression as the attached slip. Either way, the initial slip must be prepared before the message is sent to the Routing Slip  EIP.


Using the Routing Slip EIP

We’ll start with a simple example that shows how to use the Routing Slip EIP to perform the sequence outlined in figure 1.

In the Java DSL, the route is as simple as this:

from("direct:start").routingSlip(header("mySlip"));    

It’s also easy in Spring XML:

<route>
      <from uri="direct:start"/>
      <routingSlip>
         <header>mySlip</header>
      </routingSlip>
 </route>

This example assumes the incoming message contains the slip in the header with the key "mySlip“. The following test method shows how you should fill out the key:

public void testRoutingSlip() throws Exception {
     getMockEndpoint("mock:a").expectedMessageCount(1);
     getMockEndpoint("mock:b").expectedMessageCount(0);
     getMockEndpoint("mock:c").expectedMessageCount(1);
     template.sendBodyAndHeader("direct:start", "Hello World",
                                "mySlip", "mock:a,mock:c");
     assertMockEndpointsSatisfied();
 }

As you can see, the value of the key is the endpoint URIs separated by commas. The comma is the default delimiter, but the routing slip supports using custom delimiters. For example, to use a semicolon, you could do this:

from("direct:start").routingSlip(header("mySlip"), ";");       

And in Spring XML, you’d do this:

<routingSlip uriDelimiter=";">
     <header>mySlip</header>
 </routingSlip>

This example expects a preexisting header containing the routing slip. But what if the message doesn’t contain such a header? In those situations, you have to compute the header in any way you like. In the next example, we look at how to compute the header using a bean.


Using a bean to compute the routing slip header

To keep things simple, the logic to compute a header that contains two or three steps has been kept in a single method, as follows:

public class ComputeSlip {
     public String compute(String body) {
         String answer = "mock:a";
         if (body.contains("Cool")) {
             answer += ",mock:b";
         }
         answer += ",mock:c";
         return answer;
     }
 }

All you have to do now is leverage this bean to compute the header to be used as the routing slip.

In the Java DSL, you can use the method call expression to invoke the bean and set the header:

from("direct:start")
     .setHeader("mySlip").method(ComputeSlip.class)
     .routingSlip(header("mySlip"));
 In Spring XML, you can do it as follows:
 <route>
     <from uri="direct:start"/>
     <setHeader headerName="mySlip">
         <method beanType="camelinaction.ComputeSlip"/>
     </setHeader>
     <routingSlip>
         <header>mySlip</header>
     </routingSlip>
 </route>

In this example, you use a method call expression to set a header that is then used by the routing slip. But you might want to skip the step of setting the header and instead use the expression directly.


Using an Expression as the routing slip

Instead of using a header expression, you can use any other Expression to generate the routing slip. For example we could use a method call expression like we covered in the previous section. Here’s how you’d do so with the Java DSL:

from("direct:start")
     .routingSlip(method(ComputeSlip.class));

The equivalent Spring XML is as follows:

<route>
     <from uri="direct:start"/>
     <routingSlip>
         <method beanType="camelinaction.ComputeSlip"/>
     </routingSlip>
 </route>

Another way of using the Routing Slip EIP in Camel is to use beans and annotations.


Using @RoutingSlip annotation

The @RoutingSlip annotation allows you to turn a regular bean method into the Routing Slip EIP. Let’s go over an example. Suppose you have the following SlipBean:

public class SlipBean {
     @RoutingSlip
     public String slip(String body) {
         String answer = "mock:a";
         if (body.contains("Cool")) {
             answer += ",mock:b";
         }
 }

As you can see, all this does is annotate the slip method with @RoutingSlip. When Camel invokes the slip method, it detects the @RoutingSlip annotation and continues routing according to the Routing Slip EIP.

WARNING

When using @RoutingSlip it’s important to not use routingSlip in the DSL at the same time. By doing this, Camel will double up using the Routing Slip EIP, which is not the intention. Instead, do as shown in the example below.

Notice that there’s no mention of the routing slip in the DSL. The route is just invoking a bean.

from("direct:start").bean(SlipBean.class);

Here it is in the XML DSL:

<bean id="myBean" class="camelinaction.SlipBean"/>
 <route>
     <from uri="direct:start"/>
     <bean ref="myBean"/>
 </route>

Why might you want to use this? Well, by using @RoutingSlip on a bean, it becomes more flexible in the sense that the bean is accessible using an endpoint URI. Any Camel client or route could easily send a message to the bean and have it continued being routed as a routing slip.

For example, using a ProducerTemplate you could send a message to the bean:

ProducerTemplate template = ...
 template.sendBody("bean:myBean", "Camel rocks");

That "Camel rocks" message would then be routed as a routing slip with the slip generated as the result of the myBean method invocation. You’ve now seen the Routing Slip EIP in action!