JMS Provider

JBoss contains its own JMS provider, called JBossMQ. This is a JMS 1.0.2-compliant JMS provider, and it may be used for all ordinary JMS programming, i.e. writing stand alone JMS-based clients.

To understand how JMS works in JBoss, one has to have an understanding of some of the concepts and terminology used in JMS. The best way to get it is to read the specification, but we also give a short JMS introduction below.

Short introduction to JMS

JMS is centered around the concept of a destination. When you send a message, you do not send it directly to the recipient(s) interested in the message; you send it to a destination, instead. Then, anyone interested in the message has to either connect to the destination and get the message or to set up a subscription at the destination; in any case, messages sent to the destination get forwarded to the recipient.

In JMS there are two types of destinations: topics and queues. These have different semantics:

  • A message sent to a topic may be received by multiple parties. Publishing with topics allows one-to-many or many-to-many communication channels. In this case, the originator of a message is called the publisher, while the recipient is called a subscriber.

  • A queue, on the other hand, will only allow the delivery of a message to a single party. A sender places the message in the queue, the receiver gets it off the queue and the message disappears from the queue. The first receiver to fetch the message will get it, while everyone else will not.

To be able to send or receive messages you have to obtain a JMS connection, which is an abstract way to get in contact with the JMS provider. Once you have obtained a connection, you will have to establish a session. Once the session is established, you either create a publisher/sender to send messages or a subscriber/receiver to receive them.

At runtime, the publisher or subscriber have to be associated with a topic destination, while the sender or receiver have to be associated with a queue destination.

In general, the way to operate within the JMS framework is as follows:

  1. Get the JNDI initial context.

  2. Look up a connection factory (for the right type of destination, topic or queue).

  3. Obtain a connection from the factory.

  4. Create a session tied to the connection.

  5. Look up the destination (topic or queue).

  6. Create a message producer or a message consumer (specific for the topic or the queue).

To get a connection to the provider and to get a destination to associate with your publisher/sender or your subscriber/receiver you have to use provider-specific parameters.

You get a connection factory or a destination by looking up these objects by way of JNDI, which is standard for any JMS provider, but the name to use in this lookup is specific to the provider being used. You will therefore have to learn what to specify for each and every different JMS provider you use, including JBossMQ.

Any configuration of the JMS provider, such as connection characteristics, what destinations are available and so forth, are also specific to the provider being used. For example, there is no standard way to create new destinations.

Configuration

While using a JMS provider, there are three items that involve provider-specific stuff:

  • Getting the JNDI initial context

  • The name of the connection factories to use

  • Administration and naming conventions for destinations.

JBoss comes with its own JNDI implementation. For a plain vanilla JMS client, you configure and look up the initial context as you do it for your other J2EE client components.

The JMS-specific stuff is tied to the JBoss JMS provider, JBossMQ. JBossMQ is configured via the XML file jbossmq.xml, found in the directory conf/default of the JBoss distribution tree.

[2.4.1. The latest JBossMQ does not use the jbossmq.xml file any more. All of its components have been added to the jboss.jcml file as MBeans, instead.]

There are basically three things for which one needs to use the jbossmq.xml file:

  • Adding new destinations

  • Configuring users

  • Obtaining the names of the available connection factories

Adding new destinations

Destinations are added to the jbossmq.xml file.

[2.4.1. The file used is jboss.jcml, instead.]

There are several default destinations already in the file, so it's easy to see how to do it. Say you need a topic destination named spool; then you would add the following entry to jbossmq.xml:

Figure 6.2. Defining a destination in jbossmq.xml

<Topic><Name>spool</Name></Topic>

[2.4.1. The entry used in jboss.jcml is an MBean, instead.]

Figure 6.3. [2.4.1] Defining a destination in jboss.jcml

<mbean code="org.jbossmq.server.TopicManager"
  name="JBossMQ:service=Topic,name=spool"/>

Destinations may also be added on the fly.

One way of doing it is through the JMX HTML management GUI. If you created a plain vanilla JBoss distribution, it's normally accessible through the port 8082, i.e. http://localhost:8082. Follow the link to Service=JMSServer under JMS and fill in either the form field newTopic or the form field newQueue.

[2.4.1. Follow the link to Service=Server under JBossMQ and fill in either the form field createTopic or the form field createQueue.]

It is also possible to add destinations programmatically. This is done by accessing the MBean and invoking the methods newTopic or newQueue.

[2.4.1. This is done by accessing the MBean and invoking the methods createTopic or createQueue.]

An example is given in the section called “Examples”.

