Sunday 9 September 2012

Liferay Multiple Instance Portlets - How to display different contents

Liferay Portal server provides the mechanism to create the multiple instances of a portlet. Its a common requirement to display different contents/data in each instance of the portlet.

For example, the following snapshot shows a portlet with 3 instances. Each instance display the different data. The first display the used memory of the JVM, next shows the Free memory and the last shows the Current Sales.


As the multiple instances of the portlet are similar but display the different data, it make sense to develop only a single portlet and create the multiple instances. This blog explains a method to achieve it.

First create a new "Liferay Project" using the eclipse wizard and select the "Allow Multiple Instances" to enable the multiple instances of the portlet, also select the "Edit Mode" of the Portlet Mode option as we would like to configure the Portlet instances from preferences option. It allows the administrator to configure the portlet instances and specify the content to display in the instance. The users without the "configuration" privileges would not be able to change the settings, and view the configured instances.

The Preferences option allows the administrator to specify the content to view in the instance. Choose the Preferences option and..


.. the following is displayed, to provide the content to display in this instance. Please provide the name as follows and save it.


The Portlet Preferences is used in the edit.jsp page to store the preferences of the instance of the portlet. Please see the following edit.jsp source code for details.

   
 <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>  
 <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui"%>  
 <%@ page import="javax.portlet.PortletPreferences"%>  
   
 <portlet:defineObjects />  
   
 Please provide the name of the portlet to view in  
 <b>MultiInstancePortlet</b>  
 .  
 <%  
      PortletPreferences prefs = renderRequest.getPreferences();  
      String content = renderRequest  
                .getParameter("<portlet:namespace/>content");  
      if (content != null) {  
           prefs.setValue("content", content);  
           prefs.store();  
 %>  
 <p>Saved Successfully!!</p>  
 <%  
      } else {  
           content = (String) prefs.getValue("content", "");  
      }  
 %>  
 <portlet:renderURL var="editURL">  
 </portlet:renderURL>  
 <aui:form action="<%=editURL%>" method="post">  
      <aui:input label="Content" name="<portlet:namespace/>content"  
           type="text" value="<%=content%>" />  
      <aui:button type="submit" />  
 </aui:form>  
   

The view.jsp is similar to the previous blog for Ajax processing using jQuery.
   
 <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>  
   
 <portlet:defineObjects />  
 <portlet:resourceURL var="resourceURL">  
 </portlet:resourceURL>  
 <script type="text/javascript">  
 function <portlet:namespace/>Data(){  
      $.post("<%=resourceURL%>", function(result) {  
                if (result == 'NOT_CONFIGURED') {  
                     $('#<portlet:namespace/>header')  
                               .text('Portlet not configured!');  
                     return;  
                }  
                var vals = result.split(',');  
                $('#<portlet:namespace/>container').css('width', vals[1] + '%');  
                $('#<portlet:namespace/>value').text(vals[1] + '%');  
                $('#<portlet:namespace/>header').text(vals[0]);  
           });//post  
      }  
      $(document).ready(function() {  
           setInterval("<portlet:namespace/>Data()", 1000);  
      });  
 </script>  
   
 <div id="<portlet:namespace/>header"  
      style="color: orange; font-size: 18px;">JVM Used Memory -</div>  
 <br />  
 <h4 id="<portlet:namespace/>value">--</h4>  
 <div style="border: 1px solid Blue; width: 100%; height: 5px;">  
      <div id="<portlet:namespace/>container"  
           style="background-color: SkyBlue; width: 0px; height: 5px;"></div>  
 </div>  
   

Finally, for backend processing the following MultiInstancePortlet.java class implements the serveResource method invoked during the POST method invocation. It checks the value of "content" in PortletPreferences of the instance and sends the data for that particular "content" type.

   
 package com.test;  
   
 import java.io.IOException;  
 import java.io.PrintWriter;  
   
 import javax.portlet.PortletException;  
 import javax.portlet.PortletPreferences;  
 import javax.portlet.ResourceRequest;  
 import javax.portlet.ResourceResponse;  
   
 import com.liferay.util.bridges.mvc.MVCPortlet;  
   
 /**  
  * Portlet implementation class MultiInstancePortlet  
  */  
 public class MultiInstancePortlet extends MVCPortlet {  
      @Override  
      public void serveResource(ResourceRequest resourceRequest,  
                ResourceResponse resourceResponse) throws IOException,  
                PortletException {  
           System.out.println("serveResource..");  
           long freemem = Runtime.getRuntime().freeMemory();  
           long totalmem = Runtime.getRuntime().totalMemory();  
           long usedmem = totalmem - freemem;  
           usedmem = usedmem / (1024 * 1024);// MB  
           freemem = freemem / (1024 * 1024);//MB  
           totalmem = totalmem / (1024 * 1024);// MB  
   
           PortletPreferences prefs = resourceRequest.getPreferences();  
           String content = prefs.getValue("content", "");  
   
           PrintWriter writer = resourceResponse.getWriter();  
           if(content.equals("JVM Used Memory")){  
                long percentage = usedmem * 100 / totalmem;  
                writer.print(content+ " - "+usedmem + "/" + totalmem + " MB," + percentage);  
           }else if(content.equals("JVM Free Memory")){  
                long percentage = freemem * 100 / totalmem;  
                writer.print(content+ " - "+freemem + "/" + totalmem + " MB," + percentage);  
           }else if(content.equals("Current Sales")){  
                long peakSale = 1000;  
                long currentSale = (long) (peakSale * Math.random());  
                long percentage = currentSale * 100 / peakSale;  
                writer.print(content+ " - "+currentSale + "/" + peakSale + " Units," + percentage);  
           }else if(content.equals("")) {  
                writer.print("NOT_CONFIGURED");  
           }  
           writer.close();  
      }  
 }  
   

