Common use case examples

Simple REST API in Spring Boot

Add the dependencies:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-servlet-starter</artifactId>    	
</dependency>
<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-swagger-java-starter</artifactId>    	
</dependency>
<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jackson-starter</artifactId>    	
</dependency>    

This will initialise a servlet with context path camel. To change this, Set the Spring configuration property: camel.component.servlet.mapping.context-path to the root where your services will be located, e.g. /services/*

Code snippets

Initialise a Camel JMS component with a transaction manager

Initialise a Camel JMS component and use the transaction manager on the classpath, e.g. if Narayana is there, it will use it:

@Bean(name = "jms-component")
public JmsComponent jmsComponent(ConnectionFactory xaJmsConnectionFactory, PlatformTransactionManager jtaTransactionManager) {
  JmsComponent jms = new JmsComponent();
  jms.setConnectionFactory(xaJmsConnectionFactory);
  jms.setTransactionManager(jtaTransactionManager);
  jms.setTransacted(true);

  return jms;
}

CXF/SOAP: populate the body with a simple JAXB element

XML DSL: Use transform and method with the ObjectFactory created by JAXB to create a simple output:

<transform>
    <method method="createMyResponseElement"
            beanType="com.example.myservice.ObjectFactory"/>
</transform>

Testing

Camel provides the following test support classes:

Class
TestSupport Contains some useful test methods, e.g. deleteDirectory, etc.
CamelTestSupport Extends TestSupport by creating a CamelContext and a ProducerTemplate.
CamelSpringTestSupport

And these runners:

Runner Description
CamelSpringBootRunner e.g. @RunWith(CamelSpringBootRunner.class) - to be used with Spring Boot applications. Add an @Autowired CamelContext camelContext to the class members. This extends the SpringJUnit4ClassRunner class from Spring.

Mock component

Use the Mock component to intercept requests, return mock data, etc.

  • The @MockEndpointsAndSkip annotation can be used to mock one set of endpoints automatically. This requires that the component being mocked already exists - e.g. an activemq component or amqp component, etc.
    • To confirm that the endpoint(s) are successfully mocked, look for this in the logs: .c.i.InterceptSendToMockEndpointStrategy : Adviced endpoint [jms://queue:MY.QUEUE?exchangePattern=InOnly] with mock endpoint [mock:jms:queue:MY.QUEUE]
    • Note that mock endpoints will not be shown in the log line which starts with: Adviced route before/after as XML....`
  • A route can’t receive from a mock: endpoint, so use replaceFromWith() to a direct endpoint if you also want to “mock” the first component in a route.

Basic assertions with mock endpoints

Use mock points to verify that a particular endpoint was invoked, and optionally do assertions on the content of the message received by the mock.

Assert that the first received message is of a given type:

MockEndpoint mock = getMockEndpoint("mock:output");
mock.message(0).body().isInstanceOf(MyCustomClass.class);

assertMockEndpointsSatisfied();

Return test data from a mock endpoint with a reusable Processor

Returning fake data from a Mock endpoint is done by providing a Processor to whenAnyExchangeReceived. We can create an inner Processor class which can be reused across multiple tests, in case there are many tests which execute in a similar way:

MockEndpoint myMockComponent;

public void shouldDoSomething() throws Exception {
    myMockComponent.whenAnyExchangeReceived(new TestDataProcessor("something"));

    // do your assertions here
}

// Inner Processor class which can be reused across multiple tests
class TestDataProcessor implements Processor {
    private String testData;

    public TestDataProcessor(String testData) {
        super();
        this.testData = testData;
    }

    @Override
    public void process(Exchange exchange) throws Exception {
        // Put the test String in the message body
        exchange.getMessage().setBody(testData);
        // optionally also set any headers here
    }
}

Camel Testing cookbook

Using files in src/test/resources as test data

Using the contents of a file as the body of a test message:

InputStream payload = getClass().getClassLoader().getResourceAsStream("barPolicyRoute.xml");
template.sendBody("direct:your-endpoint", payload);

Adding a bean to the Camel registry for testing

Camel 3.x method:

Extend the CamelTestSupport class and then annotate any fields with @BindToRegistry, which adds that bean to the registry, e.g.:

@BindToRegistry("digitalOceanClient")
DigitalOceanClient digitalOceanClient = new DigitalOceanClientMock();

Or override the method bindToRegistry(Registry registry) in the class CamelTestSupport:

@Override
protected void bindToRegistry(Registry registry) throws Exception {
    // add ActiveMQ with embedded broker
    ConnectionFactory connectionFactory = CamelJmsTestHelper.createConnectionFactory();
    JmsComponent amq = jmsComponentAutoAcknowledge(connectionFactory);
    amq.setCamelContext(context);

    registry.bind("jms", amq);
}

Camel 2.x method:

To add clients, components, or anything that needs to be available to Camel when the test runs, extend the CamelTestSupport class and override createRegistry():

@Override
protected JndiRegistry createRegistry() throws Exception {
  JndiRegistry registry = super.createRegistry();

  // Instantiate the object and add it to the Camel registry
  MyClientBean clientBean = new MyClientBean();
  registry.bind("clientBean", clientBean);

  return registry;
}

Assertions on a synchronous exchange

Use an inline processor and the request method to invoke an endpoint synchronously:

Exchange rtn = template.request("direct:hello", new Processor() {
  @Override
  public void process(Exchange exchange) throws Exception {
    exchange.getMessage().setHeader("foo", "bar");
  }
});
assertEquals("bar", rtn.getMessage().getHeader("foo"));

Testing a custom Camel Processor

To test your own custom Camel Processor without having to test it from within a route, you’ll need an Exchange object, which itself needs a CamelContext. We can easily do this with a DefaultCamelContext and an ExchangeBuilder:

// import org.apache.camel.builder.ExchangeBuilder;
// import org.apache.camel.impl.DefaultCamelContext;

@Test
public void testCustomProcessor() throws Exception {
    CustomProcessor customProcessor = new CustomProcessor();
    CamelContext context = new DefaultCamelContext();
    Exchange exchange = new ExchangeBuilder(context).withBody("INPUT_STRING").build();

    // Pass exchange to Processor
    customProcessor.process(exchange);

    // Assert that the Exchange changed
    assertEquals("EXPECTED_OUTPUT", exchange.getMessage().getBody(String.class));
}

Testing a Timer with NotifyBuilder

Test that a number of messages flow through the route successfully:

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(classes = Application.class)
public class ApplicationTest {

    @Autowired
    private CamelContext camelContext;

    @Test
    public void shouldSendMessage() throws Exception {
        // Assert that Camel produces 1 message
        // (which is generated by the Timer component)
        NotifyBuilder notify = new NotifyBuilder(camelContext).whenDone(1).create();
        // whenDone = sets a condition when number of Exchange is done being processed.

        assertTrue(notify.matches(10, TimeUnit.SECONDS));

    }
}

Using AdviceWith in Camel 3.x

If using Camel 3 onwards, use this helper class to apply an adviceWith to a route:

AdviceWithRouteBuilder.adviceWith(context, "main-route",
    a -> a.weaveAddLast().to("mock:output")
);

Use AdviceWith to add an endpoint

Adding a mock component (mock:output) to the end of a route:

RouteDefinition route = context.getRouteDefinitions().get(0);
route.adviceWith(context, new AdviceWithRouteBuilder() {
  @Override
  public void configure() throws Exception {
    weaveAddLast().to("mock:output");
  }
});

Using AdviceWith with Spring Boot

If you want to use adviceWith() to modify Camel routes in your Spring Boot app then:

  • Annotate your test class with @UseAdviceWith
  • If you don’t, then the CamelSpringBootJUnit4ClassRunner will attempt to start your routes.
  • This would be bad, because then your routes will be started with the non-adviced configuration.

Example:

@RunWith(CamelSpringBootRunner.class)
@UseAdviceWith
@SpringBootTest(classes = MyApplication.class,
        properties = { "key = value"})
public class MyApplicationTest {

    @Autowired
    private CamelContext context;

    @Before
    public void setUp() throws Exception {

        RouteDefinition initiate = context.getRouteDefinition("my-route");
        initiate.adviceWith(context, new AdviceWithRouteBuilder() {
            @Override
            public void configure() throws Exception {
                // Replace the `from` component in this route with a direct:start
                replaceFromWith("direct:start");
            }
        });
    }

    @Test
    public void testXslt() throws Exception {
        context.start();
        // do stuff
        context.stop();
    }
}

Test templates

Using CamelTestSupport

A basic Camel test support class, useful for playing around/POCs. Define a Camel route and test it, all in one single test class:

public class MyQuickCamelTest extends CamelTestSupport {

  @Test
  public void testRoute() throws Exception {
    // Test and assertions go here
  }

  @Override
  protected RoutesBuilder createRouteBuilder() throws Exception {
    return new RouteBuilder() {
      @Override
      public void configure() throws Exception {
        // Route goes here:
        // from("direct:...").to("something:else")....
      }
    };
  }
}

Using CamelSpringTestSupport

For Spring-based applications. Allows you to get standard Camel objects like ProducerTemplate and methods like getMockEndpoints, without having to inject any dependencies:

public class BeanTest extends CamelSpringTestSupport {

  @Override
  protected AbstractApplicationContext createApplicationContext() {
    // provide the path to your Spring XML config
    return new ClassPathXmlApplicationContext("com/example/MyApplication-context.xml");
  }

  @Test
  public void testSingleMethod() throws Exception {
    RouteDefinition route = context.getRouteDefinitions().get(0);
    route.adviceWith(context, new AdviceWithRouteBuilder() {
      @Override
      public void configure() throws Exception {
        weaveAddLast().to("mock:output");
      }
    });

    MockEndpoint mock = getMockEndpoint("mock:output");
    mock.expectedBodiesReceived("Expected body here");

    template.sendBody("direct:start", null);

    assertMockEndpointsSatisfied();
  }

}

Spring Boot Camel test

Assuming that you have already defined a Camel Context (using XML or otherwise), then first add these dependencies:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-test-spring</artifactId>
  <scope>test</scope>
</dependency>

And then create a simple test class like this:

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(classes = MyApplication.class)
public class MyApplicationTest {

    @Autowired
    private CamelContext camelContext;

    @Test
    public void shouldDoSomething() throws Exception {
        // Put assertions here
    }

}

Blueprint testing with CamelBlueprintTestSupport

An example test class for Camel routes implemented with OSGi Blueprint:

import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
import org.junit.Test;

public class MyApplicationTest extends CamelBlueprintTestSupport {

    @Override
    protected String getBlueprintDescriptor() {
        return "/OSGI-INF/blueprint/camel-context.xml";
    }

    @Test
    public void testReturnsProperty() throws Exception {
        // assert that the CamelContext starts up correctly
        assertTrue(context.getStatus().isStarted());
    }

}

Camel 2 to 3 migration notes

RouteBuilder class not found:

  • Camel Spring Boot dependencies have moved to the Maven groupId org.apache.camel.springboot

Package org.apache.camel.builder.xml does not exist:

  • The XPathBuilder class has moved.
  • Add camel-xpath dependency
  • Replace import with: import static org.apache.camel.language.xpath.XPathBuilder.xpath

org.apache.camel.impl.JndiRegistry in org.apache.camel.impl has been deprecated:

  • No longer need to explicitly create and configure a registry
  • Use the @BindToRegistry("myBeanName") annotation against a field member.
  • Or override the method void bindToRegistry(Registry registry)
  • See the section below on Adding a bean to the Camel registry for testing

cannot find symbol: class DefaultExchange, location: package org.apache.camel.impl:

  • Moved to org.apache.camel.support.DefaultExchange

getOut() in org.apache.camel.Exchange has been deprecated:

  • Use getMessage() instead.

adviceWith() no longer exists in RouteDefinition class:

  • Switch to: AdviceWithRouteBuilder.adviceWith(context, "main-route", a -> a.weaveAddLast().to("mock:output"));

saxon=true no longer exists on XSLT component:

  • Functionality has been moved to the xslt-saxon component instead.

Data formats: <unmarshal ref="myformat"/> is no longer supported syntax:

  • Use <unmarshal><custom ref="myformat"></unmarshal> instead.