Getting Started with Red Hat build of Apache Camel for Quarkus


Red Hat build of Apache Camel 4.8

Getting Started with Red Hat build of Apache Camel for Quarkus

Abstract

This guide introduces Red Hat build of Apache Camel for Quarkus and explains the various ways to create and deploy an application using Red Hat build of Apache Camel for Quarkus.

Preface

Providing feedback on Red Hat build of Apache Camel documentation

To report an error or to improve our documentation, log in to your Red Hat Jira account and submit an issue. If you do not have a Red Hat Jira account, then you will be prompted to create an account.

Procedure

  1. Click the following link to create ticket
  2. Enter a brief description of the issue in the Summary.
  3. Provide a detailed description of the issue or enhancement in the Description. Include a URL to where the issue occurs in the documentation.
  4. Clicking Submit creates and routes the issue to the appropriate documentation team.

This guide introduces Red Hat build of Apache Camel for Quarkus, the various ways to create a project and how to get started building an application using Red Hat build of Apache Camel for Quarkus:

Red Hat build of Apache Camel for Quarkus brings the integration capabilities of Apache Camel and its vast component library to the Quarkus runtime.

The benefits of using Red Hat build of Apache Camel for Quarkus include the following:

  • Enables users to take advantage of the performance benefits, developer joy and the container first ethos which Quarkus provides.
  • Provides Quarkus extensions for many of the Apache Camel components.
  • Takes advantage of the many performance improvements made in Camel, which results in a lower memory footprint, less reliance on reflection and faster startup times.

1.1.1. Languages

In Red Hat build of Apache Camel for Quarkus, you can define Camel routes using the following languages:

  • Java DSL
  • YAML
  • XML IO

1.2. Tooling

1.2.1. IDE plugins

Quarkus has plugins for most of the popular development IDEs which provide Quarkus language support, code/configuration completion, project creation wizards and much more. The plugins are available at each respective IDE marketplace.

Check the plugin documentation to discover how to create projects for your preferred IDE.

1.2.2. Camel content assist

The following plugins provide support for content assist when editing Camel routes and application.properties:

1.3.1. Overview

You can use code.quarkus.redhat.com to generate a Quarkus Maven project which automatically adds and configures the extensions that you want to use in your application.

This section walks you through the process of creating a Quarkus Maven project with Red Hat build of Apache Camel for Quarkus including:

  • Creating the skeleton application using code.quarkus.redhat.com
  • Adding a simple Camel route
  • Exploring the application code
  • Compiling the application in development mode
  • Testing the application

You can bootstrap and generate projects on code.quarkus.redhat.com.

The Red Hat build of Apache Camel for Quarkus extensions are located under the 'Integration' heading.

If you need additional extensions, use the 'search' field to find them.

Select the component extensions that you want to work with and click 'Generate your application' to download a basic skeleton project.

You can also push the project directly to GitHub.

For more information about using code.quarkus.redhat.com to generate Quarkus Maven projects, see Creating a Quarkus Maven project using code.quarkus.redhat.com in the Getting started with Red Hat build of Quarkus guide.

Procedure

  1. In the code.quarkus.redhat.com website, select the following extensions:

    • camel-quarkus-rest
    • camel-quarkus-jackson
    • camel-quarkus-direct

      Note

      Do not compile the application on code.quarkus.redhat.com (in the final step of the procedure). Instead, use the compile command described in the Section 1.3.5, “Development mode” section below.

  2. Navigate to the directory where you extracted the generated project files from the previous step:

    $ cd <directory_name>
    Copy to Clipboard Toggle word wrap

1.3.3. Explore the application code

The application has two compile dependencies which are managed within the com.redhat.quarkus.platform:quarkus-camel-bom that is imported in <dependencyManagement>.:

pom.xml

<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>com.redhat.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>
    <!-- The latest 3.15.x version from https://maven.repository.redhat.com/ga/com/redhat/quarkus/platform/quarkus-bom -->
</quarkus.platform.version>

...

<dependency>
    <groupId>${quarkus.platform.group-id}</groupId>
    <artifactId>${quarkus.platform.artifact-id}</artifactId>
    <version>${quarkus.platform.version}</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
<dependency>
    <groupId>${quarkus.platform.group-id}</groupId>
    <artifactId>quarkus-camel-bom</artifactId>
    <version>${quarkus.platform.version}</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
Copy to Clipboard Toggle word wrap

Note

For more information about BOM dependency management, see Developing Applications with Red Hat build of Apache Camel for Quarkus

The application is configured by properties defined within src/main/resources/application.properties, for example, the camel.context.name can be set there.

1.3.4. Adding a simple Camel route

Note

