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;

/**
 * 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

Found the article helpful? if so please follow us on Socials