Logo Computer scientist,
engineer, and educator
• Articles • Articles about computing • Articles about software development • Apache integration software

JMS messaging from the ground up using Apache ActiveMQ

In this article I explain how to code and run very simple JMS messaging clients, which deliver messages to, and consume messages from, an Apache ActiveMQ broker. The broker will be used in as near as possible its default configuration, so setting up is very straightforward.

As with all the articles in this series, no technical details are concealed. I'll be using nothing more than command-line tools and a text editor, along with ActiveMQ and the Java JDK, to build and test the examples, so you can see exactly how everything fits together.

For simplicity, I'm assuming that the message producer, consumer, and broker are on the same physical host. In practice, of course, they probably won't be, but the examples will work with in a distributed system with minor changes.

Prerequisites

This article is written for reasonably experienced Java programmers, who have at least heard of JMS. I don't assume any knowledge of ActiveMQ, but I do assume that the reader is happy working at a command prompt to install software, and editing configuration files. Other than a command prompt and a text editor, you'll need to have a reasonably-modern Java JDK — any Sun/Oracle JDK 1.6 or later should be sufficient.

The specific command-line examples presented are for a Linux system, because that's what I use, but they will work with minor modifications on most platforms for which a Java JDK is available.

About JMS and ActiveMQ

JMS (Java Message Services) is a set of Java APIs for interacting with an asynchronous message broker. A message broker stores and makes available messages of various types on certain named destinations; applications can post messages to these destinations, and be informed when new data has been posted by other clients. Popular message brokers include OpenMQ, Webshere MQ (formerly MQSeries), and, of course, Apache ActiveMQ. Many modern application servers, such as GlassFish and Oracle Weblogic, integrate a JMS-compliant message broker.

A message broker (in the JMS sense) supports inter-application communication, rather than user-to-user communication. Clearly there is a conceptual similarity between this kind of message broker and, for example, an email or instant messaging system. However, while the data handled by a JMS message broker might be human-readable, it need not be.

Message brokers are frequently used for integrating applications and systems from different vendors, because they lend themselves to the creation of loosely-coupled architectures. Although many data types can be accomodated, a particularly significant application is to pass XML data, often in SOAP format, between heterogenous systems. In this mode of operation, message brokers tend to converge with Web Services.

Apache ActiveMQ is an all-Java message broker that supports JMS as well as a propietary API; bindings are available for other languages than Java, notably C and Python. ActiveMQ supports Web-based management, clustering, and load sharing, and can be integrated into an OSGi container such as Apache Karaf. It has a built-in persistence system based on the Kaha persistence engine, although it seems that this might be changing in future releases. For production deployments, ActiveMQ can use a relational database as its message store, via the JDBC API.

About the sample application

The sample application consists of two stand-alone Java programs: Producer.java puts a single "Hello, World" text message onto a queue called test_queue, while Consumer.java waits for new messges to be delivered to test_queue, and displays them when they arrive. Both are implemented using standard JMS API calls so far as practicable, and represent about the simplest application of JMS that I was able to think of.

Step 1: Obtaining and installing ActiveMQ

Downloading and unpacking the distribution

ActiveMQ binaries are available from the ActiveMQ download page. For this article, I used the version 5.9 snapshot, from here. By the time you read this, there will probably be a later version.

Some of the Apache integration products, particularly Karaf and ServiceMix, have built-in text consoles, and so have different binaries for different platforms. For ActiveMQ this is not the case — there is only one binary bundle, and installing it consists of unpacking the bundle in any convenient directory. In this example I have unpacked the binary in the directory /home/kevin/lib, and the ActiveMQ installation directory is therefore

/home/kevin/lib/apache-activemq-5.9-SNAPSHOT
Most of the steps described below refer to this directory and, of course, you'll have to modify them with the directory that is appropriate on your system.

Basic configuration

ActiveMQ comes with a default configuration, and should start up for testing purposes with minimal changes (ideally, no changes). The default configuration has the following important settings, so far as this article is concerned.

Default client TCP port: 61616. This is the port that clients will connect to, for producing and consuming messages. To change this port, look in the file conf/activemq.xml.

Default administration port: 8161. This is the port for the Web-based administration console. To change this port, look in the file conf/jetty.xml (the Web console is based on the Jetty servlet container). The default administrator credentials (admin/admin) can also be set in this file.