[2.4.1. Destinations created in this way will be transient, living only as long as the server is up. When the JBoss server is rebooted, the dynamically created destinations will be gone.]

All destinations in JBossMQ have a prefix that indicates the type of the destination; for topics it is topic and for queues it is queue. To look up the testTopic destination you will therefore need to lookup the name topic/testTopic.

Managing users

In JMS it is possible to associate a connection with a user. Unfortunately there is no known way of restricting access to JBossMQ or to a particular destination to a given user.

For most the most part, creating users is not needed in JBossMQ, except if one wants to have a durable topic subscriber. In that case, a user is required.

Users are added through the file jbossmq.xml found in directory conf/default. User john is added like this:

Figure 6.4. Adding a JBossMQ user

<User>
  <Name>john</Name>
  <Password>needle</Password>
  <Id>DurableSubscriberExample</Id>
</User>

[2.4.1. Users are instead configured in the file jbossmq-state.xml, also found in directory conf/default. The entries look the same as those in jbossmq.xml.]

Connection Factories

JBossMQ includes several different connection factories for topics and queues, each of them with its own special characteristics. When looking up a connection factory through JNDI, one needs to know the name to use. All the available factories and their properties, including their names, are listed as separate entries in the file jbossmq.xml.

[2.4.1. The available factories are listed in file jboss.jcml, instead.]

There are three types of connection factories, depending on the communication protocol used:

OIL

A fast two-way socket-based communication protocol. It's the default.

UIL

Protocol multiplexed over one socket. Good to have when going through firewalls or when the client is not able to look up the server IP address correctly.

RMI

The first protocol written, stable but slow.

[2.4.0. A fourth type of connection factory was introduced at this time:

INVM

Very fast in-VM protocol that does not use sockets. Available when the client is in the same virtual machine as JBossMQ.]

The following table lists all the JMS connection factories available in JBoss:

Table 6.2. JMS Connection Factories in JBoss

Destination typeJNDI nameFactory type
TopicTopicConnectionFactoryOIL
QueueQueueConnectionFactoryOIL
TopicXATopicConnectionFactoryOIL. Supports XA transaction
QueueXAQueueConnectionFactoryOIL. Supports XA transaction
TopicUILTopicConnectionFactoryUIL
QueueUILQueueConnectionFactoryUIL
TopicUILXATopicConnectionFactoryUIL. Supports XA transaction
QueueUILXAQueueConnectionFactoryUIL. Supports XA transaction
TopicRMITopicConnectionFactoryRMI
QueueRMIQueueConnectionFactoryRMI
TopicRMIXATopicConnectionFactoryRMI. Supports XA transaction
QueueRMIXAQueueConnectionFactoryRMI. Supports XA transaction

[2.4.0. Besides all the same connection factories available before, there were some new ones introduced for this version of JBoss. The following table lists all the additional JMS connection factories available.]

Table 6.3. [2.4.0] Additional JMS Connection Factories

Destination typeJNDI nameFactory type
Topicjava:/INVMTopicConnectionFactoryINVM
Queuejava:/INVMQueueConnectionFactoryINVM
Topicjava:/INVMXATopicConnectionFactoryINVM. Supports XA transaction
Queuejava:/INVMXAQueueConnectionFactoryINVM. Supports XA transaction

[2.4.1. The names have changed. The same factory is now used for both topic- and queue-based connections. The following table lists all the JMS connection factories available in this version of JBoss.]

Table 6.4. [2.4.1] JMS Connection Factories

Destination typeJNDI nameFactory type
Topic/QueueRMIConnectionFactoryRMI
Topic/QueueRMIXAConnectionFactoryRMI. Supports XA transaction
Topic/Queuejava:/ConnectionFactoryINVM
Topic/Queuejava:/XAConnectionFactoryINVM. Supports XA transaction
Topic/QueueConnectionFactoryOIL
Topic/QueueXAConnectionFactoryOIL. Supports XA transaction
Topic/QueueUILConnectionFactoryUIL
Topic/QueueUILXAConnectionFactoryUIL. Supports XA transaction

Advanced JMS Configuration in JBoss

The previous section explained the basic configuration tasks needed to get going with JBossMQ. In this section we will go over other configuration customizations that are possible with JBossMQ.

The JMS Persistence Manager

A JBossMQ Persistence Manager (PM) is responsible for storing JMS messages marked as persistent, to allow the recovery of the persistent messages if the server suffers a failure. The persistent JMS messages are stored in a simple log file, which is why this type of PM is called Logged.

[2.4.1. Now, the persistence of messages can be accomplished in different ways, each one with its advantages and drawbacks.]

Table 6.5. [2.4.1] Available JMS Persistence Managers

