Camel Tutorial - Jonathan-Eid/kennuware-wiki GitHub Wiki
git clone https://github.com/code-not-found/apache-camel.git
cd apache-camel/apache-camel-rest
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>${apache-camel-version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-servlet-starter</artifactId>
<version>${apache-camel-version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>${apache-camel-version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-swagger-java-starter</artifactId>
<version>${apache-camel-version}</version>
</dependency>
Apache provides a camel-spring-boot-starter for the core Camel framework. Each additional component requires it's own additional import
camel-servlet-starter will be used to start the REST API server
camel-swagger-java-starter will define our REST methods
camel-jackson-start will handle serialization/deserialization between JSON and POJO
We will be adding Twilio integration so add the Camel component as a dependency into the pom.xml
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-twilio-starter</artifactId>
<version>${apache-camel-version}</version>
</dependency>
camel:
component:
servlet:
mapping:
context-path: /parts-inc/sales/api/*
The previous developer already configured the servlet component to define the route path for the REST API server.
We need to define configuration for our Twilio component, on the same indent level as the servlet config, add:
twilio:
account-sid: <TWILIO SID>
username: <TWILIO USERNAME>
password: <TWILIO PASSWORD>
Your application.yml file should now look like
camel:
component:
servlet:
mapping:
context-path: /parts-inc/sales/api/*
twilio:
account-sid: <TWILIO SID>
username: <TWILIO USERNAME>
password: <TWILIO PASSWORD>
package com.codenotfound.router;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.stereotype.Component;
import com.codenotfound.model.Order;
import com.codenotfound.service.OrderNotFoundException;
@Component
public class OrderRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
restConfiguration()
// use the servlet component and run on port 8080
.component("servlet").port("8080")
// and enable json binding mode
.bindingMode(RestBindingMode.json)
// enable swagger
.apiContextPath("/swagger-doc")
// setup the api properties
.apiProperty("api.title", "Order API")
.apiProperty("api.version", "1.0.0");
onException(OrderNotFoundException.class).handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(404))
.setBody(constant(""));
// rest operations under the orders context-path
rest("/orders").description("Order REST service")
.consumes("application/json").produces("application/json")
.get("/{id}").outType(Order.class)
.description("Find an order by ID").param().name("id")
.description("The order ID").endParam().responseMessage()
.code(200).message("The order for the given ID")
.endResponseMessage().responseMessage().code(404)
.message("Order not found").endResponseMessage()
.to("bean:orderService?method=findById(${header.id})")
.post().type(Order.class).outType(String.class)
.description("Service to submit a new order")
.responseMessage().code(200).message("The created order id")
.endResponseMessage().responseMessage().code(400)
.message("Invalid input data").endResponseMessage()
.responseMessage().code(500).message("Server error")
.endResponseMessage()
.to("bean:orderService?method=createOrder")
Each Camel route class extends a RouteBuilder class that implements a configure method. The class is annotated by @Component so Spring Boot can detect it and automagically add it to the context.
The beginning of the configure() method shows the servlet component being set to listen on port "8080" in the restConfiguration() route.
Now bring your attention to the "rest(/orders)" route. This is where our rest component is defining the entry into our Orders API.
The preliminary block of the route defines the content type the requests and responses will use, JSON in this case.
The next block defines our GET method, which will retrieve and order by id and respond back to the user
What we will be changing is the .post() route block. Instead of just receiving a response and putting a new order into the database, we will also send out an SMS confirmation using twilio and Camel will be used to write that integration!
package com.codenotfound.router;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.stereotype.Component;
import com.twilio.type.PhoneNumber;
@Component
public class TwilioRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:twilio")
.setHeader("CamelTwilioTo", constant(new PhoneNumber("+13472766941")))
.setHeader("CamelTwilioFrom", constant( new PhoneNumber("+13392246398")))
.setHeader("CamelTwilioBody", simple("Hey, ${body.customer} your order for # ${body.id} has been processed"))
.to("twilio://message/creator");
}
}
The .post() method will eventually get sent to the "direct:twilio" component where Camel will prepare a message to send out to Twilio. The ${body} is our Order object.
from("direct:processOrder")
.multicast()
.to("bean:orderService?method=createOrder")
.to("direct:twilio")
.end();
The multicast() implements the Multicast EIP that will send the same message to different endpoints, in this case the new direct component we defined and the previous method that handles creating orders.
.post().type(Order.class).outType(String.class)
.description("Service to submit a new order")
.responseMessage().code(200).message("The created order id")
.endResponseMessage().responseMessage().code(400)
.message("Invalid input data").endResponseMessage()
.responseMessage().code(500).message("Server error")
.endResponseMessage()
.to("bean:orderService?method=createOrder")
to
.post().type(Order.class).outType(String.class)
.description("Service to submit a new order")
.responseMessage().code(200).message("The created order id")
.endResponseMessage().responseMessage().code(400)
.message("Invalid input data").endResponseMessage()
.responseMessage().code(500).message("Server error")
.endResponseMessage()
.to("direct:processOrder")
package com.codenotfound.router;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.stereotype.Component;
import com.codenotfound.model.Order;
import com.codenotfound.service.OrderNotFoundException;
@Component
public class OrderRouter extends RouteBuilder {
@Override
public void configure() throws Exception {
restConfiguration()
// use the servlet component and run on port 8080
.component("servlet").port("8080")
// and enable json binding mode
.bindingMode(RestBindingMode.json)
// enable swagger
.apiContextPath("/swagger-doc")
// setup the api properties
.apiProperty("api.title", "Order API")
.apiProperty("api.version", "1.0.0");
onException(OrderNotFoundException.class).handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(404))
.setBody(constant(""));
// rest operations under the orders context-path
rest("/orders").description("Order REST service")
.consumes("application/json").produces("application/json")
.get("/{id}").outType(Order.class)
.description("Find an order by ID").param().name("id")
.description("The order ID").endParam().responseMessage()
.code(200).message("The order for the given ID")
.endResponseMessage().responseMessage().code(404)
.message("Order not found").endResponseMessage()
.to("bean:orderService?method=findById(${header.id})")
.post().type(Order.class).outType(String.class)
.description("Service to submit a new order")
.responseMessage().code(200).message("The created order id")
.endResponseMessage().responseMessage().code(400)
.message("Invalid input data").endResponseMessage()
.responseMessage().code(500).message("Server error")
.endResponseMessage()
.to("direct:processOrder")
.put().type(Order.class)
.description("Service to update an existing order")
.responseMessage().code(400).message("Invalid input data")
.endResponseMessage().responseMessage().code(500)
.message("Server error").endResponseMessage()
.to("bean:orderService?method=updateOrder")
.delete("{id}")
.description("Service to cancel an existing order").param()
.name("id").description("The order id").endParam()
.responseMessage().code(404).message("Order not found")
.endResponseMessage().responseMessage().code(500)
.message("Server error").endResponseMessage()
.to("bean:orderService?method=cancelOrder(${header.id})");
from("direct:processOrder")
.multicast()
.to("bean:orderService?method=createOrder")
.to("direct:twilio")
.end();
}
}
When a user posts a new order to the server, they will get an SMS confirmation of that order: