Tutorial: Integrating with Apache Camel

Running the rule over the open source integration framework

Tutorial: Integrating with Apache Camel

Since its creation by the Apache community in 2007, the open source integration framework Apache Camel has become a developer favourite. It is recognised as a key technology to design SOA / Integration projects and address complex enterprise integration use cases. This article, the first part of a series, will reveal how the framework generates, from the Domain Specific Language, routes where exchanges take place, how they are processed according to the patterns chosen, and finally how integration occurs.

Introduction

From a general point of view, designing an integration architecture is not such an obvious task even if the technology and the frameworks you want to use are relatively easy to understand and implement. The difficulties lie in the volume of messages, transformations to apply, synchronicity or asynchronocity of exchanges, processes running sequentially or in parallel, and of course the monitoring of such projects running in multiple JVMs.
In traditional Java applications, we call methods from classes, while objects are passed and/or returned. A service (such as payment or billing, ...) is a collection of classes. Called methods are chained and objects transport information, sometimes enlisted within transactions but always deployed within the same Java Container (Web, JEE, Standalone). Unless we have to call external systems or integrate legacy applications, RDBMS etc, most of the calls are done locally and synchronously.
If a service wants to be reusable, it needs to be packaged, versioned in a library and communicate to the project which will use it. This approach is fine for projects maintained by in-house development teams where costs can be supported by IT departments but it suffers from different issues and requires us, most of the time, to use the same programming language or specific technology to interconnect process (RPC, IIOP, …), container where code is deployed.

Figure 1: SOA
To allow applications to be developed independently, without such constraints, the decoupling must be promoted between the issuer of a request/message from the service in charge to consume it. Such a new architecture paradigm is called Service Oriented Architecture and uses a transport layer to exchange information between systems. One of the immediate benefits of SOA is to promote a contract based approach to define the Services which are exposed between applications and manage them according to ‘governance rules’.
The SOA approach has been able to federate different teams, tackle problems surrounding the development of more complex projects. This IT transformation is required as companies need to be more agile to adapt to the market needs, information must be provided in real-time and business adaptions need to be supported by existing legacy and back systems.
While the SOA philosophy has been widely adopted, the learning curve to master XML, XDS Schemas, Web Services and Business Process Engine, the creation and management of transversal teams, the governance needed to manage services and the skills to acquired have certainly been factors in explaining why SOA still struggle to be adopted by corporate companies. Moreover, IT departments are not only concerned by promoting and managing the Web Services and Registry but also to interconnect, exchange, transform and validate information between disparate systems. This integration aspect of IT work has been completely “underestimated” when SOA principles have been elaborated.

Enterprise Integration Patterns

In 2005, Gregory Hope and Bobby Wolf have published a book called 'Enterprise Integration Patterns' where they not only spend their time to describe complex use cases, but they also define a vocabulary, grammar and design icons to express those complex integration patterns that IT departments have to address. This book has changed the way how development teams (Business/Functional analysts, Data Modelers and Developers) collaborate together to design Integration / SOA projects. The discussions were not only focused any more just on how Services, XML should be structured and business processes imagined, but also on how patterns should be used to solve integration use cases (aggregation, splitting, filtering, content based routing, dynamic routing). This book has leveraged actors towards a more agile programming approach. To support the EIP described in this book and help the developers to solution integration use cases, the Apache Camel Integration Java framework was created 5 years ago.

EIP design icons

Discover Apache Camel

Representing the EIP patterns for aggregated or routing which requires that we ‘express’ them using a language. This language is not a new programming language, moreover a language specific to a Domain, which describes problems adequately to the chosen domain (Integration). Apache Camel is a Java Integration Framework that supports such Domain Specific Language (aka. DSL; for further information, see the Camel documentation) using object-oriented language like Java, Scala, Groovy etc. No parser, compiler or interpreter is required but instead a list of commands, instructions which are sequenced:
instruction1().instruction2()....instructionN(); 
Apache Camel is also defined as a “Mediation and Routing” engine. Let’s think of the global road network: we can transport vehicles of different type and size, with passengers of different origin, color, age, sex, between cities and capitals. According to traffic conditions, the trip can be adapted and alternative roads used. Likewise Apache Camel transports ‘messages’ along Routes.
from("Brussels")
 .to("Paris"); // Transport passengers from Brussels Capital to Paris