PM NameAdvantagesDisadvantages
FileVery StableNot fast
RollingloggedVery FastVery new, somewhat unproven
JDBCShould provide better stability/scalabilityHas JDBC overhead
LoggedFastLog files grow without bound, memory management problems during recovery

[2.4.1. The default Persistence Manager is the File Persistence Manager. You can change it, but you must have one and only one PM configured for one instance of the JMS server.]

The Persistence Manager can be configured in file jbossmq.xml.

[2.4.1. Now the type of Persistence Manager you wish to use can be configured in the jboss.jcml file.]

Setting up the Logged Persistence Manager

The Logged Persistence Manager is the original one.

[2.4.1. As it was developed just as a hack to get message persistence working in JBoss, its use is not recommended anymore, now that there are better options.]

Figure 6.5. Definition of the Logged Persistence Manager in jbossmq.xml

<PersistenceManager>
  <DataDirectory>../../db/jbossmq/</DataDirectory>
</PersistenceManager>

Figure 6.6. [2.4.1] Definition of the Logged Persistence Manager in jboss.jcml

<mbean code="org.jboss.mq.pm.logged.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="DataDirectory">../../db/jbossmq/</attribute>
</mbean>

The Logged Persistence Manager allows you to specify the following attributes when setting up the configuration:

Table 6.6. Settable attributes of the Logged Persistence Manager

AttributeDescription
DataDirectoryPath to the directory the Persistence Manager will use to store its persistent data files. If a relative path is specified, it is considered relative to the jbossmq.xml file. [2.4.1. Relative to the jboss.jcml file.]

[2.4.1] Setting up the File Persistence Manager

The File Persistence Manager uses the concept of one file per persisted message. This method of message persistence is not the most effective, performance-wise, but it is very stable.

The File Persistence Manager is the default one enabled with a standard JBoss distribution. If you look at the jboss.jcml file you will see an XML section that looks like this:

Figure 6.7. [2.4.1] Definition of the File Persistence Manager in jboss.jcml

<mbean code="org.jboss.mq.pm.file.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="DataDirectory">../../db/jbossmq/</attribute>
</mbean>

The File Persistence Manager allows you to specify the following attributes when setting up the MBean configuration:

Table 6.7. [2.4.1] Settable MBean attributes of the File Persistence Manager

AttributeDescription
DataDirectoryPath to the directory the Persistence Manager will use to store its persistent data files. If a relative path is specified, it is considered relative to the jboss.jcml file.

[2.4.1] Setting up the Rollinglogged Persistence Manager

The Rollinglogged Persistence Manager is the most recently created but it has very promising performance characteristics. It uses log files to persist multiple messages, so there isn't much of the I/O overhead incurred when creating a file.

Figure 6.8. [2.4.1] Definition of the Rollinglogged Persistence Manager

<mbean code="org.jboss.mq.pm.rollinglogged.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="DataDirectory">../../db/jbossmq/</attribute>
</mbean>

The Rollinglogged Persistence Manager allows you to specify the following attributes when setting up the MBean configuration:

Table 6.8. [2.4.1] Settable MBean attributes of the Rollinglogged Persistence Manager

AttributeDescription
DataDirectoryPath to the directory the Persistence Manager will use to store its persistent data files. If a relative path is specified, it is considered relative to the jboss.jcml file.

[2.4.1] Setting up the JDBC Persistence Manager

The JDBC Persistence Manager uses database tables to store the messages. It requires a JBoss-configured DataSource to access the database.

Figure 6.9. [2.4.1] Definition of the JDBC Persistence Manager in jboss.jcml

<mbean code="org.jboss.mq.pm.jdbc.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="JmsDBPoolName">DefaultDS</attribute>
</mbean>

The JDBC Persistence Manager allows you to specify the following attributes when setting up the MBean configuration:

Table 6.9. [2.4.1] Settable MBean attributes of the JDBC Persistence Manager

AttributeDescription
JmsDBPoolNameThe JNDI name of the datasource to be used.

Examples

As mentioned above, there are basically three things that are specific to the JMS provider being used and needed to get JMS up and working: configuring the JNDI initial context, the names of the connection factories and the naming conventions used for destinations.

When writing production code the best thing to do, of course, is to isolate the provider-specific stuff to make it easier to move between different JMS providers. The examples presented here, however, are not focused on writing production code, but on explaining what needs to be done to work with JBossMQ.

One nice way of configuring JNDI is through the property file jndi.properties. By filling such a file in with the correct values and including in the classpath the directory where it is placed, it is very easy to obtain the correct initial context.