In this example, we use the simple example from the camel-quarkus-examples repository. It consists of the two simple classes Fruit.java, Legume.java and the route definitions Routes.java .

Procedure

  1. Create a file named Fruit.java in the src/main/java/org/acme/ subfolder.
  2. Add the class as shown in the following code snippet:

    Fruit.java

    package org.acme.rest.json;
    
    import java.util.Objects;
    
    import io.quarkus.runtime.annotations.RegisterForReflection;
    
    /**
     * A REST entity representing a fruit.
     */
    @RegisterForReflection // Lets Quarkus register this class for reflection during the native build
    public class Fruit {
        private String name;
        private String description;
    
        public Fruit() {
        }
    
        public Fruit(String name, String description) {
            this.name = name;
            this.description = description;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Fruit)) {
                return false;
            }
    
            Fruit other = (Fruit) obj;
    
            return Objects.equals(other.name, this.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(this.name);
        }
    }
    Copy to Clipboard Toggle word wrap

  3. Create a file named Legume.java in the src/main/java/org/acme/ subfolder.
  4. Add the class as shown in the following code snippet:

    Legume.java

    package org.acme.rest.json;
    
    import java.util.Objects;
    
    import io.quarkus.runtime.annotations.RegisterForReflection;
    
    /**
     * A REST entity representing a legume.
     */
    @RegisterForReflection // Lets Quarkus register this class for reflection during the native build
    public class Legume {
        private String name;
        private String description;
    
        public Legume() {
        }
    
        public Legume(String name, String description) {
            this.name = name;
            this.description = description;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Legume)) {
                return false;
            }
    
            Legume other = (Legume) obj;
    
            return Objects.equals(other.name, this.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(this.name);
        }
    }
    Copy to Clipboard Toggle word wrap

  5. Create a file named Routes.java in the src/main/java/org/acme/ subfolder.
  6. Add a Camel Rest route as shown in the following code snippet:

    Routes.java

    package org.acme.rest.json;
    
    import java.util.Collections;
    import java.util.LinkedHashSet;
    import java.util.Set;
    
    import org.apache.camel.builder.RouteBuilder;
    import org.apache.camel.model.rest.RestBindingMode;
    
    /**
     * Camel route definitions.
     */
    public class Routes extends RouteBuilder {
        private final Set<Fruit> fruits = Collections.synchronizedSet(new LinkedHashSet<>());
        private final Set<Legume> legumes = Collections.synchronizedSet(new LinkedHashSet<>());
    
        public Routes() {
    
            /* Let's add some initial fruits */
            this.fruits.add(new Fruit("Apple", "Winter fruit"));
            this.fruits.add(new Fruit("Pineapple", "Tropical fruit"));
    
            /* Let's add some initial legumes */
            this.legumes.add(new Legume("Carrot", "Root vegetable, usually orange"));
            this.legumes.add(new Legume("Zucchini", "Summer squash"));
        }
    
        @Override
        public void configure() throws Exception {
    
            restConfiguration().bindingMode(RestBindingMode.json);
    
            rest("/fruits")
                    .get()
                    .to("direct:getFruits")
    
                    .post()
                    .type(Fruit.class)
                    .to("direct:addFruit");
    
            rest("/legumes")
                    .get()
                    .to("direct:getLegumes");
    
            from("direct:getFruits")
                    .setBody().constant(fruits);
    
            from("direct:addFruit")
                    .process().body(Fruit.class, fruits::add)
                    .setBody().constant(fruits);
    
            from("direct:getLegumes")
                    .setBody().constant(legumes);
        }
    }
    Copy to Clipboard Toggle word wrap

    For more information about this example, see camel-quarkus-examples repository.

1.3.5. Development mode

$ mvn clean compile quarkus:dev
Copy to Clipboard Toggle word wrap

This command compiles the project, starts your application, and lets the Quarkus tooling watch for changes in your workspace. Any modifications you make to your project will automatically take effect in the running application.

