Tom Donohue Tom Donohue

How to use Camel with Spring Java configuration

Years ago, XML was the darling of development. It was the future! You would soon be writing everything in XML. Web services, data transfer, texts with your hot date… these would all be delivered in XML. 🤖

Spring rode this wave of support, and in classic versions of Spring Framework (3.0 FTW!), XML was the way to configure your apps. You would write your entire application in a single beans.xml, or whatever. Result.

But in recent years, the favour has tipped the other way.

XML isn’t cool anymore. Code is.

Recent comments from the Spring team don’t beat around the bush. They’re not huge fans of XML any more:

If you must - screengrab from Spring documentation

Converting a Spring XML application to Java configuration? It’s certainly not a 5-minute job.

That’s especially the case with Camel, where it has two different languages (DSLs). One for XML, one for Java. So you’re looking at a considerable rewrite if you want to completely go from XML to Java.

Having said that, there is a process for doing this. You just need to break it down into steps. In this article I’ll go through the main areas that you might need to change between XML and Java configuration, along with some examples.

XML vs Java

The syntax between Camel’s Java DSL and the XML is clearly very different.

But you should find that all the functionality you configured in XML is also available in Java configuration.

The main things you need to look out for are:

  • Initialising the Camel Context
  • Initialising your Camel routes
  • Customising any Camel components
  • Injecting any of your own custom beans

With this in mind, here’s a rundown of how to implement each of these when moving from XML to Java configuration.

Related reading - If you’re not familiar with Apache Camel, then I encourage you to read the first part of my Apache Camel Tutorial

Initialising the Camel Context

With Spring XML configuration, initialising the Camel Context is easy - in fact you barely have to do anything at all:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <!-- your Camel stuff goes here -->
</camelContext>

But for Java-based configuration of Spring Framework apps, you’ll need to initialise things a bit first. You need to start Spring and set up your configuration. There are two support classes CamelConfiguration and org.apache.camel.spring.javaconfig.Main to help you:

@Configuration
@ComponentScan
public class Application extends CamelConfiguration {

  public static void main(String[] args) throws Exception {
    // import org.apache.camel.spring.javaconfig.Main
    Main main = new Main();
    main.setConfigClass(Application.class);
    main.run();
  }

  // Beans can go here!
}

Note the use of @ComponentScan, which tells Spring that it should automatically search your package to find any beans to instantiate. This is important!

This will start up the Camel Context and create any beans that you’ve defined inside your Application class and instantiate any other components in your package.

And how do you define your beans? Read on to see some examples.

Beans, so many beans

Beans are used for lots of things in Camel. Everything from file filters, to aggregation strategies, to transformation classes; they’re all implemented in beans.

Custom beans which you might previously have defined in Spring XML config like this…

<bean id="myAggregationStrategy"
      class="com.example.MyAggregationStrategy"/>

…can be initialised in Spring Java config by just adding the @Component annotation to the class. This tells Spring to instantiate a bean of your class, and add it into the Spring context:

@Component
public class MyAggregationStrategy implements AggregationStrategy {
  // ...
}

Since the class name is MyAggregationStrategy, Spring’s magic bean wiring thing will automatically create a bean called myAggregationStrategy (camel-cased).

Configuring Camel components

Some Camel components require some specific configuration upfront.

A prime example of this is the JMS Component. You need to create your own custom instance of the component, and hook it up to a connection factory object which tells the component where your JMS broker is.

Here’s what typical XML config for the JMS component looks like:

<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
  <property name="connectionFactory">
    <bean class="org.apache.activemq.ActiveMQConnectionFactory">
      <property name="brokerURL" value="vm://localhost"/>
    </bean>
  </property>
</bean>

If you want to do this using Java configuration, you’ll need to write the logic to create a JmsComponent inside a method, return your new object from that method, and annotate the method with @Bean.

For example, the code above would look like this in Java configuration:

@Bean
public JmsComponent jms() {
  ActiveMQConnectionFactory amqcf = new ActiveMQConnectionFactory();
  amqcf.setBrokerURL("vm://localhost");

  JmsComponent jms = new JmsComponent();
  jms.setConnectionFactory(amqcf);

  return jms;
}

Because your method name is jms(), Spring’s magic bean wiring thing will automatically create a bean called jms.

On startup, Spring will execute your method. The component returned will be added as a bean inside the Spring container.

Camel will use Spring lookup to find this bean, so you’ll be able to use it in routes like this:

from("jms:queue:customers.in")....

Camel routes

Camel routes are a special case, because in Java DSL, they must be defined inside a Route Builder. So you need to first write a RouteBuilder class.

Then tell Spring to read this class on startup by adding it with the @Component annotation.

In XML DSL, you’d define a route like this:

<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route id="sample-route">
    <from uri="file:src/data?noop=true"/>
    <to uri="log:output"/>
  </route>
</camelContext>

Using Java configuration, we first write a RouteBuilder class. And then we annotate that with @Component, which, when used with @ComponentScan, means this class will be instantiated and wired into the Spring context.

Here’s what the equivalent implementation in Java would look like:

@Component
public class MyRoute extends RouteBuilder {

  @Override
  public void configure() throws Exception {
    from("file:src/data?noop=true")
        .to("log:output");
  }
}

TL;DR

In summary, here’s what you need to do when migrating from Spring XML DSL to Java DSL for configuring Camel routes:

  • The magic Spring annotations you need are: @Bean, @Component and @ComponentScan
  • Write Java code to instantiate your objects (and any child objects - e.g. connection factories)
  • Add the @Bean annotation to any Camel objects that you want Spring to register as beans in the Spring container, such as:
    • Camel components (e.g. JMS component)
    • Connection factories
    • etc…
  • Add the @Component annotation to any of your custom objects that you want Spring to register as beans in the Spring container, such as:
    • RouteBuilder classes
    • Custom aggregation strategy components
    • Custom file filters
    • etc…
  • Make sure that your classes are located in packages underneath your top-level package (this is so that Spring’s @ComponentScan can automatically find your classes)

Questions? Comments? Feedback? Please comment below!

Comments

What do you think? You can use Markdown in your comment.

To write code, indent each line with 4 spaces. Or, to paste a lot of code, you can put it in pastebin.com and share the link in your comment.