To do so, create a file named jndi.properties with the following lines:

Figure 6.10. Client jndi.properties file example

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

Place it in a directory somewhere and make the directory part of your classpath.

If done this way, getting the initial context is simple:

Figure 6.11. Getting the initial context

// Get the initial context
Context context = new InitialContext();

In some situations, however, it might be better to configure JNDI manually; for example, when the environment in which the class is running already has a configured initial context, but that's not the one you want to use.

This is done by filling in a few properties in a Hashtable and instantiating the InitialContext with the hash table, like this:

Figure 6.12. Getting the initial context with specified properties

// Populate with needed properties
Hashtable props = new Hashtable();
props.put(Context.INITIAL_CONTEXT_FACTORY,
  "org.jnp.interfaces.NamingContextFactory");
props.put(Context.PROVIDER_URL, "localhost:1099");
props.put("java.naming.rmi.security.manager", "yes");
props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming");

// Get the initial context with given properties
Context context = new InitialContext(props);

The class ManualJNDI.java in directory org/jboss/docs/jms/client is an example of this. It may be tested with the following Ant command:

Figure 6.13. Running the manual JNDI example

ant jms-manual-jndi

Once we have the context, we continue by looking up a connection factory. To do it, we use one of the available names. Let's look up the standard topic connection factory:

Figure 6.14. Looking up a TopicConnectionFactory

// Get the connection factory
TopicConnectionFactory topicFactory =
  (TopicConnectionFactory)context.lookup("TopicConnectionFactory");

[2.4.1. The name should be ConnectionFactory, instead.]

Figure 6.15. [2.4.1] Looking up a TopicConnectionFactory

// Get the connection factory
TopicConnectionFactory topicFactory =
  (TopicConnectionFactory)context.lookup("ConnectionFactory");

For a Queue, it would instead look like this:

Figure 6.16. Looking up a QueueConnectionFactory

// Get the connection factory
QueueConnectionFactory queueFactory =
  (QueueConnectionFactory)context.lookup("QueueConnectionFactory");

[2.4.1. The name should be ConnectionFactory, instead.]

Figure 6.17. [2.4.1] Looking up a QueueConnectionFactory

// Get the connection factory
QueueConnectionFactory queueFactory =
  (QueueConnectionFactory)context.lookup("ConnectionFactory");

Once we have the factory we create a connection; and then we can establish a session in the connection. For a topic, this might look like this:

Figure 6.18. Creating topic connections and sessions

// Create the connection
topicConnection = topicFactory.createTopicConnection();

// Create the session
topicSession = topicConnection.createTopicSession(
  // No transaction
  false,
  // Auto ack
  Session.AUTO_ACKNOWLEDGE);

A session may be created configured to involve transactions or not; if transactions are not involved, it may be set up with different types of acknowledgement modes. Read a JMS reference text to learn more about transactions in JMS, or check the section called “JMS as a managed resource”).

Similarly, establishing a session for a queue might look like this:

Figure 6.19. Creating queue connections and sessions

// Create the connection
queueConnection = queueFactory.createQueueConnection();

// Create the session
queueSession = queueConnection.createQueueSession(
  // No transaction
  false,
  // Auto ack
  Session.AUTO_ACKNOWLEDGE);

Now it is time to create the part that either publishes/sends messages or subscribes to/receives messages.

To do this we need to look up a destination. To look up the topic destination testTopic we would write:

Figure 6.20. Looking up a topic destination

// Look up the destination
Topic topic = (Topic)context.lookup("topic/testTopic");

To look up the queue destination testQueue we would write, instead:

Figure 6.21. Looking up a queue destination

// Look up the destination
Queue queue = (Queue)context.lookup("queue/testQueue");

Note that in JBossMQ the prefix topic/ is always placed before the topic name and the prefix queue/ is always placed before the queue name.

In JMS, each possible role a client may play, as a publisher or subscriber for topics and as a sender or receiver for queues, has its own object hierarchy and its own special creation methods.

Let's start by looking at what one has to do to publish/send a message. For a topic, we need to create a TopicPublisher through the TopicSession by giving to the session the topic we have looked up in the context:

Figure 6.22. Create a topic publisher

// Create a publisher
TopicPublisher topicPublisher = topicSession.createPublisher(topic);

For a queue, instead, we need to create a QueueSender though the QueueSession by giving to the session the queue we have looked up in the context:

Figure 6.23. Create a queue sender

// Create a sender
QueueSender queueSender = queueSession.createSender(queue);

Once we have a publisher/sender, we need to create a message through the session, fill it with the message data and publish/send it through the publisher/sender created before. For a topic it would look like this:

Figure 6.24. Create a TextMessage and publish it

// Create a message
TextMessage message = topicSession.createTextMessage();
message.setText(msg);

// Publish the message
topicPublisher.publish(topic, message);

The code for a queue is very similar:

Figure 6.25. Create a TextMessage and send it

// Create a message
TextMessage message = queueSession.createTextMessage();
message.setText(msg);

// Send the message
queueSender.send(queue, message);

A complete and working topic publisher might look like this:

Figure 6.26. Complete topic publisher, from HelloPublisher.java in directory org/jboss/docs/jms/client

package org.jboss.docs.jms.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.jms.TopicConnectionFactory;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.TopicPublisher;
import javax.jms.Topic;
import javax.jms.TextMessage;
import javax.jms.Session;
import javax.jms.JMSException;
/**
 * <p>Simple JMS client, publish text messages to testTopic Topic.
 * </p>
 *
 * <p><b>NOTE</b>This code is a showcase only. It may not provide
 * a stable production example.</p>
 * @author Peter Antman
 * @version $Revision: 3.1 $
 */
public class HelloPublisher {

  /**
   * Topic connection, hold on to this so you may close it.
   */
  TopicConnection topicConnection;

  /**
   * Topic session, hold on to this so you may close it.
   * Also used to create messages.
   */
  TopicSession topicSession;

  /**
   * Use this to publish messages.
   */
  TopicPublisher topicPublisher;

  /**
   * Destination where to publish.
   */
  Topic topic;


  /**
   * Sets up all the JMS fixtures.
   *
   * Use close() when finished with object.
   *
   * @param factoryJNDI name of the topic connection factory to look up.
   * @param topicJNDI name of the topic destination to look up
   */
  public HelloPublisher(String factoryJNDI, String topicJNDI)
    throws JMSException, NamingException {

    // Get the initial context
    Context context = new InitialContext();

    // Get the connection factory
    TopicConnectionFactory topicFactory =
      (TopicConnectionFactory)context.lookup(factoryJNDI);

    // Create the connection
    topicConnection = topicFactory.createTopicConnection();

    // Create the session
    topicSession = topicConnection.createTopicSession(
      // No transaction
      false,
      // Auto ack
      Session.AUTO_ACKNOWLEDGE);

    // Look up the destination
    topic = (Topic)context.lookup(topicJNDI);

    // Create a publisher
    topicPublisher = topicSession.createPublisher(topic);

  }

  /**
   * Publish the given String as a JMS message to the testTopic topic.
   */
  public void publish(String msg) throws JMSException {

    // Create a message
    TextMessage message = topicSession.createTextMessage();
    message.setText(msg);

    // Publish the message
    topicPublisher.publish(topic, message);

  }

  /**
   * Close session and connection.
   * When done, no publishing is possible any more.
   */
  public void close() throws JMSException {

    topicSession.close();
    topicConnection.close();

  }

  /**
   * Run an example publishing 10 messages to testTopic.
   * Only works up to and including JBoss 2.4.0
   */
  public static void main(String[] args) {
    try {

      // Create the HelloPublisher, giving it the name of the
      // TopicConnection Factory and the Topic destination to
      // use in lookup.
      HelloPublisher publisher = new HelloPublisher(
        // Name of ConnectionFactory
        "TopicConnectionFactory",
        // Name of destination to publish to
        "topic/testTopic");

      // Publish 10 messages
      for (int i = 1; i < 11; i++) {

        String msg = "Hello World no. " + i;
        System.out.println("Publishing message: " + msg);
        publisher.publish(msg);

      }

      // Close down your publisher
      publisher.close();

    } catch(Exception ex) {

      System.err.println(
        "An exception occurred while testing HelloPublisher: " + ex);
      ex.printStackTrace();

    }

  }

} // HelloPublisher

[2.4.1. There is only one thing that needs to be changed to make it work: Change the TopicConnectionFactory to ConnectionFactory. Below, the modified main() method is shown.]

Figure 6.27. [2.4.1] Topic publisher main(), from HelloPublisher25.java in directory org/jboss/docs/jms/client

/**
 * Run an example publishing 10 messages to testTopic.
 * Only works from JBoss 2.4.1 and up.
 */
public static void main(String[] args) {
  try {

    // Create the HelloPublisher, giving it the name of the
    // TopicConnection Factory and the Topic destination to
    // use in lookup.
    HelloPublisher25 publisher = new HelloPublisher25(
      // Name of ConnectionFactory
      "ConnectionFactory",
      // Name of destination to publish to
      "topic/testTopic");

    // Publish 10 messages
    for (int i = 1; i < 11; i++) {

      String msg = "Hello World no. " + i;
      System.out.println("Publishing message: " + msg);
      publisher.publish(msg);

    }

    // Close down your publisher
    publisher.close();

    } catch(Exception ex) {

    System.err.println(
      "An exception occurred while testing HelloPublisher25: " + ex);
    ex.printStackTrace();

  }

}