Default data store: built-in Kaha database, in the data directory. This fact is important for two reasons. First, earlier versions of ActiveMQ wrote the default database in the current working directory, and a lot of documentation still misleadingly states this to be the case. Second, you need to install ActiveMQ in a directory to which the account that will run it has write access. If you install in /usr, for example, you'll be forced to run the message broker as root, or to change the setting of kahaDB directory, and a bunch of other things, in activemq.xml.

Auto-creation of destinations: enabled. You should not need to create named JMS destinations administratively — they will be created dynamically as clients request them.

Starting the message broker

Assuming that the default configuration is acceptable, or you have edited it so that it is, starting is simply a matter of running the activemq script or batch file with the start argument. For example:
[kevin@lars]$ /home/kevin/lib/apache-activemq-5.9-SNAPSHOT/bin/activemq start

INFO: Using default configuration
INFO: Using java '/bin/java'
INFO: Starting - inspect logfiles specified in logging.properties and log4j.properties to get details
INFO: pidfile created : '/home/kevin/lib/apache-activemq-5.9-SNAPSHOT/data/activemq-lars.pid' (pid '10348')
Unlike earlier versions of ActiveMQ, recent ones detach from the console and run as daemon processes. You can find the process ID by looking in the PID file generated at startup. You should be able to stop the message broker by running activemq stop, but this does not seem to work cleanly on version 5.9.0 (at least when I tried it) — the process is actually killed by sending a 'kill' to the process ID.

Using the Web-based administration console

Assuming that you haven't changed the default administration port, you can run the Web-based console by pointing a Web browser at the URL
http://localhost:8161/hawtio
The default credentials are admin/admin.

ActiveMQ has relatively recently switched over to using the hawt.io console framework for its administration — this framework uses plugins for the specific processes it manages. To get to the ActiveMQ-specific administration, click the 'ActiveMQ' button in the top-left of the page.

The Web-based console is reasonably self-explanatory, but some basic operations will be described later.

Step 2: Coding and running a JMS message producer

Preparing the directory structure

Because we're building and running the JMS clients entirely at the command line, the directory structure must be well-defined. The diagram below shows the structure I'm assuming; this structure will be created automatically if you unpack the source code bundle (see the Download section at the end of the article).
target
  classes              # Compiled classes will go here

src
  net
    kevinboone
      amqtest
        Producer.java  # source for the message producer
        Consumer.java  # Source for the message consumer

log4j.properties       # log4j configuration file (see below)

A note about logging

The ActiveMQ client libraries, which provide run-time support to JMS clients, use the SLF4J logging API for logging (as all the Apache integration products do). SLF4J has, on its own, very limited logging capabilities. In practice, it is usually integrated with a logging implementation like log4j. None of the examples in this article do any logging and, unless something goes catastrophically wrong, there will be no log output from the ActiveMQ libraries either. Consequently, it's unnecessary to configure logging at all to run the examples. However, if you don't, you'll get a heap of irritating error messages saying that logging is not properly configured. You can either ignore these, or configure some logging. In the examples, I've assumed the use of log4j. Since all the log4j bits are bundled with ActiveMQ, this requires minimal work — just the provision of a log4j.properties file, and a couple of extra libraries on the class search path. Here is a minimal log4j.properties file — the contents are completely unimportant here, so long as they are valid.
log4j.rootLogger=INFO, out
log4j.logger.org.apache.camel=DEBUG
log4j.appender.out=org.apache.log4j.ConsoleAppender
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=%-30.30c{1} %-5p %m%n

Coding the producer

There isn't space in this article to describe the JMS API in detail. To use ActiveMQ from a Java program, we need only standard JMS API calls, with one exception — the method that obtains the initial connection to the broker. I hope the source below is reasonably self-explanatory, particularly if you already understand JMS. Please note that if you changed the message broker's TCP port earlier, you'll need to change the connection URL in this code to refer to the new port. Naturally, this code will work across a network — you don't have to specify 'localhost' as the hostname. However, you'll need to copy the necessary JAR files from the ActiveMQ distribution (see below for a list of these) to the machine where you're running the producer.
/*
Producer.java

Simple JMS producer for Apache ActiveMQ

(c)2013 Kevin Boone
 */

