Monday, November 18, 2013

Lifeay Message Bus Portlet/Handling bulk operations in Liferay

Lifeay Message Bus Portlet/Handling bulk operations in Liferay


Objective:

Perform huge time taking operation in liferay application.
Scenario:

In liferay application if we want send thousands of mails or SMS then the response time is more. If mails are more then to complete response user need to wait until request is completed. User cannot do any other operations in application.

These scenarios are common in real time. Here liferay provide Message Bus so that we can delegate task to other process and user continues with other operation in the application.exactly we can when response time is more in the application then we will delegate the operation to Message Bus.

Liferay message bus has lots of use, for now I just explained how to send bulk mails using message bus.

Here once request is started we delegate request handling to message bus so that message bus take caring bulk operation. Mean while request will be relapsed quickly and process is run in back end.

Download LiferayBulkMailSender from following location

You can find source and war file


Note: 

Portlet developed in Liferay 6.1GA2 EE version

If you want deploy in CE version you just do changes in liferay-plugin-package.properties

Liferay 6.1 EE version

name=LiferayBulkMailSender
module-group-id=liferay-ee
module-incremental-version=1
tags=
short-description=
change-log=
page-url=http://www.liferay.com
author=Liferay, Inc.
licenses=EE
portal-dependency-jars=\
    jstl-api.jar,\
    jstl-impl.jar
portal-dependency-tlds=c.tld
liferay-versions=6.1.20

Liferay 6.1 CE version

name = LiferayBulkMailSender
module-group-id=liferay
module-incremental-version=1
tags=
short-description=
change-log=
page-url=http://www.liferay.com
author=Liferay, Inc.
licenses=LGPL
portal-dependency-jars=\
    jstl-api.jar,\
    jstl-impl.jar
portal-dependency-tlds=c.tld
liferay-versions=6.1.1

Procedure for deploy portlet:

You can use war file and directly place in your portal deploy folder and test or you can also use source to deploy portlet.

Once port let is deployed successfully you can see the portlet in sample category
Please do portal mail configuration to send mails.

Whats is message Bus?

The Message Bus can be used to exchange messages between Liferay and other web applications, including deployed portlets and plugins in general.

The developer can add Destinations to the Message Bus, for each Destination you can register several MessageListeners. The client sends a message to a Destination and the Message Bus will deliver this message to every MessageListener that is listening to this Destination.

Note:

Message bus can use in many scenarios



Concept:

Message bus like tunnel between two or more different nodes there we register all destinations each destination have multiple listeners to receive messages.
We can say massage bus is responsible to transfer messages from message senders to message listeners.

Building block of system:

Message Bus:

It is responsible to transfer messages from message senders to message listenrsss.

Destinations:

 Addresses or end points 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. We can say intiating the message bus

Note:

Message bus can send message between multiple plug-in portlets or  within same portlet.
Here I am explaining within one plug-in portlet.

Steps to implement the concept

Step:1

Create messaging-spring.xml  in /src/META-INF/messaging-spring.xml of your plug-in portlet.

Step: 2

Add following property in service.propertis file
spring.configs=WEB-INF/classes/META-INF/messaging-spring.xml

Note:
If your portlet already use service builder or service layer then service.propertis already generated if not you can create in /src/service.properties directory and add property.

Step: 3

Configure Message Destination and Message Listers in messaging-spring.xml   by using bean called PluginMessagingConfigurator this will take two properties one is messageListeners is map type and other is destinations is list type.

Once we register listeners and destination we need to configure respective beans which we referred as destinations and listeners means implementation beans.

Step: 4

Implement the listeners
Whatever our implementation we have to write in listener and listener should implement MessageListener and we need to implement receive(-----) method.

Things we need to Do:

  • Create message
  • Add properties to message means data.
  • Send message to destination (initiating Message Bus or start senders)
  • Receive message in destination (Listener)


Scenario:

Assume now we want send some mail notification to all portal users assume portal may have thousands of users. If we want send mail to more users to complete request it may take several minutes. Until complete the request user can’t do other operation.

Now we are going to handle this scenario using massage bus with asynchronous serial destination.

Note:

we have two types of destination asynchronous and synchronous.

When we use synchronous until finish request we can’t do any operation in application. If you use asynchronous we can do other operation before finish request. Means the task delegate to massage bus and it will be take caring mail sending process.

To configure asynchronous destination we will use SerialDestincation.java class

As I said earlier steps now we will implement for bulk mails sender

Create messaging-spring.xml   and Configure listeners and destinations

Create messaging-spring.xml  in  src/META-INF and configure destination and leisters