You can check the application in your browser. (For example, for the rest-json sample application, access http://localhost:8080/fruits)

If you change the application code, for example, change 'Apple' to 'Orange', your application automatically updates. To see the changes applied, refresh your browser.

Refer to Quarkus documentation Development mode section for more details about the development mode.

1.3.6. Testing

1.3.6.1. JVM mode

To test the Camel Rest route that we have created in JVM mode, add a test class as follows:

Procedure

  1. Create a file named RoutesTest.java in the src/test/java/org/acme/ subfolder.
  2. Add the RoutesTest class as shown in the following code snippet:

    RoutesTest.java

    package org.acme;
    
    import io.quarkus.test.junit.QuarkusTest;
    import org.junit.jupiter.api.Test;
    
    import static io.restassured.RestAssured.given;
    import org.hamcrest.Matchers;
    
    @QuarkusTest
    public class RoutesTest {
    
        @Test
        public void testFruitsEndpoint() {
    
            /* Assert the initial fruit is there */
            given()
                    .when().get("/fruits")
                    .then()
                    .statusCode(200)
                    .body(
                            "$.size()", Matchers.is(1),
                            "name", Matchers.contains("Orange"));
    
            /* Add a new fruit */
            given()
                    .body("{\"name\": \"Pear\"}")
                    .header("Content-Type", "application/json")
                    .when()
                    .post("/fruits")
                    .then()
                    .statusCode(200);
    
            /* Assert that pear was added */
            given()
                    .when().get("/fruits")
                    .then()
                    .statusCode(200)
                    .body(
                            "$.size()", Matchers.is(2),
                            "name", Matchers.contains("Orange", "Pear"));
        }
    
    }
    Copy to Clipboard Toggle word wrap

The JVM mode tests are run by maven-surefire-plugin in the test Maven phase:

$ mvn clean test
Copy to Clipboard Toggle word wrap
1.3.6.2. Native mode

To test the Camel Rest route that we have created in Native mode, add a test class as follows:

Procedure

  1. Create a file named NativeRoutesIT.java in the src/test/java/org/acme/ subfolder.
  2. Add the NativeRoutesIT class as shown in the following code snippet:

    NativeRoutesIT.java

    package org.acme;
    
    import io.quarkus.test.junit.NativeImageTest;
    
    @NativeImageTest
    public class NativeRoutesIT extends RoutesTest {
    
        // Execute the same tests but in native mode.
    }
    Copy to Clipboard Toggle word wrap

    The native mode tests are verified by maven-failsafe-plugin in the verify phase.

  3. Pass the native property to activate the profile that runs them:

    $ mvn clean verify -Pnative
    Copy to Clipboard Toggle word wrap
Tip

For more details, and how to use the CamelTestSupport style of testing, see Testing Camel Quarkus Extensions.

1.3.7. Packaging and running the application

1.3.7.1. JVM mode

Procedure

  1. Run mvn package to prepare a thin jar for running on a stock JVM:

    $ mvn clean package
    $ ls -lh target/quarkus-app
    ...
    -rw-r--r--. 1 user user 238K Oct 11 18:55  quarkus-run.jar
    ...
    Copy to Clipboard Toggle word wrap
    Note

    The thin jar contains just the application code. You also need the dependencies in target/quarkus-app/lib to run it.

  2. Run the jar as follows:

    $ java -jar target/quarkus-app/quarkus-run.jar
    ...
    [io.quarkus] (main) Quarkus started in 1.163s. Listening on: http://[::]:8080
    Copy to Clipboard Toggle word wrap
Note

The boot time should be around a second.

1.3.7.2. Native mode

Procedure

To prepare a native executable, do as follows:

  1. Run the command mvn clean package -Pnative:

    $ mvn clean package -Pnative
    $ ls -lh target
    ...
    -rwxr-xr-x. 1 user user  46M Oct 11 18:57  code-with-quarkus-1.0.0-SNAPSHOT-runner
    ...
    Copy to Clipboard Toggle word wrap
    Note

    The runner has no .jar extension and has the x (executable) permission set. You can run it directly:

    $ ./target/*-runner
    ...
    [io.quarkus] (main) Quarkus started in 0.013s. Listening on: http://[::]:8080
    ...
    Copy to Clipboard Toggle word wrap

    The application started in 13 milliseconds.

  2. View the memory usage with the ps -o rss,command -p $(pgrep code-with) command :

    $ ps -o rss,command -p $(pgrep code-with)
      RSS COMMAND
    65852 ./target/code-with-quarkus-1.0.0-SNAPSHOT-runner
    Copy to Clipboard Toggle word wrap

    The application uses 65 MB of memory.

Tip

See Producing a native executable in the Compiling your Quarkus applications to native executables guide for additional information about preparing a native executable.

Tip

1.4. Testing Camel Quarkus Extensions

Testing offers a good way to ensure Camel routes behave as expected over time. If you haven’t already, read the Camel Quarkus user guide First Steps and the Quarkus documentation Testing your application section.

When it comes to testing a route in the context of Quarkus, the recommended approach is to write local integration tests. This has the advantage of covering both JVM and native mode.

In JVM mode, you can use the CamelTestSupport style of testing.

1.4.1. Running in JVM mode

In JVM mode, use the @QuarkusTest annotation to bootstrap Quarkus and start Camel routes before the @Test logic executes.

For example:

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

@QuarkusTest
class MyTest {
    @Test
    public void test() {
        // Use any suitable code that sends test data to the route and then assert outcomes
        ...
    }
}
Copy to Clipboard Toggle word wrap
Tip

You can find a sample implementation in the Camel Quarkus source:

1.4.2. Running in native mode

Note

Always test that your application works in native mode for all supported extensions.

You can reuse the test logic defined for JVM mode by inheriting the logic from the respective JVM mode class.

Add the @QuarkusIntegrationTest annotation to tell the Quarkus JUnit extension to compile the application under test to native image and start it before running the tests.

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
class MyIT extends MyTest {
   ...
}
Copy to Clipboard Toggle word wrap
Tip

You can find a sample implementation in the Camel Quarkus source:

A native executable does not need a JVM to run, and cannot run in a JVM, because it is native code, not bytecode.

There is no point in compiling tests to native code so they run using a traditional JVM.

This means that communication between tests and the application must go over the network (HTTP/REST, or any other protocol your application speaks), through watching filesystems (log files for example), or any other interprocess communication.

1.4.3.1. @QuarkusTest in JVM mode

In JVM mode, tests annotated with @QuarkusTest execute in the same JVM as the application under test.

This means you can use @Inject to add beans from the application into the test code.

You can also define new beans or even override the beans from the application using @jakarta.enterprise.inject.Alternative and @jakarta.annotation.Priority.

1.4.3.2. @QuarkusIntegrationTest in native mode

In native mode, tests annotated with @QuarkusIntegrationTest execute in a JVM hosted in a process separate from the running native application.

An important consequence of this, is that all communication between the tests and the native application, must take one or more of the following forms:

  • Network calls. Typically, HTTP or any other network protocol your application supports.
  • Watching the filesystem for changes. (For example via Camel file endpoints.)
  • Any other kind of interprocess communication.

QuarkusIntegrationTest provides additional features that are not available through @QuarkusTest:

  • In JVM mode, you can launch and test the runnable application JAR produced by the Quarkus build.
  • In native mode, you can launch and test the native application produced by the Quarkus build.
  • If you add a container image to the build, a container starts, and tests execute against it.

For more information about QuarkusIntegrationTest, see the Quarkus testing guide.

1.4.4. Testing with external services

1.4.4.1. Testcontainers

Sometimes your application needs to access some external resource, such as a messaging broker, a database, or other service.

If a container image is available for the service of interest, you can use Testcontainers to start and configure the services during testing.

For the application to work properly, it is often essential to pass the connection configuration data (host, port, user, password of the remote service) to the application before it starts.

In the Quarkus ecosystem, QuarkusTestResourceLifecycleManager serves this purpose.

You can start one or more Testcontainers in the start() method and return the connection configuration from the method in the form of a Map.

The entries of this map are then passed to the application in different ways depending on the mode:

  • Native mode: a command line (-Dkey=value)
  • JVM Mode: a special MicroProfile configuration provider
Note

Command line and MicroProfile settings have a higher precedence than the settings in the application.properties file.

import java.util.Map;
import java.util.HashMap;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;

public class MyTestResource implements QuarkusTestResourceLifecycleManager {

    private GenericContainer<?> myContainer;

    @Override
    public Map<String, String> start() {
        // Start the needed container(s)
        myContainer = new GenericContainer(DockerImageName.parse("my/image:1.0.0"))
                .withExposedPorts(1234)
                .waitingFor(Wait.forListeningPort());

        myContainer.start();

        // Pass the configuration to the application under test
        // You can also pass camel component property names / values to automatically configure Camel components
        return new HashMap<>() {{
                put("my-container.host", container.getHost());
                put("my-container.port", "" + container.getMappedPort(1234));
        }};
    }

    @Override
    public void stop() {
        // Stop the needed container(s)
        myContainer.stop();
        ...
    }
}
Copy to Clipboard Toggle word wrap

Reference the defined test resource from the test classes with @QuarkusTestResource:

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@QuarkusTestResource(MyTestResource.class)
class MyTest {
   ...
}
Copy to Clipboard Toggle word wrap
Tip

You can find a sample implementation in the Camel Quarkus source:

1.4.4.2. WireMock

Instead of having the tests connect to live endpoints, for example, if they are unavailable, unreliable, or expensive, you can stub HTTP interactions with third-party services & APIs.

You can use WireMock for mocking & recording HTTP interactions. It is used extensively throughout the Camel Quarkus test suite for various component extensions.

1.4.4.2.1. Setting up WireMock

Procedure

  1. Set up the WireMock server.

    Note

    Always configure the Camel component under test to pass any HTTP interactions through the WireMock proxy. You can achieve this by configuring a component property that determines the API endpoint URL.

    import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
    import static com.github.tomakehurst.wiremock.client.WireMock.get;
    import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
    import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import com.github.tomakehurst.wiremock.WireMockServer;
    
    import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
    
    public class WireMockTestResource implements QuarkusTestResourceLifecycleManager {
    
        private WireMockServer server;
    
        @Override
        public Map<String, String> start() {
            // Setup & start the server
            server = new WireMockServer(
                wireMockConfig().dynamicPort()
            );
            server.start();
    
            // Stub an HTTP endpoint. WireMock also supports a record and playback mode
            // https://wiremock.org/docs/record-playback/
            server.stubFor(
                get(urlEqualTo("/api/greeting"))
                    .willReturn(aResponse()
                        .withHeader("Content-Type", "application/json")
                        .withBody("{\"message\": \"Hello World\"}")));
    
            // Ensure the camel component API client passes requests through the WireMock proxy
            Map<String, String> conf = new HashMap<>();
            conf.put("camel.component.foo.server-url", server.baseUrl());
            return conf;
        }
    
        @Override
        public void stop() {
            if (server != null) {
                server.stop();
            }
        }
    }
    Copy to Clipboard Toggle word wrap
  2. Ensure your test class has the @QuarkusTestResource annotation with the appropriate test resource class specified as the value. The WireMock server will be started before all tests are executed and will be shut down when all tests are finished.
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@QuarkusTestResource(WireMockTestResource.class)
class MyTest {
   ...
}
Copy to Clipboard Toggle word wrap

The WireMock server starts before all tests execute and shuts down when all tests finish.

Tip

You can find a sample implementation in the Camel Quarkus integration test source tree:

Since Camel Quarkus 2.13.0, you can use CamelQuarkusTestSupport for testing. It is a replacement for CamelTestSupport, which does not work well with Quarkus.

Important

CamelQuarkusTestSupport only works in JVM mode. If you need to test in native mode, then use one of the alternate test strategies described above.

Add the following dependency into your module (preferably in the test scope):

<dependency>
    <groupId>org.apache.camel.quarkus</groupId>
    <artifactId>camel-quarkus-junit5</artifactId>
    <scope>test</scope>
</dependency>
Copy to Clipboard Toggle word wrap

You can use CamelQuarkusTestSupport in your test like this:

@QuarkusTest
@TestProfile(SimpleTest.class) //necessary only if "newly created" context is required for the test (worse performance)
public class SimpleTest extends CamelQuarkusTestSupport {
    ...
}
Copy to Clipboard Toggle word wrap
1.4.5.2. Customizing the CamelContext for testing

You can customize the CamelContext for testing with configuration profiles, CDI beans, observers, mocks etc. You can also override the createCamelContext method and interact directly with the CamelContext.

Important

When using createCamelContext you MUST NOT instantiate and return a new CamelContext. Instead, invoke super.createCamelContext() and modify the returned CamelContext as needed. Failing to follow this rule will result in an exception being thrown.

@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {

    @Override
    protected CamelContext createCamelContext() throws Exception {
        // Must call super to get a handle on the application scoped CamelContext
        CamelContext context = super.createCamelContext();
        // Apply customizations
        context.setTracing(true);
        // Return the modified CamelContext
        return context;
    }
}
Copy to Clipboard Toggle word wrap
1.4.5.3. Configuring routes for testing

Any classes that extend RouteBuilder in your application will have their routes automatically added to the CamelContext. Similarly, any XML or YAML routes configured from camel.main.routes-include-pattern will also be loaded.

This may not always be desirable for your tests. You control which routes get loaded at test time with configuration properties:

  • quarkus.camel.routes-discovery.include-patterns
  • quarkus.camel.routes-discovery.exclude-patterns,
  • camel.main.routes-include-pattern
  • camel.main.routes-exclude-pattern.

You can also define test specific routes per test class by overriding createRouteBuilder:

@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    @Test
    void testGreeting() {
        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
        mockEndpoint.expectedBodiesReceived("Hello World");

        template.sendBody("direct:start", "World");

        mockEndpoint.assertIsSatisified();
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start")
                    .transform().simple("Hello ${body}")
                    .to("mock:result");
            }
        };
    }
}
Copy to Clipboard Toggle word wrap
1.4.5.4. CamelContext test lifecycle

One of the main differences in CamelQuarkusTestSupport compared to CamelTestSupport is how the CamelContext lifecycle is managed.

On Camel Quarkus, a single CamelContext is created for you automatically by the runtime. By default, this CamelContext is shared among all tests and remains started for the duration of the entire test suite execution.

This can potentially have some unintended side effects for your tests. If you need to have the CamelContext restarted between tests, then you can create a custom test profile, which will force the application under test to be restarted.

For example, to define a test profile:

@QuarkusTest
class MyTestProfile implements QuarkusTestProfile {
    ...
}
Copy to Clipboard Toggle word wrap

Then reference it on the test class with @TestProfile:

// @TestProfile will trigger the application to be restarted
@TestProfile(MyTestProfile.class)
@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    ...
}
Copy to Clipboard Toggle word wrap
Note

You cannot manually restart the CamelContext by invoking its stop() and start() methods. This will result in an exception.

1.4.5.5. Examples
1.4.5.5.1. Simple RouteBuilder and test class

Simple RouteBuilder:

public class MyRoutes extends RouteBuilder {
    @Override
    public void configure() {
        from("direct:start")
            .transform().simple("Hello ${body}")
            .to("mock:result");
    }
}
Copy to Clipboard Toggle word wrap

Test sending a message payload to the direct:start endpoint:

@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    @Test
    void testGreeting() {
        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
        mockEndpoint.expectedBodiesReceived("Hello World");

        template.sendBody("direct:start", "World");

        mockEndpoint.assertIsSatisified();
    }
}
Copy to Clipboard Toggle word wrap
1.4.5.5.2. Using AdviceWith
@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
    @BeforeEach
    public void beforeEach() throws Exception {
        AdviceWith.adviceWith(this.context, "advisedRoute", route -> {
            route.replaceFromWith("direct:replaced");
        });
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").routeId("advisedRoute")
                    .transform().simple("Hello ${body}")
                    .to("mock:result");
            }
        };
    }

    @Test
    void testAdvisedRoute() throws Exception {
        MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
        mockEndpoint.expectedBodiesReceived("Hello World");

        template.sendBody("direct:replaced", "World");

        mockEndpoint.assertIsSatisfied();
    }
}
Copy to Clipboard Toggle word wrap
1.4.5.5.3. Explicitly enabling advice

When explicitly enabling advice you must invoke startRouteDefinitions when completing your AdviceWith setup.

Note

Invoking startRouteDefinitions is only required if you have routes configured that are NOT being advised.

1.4.5.6. Limitations

CamelQuarkusTestSupport inherits some test lifecycle methods from CamelTestSupport. However, they should not be used and instead are replaced with equivalent methods in CamelQuarkusTestSupport.

Expand
CamelTestSupport lifecycle methodsCamelQuarkusTestSupport equivalent

afterAll

doAfterAll

afterEach, afterTestExecution

doAfterEach

beforeAll

doAfterConstruct

beforeEach

doBeforeEach

The CamelQuarkusTestSupport implementation of createCamelRegistry will throw UnsupportedOperationException.

If you need to bind or unbind objects to the Camel registry, then you can do it by one of the following methods.

  • Produce named CDI beans

    public class MyBeanProducers {
        @Produces
        @Named("myBean")
        public MyBean createMyBean() {
            return new MyBean();
        }
    }
    Copy to Clipboard Toggle word wrap
  • Override createCamelContext (see example above) and invoke camelContext.getRegistry().bind("foo", fooBean)
  • Use the @BindToRegistry annotation

    @QuarkusTest
    class SimpleTest extends CamelQuarkusTestSupport {
        @BindToRegistry("myBean")
        MyBean myBean = new MyBean();
    }
    Copy to Clipboard Toggle word wrap
    Note

    Beans bound to the Camel registry from individual test classes, will persist for the duration of the test suite execution. This could have unintended consequences, depending on your test expectations. You can use test profiles to restart the CamelContext to avoid this.

Chapter 2. Deploying Quarkus applications

You can deploy your Quarkus application on OpenShift by using any of the following build strategies:

  • Docker build
  • S2I Binary
  • Source S2I

For more details about each of these build strategies, see Chapter 1. OpenShift build strategies and Quarkus of the Deploying your Quarkus applications to OpenShift Container Platform guide.

Note

The OpenShift Docker build strategy is the preferred build strategy that supports Quarkus applications targeted for JVM as well as Quarkus applications compiled to native executables. You can configure the deployment strategy using the quarkus.openshift.build-strategy property.

Chapter 3. Setting up Maven locally

Typical Red Hat build of Apache Camel application development uses Maven to build and manage projects.

3.1. Preparing to set up Maven

Maven is a free, open source, build tool from Apache. Typically, you use Maven to build Fuse applications.

Procedure

  1. Download Maven 3.8.6 or later from the Maven download page.

    Tip

    To verify that you have the correct Maven and JDK version installed, open a command terminal and enter the following command:

    mvn --version
    Copy to Clipboard Toggle word wrap

    Check the output to verify that Maven is version 3.8.6 or newer, and is using OpenJDK 17.

  2. Ensure that your system is connected to the Internet.

    While building a project, the default behavior is that Maven searches external repositories and downloads the required artifacts. Maven looks for repositories that are accessible over the Internet.

    You can change this behavior so that Maven searches only repositories that are on a local network. That is, Maven can run in an offline mode. In offline mode, Maven looks for artifacts in its local repository. See Section 3.3, “Using local Maven repositories”.

3.2. Adding Red Hat repositories to Maven

To access artifacts that are in Red Hat Maven repositories, you need to add those repositories to Maven’s settings.xml file.

Maven looks for the settings.xml file in the .m2 directory of the user’s home directory. If there is not a user specified settings.xml file, Maven uses the system-level settings.xml file at M2_HOME/conf/settings.xml.

Prerequisite

You know the location of the settings.xml file in which you want to add the Red Hat repositories.

Procedure

In the settings.xml file, add repository elements for the Red Hat repositories as shown in this example:

Note

If you are using the camel-jira component, also add the atlassian repository.

<?xml version="1.0"?>
<settings>

  <profiles>
    <profile>
      <id>extra-repos</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <repositories>
       <repository>
            <id>redhat-ga-repository</id>
            <url>https://maven.repository.redhat.com/ga</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>atlassian</id>
            <url>https://packages.atlassian.com/maven-external/</url>
            <name>atlassian external repo</name>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
            <id>redhat-ga-repository</id>
            <url>https://maven.repository.redhat.com/ga</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>

  <activeProfiles>
    <activeProfile>extra-repos</activeProfile>
  </activeProfiles>

</settings>
Copy to Clipboard Toggle word wrap

3.3. Using local Maven repositories

If you are running a container without an Internet connection, and you need to deploy an application that has dependencies that are not available offline, you can use the Maven dependency plug-in to download the application’s dependencies into a Maven offline repository. You can then distribute this customized Maven offline repository to machines that do not have an Internet connection.

Procedure

  1. In the project directory that contains the pom.xml file, download a repository for a Maven project by running a command such as the following:

    mvn org.apache.maven.plugins:maven-dependency-plugin:3.1.0:go-offline -Dmaven.repo.local=/tmp/my-project
    Copy to Clipboard Toggle word wrap

    In this example, Maven dependencies and plug-ins that are required to build the project are downloaded to the /tmp/my-project directory.

  2. Distribute this customized Maven offline repository internally to any machines that do not have an Internet connection.

When running the applications you need access to the artifacts that are in the Red Hat Maven repositories. These repositories are added to Maven’s settings.xml file. Maven checks the following locations for settings.xml file:

  • looks for the specified url
  • if not found looks for ${user.home}/.m2/settings.xml
  • if not found looks for ${maven.home}/conf/settings.xml
  • if not found looks for ${M2_HOME}/conf/settings.xml
  • if no location is found, empty org.apache.maven.settings.Settings instance is created.

3.4.1. About Maven mirror

Maven uses a set of remote repositories to access the artifacts, which are currently not available in local repository. The list of repositories almost always contains Maven Central repository, but for Red Hat Fuse, it also contains Maven Red Hat repositories. In some cases where it is not possible or allowed to access different remote repositories, you can use a mechanism of Maven mirrors. A mirror replaces a particular repository URL with a different one, so all HTTP traffic when remote artifacts are being searched for can be directed to a single URL.

3.4.2. Adding Maven mirror to settings.xml

To set the Maven mirror, add the following section to Maven’s settings.xml:

<mirror>
      <id>all</id>
      <mirrorOf>*</mirrorOf>
      <url>http://host:port/path</url>
</mirror>
Copy to Clipboard Toggle word wrap

No mirror is used if the above section is not found in the settings.xml file. To specify a global mirror without providing the XML configuration, you can use either system property or environmental variables.

To set the Maven mirror using either environmental variable or system property, you can add:

  • Environmental variable called MAVEN_MIRROR_URL to bin/setenv file
  • System property called mavenMirrorUrl to etc/system.properties file

To use an alternate Maven mirror url, other than the one specified by environmental variables or system property, use the following maven options when running the application:

  • -DmavenMirrorUrl=mirrorId::mirrorUrl

    for example, -DmavenMirrorUrl=my-mirror::http://mirror.net/repository

  • -DmavenMirrorUrl=mirrorUrl

    for example, -DmavenMirrorUrl=http://mirror.net/repository. In this example, the <id> of the <mirror> is just a mirror.

3.5. About Maven artifacts and coordinates

In the Maven build system, the basic building block is an artifact. After a build, the output of an artifact is typically an archive, such as a JAR or WAR file.

A key aspect of Maven is the ability to locate artifacts and manage the dependencies between them. A Maven coordinate is a set of values that identifies the location of a particular artifact. A basic coordinate has three values in the following form:

groupId:artifactId:version

Sometimes Maven augments a basic coordinate with a packaging value or with both a packaging value and a classifier value. A Maven coordinate can have any one of the following forms:

groupId:artifactId:version
groupId:artifactId:packaging:version
groupId:artifactId:packaging:classifier:version
Copy to Clipboard Toggle word wrap

Here are descriptions of the values:

groupdId
Defines a scope for the name of the artifact. You would typically use all or part of a package name as a group ID. For example, org.fusesource.example.
artifactId
Defines the artifact name relative to the group ID.
version
Specifies the artifact’s version. A version number can have up to four parts: n.n.n.n, where the last part of the version number can contain non-numeric characters. For example, the last part of 1.0-SNAPSHOT is the alphanumeric substring, 0-SNAPSHOT.
packaging
Defines the packaged entity that is produced when you build the project. For OSGi projects, the packaging is bundle. The default value is jar.
classifier
Enables you to distinguish between artifacts that were built from the same POM, but have different content.

Elements in an artifact’s POM file define the artifact’s group ID, artifact ID, packaging, and version, as shown here:

<project ... >
  ...
  <groupId>org.fusesource.example</groupId>
  <artifactId>bundle-demo</artifactId>
  <packaging>bundle</packaging>
  <version>1.0-SNAPSHOT</version>
  ...
</project>
Copy to Clipboard Toggle word wrap

To define a dependency on the preceding artifact, you would add the following dependency element to a POM file:

<project ... >
  ...
  <dependencies>
    <dependency>
      <groupId>org.fusesource.example</groupId>
      <artifactId>bundle-demo</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>
  ...
</project>
Copy to Clipboard Toggle word wrap
Note

It is not necessary to specify the bundle package type in the preceding dependency, because a bundle is just a particular kind of JAR file and jar is the default Maven package type. If you do need to specify the packaging type explicitly in a dependency, however, you can use the type element.

Chapter 4. Sample applications

4.1. Red Hat build of Quarkus Examples

The Red Hat build of Quarkus examples repository contains a number of examples on how to integrate with Camel for a variety of use cases. They provide best practice advice and describe common patterns that we see in integration and messaging problems.

The examples can be run using Maven. When using the mvn command, Maven will attempt to download the required dependencies from a central repository to your local repository.

4.2. Examples repository

Expand
ExampleDescription

Artemis to ElasticSearch

Shows how the message is consumed from the Apache Artemis broker using MQTT protocol, transformed, and loaded into ElasticSearch.

Leader election in Kubernetes

Shows how to use Camel’s master component for Kubernetes leader election.

Custom main()

Shows how to start Camel from a custom main() method.

Camel Quarkus CXF SOAP example

Shows how to use Camel CXF SOAP component.

Extract, Transform, and Load between DBs

Shows how to extract, transform, and load between two databases.

File consumer with Bindy & FTP

Shows how to consume CSV files, marshal & unmarshal the data, and send it via FTP.

Health

Shows how to use Camel health-checks with Quarkus.

HTTP with vanilla JAX-RS or Camel HTTP

Shows how to create HTTP endpoints using either platform-http or RESTEasy.

JMS and JPA

Demonstrates a Camel Quarkus application supporting JTA transactions across multiple transactional resources.

JPA idempotent repository

Shows how to consume a message only once, even when delivered multiple times.

JTA and JPA

Demonstrates JTA transactions across MySQL and a simulated XAResource.

Kafka example

Shows how to produce and consume messages in a Kafka topic using Strimzi Operator.

Kamelet Chuck Norris

Shows how to build a simple Kamelet for use in Camel applications.

Message Bridge

Demonstrates AMQ and IBM MQ clients with connection pooling and XA transactions.

Observability

Demonstrates adding metrics, health checks, and distributed tracing support.

OpenAPI Contract First

Shows how to run with Contract First OpenAPI.

Platform HTTP security with Keycloak

Shows how to secure platform HTTP with Keycloak.

REST with Jackson

Demonstrates creating a REST service using Camel REST DSL and Jackson.

Saga and LRA

Shows how to use saga and LRA patterns.

Timer to Log

Uses the Camel timer component to output a Hello world message to the console.

Tokenize a CSV file

Shows how to define a Camel route in XML for tokenizing a CSV file.

Vertx-Websocket Chat

Shows how to configure a WebSocket server and interact with connected peers.

Legal Notice

Copyright © 2025 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust. Explore our recent updates.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Theme

© 2026 Red Hat
Back to top