This chapter focuses on a few of the essential and advanced concepts of Liferay DXP. You learn how to set communication between two portlets using inter-portlet communication. There are several approaches to IPC, including IPC via events, an approach that is commonly used in the industry because of its versatility. Another critical concept—the message bus—is also covered in this chapter so that you understand how messaging works in Liferay for background processes. The most implemented form of the message bus is the asynchronous message bus, mainly used for sending bulk mail or batch-processing tasks. Other important topics covered in this chapter include scheduler implementation and scheduled tasks managed by them, such as periodic reports, and so on.
Inter-Portlet Communication
Inter-portlet communication, or IPC, enables communication between two portlets when are deployed on the same instance of the Liferay DXP server. These portlets can be on the same page or different pages, and there are different approaches to IPC depending on the scenario. This communication entails passing a message and some data from Portlet A to Portlet B. The complete concept of inter-portlet communication is based on two kinds of portlets—a sender and a receiver portlet. They are normal portlets but include extra definitions in their metadata to identify their behavior. The portlet invoking the IPC process is referred to as the sender portlet, whereas the one receiving communication is referred to as the receiver portlet. Sender and receiver portlets are also often referred to as producer portlets and consumer portlets, respectively.
The first Portlet Specification (1.0, JSR 168) had minimal capability for IPC. The only way to achieve IPC was portlet session-based; essentially, just setting attributes in a session’s portlet makes data accessible to the other portlets. Then came the Portlet Specification 2.0 (JSR 286). It revolutionized the IPC process. It came up with many improved approaches to achieve IPC, such as events, and so on.
Liferay portals support all types of inter-portlet communication (IPC) enlisted in Portlet Specification 1.0 (JSR 168) and Portlet Specification 2.0 (JSR 286).
There are five ways of achieving IPC; let’s review them individually.
IPC via Public Render Parameters
Public Render Parameters (PRPs) are one of the most straightforward mechanisms to achieve inter-portlet communication. These portlets may be on different pages or the same page. This approach to inter-portlet communication works on the render parameters, which multiple portlets can access. This approach works on portlets added to the same page by default Liferay configurations; however, this behavior can be modified using scopes.
To use PRPs, you need to define specific portlet parameters. This portlet acts as the producer portlet. This specified parameter goes to another portlet, which has a specific value. As the producer portlet’s parameter is accessible by another portlet, the data being carried by this parameter becomes accessible to another portlet. The portlet accessing the parameter to access data is the consumer portlet in this scenario. To make a portlet a consumer, you specify it in the portlet config, similar to how you defined the producer portlet.
Select apressIPCSender and apressIPCReceiver; you will work with these portlets to achieve all varieties of IPC. (I do not discuss portlet creation in detail here, as it’s covered in Chapter 3.)
View.jsp of apressIPCSender Portlet for PRP
ApressIPCSenderPortlet Controller Class for PRP
View.jsp of the apressIPCReceiver Portlet for PRP
The ApressIPCReceiverPortlet Controller Class for PRP
As you can see, javax.portlet.supported-public-render-parameter=inputMessage should be the same in the sender and receiver portlets’ container component property to achieve IPC via the Public Render Parameter.
When you submit text from the apressIPCSender portlet, that text will be received by the apressIPCReceiver portlet.
IPC via Private Session Attributes
This approach to inter-portlet communication is very similar to the previous approach. The only difference is that instead of specifying a public parameter, the complete portlet session is accessible to other portlets. The producer and consumer portlets can be available on the same page or another page.
Let’s look at this with an example that uses the same apressIPCSender and apressIPCReceiver portlets to achieve a Private Session Attributes (PSA) IPC.
ApressIPCSenderPortlet Controller Class for PSA
View.jsp of apressIPCReceiver Portlet for PSA
ApressIPCReceiverPortlet Controller Class for PSA
IPC via Server-Side Events
View.jsp of the apressIPCSender Portlet for an Event
ApressIPCSenderPortlet Controller Class for an Event
View.jsp of the apressIPCReceiver Portlet for an Event
ApressIPCReceiverPortlet Controller Class for an Event
You can observe that javax.portlet.supported-publishing-event=produceMessage;http://inthiyaz.com should be identical in the sender and receiver portlets container’s component property to achieve IPC via Private Session Attributes.
Client-Side IPC via Ajax
Client-Side inter-portlet communication is one of the approaches wherein IPC is achieved from the client side instead of the server side. This is one of two approaches to achieve this. Client-side IPC can communicate with a portlet on the same page. Because client-side IPC uses JavaScript to communicate, all portlets that participate in IPC must be on the same page. Liferay provides a JavaScript library for its implementation. The client-side IPC using Ajax is very similar to event-based IPC; the difference is that this is achieved by Ajax instead of by a server-side event.
There may be one sender and many receivers, but the sender and receivers must be on the same page.
Client-Side IPC via Cookies
Like client-side IPC with Ajax, you can achieve IPC through browser cookies. The data is stored in cookies, and other portlets will access this information. However, this comes with limitations regarding data privacy, which is why this is rarely used. Another shortcoming is that if cookie access is disabled on the browser, this will fail. There are several ways to achieve this, as you can set and read cookies using plain JavaScript or jQuery. The following sample code in JavaScript illustrates this approach.
This explains how IPC can be achieved with different approaches; in the next section, you learn about the Liferay message bus.
Liferay Message Bus
Liferay’s message bus is a loosely coupled approach to exchanging messages. This loose coupling lets message producers continue to the next task/instruction while the consumer gets the message and starts processing it. Inherently, this is a service-level API that components can use to send and receive messages. This Message Bus API is part of Liferay’s global class loader, making it accessible across the Liferay server. This works well in a clustered environment.
The message bus is very helpful in scenarios where you must do parallel processing. For example, you need to update the search index once the data is updated, but user flow need not stop for it. You can pass messages to a message bus with essential data to update the search index, while the user flow can return with the data update process.
The message bus system contains the following components:
Message bus: The message bus ensures the transfer of messages between senders and listeners. The following code is used to create a message bus.
com.liferay.portal.kernel.messaging.MessageBus. .sendMessage(destinationName, messageobj)
- Destination: A destination is an address to which listeners are registered to receive messages. Message bus destinations are based on the destination configurations and registered as OSGI services, and they detect the destination services and manage their associated destinations. There are three types of destinations:
Parallel destination
Serial destination
Synchronous destination
- Destinations are based on the DestinationConfiguration (com.liferay.portal.kernel.messaging.DestinationConfiguration), which provides three static methods for creating various types of destinations:
createParallelDestinationConfiguration(String destinationName)
createSerialDestinationConfiguration(String destinationName)
createSynchronousDestinationConfiguration(String destinationName)
- Listener: A listener is the receiver of the messages. It consumes messages received at destinations and processes them. In Liferay, you have three ways for message listeners to listen to messages:
Automatic Registration as a Component (see Listing 4-12)
MessageBusRegisteredSynchronousMessageListener Class
Registering via a message bus reference (see Listing 4-13)
MessageBusRegistrator Class
Registering directly to the destination (see Listing 4-14)
DestinationListenerRegistrator Class
Sender: A sender is the one who sends messages to start the processing in the listener. This is the one who initiates the process.
Synchronous Message Bus
MessageSenderServiceImpl Class for a Synchronous Message
Asynchronous Message Bus
MessageSenderServiceImpl Class for Synchronous and Asynchronous Messages
This explains how the Liferay message bus can be implemented; in the next section, you learn about the Liferay scheduler.
Liferay Scheduler
Liferay’s scheduler mechanism was developed on top of the Quartz scheduler engine, which is an open-source Java implementation for scheduling jobs. Liferay’s implementation of Quartz makes it simple to use and easy to implement, which you’ll see soon.
- 1.
Memory (StorageType.MEMORY): This storage type does not persist job information anywhere (only in memory) that is not cluster-aware. So, if there is a server outage at the scheduled time, the scheduler will not run, and in case it is used in a clustered environment, it will run n number of times, causing duplicate execution of code, where n is the number of nodes in the cluster.
- 2.
Memory Clustered (StorageType.MEMORY_CLUSTERED): This storage type is similar to the previous MEMORY storage type, where the job information is kept in memory. Still, the only difference is that this type is cluster-aware. This means it will be affected during outages but will not rerun in a clustered environment. This is the default storage type.
- 3.
StorageType.PERSISTED: In contrast to the MEMORY storage type, job details are persisted in the database. This takes care of the missed job in the case of outages and it is cluster-aware. This information is stored in the database tables.
In the case of the clustered environment, using either MEMORY_CLUSTERED or PERSISTED is recommended to ensure your job doesn’t run on every node. The MEMORY type is recommended if the job needs to run on all the nodes of the cluster (such as taking periodic thread dumps, and so on).
SchedulerServiceImpl Class for Scheduler
Summary
This chapter covered a few advanced concepts of Liferay DXP. Inter-portlet communication is practically vital for real-world applications, and in most applications, IPC via events is used because of its versatility. Message busses also play a vital role, and the most commonly implemented form is the asynchronous message bus. It’s primarily used for sending bulk mail or batch-processing tasks. The scheduler implementation manages the execution of scheduled tasks, such as periodic reports.
In the next chapter, you will learn about the Liferay Service Builder.