Both of these examples are available in the examples directory org/jboss/docs/jms/client as HelloPublisher.java and HelloPublisher25.java.

As we know, it is not only possible to send/publish messages with JMS, but it is also possible to obtain published/sent messages. Time-wise, there are two ways of doing it:

  • Synchronously: This means that you need to manually go and fetch them. We do not show here any examples of how to do that.

  • Asynchronously: This means that you need to register an object that implements the MessageListener interface. It is then up to the JMS provider to invoke this object each time there is a message waiting for you at the destination.

In principle, asynchronous receiving is done the same way for both topics and queues, but the classes and method signatures are different in both cases. First, you must implement a MessageListener interface. For simple cases it is often good enough to implement the listener in the same class where you write the other JMS code.

Figure 6.28. A MessageListener

public class HelloSubscriber implements MessageListener {

  public void onMessage(Message m) {

    // Unpack the message, be careful when casting to the correct
    // message type. onMessage should not throw any application
    // exceptions.
    try {

      String msg = ((TextMessage)m).getText();
      System.out.println("HelloSubscriber got message: " + msg);

    } catch(JMSException ex) {

      System.err.println("Could not get text message: " + ex);
      ex.printStackTrace();

    }

  }

}

Then you need to create a subscriber/receiver and register the listener with it. You then have to start the connection the get the JMS provider to start sending messages to the subscriber/receiver.

For a topic it might look like this:

Figure 6.29. Create and start a topic subscriber

// Create a subscriber
topicSubscriber = topicSession.createSubscriber(topic);

// Set the message listener, which is this class since we implement
// the MessageListener interface
topicSubscriber.setMessageListener(this);

// OBS! For the message listener to receive any messages the connection
// has to be started.
topicConnection.start();

And for a queue like this:

Figure 6.30. Create and start a queue receiver

// Create a subscriber
queueReceiver = queueSession.createReceiver(queue);

// Set the message listener, which is this class since we
// implement the MessageListener interface
queueReceiver.setMessageListener(this);

// OBS! For the message listener to receive any messages
// the connection has to be started.
queueConnection.start();

Let's look at a complete example of a topic subscriber:

Figure 6.31. Topic subscriber example, from HelloSubscriber.java in directory org/jboss/docs/jms/client

package org.jboss.docs.jms.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.jms.TopicConnectionFactory;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.jms.Topic;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.Session;
import javax.jms.MessageListener;
import javax.jms.JMSException;

/**
 * <p>Simple JMS client, subscribes for messages on testTopic Topic.
 * </p>
 *
 * <p><b>NOTE</b>This code is a showcase only. It may not provide
 * a stable production example.</p>
 *
 * @author Peter Antman
 * @version $Revision: 3.1 $
 */
public class HelloSubscriber implements MessageListener {

  /**
   * Topic connection, hold on to this so you may close it.
   */
  TopicConnection topicConnection;

  /**
   * Topic session, hold on to this so you may close it.
   * Also used to create messages.
   */
  TopicSession topicSession;

  /**
   * Subscriber
   */
  TopicSubscriber topicSubscriber;

  /**
   * Destination to subscribe to
   */
  Topic topic;

  /**
   * Sets up all the JMS fixtures.
   *
   * Use close() when finished with object.
   *
   * @param factoryJNDI name of the topic connection factory to look up.
   * @param topicJNDI name of the topic destination to look up
   */
  public HelloSubscriber(String factoryJNDI, String topicJNDI)
    throws JMSException, NamingException
  {

    // Get the initial context
    Context context = new InitialContext();

    // Get the connection factory
    TopicConnectionFactory topicFactory =
      (TopicConnectionFactory)context.lookup(factoryJNDI);

    // Create the connection
    topicConnection = topicFactory.createTopicConnection();

    // Create the session
    topicSession = topicConnection.createTopicSession(
      // No transaction
      false,
      // Auto ack
      Session.AUTO_ACKNOWLEDGE);

    // Look up the destination
    topic = (Topic)context.lookup(topicJNDI);

    // Create a subscriber
    topicSubscriber = topicSession.createSubscriber(topic);

    // Set the message listener, which is this class since we implement
    // the MessageListener interface
    topicSubscriber.setMessageListener(this);

    System.out.println(
      "HelloSubscriber subscribed to topic: " + topicJNDI);

    // OBS! For the message listener to receive any messages the
    // connection has to be started
    topicConnection.start();
  }

