ballerina-guides/messaging-with-jms-queues

Name: messaging-with-jms-queues

Owner: ballerina-guides

Description: null

Created: 2018-02-22 05:48:43.0

Updated: 2018-04-30 13:54:32.0

Pushed: 2018-04-30 13:54:30.0

Homepage: null

Size: 291

Language: Ballerina

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Build Status

Messaging with JMS

Java Message Service (JMS) is used to send messages between two or more clients. JMS supports two models: point-to-point model and publish/subscribe model. This guide is based on the point-to-point model where messages are routed to an individual consumer that maintains a queue of “incoming” messages. This messaging type is built on the concept of message queues, senders, and receivers. Each message is addressed to a specific queue, and the receiving clients extract messages from the queues established to hold their messages. In the point-to-point model, each message is guaranteed to be delivered and consumed by one consumer in an asynchronous manner.

This guide walks you through the process of using Ballerina to send messages with JMS queues using a message broker.

The following are the sections available in this guide.

What you?ll build

To understanding how you can use JMS queues for messaging, let's consider a real-world use case of an online bookstore service using which a user can order books for home delivery. Once an order placed, the service will add it to a JMS queue named “OrderQueue” if the order is valid. Hence, this bookstore service acts as the JMS message producer. An order delivery system, which acts as the JMS message consumer polls the “OrderQueue” and gets the order details whenever the queue becomes populated. The below diagram illustrates this use case.

alt text

In this example Apache ActiveMQ has been used as the JMS broker. Ballerina JMS Connector is used to connect Ballerina and JMS Message Broker. With this JMS Connector, Ballerina can act as both JMS Message Consumer and JMS Message Producer.

Prerequisites
Optional Requirements
Implementation

If you want to skip the basics, you can download the git repo and directly move to the “Testing” section by skipping “Implementation” section.

Create the project structure

Ballerina is a complete programming language that supports custom project structures. Use the following package structure for this guide.

aging-with-jms-queues
 guide
  ??? bookstore_service
  ?    ??? bookstore_service.bal
  ?    ??? tests
  ?         ??? bookstore_service_test.bal
  ??? order_delivery_system
       ??? order_delivery_system.bal
Developing the service

Let's get started with the implementation of the order_delivery_system, which acts as the JMS message consumer. Refer to the code attached below. Inline comments added for better understanding.

order_delivery_system.bal
rt ballerina/log;
rt ballerina/jms;

