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