Friday, January 26, 2007

Java Message Service

I was pushing the Java Message Service (JMS) at work today for a few projects. I think people have a huge misconception about JMS. By using a broker and having entities communicate using messages, you completely decouple your delivery, synchronization and communication mechanizm from your business logic.

If I'm a message producer and I open up a connection to a broker and send a message, my code is no different if that broker is:
  1. on the same machine
  2. in the same VM
  3. On a machine across the internet
  4. really a cluster of brokers performing load balancing and fail over.
This means your code is already scalable even if you don't need it. Also, it means you don't have to worry about guaranteed delivery, persistence, and recovery. Its all done for you. You just write the components that send and receive code. Plus, with some of the great JMS brokers like ActiveMQ, you have lots of options in transport and optimization.

Take this example, here we have my producer



/** This is an inVM connection. This could also be a connection
* to a broker on another machine or a cluster of brokers. T
* The only thing that would change is this string.
*/
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
Connection connection = factory.createQueueConnection();
connection.start();
QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

// go ahead and create the queue, if it already exists, we will
// just get the one that is there
Queue queue = session.createQueue("MyQueue");
// Creates a message producer to send messages to the queue
MessageProducer producer = session.createProducer(queue);

// create a generic message, you have several to choose from
Message message = session.createMessage();
// normally you would want to actually put stuff in the message

// send the message
producer.send(message);

// send more messages if you want
// cleanup
producer.close();
session.close();
connection.stop();






And here is my reciever



import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import org.apache.activemq.ActiveMQConnectionFactory;

public class MyMessageConsumer implements MessageListener{

QueueConnectionFactory factory = null;
QueueConnection connection = null;
Queue queue = null;
QueueSession session = null;
MessageConsumer consumer = null;

/**
* Opens a connection to the broker, creates a queue session and starts
* consuming messages. The nice thing about active MQ
* that if you are using an inVM broker, you don't actually have to start it.
* It will be created when the first person opens a connection.
* @throws Exception
*/
public void start() throws Exception
{

/** This is an inVM connection. This could also be a connection
* to a broker on another machine or a cluster of brokers. T
* The only thing that would change is this string.
*/
factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");

connection = factory.createQueueConnection();

session = connection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);

// go ahead and create the queue, if it already exists, we will
// just get the one that is there
queue = session.createQueue("MyQueue");

// create a cosumer and add ourselves as the message listener.
// the consumer abstracts threading and all
consumer = session.createConsumer(queue);
consumer.setMessageListener(this);

// even though we have created a consumer, messages won't be delivered
// to us until we start the connetion it belongs to.
connection.start();
}

public void stop() throws Exception
{
connection.stop();
connection.close();
}

/**
* Callback methoid from our queue session
* when a message arrives.
*/
public void onMessage(Message message) {

try {
System.out.println("Hey! I got a message");
// let the broker know we have the message. If we didn't do this
// the broker would assume something bad happened and redeliver.
message.acknowledge();
}
catch (Exception jms)
{
jms.printStackTrace();
}
}
}


Pretty simple. My producer opens a connection to the broker and publishes a message to the Queue "MyQueue". My receiver creates a consumer on the queue "MyQueue" and sets itself as the message listener. When it receives a message it prints out a string.

In this example the broker is in the same vm as the producer and consumer. To have a broker running on a different machine or to have either the consumer or producer on different machines or to even have a cluster of brokers requires virtually no change to this code. That means this code is scalable from a single JavaVM to 100 servers clustered. Talk about planning for the future. Also note that I in any of my code I don't have to worry about synchronization, threading, and concurrently. Its all abstracted from my by the broker and the JMS clients.

So why don't people use Message Driven Architecture more often? I think a lot of the reason is the learning curve. It is a new methodology and those are difficult to learn. But once you do it makes life so much easer. So go learn JMS, it will make your life easier.

No comments: