Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Adding section on the Camel Maven plugin
Wiki Markup
{warning}This tutorial is a work in progress.  Any additions, corrections or feedback very much appreciated.  Please use the comment box if making a correction explaining the mistake.{warning}

h1. Preface
This tutorial aims to guide the reader through the stages of creating a project which uses Camel to facilitate the routing of messages from a JMS queue to a [Spring|http://www.springramework.org] service.  The route works in a synchronous fashion returning a response to the client.

{toc}

h1. TODOs
* Embellish the tutorial with more detail, explaining at each stage what is happening.
* Link to specific sections of Camel documentation when referring to components.
* -Attach completed example project.-
* Explain in more detail what is happening when the JMS component is being defined in camel-server.xml
* Show how logging can be introduced to monitor exchange body contents.
* Detail how time-outs can be configured.
* Show how to catch lost messages.
* Show how to use a wiretap.
* Can we make this ActiveMQ embedded so that it does not require the reader to download and start it manually?

h1. Prerequisites

This tutorial uses ActiveMQ as the JMS broker. [Download|http://activemq.apache.org/download.html] and extract it, then start the broker using script in the bin directory.
It is assumed that the reader is familiar with Spring (including v2.5 features) and Maven.

h1. Create the Camel Project

{info}
For the purposes of the tutorial a single Maven project will be used for both the client and server.  Ideally you would break your application down into the appropriate components.
{info}

{code}
Warning

This tutorial is a work in progress. Any additions, corrections or feedback very much appreciated. Please use the comment box if making a correction explaining the mistake.

Preface

This tutorial aims to guide the reader through the stages of creating a project which uses Camel to facilitate the routing of messages from a JMS queue to a Spring service. The route works in a synchronous fashion returning a response to the client.

Table of Contents

TODOs

  • Embellish the tutorial with more detail, explaining at each stage what is happening.
  • Link to specific sections of Camel documentation when referring to components.
  • Attach completed example project.
  • Explain in more detail what is happening when the JMS component is being defined in camel-server.xml
  • Show how logging can be introduced to monitor exchange body contents.
  • Detail how time-outs can be configured.
  • Show how to catch lost messages.
  • Show how to use a wiretap.
  • Can we make this ActiveMQ embedded so that it does not require the reader to download and start it manually?

Prerequisites

This tutorial uses ActiveMQ as the JMS broker. Download and extract it, then start the broker using script in the bin directory.
It is assumed that the reader is familiar with Spring (including v2.5 features) and Maven.

Create the Camel Project

Info

For the purposes of the tutorial a single Maven project will be used for both the client and server. Ideally you would break your application down into the appropriate components.

Code Block
mvn archetype:create -DgroupId=org.example -DartifactId=CamelWithJmsAndSpring

Update the POM with Dependencies

Code Block

{code}

h2. Update the POM with Dependencies

{code}
<dependencies>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.14</version>
  </dependency>
  <dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.0.0</version>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-core</artifactId>
    <version>1.3.0</version>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-jms</artifactId>
    <version>1.3.0</version>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-spring</artifactId>
    <version>1.3.0</version>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit-dep</artifactId>
    <version>4.4</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Writing the Server

Create the Spring Service

...

{code}

h1. Writing the Server

h2. Create the Spring Service

For this example the Spring service on the server will be a simple multiplier which trebles in the received value.  The classes should reside in the package {{org.example.server}}.

...



{code
:title
=org/example/server/Multiplier.java
}
public interface Multiplier {

  /**
   * Multiplies the given number by a pre-defined constant.
   *
   * @param originalNumber The number to be multiplied
   * @return The result of the multiplication
   */
  int multiply(int originalNumber);

}
Code Block
title
{code}

{code:title=org/example/server/Treble.java
}
@Service(value="multiplier")
public class Treble implements Multiplier {

  /* (non-Javadoc)
   * @see org.example.server.Multiplier#multiply(int)
   */
  public int multiply(final int originalNumber) {
    return originalNumber * 3;
  }

}
{code}

Using Spring annotations the bean is defined with the name *multiplier*.

...



h2. Define the Camel Routes

...



{code
:title
=org/example/server/ServerRoutes.java
}
public class ServerRoutes extends RouteBuilder {

  /* (non-Javadoc)
   * @see org.apache.camel.builder.RouteBuilder#configure()
   */
  @Override
  public void configure() throws Exception {
    from("jms:queue:numbers").beanRef("multiplier", "multiply");
  }

}
{code}

This defines a Camel route _from_ the JMS queue named *numbers* _to_ the Spring bean named *multiplier*.  Camel will create a consumer to the JMS queue which forwards all received messages onto the the Spring bean, using the method named *multiply*.

h2.

...

 Configure Spring

...



The Spring config file is placed under {{META-INF/spring}} as this is the default location used by the [Camel Maven Plugin], which we will later use to run our server.

...



{code
:xml
xml
|title
=META-INF/spring/camel-server.xml
}
<?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:context="http://www.springframework.org/schema/context"
	xmlns:camel="http://activemq.apache.org/camel/schema/spring"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
		http://www.springframework.org/schema/context
		http://schemas.leadx.com/spring/spring-context-2.5.xsd
		http://activemq.apache.org/camel/schema/spring
		http://activemq.apache.org/camel/schema/spring/camel-spring-1.3.0.xsd">
		
	<context:component-scan base-package="org.example.server" />

	<camel:camelContext id="camel">
		<camel:package>org.example.server</camel:package>
	</camel:camelContext>

	<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
		<property name="connectionFactory">
			<bean class="org.apache.activemq.ActiveMQConnectionFactory">
				<property name="brokerURL" value="tcp://localhost:61616" />
			</bean>
		</property>
	</bean>

</beans>
{code}

|component-scan

...

| Defines the package to be scanned for Spring stereotype annotations, in this case, to load the "multiplier" bean

...

 |
|camel-context

...

 | Defines the package to be scanned for Camel routes.  Will find the {{ServerRoutes}} class and create the routes contained within it

...

 |
|jms bean

...

| Creates the Camel JMS

...

Run the Server

Code Block
title
 component |

h2. Run the Server
{code:title=org/example/server/CamelServer.java
}
public class CamelServer {

  public static void main(final String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("camel-server.xml");
  }

}
{code}

The {{main}} method can then be executed to start the server.

h1.

...

 Creating the Client

...



We will initially create a client by directly using {{CamelTemplate}}.  We will later create a client which uses Spring remoting to hide the fact that messaging is being used.

...

Code Block
1xml
title


{code:title=camel-client.xml|xml
}
<?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://activemq.apache.org/camel/schema/spring"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
		http://activemq.apache.org/camel/schema/spring
		http://activemq.apache.org/camel/schema/spring/camel-spring-1.3-SNAPSHOT.xsd">

	<camel:camelContext id="camel" />
	<camel:template id="camelTemplate" />
	
	<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
		<property name="connectionFactory">
			<bean class="org.apache.activemq.ActiveMQConnectionFactory">
				<property name="brokerURL" value="tcp://localhost:61616" />
			</bean>
		</property>
	</bean>
</beans>
{code}

The client will not use the [Camel Maven Plugin] so the Spring XML has been placed in _src/main/resources_ so not to conflict with the server configs.

...

camelContext

The Camel context is defined but does not contain any routes

tempate

The CamelTemplate is used to place messages onto the JMS queue

jms bean

This initialises the Camel JMS component, allowing us to place messages onto the queue

Code Block
title


|camelContext |The Camel context is defined but does not contain any routes|
|tempate |The {{CamelTemplate}} is used to place messages onto the JMS queue|
|jms bean |This initialises the Camel JMS component, allowing us to place messages onto the queue|

{code:title=org/example/client/CamelClient.java
}
public class CamelClient {

  public static void main(final String[] args) {

    ApplicationContext context = new ClassPathXmlApplicationContext("camel-client.xml");
    CamelTemplate<JmsExchange> camelTemplate = (CamelTemplate) context.getBean("camelTemplate");

    int response = (Integer)camelTemplate.sendBody("jms:queue:numbers",
      ExchangePattern.InOut,
      22
    );

    Assert.assertEquals(66, response);
    System.out.println(response);

  }
}
{code}

The {{CamelTemplate}} is retrieved from a Spring {{ApplicationContext}} and used to manually place a message on the "numbers" JMS queue.  The exchange pattern (*ExchangePattern.InOut*) states that the call should be synchronous, and that we will receive a response.  We then assert that the response is three times the value of the original.

...



Before running the client be sure that both the ActiveMQ broker and the {{CamelServer}} are running.

...



h1. Using Spring Remoting

...



[Spring Remoting] "eases the development of remote-enabled services".  It does this by allowing you to invoke remote services through your regular Java interface, masking that a remote service is being called.

...

Code Block
1xml
title


{code:title=camel-client-remoting.xml|xml
}
<?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://activemq.apache.org/camel/schema/spring"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
		http://activemq.apache.org/camel/schema/spring
		http://activemq.apache.org/camel/schema/spring/camel-spring-1.3-SNAPSHOT.xsd">

	<camel:camelContext id="camel" />
	
	<camel:proxy
		id="multiplier"
		serviceInterface="org.example.server.Multiplier"
		serviceUrl="jms:queue:numbers"
	/>
	
	<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
		<property name="connectionFactory">
			<bean class="org.apache.activemq.ActiveMQConnectionFactory">
				<property name="brokerURL" value="tcp://localhost:61616" />
			</bean>
		</property>
	</bean>
</beans>
{code}

First we create a new Spring config file.  This has a few changes made from _camel-client.xml_.  Firstly the Camel template has been removed, as it will not be used.  Secondly a *proxy* is defined.  This will create a proxy service bean for you to use to make the remote invocations.  The *serviceInterface* property details which Java interface is to be implemented by the proxy.  *serviceUrl* defines where messages sent to this proxy bean will be directed.  Here we define the JMS endpoint with the "numbers" queue we used when working with Camel template directly.  The value of the *id* property is the name that will be the given to the bean when it is exposed through the Spring {{ApplicationContext}}.  We will use this name to retrieve the service in our client.  I have named the bean _multiplierProxy_ simply to highlight that it is not the same multiplier bean as is being used by {{CamelServer}}.  They are in completely independent contexts and have no knowledge of each other.  As you are trying to mask the fact that remoting is being used in a real application you would generally not include proxy in the name.

...



{code
:title
=org/example/client/CamelClientRemoting.java
}
public class CamelClientRemoting {

  public static void main(final String[] args) {

    ApplicationContext context = new ClassPathXmlApplicationContext("camel-client-remoting.xml");
    Multiplier multiplier = (Multiplier) context.getBean("multiplierProxy");

    int response = multiplier.multiply(22);

    Assert.assertEquals(66, response);
    System.out.println(response);
  }

}
{code}

Again, the client is similar to the original client, but with some important differences.

...



# The Spring context is created with the new _camel-client-remoting.xml

...

_
# We retrieve the proxy bean instead of a {{CamelTemplate}}.  In a non-trivial example you would have the bean injected as in the standard Spring manner.

...


# The multiply method is then called directly.  In the client we are now working to an interface.  There is no mention of Camel or JMS inside our Java code.

...

TODO: Using the Camel Maven Plugin

The Camel Maven Plugin.

TODO: Detail how to use the Maven plugin as an alternative to CamelServer

TODO: Testing

...



h1. Using the Camel Maven Plugin

{note} 
Require an explanation here about the deployment options of Camel based projects (e.g. running through Maven, creating a host application, deploying into ActiveMQ) and the pros/cons of each. 
{note}

The [Camel Maven Plugin] allows you to run your Camel routes directly from Maven.  This negates the need to create a host application, as we did with Camel server, simply to start up the container.  This can be very useful during development to get Camel routes running quickly.

{code|title=pom.xml}
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-maven-plugin</artifactId>
    </plugin>      
  </plugins>
</build>
{code}

All that is required is a new plugin definition in your Maven POM.  As we have already placed our Camel config in the default location (camel-server.xml has been placed in META-INF/spring/) we do not need to tell the plugin where the route definitions are located.  Simply run {{mvn camel:run}}.

h1. TODO: Testing

TODO: Detail how to unit and integration test this example.