Each Camel route starts with the from instruction which is particularly important as it acts as a consumer and plays a specific role depending on whether it will be triggered (‘event drived architecture’) or be able to read data at regular intervals (‘poll architecture’). The consumer is a factory and whenever data are received, then ‘Messages’ will be created and transported by the Apache Camel route.

Of course Apache Camel does not at all transport ‘passengers’ in a route but ‘messages’. These Messages will pass through a collection of steps, aka processors to transform, validate, format, enrich the content of the information received. The framework provides different processors which have been specialized (Bean, Log) to simplify the manipulations that we would like to apply, like the code below:
from("Brussels")
       .bean("Border","validPassport")
       .log("Passport has been controlled")
       .bean("Border","controlTicket")
       .to("log:travel://LogLevel=INFO" + "Ticket has been controlled")
       .to("Paris");
Each processor placed after the ‘from’ pass the information and “form” a chain like the wagons of a train, as below:
from("")
       ...
       .to("log:travel://LogLevel=INFO" + "Ticket has been controlled") //
       .to("file:///outputDirectoryWhereFileWillbeCreated") //
       .to("http://www.google.be?doASearch") // Call External HTTP Server
       .to("jms://queue:outputQueue; // Response received is published in a queue
Nevertheless, certain processors will produce a message that Camel will send to a Server (SMTP, FTP), Application (RDBMS), Broker (JMS), to another camel route (DIRECT, SEDA, VM) and in some cases will wait till they get a response (HTTP, TCP/IP, WS, REST,WebSocket).
One of the key benefits of Camel is that it offers the possibility to take decisions according to the information that it carry in using a Message structure. Such a Message which corresponds to an object Exchange contains the information or object carried in a Body, but also the metadata part of Headers.
The metadata allows you to document the objects transported but also to know from where they are coming from, their origin (File, Ftp, WebService, SMTP, JDBC, JPA, JMS, …) and where they should go. To support the decisions, Camel uses one of the EIP patterns Content Based Router, Filter, Aggregator, Splitter, … with a specific language called Expression Language (Simple, Constant, Xpath, Xquery, SQL, Bean, Header, Body, OGNL, Mvel, EL, ...).

Message structure
The decisions are taken by Predicates that we can compare to If/Then/Else, While/For statements. The routing engine will determine what to do with the “Message(s)” and where they should go.

The choice/when which is used by the Content Based Router will calculate (using the predicate and expression language) if the condition is met. If this is the case, then the exchange is moved to the processors defined in the path of the branch, otherwise they will move into another pipeline. All this is demonstrated below:
  // 
  from("Brussels")
       .bean("Border","validPassport")
       .choice()
            .when()
                .simple(${header.isValid}==true) // Simple language checks if  the status is equal to true
                   .log("Passenger has been controlled")
                   .log("We can now control their ticket")
                   .bean("Border","controlTicket")
                   .to("Paris")
            .otherwise()
                   .log("Your are not authorized to continue your trip");  
For some components used, a response is expected from the receiver called (HTTP, WebService, REST, JMS – Request/Reply, TCP/IP, …) or by the sender issuing the message. In this case, Camel will adapt the pattern used to internally transport the message. This pattern is normally of type InOnly but when a response is required, the pattern to be used will be InOut. To transport the information and to avoid that we mix an incoming Message with outgoing Message, Apache Camel will use two different objects for that purpose, which are in or out. When no response is required which is the case when we use by example a File component, then the out object is always null.


One step further

As the traffic is controlled by operators, Apache Camel provides an environment to manage routes (Start/Stop/Suspend/Resume the traffic in routes). This environment is called a Container or more precisely a CamelContext.

The container is not only the runtime where the routes are deployed and but also acts as complex ecosystem. It can trace the exchanges, how to manage using JMX information exposed by the framework, how to handle the thread’s pools, how the routes can be discovered, how we should shutdown the routes and generate unique identifiers that we use when an exchange is created.
The CamelContext will also register the components that we need to consume or produce that information. According to the scheme name contained in the URI, Apache Camel will scan the classes loaded by the classloader to find the components that it would like use:
"scheme://properties?key1=val2&key2=val3 //
"file:///home/user/integration? "
"timer://myTimer?delay=2s&period=10S"
The Component class is a factory which will create an Endpoint object based on the parameters of the collected from the URI (?key1=value1&key1=value2 ...). This object contains the methods required to create a Producer or Consumer according to the role played by the component.
Typically, the Polling Consumer regularly scans a directory of a file system, has a listener to a JMS reading JMS messages and will create an Exchange that it will propagate to the next processor as shown below:
@Override
protected int poll() throws Exception {
 Exchange exchange = endpoint.createExchange();
// create a message body
 Date now = new Date();
 exchange.getIn().setBody("Hello World! The time is " + now);
 try {
// send message to next processor in the route
 getProcessor().process(exchange);
 return 1; // number of messages polled
 } 
}
At the opposite end, the Producer will wait till it gets a Camel Exchange from a processor, then will manipulate the “Message”, enrich it and change the ‘Metadata’:
public void process(Exchange exchange) throws Exception {
// Add a new property
 exchange.getIn().setHeader("FrequentFlyer","true); 
}
A Camel project typically consists of a Java Main class where we will create a DefaultCamelContext, register the Camel Routes and start the container. As described in the following example, a RouteBuilder class is required, as is its Configure method to call the static methods (= instructions) to design a Camel Route (= collection of Processors). A Route Builder allows to create one to many Camel Routes.
  // 
  public class MainApp {
    public static void main(String[] args) throws Exception {
        // CamelContext = container where we will register the routes
        DefaultCamelContext camelContext = new DefaultCamelContext();
        // RouteBuilder = Where we design the Routes using here Java DSL
        RouteBuilder routeBuilder = new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from(„file:///travelers“)
                  .bean(“Flight”,”TransportPassenger”)
                  .to(„file:///authorizedTravelers“);
            }
        };
            // Add the routes to the container 
        camelContext.addRoutes(routeBuilder);

        // Start the container
        camelContext.start();
        // When work is done we shutdown it
        camelContext.stop(); 
Compared to other integration frameworks, Apache Camel is unique, as it is able to handle Java Objects and is able to automatically convert the object type to the one which is expected by the Processor or Predicate.

During the crea tion of the CamelContext, all the classes in charge of doing type conversion (File to String, Reader, String to DOM, …) will be loaded in an internal registry which is queried during exchange processing by the Camel Processors. These Converter classes come from the different jars, part of the java classpath. While such a process is done by default by Apache Camel, you can also use a specific instruction to tell which specific converter should be applied to the object type received. See below:
// 
 from("file:///travelers") // The File endpoint polls every 1s second files
 // available under "travelers" directory
 .convertBodyTo("String") // We convert Camel Generic File object to a String 
// which is required by Xpath expression language during Content Based Routing
 .choice()
 .when()
 .xpath("/traveler/@controlled" = 'true') // Check if condition is 
// matched using as expression left hand side part and condition, right hand side
 .log("Passenger has been controlled")
 .to("file:///authorizedTravelers")
 .otherwise()
 .log("Your are not authorized to continue your trip"); 

Next Time

During this first part of the Apache Camel article, we have introduced some of the basic functionalities of this Java Integration Framework, implementing the Enterprise Integration Patterns, which uses a Domain Specific Language to design route transporting Messages between systems/applications.
The DSL allows us to define instructions which are read sequentially. When information is received or consumed by a Camel Component, then an Exchange is created and moved to a collection of processors doing transformations on the content of the message linked to a Body object. The framework is able to take decisions (Content Based Router, Filter, …) using one of the Expression Languages supported (Xpath, Simple, SQL, Header, Body, Constant, EL, JavaScript, …) with a Predicate which allows us to define the condition. During the transportation of the Exchange, Camel will automatically convert object from and / or to a specific type using an internal registry containing Converting strategies. A Camel project typically uses a Container called a CamelContext to register the Camel Routes, Endpoints. In the second part of the series, we will cover more advanced features of Camel (Transformation of Complex Data Format, mutltithreading, asynchronous exchange, ...).

0 comments:

Post a Comment