Hello everyone! Here it is – our final post in the series about Quarkus! (By the way, check out our webinar . We will show how to get started "from scratch" or migrate existing solutions)

In post, we discussed the relevant tools that can be used to quantitatively assess the improvements gained from modernizing Java applications.
Starting from version 0.17.0, supports the use of the Advanced Message Queuing Protocol (), which is an open standard for business messaging between applications or organizations.
is a service built on the open-source project and implements a messaging mechanism on the platform. For more details on how it works, see . Today, we will show how to combine AMQ Online and Quarkus to build a modern messaging system based on OpenShift using two new technologies related to message processing.
It is assumed that you have already deployed AMQ Online on the OpenShift platform (if not, see ).
. To begin, we will create a Quarkus application that will represent a simple order processing system using reactive messaging. This application will include an order generator that sends orders to a message queue at fixed intervals, as well as an order processor that will handle messages from the queue and generate confirmations available for viewing in a browser.
After creating the application, we will show how to integrate messaging system configuration into it and utilize AMQ Online to initialize the required resources on this system.
Quarkus Application
Our Quarkus application runs on OpenShift and is a modified version of the . A complete example of the client part can be found .
Order Generator
The generator monotonously sends incrementing order IDs to the address "orders" every 5 seconds.
@ApplicationScoped
public class OrderGenerator {
private int orderId = 1;
@Outgoing("orders")
public Flowable generate() {
return Flowable.interval(5, TimeUnit.SECONDS)
.map(tick -> orderId++);
}
}
Order Processor
The order handler is even simpler; it just returns the confirmation ID to the "confirmations" address.
@ApplicationScoped
public class OrderProcessor {
@Incoming("orders")
@Outgoing("confirmations")
public Integer process(Integer order) {
// The confirmation ID is equal to double the order ID <img draggable="false" class="emoji" alt=":-)" src="https://s.w.org/images/core/emoji/11.2.0/svg/1f642.svg">
return order * 2;
}
}
Confirmation Resources
A confirmation resource is an HTTP endpoint for listing confirmations generated as a result of our application's work.
@Path("/confirmations")
public class ConfirmationResource {
@Inject
@Stream("confirmations") Publisher orders;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello";
}
@GET
@Path("/stream")
@Produces(MediaType.SERVER_SENT_EVENTS)
public Publisher stream() {
return orders;
}
}
Settings
To connect to AMQ Online, our application will need some configuration data, specifically: Quarkus connector configuration, AMQP endpoint information, and client credentials. It's best to keep all configuration data in one place, but we'll intentionally separate them to show possible configuration options for the Quarkus application.
Connectors
Connector configuration can be provided at compile time using the application's properties file:
mp.messaging.outgoing.orders.connector=smallrye-amqp
mp.messaging.incoming.orders.connector=smallrye-amqp
To keep things simple, we'll use a message queue only for the "orders" address. The "confirmations" address in our application will use an in-memory queue.
AMQP Endpoint
At compile time, the host name and port number for the AMQP endpoint are unknown, so they need to be injected. The endpoint can be specified in the configmap created by AMQ Online; thus, we will define them through environment variables in the application manifest:
spec:
template:
spec:
containers:
- env:
- name: AMQP_HOST
valueFrom:
configMapKeyRef:
name: quarkus-config
key: service.host
- name: AMQP_PORT
valueFrom:
configMapKeyRef:
name: quarkus-config
key: service.port.amqp
Credentials
A service account token can be used to authenticate our application in OpenShift. To do this, we first need to create a custom ConfigSource that will read the authentication token from the pod's filesystem:
public class MessagingCredentialsConfigSource implements ConfigSource {
private static final Set propertyNames;
static {
propertyNames = new HashSet();
propertyNames.add("amqp-username");
propertyNames.add("amqp-password");
}
@Override
public Set getPropertyNames() {
return propertyNames;
}
@Override
public Map getProperties() {
try {
Map properties = new HashMap();
properties.put("amqp-username", "@@serviceaccount@@");
properties.put("amqp-password", readTokenFromFile());
return properties;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public String getValue(String key) {
if ("amqp-username".equals(key)) {
return "@@serviceaccount@@";
}
if ("amqp-password".equals(key)) {
try {
return readTokenFromFile();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return null;
}
@Override
public String getName() {
return "messaging-credentials-config";
}
private static String readTokenFromFile() throws IOException {
return new String(Files.readAllBytes(Paths.get("/var/run/secrets/kubernetes.io/serviceaccount/token")), StandardCharsets.UTF_8);
}
}
Building and Deploying the Application
Since the application needs to be compiled into an executable file, a GraalVM virtual machine will be required. For more details on setting up the environment for this, see the relevant instructions in .
Then, following the instructions provided there, download the source code, build it, and deploy our application:
git clone https://github.com/EnMasseProject/enmasse-example-clients
cd enmasse-example-clients/quarkus-example-client
oc new-project myapp
mvn -Pnative -Dfabric8.mode=openshift -Dfabric8.build.strategy=docker package fabric8:build fabric8:resource fabric8:apply
After these commands, the application will be deployed, but it won't run until we set up the necessary messaging resources in AMQ Online.
Configuring the Messaging System
Now we need to specify the resources in the messaging system that our application requires. To do this, we need to create: 1) an address space to initialize the endpoint for the messaging system; 2) an address to configure the addresses we use in our application; 3) a messaging system user to set up client credentials.
Address Space
The AddressSpace object in AMQ Online is a group of addresses that share connection endpoints, as well as authentication and authorization policies. When creating an address space, you can specify how the messaging system endpoints will be provided:
apiVersion: enmasse.io/v1beta1
kind: AddressSpace
metadata:
name: quarkus-example
spec:
type: brokered
plan: brokered-single-broker
endpoints:
- name: messaging
service: messaging
exports:
- name: quarkus-config
kind: configmap
Address
Addresses are used for sending and receiving messages. Each address has a type that defines its semantics, as well as a plan that specifies the amount of reserved resources. An address can be defined, for example, like this:
apiVersion: enmasse.io/v1beta1
kind: Address
metadata:
name: quarkus-example.orders
spec:
address: orders
type: queue
plan: brokered-queue
Messaging System User
To ensure that only trusted applications can send and receive messages to your addresses, a user must be created in the messaging system. For applications running on the cluster, clients can be authenticated using an OpenShift service account. A user "serviceaccount" can be defined, for example, like this:
apiVersion: user.enmasse.io/v1beta1
kind: MessagingUser
metadata:
name: quarkus-example.app
spec:
username: system:serviceaccount:myapp:default
authentication:
type: serviceaccount
authorization:
- operations: ["send", "recv"]
addresses: ["orders"]
Permissions for Application Configuration
For AMQ Online to create the configmap we used to inject AMQP endpoint information, a role and role binding (Role and RoleBinding) must be specified:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: quarkus-config
spec:
rules:
- apiGroups: [ "" ]
resources: [ "configmaps" ]
verbs: [ "create" ]
- apiGroups: [ "" ]
resources: [ "configmaps" ]
resourceNames: [ "quarkus-config" ]
verbs: [ "get", "update", "patch" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: quarkus-config
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: quarkus-config
subjects:
- kind: ServiceAccount
name: address-space-controller
namespace: amq-online-infra
How to Apply Configurations
You can apply the messaging system configuration like this:
cd enmasse-example-clients/quarkus-example-client
oc project myapp
oc apply -f src/main/resources/k8s/addressspace
oc apply -f src/main/resources/k8s/address
Application Verification
To ensure that the application has started, let's first check if the relevant addresses have been created and are active:
until [[ `oc get address quarkus-example.prices -o jsonpath='{.status.phase}'` == "Active" ]]; do echo "Not yet ready"; sleep 5; done
Then, let's check the application's route URL (simply open this address in the browser):
echo "http://$(oc get route quarkus-example-client -o jsonpath='{.spec.host}')/prices.html"
The browser should show that the tickets are being updated periodically as messages are sent and received by AMQ Online.
Summing up
So, we wrote a Quarkus application that uses AMQP for messaging, configured this application to run on the Red Hat OpenShift platform, and implemented its configuration based on the AMQ Online setup. Next, we created the manifests necessary to initialize the messaging system for our application.
This concludes our series on Quarkus, but there's much more exciting content ahead, so stay tuned!
Source: habr.com
