Using Message Bus

The Message Bus is a service level API used to exchange messages within Liferay. The Message Bus is a mechanism for sending message payloads to different components in Liferay, providing loose coupling between message producers and consumers to prevent class loading issues. It’s located in the global class loader, making it accessible to every deployed web application. Remote messaging isn’t supported, but messages are sent across a cluster when ClusterLink is enabled.

Destinations: Addresses or endpoints to which listeners register to receive messages.

Listeners: Consume messages received at destinations. They receive all messages sent to their registered destinations.

Senders: Invoke the Message Bus to send messages to destinations.

Your services can send messages to one or more destinations, and can listen to one or more destinations. The figure below depicts this. An individual service can be both a message sender and a message listener. For example, in the figure below both Plugin 2 - Service 3 and Plugin 5 - Service 7 send and listen for messages.

Figure 11.4: Example, Message Bus system

The Message Bus supports synchronous and asynchronous messaging:

Synchronous messaging: After it sends a message, the sender blocks waiting for a response from a recipient.

Asynchronous messaging: After it sends a message, the sender is free to continue processing. The sender can be configured to receive a call-back or can simply send and forget. We’ll show you how to implement both synchronous and asynchronous messaging in this section.

Call-back: The sender can include a call-back destination key as the response destination for the message. The recipient (listener) can then send a response message back to the sender via this response destination.

Send-and-Forget: The sender includes no call-back information in the message sent and continues with processing.

Configuration of Message Bus is done using the following files:

WEB-INF/src/META-INF/messaging-spring.xml: Specifies your destinations, listeners, and their mappings to each other.

WEB-INF/web.xml: Holds a listing of deployment descriptors for your plugin. Make sure you add messaging-spring.xml to your list of Spring configurations in this file.

You can control your Message Types by using either the Message or JSONObject class. Liferay core services are typically serialized and deserialized in JSON. In our examples we’ll demonstrate both types of message classes.

So far we’ve introduced the Message Bus System, including message types, destinations, senders, listeners, and approaches to sending messages. Next we’ll show you how easy it is to create your destinations, register listeners, and send your messages. To demonstrate, we’ll implement a business use case.

Our use case will consider Jungle Gyms R-Us and its distribution of playground equipment, buying the equipment from manufacturers and selling the equipment to retailers. We’ll focus on the company’s process for procuring new jungle gym equipment. Let’s lay out this process now.

Jungle Gyms R-Us employs the following departments in their procurement process:

Procurement Department: Scouts out the latest equipment deals of manufacturers.

Finance Department: Determines whether the equipment can be purchased based on budget.

The departments currently use email to exchange comments about new equipment purchases, but someone always seems to be left out of the loop. One time, Sales was gung-ho about getting their hands on the latest and greatest spring rider animals from Boingo-Boingo Industries, but they didn’t consider the failing safety reviews discovered by the Legal department, because the Legal department forgot to copy the Sales department in their email to Procurement. Tempers flew, feelings were hurt, and everybody avoided hanging out in the company breakroom for the next couple of weeks.

Jungle Gyms R-Us could use Liferay’s Workflow with Kaleo to resolve the communication breakdown, but we’ll resolve the Jungle Gym’s communication woes using Message Bus, to show you how it works. Here are the inter-department message exchanges we’ll accommodate:

In our example, equipment purchases can’t proceed without approval from Finance and Legal departments. Since special offers from the manufacturers often only last for a couple hours, Procurement makes it their top priority to get approval as soon as possible. Implementing their exchange using synchronous messaging makes the most sense.

Figure 11.5: Synchronous messaging

The following table describes how we’ll set things up:

Destination Key

Type

Sender

Receivers

jungle/finance/purchase

synchronous

Procurement

Finance

jungle/finance/purchase/response

synchronous

Finance

Procurement

jungle/legal/purchase

synchronous

Procurement

Legal

jungle/legal/purchase/response

