Red Hat

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!

Eclipse MicroProfile OpenTracing comes to WildFly

Until WildFly 19 you could use Eclipse MicroProfile OpenTracing (MPOT) to trace your application using environment variables relying on the SmallRye OpenTracing implementation. With WildFly 19 you can now configure several Jaeger Tracers to be used in your applications.

Installing Jaeger

Let’s start a jaeger instance using docker :

docker run -d --name jaeger \
  -p 6831:6831/udp \
  -p 5778:5778 \
  -p 14268:14268 \
  -p 16686:16686 \
  jaegertracing/all-in-one:1.16

Now, you can navigate to http://localhost:16686 to access the Jaeger UI.

Configuring the OpenTracing subsystem

You can use either UDP or TCP to send your log spans to Jaeger. Note that those configurations are exclusive, so if you configure a TCP endpoint then Jaeger won’t use the UDP configuration.

Using UDP

First we use the WildFly CLI tool to define an outbound socket binding towards the Jaeger tracer.

[standalone@localhost:9990 /] /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=jaeger:add(host=localhost, port=6831)
{"outcome" => "success"}

Now we can define our MPOT tracer configuration:

[standalone@localhost:9990 /] /subsystem=microprofile-opentracing-smallrye/jaeger-tracer=jaeger-demo:add(sampler-type=const, sampler-param=1, reporter-log-spans=true, sender-binding=jaeger)
{"outcome" => "success"}

Using TCP

If you want to use TCP instead of UDP you need to configure the sender-endpoint and set its value to http://localhost:14268/api/traces.

[standalone@localhost:9990 /] /subsystem=microprofile-opentracing-smallrye/jaeger-tracer=jaeger-demo:write-attribute(name="sender-endpoint", value="http://localhost:14268/api/traces")
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}
[standalone@localhost:9990 /] reload

Setting the default tracer

Let’s define this new tracer as the default tracer to be used by WildFly:

[standalone@localhost:9990 /] /subsystem=microprofile-opentracing-smallrye:write-attribute(name=default-tracer, value=jaeger-demo)
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}
[standalone@localhost:9990 /] reload

Instead of relying on the default tracer attribute, you can use the web.xml deployment descriptor and set the init-parameter smallrye.opentracing.tracer.configuration to the name of the tracer to use.

Deploying a demo application

We provide a sample application at https://github.com/ehsavoie/opentracing-demo. This application is a simple JAXRS endpoint producing XML or JSON and failing randomly.

git clone git@github.com:ehsavoie/opentracing-demo.git
cd opentracing-demo
mvn clean install
cp target/opentracing-demo.war $WILDFLY_HOME/standalone/deployments
$WILDFLY_HOME/bin/standalone.sh

Now, you can navigate to http://localhost:8080/opentracing-demo to access the deployed application. Generate a few traces by clicking on the links several times. Note that in order to demonstrate error traces the application deliberately throws exceptions randomly, so don’t be surprised if some requests fail.

Now open the Jaeger UI and search for traces related to the service opentracing-demo.war, you should see the result of your previous actions.

jaegertraces

Using Git for configuration history

Until now the history of configuration in WildFly was using the folder + filename pattern. Now we have moved to a proper SCM integrating Git to manage history.

You can now take advantage of a full Git support for your configuration history:

  • every change in your configuration is now a commit.

  • you can use branches to develop in parallel.

  • you can create tags for stable points in your configuration.

  • pull configuration from a remote repository.

  • push your configuration history to a remote repository.

  • use the git-bisect tool at your disposal when things go wrong.

Now if we execute a management operation that modifies the model, for example adding a new system property using the CLI:

[standalone@localhost:9990 /] /system-property=test:add(value="test123")
{"outcome" => "success"}

What happens is:

  • The change is applied to the configuration file.

  • The configuration file is added to a new commit.

The notion of configuration has been updated with the Git support. It covers more than 'just' the standalone.xml history but also the content files (aka managed deployments).

Thus even your deployments are in history, which makes sense in a way since those deployments appear in the configuration file.

Starting with a local Git repository

To start using Git you don’t have to create the repository, WildFly can do that for you. Just start your server with the following command line:

$ __WILDFLY_HOME__/bin/standalone.sh --git-repo=local --git-branch=my_branch

If a --git-branch parameter is added then the repository will be checked out from the supplied branch. Please note that the branch will not be automatically created and must already exist in the repository. By default, if no parameter is specified, the branch master will be used. If a --git-branch parameter is added then the repository will be checked out from the supplied branch. Please note that the branch will not be automatically created and must already exist in the repository. By default, if no parameter is specified, the branch master will be used.

Starting with a remote Git Repository

To start WildFly with a configuration from a remote Git repository is simple too, just use the following command line:

$ __WILDFLY_HOME__/bin/standalone.sh --git-repo=https://github.com/USER_NAME/wildfly-config.git --git-branch=master

Be careful with this as the first step is to delete the configuration files to avoid conflicts when pulling for the first time.

Note that you can use remote aliases if you have added them to your .gitconfig.

Snapshots

In addition to the commits taken by the server as described above, you can manually take snapshots which will be stored as tags in the Git repository.

The ability to take a snapshot has been enhanced to allow you to add a comment to it. This comment will be used when creating the Git tag.

This is how you can take a snapshot from the JBoss CLI tool:

[standalone@localhost:9990 /] :take-snapshot(name="snapshot", comment="1st snapshot")
{
    "outcome" => "success",
    "result" => "1st snapshot"
}

You can also use the CLI to list all the snapshots:

[standalone@localhost:9990 /] :list-snapshots
{
    "outcome" => "success",
    "result" => {
        "directory" => "",
        "names" => [
            "snapshot : 1st snapshot",
            "refs/tags/snapshot",
            "snapshot2 : 2nd snapshot",
            "refs/tags/snapshot2"
        ]
    }
}

To delete a particular snapshot:

[standalone@localhost:9990 /] :delete-snapshot(name="snapshot2")
{"outcome" => "success"}

Note that this is a real Git repository, thus using the git client of your choice you can list those tags, or browse the history.

Publishing

You may 'publish' your changes on a remote repository (provided you have write access to it) so you can share them. For example, if you want to publish on GitHub, you need to create a token and allow for full control of the repository. Then use that token in an Elytron configuration file like this:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <authentication-client xmlns="urn:elytron:1.1">
        <authentication-rules>
            <rule use-configuration="test-login">
            </rule>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="test-login">
                <sasl-mechanism-selector selector="BASIC" />
                <set-user-name name="$GITHUB_USERNAME" />
                <credentials>
                    <clear-password password="$GITHUB_TOKEN" />
                </credentials>
                <set-mechanism-realm name="testRealm" />
            </configuration>
        </authentication-configurations>
    </authentication-client>
</configuration>

Then, to publish your changes:

[standalone@localhost:9990 /] :publish-configuration(location="origin")
{"outcome" => "success"}

References

For the official documentation regarding Git history : Official Documentation.

back to top