Portlet to Portlet Communication

Introduction #

The first version of the portlet specification, JSR-168/Portlet 1.0, did not include any support for Inter Portlet Communication. The second version, JSR-286/Portlet 2.0, has supported for IPC Mechanism.

JSR-286 makes it easy to share the data between two portlets. Using IPC mechanisms, we can share the data from ACTION to VIEW phase and VIEW-VIEW Phase.

There are 3 ways to share the data between 2 portlets.

  1. Portlet session
  2. IPC Mechanisms
    1. Public Render Parameters
    2. Event
    3. Client-Side IPC
  3. Cookies

1. Portlet Session #

By default, Each WAR has its own session and will not be shared with other WARs. Liferay provides a mechanism by which Portlets can share session attributes across WARs.

A PortletSession is created for each user per portlet application. This makes the PortletSession useful for communicating all user related information among different portlets in the same portal application.

Step 1: Set below attributes in Portlet1 #

liferay-portlet.xml

    <portlet>
        <private-session-attributes>false</private-session-attributes>
    </portlet>

Step 2: To set the Session #

    PortletSession session = renderRequest.getPortletSession();
    session.setAttribute("sessionValue", some-value ,PortletSession.APPLICATION_SCOPE);

Step 3: Get the Session Value in Portlet2 #

    PortletSession ps = renderRequest.getPortletSession();
    String tabNames = (String)ps.getAttribute("sessionValue ",PortletSession.APPLICATION_SCOPE);

2. Inter Portlet Communication (IPC) Mechanism #

2.1 Public-render-parameter Inter-portlet Communication #

 Illustration of IPC through public-render parameters

In JSR 168, the render parameters set in processAction is only available in the render of the same portlet. With the Public Render Parameters feature, the render parameters set in the processAction of one portlet will be available in render of other portlets also.

By adding the following property in portlet-ext, we can enable portlets to share render states with other portlets that are on different pages:

    portlet.public.render.parameter.distribution=layout-set

Step 1: Add below attribute in "Sender-Portlet" #

    <portlet-app>
        <portlet>
            <supported-public-render-parameter>id1</supported-public-render-parameter>
        </portlet>
         
        <public-render-parameter>
            <identifier>id1</identifier>
            <qname xmlns:x="http://abc.com/userId">x:param1</qname>
        </public-render-parameter>
    </portlet-app>

Note:We can declare a list of public paramters for a portlet application.

Step 2 #

We can set render parameter in the processAction() method by using the defined public render parameter identifier as the key.

    response.setRenderParameter("id1", "someIdValue");

E.g.

    public void processAction(ActionRequest  request, ActionResponse response) throws IOException, PortletException  {
        ........
        response.setRenderParameter("id1", “someIdValue”); ........
    }

Step 3: Receiver Portlet Portlet "portlet.xml" #

Specify the render parameter the portlet would like to share in the portlet section.

    <portlet-app>
        <portlet>
            <portlet-name >PortletB< /portlet-name>
            <supported-public-render-parameter>id1</supported-public-render-parameter>
        </portlet >
        <public-render-parameter>
            <identifier>id1</identifier>
            <qname xmlns:x="http://abc.com/userId">x:param1</qname>
        </public-render-parameter>
    </portlet-app>

Step 4 #

A portlet can read public render parameter using following method request.getPublicParameterMap()

Note:Public render parameters are merged with regular parameters so can also be read using

    request.getParameter(“id1”);

Step 5 #

A portlet can remove a public render parameter by invoking following methods.

    response.removePublicRenderParameter("id1")

2.2 Event Inter-portlet Communication #

 Comic Illustration of IPC using Events

Portlet events that a portlet can receive and send.

In JSR-168 The only way to achieve eventing was through portlet session. The limitation of this approach was that the Portlets has to be in the same web application.

In JSR-286 JSR 286 (Portlet 2.0) defines a lifecycle for events, so that eventing is possible between portlets that are in different web applications.

By adding the following property in portal-ext.properties, we can enable portlets to send and receive events from other portlets that are on different pages

    portlet.event.distribution=layout-set

Step 1: Sender Portlet #

portlet.xml

The portlet standard defines a way of telling the portlet container which portlet is responsible for sending an event.

Add this inside <portlet> tag:

    <portlet-app>
        <portlet>
            <supported-publishing-event xmlns:x='http://liferay.com'>
                <qname>x:empinfo</qname>
            </supported-publishing-event>>
        </portlet>

        <event-definition xmlns:x='http://liferay.com'>
            <qname>x:empinfo</qname>
            <value-type>java.lang.String</value-type>
        </event-definition>
    </portlet-app>