  /**
   * Implementation of the MessageListener interface,
   * messages will be received through this method.
   */
  public void onMessage(Message m) {

    // Unpack the message, be careful when casting to the correct
    // message type. onMessage should not throw any application
    // exceptions.
    try {

      String msg = ((TextMessage)m).getText();
      System.out.println("HelloSubscriber got message: " + msg);

    } catch(JMSException ex) {

      System.err.println("Could not get text message: " + ex);
      ex.printStackTrace();

    }

  }

  /**
   * Close session and connection.
   */
  public void close() throws JMSException {

    topicSession.close();
    topicConnection.close();

  }

  /**
   * Run an example subscribing to topic testTopic.
   * Only works up to and including JBoss 2.4.0
   */
  public static void main(String[] args) {

    try {

      // Create the HelloSubscriber, giving it the name of the
      // TopicConnection Factory and the Topic destination to use in
      // lookup.
      HelloSubscriber subscriber = new HelloSubscriber(
        // Name of ConnectionFactory
        "TopicConnectionFactory",
        // Name of destination to publish to
        "topic/testTopic");

    } catch(Exception ex) {

      System.err.println(
        "An exception occurred while testing HelloSubscriber: " + ex);
      ex.printStackTrace();

    }

  }

} // HelloSubscriber

You will find the files HelloSubscriber.java and HelloSubscriber25.java in the same directory as HelloPublisher; note that HelloSubscriber25.java only works with JBoss version 2.4.1. There are also in the same directory complete examples of how to work with queues in the files HelloSender.java and HelloReceiver.java.

The examples may be run and tested with the provided Ant build file build.xml.

To run the topic examples, open two terminal windows (or virtual sessions) and change to the build directory. In the first terminal window type:

Figure 6.32. Running the HelloSubscriber example

ant jms-hello-subscriber

If all went well, you should see a message at the end looking like this:

Figure 6.33. Output of running the HelloSubscriber example

jms-hello-subscriber:
     [echo] Using resources from: /home/pra/src/examples/resources
     [java] HelloSubscriber subscribed to topic: topic/testTopic

Now, type in the other window:

Figure 6.34. Running the HelloPublisher example

ant jms-hello-publisher

In the subscriber window you should now see:

Figure 6.35. Output of running the HelloPublisher example

     [java] HelloSubscriber got message: Hello World no. 1
     [java] HelloSubscriber got message: Hello World no. 2
     [java] HelloSubscriber got message: Hello World no. 3
     [java] HelloSubscriber got message: Hello World no. 4
     [java] HelloSubscriber got message: Hello World no. 5
     [java] HelloSubscriber got message: Hello World no. 6
     [java] HelloSubscriber got message: Hello World no. 7
     [java] HelloSubscriber got message: Hello World no. 8
     [java] HelloSubscriber got message: Hello World no. 9
     [java] HelloSubscriber got message: Hello World no. 10

To run a JMS client with JBossMQ without the help of the already defined build.xml file you will need to set up a correct classpath. The library files needed to run a client are actually different for all the treated versions of JBoss. Among the examples, under the org/jboss/docs/jms/bin directory, there are three shell scripts that show how to run a JMS client for each of the different versions of JBoss.

First of all, you need to have the classes of your own client in the classpath. You should also have included in the classpath the directory where your jndi.properties file is located.

You will also need in the classpath the following JAR files included in the standard JBoss distribution:

  • client/jbossmq-client.jar

  • client/jnp-client.jar

  • client/jta-spec1_0_1.jar

  • lib/ext/jms.jar

[2.4.0. You should have these, instead:

  • client/jbossmq-client.jar

  • client/jnp-client.jar

  • client/jta-spec1_0_1.jar

  • client/jboss-j2ee.jar

  • lib/ext/oswego-concurrent.jar]

[2.4.1. You should have these, instead:

  • client/jbossmq-client.jar

  • client/jnp-client.jar

  • client/jta-spec1_0_1.jar

  • client/jboss-j2ee.jar

  • lib/ext/oswego-concurrent.jar

  • client/log4j.jar]

Here it is an example shell script to run the HelloPublisher client program:

Figure 6.36. Shell script for HelloPublisher from org/jboss/docs/jms/bin/runHelloPublisher22.sh

#!/bin/bash

#
# Example script to run JMS client against JBoss 2.2.x
#

# Directory where jndi.properties is located
JNDI_RESOURCE_DIR=../resources/