package net.kevinboone.amqtest;

// Note that the only Apache-specific class referred to in the source is
//  the one that provides the initial broker connection. The rest is
//  standard JMS
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;

public class Producer
  {
  public static void main (String[] args)
      throws Exception
    {
    // Create a connection factory referring to the broker host and port
    ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory
      ("tcp://localhost:61616");

    // Note that a new thread is created by createConnection, and it
    //  does not stop even if connection.stop() is called. We must
    //  shut down the JVM using System.exit() to end the program
    Connection connection = factory.createConnection();

    // Start the connection
    connection.start();

    // Create a non-transactional session with automatic acknowledgement
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

    // Create a reference to the queue test_queue in this session. Note
    //  that ActiveMQ has auto-creation enabled by default, so this JMS
    //  destination will be created on the broker automatically
    Queue queue = session.createQueue("test_queue");

    // Create a producer for test_queue
    MessageProducer producer = session.createProducer(queue);

    // Create a simple text message and send it
    TextMessage message = session.createTextMessage ("Hello, world!");
    producer.send(message);

    // Stop the connection — good practice but redundant here
    connection.stop();

    System.exit(0);
    }
  }

Compiling the producer

The producer client uses JMS API calls, and one class from the ActiveMQ client library (ActiveMQConnectionFactory). To compile the code, therefore, you'll need to make sure that the class search path includes the relevant JARs, which are geronimo-jms and activemq-client respectively.
$ ACTIVEMQ_HOME=/home/kevin/lib/apache-activemq-5.9-SNAPSHOT

$javac -d target/classes \
-classpath ${ACTIVEMQ_HOME}/lib/geronimo-jms_1.1_spec-1.1.1.jar:\
${ACTIVEMQ_HOME}/lib/activemq-client-5.9-SNAPSHOT.jar \
src/net/kevinboone/amqtest/Producer.java
The use of the backslash character in the above command indicates that the four lines are to be concatenated into one long line. Using Linux you can actually type a backslash here and continue on the next line; I don't think this works in a Windows DOS Box — you have to type a huge long line. Of course, in reality the build environment or IDE would take care of this stuff.

With luck, this invocation of javac will place the compiled classes in the directory target/classes, which we'll include in the class search path when running the code.

Running the producer

To run the producer, we need to invoke the java command on the class net.kevinboone.amqtest.Producer, with a suitable class search path. The class path in this case must include the following elements:
  • The directory target/classes, as that's where the compiled code is
  • The current directory '.', because this is the location of the log4j.properties file, and log4j will inspect the class path to find its configuration
  • The JMS API classes in the geronimo-jms JAR
  • The ActiveMQ client library, activemq-client
  • The JAR files that include the SLF4J logging API, the log4j implementation, and the interface beween slf4j and log4j
  • The J2EE management API classes, which are used by the ActiveMQ client library
Inlcuding all that lot leads to a long and ugly command to run the producer:
$ ACTIVEMQ_HOME=/home/kevin/lib/apache-activemq-5.9-SNAPSHOT

$ java -classpath .:\
target/classes:\
${ACTIVEMQ_HOME}/lib/optional/log4j-1.2.17.jar:\
${ACTIVEMQ_HOME}/lib/optional/slf4j-log4j12-1.7.5.jar:\
${ACTIVEMQ_HOME}/lib/geronimo-jms_1.1_spec-1.1.1.jar:\
${ACTIVEMQ_HOME}/lib/activemq-client-5.9-SNAPSHOT.jar:\
${ACTIVEMQ_HOME}/lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar:\
${ACTIVEMQ_HOME}/lib/slf4j-api-1.7.5.jar \
net.kevinboone.amqtest.Producer
Again, this is intended to be entered as one long command (probably embedded in a script or batch file).

Testing the producer

It's reasonable to assume that if you run the producer and don't get a pagefull of exceptions, it has worked, and the messages have been delivered to the broker. However, it's easy to check using the Web-based console. If you expand the 'Queue' entry in the menu in the left-hand pane, and then select 'test_queue', you'll see various statistics concerning the queue. Of particular importance are the values for Enqueue count and Queue size (see screenshot below).


Run the producer a few times, and you should see these message counts increase.

