JBoss HornetQ simple tutorial

HornetQ is the former JBoss EAP 6 / AS 7 messaging broker. It has been now deprecated. We recommend checking Artemis MQ which is the new version of the Messaging Broker that is included in WildFly application server. Getting started with ActiveMQ Artemis

HornetQ is an open source project to build a multi-protocol, embeddable, very high performance, clustered, asynchronous messaging system.
Why learning another messaging system ? here are a few good reasons:

  1. Hornet Q is an open source messaging system which can be used as standalone or on any application server or even embedded in your applications.
  2. HornetQ delivers amazing messaging performance.
  3. HornetQ provides seamless clustering capabilities.
  4. It’s the default Messaging system in JBoss EAP 6 and WildFly 8/9.

Since WildFly 10 and JBoss EAP 7 Apache Artemis MQ is the default Messagins system. This project, however, is derived from the HornetQ project, recently donated to the  Apache foundation. The new messaging provides retains compatibility with the former HornetQ while providing several new features.

It takes just a few minutes to be operative, let’s start:

Download the latest stable release from http://www.jboss.org/hornetq/downloads.html and unzip in a convenient location.

Starting HornetQ

HornetQ can be started in 3 ways:

  • Standalone server
  • Inside an application server
  • Embedded in your applications

#1 Standalone HornetQ server

Launching HornetQ as standalone server is fairly trivial: reach the “bin” folder in your HornetQ distribution and launch run.bat/run.sh

start hornet

#2 Inside JBoss EAP 6 / WildFly 8

