Let’s have a look at what’s new in Camel 3 with an example project which shows how to run a Camel 3 standalone via the built-in Main class. This example also demonstrates how you can configure the Camel application via Camel built-in dependency-injection that supports binding via the @BindToRegistry and @PropertyInject annotations.
Apache Camel 3 is a new family of products which include:
- Camel 3: The core integration framework
- Camel K: A lightweight Serverless Integration Platform Camel on Kubernetes & Knative
- Camel Quarkus: Camel extensions for Quarkus Optimized JVM & Native compiled Java (GraalVM)
camel-core:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> </dependency>
This contains all camel core dependencies (33 jars) and it’s what you will probably need when migrating from Camel 2.x
camel-core-engine:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core-engine</artifactId> </dependency>
This contains only the core camel dependencies (12 jars).
Creating a sample project.
Apache Camel is distributed with the following archetypes: https://camel.apache.org/manual/latest/camel-maven-archetypes.html
We will be using the camel-archetype-java to generate a sample project:
mvn archetype:generate \ -DarchetypeGroupId=org.apache.camel.archetypes \ -DarchetypeArtifactId=camel-archetype-java \ -DarchetypeVersion=3.0.0 \ -DgroupId=com.mastertheboss.camel \ -DartifactId=camel-demo \ -Dversion=1.0-SNAPSHOT
The project will be created with some sample classes. In our example, we will however make it a bit more interesting by adding some Property Injection and we will also use Camel Main to run it. First of all, let’s add a Route to the Project:
package com.mastertheboss.camel; import org.apache.camel.builder.RouteBuilder; public class MyRouteBuilder extends RouteBuilder { public void configure() { from("quartz:foo?cron={{myCron}}").bean("greetingBean", "hello").process((exchange) -> { exchange.getIn().setBody(exchange.getIn().getBody(String.class).toUpperCase()); }).log("${body}"); } }
This route will a Cron-based route, where the Message is constructed from the GreetingBean’s hello method and the final result is uppercased.
Below we include the GreetingBean class:
package com.mastertheboss.camel; public class GreetingBean { private String hi; public GreetingBean(String hi) { this.hi = hi; } public String hello() { return hi + " how are you?"; } }
Then, in order to create an instance of the GreetingBean, we will be using a Configuration class which injects the property “hi” from the configuration into the Bean:
package com.mastertheboss.camel; import org.apache.camel.BindToRegistry; import org.apache.camel.PropertyInject; public class CustomConfiguration { @BindToRegistry public GreetingBean greetingBean(@PropertyInject("hi") String hi) { // this will create an instance of this bean with the name of the method (eg myBean) return new GreetingBean(hi); } public void configure() { // this method is optional and can be removed if no additional configuration is needed. } }
Then, our Camel Main class, which also includes a main method so it can be run also from within your IDE:
package com.mastertheboss.camel; import org.apache.camel.main.Main; /** * A Camel Application */ public class MainApp { /** * A main() so we can easily run these routing rules in our IDE */ public static void main(String... args) throws Exception { // use Camels Main class Main main = new Main(); main.addConfigurationClass(CustomConfiguration.class); main.addRouteBuilder(MyRouteBuilder.class); main.run(args); } }
In order to run this example, we will set the following properties in the configuration file application.properties:
camel.main.name = CamelHelloWorld # properties used in the route myCron = 0/2 * * * * ? # application properties hi = Hello
And finally, the dependencies needed to run the example:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mastertheboss.camel</groupId> <artifactId>camel-demo</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>A Camel Route</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencyManagement> <dependencies> <!-- Camel BOM --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-parent</artifactId> <version>3.0.0</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-main</artifactId> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-quartz</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> <build> <defaultGoal>install</defaultGoal> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- Allows the example to be run via 'mvn compile exec:java' --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <configuration> <mainClass>com.mastertheboss.camel.MainApp</mainClass> <includePluginDependencies>false</includePluginDependencies> </configuration> </plugin> <!-- Run 'mvn camel-main:generate' --> <!-- generate autowire.properties file that can automatic detect resources from the classpath to make convention over configuration for Camel components --> <plugin> <groupId>org.apache.camel</groupId> <artifactId>camel-main-maven-plugin</artifactId> <version>3.0.0</version> <configuration> <logClasspath>false</logClasspath> </configuration> </plugin> </plugins> </build> </project>
That’s all. Provided that you have deleted the initial classes created by the archetype, you should end up with the following project tree:
src ├── main │ ├── java │ │ └── com │ │ └── mastertheboss │ │ └── camel │ │ ├── CustomConfiguration.java │ │ ├── GreetingBean.java │ │ ├── MainApp.java │ │ └── MyRouteBuilder.java │ └── resources │ ├── application.properties │ ├── log4j2.properties │ └── META-INF │ └── spring-configuration-metadata.json
Run the main class and verify that the Property is injected correctly and the Route is fired:
[duler-CamelHelloWorld_Worker-1] route1 INFO HELLO HOW ARE YOU? [duler-CamelHelloWorld_Worker-2] route1 INFO HELLO HOW ARE YOU?
Using type-safe DSL Routes
Camel end users would experience the problem if you made a configuration mistake in the endpoint, which then makes Camel fail on startup. Since Camel 3, there is a new type-safe DSL for endpoints which you can use in Java routes. In order to use type-safe DSL you need some adjustments to your code. First of all, you need to extend the class org.apache.camel.builder.endpoint.EndpointRouteBuilder. Here is a basic example without and with the endpoint DSL:
package com.mastertheboss.camel; import org.apache.camel.builder.endpoint.EndpointRouteBuilder; public class MyDSLRouteBuilder extends EndpointRouteBuilder { public void configure() { from(quartz("foo?cron={{myCron}}")). bean("greetingBean", "hello").process((exchange) -> { exchange.getIn().setBody(exchange.getIn().getBody(String.class).toUpperCase()); }).log("${body}"); } }
Then, you need to add to your dependencies the camel-endpointdsl artifact:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-endpointdsl</artifactId> </dependency>
You can find the source code for this tutorial here: https://github.com/fmarchioni/mastertheboss/tree/master/camel/camel-demo