Please note that for simplicity, this example shows a very basic mechanism for contents type matching but it could be configurable in some database.

Writing an Ajax Portlet using Liferay

In this blog, I walk-through the steps to create a simple Ajax Portlet using Liferay Portal server. If you have not installed the Liferay server, please follow the steps of "Installation Guide" and also the "Getting Started Tutorial" for the SDK setup and Portlet creation using eclipse.

http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE+Installation+Guide
http://www.liferay.com/community/wiki/-/wiki/Main/Liferay+IDE+Getting+Started+Tutorial

The following snapshot shows a simple Ajax portlet that displays the current memory usage of the JVM and updates the values every second. We would be developing this portlet in this blog..


For the Ajax invocation, the complete page is not refreshed and only a part is changed based on the content received from the server. The following code gets the Used memory/Total Memory of the current (Liferay) JVM and also the percentage value to display in the HTML DIV element.

 package com.test;  
   
 import java.io.IOException;  
 import java.io.PrintWriter;  
   
 import javax.portlet.PortletException;  
 import javax.portlet.ResourceRequest;  
 import javax.portlet.ResourceResponse;  
   
 import com.liferay.util.bridges.mvc.MVCPortlet;  
   
 /**  
  * Portlet implementation class AjaxPortlet  
  */  
 public class AjaxPortlet extends MVCPortlet {
      @Override  
      public void serveResource(ResourceRequest resourceRequest,  
                ResourceResponse resourceResponse) throws IOException,  
                PortletException {  
           System.out.println("serveResource..");  
           long freemem = Runtime.getRuntime().freeMemory();  
           long totalmem = Runtime.getRuntime().totalMemory();  
           long usedmem = totalmem - freemem;  
           long percentage = usedmem*100/totalmem;  
           usedmem = usedmem/(1024*1024);//MB  
           totalmem = totalmem/(1024*1024);//MB  
             
           PrintWriter writer = resourceResponse.getWriter();  
           writer.print(usedmem+"/"+totalmem+" MB,"+percentage);  
           writer.close();  
      }  
 }

For the front-end processing, we need to define the resourceURL and invoke it using HTTP POST method and updates the values received from the server. I am using the jQuery framework for the Ajax invocation, so add the jquery library in the path and update the liferay-portlet.xml. The complete source code of the view.jsp file:

 <%--  
 /**  
 * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.  
 *  
 * This library is free software; you can redistribute it and/or modify it under  
 * the terms of the GNU Lesser General Public License as published by the Free  
 * Software Foundation; either version 2.1 of the License, or (at your option)  
 * any later version.  
 *  
 * This library is distributed in the hope that it will be useful, but WITHOUT  
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS  
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more  
 * details.  
 */  
 --%>  
   
 <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>  
   
 <portlet:defineObjects />  
 <portlet:resourceURL var="resourceURL">  
 </portlet:resourceURL>  
 <script type="text/javascript">  
 function <portlet:namespace/>Data(){  
      $.post("<%=resourceURL%>", function(result) {  
                var vals=result.split(',');  
                $('#<portlet:namespace/>container').css('width', vals[1]+'%');  
                $('#<portlet:namespace/>value').text(vals[1]+'%');  
                $('#<portlet:namespace/>header').text('JVM Used Memory - '+vals[0]);  
           });//post  
      }  
      $(document).ready(function() {  
           setInterval("<portlet:namespace/>Data()", 1000);  
      });  
 </script>  
   
 <div id="<portlet:namespace/>header" style="color:orange; font-size: 18px;">  
      JVM Used Memory -   
 </div>  
 <br/>  
 <h4 id="<portlet:namespace/>value">--</h4>  
 <div style="border: 2px solid Blue; width: 100%; height: 5px;">  
      <div id="<portlet:namespace/>container"  
           style="background-color: SkyBlue; width: 0px; height: 5px;"></div>  
 </div>  
   


Also the setInterval method triggers the POST method invocation after every second to update the memory used.