Spring Boot and Apache Camel
We take a look at the Apache Camel integration tool, and how it can be used with Spring Boot in your Java-based coding projects.
Join the DZone community and get the full member experience.
Join For FreeAs the world of software moves on, more complex systems are being developed, which have to integrate with each other. It started with SOA and it continues with microservices.
Camel is the number one integration tool that comes to my mind, since, nowadays, Spring Boot with Camel is a very strong combination.
The first step is to include the Camel dependencies to our Spring project.
buildscript {
ext {
springBootVersion = '1.5.9.BUILD-SNAPSHOT'
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
group = 'com.gkatzioura'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
compile('org.apache.camel:camel-spring-boot-starter:2.20.0')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.apache.camel:camel-test-spring:2.20.0')
}
In order to have a faster project setup from scratch, you can always use the online Spring initializer.
Now let's add a simple route:
package com.gkatzioura.springcamel.routes;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class TimerRoute extends RouteBuilder {
public static final String ROUTE_NAME = "TIMER_ROUTE";
@Override
public void configure() throws Exception {
from("timer:initial//start?period=10000")
.routeId(ROUTE_NAME)
.to("log:executed");
}
}
We don't have to worry about the Camel context configuration since the Camel auto-configuration creates a SpringCamelContext for you and takes care of the proper initialization and shutdown of that context.
Also, Camel auto-configuration collects all the RouteBuilder instances from the Spring context and automatically injects them into the provided CamelContext. Thus we don't have to register our routes to the CamelContext.
As you can see, our route has a timer with a period of 10000 milliseconds which routes to a log endpoint. The log endpoint will print the executed string every 10000 milliseconds.
Keep in mind that if no routeId is specified, Camel will assign a name on its own, therefore giving a name to our route definition is a good practice in case we want to retrieve the root definition.
In order for Camel to stay up, we need to keep our main thread blocked. Thus, we add this configuration to our application.yml file.
camel:
springboot:
main-run-controller: true
Instead of this, we can include the spring-boot-starter-web dependency, but our application has as few dependencies as possible, and we need to keep it this way.
However, the most difficult part of the integration with other systems is testing. Throughout the years, there have been rapid advancements in testing and the tools that we use.
Camel also comes packaged with some great tools in order to unit test.
For example, we will implement a test of the route specified previously.
@RunWith(CamelSpringBootRunner.class)
@SpringBootTest
public class SpringCamelApplicationTests {
@EndpointInject(uri = MOCK_RESULT)
private MockEndpoint resultEndpoint;
@Autowired
private CamelContext camelContext;
@EndpointInject(uri = MOCK_TIMER)
private ProducerTemplate producer;
private static final String MOCK_RESULT = "mock:result";
private static final String MOCK_TIMER = "direct:mock-timer";
@Before
public void setup() throws Exception {
camelContext.getRouteDefinition(TimerRoute.ROUTE_NAME)
.autoStartup(true)
.adviceWith(camelContext, new AdviceWithRouteBuilder() {
@Override
public void configure() throws Exception {
replaceFromWith(MOCK_TIMER);
interceptSendToEndpoint("log*")
.skipSendToOriginalEndpoint()
.to(MOCK_RESULT);
}
});
}
@Test
public void sendMessage() throws Exception {
resultEndpoint.expectedMessageCount(1);
producer.sendBody("A message");
resultEndpoint.assertIsSatisfied();
}
}
Let's have a look at each part of the test.
Our JUnit runner of choice would be the CamelSpringBootRunner.class
@RunWith(CamelSpringBootRunner.class)
We inject a ProducerTemplate. The ProducerTemplate interface allows you to send message exchanges to endpoints in a variety of different ways to make it easy to work with Camel Endpoint instances from Java code.
Then we inject a MockEndpoint. The MockEndpoint will serve us by replacing the original endpoint. Then we will set the expected number of messages to be received. Once the processing is done, we assert that the amount of received messages is satisfied.
On our setup method, we will replace our original endpoint with the fake producer template endpoint. Thus, our route will receive the events that we will issue from the ProducerTemplate.
Then we will also intercept the log endpoint and direct the message to the MockEndpoint previously specified.
So we ended up with a camel application and a unit test for the route specified.
You can find the source code on GitHub.
Published at DZone with permission of Emmanouil Gkatziouras, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments