The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

使用JMS

This guide demonstrates how your Quarkus application can use JMS messaging via the Apache Qpid JMS AMQP client, or alternatively the Apache ActiveMQ Artemis JMS client.

这项技术被认为是preview。

preview(预览) 中,不保证向后兼容和在生态系统中的存在。具体的改进可能需要改变配置或API,并且正在计划变得 稳定 。欢迎在我们的 邮件列表 中提供反馈,或在我们的 GitHub问题列表 中提出问题。

For a full list of possible statuses, check our FAQ entry.

先决条件

完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • JDK 17+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.9.6

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

  • A running Artemis server, or Docker to start one

应用结构

In this guide, we are going to generate (random) prices in one component. These prices are written to a queue (prices) using a JMS client. Another component reads from the prices queue and stores the latest price. The data can be fetched from a browser using a fetch button from a Jakarta REST resource.

The guide can be used either via the Apache Qpid JMS AMQP client as detailed immediately below, or alternatively with the Apache ActiveMQ Artemis JMS client given some different configuration as detailed later.

Qpid JMS - AMQP

In the detailed steps below we will use the Apache Qpid JMS client via the Quarkus Qpid JMS extension. Qpid JMS uses the AMQP 1.0 ISO standard as its wire protocol, allowing it to be used with a variety of AMQP 1.0 servers and services such as ActiveMQ Artemis, ActiveMQ 5, Qpid Broker-J, Qpid Dispatch router, Azure Service Bus, and more.

解决方案

我们建议您按照下一节的说明逐步创建应用程序。然而,您可以直接转到已完成的示例。

Clone the Git repository: git clone https://github.com/amqphub/quarkus-qpid-jms-quickstart.git, or download an archive.

创建Maven项目

首先,我们需要一个新的工程项目。用以下命令创建一个新项目:

CLI
quarkus create app org.acme:jms-quickstart \
    --extension='rest,qpid-jms' \
    --no-code
cd jms-quickstart

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.9.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=jms-quickstart \
    -Dextensions='rest,qpid-jms' \
    -DnoCode
cd jms-quickstart

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

For Windows users:

  • If using cmd, (don’t use backward slash \ and put everything on the same line)

  • If using Powershell, wrap -D parameters in double quotes e.g. "-DprojectArtifactId=jms-quickstart"

This command generates a new project importing the quarkus-qpid-jms extension:

pom.xml
<dependency>
    <groupId>org.amqphub.quarkus</groupId>
    <artifactId>quarkus-qpid-jms</artifactId>
</dependency>
build.gradle
implementation("org.amqphub.quarkus:quarkus-qpid-jms")

Starting the broker

Then, we need an AMQP broker. In this case we will use an Apache ActiveMQ Artemis server. You can follow the instructions from the Apache Artemis website or start a broker via docker using the ArtemisCloud container image:

docker run -it --rm -p 8161:8161 -p 61616:61616 -p 5672:5672 -e AMQ_USER=quarkus -e AMQ_PASSWORD=quarkus quay.io/artemiscloud/activemq-artemis-broker:1.0.25

The price producer

Create the src/main/java/org/acme/jms/PriceProducer.java file, with the following content:

package org.acme.jms;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSContext;

import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;

/**
 * A bean producing random prices every 5 seconds and sending them to the prices JMS queue.
 */
@ApplicationScoped
public class PriceProducer implements Runnable {

    @Inject
    ConnectionFactory connectionFactory;

    private final Random random = new Random();
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    void onStart(@Observes StartupEvent ev) {
        scheduler.scheduleWithFixedDelay(this, 0L, 5L, TimeUnit.SECONDS);
    }

    void onStop(@Observes ShutdownEvent ev) {
        scheduler.shutdown();
    }

    @Override
    public void run() {
        try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) {
            context.createProducer().send(context.createQueue("prices"), Integer.toString(random.nextInt(100)));
        }
    }
}

The price consumer

The price consumer reads the prices from JMS, and stores the last one. Create the src/main/java/org/acme/jms/PriceConsumer.java file with the following content:

package org.acme.jms;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSConsumer;
import jakarta.jms.JMSContext;
import jakarta.jms.JMSException;
import jakarta.jms.Message;

import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;

/**
 * A bean consuming prices from the JMS queue.
 */
@ApplicationScoped
public class PriceConsumer implements Runnable {

    @Inject
    ConnectionFactory connectionFactory;

    private final ExecutorService scheduler = Executors.newSingleThreadExecutor();

    private volatile String lastPrice;

    public String getLastPrice() {
        return lastPrice;
    }

    void onStart(@Observes StartupEvent ev) {
        scheduler.submit(this);
    }

