Basic Enterprise Integration Patterns with Camel

Enterprise Integration Patterns (EIP) are a set of design patterns used for integrating different systems in an enterprise environment. Apache Camel is an open-source integration framework that implements these patterns to enable developers to easily build and implement message-oriented middleware applications. In this tutorial, we will cover some basic EIPs and how to implement them using Camel.

Introduction to EIP

Enterprise Integration Patterns provide a set of design patterns for integrating disparate systems in an enterprise environment. These patterns are designed to handle common integration scenarios, such as message routing, message transformation, and message aggregation.

Using EIPs can help you to design more flexible, scalable, and maintainable integration solutions. These patterns also enable you to easily reuse components and reduce the amount of custom code needed to implement your integration solutions.

Router Pattern

camel router

Message routing is the process of directing messages to the appropriate endpoint based on a set of rules or conditions. Camel provides several routing patterns, including:

  • Simple routing
  • Dynamic routing
  • Content-based routing

To implement message routing in Camel, you can use the choice() method to specify different routes based on certain conditions.

Create Content-based routers in Java:

Here is a simple Route which determines the Route flow from the value of the Header “subject” field:

from("direct:messages")
  .choice()
    .when(header("subject").regex(".*(urgent|important).*"))
      .to("direct:highPriority")
    .when(header("subject").regex(".*(spam|junk).*"))
      .to("direct:spamFolder")
    .otherwise()
      .to("direct:inbox");

As you can see, you can perform content-based routeing with a chained call to the choice method and multiple when chained calls.

Each of them defines a predicate and/or expression to check a condition. If it is true, the destination target declared using the to method call will be invoked. If none of the conditions in the when method calls are valid, the destination after the otherwise method will be invoked.

Create Content based Routing in Spring:

Here is the equivalent in Spring:

<route>
  <from uri="direct:messages" />
  <choice>
    <when>
      <simple>${header.subject} regex '.*(urgent|important).*'</simple>
      <to uri="direct:highPriority" />
    </when>
    <when>
      <simple>${header.subject} regex '.*(spam|junk).*'</simple>
      <to uri="direct:spamFolder" />
    </when>
    <otherwise>
      <to uri="direct:inbox" />
    </otherwise>
  </choice>
</route>

Splitter Pattern

camel splitter eip

The splitter pattern can be used to process a message which contains multiple elements, each of which should be processed in a different way. A large text file broken into multiple parts can be processed by a message queue more effectively than a large payload. This processing is implemented by Camel using the splitter pattern from EIP.

Thes splitter pattern works closely with Camel expressions to find and split the contents into multiple parts.

The Java DSL for a splitter pattern looks like this:

from( source-URI )  .split(expression)  .to( target-URI );

The following Splitter Pattern coded in Java shows how to split a set of records contained in a file to a JMS destination:

from("file:/path/to/file")
  .split(body().tokenize("\n"))
    .to("jms:queue:records");

In this example, the from() method defines the input endpoint of the route as a file located at /path/to/file. The split() method is used to split the contents of the file into separate records, using the tokenize("\n") method to split on newline characters. Each record is then sent to a JMS queue named records.

And here is the equivalent coded in Spring:

<route>
  <from uri="file:/path/to/file" />
  <split>
    <tokenize token="\n" />
    <to uri="jms:queue:records" />
  </split>
</route>

By using the Splitter pattern in this way, you can easily break up large data sets into smaller, more manageable chunks and process them independently in your Camel route.

Aggregator

The Aggregator pattern is used to combine multiple messages into a single message. Camel provides the aggregate() method to implement the Aggregator pattern.

from("jms:queue:inputQueue")
    .aggregate(header("orderId"), new MyAggregationStrategy())
    .completionTimeout(3000)
    .to("jms:queue:outputQueue");

In this example, we are aggregating messages based on the orderId header using a custom aggregation strategy. Messages with the same orderId are combined into a single message. The completionTimeout() method specifies the maximum time to wait for all messages to be aggregated before sending the output message to the output queue.

WireTap

camel wire tap eip

In integration projects, systems usually require replication mechanisms. This requirement usually causes some level of code duplication: for example what if you wanted the message headers, exchange properties, or other data in the message exchange? Ideally you could copy the whole incoming exchange and send that to another channel for auditing.

The wire tap pattern implements a replication process, adding new paths to a route, without large changes in the source code. Camel implements the wire tap pattern using a simple but effective mechanism: a method call passing as a parameter to an output destination.

from("file:/path/to/file")
  .wireTap("log:com.example.myapp?level=INFO")
  .to("jms:queue:records");

In this example, the wireTap() method sends a copy of each message to a separate logging endpoint, represented by the log: component. This enables you to log the contents of each message without interfering with the main processing of the route. The logging endpoint logs messages at the INFO level, but you could configure it to log at any other level that suits your needs.

Here is another example:

from("jms:queue:input")
  .wireTap("jpa:com.example.myapp.AuditLog")
  .to("bean:processMessageBean");

In this example, the wireTap() method is used to send a copy of each message to a separate auditing endpoint, represented by the jpa: component. This enables you to keep a record of all message processing activities for auditing or compliance purposes. The auditing endpoint is configured to use the JPA (Java Persistence API) to persist the audit records to a database table named AuditLog.

RecipientList

camel tutorial recipientlist

Some messages will be routed to multiple destinations, depending on a certain condition. A common use case is when a shared service sends a message to a specific customer. To avoid sending confidential data to the wrong customer, the recipient list pattern can be used.

In the following example, according to the header contents, the file will be sent to a number of recipients:

from("jms:xmlOrders")
  .recipientList(header("customers"));

The from() method specifies the input endpoint for the route, which is a JMS queue named xmlOrders.

The recipientList() method is then used to dynamically determine the recipients for each incoming message, based on the value of the customers header. The header("customers") expression retrieves the value of the customers header from the incoming message, which is assumed to be a comma-separated list of endpoint URIs.

For example, if the customers header on an incoming message has the value jms:queue:customerA,jms:queue:customerB, then the recipientList() method will route a copy of the message to both of those endpoints. If the header has a different set of recipients, then the message will be routed to those endpoints instead.

The Recipient List pattern is useful when you need to route messages to a dynamic list of recipients that can change on a per-message basis. This is often the case in multi-tenant systems, where different tenants may have their own separate endpoints for receiving messages. By using the Recipient List pattern, you can maintain a high degree of flexibility and adaptability in your routing logic, without having to hard-code endpoint URIs into your routes.

In the above example, we assume that the header of the JMS message contains a field named customers that we use to construct the destination name.

Conclusion

In this tutorial, we covered some basic Enterprise Integration Patterns and how to implement them using Apache Camel. Using EIPs can help you to design more flexible, scalable, and maintainable integration solutions. Camel provides a comprehensive set of components, DSLs, and APIs for implementing these patterns.

In practice, there are many more EIPs that can be used to solve different integration challenges, such as message filtering, message transformation, and error handling. By mastering these patterns, you can build robust and efficient integration solutions that meet the needs of your organization.

Found the article helpful? if so please follow us on Socials
Twitter Icon       Facebook Icon       LinkedIn Icon       Mastodon Icon