Routing Camel messages to Artemis embedded in WildFly

This is the second tutorial about Camel 3. In the first one (Getting started with Camel 3) we have discussed how to set up a basic Camel 3 projects and run a simple demo with it. Now we will show how to use the messasing API to connect to a remote ArtemisMQ embedded in a WildFly distribution.

Assumed that you have already set up a basic Camel project, let’s create the Route class which does the work to send JMS messages to the Queue named “demoQueue”:

package com.mastertheboss.camel;

import org.apache.camel.builder.RouteBuilder;

public class MyJMSRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        from("timer:foo?period=3s")
                .transform(constant("Hello World"))
                .to("jms:queue:demoQueue");

        from("jms:queue:demoQueue").to("log:demoQueue");
    }
}

The RouteBuilder class will send JMS messages containing “Hello World” every 3 seconds.

Now the Camel Main class to start the route:

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();

        // and add the routes (you can specify multiple classes)
        main.addRouteBuilder(MyJMSRouteBuilder.class);
        // now keep the application running until the JVM is terminated (ctrl + c or sigterm)
        main.run(args);


    }

}

That’s all. Within our application.properties we will setup JMS component with connection to ActiveMQ Artemis broker:

camel.main.name = CamelJMSHelloWorld

# properties used in the route
myCron = 0/2 * * * * ?

# setup JMS component with connection to ActiveMQ Artemis broker
camel.component.jms.connection-factory.brokerURL=tcp://localhost:61616
camel.component.jms.connection-factory.target-connection-factory.user=guest
camel.component.jms.connection-factory.target-connection-factory.password=guest

Now let’s move to the WildFly side. Start by creating a JMS remote acceptor which is bound to port 61616:

/socket-binding-group=standard-sockets/socket-binding=artemis-server:add(port=61616)

/subsystem=messaging-activemq/server=default/remote-acceptor=artemis-acceptor:add(socket-binding=artemis-server)

Within the messaging susbsytem you should be able to see:

        <subsystem xmlns="urn:jboss:domain:messaging-activemq:8.0">
            <server name="default">
                <!-- default configuration here -->
               
                  <remote-acceptor name="artemis-acceptor" socket-binding="artemis-server"/>
            </server>
        </subsystem>

In turn, the remote-acceptor references a socket-binding bound to port 61616:

<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
	<socket-binding name="artemis-server" port="61616"/>
    <!-- Other bindings --> 
</socket-binding-group>

Next, let’s create as well the JMS Queue named demoQueue from the CLI:

jms-queue add --queue-address=demoQueue --entries=queues/demoQueue

Finally, we need to create an user which is allowed to connect to the JMS server. Within our Camel configuration we have already configured it to be guest/guest therefore we will add this user with the shell script add-user.sh:

$ ./add-user.sh 

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Using realm 'ApplicationRealm' as discovered from the existing property files.
Username : guest
Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file.
 - The password should be different from the username
 - The password should not be one of the following restricted values {root, admin, administrator}
 - The password should contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s)
Password : 
WFLYDM0098: The password should be different from the username
Are you sure you want to use the password entered yes/no? yes
Re-enter Password : 
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: guest
About to add user 'guest' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'guest' to file '/home/francesco/jboss/wildfly-18.0.1.Final/standalone/configuration/application-users.properties'
Added user 'guest' to file '/home/francesco/jboss/wildfly-18.0.1.Final/domain/configuration/application-users.properties'
Added user 'guest' with groups guest to file '/home/francesco/jboss/wildfly-18.0.1.Final/standalone/configuration/application-roles.properties'
Added user 'guest' with groups guest to file '/home/francesco/jboss/wildfly-18.0.1.Final/domain/configuration/application-roles.properties'
Is this new user going to be used for one AS process to connect to another AS process? 
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? yes

We are done! On before launching the Camel Main, we need to generate autowiring for the dependencies found in our project, so we will run:

mvn camel-main:generate

Now we can finally run the Camel main with:

mvn exec:java

You should be able to see, from the CLI or Web console that messages are being received in the Queue demoQueue:

/subsystem=messaging-activemq/server=default/jms-queue=demoQueue:read-resource(include-runtime=true)
{
    "outcome" => "success",
    "result" => {
        "consumer-count" => 1,
        "dead-letter-address" => "jms.queue.DLQ",
        "delivering-count" => 0,
        "durable" => true,
        "entries" => ["queues/demoQueue"],
        "expiry-address" => "jms.queue.ExpiryQueue",
        "legacy-entries" => undefined,
        "message-count" => 0L,
        "messages-added" => 24L,
        "paused" => false,
        "queue-address" => "jms.queue.demoQueue",
        "scheduled-count" => 0L,
        "selector" => undefined,
        "temporary" => false
    }
}

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

Calling a JAX-WS Web service from Camel

In this tutorial we will demonstrate how to invoke from Camel a JAX-WS Web service running on WildFly, without writing a single line of code.

First of all, here is the Web service implementation:

import javax.jws.WebService;
@WebService
public class SimpleWebSevice implements Simple {

	@Override
	public String hello(String s) {
		System.out.println("Called Web service with: "+s);
		return "Hello "+s;
	}

}

And its interface:

public interface Simple {
      public String hello(String s);
}

The Web service is packaged in a Web application named DemoWS.war therefore its WSDL is available as: http://localhost:8080/DemoWS/SimpleWebService?wsdl

Creating the Camel application

Now create a new Camel Maven project as described in this tutorial:  Using maven to set up a Camel project

We can describe the Camel route either as Java DSL or via Spring. Using spring, we will create the file camel-context.xml under src/main/resources/META-INF/spring

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
	xmlns:cxf="http://camel.apache.org/schema/cxf"
	xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
         http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd">

	<cxf:cxfEndpoint id="SimpleWebService"
		address="http://localhost:8080/DemoWS/SimpleWebService" wsdlURL="http://localhost:8080/DemoWS/SimpleWebService?wsdl"
		serviceClass="com.sample.SimpleWebService" />

	<camelContext xmlns="http://camel.apache.org/schema/spring">
		<route id="wsClient">
			<from uri="timer:foo?repeatCount=1" />
			<setBody>
				<simple>Hello World!</simple>
			</setBody>
			<log message="${body}" />
			<to uri="cxf:bean:SimpleWebService?defaultOperationName=hello" />
			<to uri="mock:result" />
		</route>
	</camelContext>

</beans> 

In the first part of the configuration file we have declared the cxf: component, which provides integration with Apache CXF for connecting to JAX-WS services hosted in CXF. Then, our route which:

1) Starts with a Timer which is executed once

2) Sets the message body

3) Logs Message

4) Invokes the hello method of SimpleWebService (passing the Message body)

5) Sends the output to the mock component which cab be used by unit tests

Provided that you have included the Camel Maven plugin in the pom.xml:

<plugin>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-maven-plugin</artifactId>
	<version>2.14.0</version>
</plugin>

Then you can start the route with:

mvn camel:run

Proxy Web Services request with Camel

In this tutorial we will show how to proxy a request to a legacy JAX-WS Web service using Camel.

A common scenario is that you have some legacy code (say some Web services) which cannot be reused becuase the interfaces or some conventions (e.g. namespaces) have changed. Proxing request to legacy code can also be a need when you need to monitor or debug some functions which are available on a middleware.

In the Camel quickstarts https://github.com/apache/camel/tree/master/examples we can find an example named camel-example-cxf-proxy that implements this pattern. I will adapt this example to simulate a real world scenario, such as the following one:

Let’s say you have this Legacy Web service implementation running on JBoss EAP 6 (or WildFly 8):

package org.apache.camel.example.cxf.proxy;

import org.apache.camel.example.reportincident.InputReportIncident;
import org.apache.camel.example.reportincident.OutputReportIncident;
import org.apache.camel.example.reportincident.ReportIncidentEndpoint;

@WebService(targetNamespace = "http://reportincident.example.camel.apache.org", name = "ReportIncidentEndpoint")
public class ReportIncidentEndpointService implements ReportIncidentEndpoint {

    public OutputReportIncident reportIncident(InputReportIncident in) {
   
        System.out.println("\n\n\nInvoked real web service: id=" + in.getIncidentId()
                + " by " + in.getGivenName() + " " + in.getFamilyName() + "\n\n\n");

        OutputReportIncident out = new OutputReportIncident();
        out.setCode("OK;" + in.getIncidentId());
        return out;
    }
    
 

}

The structure of the InputReportIncident is stated in the WSDL as follows:

<xs:complexType>
   <xs:sequence>
	<xs:element name="incidentId" type="xs:string"/>
	<xs:element name="incidentDate" type="xs:string"/>
	<xs:element name="givenName" type="xs:string"/>
	<xs:element name="familyName" type="xs:string"/>
	<xs:element name="summary" type="xs:string"/>
	<xs:element name="details" type="xs:string"/>
	<xs:element name="email" type="xs:string"/>
	<xs:element name="phone" type="xs:string"/>
   </xs:sequence>
</xs:complexType>

Now we suppose that the structure has changed so that “givenName” is now named “firstName“:

<xs:complexType>
   <xs:sequence>
	<xs:element name="incidentId" type="xs:string"/>
	<xs:element name="incidentDate" type="xs:string"/>
	<xs:element name="firstName" type="xs:string"/>
	<xs:element name="familyName" type="xs:string"/>
	<xs:element name="summary" type="xs:string"/>
	<xs:element name="details" type="xs:string"/>
	<xs:element name="email" type="xs:string"/>
	<xs:element name="phone" type="xs:string"/>
   </xs:sequence>
</xs:complexType>

How can Camel help us on it ? in a simple way, we will expose the Webservice using Camel through the cxf:cxfEndpoint. We will then define a target endpoint available on EAP 6 at: http://localhost:8080/targetWS/ReportIncidentEndpoint

We use a simple Java Bean to manipulate the content of the SOAP packet, so that the Web service is compatible with the legacy Web service. Here is the camel-context.xml file that we will use:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xmlns:cxf="http://camel.apache.org/schema/cxf"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
       http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd">

  
  <context:property-placeholder location="classpath:incident.properties,file:target/custom.properties"
                                ignore-resource-not-found="true"/>

 

  <!-- bean that enriches the SOAP request -->
  <bean id="enrichBean" class="org.apache.camel.example.cxf.proxy.EnrichBean"/>

  <!-- this is the CXF web service we use as the front end -->
  <cxf:cxfEndpoint id="reportIncident"
                   address="http://localhost:${proxy.port}/camel-example-cxf-proxy/webservices/incident"
                   endpointName="s:ReportIncidentEndpoint"
                   serviceName="s:ReportIncidentEndpointService"
                   wsdlURL="etc/report_incident.wsdl"
                   xmlns:s="http://reportincident.example.camel.apache.org"/>

  <!-- this is the Camel route which proxies the real web service and forwards SOAP requests to it -->
  <camelContext xmlns="http://camel.apache.org/schema/spring">

    <!-- property which contains port number -->
    <propertyPlaceholder id="properties" location="classpath:incident.properties,file:target/custom.properties"/>

    <endpoint id="callRealWebService" uri="http://localhost:8080/targetWS/ReportIncidentEndpoint"/>

    <route>
      <!-- CXF consumer using MESSAGE format -->
      <from uri="cxf:bean:reportIncident?dataFormat=MESSAGE"/>
      <!-- log input received -->
      <to uri="log:input"/>
      <!-- enrich the input by ensure the incidentId parameter is set -->
      <to uri="bean:enrichBean"/>
      <!-- Need to remove the http headers which could confuse the http endpoint -->
      <removeHeaders pattern="CamelHttp*"/>
      <!-- send proxied request to real web service -->
      <to ref="callRealWebService"/>
      <!-- log answer from real web service -->
      <to uri="log:output"/>
    </route>

  </camelContext>

</beans>

We just need to code the EnrichBean. This class actually replaces the node named “givenName” with “firstName”. I have added some log trace of the SOAP packets to show excatly what’s going on.

