Book Image

Apache Camel Developer's Cookbook

Book Image

Apache Camel Developer's Cookbook

Overview of this book

Apache Camel is a de-facto standard for developing integrations in Java, and is based on well-understood Enterprise Integration Patterns. It is used within many commercial and open source integration products. Camel makes common integration tasks easy while still providing the developer with the means to customize the framework when the situation demands it. Tasks such as protocol mediation, message routing and transformation, and auditing are common usages of Camel. Apache Camel Developer's Cookbook provides hundreds of best practice tips for using Apache Camel in a format that helps you build your Camel projects. Each tip or recipe provides you with the most important steps to perform along with a summary of how it works, with references to further reading if you need more information. This book is intended to be a reliable information source that is quicker to use than an Internet search. Apache Camel Developer's Cookbook is a quick lookup guide that can also be read from cover to cover if you want to get a sense of the full power of Apache Camel. This book provides coverage of the full lifecycle of creating Apache Camel-based integration projects, including the structure of your Camel code and using the most common Enterprise Integration patterns. Patterns like Split/Join and Aggregation are covered in depth in this book. Throughout this book, you will be learning steps to transform your data. You will also learn how to perform unit and integration testing of your code using Camel's extensive testing framework, and also strategies for debugging and monitoring your code. Advanced topics like Error Handling, Parallel Processing, Transactions, and Security will also be covered in this book. This book provides you with practical tips on using Apache Camel based on years of hands-on experience from hundreds of integration projects.
Table of Contents (20 chapters)
Apache Camel Developer's Cookbook
Credits
About the Authors
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Index

Asynchronously connecting routes


It is not uncommon to have a portion of route take a long time to process. Rather than tying up a thread that might otherwise be servicing requests from a consumer endpoint, it may be preferable to split out the time consuming step into a separate route, and let that stage of processing be handled by a dedicated pool of threads.

This recipe will show you how to call a route from another, such that the calling route does not block waiting for the response from the called route.

Getting ready

Split out the long running steps into their own shared routes, and assign them with a seda: endpoint with a name that is unique to the Camel context.

The Java code for this recipe is located in the org.camelcookbook.structuringroutes.seda package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with seda.

How to do it...

Create a shared route using the seda: endpoint, and then call it from other routes using the same named seda: endpoint.

  1. Create a route consuming (from) using a seda: endpoint. Optionally, define the number of threads that will consume from this endpoint using the concurrentConsumers attribute.

    In the XML DSL, this is written as:

    <route>
      <from
        uri="seda:longRunningPhase?concurrentConsumers=15"/>
      <process ref="longRunningProcessor"/>
      <to uri="..."/>
    </route>

    In the Java DSL, the same thing appears as:

    from("seda:longRunningPhase?concurrentConsumers=15")
      .process(new LongRunningProcessor())
      .to(...); // remainder of route
  2. In the calling route, pass the current exchange to the shared route by invoking the seda: endpoint by name.

    In the XML DSL, this is done as follows:

    <route>
      <from uri="timer:ping?period=200"/>
      <to uri="seda:longRunningPhase"/>
    </route>

    In the Java DSL, this same thing is written as:

    from("timer:ping?period=200")
      .to("seda:longRunningPhase");

How it works...

In the preceding example, a timer: endpoint is used to trigger messages on a regular basis, every 200 ms. The Timer Component uses one thread per timer name (ping). An event can only be raised 200 ms later if the thread is not processing the previous exchange.

As part of our integration, we want to trigger events regularly, and yet have a long-running processor as part of the route. Camel allows us to deal with this scenario by splitting the long-running part into a shared route, and linking the two routes with a seda: endpoint.

Note

SEDA is an acronym that stands for Staged Event-Driven Architecture. It is designed as a mechanism to regulate the flow between different phases of message processing. The idea is to smooth out the frequency of message output from an overall process so that it matches the input.

In practical terms, it allows an endpoint's consumer threads to offload the work of long-running operations into the background, thereby freeing them up to consume messages from the transport.

When an exchange is passed to a seda: endpoint, it is placed into a BlockingQueue. The list exists within the Camel context, which means that only those routes that are within the same context can be joined by this type of endpoint. The queue is unbounded by default, although that can be changed by setting the size attribute on the URI of the consumer.

By default, a single thread assigned to the endpoint reads exchanges off the list and processes them through the route. As seen in the preceding example, it is possible to increase the number of concurrentConsumers to ensure that exchanges are getting processed from that list in a timely fashion.

Assuming our slow processor takes 3,000 ms to complete, we would need to use a number of threads equal to the processing time/the timer frequency to ensure that the triggered events are processed in a timely fashion. Therefore, plugging in the numbers, 3,000 ms / 200 ms, we arrive at 15 threads.

There's more...

It is possible to define multiple routes that consume from the same logical name, unlike a direct: endpoint. To enable this, both routes should set multipleConsumers=true on the seda: URI:

from("seda:timerEvents?multipleConsumers=true")
  // define one set of routing logic here

from("seda:timerEvents?multipleConsumers=true")
  // another here

The effect will be that each route gets its own copy of the exchange, making it a sort of simple in-memory publish-subscribe system. It is often much cleaner to handle this type of requirement using the Multicast pattern. See the Multicast – routing the same message to many endpoints recipe in Chapter 2, Message Routing, for more details.

The SEDA pattern is best suited to processing the InOnly messages, where one route finishes processing and hands off to another to deal with the next phase. It is possible to ask for a response from a seda: endpoint by calling it when the message exchange pattern is InOut.

In this instance, the endpoint will act much like a synchronous direct: endpoint, only with a timeout if the task runs for longer than expected. The default timeout is 30 seconds, but this can be overridden in the producer URI using the timeout attribute.

Note

It is important to note that when using seda:, the shared route does not take part in any transactions started by the top-level route, as transactions are bound to a thread and the seda: route is using its own thread. If the system unexpectedly halts for whatever reason (for example, a power outage), any messages that are being processed within that route will be lost.

If you would like to get the benefits of a SEDA, but have the messages persisted to disk instead of in-memory, and be processed within a transaction, use the JMS or ActiveMQ endpoints with a message broker instead. This also gives you the ability to share the work across Camel contexts and JVMs.

See also

To trigger a background job to run based on the current exchange while your main route continues processing, refer to the Wire Tap EIP. See Wire Tap sending a copy of the message elsewhere recipe in Chapter 2, Message Routing, for more details.