Synchronous messaging occurs when the sender blocks, waiting for a response from the recipient. During this block, the sender cannot process any additional information. This means that the thread the message is sent from is effectively locked until it receives a response. You should therefore use synchronous messaging only in cases where it is absolutely necessary that a response is received before moving on. You can think of this as a sort of high priority read receipt for a message. This tutorial uses such a case to illustrate the steps required to set up and use synchronous messaging between two portlets in a plugin project.
Imagine the following scenario. A rock concert requires many things to be set up before the show can go on. The amplifiers, sound system, lighting, and any other stage effects have to be installed and tested for the show to be successful. Naturally, the tour manager has chosen Liferay Portal for managing all these tasks. The manager has a Tasks portlet for submitting setup tasks, which then need to go to the roadies’ Setup portlet on a separate page. The manager also needs confirmation, before moving on with other things, that the roadies’ Setup portlet has received each request. Synchronous messaging to the rescue!
Deciding on Destination Keys
You first need to figure out what your destination keys will be. Destination
keys serve as the specific locations where messages are sent. You can think of
them as the mailing addresses of the Message Bus system. The destination keys
are included with the message and registered as destinations in
WEB-INF/src/META-INF/messaging-spring.xml
. In this example, the destination
keys are chosen to reflect the package names of the two portlets.
The following table shows the destination keys, senders, and receivers for the Tasks and Setup portlets described above:
Destination Key | Sender | Receivers |
---|---|---|
tour/roadie/setup | Tasks | Setup |
tour/manager/task | Setup | Tasks |
The receiver sends its response messages to a destination where the sender listens. This way, a full-bodied response message is sent back to the sender along with the response object. Now that you know what your destination keys are, you can use them when writing the code that sends and receives the messages. You’ll start with the message sender first.
Implementing the Message Sender
Now it’s time to write the message sender code. This code goes in the method of
your application that you want it to be called with. For example, the message
sender code for the tour manager’s Tasks portlet is in the _updateTask
method
of
TasksPortlet.java
,
that adds a new task. This is because a synchronous message needs to be sent
each time the tour manager adds a new task to the portlet.
The sender takes the following steps:
-
Creates the message using Liferay’s
Message
class.Message message = new Message();
-
Stuffs the message with key/value pairs using the
put
method. In this example, key/value pairs of a Task entity are added.message.put("name", name); message.put("description", description); message.put("status", status);
-
Sets a response ID and response destination for listeners to use in replying back.
message.setResponseId("1111"); message.setResponseDestinationName("tour/manager/task");
-
Sends the message to the destination with a timeout value of 10,000 milliseconds. This is how long the sender blocks for while waiting for a response. If no response is received, then a
MessageBusException
is thrown.try { String roadieResponse = (String) MessageBusUtil.sendSynchronousMessage("tour/roadie/setup", message, 10000); } catch (MessageBusException e) { e.printStackTrace(); }
Make sure to add the following imports to your message sender file:
import com.liferay.portal.kernel.messaging.Message; import com.liferay.portal.kernel.messaging.MessageBusException; import com.liferay.portal.kernel.messaging.MessageBusUtil;
Now that you’ve got your message sender implemented, it’s time to head to the next stop on the Message Bus–the message listener!
Implementing the Message Listener
Implementing the message listener is slightly more involved than implementing
the message sender. To implement the listener, you need to make a class that
implements Liferay’s MessageListener
interface. You can find the listener of
the tour manager’s Tasks portlet here:
SetupMessagingImpl.java
.
It’s in the package com.tour.portlet.tasks.messaging.impl
.
The listener class takes the following steps:
-
Implements the
receive(Message message)
method of thecom.liferay.portal.kernel.messaging.MessageListener
interface. -
Extracts values from the
Message
parameter by getting values associated with known keys. For example, this example gets the"name"
key that was created by the sender.String name = (String) message.get("name");
-
Creates and sends a response
Message
object based on the message received via theMessageBusUtil.createResponseMessage(message)
method. This method accesses the response destination name from themessage
variable and sets the destination of the response message. ThesetPayload
method sets the response message’s payload. In this example, the payload is set to"RECEIVED"
, which is in turn used by the original sender to display a success message.Message responseMessage = MessageBusUtil.createResponseMessage(message); responseMessage.setPayload("RECEIVED");
-
Sends the response
Message
to the response destination.MessageBusUtil.sendMessage(responseMessage.getDestinationName(), responseMessage);
Make sure to add the following imports to your message listener file:
import com.liferay.portal.kernel.messaging.Message; import com.liferay.portal.kernel.messaging.MessageBusUtil; import com.liferay.portal.kernel.messaging.MessageListener;
Now you have both a sender and a listener implemented for your messages! There’s just one more thing to take care of before you’re done.
Configuring the Message Bus
For the Message Bus to direct messages from destinations to listeners
successfully, you must register the listeners by configuring the appropriate
mappings in your plugin’s WEB-INF/src/META-INF/messaging-spring.xml
file.
Create the WEB-INF/src/META-INF/messaging-spring.xml
file if it’s not already
in your plugin. For example, here’s the configuration for the custom Tasks and
Setup portlets:
<?xml version="1.0"?>
<beans
default-destroy-method="destroy"
default-init-method="afterPropertiesSet"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
>
<!-- Listeners -->
<bean id="messageListener.setup_listener" class="com.tour.portlet.tasks.messaging.impl.SetupMessagingImpl" />
<!-- Destinations -->
<bean id="tour.roadie.setup" class="com.liferay.portal.kernel.messaging.SynchronousDestination">
<property name="name" value="tour/roadie/setup" />
</bean>
<bean id="tour.manager.task" class="com.liferay.portal.kernel.messaging.SynchronousDestination">
<property name="name" value="tour/manager/task" />
</bean>
<!-- Configurator -->
<bean id="messagingConfigurator" class="com.liferay.portal.kernel.messaging.config.PluginMessagingConfigurator">
<property name="messageListeners">
<map key-type="java.lang.String" value-type="java.util.List">
<entry key="tour/roadie/setup">
<list value-type="com.liferay.portal.kernel.messaging.MessageListener">
<ref bean="messageListener.setup_listener" />
</list>
</entry>
</map>
</property>
<property name="destinations">
<list>
<ref bean="tour.roadie.setup"/>
<ref bean="tour.manager.task"/>
</list>
</property>
</bean>
</beans>
This configuration specifies the following beans:
- Listener beans: Specify the listener classes to handle messages.
- Destination beans: Specify the class type and key names of the destinations.
- Configurator bean: Maps listeners to their destinations.
Now you just need to register this messaging-spring.xml
file in your
docroot/WEB-INF/web.xml
file. To do so, place the following code just above
the closing </web-app>
tag in the web.xml
file:
<listener>
<listener-class>com.liferay.portal.kernel.spring.context.PortletContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>portalContextConfigLocation</param-name>
<param-value>/WEB-INF/classes/META-INF/messaging-spring.xml</param-value>
</context-param>
Save and redeploy your portlet. Your plugin should now send and receive messages the way you’ve configured it. In the case of the tour manager, the Tasks portlet now displays the success message when a setup task is added.
Likewise, the roadies’ Setup portlet now gets any new tasks added by the road manager.
Congratulations! You’ve completed your first trip on the Message Bus!
Related Topics
Developing with the Plugins SDK