Getting Started with Camel, Java, and Spring

At home and at work, I find that the things that I have to do over and over are the most painful. At home, it’s the dishes; at work, it reading data out of a file or doing Hibernate mappings. No matter what I do, I can’t seem to escape them, even while, for the most part, the inventors of the tools I use have done a pretty good job of making the work less of a chore.

At a recent job, I was asked to do some comma-separated value (CSV) file processing. A Microsoft Access power user was going to be running some queries, and our business users wanted to alter the behavior of our system based off the records that he found. Multiple files were going to be produced each day, and dropped off in a directory where I could pick them up.

A simplistic view of this process would be:

  1. User puts file in directory.
  2. System reads file from directory.
  3. System breaks each line into a separate record.
  4. System processes each record.
  5. System archives the file.

Not knowing better, when I got the first request, I opted to code myself out of the problem. When the second similar request came along, I copied the code, tweaked what needed to be changed, and was on my merry way. When the third request rolled around it was time for some cleanup, so I extracted out the common parts, removing the duplication, and had myself a little library I could use when the next request rolled along.

But, here’s the thing – I knew someone else had done this before.

Discovering Camel

About six months later, I found out why I had this nagging suspicion when I started reading about Camel. If you haven’t heard of Camel before, it’s a tool designed to express Enterprise Integration Patterns (EIPs) in code. An EIP is a common way of describing a problem that has been run into by developers over time. Hopefully an example will help clear it up a little more.

In our problem up above, the following two EIPs are in play.

  • File Transfer: Two systems have data that needs to be shared, and they opt to share that info with a file.
  • Message Transformation: EIPs work by exchanging messages between systems, but what if both systems don’t understand that message? The answer is that it needs to be transformed from its original format to one that the destination can understand.

The actual EIP descriptions are only slightly more complicated than what I wrote here, but you can see how our problem fits in. Now that you know what an Enterprise Integration Pattern is, we’ll focus on introducing you to Camel by working through the File Transfer portion of our problem.

The Camel API

The Camel Context

You can’t get into Camel without running face first into the Camel Context. The context is the home for all of Camel’s routes, which are the processes you define. Since this article is on Camel, Java, and Spring, we’ll build our examples out in an XML Application Context, but to do that we’ll need a few dependencies. Substitute the versions as you like…

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
 
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>${camel.version}</version>
        </dependency>
 
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring</artifactId>
            <version>${camel.version}</version>
        </dependency>
</dependencies>

Now that our dependencies are out of the way, here’s an example of how that Camel context looks like when it’s managed by Spring.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
 
    <camel:camelContext>
        <!--routes go here-->
    </camel:camelContext>
 
</beans>

A full overview of the Camel Context is beyond the scope of this post, but like I described above, the most important thing the Camel Context does is hold onto your routes. It also contains various configuration settings that can be applied to all of the routes in your context, including error handling (more on that in later posts).

The Camel Route

Camel’s route is where you specify the details of the process you are implementing. There are several ways to define the route, including Java and XML. For our example, we’re going to start with a simple route in Java.

@Component
public class FileMover extends SpringRouteBuilder {
    private @Value("${camel.ride.app.output.directory}") String outputDirectory;
    private @Value("${camel.ride.app.input.directory}") String inputDirectory;
 
    @Override
    public void configure() throws Exception {
        from("file:" + inputDirectory).to("file:" + outputDirectory);
    }
 
}

The key points to look at in the class are the extension of the SpringRouteBuilder class, which grants you access to the “from” method, which takes an Endpoint (or the String representation of one) and returns the RouteDefinition for the route we are defining. We then invoke the “to” method on that definition and pass in another Endpoint as our destination. Camel is able to process to and from a wide variety of EndPoints, including JMS message topics and queues, FTP, and, as in our case, directories.

Registering The Route

This route still needs to be configured into your Camel Context, which requires a small change to the XML we defined earlier, and since we used annotations and a Spring property file, we’ll need to set up a Spring component scan and property configurer.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
 
    <context:component-scan base-package="org.burgers.camel.ride.app"/>
    <context:property-placeholder location="classpath*:camel-ride-app-env.properties"/>    
 
    <camel:camelContext id="camel">
        <camel:routeBuilder ref="fileMover"/>
    </camel:camelContext>
 
</beans>

So what is going on here? Spring and Camel worked together to create an instance of the SpringCamelContext class, and registered our simple route that moved files from whatever values you have defined for the property “camel.ride.app.input.directory” to whatever you have defined for “camel.ride.app.output.directory”.

A half dozen lines of code, another half dozen lines of XML, and you’ve written your first Camel route with Java and Spring.

What’s next?

In my next post we’ll discuss how to access the contents of the file we’re processing, marshal that data into multiple objects, and do some processing on those objects. In the meantime, if you’re interested in the full source for this project and other examples, check out the simple route branch of the camel ride repository on GitHub. More examples coming soon…

One comment

Comments are closed.