Using a Message Broker - PART 1: Docker Image

In this guide, we will extend the example created in WildFly Java Microservice - PART 1: Docker Image and add an external Message Broker connectivity.

Prerequisites

To complete this guide, you need:

External Message Broker

Apache Artemis

We will use Apache Artemis in this guide in its containerized version: see Artemis Official Image.

Start Apache Artemis:

podman network create demo-network

podman run --rm --name my-artemis \
    --network=demo-network \
    -p 8161:8161 \
    -p 61616:61616\
    -e AMQ_USER=admin \
    -e AMQ_PASSWORD=admin \
    -e AMQ_DATA_DIR=//home/jboss/data \
    quay.io/artemiscloud/activemq-artemis-broker-kubernetes:latest
Note
we started the container with the --rm flag: this way it is disposed of automatically when we stop it
Note
We started the my-artemis container with the --network=bridge option: later in this guide, this will allow us to connect to the my-artemis container from the my-jms-app container;

Maven Project

pom.xml

dependencies

Add the following dependencies to the pom-xml file dependencies section:

        <dependency>
            <groupId>jakarta.ejb</groupId>
            <artifactId>jakarta.ejb-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.enterprise</groupId>
            <artifactId>jakarta.enterprise.cdi-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jakarta.jms</groupId>
            <artifactId>jakarta.jms-api</artifactId>
            <scope>provided</scope>
        </dependency>

wildfly-maven-plugin

We need to add some features that are necessary when to connect to the broker and to support a Message Driven Bean.

We can add these features by adding the remote-activemq layer to our application; add the following to the the wildfly-maven-plugin configuration in the pom.xml file:

    <layers>
        …​
        <layer>ejb</layer>
        <layer>remote-activemq</layer>
        …​
    </layers>

You should end up with the wildfly-maven-plugin configured like in the following:

    <plugin>
        <groupId>org.wildfly.plugins</groupId>
        <artifactId>wildfly-maven-plugin</artifactId>
        <version>5.0.0.Final</version>
        <configuration>
            <feature-packs>
                <feature-pack>
                    <location>org.wildfly:wildfly-galleon-pack:32.0.0.Final</location>
                </feature-pack>
                <feature-pack>
                    <location>org.wildfly.cloud:wildfly-cloud-galleon-pack:7.0.0.Final</location>
                </feature-pack>
            </feature-packs>
            <layers>
                <layer>cloud-server</layer>
                <layer>ejb</layer>
                <layer>remote-activemq</layer>
            </layers>
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>package</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

Java Classes

Add the following classes to the project:

org.wildfly.examples.mdb.GettingStartedQueueMDB :
package org.wildfly.examples.mdb;

import jakarta.ejb.ActivationConfigProperty;
import jakarta.ejb.MessageDriven;
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.MessageListener;
import jakarta.jms.TextMessage;

import java.util.logging.Logger;

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

    private static final Logger LOGGER = Logger.getLogger(GettingStartedQueueMDB.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);
        }
    }
}
org.wildfly.examples.GettingStartedQueueEndpoint :
package org.wildfly.examples;

import jakarta.annotation.Resource;
import jakarta.inject.Inject;
import jakarta.jms.JMSContext;
import jakarta.jms.JMSDestinationDefinition;
import jakarta.jms.JMSDestinationDefinitions;
import jakarta.jms.JMSException;
import jakarta.jms.Queue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@JMSDestinationDefinitions(
    value = {
        @JMSDestinationDefinition(
            name = "java:/queue/gettingStartedQueue",
            interfaceName = "jakarta.jms.Queue",
            destinationName = "getting-started-queue",
            properties = {"enable-amq1-prefix=false"}
        )
    }
)
@Path("/message")
public class GettingStartedQueueEndpoint {

    @Resource(lookup="java:/queue/gettingStartedQueue")
    private Queue queue;

    @Inject
    private JMSContext context;

    @GET
    @Path("/send")
    @Produces(MediaType.TEXT_PLAIN)
    public Response sendMessage(final @QueryParam("content") String content) throws JMSException {
        String response = "Sent " + content + " to " + queue.getQueueName();
        context.createProducer().send(queue, content);
        return Response.ok(response).build();
    }
}

Build the application

$ mvn clean package
...
[INFO] Copy deployment /home/ehugonne/dev/wildfly/guides/get-started-microservices-on-kubernetes/simple-microservice-jms/target/ROOT.war to /home/ehugonne/dev/wildfly/guides/get-started-microservices-on-kubernetes/simple-microservice-jms/target/server/standalone/deployments/ROOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.848 s
[INFO] Finished at: 2024-07-18T10:06:17+02:00
[INFO] ------------------------------------------------------------------------

Docker Image

Build the Docker Image

Build the Docker Image with the following command:

$ podman build -t my-jms-app:latest .
STEP 1/3: FROM quay.io/wildfly/wildfly-runtime:latest
STEP 2/3: COPY --chown=jboss:root target/server $JBOSS_HOME
-→ 4609f8ed0c7f
STEP 3/3: RUN chmod -R ug+rwX $JBOSS_HOME
COMMIT my-jms-app:latest
-→ db4677f5bf4f
Successfully tagged localhost/my-jms-app:latest
db4677f5bf4f471f5624bd63a21fce3d91b7b3b93e985d3e86a8a4b0682d85cd

Run the Docker Image

Note that, when running the my-jms-app:latest Docker Image:

podman run --rm --network=demo-network -p 8080:8080 -p 9990:9990 \
    --name=my-jms-app \
    -e JBOSS_MESSAGING_CONNECTOR_HOST=my-artemis \
    my-jms-app:latest
Note
We started the my-jms-app container with the --network=demo-network option just like we did when we started the my-artemis container: the two containers now run in the same demo-network network and we can connect to my-artemis container from the my-jms-app container using the my-artemis name.

Check the application

Hit the following URLs, using a utility like curl:

Send and consume messages using a queue:
$ curl -X GET http://localhost:8080/hello/message/send?content=Hello%20World
Sent Hello World to getting-started-queue
< Back to Guides