Chapter 5. Using JMS

Although JBoss Messaging provides a JMS agnostic messaging API, many users will be more comfortable using JMS.

JMS is a very popular API standard for messaging, and most messaging systems provide a JMS API. If you are completely new to JMS we suggest you following the Sun JMS tutorial - a full JMS tutorial is out of scope for this guide.

JBoss Messaging also ships with a wide range of examples, many of which demonstrate JMS API usage. A good place to start would be to play around with the simple JMS Queue and Topic example, but we also provide examples for many other parts of the JMS API. A full description of the examples is available in Chapter 9, Examples.

In this section we'll go through the main steps in configuring the server for JMS and creating a simple JMS program. We'll also show how to configure and use JNDI, and also how to use JMS with JBoss Messaging without using any JNDI.

5.1. A simple ordering system

For this chapter we're going to use a very simple ordering system as our example. It's a somewhat contrived example because of its extreme simplicity, but it serves to demonstrate the very basics of setting up and using JMS.

We will have a single JMS Queue called OrderQueue, and we will have a single MessageProducer sending an order message to the queue and a single MessageConsumer consuming the order message from the queue.

The queue will be a durable queue, i.e. it will survive a server restart or crash. We also want to predeploy the queue, i.e. specify the queue in the server JMS configuration so it's created automatically without us having to explicitly create it from the client.

5.2. JMS Server Configuration

The file jbm-jms.xml on the server classpath contains any JMS Queue, Topic and ConnectionFactory instances that we wish to create and make available to lookup via the JNDI.

A JMS ConnectionFactory object is used by the client to make connections to the server. It knows the location of the server it is connecting to, as well as many other configuration parameters. In most cases the defaults will be acceptable.

We'll deploy a single JMS Queue and a single JMS Connection Factory instance on the server for this example but there are no limits to the number of Queues, Topics and Connection Factory instances you can deploy from the file. Here's our configuration:

<configuration xmlns="urn:jboss:messaging" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:jboss:messaging ../schemas/jbm-jms.xsd ">
    
    <connection-factory name="ConnectionFactory">
        <connector-ref connector-name="netty"/>
        <entries>
            <entry name="ConnectionFactory"/>           
        </entries>
    </connection-factory>
    
    <queue name="OrderQueue">
        <entry name="queues/OrderQueue"/>
    </queue>
    
</configuration> 
        

We deploy one ConnectionFactory called ConnectionFactory and bind it in just one place in JNDI as given by the entry element. ConnectionFactory instances can be bound in many places in JNDI if you require.

Note

The JMS connection factory references a connector called netty. This is a reference to a connector object deployed in the main core configuration file jbm-configuration.xml which defines the transport and parameters used to actually connect to the server.

5.3. JNDI configuration

When using JNDI from the client side you need to specify a set of JNDI properties which tell the JNDI client where to locate the JNDI server, amongst other things. These are often specified in a file called jndi.properties on the client classpath, or you can specify them directly when creating the JNDI initial context. A full JNDI tutorial is outside the scope of this document, please see the Sun JNDI tutorial for more information on how to use JNDI.

For talking to the JBoss JNDI Server, the jndi properties will look something like this:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://myhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces                        
        

Where myhost is the hostname or IP address of the JNDI server. 1099 is the port used by the JNDI server and may vary depending on how you have configured your JNDI server.

In the default standalone configuration, JNDI server ports are configured in the jbm-jboss-beans.xml file where the JNDIServer bean is confgured, here's a snippet from the file:

<bean name="JNDIServer" class="org.jnp.server.Main">
    <property name="namingInfo">
        <inject bean="Naming"/>
    </property>
    <property name="port">1099</property>
    <property name="bindAddress">localhost</property>
    <property name="rmiPort">1098</property>
    <property name="rmiBindAddress">localhost</property>
</bean>                        
        

Note

If you want your JNDI server to be available to non local clients make sure you change it's bind address to something other than localhost!

