Connecting WildFly to a remote Artemis MQ broker using SSL

In this article we have covered how to connect WildFly to a remote ArtemisMQ Server with no encryption of data. Let’s see how we can secure the communication between WildFly and Artemis MQ.

Configuring SSL on Artemis MQ

We will configure at first SSL on the broker side. Within the broker.xml configuration file, we need to secure the acceptor used for the communication. We will create a keystore at first and a truststore. The keystore will be used on the broker side, the truststore will be used by the client, which is in our case WildFly:

$ keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -validity 365 -keystore server.keystore -dname "cn=Server Administrator,o=Acme,c=GB" -keypass secret -storepass secret

$ keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -validity 365 -keystore client.keystore -dname "CN=client" -keypass secret -storepass secret

$ keytool -exportcert  -keystore server.keystore -alias localhost -keypass secret -storepass secret -file server.crt
 
$ keytool -exportcert  -keystore client.keystore -alias client -keypass secret -storepass secret -file client.crt

$ keytool -importcert -keystore server.truststore -storepass secret -alias client -trustcacerts -file client.crt -noprompt
 
$ keytool -importcert -keystore client.truststore -storepass secret -alias localhost -trustcacerts -file server.crt -noprompt

In our case, we don’t need all the above files but just:

  • server.keystore for ArtemisMQ
  • client.trustore for Wildfly

Let’s add a reference to the path where server.keystore is in our acceptor:

  <acceptor name="artemis">tcp://0.0.0.0:61616?sslEnabled=true;keyStorePath=/home/amq-broker-7.7.0/mybroker/server.keystore;keyStorePassword=secret;enabledProtocols=TLSv1.2;tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true</acceptor>

Start Artemis MQ and verify that the broker now uses SSL with a simple openssl command:

$ openssl s_client -connect localhost:61616

No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: ECDH, P-256, 256 bits
SSL handshake has read 1281 bytes and written 429 bytes
Verification error: self signed certificate
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: BA7EEBF0A8101B360C2ADF9746E4AB9BF2BD9EA7F43CE64B656900EF05886007
    Session-ID-ctx: 
    Master-Key: 1AB4A17000C324FB72E5F17720717609CB67F10AF7BBC196437A8F3DEEF60A427FC82A5765EE4D2046B18823BC2CBFD8
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1624781386
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: yes

Configuring WildFly Remote Connector to use SSL

On the WildFly side, we will start from configuration we have already set up in our first tutorial, which includes a remote-connector:

<subsystem xmlns="urn:jboss:domain:messaging-activemq:8.0">
    <server name="default">
      
      	<remote-connector name="remote-artemis" socket-binding="remote-artemis"/>

      	<pooled-connection-factory name="remote-artemis" entries="java:/RemoteJmsXA java:jboss/RemoteJmsXA" connectors="remote-artemis" ha="false" user="admin" password="admin" min-pool-size="15" max-pool-size="30" statistics-enabled="true">
                    <inbound-config rebalance-connections="true" setup-attempts="-1" setup-interval="5000"/>
     	</pooled-connection-factory>

    </server>
</subsystem>

The remote-connector points to a socket-binding which contains the address and port of the remote AMQ server:

<outbound-socket-binding name="remote-artemis">
    <remote-destination host="127.0.0.1" port="61616"/>
</outbound-socket-binding>

We need to add SSL configuration to the remote-connector element:

    <remote-connector name="remote-artemis" socket-binding="remote-artemis">
        <param name="ssl-enabled" value="true"/>
        <param name="trust-store-password" value="secret"/>
        <param name="trust-store-path" value="/home/wildfly/standalone/configuration/client.truststore"/>
    </remote-connector>

In the above example, we have copied the file client.trustore in the configuration folder of WildFly. Adjust it to the actual location of the file.

To achieve the above configuration, you can execute the following CLI command:

/subsystem=messaging-activemq/remote-connector=remote-artemis:write-attribute(name=params,value={ssl-enabled=true,trust-store-password=secret,trust-store-path=/home/wildfly/standalone/configuration/client.truststore})

Now start Artemis MQ, then start WildFly and start sending messages to check the connectivity.

That’s it! We have just covered how to connect WildFly to a Remtote Artemis MQ server using SSL.