package org.apache.camel.example.cxf.proxy;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class EnrichBean {

    public Document enrich(Document doc) {
 
        try {
            System.out.println("Before =============");
            printDocument(doc, System.out);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        // Modify SOAP Request
        NodeList nodes = doc.getElementsByTagName("givenName");

        doc.renameNode(nodes.item(0), null, "firstName");

        try {
            System.out.println("After =============");
            printDocument(doc, System.out);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return doc;
    }

    public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

        transformer.transform(new DOMSource(doc),
                new StreamResult(new OutputStreamWriter(out, "UTF-8")));
    }

}

Compile the project with:

mvn clear compile

Execute the Camel route with:

mvn camel:run

You should see the Web service is available at: http://localhost:9080/camel-example-cxf-proxy/webservices/incident?wsdl

Now launch any Web service client like SOAP UI which will assist you in creating a sample SOAP request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rep="http://reportincident.example.camel.apache.org">
   <soapenv:Header/>
   <soapenv:Body>
      <rep:inputReportIncident>
         <incidentId>1</incidentId>
         <incidentDate>2011-11-18</incidentDate>
         <givenName>Bob</givenName>
         <familyName>Smith</familyName>
         <summary>Bla bla</summary>
         <details>More bla</details>
         <email>mymail</email>
         <phone>12345678</phone>
      </rep:inputReportIncident>
   </soapenv:Body>
</soapenv:Envelope>

The JAX-WS legacy Webservice running on JBoss will intercept the request and return a SOAP OK response:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:outputReportIncident xmlns:ns2="http://reportincident.example.camel.apache.org">
         <code>OK;1</code>
      </ns2:outputReportIncident>
   </soap:Body>
</soap:Envelope>

Do you want to see how to deploy the Project as OSGI bundle on JBoss Fuse ? Then check the next tutorial: Proxy Web Services request with JBoss Fuse

Debugging Camel Routes with JBoss Developer Studio

JBoss Developer Studio has been vastly improved to let you develop and test your Camel routes. Debugging Camel routes has never been easier!

Just open the Designer. As you can see, if you hover the pointer on a Component which can be debugged, two options will turn on:

The first one (The red one) will set an unconditional breakpoint on that component, before the transition to the next one. In order to test it, just launch your Route with Debug As | Local Camel Context. Once a breakpoint is hit, the Developer Studio will automatically change to the Debug perspective:

On the other hand, if you want to set a conditional breakpoint, then you have to specify the Conditional Expression that will be evaluated when you hit the break point:

This feature is available since JBoss Tools Integration Stack 4.2 / Developer Studio Integration Stack 8.0.

HelloWorld Camel tutorial

The following is the most basic example of data integration using Camel. We will show how to copy files from one folder to another using a simple Camel route.

Provided that you have downloaded Camel, launch your IDE and create a simple Java project containing this class:

import java.util.Scanner;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

public class App {
  public static void main(String args[]) throws Exception {
    CamelContext context = new DefaultCamelContext();
    context.addRoutes(new RouteBuilder() {
      public void configure() {
        from("file:/home/francesco?noop=true").to("file:/home/camel");
      }
    });
    context.start();
    System.out.println("Press enter to continue...");
    Scanner keyboard = new Scanner(System.in);
    keyboard.nextLine();
    context.stop();
  }
}

The purpose of this simple code is to route files from folder /home/francesco to /home/camel.

The most interesting part is the CamelContext: every Camel application uses a CamelContext that’s subsequently started and then stopped. By starting the CamelContext, you will be able to access Routes in Camel are defined in such a way that they flow when read. This route can be read like this: route files from /home/francesco with the noop option set, and send to /home/camel.

The String “file:/home/francesco?noop=true” is the Endpoint URI of a Route. It is composed of three parts: The Schema (file), the Context Path (/home/francesco) and Options (noop=true)

The noop option tells Camel to leave the source file as is. If you didn’t use this option, the file would be moved.

In order to compile this example, you will need to include the Camel core libraries and the slf4j log library:

Running with Maven

Creating the same project with Maven is pretty easy as well: start by setting up a quickstart project:

D:\maven>mvn archetype:generate -DgroupId=com.sample -DartifactId=hellocamel -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Now replace the com.sample.App class that is created with the quickstart archetype with the one above. Finally, include Camel core dependency in your pom.xml:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-core</artifactId>
  <version>2.14.1</version>
</dependency>

You can run your application using the exec task as follows:

mvn exec:java -Dexec.mainClass="com.sample.App" 

3 Ways to run a Camel Application

What we have just seen is just an example to run a Camel based application. Actually there are three ways to run a Camel application:

1 – Running as a Java standalone application

Camel runs in a standalone application triggering the CamelContext initialization. This approach has been described at the beginning of this tutorial and just needs to initialize the CamelContext from your Java code and package the required Camel libraries in your application:

public class JavaRouter {

   public static void main(String[] args) throws Exception {

      context.addRoutes(new RouteBuilder() {

         public void configure() {
            from("test-jms:queue:test.queue").to("file://test");
         }

   });
}

2 – Running Camel with Spring and a Java starter class

An alternative way is to trigger the startup with the Spring Framework.

public class SpringRouter {

   public static void main(String[] args) throws Exception {

      AbstractXmlApplicationContext springCtx = new  ClassPathXmlApplicationContext("META-INF/camelContext.xml");

      springCtx.start();

      Thread.sleep(10000);

      springCtx.stop();

      springCtx.destroy();

   }

}

Here is the example camelContext.xml file:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

  <camelContext id="camel5" xmlns="http://camel.apache.org/schema/spring">

       <routeBuilder ref="myBuilder" />    

  </camelContext>

  <bean id="myBuilder" class="org.apache.camel.spring.example.test1.MyRouteBuilder"/>

</beans>

3 – Running Camel with Spring and Camel Maven plugin

The last option allows you to write and execute Camel routes without writing Java code at all. You will define your route in Spring’s application context file and use Camel’s Maven plugin to compile and run the code. Here is the application-context.xml file:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
  xmlns:util="http://www.springframework.org/schema/util"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd  
   http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">

  <camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
      <from uri="file://data/in" />
      <to uri="file://data/out" />
    </route>
  </camelContext>

</beans>

And this is the pom.xml file:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.esample.camel</groupId>
  <artifactId>camel-spring-maven</artifactId>
  <version>1.0.0.BUILD-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Using Camel for Simple file copy - with Spring integration</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>3.1.2.RELEASE</spring.version>
    <camel.version>2.10.2</camel.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-spring</artifactId>
      <version>${camel.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-maven-plugin</artifactId>
        <version>${camel.version}</version>
        <!-- the file must be in the classpath -->
        <configuration>
           <applicationContextUri>application-context.xml</applicationContextUri>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

To run the above example, execute the following command in the shell:

mvn clean compile camel:run

 

Building a Camel route to remote ActiveMQ

In this tutorial we will demonstrate how to create a simple Camel route which sends messages (based on a timer) to an ActiveMQ server.

So first of all, let’s start ActiveMQ and verify that it’s listening on the TCP port:

For example on a Windows machine:

C:\>netstat -an | find “61616”

TCP 0.0.0.0:61616 0.0.0.0:0 LISTENING

TCP [::]:61616 [::]:0 LISTENING

And now let’s create our simple route using Java DSL:

package com.sample;

import javax.jms.ConnectionFactory;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsComponent;
import org.apache.camel.impl.DefaultCamelContext;

 
public class App {
	public static void main(String args[]) throws Exception {
		CamelContext context = new DefaultCamelContext();
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
				"tcp://0.0.0.0:61616");
		context.addComponent("jms",
				JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
		context.addRoutes(new RouteBuilder() {
			public void configure() {
				from("timer:foo?period=1s").setBody(simple("Message at ${date:now:yyyy-MM-dd HH:mm:ss}")).to(
						"jms:queue:activemq/queue/TestQueue");
			}
		});
		context.start();
		Thread.sleep(10000);
		context.stop();
	}
}

As you can see, we are binding to an ActiveMQConnectionfactory listening on the address and port we have just checked.

Next the The JMSComponent allows messages to be sent to (or consumed from) a JMS Queue or Topic. The URI format for messages is:

jms:[queue:|topic:]destinationName[?options]

And next our route which uses as input the timer: component that is used to generate message exchanges when a timer fires. BTW You can only consume events from this endpoint. The body of the message includes a simple transformation of a text and the current date.

The destination is a jms queue named “TestQueue” which is available on ActiveMQ at the JNDI “activemq/queue/TestQueue” (If you don’t have it, ActiveMQ will create it for you on the fily).

Here is the pom.xml I have used to build the project:

<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.sample</groupId>
	<artifactId>camel-activemq</artifactId>
	<packaging>jar</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>camel-activemq</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-core</artifactId>
			<version>2.14.1</version>
		</dependency>

		<!-- camel jms and activemq -->
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-jms</artifactId>
			<version>2.14.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-camel</artifactId>
			<version>5.7.0</version>
		</dependency>

		<!-- embed ActiveMQ broker -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-broker</artifactId>
			<version>5.10.1</version>
		</dependency>

		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-client</artifactId>
			<version>5.10.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-pool</artifactId>
			<version>5.10.1</version>
		</dependency>


		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.10</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-simple</artifactId>
			<version>1.7.10</version>
		</dependency>

	</dependencies>
</project> 

Execute the Maven application as:

mvn clean install exec:java -Dexec.mainClass="com.sample.App

Finally check on the ActiveMQ console that messages have been received:

Source code for this example available at: https://github.com/fmarchioni/mastertheintegration/tree/master/camel/jms/camel-activemq

Simple Transformation with Camel

Camel provides many ways for data transformation. In this tutorial we will cover a simple example of data transformation by replacing the content of one file and writing a new file as result.

Data transformation in Camel is a broad term that covers two types of transformation:

  • Data format transformation: which means that the message body is transformed from one form to another. For example, a JSON Text file is formatted as CSV.
  • Data type transformation: which means that the data type of the message body is transformed from one type to another. For example a java.lang.String is transformed into a JMS Message

Let’s see a simple example of the first type (Data format transformation):

package com.sample;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;

public class SimpleTransform {

	public static void main(String args[]) throws Exception {
		CamelContext context = new DefaultCamelContext();
		context.addRoutes(new RouteBuilder() {
			public void configure() {
				from("file:C:\\camel\\in\\").process(new SimpleProcessor())
				.to("file:C:\\camel\\out\\");
			}
		});
		context.start();
		Thread.sleep(10000);
		context.stop();
	}

}

Within this simple Route, we are copying files from the folder C:\camel\in into the folder C:\camel\out and transforming the content using the SimpleProcessor class which follows here:

package com.sample;

import java.util.StringTokenizer;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;

public class SimpleProcessor implements Processor {
	public void process(Exchange exchange) throws Exception {
		String custom = exchange.getIn().getBody(String.class);
 	
		StringTokenizer st = new StringTokenizer(custom," ");
		StringBuilder csv = new StringBuilder();
		csv.append(custom.replaceAll("\n", "<br/>"));
	 
		exchange.getIn().setBody(csv.toString());
	}
}

The SimpleProcessor class, which implements org.apache.camel.Processor is merely replacing the carriage return (“\n”) with an HTML equivalent (“<br/”).

The Camel Exchange interface defines two methods for retrieving messages: getIn and getOut . The getIn method returns the incoming message, and the getOut method accesses the outbound message.

Using the Transform method of Java DSL

You can also use the Transform() method of the Java DSL to transform messages. By allowing the use of expressions, transform() permits great flexibility, and using expressions directly within the DSL can sometimes save time.

Letâ€s see the equivalent Route built using the Transform pattern:

CamelContext context = new DefaultCamelContext();
		context.addRoutes(new RouteBuilder() {
			public void configure() {
				from("file:D:\\camel\\in\\").transform(body().regexReplaceAll("\n", "<br/>"))
				.to("file:D:\\camel\\out\\");
			}
		});

Using Maven to set up a Camel project

In this Camel tutorial we will learn how to set up a basic Camel project using a Maven archetype which is based on Spring XML configuration file.

There are a few different approaches to configuring components and endpoints.

1. You can explicitly configure a Component using Java code as shown in this example:

context.addRoutes(new RouteBuilder() {
    public void configure() {
        from("test-jms:queue:test.queue").to("file://test");
    }
});

2. You can configure your Component or Endpoint instances in your Spring XML. When using Spring the CamelContext can be pre configured based on defined beans in spring XML.

The advantage of using Spring based Camel project is that you can build complex Camel routes declaratively, that is just by filling up the Camel Spring configuration file, named camel-context.xml

We will show here how to build a Spring based Camel project using a Maven Archetype:

mvn archetype:generate                    

  -DarchetypeGroupId=org.apache.camel.archetypes   

  -DarchetypeArtifactId=camel-archetype-spring    

  -DarchetypeVersion=1.5.0              

  -DgroupId=myGroupId                  

  -DartifactId=myArtifactId

Here is camel-context.xml which contains the route definition:

<?xml version="1.0" encoding="UTF-8"?>
 

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

  <camel:camelContext xmlns="http://camel.apache.org/schema/spring">
 
    <camel:route>
      <camel:from uri="file:src/data?noop=true"/>
      <camel:choice>
        <camel:when>
          <camel:xpath>/person/city = 'London'</camel:xpath>
          <camel:log message="UK message"/>
          <camel:to uri="file:target/messages/uk"/>
        </camel:when>
        <camel:otherwise>
          <camel:log message="Other message"/>
          <camel:to uri="file:target/messages/others"/>
        </camel:otherwise>
      </camel:choice>
    </camel:route>
  </camel:camelContext>

</beans>

What is doing this route is pretty simple: it takes (without removing them, as there is the noop=true option) as input XML files contained in the src/data folder.

These files are processed using an XPath expression which checks for the Path /person/city. If the path belongs to London, files are moved in the target/messages/uk folder. Otherwise files are moved in the target/messages/others folder.

Run the Maven project using:

mvn camel:run

Routing JMS messages to JBoss EAP 6 using Camel

This tutorial shows how you can connect to HornetQ embedded in JBoss EAP 6 using a Camel Route.

First of all, you need to create an application user on the application server. The user will be granted the guest role which by default has permission to send and consume messages on all JMS destinations, as you can see from this snippet:

<security-settings>

	<security-setting match="#">

		<permission type="send" roles="guest"/>

		<permission type="consume" roles="guest"/>

		<permission type="createNonDurableQueue" roles="guest"/>

		<permission type="deleteNonDurableQueue" roles="guest"/>

	</security-setting>

</security-settings>

So, launch the add-user.sh script contained in the JBOSS_HOME/bin folder and follow this transcript:

What type of user do you wish to add?

a) Management User (mgmt-users.properties)

b) Application User (application-users.properties)

(a): b

Enter the details of the new user to add.

Using realm ‘ApplicationRealm’ as discovered from the existing property files.

Username : jmsuser

Password :

Re-enter Password :

What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]: guest

