Getting started with Camel 3

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; 
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
    │               ├── MyDSLRouteBuilder.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:

<?xml version="1.0" encoding="UTF-8"?><project>
       
   <dependency>
             
      <groupId>org.apache.camel</groupId>
             
      <artifactId>camel-endpointdsl</artifactId>
           
   </dependency>
    
</project>

You can find the source code for this tutorial here: https://github.com/fmarchioni/mastertheboss/tree/master/camel/camel-demo