5.4. The code

Here's the code for the example:

First we'll create a JNDI initial context from which to lookup our JMS objects:

InitialContect ic = new InitialContext();

Now we'll look up the connection factory:

ConnectionFactory cf = (ConnectionFactory)ic.lookup("/ConnectionFactory");

And look up the Queue:

Queue orderQueue = (Queue)ic.lookup("/queues/OrderQueue");

Next we create a JMS connection using the connection factory:

Connection connection = cf.createConnection();

And we create a non transacted JMS Session, with AUTO_ACKNOWLEDGE acknowledge mode:

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

We create a MessageProducer that will send orders to the queue:

MessageProducer producer = session.createProducer(orderQueue);

And we create a MessageConsumer which will consume orders from the queue:

MessageConsumer consumer = session.createConsumer(orderQueue);

We make sure we start the connection, or delivery won't occur on it:

connection.start();

We create a simple TextMessage and send it:

TextMessage message = session.createTextMessage("This is an order");
producer.send(message);

And we consume the message:

TextMessage receivedMessage = (TextMessage)consumer.receive();
System.out.println("Got order: " + receivedMessage.getText());
        

It's as simple as that. For a wide range of working JMS examples please see the examples directory in the distribution.

5.5. Directly instantiating JMS Resources without using JNDI

Although it's a very common JMS usage pattern to lookup JMS Administered Objects (that's JMS Queues, Topics and Connection Factories) from JNDI, in some cases a JNDI server is not available and you still want to use JMS, or you just think "Why do I need JNDI? Why can't I just instantiate these objects directly?"

With JBoss Messaging you can do exactly that. JBoss Messaging supports the direct instantiation of JMS Queue, Topic and Connection Factory instances, so you don't have to use JNDI at all.

For a full working example of direct instantiation please see the JMS examples in Chapter 9, Examples.

Here's our simple example, rewritten to not use JNDI at all:

We create the JMS ConnectionFactory object directly, note we need to provide connection parameters and specify which transport we are using, for more information on connectors please see Chapter 14, Configuring the Transport.

              
TransportConfiguration transportConfiguration = 
                     new TransportConfiguration(NettyConnectorFactory.class.getName());                
ConnectionFactory cf = new JBossConnectionFactory();
        

We create the JMS Queue Object directly:

Queue orderQueue = new JBossQueue("OrderQueue");

Next we create a JMS connection using the connection factory:

Connection connection = cf.createConnection();

And we create a non transacted JMS Session, with AUTO_ACKNOWLEDGE acknowledge mode:

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

We create a MessageProducer that will send orders to the queue:

MessageProducer producer = session.createProducer(orderQueue);

And we create a MessageConsumer which will consume orders from the queue:

MessageConsumer consumer = session.createConsumer(orderQueue);

We make sure we start the connection, or delivery won't occur on it:

connection.start();

We create a simple TextMessage and send it:

TextMessage message = session.createTextMessage("This is an order");
producer.send(message);

And we consume the message:

TextMessage receivedMessage = (TextMessage)consumer.receive();
System.out.println("Got order: " + receivedMessage.getText());
        

5.6. Setting The Client ID

This represents the client id for a JMS client and is needed for creating durable subscriptions. It is possible to configure this on the connection factory and can be set via the client-id element. Any connection created by this connection factory will have this set as its client id.

5.7. Setting The Batch Size for DUPS_OK

When the JMS acknowledge mode is set to DUPS_OK it is possible to configure the consumer so that it sends the acknowledgements in batches rather that one at a time, saving valuable bandwidth. This can be configured via the connection factory via the dups-ok-batch-size element and is set in bytes. The default is 1024 * 1024.

5.8. Setting The Transaction Batch Size

When receiving messages in a transaction it is possible to configure the consumer to send acknowledgements in batches rather than individually saving valuable bandwidth. This can be configured on the connection factory via the transaction-batch-size element and is set in bytes. The default is 1024 * 1024.