Authors Top

If you have a few years of experience in the Java ecosystem, and you’d like to share that with the community, have a look at our Contribution Guidelines.

Spring Top – Temp

Get started with Spring 5 and Spring Boot 2, through the reference Learn Spring course:

>> LEARN SPRING
Lightrun – Third Party Code
announcement - icon

Flakiness in REST requests is a common issue. A request can get a 200 OK in one scenario and a 409 next time. Sometimes a request can even succeed and fail intermittently on the same exact request. In short, working over HTTP can be a bit of a mess without solid tooling.

Also, while it’s easy enough to debug these issues locally when developing the application, we’re talking about production here - we can’t afford the downtime while you’re stepping in and out of code. Uptime is kind of the whole point.

With Lightrun, you can get the same level of access you get with a local debugger or profiler - no downtime required. You can add logs, metrics, and snapshots (think breakpoints, but without stopping the running service), in a safe and read-only manner - without redeploying, restarting, or even stopping the running service. Performance and security are maintained throughout the process.

Learn how to debug a live REST API (built with Spring, of course), using Lightrun, in this 5-minute tutorial:

>> Debugging REST Requests in Spring-Based applications using the Lightrun Platform

1. Overview

Apache Camel is a powerful open-source integration framework implementing several of the known Enterprise Integration Patterns.

Typically when working with message routing using Camel, we'll want a way to process messages differently based on their content. For this, Camel provides a powerful feature called the Content Based Router from the collection of EIP patterns.

In this tutorial, we'll take a look at several ways we can route messages based on some condition.

2. Dependencies

All we'll need to get started is the camel-spring-boot-starter added to our pom.xml:

<dependency>
    <groupId>org.apache.camel.springboot</groupId>
     <artifactId>camel-spring-boot-starter</artifactId>
     <version>3.18.1</version>
</dependency>

Then, we'll need to add the camel-test-spring-junit5 dependency to our pom.xml:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-test-spring-junit5</artifactId>
    <version>3.18.1</version>
</dependency>

As the name suggests, this dependency is specifically for our unit tests.

3. Defining a Simple Camel Spring Boot Application

Throughout this tutorial, the focus of our examples will be a simple Apache Camel Spring Boot application.

So let's start by defining our application entry point:

@SpringBootApplication
public class ConditionalRoutingSpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConditionalRoutingSpringApplication.class, args);
    }
}

As we can see, this is a standard Spring Boot application.

4. Conditional Routing

To quickly recap, a route in Apache Camel is a fundamental building block, normally formed of a sequence of steps, executed in order by Camel, that consumes and processes a message.

For example, a route will typically receive a message, using a consumer perhaps from a file on disk or a message queue. Then, Camel executes the rest of the steps in the route, which either processes the message somehow or sends it to other endpoints.

Without a doubt, we'll want a way to conditionally route a message based on some fact. For this, Camel provides the choice and when constructs. We can think of this as the equivalent of the if-else statement in Java.

With that in mind, let's go ahead and create our first route with some conditional logic.

5. Creating a Route

In this example, we'll define an elementary route with some conditional logic based on the contents of the message body received:

@Component
public class ConditionalBodyRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("direct:start-conditional")
          .routeId("conditional-body-route")
          .choice()
            .when(body().contains("Baeldung"))
              .setBody(simple("Goodbye, Baeldung!"))
              .to("mock:result-body")
            .otherwise()
              .to("mock:result-body")
          .end();
    }
}

As we can see in our trivial example, we configure our route to consume messages from a direct endpoint called start-conditional.

Now let's walk through the key parts of our route:

  • First, we begin the route using the choice() method – this tells Camel that the following lines will contain some conditions to evaluate
  • Next, the when() method indicates a new condition to be evaluated – in this example, we simply check to see if the message body contains the string Baeldung. We can add as many when conditions as we need
  • To conclude our route, we use the otherwise() method to define what to do when none of the previous when conditions are met.
  • Finally, the route terminates using the end() method, which closes the choice block.

To summarise, when we run our route, if our message body contains the string Baeldung, we'll set the message body to Goodbye, Baeldung! and send the result to a mock endpoint called result-body.

Alternatively, we'll just route the original message to our mock endpoint.

6. Testing the Route

With the last section in mind, let's go ahead and write a unit test to explore how our route behaves:

@SpringBootTest
@CamelSpringBootTest
class ConditionalBodyRouterUnitTest {

    @Autowired
    private ProducerTemplate template;

    @EndpointInject("mock:result-body")
    private MockEndpoint mock;

    @Test
    void whenSendBodyWithBaeldung_thenGoodbyeMessageReceivedSuccessfully() throws InterruptedException {
        mock.expectedBodiesReceived("Goodbye, Baeldung!");

        template.sendBody("direct:start-conditional", "Hello Baeldung Readers!");

        mock.assertIsSatisfied();
    }
}

As we can see, our test consists of three simple steps:

  • First, let's set an expectation that our mock endpoint will receive the given message body
  • Then we'll send a message to our direct:start-conditional endpoint using our template. Note, we'll ensure our message body contains the string Baeldung
  • To conclude our test, we use the assertIsSatisfied method to validate that our initial expectation on our mock endpoint has been satisfied

This test confirms that our conditional routing is working correctly. Awesome!

Be sure to see our previous tutorial to learn more about how to write reliable, self-contained unit tests for our Camel routes.

7. Building Other Conditional Predicates

So far, we've explored one option on how to build our when predicate – inspecting the message body of our exchange. However, there are several other options available to us.

For example, we can also control our conditions by inspecting the value of a given message header:

@Component
public class ConditionalHeaderRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("direct:start-conditional-header")
            .routeId("conditional-header-route")
            .choice()
              .when(header("fruit").isEqualTo("Apple"))
                .setHeader("favourite", simple("Apples"))
                .to("mock:result")
              .otherwise()
                .setHeader("favourite", header("fruit"))
                .to("mock:result")
            .end();
    }
}

This time around, we've modified the when method to look at the value of a header called fruit. It is also perfectly possible to use the Simple language Camel provides in our when conditions.

8. Working With Java Beans 

Moreover, we can also use the Camel Bean language when we want to use the result of a Java method call in our predicates.

First, we need to create a Java bean containing a method that returns a boolean:

public class FruitBean {

    public static boolean isApple(Exchange exchange) {
        return "Apple".equals(exchange.getIn().getHeader("fruit"));
    }
}

Here we also optionally add the Exchange as an argument so Camel will pass the Exchange to our method automatically.

Then we can go ahead and use our FruitBean from our when block:

@Component
public class ConditionalBeanRouter extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        
        from("direct:start-conditional-bean")
            .routeId("conditional-bean-route")
            .choice()
              .when(method(FruitBean.class, "isApple"))
                .setHeader("favourite", simple("Apples"))
                .to("mock:result")
              .otherwise()
                .setHeader("favourite", header("fruit"))
                 .to("mock:result")
              .endChoice()
            .end();
    }
}

9. Conclusion

In this article, we learned how we could route messages based on some kind of condition in our routes. First, we created a simple Camel application with one route to inspect the message body.

Then we learned about several other techniques for building predicates in our routes using message headers and Java beans.

As always, the full source code of the article is available over on GitHub.

Spring bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> THE COURSE
Generic footer banner
guest
0 Comments
Inline Feedbacks
View all comments