Coding a JMS client to a JBoss EAP 6 - WildFly JMS cluster

User Rating: 5 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Active
 

This tutorial demonstrates how to code a remote JMS client application which connects to a full-ha profile configured on EAP 6 / AS 7 domain. We will show how sessions can be balanced on different cluster nodes.

First of all, here is our simple cluster, which is made up just of two nodes:

jms cluster jboss 7 wildfly

Our JMS Client needs to perform the following steps:

1. Get an initial context, using a comma separated list of servers

2. Look-up a JMS Connection Factory object from JNDI  

3. Look-up the JMS Queue object from JNDI

4. Create two JMS connections from the same factory. Since ConnectionFactory used round-robin load balancing, the first connection will be toward the server1 and the second connection will be toward server2       

5. Create the JMS sessions and the producers for each connection

6. Send 100 messages on each Session

Here is the code, which has been adapted from JBoss EAP quickstarts: https://github.com/jboss-developer/jboss-eap-quickstarts

package com.sample.jms;

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

public class HelloWorldJMSClientCluster {

    private static final Logger log = Logger.getLogger(HelloWorldJMSClientCluster.class.getName());

    // Set up all the default values
    private static final String DEFAULT_MESSAGE = "Hello, World!";
    private static final String DEFAULT_CONNECTION_FACTORY = "jms/RemoteConnectionFactory";
    private static final String DEFAULT_DESTINATION = "jms/queue/test";
    private static final String DEFAULT_MESSAGE_COUNT = "100";
    private static final String DEFAULT_USERNAME = "jmsuser";
    private static final String DEFAULT_PASSWORD = "Password1!";
    private static final String INITIAL_CONTEXT_FACTORY = "org.jboss.naming.remote.client.InitialContextFactory";
    private static final String PROVIDER_URL = "remote://localhost:4447,remote://localhost:4547";

    public static void main(String[] args) throws Exception {

        ConnectionFactory connectionFactoryA = null;
        ConnectionFactory connectionFactoryB = null;
        Connection connectionA = null;
        Connection connectionB = null;
        Session sessionA = null;
        Session sessionB = null;
        MessageProducer producerA = null;
        MessageProducer producerB = null;
        // MessageConsumer consumer = null;
        Destination destination = null;
        TextMessage message = null;
        Context context = null;

        try {
            // Set up the context for the JNDI lookup
            final Properties env = new Properties();
            env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL, PROVIDER_URL));
            env.put(Context.SECURITY_PRINCIPAL, System.getProperty("username", DEFAULT_USERNAME));
            env.put(Context.SECURITY_CREDENTIALS, System.getProperty("password", DEFAULT_PASSWORD));
            context = new InitialContext(env);

            // Perform the JNDI lookups
            String connectionFactoryString = System.getProperty("connection.factory", DEFAULT_CONNECTION_FACTORY);
            log.info("Attempting to acquire connection factory \"" + connectionFactoryString + "\"");
            connectionFactoryA = (ConnectionFactory) context.lookup(connectionFactoryString);
            connectionFactoryB = (ConnectionFactory) context.lookup(connectionFactoryString);

            log.info("Found connection factory \"" + connectionFactoryString + "\" in JNDI");

            String destinationString = System.getProperty("destination", DEFAULT_DESTINATION);
            log.info("Attempting to acquire destination \"" + destinationString + "\"");
            destination = (Destination) context.lookup(destinationString);
            log.info("Found destination \"" + destinationString + "\" in JNDI");

            // Create the JMS connection, session, producer, and consumer
            connectionA = connectionFactoryA.createConnection(System.getProperty("username", DEFAULT_USERNAME), System.getProperty("password", DEFAULT_PASSWORD));
            sessionA = connectionA.createSession(false, Session.AUTO_ACKNOWLEDGE);
            producerA = sessionA.createProducer(destination);
            // consumer = session.createConsumer(destination);
            connectionA.start();

            connectionB = connectionFactoryB.createConnection(System.getProperty("username", DEFAULT_USERNAME), System.getProperty("password", DEFAULT_PASSWORD));
            sessionB = connectionB.createSession(false, Session.AUTO_ACKNOWLEDGE);
            producerB = sessionB.createProducer(destination);
            // consumer = session.createConsumer(destination);
            connectionB.start();

            int count = Integer.parseInt(System.getProperty("message.count", DEFAULT_MESSAGE_COUNT));
            String content = System.getProperty("message.content", DEFAULT_MESSAGE);