About to add user ‘jmsuser’ for realm ‘ApplicationRealm’

Is this correct yes/no? yes

Done with user creation, now start the application server using a full configuration (or full-ha):

./standalone.sh -c standalone-full.xml

Now create a queue using a management instrument like the CLI:

jms-queue add --queue-address=ExampleQueue --entries=queue/exampleQueue,java:jboss/exported/jms/queue/exampleQueue

Building the Camel route

We have finished with the application server. Now it’s time to build our Camel route. We will use Spring XML file to build the route:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

	<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">

		<property name="environment">

			<props>

				<prop key="java.naming.factory.initial">org.jboss.naming.remote.client.InitialContextFactory

				</prop>

				<prop key="java.naming.provider.url">remote://localhost:4447</prop>

			</props>

		</property>

	</bean>

	<bean name="jms" class="org.apache.camel.component.jms.JmsComponent">

		<property name="connectionFactory" ref="authenticatedConnectionFactory" />

	</bean>

	<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">

		<property name="jndiTemplate">

			<ref bean="jndiTemplate" />

		</property>

		<property name="jndiName">

			<value>java:jms/RemoteConnectionFactory</value>

		</property>

	</bean>

	<bean id="authenticatedConnectionFactory"
		class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">

		<property name="targetConnectionFactory" ref="jmsQueueConnectionFactory" />
		<property name="username" value="jmsuser" />
		<property name="password" value="Password1!" />

	</bean>

	<camel:camelContext xmlns="http://camel.apache.org/schema/spring">

		<camel:route>

			<camel:from uri="timer:foo?period=1s" />

			<setBody>

				<simple>${body}Message at ${date:now:yyyy-MM-dd HH:mm:ss}</simple>

			</setBody>

			<camel:to uri="jms:queue:ExampleQueue" />

		</camel:route>

	</camel:camelContext>

</beans>

So what are ther key properties of this example: first of all you have to specify the server remote address:

<prop key="java.naming.provider.url">remote://localhost:4447</prop>

Next,we have to specify the name of the RemoteConnectionFactory to be used, which by default is java:jms/RemoteConnectionFactory

<property name="jndiName">
	<value>java:jms/RemoteConnectionFactory</value>
</property>

The, we have to specify the authenticatedConnectionFactory information using the user that we have formerly created on JBoss EAP:

<bean id="authenticatedConnectionFactory"

	class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">

	<property name="targetConnectionFactory" ref="jmsQueueConnectionFactory" />
	<property name="username" value="jmsuser" />
	<property name="password" value="Password1!" />

</bean>

And finally the Camel route: we are addressing messages from the Timer every second to the ExampleQueue. Mind that we are using a simple transformation to provide some body with the date of the messages

Find the complete project on Github