    void onStop(@Observes ShutdownEvent ev) {
        scheduler.shutdown();
    }

    @Override
    public void run() {
        try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) {
            JMSConsumer consumer = context.createConsumer(context.createQueue("prices"));
            while (true) {
                Message message = consumer.receive();
                if (message == null) return;
                lastPrice = message.getBody(String.class);
            }
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
}

The price resource

Finally, let’s create a simple Jakarta REST resource to show the last price. Create the src/main/java/org/acme/jms/PriceResource.java file with the following content:

package org.acme.jms;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

/**
 * A simple resource showing the last price.
 */
@Path("/prices")
public class PriceResource {

    @Inject
    PriceConsumer consumer;

    @GET
    @Path("last")
    @Produces(MediaType.TEXT_PLAIN)
    public String last() {
        return consumer.getLastPrice();
    }
}

HTML页面

最后,HTML页面使用SSE读取转换后的价格。

Create the src/main/resources/META-INF/resources/prices.html file, with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Prices</title>

    <link rel="stylesheet" type="text/css"
          href="https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly.min.css">
    <link rel="stylesheet" type="text/css"
          href="https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly-additions.min.css">
</head>
<body>
<div class="container">

    <h2>Last price</h2>
    <div class="row">
    <p class="col-md-12"><button id="fetch">Fetch</button>The last price is <strong><span id="content">N/A</span>&nbsp;&euro;</strong>.</p>
    </div>
</div>
</body>
<script>
    document.getElementById("fetch").addEventListener("click", function() {
        fetch("/prices/last").then(function (response) {
            response.text().then(function (text) {
                document.getElementById("content").textContent = text;
            })
        })
    })
</script>
</html>

Nothing spectacular here. On each fetch, it updates the page.

Configure the Qpid JMS properties

We need to configure the Qpid JMS properties used by the extension when injecting the ConnectionFactory.

This is done in the src/main/resources/application.properties file.

# Configures the Qpid JMS properties.
quarkus.qpid-jms.url=amqp://localhost:5672
quarkus.qpid-jms.username=quarkus
quarkus.qpid-jms.password=quarkus

More detail about the configuration are available in the Quarkus Qpid JMS documentation.

让它运行起来

If you followed the instructions, you should have the Artemis server running. Then, you just need to run the application using:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

Open http://localhost:8080/prices.html in your browser.

以本机可执行文件运行

您可以使用以下命令构建本机可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

Or, if you don’t have GraalVM installed, you can instead use Docker to build the native executable using:

CLI
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
Maven
./mvnw install -Dnative -DskipTests -Dquarkus.native.container-build=true
Gradle
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true

and then run with:

./target/jms-quickstart-1.0.0-SNAPSHOT-runner

Open http://localhost:8080/prices.html in your browser.


Artemis JMS

The above steps detailed using the Qpid JMS AMQP client, however the guide can also be used with the Artemis JMS client. Many of the individual steps are exactly as previously detailed above for Qpid JMS. The individual component code is the same. The only differences are in the dependency for the initial project creation, and the configuration properties used. These changes are detailed below and should be substituted for the equivalent step during the sequence above.

解决方案

You can go right to the completed example.

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或下载一个 存档

The Artemis JMS solution is located in the jms-quickstart directory.

创建Maven项目

Create a new project with the following command:

CLI
quarkus create app org.acme:jms-quickstart \
    --extension='rest,artemis-jms' \
    --no-code
cd jms-quickstart

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.9.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=jms-quickstart \
    -Dextensions='rest,artemis-jms' \
    -DnoCode
cd jms-quickstart

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

For Windows users:

  • If using cmd, (don’t use backward slash \ and put everything on the same line)

  • If using Powershell, wrap -D parameters in double quotes e.g. "-DprojectArtifactId=jms-quickstart"

This creates a new project importing the quarkus-artemis-jms extension:

pom.xml
<dependency>
    <groupId>io.quarkiverse.artemis</groupId>
    <artifactId>quarkus-artemis-jms</artifactId>
</dependency>
build.gradle
implementation("io.quarkiverse.artemis:quarkus-artemis-jms")

With the project created, you can resume from Starting the broker in the detailed steps above and proceed until configuring the application.properties file, when you should use the Artemis configuration below instead.

Configure the Artemis properties

We need to configure the Artemis connection properties. This is done in the src/main/resources/application.properties file.

# Configures the Artemis properties.
quarkus.artemis.url=tcp://localhost:61616
quarkus.artemis.username=quarkus
quarkus.artemis.password=quarkus

With the Artemis properties configured, you can resume the steps above from 让它运行起来.

配置参考

To know more about how to configure the Artemis JMS client, have a look at the documentation of the extension.

Related content