How to configure an MDB singleton in a cluster

In this tutorial we will provide details how to configure MDB deployments as Clustered Singleton MDBs on WildFly 10 or later.

First of all, what is a "Clustered Singleton MDBs" ? they are a special kind of MDB which is deployed in a cluster; however only one node is active to consume messages serially. When the node fails, another node is elected as the active node's "Clustered Singleton MDBs" and starts consuming the messages.

The simplest way, though not the only one, to configure an MDB as Clustered Singleton is via the special element which can be added in the jboss-ejb3.xml file as follows:

<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
               xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:c="urn:clustering:1.1"
               xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
               version="3.1"
               impl-version="2.0">
    <assembly-descriptor>
        <c:clustering>
            <ejb-name>HelloWorldQueueMDB</ejb-name>
            <c:clustered-singleton>true</c:clustered-singleton>
        </c:clustering>
    </assembly-descriptor>
</jboss:ejb-jar>

Here is a sample MDB from the quickstart which can be configured to be clustered as Singleton:

package org.jboss.as.quickstarts.mdb;

import java.util.logging.Logger;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

 
@MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "queue/HELLOWORLDMDBQueue"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
public class HelloWorldQueueMDB implements MessageListener {

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

    /**
     * @see MessageListener#onMessage(Message)
     */
    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);
        }
    }
}

In order to let it work, you need to create the JMS destination and expose its entries:

jms-queue add --queue-address=HELLOWORLDMDBQueue --entries=queue/HELLOWORLDMDBQueue,java:jboss/exported/jms/queue/HELLOWORLDMDBQueue

Now as you deploy the MDB singleton, you will notice from the Server's log that it has been distributed to the cluster and one server was elected as singleton provider:

2017-10-19 15:28:21,325 INFO  [org.wildfly.clustering.server] (DistributedSingletonService - 1) WFLYCLSV0003: master:server-one elected as the singleton provider of the org.wildfly.ejb3.clustered.singleton service

If you check the JMS resources for this node, you will see that the default number of consumers have been created for this server:

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

On the other hand, if you try to check the JMS resources on another server in the cluster, you will see there are no available consumer for this resource:

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

Now, as proof of concept, let's stop the node who is managing the Singleton:

[domain@localhost:9990 /] /host=master/server-config=server-one:stop
{
    "outcome" => "success",
    "result" => "STOPPING"
}

As you can see from the logs of the server-two, the ownership of the Singleton has been moved:

2017-10-19 15:30:05,910 INFO  [org.wildfly.clustering.server] (DistributedSingletonService - 1) WFLYCLSV0003: master:server-two elected as the singleton provider of the org.wildfly.ejb3.clustered.singleton service

Now the server-two got transferred the consumer which can handle message consuming:

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

As ifnal note, it is worth mentioning that you can use the @org.jboss.ejb3.annotation.ClusteredSingleton in your MDB class whichrequires no extra configuration at the server, apart from running the service in a cluster.

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

Follow us on Twitter