            log.info("Sending " + count + " messages with content: " + content);

          
            for (int i = 0; i < count; i++) {
                message = sessionA.createTextMessage(content);
                producerA.send(message);
            }
            log.info("Sent messages ");

            for (int i = 0; i < count; i++) {
                message = sessionB.createTextMessage(content);
                producerB.send(message);
            }
            log.info("Sent messages ");

        } catch (Exception e) {
            log.severe(e.getMessage());
            throw e;
        } finally {
            if (context != null) {
                context.close();
            }

            // closing the connection takes care of the session, producer, and consumer
            if (connectionA != null) {
                connectionA.close();
            }
            if (connectionB != null) {
                connectionB.close();
            }
        }
    }
}

The most interesting part is the provider URL connection string, which contains the list of the servers in the cluster:

private static final String PROVIDER_URL = "remote://localhost:4447,remote://localhost:4547";

In order to run our JMS client, you need to bind a Queue named testQueue to the JNDI binding "java:jboss/exported/jms/queue/test"  

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

Finally, you need to create a user named "jmsuser" as part of the "guest" group. See this tutorial for more information: How to code a remote JMS client for WildFly 8

Now execute the class and check that messages have been evenly distributed between server-one and server-two:

[domain@localhost:9999 /] /host=master/server=server-one/subsystem=messaging/hornetq-server=default/jms-queue=testQueue:read-resource(include-runtime=true)
{
    "outcome" => "success",
    "result" => {
        "consumer-count" => 0,
        "dead-letter-address" => "jms.queue.DLQ",
        "delivering-count" => 0,
        "durable" => true,
        "entries" => [
            "queue/test",
            "java:jboss/exported/jms/queue/test"
        ],
        "expiry-address" => "jms.queue.ExpiryQueue",
        "message-count" => 100L,
        "messages-added" => 100L,
        "paused" => false,
        "queue-address" => "jms.queue.testQueue",
        "scheduled-count" => 0L,
        "selector" => undefined,
        "temporary" => false
    }
}
[domain@localhost:9999 /] /host=master/server=server-two/subsystem=messaging/hornetq-server=default/jms-queue=testQueue:read-resource(include-runtime=true)
{
    "outcome" => "success",
    "result" => {
        "consumer-count" => 0,
        "dead-letter-address" => "jms.queue.DLQ",
        "delivering-count" => 0,
        "durable" => true,
        "entries" => [
            "queue/test",
            "java:jboss/exported/jms/queue/test"
        ],
        "expiry-address" => "jms.queue.ExpiryQueue",
        "message-count" => 100L,
        "messages-added" => 100L,
        "paused" => false,
        "queue-address" => "jms.queue.testQueue",
        "scheduled-count" => 0L,
        "selector" => undefined,
        "temporary" => false
    }
}

Changing the balancing policy

The default balancing policy is a org.hornetq.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy. You can opt for the following alternatives:

  • FirstElementConnectionLoadBalancingPolicy
  • RandomConnectionLoadBalancingPolicy
  • RandomStickyConnectionLoadBalancingPolicy
  • RoundRobinConnectionLoadBalancingPolicy

The balancing policy can be changed on the client side or on the server side. In order to change it on the server side, include the connection-load-balancing-policy-class-name in your connection factory definition. For example:

<connection-factory name="RemoteConnectionFactory">
   . . .
  <connection-load-balancing-policy-class-name>
  org.hornetq.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy
  </connection-load-balancing-policy-class-name>
</connection-factory>

Coding a WildFly JMS client

When running WildFly, you must be aware that the JMS Server is mediated by Undertow Web server which does an HTTP upgrade to HornetQ. In terms of code, what you need to change is the PROVIDER_URL String which now references the HTTP servers:

private static final String PROVIDER_URL = "http-remoting://127.0.0.1:8080,http-remoting://127.0.0.1:8180";

 

 

Related articles available on mastertheboss.com

How to configure a Queue in JBoss ?

This article has been moved here: JBoss JMS configuration

How to create a Queue with Jmx Console ?

  Bring up the JMX Console in your browser and look for the sect

JBoss JMS Queue example

The following article shows how to create a simple JMS Queue Prod

JBoss JMS Topic example

The following article shows how to create a simple JMS Topic Publ

JBoss HornetQ simple tutorial

HornetQ is an open source project to build a multi-protocol, embe

How do I configure a Queue/Topic to work in a cluster?

  JBoss AS 5 Just set the Clustered attribute to "true" in your