How to configure IBM MQ Resource Adapter on WildFly

This guide will take through the steps required to install IBM MQ Resource Adapter on WildFly and verify the installation with a Docker Image of IBM MQ.

To get started, the first thing which is required is the IBM MQ Resource Adapter which can be downloaded from IBM site (you need to register on IBM site to be able to download it).

Here are the steps to Download IBM MQ Resource Adapter:

  1. Go to Fix Central (https://www.ibm.com/support/fixcentral/)
  2. Next, in the Product Selector field enter “WebSphere MQ” and select it from the list.
  3. Then, from the “Installed Version” field select the latest version..
  4. Next, in the “Platform” field select your platform or just choose “All”, then press Continue.
  5. On the next page select the “Browse for Fixes” button and make sure that the “Show fixes that apply to this version” button is also selected, then press Continue.
  6. Select the WS-MQ-Java-InstallRA version you want and download it

Finally, copy the file wmq.jmsra.rar file into the deployments directory of WildFly (f.e. $JBOSS_HOME/standalone/deployments).

Start IBM MQ

The simplest way to start IBM MQ is by means of Docker. You can pull and start the latest version of IBM MQ as follows:

docker run \
  --env LICENSE=accept \
  --env MQ_QMGR_NAME=QM1 \
  --publish 1414:1414 \
  --publish 9443:9443 \
  --detach \
  --volume qm1data:/mnt/mqm \
  ibmcom/mq

The above command creates and starts a Queue Manager called QM1, and maps port 1414 on the host to the MQ listener on port 1414 inside the container, as well as port 9443 on the host to the web console on port 9443 inside the container:

Update WildFly configuration

Next, update the configuration in your WildFly profile to include a reference to the resource adapter wmq.jmsra.rar:

<subsystem xmlns="urn:jboss:domain:resource-adapters:5.0">
    <resource-adapters>
        <resource-adapter id="wmq.jmsra">
            <archive>
                wmq.jmsra.rar
            </archive>
            <transaction-support>NoTransaction</transaction-support>
            <connection-definitions>
                <connection-definition class-name="com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl"
                                       jndi-name="java:jboss/jms/ivt/IVTCF" enabled="true" use-java-context="true"
                                       pool-name="IVTCF">
                    <config-property name="channel">SYSTEM.DEF.SVRCONN
                    </config-property>
                    <config-property
                                     name="hostName">localhost
                    </config-property>
                    <config-property name="transportType">
                        CLIENT
                    </config-property>
                    <config-property name="queueManager">
                        QM1
                    </config-property>
                    <config-property name="port">
                        1414
                    </config-property>
                </connection-definition>
                <connection-definition class-name="com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl"
                                       jndi-name="java:jboss/jms/ivt/JMS2CF" enabled="true" use-java-context="true"
                                       pool-name="JMS2CF">
                    <config-property name="channel">
                        SYSTEM.DEF.SVRCONN
                    </config-property>
                    <config-property name="hostName">
                        localhost
                    </config-property>
                    <config-property name="transportType">
                        CLIENT
                    </config-property>
                    <config-property name="queueManager">
                        QM1
                    </config-property>
                    <config-property name="port">
                        1414
                    </config-property>
                </connection-definition>
            </connection-definitions>
        <admin-objects>
            <admin-object class-name="com.ibm.mq.connector.outbound.MQQueueProxy"
                          jndi-name="java:jboss/jms/ivt/IVTQueue" pool-name="IVTQueue">
                <config-property name="baseQueueName">
                    EXAMPLE.QUEUE
                </config-property>
                </admin-object>
            </admin-objects>
        </resource-adapter>
    </resource-adapters>
</subsystem>

Within this configuration, we have exposed as admin object the Queue named EXAMPLE.QUEUE which is bound to the JNDI name jndi-name=”java:jboss/jms/ivt/IVTQueue”.

Now start WildFly and verify that the AdminObject and ConnectionFactory are registered:

2021-05-10 15:12:46,610 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-6) WFLYJCA0002: Bound JCA AdminObject [java:jboss/jms/ivt/IVTQueue]
2021-05-10 15:12:46,610 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-6) WFLYJCA0002: Bound JCA ConnectionFactory [java:jboss/jms/ivt/IVTCF]
2021-05-10 15:12:46,610 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-6) WFLYJCA0002: Bound JCA ConnectionFactory [java:jboss/jms/ivt/JMS2CF]

