How can I share session data across portlets in Liferay?

Before I answer this question, consider:

What are you actually trying to accomplish? Are you sure that you need share session data to fulfill your goal? Can you use another (better) method to share data between several portlets across several requests?

David Nebinger, Liferay expert and prolific community member, has said that "Session Storage is Evil." Perhaps you could dismiss his statement as hyperbole, but considering all his supporting evidence (and other experts who have echoed his concerns), 1 I think that would be a mistake. Storing (and sharing) data in the session has many drawbacks. For example, sessions must be replicated in a cluster or risk data loss and a disruptive user experience, data in the session must be accessed/modified in a thread-safe way, and any session data increases your application’s memory footprint and reduces scalability. More importantly, the HttpSession (which the PortletSession is based on) is not designed for sharing data between applications (see Servlet Spec 3.1 Section 7.3: Session Scope). Finally, sharing data via the session creates tight, opaque coupling between applications. So before you store and share any data via the PortletSession, consider the following alternatives:

1. Use Public Render Parameters to share string values between portlets.

Ultimately, Public Render Parameters (PRPs) are stored in the session (in Liferay’s implementation at least), so they still have some similar drawbacks to session storage. However, PRPs are limited to strings, so their impact on memory is less extreme than other objects that might be stored in the session. Also PRPs are more clearly defined and allow more loose coupling between portlets than shared session data. They also provide bookmarkability with idempotent URLs. Use PRPs to share data such as database keys and similar strings that allow other portlets to determine what or how certain data should be displayed. PRPs should not be used to mutate model data in other portlets, unlike Portlet Events.

2. Use Portlet Events to publish events which can mutate model data that is shared by multiple portlets.

Events don’t use session storage, so they will usually have less of an effect on your application’s memory footprint than session data. Although events might require more complex code than PortletSession.get/setAttribute(), events (like PRPs) enable clearly-defined, loose coupling between portlets via the publish/subscribe design pattern. Use Portlet Events when an action in one portlet may be interesting and even provoke changes in data in another portlet.

3. Use Service Builder to cache data and persist data to the database.

Persisting data to the database with Service Builder is an extreme option with potential drawbacks for scalability and increased complexity. Creating a new Service with Service Builder, potentially adds two new JARs to your project. However, it will ensure that the data is available to all portlets via the service APIs. You can also enable caches to improve scalability. Perhaps the data you are sharing in session storage is not really temporary or it should be preserved even if a server goes down. In that case, you should use Service Builder.


How to Share Session Data Between Portlets:

Hopefully 99.9% of people have decided to use PRPs, Events, or Service Builder to share data between portlets and have stopped reading at this point. But for the sake of the 0.1% who legitimately need to use session sharing, I’ve provided a list of ways that you can do that. However, I must again urge caution when relying session sharing and using session storage in general. Please make sure that you are following best practices so that the “easy-to-use” session doesn’t become a source of impossible-to-debug problems. To that end, I encourage you to use the earlier entries in this list and limit the state that is shared between the portlets as much as possible.2

  1. Use PortletSession.APPLICATION_SCOPE to share one or more session attributes between portlets in the same application/WAR.

    For example:

    // Portlet A
    portletRequest.getPortletSession(true)
        .setAttribute(CONSTANTS.ATTR_NAME, "value", PortletSession.APPLICATION_SCOPE);
    

    // Portlet B (in the same WAR)
    String attrValue = portletRequest.getPortletSession(true)
        .getAttribute(CONSTANTS.ATTR_NAME, PortletSession.APPLICATION_SCOPE);
    
    Pros:
    • Portlet standard method of accessing/sharing session scoped data between portlets.
    • Only exposes the necessary attribute(s) to other portlets (instead of exposing the entire session).
    • Only exposes the data to portlets in the same application/WAR.
    Cons:
    • Cannot share data between portlets in different WARs.
  2. Use Liferay session.shared.attributes prefixes (such as LIFERAY_SHARED_) to share one or more session attributes between portlets in different applications/WARs.

    Liferay exposes certain session attributes to all portlets based on certain prefix values. Although these prefixes are configurable via portal-ext.properties, I recommend using one of the default prefixes: LIFERAY_SHARED_.

    For example:

    // Portlet A
    portletRequest.getPortletSession(true)
        .setAttribute("LIFERAY_SHARED_" + CONSTANTS.ATTR_NAME, "value", PortletSession.APPLICATION_SCOPE);
    

    // Portlet B (in a different WAR)
    String attrValue = portletRequest.getPortletSession(true)
        .getAttribute("LIFERAY_SHARED_" + CONSTANTS.ATTR_NAME, PortletSession.APPLICATION_SCOPE);
    
    Pros:
    • Only exposes the necessary attribute(s) to other portlets (instead of exposing the entire session).
    Cons:
    • Exposes session attribute(s) to all portlets.
    • Tight coupling without indicating which other portlets might be utilizing this data.
    • Non-standard method of sharing session data.
  3. Use <private-session-attributes>false</private-session-attributes> (in your liferay-portlet.xml ) to share all session data between portlets in different applications/WARs.

    For example:

    Portlet A:

    liferay-portlet.xml:

    <liferay-portlet-app>
        <portlet>
            <portlet-name>portlet.a</portlet-name>
            <!-- your config here -->
            <private-session-attributes>false</private-session-attributes>
            <!-- ... -->
    

    portletRequest.getPortletSession(true)
        .setAttribute(CONSTANTS.ATTR_NAME, "value", PortletSession.APPLICATION_SCOPE);
    

    Portlet B:

    liferay-portlet.xml:

    <liferay-portlet-app>
        <portlet>
            <portlet-name>portlet.b</portlet-name>
            <!-- your config here -->
            <private-session-attributes>false</private-session-attributes>
            <!-- ... -->
    

    // Portlet B (in a different WAR)
    String attrValue = portletRequest.getPortletSession(true)
        .getAttribute(CONSTANTS.ATTR_NAME, PortletSession.APPLICATION_SCOPE);
    
    Pros:
    • Exposes all PortletSession.APPLICATION_SCOPE attributes without the need for special prefixes.
    Cons:
    • Exposes all PortletSession.APPLICATION_SCOPE attributes to all portlets with <private-session-attributes>false</private-session-attributes>.
    • Tight coupling without indicating which other portlets might be utilizing this data.
    • Non-standard method of sharing session data.
    • May cause memory leaks or other failures in standard frameworks, such as the JSF Portlet Bridge.
  4. Access the HttpSession by unwrapping the HttpServletRequest from the PortletRequest.

    Don’t do this. It’s not guaranteed to work in Liferay 7.0+ since you may not be able to access the app server’s HttpServletRequest and you can accomplish essentially the same goal with #3.


  1. For more details see this forum post by Olaf Kock (top Liferay answerer on StackOverflow) and Mark Needham’s Rules of Thumb: Don’t Use the Session blog.
  2. The more data is shared between portlets, the more it starts to look like (evil) global state to me.
Blogs

Nice overview. I would like to see a design of more complex workflow form (multiple pages, next, back and submit buttons)  in Liferay implemented based on standard MVC pattern with actions and render methods. Note. Do not use frontend libraries and frameworks only plain HTML.