nitialize a JMS connection with the provider
Apache ActiveMQ' has been used as the message broker
Connection conn = new({
initialContextFactory:"org.apache.activemq.jndi.ActiveMQInitialContextFactory",
providerUrl:"tcp://localhost:61616"


nitialize a JMS session on top of the created connection
Session jmsSession = new(conn, {
// Optional property. Defaults to AUTO_ACKNOWLEDGE
acknowledgementMode:"AUTO_ACKNOWLEDGE"


nitialize a queue receiver using the created session
oint jms:QueueReceiver jmsConsumer {
session:jmsSession,
queueName:"OrderQueue"


MS service that consumes messages from the JMS queue
ind the created consumer to the listener service
ice<jms:Consumer> orderDeliverySystem bind jmsConsumer {
// Triggered whenever an order is added to the 'OrderQueue'
onMessage(endpoint consumer, jms:Message message) {
    log:printInfo("New order received from the JMS Queue");
    // Retrieve the string payload using native function
    string stringPayload = check message.getTextMessageContent();
    log:printInfo("Order Details: " + stringPayload);
}

In Ballerina, you can directly set the JMS configurations in the endpoint definition.

In the above code, orderDeliverySystem is a JMS consumer service that handles the JMS message consuming logic. This service binds to a jms:QueueReceiver endpoint that defines the jms:Session and the queue to which the messages are added.

jms:Connection is used to initialize a JMS connection with the provider details. initialContextFactory and providerUrl configurations change based on the JMS provider you use.

jms:Session is used to initialize a session with the required connection properties.

Resource onMessage will be triggered whenever the queue specified as the destination gets populated.

Let's next focus on the implementation of the bookstore_service , which contains the JMS message producing logic as well as the service logic for the online bookstore considered in this guide. This service has two resources, namely getBookList and placeOrder.

Resource getBookList can be consumed by a user to get a list of all the available books through a GET request. The user receives a JSON response with the names of all the available books.

Resource placeOrder can be consumed by a user to place an order for a book delivery. The user needs to send a POST request with an appropriate JSON payload to the service. Service will then check for the availability of the book and send a JSON response to the user. If the book is available then the order will be added to the JMS queue OrderQueue, which will be consumed by the order delivery system later. Skeleton of the bookstore_service.bal is attached below.

bookstore_service.bal
rt ballerina/log;
rt ballerina/http;
rt ballerina/jms;

truct to construct an order
 bookOrder {
string customerName;
string address;
string contactNumber;
string orderedBookName;


lobal variable containing all the available books
[] bookInventory = ["Tom Jones", "The Rainbow", "Lolita", "Atonement", "Hamlet"];

nitialize a JMS connection with the provider
providerUrl' and 'initialContextFactory' vary based on the JMS provider you use
Apache ActiveMQ' has been used as the message broker in this example
Connection jmsConnection = new({
initialContextFactory:"org.apache.activemq.jndi.ActiveMQInitialContextFactory",
providerUrl:"tcp://localhost:61616"


nitialize a JMS session on top of the created connection
Session jmsSession = new(jmsConnection, {
acknowledgementMode:"AUTO_ACKNOWLEDGE"


nitialize a queue sender using the created session
oint jms:QueueSender jmsProducer {
session:jmsSession,
queueName:"OrderQueue"


ervice endpoint
oint http:Listener listener {
port:9090


ook store service, which allows users to order books online for delivery
p:ServiceConfig {basePath:"/bookstore"}
ice<http:Service> bookstoreService bind listener {
// Resource that allows users to place an order for a book
@http:ResourceConfig {methods:["POST"], consumes:["application/json"],
    produces:["application/json"]}
placeOrder(endpoint caller, http:Request request) {

    // Try parsing the JSON payload from the request

    // Check whether the requested book is available

    // If the requested book is available, then add the order to the 'OrderQueue'

    // Send an appropriate JSON response
}

// Resource that allows users to get a list of all the available books
@http:ResourceConfig {methods:["GET"], produces:["application/json"]}
getBookList(endpoint client, http:Request request) {
  // Send a JSON response with all the available books  
}

Similar to the JMS consumer, here also we require to provide JMS configuration details when defining the jms:QueueSender endpoint. We need to provide the JMS session and the queue to which the producer pushes the messages.

To see the complete implementation of the above, refer to the bookstore_service.bal.

Testing
Invoking the service
 ballerina run order_delivery_system
url -v -X GET localhost:9090/bookstore/getBookList

The bookstoreService sends a response similar to the following.

 HTTP/1.1 200 OK
"Tom Jones","The Rainbow","Lolita","Atonement","Hamlet"]
url -v -X POST -d \
{"Name":"Bob", "Address":"20, Palm Grove, Colombo, Sri Lanka", 
ContactNumber":"+94777123456", "BookName":"The Rainbow"}' \
http://localhost:9090/bookstore/placeOrder" -H "Content-Type:application/json"

The bookstoreService sends a response similar to the following.

 HTTP/1.1 200 OK
"Message":"Your order is successfully placed. Ordered book will be delivered soon"} 

Sample Log Messages:

INFO  [bookstore_service] - New order added to the JMS Queue;
    CustomerName: 'Bob', OrderedBook: 'The Rainbow';

INFO  [order_delivery_system] - New order received from the JMS Queue
INFO  [order_delivery_system] - Order Details: {"customerName":"Bob", 
    "address":"20, Palm Grove, Colombo, Sri Lanka", "contactNumber":"+94777123456",
    "orderedBookName":"The Rainbow"} 
Writing unit tests

In Ballerina, the unit test cases should be in the same package inside a folder named as 'tests'. When writing the test functions the below convention should be followed.

This guide contains unit test cases for each resource available in the 'bookstore_service' implemented above.

To run the unit tests, navigate to messaging-with-jms-queues/guide and run the following command.

 ballerina test

When running these unit tests, make sure that the ActiveMQ is up and running.

To check the implementation of the test file, refer to the bookstore_service_test.bal.

Deployment

Once you are done with the development, you can deploy the services using any of the methods that we listed below.

Deploying locally

As the first step, you can build Ballerina executable archives (.balx) of the services that we developed above. Navigate to messaging-with-jms-queues/guide and run the following command.

 ballerina build
Deploying on Docker

You can run the service that we developed above as a docker container. As Ballerina platform includes Ballerina_Docker_Extension, which offers native support for running ballerina programs on containers, you just need to put the corresponding docker annotations on your service code. Since this guide requires ActiveMQ as a prerequisite, you need a couple of more steps to configure it in docker container.

First let's see how to configure ActiveMQ in docker container.

Now let's see how we can deploy the bookstore_service we developed above on docker. We need to import ballerinax/docker and use the annotation @docker:Config as shown below to enable docker image generation during the build time.

bookstore_service.bal
rt ballerinax/docker;
ther imports

ype definition for a book order

[] bookInventory = ["Tom Jones", "The Rainbow", "Lolita", "Atonement", "Hamlet"];

jms:Connection' definition

jms:Session' definition

jms:QueueSender' endpoint definition

ker:Config {
registry:"ballerina.guides.io",
name:"bookstore_service",
tag:"v1.0"


ker:CopyFiles {
files:[{source:<path_to_JMS_broker_jars>,
        target:"/ballerina/runtime/bre/lib"}]


ker:Expose{}
oint http:Listener listener {
port:9090


p:ServiceConfig {basePath:"/bookstore"}
ice<http:Service> bookstoreService bind listener {
ballerina build bookstore_service

un following command to start docker container: 
ocker run -d -p 9090:9090 ballerina.guides.io/bookstore_service:v1.0
 docker run -d -p 9090:9090 ballerina.guides.io/bookstore_service:v1.0

Here we run the docker image with flag-p <host_port>:<container_port> so that we use the host port 9090 and the container port 9090. Therefore you can access the service through the host port.

Deploying on Kubernetes
bookstore_service.bal
rt ballerinax/kubernetes;
ther imports

ype definition for a book order

[] bookInventory = ["Tom Jones", "The Rainbow", "Lolita", "Atonement", "Hamlet"];

jms:Connection' definition

jms:Session' definition

jms:QueueSender' endpoint definition

ernetes:Ingress {
stname:"ballerina.guides.io",
me:"ballerina-guides-bookstore-service",
th:"/"


ernetes:Service {
rviceType:"NodePort",
me:"ballerina-guides-bookstore-service"


ernetes:Deployment {
age:"ballerina.guides.io/bookstore_service:v1.0",
me:"ballerina-guides-bookstore-service",
pyFiles:[{target:"/ballerina/runtime/bre/lib",
              source:<path_to_JMS_broker_jars>}]


oint http:Listener listener {
port:9090


p:ServiceConfig {basePath:"/bookstore"}
ice<http:Service> bookstoreService bind listener {
 ballerina build bookstore_service

un following command to deploy kubernetes artifacts:  
ubectl apply -f ./target/bookstore_service/kubernetes
 kubectl apply -f ./target/bookstore_service/kubernetes 

eployment.extensions "ballerina-guides-bookstore-service" created
ngress.extensions "ballerina-guides-bookstore-service" created
ervice "ballerina-guides-bookstore-service" created
 kubectl get service
 kubectl get deploy
 kubectl get pods
 kubectl get ingress

Node Port:

url -v -X POST -d '{"Name":"Bob", "Address":"20, Palm Grove, Colombo, Sri Lanka", 
ContactNumber":"+94777123456", "BookName":"The Rainbow"}' \
http://localhost:<Node_Port>/bookstore/placeOrder" -H \
Content-Type:application/json"  

Ingress:

Add /etc/hosts entry to match hostname.

27.0.0.1 ballerina.guides.io

Access the service

url -v -X POST -d '{"Name":"Bob", "Address":"20, Palm Grove, Colombo, Sri Lanka", 
ContactNumber":"+94777123456", "BookName":"The Rainbow"}' \
http://ballerina.guides.io/bookstore/placeOrder" -H "Content-Type:application/json" 
Observability

Ballerina is by default observable. Meaning you can easily observe your services, resources, etc. However, observability is disabled by default via configuration. Observability can be enabled by adding following configurations to ballerina.conf file in messaging-with-jms-queues/guide/.

.observability]

.observability.metrics]
ag to enable Metrics
led=true

.observability.tracing]
ag to enable Tracing
led=true

NOTE: The above configuration is the minimum configuration needed to enable tracing and metrics. With these configurations default values are load as the other configuration parameters of metrics and tracing.

Tracing

You can monitor ballerina services using in built tracing capabilities of Ballerina. We'll use Jaeger as the distributed tracing system. Follow the following steps to use tracing with Ballerina.

Metrics

Metrics and alerts are built-in with ballerina. We will use Prometheus as the monitoring tool. Follow the below steps to set up Prometheus and view metrics for bookstore_service service.

b7a.observability.metrics]
nabled=true
rovider="micrometer"

b7a.observability.metrics.micrometer]
egistry.name="prometheus"

b7a.observability.metrics.prometheus]
ort=9700
ostname="0.0.0.0"
escriptions=false
tep="PT1M"

NOTE: Ballerina will by default have following metrics for HTTP server connector. You can enter following expression in Prometheus UI

Logging

Ballerina has a log package for logging to the console. You can import ballerina/log package and start logging. The following section will describe how to search, analyze, and visualize logs in real time using Elastic Stack.

i) Create a file named logstash.conf with the following content

t {  
ts{ 
 port => 5044 



er {  
k{  
 match => { 
 "message" => "%{TIMESTAMP_ISO8601:date}%{SPACE}%{WORD:logLevel}%{SPACE}
 \[%{GREEDYDATA:package}\]%{SPACE}\-%{SPACE}%{GREEDYDATA:logMessage}"
 }  



ut {  
sticsearch{  
 hosts => "elasticsearch:9200"  
 index => "store"  
 document_type => "store_logs"  


ii) Save the above logstash.conf inside a directory named as {SAMPLE_ROOT}\pipeline

iii) Start the logstash container, replace the {SAMPLE_ROOT} with your directory name

cker run -h logstash --name logstash --link elasticsearch:elasticsearch \
--rm -v ~/{SAMPLE_ROOT}/pipeline:/usr/share/logstash/pipeline/ \
044:5044 docker.elastic.co/logstash/logstash:6.2.2

i) Create a file named filebeat.yml with the following content

beat.prospectors:
pe: log
ths:
- /usr/share/filebeat/ballerina.log
ut.logstash:
sts: ["logstash:5044"]  

NOTE : Modify the ownership of filebeat.yml file using $chmod go-w filebeat.yml

ii) Save the above filebeat.yml inside a directory named as {SAMPLE_ROOT}\filebeat

iii) Start the logstash container, replace the {SAMPLE_ROOT} with your directory name

cker run -v {SAMPLE_ROOT}/filbeat/filebeat.yml:/usr/share/filebeat/filebeat.yml \
SAMPLE_ROOT}/guide/bookstore_service/ballerina.log:/usr/share\
ebeat/ballerina.log --link logstash:logstash docker.elastic.co/beats/filebeat:6.2.2

This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.