HornetQ is the JBoss EAP 6 and WildFly 8 messasing broker. To be more specific, the messaging suite of the application server is based on two core building blocks which let you create and consume JMS messages:

  • HornetQ itself is the JBoss EAP’s messaging provider featuring a multi-protocol, embeddable, very high performance, clusterable messaging implementation.
  • Netty project (http://netty.io/index.html) is an asynchronous event-driven network application framework which is used by HornetQ to transport messages from one JVM to another.

Please note that the messaging subsystem is not included in the default server configuration, therefore in order to use it you have to point to one of the standalone-full.xml or standalone-full-ha.xml configurations.So, for example, if you plan to run JMS applications on a non- clustered EAP server all you have to do is starting it like that:

$ ./standalone.sh –c standalone-full.xml

#3 Embedded in your Java code

HornetQ can be started as well in embedded mode. This can be particularly useful for testing purposes if you don’t want to start/stop manually the server on your machine.
An example of how to start an embedded HornetQ server is contained in the examples/core/embedded which can be found in the HornetQ distribution:

package org.hornetq.core.example;

import java.util.Date;

import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.core.client.ClientConsumer;
import org.hornetq.api.core.client.ClientMessage;
import org.hornetq.api.core.client.ClientProducer;
import org.hornetq.api.core.client.ClientSession;
import org.hornetq.api.core.client.ClientSessionFactory;
import org.hornetq.api.core.client.HornetQClient;
import org.hornetq.api.core.client.ServerLocator;
import org.hornetq.core.config.Configuration;
import org.hornetq.core.config.impl.ConfigurationImpl;
import org.hornetq.core.remoting.impl.invm.InVMAcceptorFactory;
import org.hornetq.core.remoting.impl.invm.InVMConnectorFactory;
import org.hornetq.core.server.HornetQServer;
import org.hornetq.core.server.HornetQServers;

/**
 *
 * This example shows how to run a HornetQ core client and server embedded in your
 * own application
 *
 * @author Tim Fox
 *
 */
public class EmbeddedExample
{

   public static void main(final String[] args) throws Exception
   {
      try
      {
         // Step 1. Create the Configuration, and set the properties accordingly
         Configuration configuration = new ConfigurationImpl();
         //we only need this for the server lock file
         configuration.setJournalDirectory("target/data/journal");
         configuration.setPersistenceEnabled(false);
         configuration.setSecurityEnabled(false);
         configuration.getAcceptorConfigurations().add(new TransportConfiguration(InVMAcceptorFactory.class.getName()));

         // Step 2. Create and start the server
         HornetQServer server = HornetQServers.newHornetQServer(configuration);
         server.start();

         // Step 3. As we are not using a JNDI environment we instantiate the objects directly
         ServerLocator serverLocator = HornetQClient.createServerLocatorWithoutHA(new TransportConfiguration(InVMConnectorFactory.class.getName()));
         ClientSessionFactory sf = serverLocator.createSessionFactory();

         // Step 4. Create a core queue
         ClientSession coreSession = sf.createSession(false, false, false);

         final String queueName = "queue.exampleQueue";

         coreSession.createQueue(queueName, queueName, true);

         coreSession.close();

         ClientSession session = null;

         try
         {

            // Step 5. Create the session, and producer
            session = sf.createSession();

            ClientProducer producer = session.createProducer(queueName);

            // Step 6. Create and send a message
            ClientMessage message = session.createMessage(false);

            final String propName = "myprop";

            message.putStringProperty(propName, "Hello sent at " + new Date());

            System.out.println("Sending the message.");

            producer.send(message);

            // Step 7. Create the message consumer and start the connection
            ClientConsumer messageConsumer = session.createConsumer(queueName);
            session.start();

            // Step 8. Receive the message.
            ClientMessage messageReceived = messageConsumer.receive(1000);
            System.out.println("Received TextMessage:" + messageReceived.getStringProperty(propName));
         }
         finally
         {
            // Step 9. Be sure to close our resources!
            if (sf != null)
            {
               sf.close();
            }

            // Step 10. Stop the server
            server.stop();
         }
      }
      catch (Exception e)
      {
         e.printStackTrace();
         throw e;
      }
   }
}

Inspecting HornetQ configuration

The configuration of HornetQ is split in several files. The following table resumes the basics of its configuration.

File Content
hornetq-beans.xml The core MBean server and Port settings.
hornetq-configuration.xml Main configuration file. Used to change JMS Storage directories and Message settings.
hornetq-jms.xml JMS Queue/Topic configuration.
hornetq-users.xml Users configuration
logging.properties The JUL configuration of the Server

As a developer you are likely to be interested to uodate the hornetq-jms.xml file to add/remove new Queue/Topics.

Deploying a simple MDB

Here is an example of MDB from the example suite which demonstrate sending a message and the MDB consuming only the message that matches the message selector.

package org.hornetq.javaee.example.server;

import org.jboss.ejb3.annotation.ResourceAdapter;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

/**
 * @author Andy Taylor
 */
@MessageDriven(name = "MDBMessageSelectorExample",
               activationConfig =
                     {
                        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
                        @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/testQueue"),
                        @ActivationConfigProperty(propertyName = "messageSelector", propertyValue = "color = 'RED'")
                     })
@TransactionManagement(value= TransactionManagementType.CONTAINER)
@TransactionAttribute(value= TransactionAttributeType.REQUIRED)
@ResourceAdapter("hornetq-ra.rar")
public class MDBMessageSelectorExample implements MessageListener
{
   public void onMessage(Message message)
   {
      try
      {
         //Step 11. We know the client is sending a text message so we cast
         TextMessage textMessage = (TextMessage)message;

         //Step 12. get the text from the message.
         String text = textMessage.getText();

         //Step 13. We check we received the right color of message
         String color = textMessage.getStringProperty("color");

         System.out.println("message " + text + " received color=" + color);

      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }
}

Ok. In order to connect to the MDB, you have to provide the following exports in your messaging configuration:

<jms-connection-factories>
    . . . . .
    <connection-factory name="RemoteConnectionFactory">
        <connectors>
            <connector-ref connector-name="netty"/>
        </connectors>
        <entries>
            <entry name="RemoteConnectionFactory"/>
            <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/>
        </entries>
    </connection-factory>

</jms-connection-factories>

<jms-destinations>
    <jms-queue name="testQueue">
        <entry name="queue/test"/>
        <entry name="java:jboss/exported/jms/queues/testQueue"/>
    </jms-queue>
</jms-destinations>

And finally here’s a generic test client which can be used to send messages to the “testQueue”:

package org.hornetq.javaee.example;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Properties;

/**
 * @author Andy Taylor
 */
public class MDBMessageSelectorClientExample
{
   public static void main(String[] args) throws Exception
   {
      Connection connection = null;
      InitialContext initialContext = null;
      try
      {
         //Step 1. Create an initial context to perform the JNDI lookup.
         final Properties env = new Properties();

         env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");

         env.put(Context.PROVIDER_URL, "remote://localhost:4447");

         env.put(Context.SECURITY_PRINCIPAL, "guest");

         env.put(Context.SECURITY_CREDENTIALS, "password");

         initialContext = new InitialContext(env);

         //Step 2. Perfom a lookup on the queue
         Queue queue = (Queue) initialContext.lookup("jms/queues/testQueue");

         //Step 3. Perform a lookup on the Connection Factory
         ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("jms/RemoteConnectionFactory");

         //Step 4.Create a JMS Connection
         connection = cf.createConnection("guest", "password");

         //Step 5. Create a JMS Session
         Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

         //Step 6. Create a JMS Message Producer
         MessageProducer producer = session.createProducer(queue);

         //Step 7. Create a Text Message and set the color property to blue
         TextMessage blueMessage = session.createTextMessage("This is a text message");

         blueMessage.setStringProperty("color", "BLUE");

         System.out.println("Sent message: " + blueMessage.getText() + " color=BLUE");

         //Step 8. Send the Message
         producer.send(blueMessage);

         //Step 9. create another message and set the color property to red
         TextMessage redMessage = session.createTextMessage("This is a text message");

         redMessage.setStringProperty("color", "RED");

         System.out.println("Sent message: " + redMessage.getText() + " color=RED");

         //Step 10. Send the Message
         producer.send(redMessage);
          //Step 10,11 and 12 in MDBMessageSelectorExample
      }
      finally
      {
         //Step 13. Be sure to close our JMS resources!
         if (initialContext != null)
         {
            initialContext.close();
         }
         if(connection != null)
         {
            connection.close();
         }
      }
   }
}

Your example is completed.In order to build the example, the following dependencies need to be set in your Maven project:

  <hornetq.client.version>2.4.0-SNAPSHOT</hornetq.client.version>

   <dependencies>
      <dependency>
         <groupId>org.jboss.spec.javax.jms</groupId>
         <artifactId>jboss-jms-api_2.0_spec</artifactId>
      </dependency>
      <dependency>
         <groupId>org.jboss.spec</groupId>
         <artifactId>jboss-javaee-6.0</artifactId>
         <version>1.0.0.Final</version>
         <type>pom</type>
         <scope>provided</scope>
      </dependency>
      
      <dependency>
         <groupId>org.hornetq</groupId>
         <artifactId>hornetq-core-client</artifactId>
         <version>${hornetq.client.version}</version>
      </dependency>
      <dependency>
         <groupId>org.hornetq</groupId>
         <artifactId>hornetq-jms-client</artifactId>
         <version>${hornetq.client.version}</version>
      </dependency>
      <dependency>
         <groupId>org.hornetq</groupId>
         <artifactId>hornetq-ra</artifactId>
         <version>${hornetq.client.version}</version>
      </dependency>
      <dependency>
         <groupId>io.netty</groupId>
         <artifactId>netty-all</artifactId>
         <version>${netty.version}</version>
      </dependency>
      <dependency>
         <groupId>org.jboss.ejb3</groupId>
         <artifactId>jboss-ejb3-ext-api</artifactId>
         <version>2.0.0-beta-2</version>
      </dependency>
   </dependencies>