Pluging Apache Qpid into WildFly

Using JBoss Generic JMS Resource Adapter you can use a JMS compatible client to connect WildFly to any broker. This article will describe how to do this with Apache Qpid and thus use JMS over AMQP.

Installing Apache Qpid

You need to download and untar Apache Qpid Broker-J 8.0.0 from https://qpid.apache.org/download.html. You need to allow for anonymous access. Please use the initial-config.json configuration file. Note that we will start Apache Qpid HTTP server on 9080 to avoid port conflict with WildFly.

cd $INSTALL_DIR
tar xvzf qpid-broker-8.0.0-bin.tgz
export QPID_WORK=$INSTALL_DIR/qpid-broker/8.0.0/work
cd pid-broker/8.0.0/bin
./qpid-server -icp initial-config.json -prop "qpid.http_port=9080"

Now you can connect to the Qpid web interface using guest/guest. You can see that we have created 2 queues: outQueue and testQueue.

Configuring WildFly

We are going to use the JBoss Generic JMS Resource Adapter. For this we need to deploy a module for the JMS provider to connect to Apache Qpid. Download the archive qpid-provider.tar.gz which provides the module to connect to Apache Qpid. Then we start WildFly with the full profile.

cd $WILDFLY_HOME
tar xvzf qpid-provider.tar.gz
cd $WILDFLY_HOME/bin
./standalone.sh -c standalone-full.xml

To configure WildFly you need to execute the following operations using the jboss-cli. You can use the following script qpid.cli.

First we will enable the use of property replacement in our deployment for easier configurability:

/subsystem=ee:write-attribute(name=spec-descriptor-property-replacement, value=true)
/subsystem=ee:write-attribute(name=annotation-property-replacement, value=true)
/subsystem=ee:write-attribute(name=jboss-descriptor-property-replacement, value=true)

Then we need to configure the JNDI provider for Apache Qpid:

/subsystem=naming/binding=java\:global\/qpid:add(binding-type=external-context, class=javax.naming.InitialContext, module=org.jboss.genericjms.provider, environment={java.naming.factory.initial=org.apache.qpid.jms.jndi.JmsInitialContextFactory, queue.testQueue=testQueue, queue.outQueue=outQueue, connectionfactory.QpidBroker="amqp://localhost:5672?jms.username=guest&jms.password=guest"})
/subsystem=naming/binding=java\:\/jms\/qpid\/queue\/testQueue:add(binding-type=lookup, value=java\:global\/qpid\/testQueue)
/subsystem=naming/binding=java\:\/jms\/qpid\/queue\/outQueue:add(binding-type=lookup, lookup=java\:global\/qpid\/outQueue)

Last we need to configure the resource adapter to connect to our Apache Qpid broker:

/subsystem=resource-adapters/resource-adapter=apache-qpid:add(module=org.jboss.genericjms, transaction-support=NoTransaction)
/subsystem=resource-adapters/resource-adapter=apache-qpid/connection-definitions=QPIDCF:add(jndi-name=java\:\/jms\/qpid\/cf\/QpidBroker, class-name=org.jboss.resource.adapter.jms.JmsManagedConnectionFactory)
/subsystem=resource-adapters/resource-adapter=apache-qpid/connection-definitions=QPIDCF/config-properties=JndiParameters:add(value="java.naming.factory.initial=org.apache.qpid.jms.jndi.JmsInitialContextFactory;connectionfactory.QpidBroker=amqp://localhost:5672?jms.username=guest&jms.password=guest")

Running the example

You can download the example code from this GitHub repository.

The example consists of two parts : a client that will send a message to the testQueue to be processed by the Message Driven Bean which will send a message on the outQueue to be consumed by the client.

This is the client code that sends a message:

try (Connection connection = factory.createConnection("guest", "guest")) {
    connection.start();
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    MessageProducer messageProducer = session.createProducer(queue);

    TextMessage message = session.createTextMessage("Hello world!");
    messageProducer.send(message, DeliveryMode.NON_PERSISTENT, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
}

The MDB code is in RemoteQueueMDB.java and will send back a message to be consumed by the client.

@Override
public void onMessage(Message message) {
    try (QueueConnection queueConnection = qcf.createQueueConnection("guest", "guest");
    QueueSession queueSession = queueConnection.createQueueSession(true, Session.SESSION_TRANSACTED);
    QueueSender queueSender = queueSession.createSender(outQueue)) {
    if (message instanceof TextMessage) {
        txtMsg = (TextMessage) message;
        msgCnt++;
        queueSender.send(message);
        queueSession.commit();
    } else {
        LOG.warnf("MDB[%d] Message of wrong type: %s", mdbID, message.getClass().getName());
    }
}

Build using Apache Maven, then deploy the MDB by copying remote-mdb-ejb-1.0.0.jar into the deployments folder and run the client:

cd client
mvn "-Dexec.args=-classpath %classpath org.wildfly.jms.demo.qpid.client.HelloWorld" -Dexec.executable=/usr/lib/jvm/java-11/bin/java -Dexec.classpathScope=runtime org.codehaus.mojo:exec-maven-plugin:1.5.0:exec

You should see the following message in the traces showing that all went nicely and that the client has received the message :

Message received 419a7c4d-afe0-4dc2-9cf0-f17f531eb1ba:1:1:1-1 with text Hello world!