Apache Camel

Apache Camel is an integration framework that supports and implements various integration related patterns. This article goes over a basic example which will give the reader a decent beginning to Camel.

Camel touts itself as "a powerful open source integration framework based on known Enterprise Integration Patterns with powerful Bean Integration." These Enterprise Integration Patterns are documented in the book by Gregor Hohpe and Bobby Woolf. The specific patterns that Camel supports can be found at Camel Patterns.

Note: Camel is used as part of IONA's FUSE ESB product. FUSE also uses ServiceMix, CXF and ActiveMQ.

One of the key features of an ESB is to allow heterogeneous applications to talk to each other in a location independent way. What this means is that App1 does not talk to App2 directly (point-2-point). Instead they communicate via a common medium, the message bus. App1 puts a message on the bus requesting a certain service. The bus looks at the message header and routes it to the appropriate endpoint. The endpoint in this case is App2. Neither App1 nor App2 are directly tied together anymore.

Apache Camel provides the routing and mediation capabilities. Using URI's we define where we want to send the messages to. Example from our sample below - jms:queue:customer. This indicates that we are dropping a message to a jms queue named customer. The bus then routes the message appropriately to the endpoint. In the case of Camel we can set up the routing in either Java DSL (Java api) or via Spring XML. In this blog I use Java DSL (Spring XML is just as easy).

In the example below I implement the following:
  • The client (consumer) needs to call a remote service (provider) to get customer information (including any available rebate information)
  • The client does not know who the provider is.
  • The client only knows that it can drop a message into the bus with the customer id and it will get back a Customer java object.
  • On the ESB itself there are two services in play. The first service returns basic customer information. Another independent service returns the rebates based on the customer type (preferred customer or regular customer).
  • On the ESB we need to wire these two services together to provide the complete customer.
  • Finally my example takes the liberty of hardcoding certain values such as customer id and account id to only focus on Camel.
For this blog I will not post every single code file but describe the key parts. I will attach a zip file containing the complete maven project.

First the simple AccountService implementation...
package com.aver;
import org.springframework.stereotype.Service;
@Service(value = "acctsvc")
public class AccountServiceImpl implements AccountService {

    public Boolean isActive(String acctId) {
        System.out.println("Invoked isActive with " + acctId);
        return acctId.equals("12345") ? true : false;
    }
}
Next the CustomerService implementation...
package com.aver;
import org.springframework.stereotype.Service;
@Service(value = "custsvc")
public class CustomerServiceImpl implements CustomerService {
    public Customer getCustomerInfo(String custid) {
        System.out.println("Invoked getCustomerInfo with " + custid);
        Customer c = new Customer();
        c.setCustId(custid);
        if (custid.equals("cust1")) {
            c.setAcctId("12345");
            c.setName("activecustomer");
            c.setType(Customer.Type.PREFERRED);
        } else {
            c.setAcctId("09876");
            c.setName("inactivecustomer");
            c.setType(Customer.Type.REGULAR);
        }
        return c;
    }
}

As you can see the CustomerService has nothing to do with the rebate information.

Next is the Rebate implementation...
package com.aver;
import org.springframework.stereotype.Service;
@Service(value = "rebatesvc")
public class LoyaltyRebateServiceImpl implements LoyaltyRebateService {

    public Customer rebates(Customer c) {
        if (c.getType() == Customer.Type.PREFERRED) {
            Rebate r = new Rebate("50% off all java 1.2 books");
            c.getRebates().add(r);
        }
        return c;
    }
}


Next we need to tell Camel what our routes are. For this blog I use the Java DSL and implement a RouteBuilder.

ServerRoutes (RouteBuilder)...
package com.aver;
import org.apache.camel.builder.RouteBuilder;
public class ServerRoutes extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("jms:queue:customer").to("custsvc").to("rebatesvc");
        from("jms:queue:account").to("acctsvc");
    }

}

Here we route the messages coming to the customer queue to the customer service and then to the rebate service.

Next we create the camel-server.xml (in META-INF/spring folder)...
The spring xml below sets up the Camel Context, the ActiveMQ JMS and configures Camel to use ActiveMQ.

   
    <!-- use spring annotations -->
    <context:component-scan base-package="com.aver" />

    <!-- camel context -->
    <camel:camelContext id="camel">
        <camel:package>com.aver</camel:package>
    </camel:camelContext>


    <!-- configure ActiveMQ -->
    <broker:broker useJmx="false" persistent="false"
        brokerName="localhost">
        <broker:transportConnectors>
            <broker:transportConnector name="tcp"
                uri="tcp://localhost:61616" />
        </broker:transportConnectors>
    </broker:broker>

    <!-- configure Camel to use the ActiveMQ broker -->
    <bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent">
        <property name="brokerURL" value="tcp://localhost:61616" />
    </bean>  


The Client...
com.aver.client.Client contains following code that mimics a caller. There also exists a camel-client.xml (please see the zip file).

// simple
Boolean response = (Boolean) camelTemplate.sendBody(
         "jms:queue:account", ExchangePattern.InOut, "12345");
System.out.println("Account Active Flag: " + response);

// returns java object
Customer c = (Customer) camelTemplate.sendBody("jms:queue:customer",
         ExchangePattern.InOut, "cust1");
System.out.println("Customer : " + c);


The client code in this case has no idea about the service classes or actual endpoint. It only knows how to address the service in the bus using URI's such as 'jms:queue:account". Camel then routes this to the actual endpoint.

Running this....
Run maven command
  • mvn clean compile exec:java  -Dexec.mainClass=org.apache.camel.spring.Main (this will bring up the Camel runtime so you can test).
  • From your eclipse project run Client.java as a regular java application (after you have started Camel server).
Click here to download the zip file containing the code.




 

 del.icio.us  Stumbleupon  Technorati  Digg 

 

What did you think of this article?




Trackbacks
  • Trackbacks are closed for this entry.
Comments

Leave a comment

Submitted comments will be subject to moderation before being displayed.

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.