Assuming you've able to get some messages onto the queue, it's time to see how to get them off again.

Step 3: Coding and running a JMS message consumer

The steps for compiling and running the consumer are exactly as for the producer described above — only the name of the Java class changes; so I won't describe in detail the contents of the class search path or other elements of the command line.

Coding the consumer

Again, I hope the source below is reasonably self-explanatory. As before, you might need to change the port number. Note that the processes of getting a connection to the broker and establishing a JMS session are exactly the same for consumption and consumption.
/*
Consumer.java

Simple JMS consumer for Apache ActiveMQ

(c)2013 Kevin Boone
 */

package net.kevinboone.amqtest;

// Note that the only Apache-specific class referred to in the source is
//  the one that provides the initial broker connection. The rest is
//  standard JMS
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;

public class Consumer
  {
  public static void main (String[] args)
      throws Exception
    {
    // Create a connection factory referring to the broker host and port
    ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory
      ("tcp://localhost:61616");

    // Note that a new thread is created by createConnection, and it
    //  does not stop even if connection.stop() is called. We must
    //  shut down the JVM using System.exit() to end the program
    Connection connection = factory.createConnection();

    // Start the connection
    connection.start();

    // Create a non-transactional session with automatic acknowledgement
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

    // Create a reference to the queue test_queue in this session. Note
    //  that ActiveMQ has auto-creation enabled by default, so this JMS
    //  destination will be created on the broker automatically
    Queue queue = session.createQueue("test_queue");


    MessageConsumer consumer = session.createConsumer(queue);

    int messages = 0;
    final int MESSAGES_TO_CONSUME=10;
    do
     {
     TextMessage message = (TextMessage)consumer.receive();
     messages++;
     System.out.println ("Message #" + messages + ": " + message.getText());
     } while (messages < MESSAGES_TO_CONSUME);

    // Stop the connection — good practice but redundant here
    connection.stop();

    System.exit(0);
    }
  }
This consumer client is actually synchronous — it will block until a certain number of messages has been consumed. In practice the use of an asynchronous listener is more common, but the above is the simplest example I could think of that demonstrates the consumption of messages.

Compiling the consumer

Apart from the class name, the process is exactly the same as for compiling the producer:
$ ACTIVEMQ_HOME=/home/kevin/lib/apache-activemq-5.9-SNAPSHOT

$javac -d target/classes \
-classpath ${ACTIVEMQ_HOME}/lib/geronimo-jms_1.1_spec-1.1.1.jar:\
${ACTIVEMQ_HOME}/lib/activemq-client-5.9-SNAPSHOT.jar \
src/net/kevinboone/amqtest/Consumer.java
Again, the use of the backslash character in the above command indicates that the four lines are to be concatenated into one long line.

Running the consumer

To run the producer, we need to invoke the java command on the class net.kevinboone.amqtest.Consumer, with a suitable class search path.
$ ACTIVEMQ_HOME=/home/kevin/lib/apache-activemq-5.9-SNAPSHOT

$ java -classpath .:\
target/classes:\
${ACTIVEMQ_HOME}/lib/optional/log4j-1.2.17.jar:\
${ACTIVEMQ_HOME}/lib/optional/slf4j-log4j12-1.7.5.jar:\
${ACTIVEMQ_HOME}/lib/geronimo-jms_1.1_spec-1.1.1.jar:\
${ACTIVEMQ_HOME}/lib/activemq-client-5.9-SNAPSHOT.jar:\
${ACTIVEMQ_HOME}/lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar:\
${ACTIVEMQ_HOME}/lib/slf4j-api-1.7.5.jar \
net.kevinboone.amqtest.Consumer
If it works, you should see the contents of the messages placed on the broker by previous invocations of the producer. In addition, you should see the Queue size figure decrease in the Web console as the messages are withdrawn from the broker.

Summary

This article has shown how to build and test what I believe are the simplest possible pair of JMS producer/consumer clients, using ActiveMQ as the broker. In practice, most of the details of building and running JMS clients are concealed either by IDE tools, or within an application server's runtime environment, or both. This kind of automation is fine for common set-ups, but it's helpful to have a more detailed, low-level understanding when handling more complicated installations.

Downloads

Source code bundle

Copyright © 1994-2013 Kevin Boone. Updated May 01 2014