Tuesday, November 2, 2010

ActiveMQ - Les wilcards

I've discovered today an interesting new functionality on ActiveMQ.
This is the "Wildcards"

From a consumer point of view, it's possible thanks to some special characters to consume messages from several destinations (queues and topics) with only one consumer.

ActiveMQ cuts the destination's name in elements. Those elements are delimited by a dot (.).
For example, the below destinations have 3 elements :

- queue.france.rennes
- queue.france.paris

Here are the special characters:
- . used to separate elements
- * used to match one element
- > used to match one or all trailing elements

For example, to consume all message for France, we can use the below consumer

String brokerURI = ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerURI);
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue france = session.createQueue("*.france.*");
MessageConsumer consumer = session.createConsumer(france);
Message message = consumer.receive();


Queue france = session.createQueue("*.france.>");

Conversely, the messages producer can send one message to several destinations (then we talk about broadcast). This is n messages, stricly identicaly sent to n destinations.
This avoids to create multiple producers for doing the same sending.

We talk about composite destinations.

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue compositeDestination = session.createQueue("queue.france.rennes, queue.france.paris");
MessageProducer producer = session.createProducer(compositeDestination);
Message message = session.createStreamMessage() ;

Notice : we have several independant messages with excalty the same content (header and body).
Careful, a composite destination named "queue.france.*" will not send the messages to all the queues, but to a queue named "queue.france.*".

ActiveMQ wilcards are very powerful with a very simple syntax and use.
Careful, this is not JMS compliant at all, but 100% ActiveMQ specific.

Talk you later.

Sunday, March 14, 2010

Camel framework introduction - Part 1

The issue of systems integration
In IT domain, we often have lots of heterogeneous systems which don't understand each other (different technologies, different communcation protocols, etc.).
But this is required and this is a need to be able to link those systems together, because systems which are not connected to each other have limited interest.

This raises many issues as data format, routes, monitoring, etc.
For instance, how do we do to connect two applications, where the first one speak old school CSV file although the second one does only understand XML over JMS ?

Entreprise Integration Patterns
Apache Camel is an integration framework, which implements EIP (Entreprise Integration Pattern) defined by Gregor Hohpe & Bobby Woolf.
EIP are integration patterns which enable us to resolv issues with proven solutions.
Camel framework supports about 50 EIP.

Endpoints represent systems, physical ressources, virtual ressources, etc.
For example, we have endpoints HTTP, JMS, FTP to interface with different protocols, but also endpoints Direct, Seda to be able to communicate into a same Camel context (we'll be back to this point).
2 types of Endpoints :
  • provider
  • consumer

  • Invoices directory: file://invoices/032010
  • Invoices JMS queue: jms://zenika/invoices

A route (path between 2 endpoints) can be very simple, or very complex as well (data transformation, multicast, messages filters, etc).
Routes enable us to route messages from origin to destination.

Camel can configure routes with 2 methods :
  • XML XBean configuration
  • DSL using (Domain Specific Language)

For example, this is a read of a file from a directory, then the write of this file into an other directory (very simple route) :
XML Method

<beans xmlns="http://www.springframework.org/schema/beans"
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

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

DSL Method
1. Java packages to scan listing (camel-context.xml file)

<beans xmlns="http://www.springframework.org/schema/beans" xsi="http://www.w3.org/2001/XMLSchema-instance" 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 xmlns="http://camel.apache.org/schema/spring">

2. Routes coding

package com.zenika.camel.routes;

import org.apache.camel.builder.RouteBuilder;

public class InvoicesRoute extends RouteBuilder {

 public void configure() throws Exception {

DSL way is less verbose and simpler to maintain.
You only have to define Java packages to be scanned, and have to code routes in Java with the DSL.

This way is also type safe, and then much more secured than the XML way.
Indeed, it's impossible to code an operation which is not supported (compilation error).

Furthermore, all Camel syntax is fully supported in the XML way.

Camel context, heart of the framework
In order to use our route, we need a Camel context.
This context loads, configures and executes routes. This is the engine of the framework.
The framework is not dedicated to be used in standalone mode (except for testing purpose).
Camel must be used inside a container (ServiceMix, ActiveMQ, CXF or Tomcat).

Here is a simple example to create the Camel context :

package com.zenika.camel.test;

import org.apache.camel.CamelContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Tester {
 public static void main(String... args) throws Exception {

  BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/camel-context.xml");
  CamelContext camelContext = (CamelContext) beanFactory.getBean("camelContext");

  Thread.sleep(2000); // to have time to run the route

With those few code lines, we have set up a route which copies all files from the invoices/in to the invoices/out directory.

We'll see next time other endpoints (as the Direct), and routes more complex as well, with data transformation and routing logic.