synchronous

Legal

Procurement

We’ve set it up so Finance sends its response messages to a destination on which Procurement will listen. That way a full-bodied response message is sent back to Procurement in addition to the response object returned from sending the message.

Implements the receive(Message message) method of the com.liferay.portal.kernel.messaging.MessageListener interface.

Extracts values from the Message parameter by getting values associated with known keys.

Creates a Message based on the message received via the MessageBusUtil.createResponseMessage(message) method, which accesses the response destination name from the message variable and sets the destination of the response message.

Sets the response message’s payload.

Sends the response Message to the response destination.

You can implement the listener for the Legal Department similarly. Next we’ll account for Legal Department-related classes in our configuration.

Message Bus Configuration for the purchase approval request process:

For Message Bus to direct messages from destinations to listeners, we must register the listeners by configuring the appropriate mappings in our plugin’s WEB-INF/src/META-INF/messaging-spring.xml file (create this file if it’s not already in your plugin). Here is the configuration:

Destination beans: Specify the class type and key names of the destinations.

Configurator bean: Maps listeners to their destinations.

When Finance sends its purchase approval request message for a new three-story spiral slide, the console reports Finance’s receipt of the message, Procurement’s receipt of the callback response from Finance, and Procurement’s receipt of the synchronous response returned from sending the message. Here’s what the console message looks like:

Asynchronous messaging consists of sending a message and then continuing with processing without blocking waiting for an immediate response. This allows the sender to continue with other tasks. It’s often necessary, however, that the listener can respond to the sender. This can be done using a call-back.

Jungle Gyms R-Us’s Procurement Department must notify the Sales and Warehouse departments of incoming equipment while simultaneously soliciting their feedback. To assure all three departments are up to speed, any responses from the Sales or Warehouse departments are posted to a shared destination.

The following table describes how we’ll set things up:

Destination Key

Type

Sender

Receivers

jungle/purchase

async serial

Procurement

Sales, Warehouse

jungle/purchase/response

synchronous

Sales, Warehouse

Procurement

The following image shows asynchronous messaging, with serial dispatching of messages:

Figure 11.6: Asynchronous messaging with serial dispatching

Let’s package the message as a JSONObject and send it to the destination:

Here’s how this listener deserializes the JSONObject from the message:

Gets the message payload and casts it to a String.

Creates a JSONObject from the payload string.

Gets values from the JSONObject using its getter methods.

The class also demonstrates how the Warehouse Department packages a response message and sends it back to the Procurement Department, using these steps:

Create a JSONObject.

Stuff it with name-value pairs.

Send the response message to the original message’s response destination.

The Sales department listener can be implemented the same way, substituting Sales as the department value; the comment would likely be different, too.

You just used the JSONObject message type to send an asynchronous response message using a call-back.

Remember, we want the Procurement, Sales, and Warehouse departments to be aware of any message regarding the new playground equipment purchasing process. Let’s leverage our destination keys and department names in handling shared responses.

Let’s look at receive(Message) for a minute. We’ve set it up to handle messages differently depending on their destinations: messages to jungle/purchase are handled as Procurement’s purchase notifications, while messages to jungle/purchase/response are treated as departmental responses to Procurement’s purchase notifications. The doReceiveResponse(Message) method performs an important task, checking that the response comes from a department other than itself, and printing an error if it doesn’t.

Here are the configuration elements we added to the messaging-spring.xml from the previous section:

Configuration bean listener map entry: Warehouse and Sales are registered to listen for the notifications from Procurement. All three departments are registered to listen for inter-departmental responses.

In the send and forget model, the sender sends messages and continues processing. We’ll apply this behavior to Jungle Gym’s company-wide new product notification.

Procurement isn’t expecting response messages from individual employees, so there’s no need for the company-wide listener to package up responses. We do, however, want everyone to get product news at the same time, so instead of dispatching news to employees serially we’ll dispatch in parallel.