Step 2: Set the event in process action #

    javax.xml.namespace.QName qName = new QName("http://liferay.com", "empinfo", "x");
    response.setEvent(qName, "Hi! You have received Event Data sent from Sender Portlet");

Step 3: Listener Portlet #

portlet.xml

    <portlet-app>

        <portlet>
            <supported-processing-event xmlns:x='http://liferay.com'>
                <qname>x:empinfo</qname>
            </supported-processing-event>
        </portlet>
        <event-definition xmlns:x='http://liferay.com'>
            <qname>x:empinfo</qname>
            <value-type>java.lang.String</value-type>
        </event-definition>
    </portlet-app>

Step 4: get the EVENT #

This Event will be called after processAction as shown in the picture:

Lifecycle for IPC Event

 Lifecycle illustrated

    @javax.portlet.ProcessEvent(qname = "{http://liferay.com}empinfo")
    public void handleProcessempinfoEvent(javax.portlet.EventRequest request, javax.portlet.EventResponse response) 
                throws javax.portlet.PortletException, java.io.IOException {
                
        javax.portlet.Event event = request.getEvent();
        String value = (String) event.getValue();

        System.out.print("value in process event>>>>>>>>>" + value);
        response.setRenderParameter("empInfo", value);
    }

2.3 Client-Side IPC #

There are 2 APIs for client side IPC.

Event generation (call from Portlet-A):

    Liferay.fire(
            '<eventName>', {
            name: value 
        }
    );

E.g.

    Liferay.fire(
            'planTravel', {
            origin: 'pune',
            destination : 'mumbai'
        }
    );

Event Listener (call from Portlet-B):

    Liferay.on(
            '<eventName>',
            function(event) {
                // your code
            }
    );

E.g.

    Liferay.on(
            '<eventName>',
            function(event) {
                showNews('', event.origin);
                showNews('', event.destination);
            }
    );

3. Cookies #

Other than the IPC mechanism, there is an easiest way to get the data between portlets on different pages through Cookies.

But there are some limitations for cookies:

  1. It will not accept more than 4KB size of data
  2. The biggest limitation is, the 20 cookies per server limit

and so it is not a good idea to use a different cookie for each variable that has to be saved.

Portlet 1 #

To Set the Cookies through jQuery:

    <script src="/html/js/jquery/cookie.js" type="text/javascript" > </script>
    
    function setCookie(docURL) {
        jQuery.cookie("cookieParam",docURL);
    }

To Set the Cookies through java/jsp:

    HttpServletResponse response = PortalUtil.getHttpServletResponse(actionResponse);
    Cookie cookieParam = new Cookie("cookieParam ", password);
    response.addCookie(cookieParam);

Portlet 2 #

To get the Cookies through jQuery:

    jQuery.cookie("cookieParam ");

To get the Cookie through java/jsp:

    String sessionid = "";
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (int i = 0; i < cookies.length; i++) {
            if (cookies[i].getName().equals("cookieParam ")) {
                sessionid = cookies[i].getValue();
                break;
            }
        }
    }

- Gnaniyar Zubair rasikow@gmail.com

下级页面

1 附件
185496 查看
平均 (10 票)
满分为 5,平均得分为 4.5。
评论
讨论主题回复 作者 日期
Excellent Research Mr. ZUBAIR.... Thanks DarshanKumar N Bhatia 2010年11月30日 上午2:58
Thanks Darshan. Gnaniyar Zubair 2010年11月30日 上午5:06
Awesome one Zubair bhai... :-) Raja Seth 2013年5月24日 上午12:22
nice summary! Thank you, Gnaniyar Zubair. Jonas Yuan 2010年11月30日 下午3:00
thanks Jonas. Gnaniyar Zubair 2010年12月2日 下午9:37
nice post,thanks zubair Dhandapani Shanmugam 2010年12月20日 下午9:22
Thanks dhandapani Gnaniyar Zubair 2010年12月21日 上午6:58
Nice post. But can you please explain how that... Sandeep Nair 2011年2月7日 上午4:32
Thanks Jonas shahrokh hassanzadegan 2011年2月12日 上午5:24
Hello Zubair Shouldn't be in the fire portlet... Alexandru Ionita 2011年3月30日 上午10:32
Hi, But if I open two instances of the same... Ravi Polepeddi 2011年11月15日 上午4:49
Thanks! Suraj Bihari 2011年11月15日 下午1:36
@Alexandru Ionita: yes.. should be... ugandhar @ 2011年11月28日 上午1:36
Really nice work! A lot of thanks! Alejandro Santamaría 2012年1月16日 上午1:03
Hello Zubair, Excellent Bro!! Thanks for... Ashish Renapurkar 2012年7月7日 上午1:19
Thanks for such a nice & concise tutorial. Prakash Khanchandani 2012年7月26日 上午3:57
thanks for solution Friends Team 2012年10月30日 下午11:12
Hi, I'm trying to use the Liferay javascript... Jeroen Koek 2012年12月31日 上午2:49
Sorry guys, I solved it myself. Due to the... Jeroen Koek 2012年12月31日 上午3:33
Nice work.. Karthikeyan M 2013年3月14日 下午10:14
awesome :) useful post Mohammad Azharuddin 2013年12月18日 上午8:31
Very well explained! +1 Pankaj Kathiriya 2014年6月18日 上午5:26
Nice Post. Although I was following first... Hardik Pathak 2015年7月15日 上午8:23

