What exactly is Apache Camel?
My take to describe this in a more accessible way...
In order to understand what Apache Camel is, you need to understand what are Enterprise Integration Patterns.
Let's start with what we presumably already know: The Singleton pattern, the Factory pattern, etc; They are merely ways of organizing your solution to the problem, but they are not solutions themselves. These patterns were analyzed and extracted for the rest of us by the Gang of Four, when they published their book: Design Patterns. They saved some of us tremendous effort in thinking of how to best structure our code.
Much like the Gang of Four, Gregor Hohpe and Bobby Woolf authored the book Enterprise Integration Patterns (EIP) in which they propose and document a set of new patterns and blueprints for how we could best design large component-based systems, where components can be running on the same process or in a different machine.
They basically propose that we structure our system to be message oriented -- where components communicate with each others using messages as inputs and outputs and absolutely nothing else. They show us a complete set of patterns that we may choose from and implement in our different components that will together form the whole system.
So what is Apache Camel?
Apache Camel offers you the interfaces for the EIPs, the base objects, commonly needed implementations, debugging tools, a configuration system, and many other helpers which will save you a ton of time when you want to implement your solution to follow the EIPs.
Take MVC. MVC is pretty simple in theory and we could implement it without any framework help. But good MVC frameworks provide us with the structure ready-to-use and have gone the extra mile and thought out all the other "side" things you need when you create a large MVC project and that's why we use them most of the time.
That's exactly what Apache Camel is for EIPs. It's a complete production-ready framework for people who want to implement their solution to follow the EIPs.
Apache Camel File-Watch Without Spring
Apache Camel is a standalone integration framework to easily integrate different systems using well know and established EIP (Enterprise Integration Patterns).
It is not tied to Spring in any way at its core and has different deployment / integration models out of which is Spring / Spring Boot for the sake of easing its adoption and configuration for Spring framework users.
Out of the runtime contexts, you can use for example Camel Main component to run your application having a File Watch component setup to watch the /tmp/
directory change events:
The main application would look like the following:
public class FileWatchApplication {
private FileWatchApplication() {
}
public static void main(String[] args) throws Exception {
// use Camels Main class
Main main = new Main(FileWatchApplication.class);
// now keep the application running until the JVM is terminated (ctrl + c or sigterm)
main.run(args);
}
}
A simple RouteBuilder
setting up your File Watch component would look like the following (note that it must be within the same (or child) package of the FileWatchApplication
class):
public class FileWatchRouteBuilder extends RouteBuilder {
@Override
public void configure() throws Exception {
// snippet configuration from the official documentation
from("file-watch:///tmp/")
.log("File event: ${header.CamelFileEventType} occurred on file ${header.CamelFileName} at ${header.CamelFileLastModified}");
}
}
Apache Camel: What marches messages along?
Under the hood I believe camel is constructing a pure graph where each node is a Camel endpoint/processor, and each edge is a route between two endpoints (a source and a destination). This graph is precisely what RouteBuilder
is building when you invoke its API. When you go to start()
a Camel route, the graph is most likely validated and translated into a series of Runnable
s that need to be executed, and probably uses some kind of custom Executor
or thread management to handle these Runnable
s.
Thus, the execution of the Runnable
s (processors processing messages as they arrive) are handled by this custom Executor
. This is the mechanism that "marches messages along", although the order in which the tasks are queued up is driven by the overarching structure of the graph composed by RouteBuilder
.
What is the CamelContext in apache?
After seeing the videos in YouTube, I knew about the Apache camel framework,
and from that I got the answer that camelcontext is nothing but context of the 'apache camel framework' framework.
As many framework have context like Spring have an applicationcontext, Ninja have a context, same the 'Apache camel framework' have a context and that is called 'camelcontext'.
So, it is the run-time system of Apache Camel (framework) and it connects its different concepts such as routes, components or endpoints.
Reference :
Basic Apache Camel Tutorial
How to install Apache Camel?
Camel, Java-projects and build-tools
Apache Camel works like any other Java library or framework, meaning that in order to use the framework you'll have to include its java binaries (i.e .jar files) to your project as dependencies. Now to make things simpler most developers use either Maven or Gradle to create, manage and build their java projects.
From these two I would recommend Maven as it seems to be the preferred option for Camel developers with most examples (including ones in official camel site) using it. To install maven follow the official how to install maven guide.
Maven archetypes
To create example camel project you can use maven archetypes which are basically project templates that can be used to create various types of new projects. If you're reading Camel in Action you might be better off using Camel version 2.x.x in your projects with Java Development Kit version 8. Camel 3.x.x is pretty similar so it should be fairly easy to learn that after learning the basics with 2.x.x.
After installing maven you can use your Java IDE (i.e IntelliJ, VSCode, Eclipse or Netbeans) to create project from maven archetype with groupId: org.apache.camel.archetypes artifactId: camel-archetype-java and version: 2.25.4
Or use maven command line command:
# Create new camel java project for Camel version 2.25.4
mvn archetype:generate -DarchetypeGroupId="org.apache.camel.archetypes" -DarchetypeArtifactId=camel-archetype-java -DarchetypeVersion="2.25.4"
Camel project
The new project should contain project file pom.xml where you can specify all the dependencies for your project. The the camel-archetype-java should have the following dependencies listed in the dependencies section of pom.xml.
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
</dependency>
<!-- logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<scope>runtime</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The example route in camel-archetype-java archetypes Myroutebuilder.java is pretty complex for beginners. Hello world on a timer is generally a more simpler test on to see if things work.
package com.example;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
public class MyRouteBuilder extends RouteBuilder {
public void configure() {
// from("file:src/data?noop=true")
// .choice()
// .when(xpath("/person/city = 'London'"))
// .log("UK message")
// .to("file:target/messages/uk")
// .otherwise()
// .log("Other message")
// .to("file:target/messages/others");
from("timer:timerName?period=3000")
.routeId("helloWorldTimer")
.log(LoggingLevel.INFO, "Hello world");
}
}
The project generated from archetype comes with exec-maven-plugin which allows you to run the project with mvn exec:java
Java development kit - JDK
If you're using JDK 11 instead of JDK 8 you'll have to modify maven-compiler-plugin configuration a bit.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<!--
<source>1.8</source>
<target>1.8</target>
-->
<source>11</source>
<target>11</target>
</configuration>
</plugin>
If you've multiple versions of JDK installed you'll have to configure your IDE to use the correct one for the project. With IntelliJ you can configure JDK used by the project from project structure settings.
With VSCode you'll need both JDK 11 and JDK 8 as VSCode Java extensions require JDK 11 to run.
Example settings.json entries for OpenJDK:
"java.configuration.runtimes": [
{
"name": "JavaSE-11",
"path": "C:\\Program Files\\AdoptOpenJDK\\jdk-11.x.x.xxx-hotspot",
"default": true
},
{
"name": "JavaSE-1.8",
"path": "C:\\Program Files\\RedHat\\java-1.8.0-openjdk-1.8.0.xxx-x",
}
],
"java.home": "C:\\Program Files\\AdoptOpenJDK\\jdk-11.x.x.xxx-hotspot"
You might also want to setup your path variable so that java -version returns correct version from the command-line.
Apache Camel: routes inheritance - moving code to the super class
RouteBuilder supports inheritance just like any other class, since onException is RouteBuilder scoped it's fine to define it in a base class. I would however advice against or at least be very careful when defining routes in something like BaseRouteBuilder.
You should also mark BaseRouteBuilder abstract and I would also try to give it more descriptive name like RouteBuilderWithGenericErrorHandler.
RouteBuilders are used to create and add RouteDefinitions to CamelContext and you're not limited to using just one RouteBuilder. Now if you add multiple RouteBuilders to CamelContext that all inherit from BaseRouteBuilder this will cause BaseRouteBuilder.configure to be called multiple times.
This can cause problems like:
- RouteId conflicts.
- Consumer endpoint URI conflicts.
- Same configuration changes getting applied multiple times.
- i.e if you use
RouteBuilder.getContext
to modify the CamelContext in some way.
- i.e if you use
In all my route I am calling at the end a http request to create the order and I am using a format to convert it to JSON.
If many of your routes share same routing logic you should split that part to separate route and use it like function. It also makes it easier to add route-scope error handling like what to do when orders api isn't responding or the body provided to direct:createOrder
consumer endpoint is invalid.
@Override
public void configure() throws Exception {
from("direct:createOrderA")
.routeId("createOrderA")
// .. do stuff for A
.to("direct:createOrder");
from("direct:createOrderB")
.routeId("createOrderB")
// .. do stuff for B
.to("direct:createOrder");
DataFormat format = ...;
from("direct:createOrder")
.routeId("createOrder")
.marshal(format)
.setHeader(
Exchange.HTTP_METHOD,
constant(org.apache.camel.component.http.HttpMethods.POST)
)
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.to("http://localhost:8080/orders/create-order");
}
In Apache Camel what does route() do in a restful declaration?
Using .route
allows you to define new route(s) within your rest-definition. It can be handy if your route is short or if you just want to process/transform/validate the message in someway before sending it to your actual consumer endpoint.
For example
rest("/someRoute")
.id("someRoute")
.description("Some description")
.post()
.consumes("text/plain")
.produces("text/plain")
.route()
.routeId("someRoutePost")
.process(new SomeMessageProcessor())
.to("direct:toSomewhere")
.end()
.endRest()
.get()
.route()
.routeId("someRouteGet")
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(405))
.setBody(constant("GET not allowed on this route"))
.end()
.endRest()
But if you just want to call direct consumer endpoint and do this stuff there instead you can do that.
it is up to ones preference really.
thanks, I see if I wanted to say call .log() I would have to put .route() first
Yes. Camel uses method-chaining with its Java-DSL where something like this is often required. When defining Rest most methods return RestDefinition
but if you look closely .route
method returns RouteDefition
instead.
To get back to RestDefition
from route one can use .endRest()
as the .end()
in the example doesn't really do anything other than make it easier to see where to RouteDefition block ends.
Update: Note that this example is for Camel 3.14.0. In Newer versions of Camel route()
and endRest()
methods have been Removed from RestDefition class. Example for Camel 3.18.1 can be found here.
Which Enterprise Integration pattern is used in camel
I am facing an issue in finding out which pattern processed the message lastly. Like whether the intercept happened after an aggregate,split or process.After which pattern my intercept processor worked. Is there any way to find out this?
You can get some details from CamelMessageHistory
exchange property which is a list of DefaultMessageHistory
objects. These contain routeId and some details about the endpoint node like its id. If you're using interceptFrom
camel stores the intercepted endpoint uri to CamelInterceptedEndpoint
header.
Intercept and print history
intercept()
.process(new Processor(){
@Override
public void process(Exchange exchange) throws Exception {
String output = "History:\n";
List historyList = (List)exchange.getProperty(Exchange.MESSAGE_HISTORY);
for (int i = 0; i < historyList.size(); i++) {
DefaultMessageHistory messageHistory = (DefaultMessageHistory)historyList.get(i);
output += "\t[" + (i + 1) + "]"
+ messageHistory.getRouteId() +":"
+ messageHistory.getNode().getShortName()
+ "\n";
}
System.out.println(output);
}
});
This however wont help you much as the information is fairly limited and many patterns like split that create a new exchange with their own new history. However for these you could look if headers like CamelSplitIndex
have been set and if it has then determine from that that split has occurred.
if there is any other way to full fill my need to store the details of routing in DB
You could keep things simple and track the state of the exchange using events instead. Just write a processor or component that you can use to write/store what happened and when during the routing. You can use breadcrumbId
as unique id for exchange as this persists even after something like split.
The processor or custom component could then simply stream these events to some file or store them to a local database like sqlite for further processing. Avoid sending them straight to external database or service to minimize impact on actual routes.
This kind of diagram is what I need to create from the routes. Thanks in Advance.
Have you looked in to Hawtio? It does a lot of this already. I am running Hawtio with Apache Karaf and it provides me with Route diagrams and fairly detailed profiling data for routes, endpoints and whatnot. Its also open source so you can modify it or use it as reference for your own application.
If you prefer to do something similar yourself you can look in to using JMX to manage and monitor camel application. For my understanding Hawtio uses it under the hood to get more information about applications running inside JVM.
Apache Camel: Aggregator vs Enrich
Thinking Aggregator and Enrich as components can be a bit misleading. They're implementations for enterprise integration patterns in camel and as such very integral to how Apache camel works.
For example pipeline pattern and content enricher are things that you're likely already using all the time with Apache camel without even realizing it. With Apache camel you're almost constantly enriching messages using components, translators, beans and processors.
Aggregation Strategy is used to configure how you want camel to combine one or more exchanges. This means it can be used to combine current exchange to a incoming exchange with enrich or to group multiple exchanges in to one when when using split or aggregate.
How can i know which one to use?
They're completely different patterns, one is for enriching exchanges with new data and one grouping exchanges together.
In terms of defining routes with Camel Java-DSL use of .enrich()
is pretty uncommon as generally it's enough to call the endpoint directly with to("uri")
.
More common is .pollEnrich("uri")
as with it one can use consumer endpoints like file, ftp or jms to enrich the exchange. Meaning if you need data from two or more files on a route you can use it to do so.
When it comes to .aggregate()
it's mainly used to group exchanges together and handle them as a group after some pre-determined condition has been met like for example every 100 exchanges or after 10 seconds of no new exchanges.
Related Topics
How to Update Path Variable Permanently from Windows Command Line
How to Convert Float to Int with Java
Loading Context in Spring Using Web.Xml
How to Split String with Some Separator But Without Removing That Separator in Java
Why Does Concurrenthashmap Prevent Null Keys and Values
What Is the Default Access Modifier in Java
Lambda Expression VS Method Reference
Java's L Number (Long) Specification
Clean Way to Combine Multiple Jars? Preferably Using Ant
How to Refer Environment Variable in Pom.Xml
How to Check CPU and Memory Usage in Java
Converting Any Object to a Byte Array in Java
Java8: Why Is It Forbidden to Define a Default Method for a Method from Java.Lang.Object
Increase the Java Heap Size Permanently
Why Do Many Collection Classes in Java Extend the Abstract Class and Implement the Interface as Well