Pages

Saturday 14 April 2012

Liferay MVCPortlet Explaination /Creation of Liferay MVCPortlet


Liferay MVC Portlet Explaination

In Liferay 6, the plugins SDK has been totally restrutctured. The ext environment in now a plugin, and customizing( adding new fields) the core portlets of Liferay is now not so easy as it was in LR5. The normal way most of us are accustomed to creating a portlet was through the StrutsPortlet class which is Liferay Struts bridge. But we cannot extend this class in the plugins SDK as the StrutsPortlet class is packaged within the portal-impl.jar file, which is not accessible in the plugins SDK.


Liferay has provided another class called the MVCPortlet, which is Liferay MVC pattern class. When we create a portlet in the Plugins SDK,  the portlet that gets generated is  called the MVCPortlet. A look at portlet.xml file tells us that the portlet class is defined as com.liferay.util.bridges.mvc.MVCPortlet. It provides features like page management, automatic display of success messages, automatically calling the appropriate action methods etc.  and probably many more. This class extends the LiferayPortlet class.  


In order to do some useful work in our plugin, we need to create a new Portlet class that extends the MVCPortlet. The benefit of extending from MVCPortlet is that our portlet now becomes smaller and easier to work with.

In this post, we will look at how to create action methods without using annotations, how to forward to a jsp page, how to redirect to another jsp page(to prevent page refresh problems etc.,) .

Lets assume that our class is called "BookPortlet" which extends MVCPortlet.
This fact needs to be mentioned in portlet.xml file as


<portlet-class>com.bookstore.portlet.BookPortlet</portlet-class>  

Creating the BookPortlet portlet class:

addBook.jsp

<portlet:actionURL name="addBook" var="addBookURL"/>                                      A

<aui:form action="<%= addBookURL.toString() %>" method="post">                      B

<aui:fieldset>
  <aui:input name="bookName" />
  <aui:button-row>
   <aui:button type="submit" />
  </aui:button-row>
   </aui:fieldset>
</aui:form>

BookPortlet.java
public class BookPortlet extends MVCPortlet{

   public void addBook(ActionRequest req, ActionResponse res) {
    
 }
}
When we click on the Submit button of the addBook.jsp page, it will automatically hit the addBook() method of BookPortlet.



We achieve this as follows:



At (A) we create an action URL using the taglib. We assign a name to the URL using the name attribute of the <portletURL> tag. This name should correspond to the  method name in the BookPortlet class.

There is no need to pass any request parameters that differentiate between various actions as we do in a Struts based portlet/ servlet, or use any annotation like @ProcessAction as we do when we inherit directly from GenericPortlet. MVCPortlet takes care of this for us.

In the addBook() method, we need to process all the input using req.getParameter("bookName"); ..and so on..and add the logic for adding the book into the database.

public class BookPortlet extends MVCPortlet{

public void addBook(ActionRequest req, ActionResponse res) {
  String bName = req.getParameter("bookName");
 
    /* logic for adding a book*/
 
 }
}


Forwarding to the view page:

After adding any entity, we would normally want to view the entity that we have added. This can be done as follows using the response object.

       res.setRenderParameter("jspPage","relative path to viewbook.jsp");
        
so our code would look like

public class BookPortlet extends MVCPortlet{

 public void addBook(ActionRequest req, ActionResponse res) {
  String bName = req.getParameter("bookName");
 
  /* logic for adding a book*/
 
   //will forward to the viewbook page
   res.setRenderParameter("jspPage","relative path to viewbook.jsp");
   }
}

The page management logic is handled through a render parameter called jspPage.

The above code works fine, but the last line does a simple forward, which has a small problem. The problem is related to  page refreshes, which can happen automatically or accidentally. If the user adds a book and comes to the view page, and then does a  page refresh using the browser's refresh button, then a duplicate book entity is added to the DB.

To prevent this, we need to do a redirect.


Redirection in MVCPortlet:

We perform a redirect operation, by creating a portletURL dynamically. This is done as follows.

String portletName = (String)actionRequest.getAttribute(WebKeys.PORTLET_ID);      (A)      
      
       PortletURL redirectURL =   PortletURLFactoryUtil.create(PortalUtil.getHttpServletRequest(actionRequest),  
     porltetName,  
      themeDisplay.getLayout().getPlid(), PortletRequest.RENDER_PHASE);                 (B)
    
      redirectURL.setParameter("jspPage", "relative path to viewbook.jsp");    
      redirectURL.setParameter("bookId", bookId);                                                 
         
     actionResponse.sendRedirect(redirectURL.toString());                                        (C)

At (B), we generate the portletURL using the create() method of the class PortletURLFactoryUtil.

This method signature requires among others the portletName as a parameter. If we simply supply the portlet name, as defined in the portlet.xml file, it does not work, as intended. During runtime, Liferay assigns an instance ID to every portlet, and this instance ID is suffixed to the portletName as portletName+ instanceID.

At (A), we get the portlet name + dynamic instance id stored in the variable portletName.  

At (C) we call the sendRedirect method.

This will prevent the page refresh problem.

public class BookPortlet extends MVCPortlet{

public void addBook(ActionRequest req, ActionResponse res) {
  String bName = req.getParameter("bookName");
 
    /* logic for adding a book*/
 
   //will forward to the viewbook page
   //res.setRenderParameter("jspPage","relative path to viewbook.jsp");
      
  //do redirect instead
   String portletName = (String)actionRequest.getAttribute(WebKeys.PORTLET_ID);

PortletURL redirectURL = PortletURLFactoryUtil.create(PortalUtil.getHttpServletRequest(actionRequest),  
     porltetName,  
      themeDisplay.getLayout().getPlid(), PortletRequest.RENDER_PHASE);  
        redirectURL.setParameter("jspPage", "relative path to viewbook.jsp");    
      redirectURL.setParameter("bookId", bookId);                                                      
      
      actionResponse.sendRedirect(redirectURL.toString());                          
 }
}   
Liferay's MVC framework speeds up the development of portlets. Portlet classes are reduced to only action methods, because the framework handles page management. Hope this will get you started fast.

2 comments:

  1. Hi Can you please help me
    http://www.liferay.com/community/forums/-/message_boards/message/22999878

    ReplyDelete
  2. Hi how do we set class object in setparameter for redirectURL

    ReplyDelete