# HelloPublisher class
CLIENT_CLASS_DIR=../../../../../build-examples/jms/classes/

# Required libs to run client
LIBS=$JBOSS_DIST/client/jbossmq-client.jar:\
$JBOSS_DIST/client/jnp-client.jar:\
$JBOSS_DIST/client/jta-spec1_0_1.jar:\
$JBOSS_DIST/lib/ext/jms.jar

# Agregated classpath
CLASSPATH=:$LIBS:\
$CLIENT_CLASS_DIR:\
$JNDI_RESOURCE_DIR

echo "Running with classpath $CLASSPATH"
$JAVA_HOME/bin/java -classpath $CLASSPATH org.jboss.docs.jms.client.HelloPublisher

To use this you have to set the environment variables JBOSS_DIST and JAVA_HOME as explained in the section called “Introduction”

Here are the Ant targets to run the JMS examples:

Figure 6.37. Ant targets for JMS examples

ant jms-hello-publisher
ant jms-hello-subscriber
ant jms-hello-publisher25
ant jms-hello-subscriber25
ant jms-hello-sender
ant jms-hello-receiver
ant jms-hello-sender25
ant jms-hello-receiver25
ant jms-manual-jndi

As a final touch we shall also explain a JBoss-specific feature: how to manage destinations on the fly. As of this writing, it is only possible to do this up to and including JBoss version 2.4.0. Is is also possible to do this in JBoss version 2.4.1, but the API has changed slightly and the created destinations are not persisted between server restarts.

There are two different ways of doing this, depending on whether the code is in the same Virtual Machine as JBoss or it is in a standalone client. Both involve invoking a JMX MBean registered by the name service=JMSServer; this MBean has four methods to manage destinations: newTopic, destroyTopic, newQueue and destroyQueue.

[2.4.1. The MBean is registered by the name service=Server and its method names are createTopic, destroyTopic, createQueue and destroyQueue.]

The difference between the two ways of managing destinations on the fly is reflected in how the MBean is invoked. If the code is in the same VM as the MBean server, you may invoke it directly. To create a topic destination you might do it like this:

Figure 6.38. Creating a topic programmatically in the same VM

// Get the MBean server
MBeanServer server = (MBeanServer)
  MBeanServerFactory.findMBeanServer(null).iterator().next();

// Invoke the MBean
server.invoke(new ObjectName("JMS", "service", "JMSServer"),
  "newTopic",
  new Object[] { "myTopic" },
  new String[] { "java.lang.String" });

[2.4.1. Because of the changes in the strings used to look up the MBean and in the method names of the MBean, it would typically look like below, instead.]

Figure 6.39. [2.4.1] Creating a topic programmatically in the same VM

// Get the MBean server
MBeanServer server = (MBeanServer)
  MBeanServerFactory.findMBeanServer(null).iterator().next();

// Invoke the MBean
server.invoke(new ObjectName("JBossMQ", "service", "Server"),
  "createTopic",
  new Object[] { "myTopic" },
  new String[] { "java.lang.String" });

If the code is not in the same VM as the MBean server, you need to go through a JMX adapter. One JMX adapter is the HTML GUI. It is possible to programmatically invoke an MBean via a URL.

Figure 6.40. Creating a topic programmatically via a URL

//These are samples, not available in the example code.
String method = "newTopic";
String destName = "myCreated";

String action = "action=" + method + "?action=" + method +
  "&param0%2Bjava.lang.String=" + destName;

String arg = "/InvokeAction//JMS%3Aservice%3DJMSServer" + "/" + action;

URL url = new URL("http", "localhost", 8082, arg);

//Invoke the URL
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.connect();

[2.4.1. The strings used would look somewhat different.]

Figure 6.41. [2.4.1] Creating a topic programmatically via a URL

//These are samples, not available in the example code.
String method = "createTopic";
String destName = "myCreated";

String action = "action=" + method + "?action=" + method +
  "&param0%2Bjava.lang.String=" + destName;

String arg = "/InvokeAction//JBossMQ%3Aservice%3DServer" + "/" + action;

URL url = new URL("http", "localhost", 8082, arg);

//Invoke the URL
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.connect();

The example code may be found in the source file DestinationHelper.java in directory org/jboss/docs/jms/client.

[2.4.1. The source file is DestinationHelper25.java in the same directory.]

You may run it with the Ant targets:

Figure 6.42. Running the DestinationHelper example

ant jms-create-dest
ant jms-destroy-dest

Figure 6.43. [2.4.1] Running the DestinationHelper example

ant jms-create-dest25
ant jms-destroy-dest25

They will create and destroy the topic myCreated.