<?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="liferay.bulk.mail_listener" class="com.meera.bulk.mail.BulkMaulSenderListenerImpl" />
    <!-- Destinations -->
    <bean id="liferay.bulk.mail.sender.destination" class="com.liferay.portal.kernel.messaging.SerialDestination">
        <property name="name" value="liferay/bulk/mail/destination" />
    </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="liferay/bulk/mail/destination">
                    <list value-  type="com.liferay.portal.kernel.messaging.MessageListener">
                        <ref bean="liferay.bulk.mail_listener" />
                    </list>
                </entry>
            </map>
        </property>
        <property name="destinations">
            <list>
                <ref bean="liferay.bulk.mail.sender.destination"/>
            </list>
        </property>
    </bean>
</beans>

Configure spring config property in service.properties file

Now add following property in service.properties in the following location src/service.properties

spring.configs=WEB-INF/classes/META-INF/messaging-spring.xml

Now create message and send message to destination


package com.meera.bulk.mail;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import com.liferay.portal.kernel.messaging.Message;
import com.liferay.portal.kernel.messaging.MessageBusUtil;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;
public class BulkMailSenderAction extends MVCPortlet {
            public void sendMessage(
                                    ActionRequest actionRequest, ActionResponse actionResponse)
                        throws IOException, PortletException {
                        System.out.println("====sendMessage===");
                        String mailSubject=ParamUtil.getString(actionRequest,"mailSubject");
                        String mailBody=ParamUtil.getString(actionRequest,"mailBody");
                        String senderMailAddress=mailBody=ParamUtil.getString(actionRequest,"senderEmailAddess");
                        Message message = new Message();
                        message.put("mailSubject",mailSubject);
                        message.put("mailBody",mailBody);
                        message.put("senderMailAddress",senderMailAddress);
                        try {
                             MessageBusUtil.sendMessage("liferay/bulk/mail/destination", message);
                        }
                        catch (Exception e) {
                            e.printStackTrace();
                        }
            }
}

Receive the message in Listener and process farther steps


package com.meera.bulk.mail;
import java.util.List;
import javax.mail.internet.InternetAddress;
import com.liferay.mail.service.MailServiceUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.mail.MailMessage;
import com.liferay.portal.kernel.messaging.Message;
import com.liferay.portal.kernel.messaging.MessageListener;
import com.liferay.portal.model.User;
import com.liferay.portal.service.UserLocalServiceUtil;
public class BulkMaulSenderListenerImpl implements MessageListener {
    public void receive(Message message) {
        try {
            doReceive(message);
        }
        catch (Exception e) {
            _log.error("Unable to process message " + message, e);
        }
    }
    protected void doReceive(Message message)
        throws Exception {
        String mailSubject = (String)message.get("mailSubject");
        String mailBody = (String)message.get("mailBody");
        String senderEmailAddess =(String)message.get("senderEmailAddess");
        List<User> usersList=UserLocalServiceUtil.getUsers(1,UserLocalServiceUtil.getUsersCount());
        MailMessage mailMessage=new MailMessage();
        mailMessage.setBody(mailBody);
        mailMessage.setSubject(mailSubject);
        mailMessage.setFrom(new InternetAddress(senderEmailAddess));
        for(User user:usersList){
            mailMessage.setTo(new InternetAddress(user.getEmailAddress()));
            MailServiceUtil.sendEmail(mailMessage);
            System.out.println("mail sent to..::============:"+user.getEmailAddress());
        }
  
}
    private static Log _log =
            LogFactoryUtil.getLog(BulkMaulSenderListenerImpl.class);
}

Now we cans send mails and we need not to wait for finish the task. Task is running back end so that user can do other operations.

Note:

In this example to show quick view i just commented the mail sending code because until configure mail configuration in portal we can’t send mails, for this reason I just commented the mail code and i written dummy for loop with one lack count iteration.

When you submit button request will be finished quickly but you can see server console back end loop is iterating for one lack times.

This is just for quick understanding of this portlet after you configure portal mail configuration then you can uncomment the mail code and test

Observations:

After deploy the portlet just drag and drop portlet and click on Send Bulk Mail button as soon as you click the button your request will be finished soon but in back end process is still continuing to send mails. To understand this please sees the server console so that you can understand all.

Important points:

  • Liferay provide message bus so that we can send message from message senders to message listeners.
  • This message bus can send or receive messages from different plug-in contexts or within same portlet.
  • Message Bus having multiple destinations and each destination has multiple listeners to receive messages.
  • Message sender is initiating message bus to send message to end points or destinations. As soon as message reaches the destination respective leisters will receive the messages and it will start further process.
  • We have two types of destination synchronous and asynchronous. Asynchronous destinations release the request even request is not complete but synchronous destinations are release the request once process or task finished.

Screens:

Mail form


Message after submit form


Server console after request is completed


Reference:




Author
Meera Prince


0 comments :

Post a Comment

Recent Posts

Recent Posts Widget

Popular Posts