Excellent Research Mr. ZUBAIR....
Thanks
在 10-11-30 上午2:58 发帖。
在 10-11-30 上午5:06 发帖以回复 DarshanKumar N Bhatia
nice summary! Thank you, Gnaniyar Zubair.
在 10-11-30 下午3:00 发帖。
在 10-12-2 下午9:37 发帖以回复 Jonas X. Yuan
在 10-12-20 下午9:22 发帖。
在 10-12-21 上午6:58 发帖以回复 Dhandapani S
Nice post. But can you please explain how that QName resolves namespace conflict in Public render parameter. In Eventing it is clear since we use qname to refer them in the lifecycle method. But in public render parameter we never make use of qname anywhere. We just use the identifier in Java as well as in supported public render parameter. How does qname actually resolve namespace issue for public render parameter?
在 11-2-7 上午4:32 发帖以回复 Gnaniyar Zubair
在 11-2-12 上午5:24 发帖。
Hello Zubair

Shouldn't be in the fire portlet the property <supported-publishing-event> instead of </supported-processing-event>. Otherwise you can't know which portlet is triggering and which portlet is listening.

regards
在 11-3-30 上午10:32 发帖。
Hi,

But if I open two instances of the same portlet in two browser tabs, how is one event different from the other ?
在 11-11-15 上午4:49 发帖以回复 Alexandru Ionita
在 11-11-15 下午1:36 发帖。
@Alexandru Ionita: yes.. should be <supported-publishing-event> for Step 1: Sender Portlet
在 11-11-28 上午1:36 发帖以回复 Suraj Bihari
Really nice work! A lot of thanks!
在 12-1-16 上午1:03 发帖以回复 Ugandhar Reddy
Hello Zubair,

Excellent Bro!! Thanks for toooooooo informative article...

Regards n thanks ...
Ashish
在 12-7-7 上午1:19 发帖。
Thanks for such a nice & concise tutorial.
在 12-7-26 上午3:57 发帖以回复 Ashish Renapurkar
在 12-10-30 下午11:12 发帖以回复 Prakash Khanchandani
Hi,
I'm trying to use the Liferay javascript api.
However this doesn't work when the user isn't logged on.
When I log on with a user the Liferay event bus is available but not when I'm not logged on.
Can you explain to me how I can enable this for the public (e.g. without a user logged on).
Thanks in advance.
在 12-12-31 上午2:49 发帖以回复 kumar g
Sorry guys,
I solved it myself.

Due to the asynchronisation of the loading of the javascript some events where fired before I had registrated the Liveray.on.
To solve this you need to fire an event that is based on the PortletsAllReady event.

If that event arrives you can fire your own event to ensure that the listener is active to receive the events on load.

Hope this helps.
在 12-12-31 上午3:33 发帖以回复 J.P. Koek
在 13-3-14 下午10:14 发帖。
Awesome one Zubair bhai... :-)
在 13-5-24 上午12:22 发帖以回复 DarshanKumar N Bhatia
在 13-12-18 上午8:31 发帖。
在 14-6-18 上午5:26 发帖。
Nice Post. Although I was following first approach to share a parameter across two portlets within a same war, it seems that below line should be set in both source and destination portlet ( Above it's mention only in porlet1)

<private-session-attributes>false</private-session-attributes>

And after setting for both portlets it worked for me.
Hope this helps someone.
在 15-7-15 上午8:23 发帖。