Depending on your WildFly version, you might find some WARN messages in the server logs:

WARN  [org.jboss.as.connector.deployers.RADeployer] (MSC service thread 1-5) IJ020017: Invalid archive: wmq.jmsra

Description: Invalid config-property-type for ManagedConnectionFactory.
Code: Class: com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl Property: CCSID Type: int

Severity: WARNING
Section: 20.7
Description: Invalid config-property-type for ManagedConnectionFactory.
Code: Class: com.ibm.mq.connector.outbound.ManagedConnectionFactoryImpl Property: sslResetCount Type: int

Severity: WARNING
Section: 20.7
Description: Invalid config-property-type for ResourceAdapter.
Code: Class: com.ibm.mq.connector.ResourceAdapterImpl Property: connectionConcurrency Type: int

These WARN messages are harmless and raised by automatic conversion of primitive int types to object types from/to the Resource Adapter.

Configuring MDB to Consume Messages from IBM MQ

Once that you have completed the Resource Adapter configuration, you can use the Admin Objects (Connection Factories, Destinations) in your MDB. Just provide the Resource Adapter name and refer to the Admin objects JNDI Name as in this example:

@MessageDriven(name="IbmMqMdb", activationConfig = {
        @ActivationConfigProperty(propertyName = "connectionFactoryLookup", propertyValue = "java:jboss/jms/ivt/IVTCF"),
        @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "java:jboss/jms/ivt/IVTQueue"),
        @ActivationConfigProperty(propertyName = "destinationType",propertyValue = "javax.jms.Queue")
})
@ResourceAdapter(value = "wmq.jmsra.rar")
public class IBMMDB implements MessageListener {
    @Override
    public void onMessage(Message message) {
       ...
    }
}

Howto configure a custom Dead Letter Queue (DLQ) in WildFly

The Dead Letter Queue Address (DLQ) is a collector for messages which failed to be delivered over a number of attemps.

In WildFly, A dead letter address is defined in the address-setting element of the messaging-activemq subsystem configuration. To read the generic dead letter queue configuration for all destinations (“#”), use the following management CLI command as an example.

/subsystem=messaging-activemq/server=default/address-setting=#:read-attribute(name=dead-letter-address)
{
    "outcome" => "success",
    "result" => "jms.queue.DLQ"
}

If a dead-letter-address is not specified, messages are removed after trying to deliver max-delivery-attempts times. By default, messages delivery is attempted 10 times. Setting max-delivery-attempts to -1 allows infinite redelivery attempts.

If you want to define a specific DLQ for a destination, you can set is as follows:

/subsystem=messaging-activemq/server=default/address-setting=jms.queue.TestQueue:add(dead-letter-address=jms.queue.MyDLQ,max-delivery-attempts=3)

In the above example, we have set a custom DLQ (“jms.queue.MyDLQ”) for the address “jms.queue.TestQueue” where messages will be delivered after 3 failed attempts.

Please note that you must configure the jms.queue.MyDLQ before hand:

jms-queue --queue-address=MyDLQ --entries=java:/jms/MyDLQ

How to create a Queue with Jmx Console ?

NOTE: The JMX Console is a tool used to manage JBoss AS 5. If you are running the newer version of the application server, follow up on this tutorial: JBoss JMS configuration

Bring up the JMX Console in your browser and look for the section labelled jboss.mq in the main agent view. Click on the link which says service=DestinationManager.

The DestinationManager MBean is the main JMS service in JBoss and you can use it to create and destroy queues and topics at runtime.

Look for the operation called createQueue. There will be two operations by that name, both of which take a different number of arguments. Look for the one that takes only one argument. That argument is the name of the queue. This takes two parameters. Enter MyQueue and click the Invoke button. This will create a queue bound under the JNDI name queue/MyQueue.

With this recipe you learnt how to configure at runtime a JMS Queue using the Jmx Console.

JBoss JMS configuration

JMS defines a vendor-neutral (but Java-specific) set of programming interfaces for interacting across different components or systems by means of messages. Messaging enables distributed communication, which is reliable and loosely coupled. A component sends a message to a destination, which in turn is retrieved by the recipient with the mediation of a JMS server.

This tutorial explains how to configure and run some JMS destinations using all versions of the application server ( WildFly, JBoss EAP 6, JBoss AS 6, JBoss AS 5, JBoss 4).

Continue reading JBoss JMS configuration

How to move JMS messages between destinations in WildFly

In this article, we will learn how to move messages from one JMS destination into another running on WildFly application server. We will also be using a filter to move messages selectively.

Let’s start create the following example JMS Destinations:

jms-queue add --queue-address=queueA --entries=java:/jms/queue/queueA
jms-queue add --queue-address=queueB --entries=java:/jms/queue/queueB

Now, in order to move messages from queueA into queueB, you can use the following CLI command:

/subsystem=messaging-activemq/server=default/jms-queue=queueA:move-messages(other-queue-name=queueB)

Also, consider that it is possible to use a filter when moving messages, using a message property as filter:

/subsystem=messaging-activemq/server=default/jms-queue=queueA:move-messages(filter="DEP_ID='12A'",other-queue-name=queueB)

Please notice that one common use case is moving messages from the Dead Letter Queue to another Queue. In this case, in order to move selectively only messages that were destined to one destination (for example, queueA), you can filter through the property “_AMQ_ORIG_QUEUE“. Here is, for example, how to restore messages from the DLQ (whose destination was queueA) into the queueB:

/subsystem=messaging-activemq/server=default/jms-queue=DLQ:move-messages(filter="_AMQ_ORIG_QUEUE='queueA'",other-queue-name=queueB)

The same thing can also be done programmaticallly, by listening to the DLQ, and filtering messages to be moved within the onMessage method:

public void onMessage(Message message) {
   try {
        String queueDestination = "queueB";
            if (message.getStringProperty("_AMQ_ORIG_QUEUE").equals("queueA")) {
               Queue queue = session.createQueue(queueDestination);
               MessageProducer messageProducer = session.createProducer(queue);
               messageProducer.send(message);
            }

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

 

 

How to connect WildFly to a remote ActiveMQ Artemis server?

In this tutorial we will cover how to connect WildFly application server (or JBoss EAP 7) to a remote Artemis MQ server by defining a Remote Connection factory which points to the AMQ broker

First of all, some background. ActiveMQ Artemis has a plugable protocol architecture and ships with 5 protocol modules out of the box:

  • AMQP
  • OpenWire
  • MQTT
  • STOMP
  • HornetQ

In addition to the protocols above ActiveMQ Artemis also offers support for it’s own highly performant native protocol “Core“. In order to connect WildFly to ArtemisMQ we will be using the “Core” protocol which is avaible also on the WildFly side.

Configuration on the AMQ side

In order to configure AMQ, you need to make sure you are using the CORE protocol and that the following properties are set, so that the Resource Adapter on WildFly can find Artemis Queues/Topics:

  • anycastPrefix=jms.queue.
  • multicastPrefix=jms.topic.

For example, the following acceptor will work:

     <acceptors>
         <acceptor name="hornetq">tcp://127.0.0.1:5445?anycastPrefix=jms.queue.;multicastPrefix=jms.topic.;protocols=CORE,HORNETQ,STOMP;useEpoll=true</acceptor>
     </acceptors>

Also, within our configuration, we have defined a Queue to be accessed by JMS Cients:

<address name="demoQueue" >
      <anycast>
        <queue name="demoQueue"/>
      </anycast>
</address>

Now, done with ArtemisMQ, let’s go to configure WildFly. First of All, we need a remote-connector and a pooled-connection-factory. Within the pooled-connection-factory, specify the JNDI name for the connection and the credentials to access the server.

<subsystem xmlns="urn:jboss:domain:messaging-activemq:8.0">
    <server name="default">
      
      	<remote-connector name="remote-artemis" socket-binding="remote-artemis"/>


      	<pooled-connection-factory name="remote-artemis" entries="java:/RemoteJmsXA java:jboss/RemoteJmsXA" connectors="remote-artemis" ha="false" user="admin" password="admin" min-pool-size="15" max-pool-size="30" statistics-enabled="true">
                    <inbound-config rebalance-connections="true" setup-attempts="-1" setup-interval="5000"/>
     	</pooled-connection-factory>

    </server>
</subsystem>

Please notice that you can use the parameter use-jndi=”false” if you want to let your Connection Factory to skip JNDI lookup of Queues.

The remote-connector points to a socket-binding which contains the address and port of the remote AMQ server_

<outbound-socket-binding name="remote-artemis">
    <remote-destination host="127.0.0.1" port="5445"/>
</outbound-socket-binding>

Then, within the naming subsystem, specify the JNDI Settings for looking up remote Queues/Topics on AMQ:

<subsystem xmlns="urn:jboss:domain:naming:2.0">
    <bindings>
        <external-context name="java:global/remoteContext" module="org.apache.activemq.artemis" class="javax.naming.InitialContext">
            <environment>
                <property name="java.naming.factory.initial" value="org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"/>
                <property name="java.naming.provider.url" value="tcp://127.0.0.1:5445"/>
                <property name="connectionFactory.ConnectionFactory" value="tcp://127.0.0.1:5445"/>
                <property name="queue.demoQueue" value="demoQueue"/>
            </environment>
        </external-context>
        <lookup name="java:/demoQueue" lookup="java:global/remoteContext/demoQueue"/>
    </bindings>
    <remote-naming/>
</subsystem>

In our case, we will be looking up the JMS Queue named demoQueue.

Coding JMS Consumers and JMS Producers

Our JMS Consumers will need to refererence the pooled-conection-factory name through the the @org.jboss.ejb3.annotation.ResourceAdapter annotation

@MessageDriven(name = "DemoMDB", activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "java:global/remoteContext/demoQueue"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "user", propertyValue = "amq"),
        @ActivationConfigProperty(propertyName = "password", propertyValue = "amq"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
@ResourceAdapter(value="remote-artemis")
public class HelloWorldQueueMDB implements MessageListener {

    private static final Logger LOGGER = Logger.getLogger(HelloWorldQueueMDB.class.toString());

    public void onMessage(Message rcvMessage) {
        TextMessage msg = null;
        try {
            if (rcvMessage instanceof TextMessage) {
                msg = (TextMessage) rcvMessage;
                LOGGER.info("Received Message from queue: " + msg.getText());
            } else {
                LOGGER.warning("Message of wrong type: " + rcvMessage.getClass().getName());
            }
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
}

On the other hand, when using JMS Clients, we need to inject the remote JMSConnectionFactory into the @JMSContext as follows:

@WebServlet("/HelloWorldMDBServletClient")
public class DemoServlet extends HttpServlet {

    private static final int MSG_COUNT = 5;

    @Inject
    @JMSConnectionFactory("java:/RemoteJmsXA")
    @JMSPasswordCredential(userName = "amq", password = "amq")
    private JMSContext context;

    @Resource(lookup = "java:global/remoteContext/demoQueue")
    private Queue queue;


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        out.write("<h1>Quickstart: Example demonstrates the use of <strong>JMS 2.0</strong> and <strong>EJB 3.2 Message-Driven Bean</strong> in JBoss EAP.</h1>");
        try {
            final Destination destination = queue;

            out.write("<p>Sending messages to <em>" + destination + "</em></p>");
            out.write("<h2>The following messages will be sent to the destination:</h2>");
            for (int i = 0; i < MSG_COUNT; i++) {
                String text = "This is message " + (i + 1);
                context.createProducer().send(destination, text);
                out.write("Message (" + i + "): " + text + "</br>");
            }
            out.write("<p><i>Go to your JBoss EAP server console or server log to see the result of messages processing.</i></p>");
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

If you have properly configured the binding subsystem, you will be able to lookup the remote Queue by this Servlet and send messages to the queue.

In the next tutorial, we will learn how to connect to a Remote Artemis MQ using SSL: Connecting WildFly to a remote Artemis MQ broker using SSL

JBoss JMS Topic example

The following article shows how to create a simple JMS Topic using WildFly and older versions of JBoss application server.

Creating a JMS Topic with WildFly

If you want to create a new Topic, the command to be executed from the CLI is the following one:

[standalone@localhost:9990 /] jms-topic add --topic-address=jms.topic.DemoTopic --entries=java:/jms/topic/demoTopic

The following topic will be added in your XML configuration:

<jms-topic name="jms.topic.DemoTopic" entries="java:/jms/topic/demoTopic"/>

On the other hand, if you are using Domain mode, you should specify the profile as argument:

[domain@localhost:9990 /] jms-topic add --profile=full-ha --topic-address=jms.topic.DemoTopic --entries=java:/jms/topic/demoTopic

To see an example application which uses JMS to send messages, check this tutorial: JBoss JMS configuration

Creating a Deployable Topic

JMS destinations can be also be created on the fly by dropping a *-jms.xml file in the deployments folder of your standalone server or packaging it along with your application. Here’s an example of a JMS Queue and a JMS Topic:

<messaging-deployment xmlns="urn:jboss:messaging-activemq-deployment:1.0">
    <server>
        <jms-destinations>
            <jms-queue name="ExampleQueue">
                <entry name="java:/jms/queue/ExampleQueue"/>
                <durable>true</durable>
            </jms-queue>
            <jms-topic name="ExampleTopic">
                <entry name="java:/jms/topic/ExampleTopic"/>
            </jms-topic>
        </jms-destinations>
    </server>
</messaging-deployment>

Creating a JMS Topic with JBoss 5

At first you need to deploy a JMS Topic to JBoss. Create a file ending with -service.xml in the deploy folder of JBoss.

Case 1: JBoss Messaging
If you are running JBoss 5, which ships with JBoss messaging you can use the following descriptor:

<mbean code="org.jboss.jms.server.destination.TopicService"
      name="jboss.messaging.destination:service=Topic,name=topicA"
      xmbean-dd="xmdesc/Topic-xmbean.xml">
   <depends optional-attribute-name="ServerPeer">jboss.messaging:service=ServerPeer</depends>
   <depends>jboss.messaging:service=PostOffice</depends>
</mbean>

Case 2: JBoss MQ
If you using JBoss older JMS provider, the configuration file is the following:

<mbean code="org.jboss.mq.server.jmx.Topic"
     name="jboss.mq.destination:service=Queue,name=topicA">
    <depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
</mbean>

Example Code:

package com.sample;


import java.util.Properties;
import java.util.Scanner;

import javax.jms.*;
import javax.naming.Context;
 

public class TopicExample implements MessageListener  
{
   
   public void example() throws Exception
   {
      String destinationName = "topic/topicA";
      
      Context ic = null;
      ConnectionFactory cf = null;
      Connection connection =  null;

      try
      {         
         ic = getInitialContext();
         
         cf = (ConnectionFactory)ic.lookup("/ConnectionFactory");
         Topic topic = (Topic)ic.lookup(destinationName);
         
         connection = cf.createConnection();
         Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
         MessageProducer publisher = session.createProducer(topic);
         MessageConsumer subscriber = session.createConsumer(topic);
         
         subscriber.setMessageListener(this);
         connection.start();
         
         TextMessage message = session.createTextMessage("Hello!");
         publisher.send(message);
               
          Scanner keyIn = new Scanner(System.in); 
          
          System.out.print("JMS Server listening. Type a Key + CR to exit\n");
         keyIn.next();
               
      }
      finally
      {         
         if(ic != null)
         {
            try
            {
               ic.close();
            }
            catch(Exception e)
            {
               throw e;
            }
         }
         
         // ALWAYS close your connection in a finally block to avoid leaks.
         // Closing connection also takes care of closing its related objects e.g. sessions.
         closeConnection(connection);
      }
   }
   public synchronized void onMessage(Message message)
   {
      TextMessage text = (TextMessage)message;
      String strMessage = null;
    try {
        strMessage = text.getText();
    } catch (JMSException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
      System.out.println("Message received: "+strMessage);
   }

   private void closeConnection(Connection con)
   {      
      try
      {
         if (con != null)
         {
            con.close();
         }         
      }
      catch(JMSException jmse)
      {
          System.out.println("Could not close connection " + con +" exception was " + jmse);
      }
   }
      
   protected boolean isQueueExample()
   {
      return true;
   }
   
   public static void main(String[] args) throws Exception
   {
      new TopicExample().example();
   }
   public static Context getInitialContext( )
    throws javax.naming.NamingException {

        Properties p = new Properties( );
        p.put(Context.INITIAL_CONTEXT_FACTORY,
                "org.jnp.interfaces.NamingContextFactory");
        p.put(Context.URL_PKG_PREFIXES,
        " org.jboss.naming:org.jnp.interfaces");
        p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
        return new javax.naming.InitialContext(p);
    }  
}

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

#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>

Introduction to ActiveMQ Artemis

ActiveMQ Artemis is a message broker. It originates from HornetQ messaging system which was donated to Apache in 2014. It retains compatibility with HornetQ while adding many interesting features. In this tutorial we will learn how to install it and start it.

Please don’t confuse ActiveMQ Artemis with the ActiveMQ broker (http://activemq.apache.org/) which is the main messaging broker developed by Apache foundation, although it is possible that in the future ActiveMQ Artemis will replace the former ActiveMQ project. But at the time of writing this is not confirmed.

You can download ActiveMQ Artemis from here: https://activemq.apache.org/artemis/download.html

Once unzipped the project move to the bin folder:

cd ~/apache-artemis-1.0.0/bin

The first step will be creating a new broker instance, choosing the “create” option followed by the storage path:

jboss@jbosseap ~/apache-artemis-1.0.0/bin $ ./artemis create /home/jboss/data

Enter an username and password for the administration:

Creating ActiveMQ Artemis instance at: /home/jboss/data
Java HotSpot(TM) Client VM warning: You have loaded library /home/jboss/apache-artemis-1.0.0/bin/lib/linux-x86_64/libartemis-native-64.so which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.

--user: is mandatory with this configuration:
Please provide the default username:
admin

--password: is mandatory with this configuration:
Please provide the default password:


--allow-anonymous: is mandatory with this configuration:
Allow anonymous access? (Y/N):
Y

You can now start the broker by executing:  

   "/home/jboss/data/bin/artemis" run

Or you can setup the broker as system service and run it in the background:

/home/jboss/data/bin/artemis-service
   /etc/init.d/artemis-service start

Now let's start the broker:

 /home/jboss/data/bin/artemis run   

Now you can run a test example on it. Start ActiveMQ Artemis first:

/home/jboss/data/bin/artemis run

Here is a simple JMS Client called ArtemisQueueDemo:

package org.apache.activemq.artemis.jms.example;

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

 
 
public class ArtemisQueueDemo  
{
   public static void main(final String[] args) throws Exception
   {
      new ArtemisQueueDemo().runExample();
   }

 
   public boolean runExample() throws Exception
   {
      Connection connection = null;
      InitialContext initialContext = null;
      try
      {
         // Step 1. Create an initial context to perform the JNDI lookup.
         Properties p = new Properties();
         p.put("java.naming.factory.initial", "org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory");
         p.put("connectionFactory.ConnectionFactory", "tcp://localhost:61616");
         p.put("queue.queue/exampleQueue", "exampleQueue");
          
         
         initialContext = new InitialContext(p);
          
       
         // lookup on the queue
         Queue queue = (Queue)initialContext.lookup("queue/exampleQueue");

         // lookup on the Connection Factory
         ConnectionFactory cf = (ConnectionFactory)initialContext.lookup("ConnectionFactory");

         // Create an authenticated JMS Connection
         connection = cf.createConnection("admin","admin");

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

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

         // Create a Text Message
         TextMessage message = session.createTextMessage("This is a text message");

         System.out.println("Sent message: " + message.getText());

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

         // Create a JMS Message Consumer
         MessageConsumer messageConsumer = session.createConsumer(queue);

         // Start the Connection
         connection.start();

         // Receive the message
         TextMessage messageReceived = (TextMessage)messageConsumer.receive(5000);

         System.out.println("Received message: " + messageReceived.getText());

         return true;
      }
      finally
      {
         // Be sure to close our JMS resources!
         if (initialContext != null)
         {
            initialContext.close();
         }
         if (connection != null)
         {
            connection.close();
         }
      }
   }
}

In order to run the above example, you can either add the Maven dependencies as indicated in the examples (examples\jms\queue) or simply add all the JAR files contained in the ARTEMIS_HOME/lib folder. You need additionally the commons-collections-X.jar in your classpath.

Output:

run:
Sent message: This is a text message
Received message: This is a text message
BUILD SUCCESSFUL (total time: 2 seconds)