Call Portlet Action Method From Another Portlet On Different Liferay Page

Sometimes, we need to call portlet action method from another portlet which may be on same or different Liferay page. For this we need to construct action URL through Java API which points to target portlet.

In this article, we will see how to achieve this. To understand it properly, let’s take a real example. 
Assume that we have two portlet

  • One portlet which takes number as input and shows factorial.
  • Second portlet which takes two numbers and show multiplication.
These portlets don’t any relation with each other. So also assume that they are placed on different Liferay Page.
 
First Let us design this two portlet independently. I am using Liferay MVC portlet to create it. Please refer my previous blog to create custom Liferay MVC Portlet.

We will give first portlet name as show-factorial and eclipse will append -portlet at the end while creating portlet so the final name will be show-factorial-portlet
 
Similar way we will give second portlet name as show-multiplication and eclipse will append -portlat at the end while creating portlet so the final name will be show-multiplication-portlat

I would recommend looking at index page ‘A Complete Liferay Guide‘ to browse all topics about liferay.

Note:- Download source code of these portlets at the end of this blog.


Explanation:-

1) For show-factorial-portlet.

  • I have created portlet class ShowFactroailPortlet in package com.techblog.opensource.portlet and added following code in it.

 


Note:- I have pasted code here for explanation. You can download it from the link at the end of this post.

  • First I take logger with the help of LogFactoryUtil (Liferay utility class).
  • multiply is the processAction method. It will be called when we submit two numbers for multiplication from JSP. It has key (name) and its value is getMultiplication”
  • In this processAction method, I am taking two parameters and converting it into numbers. After that I am multiplying it and setting it in render parameter so that it will be available in render method (This is one of the way to send value from Action method to render method). Note:- setRenderParameter method is available only for object of type ActionResponse.
  • doView method is associate with portlet’s view mode and will be called whenever portlet get render.
  • In this method I am fetching the parameter(multiplication) which I have sent in Action method and storing it to request scope so that it can be available in JSP (via EL)
  • Note:- I have used ParamUtil.get method of Liferay utility class ParamUtil  to get request parameter. Third argument is the default value which will be send in case the parameter is not exist.
I have added the following code in view.jsp file

Explanation:-

  • The firs line is the Taglib declaration.
  • In second line, we have created portlet ActionURL. We gave name =”getMultiplication” which is the same as key value of processAction method in portlet class. So this ActionURL will call portlet’s processAction method (multiply).
  • This ActionURL will be used in JSP by defining var value. We gave value of var  as getMultiplicationUrl so it can be accessed as ${getMultiplicationUrl} (as EL).
  • Next to it, I have created form which having two text box, taking two inputs and one submit button which will submit the form.
  • action of this form is the ActionURL which we have created. We have set it through ${getMultiplicationUrl

call portlet action method - Multiply_Portlet

Give two numbers and click on Submit button and you will get multiplication of these two number. You can also observe the log that we have put in render and action method.

 
2) For show-factorial-portlet. (Note:- Download the code and refer it)
  • Very similar way I have create show-factorial-portlet.
  • This portlet having class name ShowFactroailPortlet which resides under package com.techblog.opensource.portlet
  • In this portlet, we are taking only single number and counting its factorial and showing it to JSP.
When we deploy and place it to page, it will look like below screenshot.
call portlet action method - show-factorial
entry and number(less than 10) and click on submit button, it will show factorial of that number.
 
These both portlet are working independently. Also let assume that they both are placed on different Liferay page.

call portlet action method

What if user wants the factorial of the number which comes after multiplication of two numbers. I mean to say I am using multiplication portlet and I am passing num1 and num2. I want the factorial no of multiplication of num1 and num2.
 
If I write the code to calculate the factorial in multiplication portlet, then its code redundancy and we haven’t re-use the code.
 
Solution:- Some how If I forward the request after processing multiplication of two numbers to factorial portlet, then It will be best approach. I don’t have to write the same logic again. Just have to pass control to another portlet placed on different liferay page.
 
So let us do this. First create two public liferay pages in guest(Liferay) community. 
  • Page-1 
  • Page-2
place multiplication portlet on Page-1 and factorial portlet on Page-2. You can observe that these two portlets are now working independently. Use can click on Page-1 and able to see multiplication portlet and if clicks on Page-2, factorial portlet will be seen.

I would like to recommend to download the source code from the link at the bottom of this blog and refer it side by side to get complete idea


We have to follow certain rules while forwarding control from one portlet to another portlet. Following are the prerequisites.

  • The portlet to which we are forwarding control should be non-instanceable. in our example we will forward control from multiplication portlet to factorial portlet. So factorial portlet should be non-instanceable.
  • The portlet to which we are forwarding control should set authentication token false. Lifeary generate token for each request (POST). So we need to insure that the factorial portlet should not generate this token.
To fulfill these prerequisite, we need to make following changes to factorial portlet (Since we have to forward from multiplication portlet to factorial portlet)
  • Make it non-instanceable
For this, open liferay-portlet.xml file for show-factorial-portlet and set <instanceable>false</instanceable> just after <icon> element.
  • Turn authentication token off.
For this, open portlet.xml file for show-factorial-portlet and add following init parameters just below <portlet-class> element.

You might have observed that even after setting auth token false, liferay does generate the token. But don’t worry, if we set this flag as false then even though liferay generate the auth token in url, it will not take it in consideration.


Next, we have to write certain code to fetch the url of factorial portlet inside multiplication portlet. So I have created util class MultiplicationUtil (in package com.techblog.opensource.util) and create methods that will do all this stuff.


I have added following two methods in this class (MultiplicationUtil)
1) getLayout :- 

  • This method will take PortletRequest, friendly url of page and community name and will return the Layout.
  • Layout in liferay represent the structure of Liferay page. Any portlet’s url will be decided by layout.
  • In this method we are making call LayoutLocalServiceUtil.getFriendlyURLLayout and passing parameter like groupId, boolean isPrivateLayout and friendly url of page.
  • first we set the isPrivateLayout to false. So it will search for public pages and if not found (if layout is null) then we do the same thing but just passing isPrivateLayout as true).
  • We are getting groupId by calling GroupLocalServiceUtil.getGroup(companyId,communityName).getGroupId().
  • pageFriendlyUrl is nothing but the friendly url of liferay page. In our case we are forwarding control to page-2 on which the factorial portlet is placed.
2) getFullPortletURL:-
  • This method takes input as PortletRequest, friendly url of liferay page (on which we wanted to go), portlet name, parameters, portlet phase and community name.
  • portlet name and phase will be the name and phase of the portlet to which we want to forward control. (factorial portlet in our case)
  • Next to it we are tacking HttpRequest from PortletRequest.
  • Then we are calling PortletURLFactoryUtil.create(), passing these parameters it is returning final url of the portlet where we wanted to go.
Now open the ShowMultiplicationPortlet.java of multiplication portlet, you will observe the following private method 
getFactorail
  • This method takes PortletRequest and multiplication Number as input.
  • This multiplication Number is nothing but the multiplication of num1 and num2 that we will pass to multiplication portlet.
  • Since our goal is to create Link which forward us to Factorial portlet from multiplication portlet. Also it will do the factorial of multiplication number (num1 * num2)
  • In this method we took few variable as below
    • pageFriendlyUrl:- is the page friendly url. Since factorial porltlet reside on page-2 page, its value will be friendly url of that page (which is “page-2“)
    • portletName:- which is portlet name of factorial porltet. Don’t confuse this porltet name with the one defined in portlet.xml file. This portlet name will be refer in urls. Here is the trick how to get it.
      • Go to the page where the portlet is deployed (in our case page-2)
      • Click on Configuraiton (Tool like) icon from the portlet header and click on Look and Feel.
      • Go to tab Advanced Style. In this tab at very top you will observe something like Portlet ID: #portlet_showfactorial_WAR_showfactorialportlet
      • so in this case the portletName will be showfactorial_WAR_showfactorialportlet (Just remove #porltet_ from beginning).
    • CommunityName:- will be the name of the community in which the factorial protlet reside. We can forward the porltet which is on different community
    • parameters:- These are the parameters which will be added in url. 
      • Its map taking string as key and string array as value.
      • we are passing “number” with the multiplication of num1 and num2.
      • The reason we take the parameter name as number because if you observe the action method of factorial portlet’s processAction method then its taking value of “numebr” request parameter. Since our goal is to directly call factorial porltet process action method and that is the reason we add one more parameter javax.portlet.action to “getFactorial”. Because its matching with the key of that process action method.
      • We also added p_p_state parameter to tell liferay that, open the factorial portlet in normal mode.
    • And at the end I am calling util’s method MultiplicationUtil.getFullPortletURL() and passing all these values. which will return the final url.
Then I made call of getFactorial method in doView() method and storing the return value in request attribute of multiplication portlet so that it will be available in view.jsp.
 
In view.jsp file (of multiplication portlet), at the end I am showing the link (which is return value of getFactorial method we stored in request attribute in doView method) which will forward me to factorial portlet.

Eventually it will look like this.
 
We are on page-1 and it showing multiplication portlet. we will give value 3 and 4 
call portlet action method - on-page-1
 
On clicking submit button it will calculate the multiplication and at the bottom of the page, the link will forward me to page-2.
call portlet action method - processing-page-1
If I click on the link at the bottom, it will forward me to page-2 and will show factorial of 12 (3*4) as below
call portlet action method - on-page-2
 
And its done. You may put some complex logic to forward to other portlet. Also you can place the second portlet in different community and forward the control to it.
 
Feel free to ask questions. I will try my best to get the answer.
 

Download Source

Download Source

Share This Post

31 Comments - Write a Comment

  1. The “show-multiplication-portlet” doesn’t start.

    I get this error:
    17:54:05,324 ERROR [RuntimePageImpl-6][render_portlet_jsp:132] null
    java.lang.NullPointerException
    at test.MultiplicationUtil.getFullPortletURL(MultiplicationUtil.java:69)
    at test.ShowMultiplicationPortlet.getFactorail(ShowMultiplicationPortlet.java:76)
    at test.ShowMultiplicationPortlet.doView(ShowMultiplicationPortlet.java:38)
    at com.liferay.portal.kernel.portlet.LiferayPortlet.doDispatch(LiferayPortlet.java:213)
    at com.liferay.util.bridges.mvc.MVCPortlet.doDispatch(MVCPortlet.java:323)
    at javax.portlet.GenericPortlet.render(GenericPortlet.java:233)
    at com.liferay.portlet.FilterChainImpl.doFilter(FilterChainImpl.java:103)
    at com.liferay.portlet.ScriptDataPortletFilter.doFilter(ScriptDataPortletFilter.java:55)
    at com.liferay.portlet.FilterChainImpl.doFilter(FilterChainImpl.java:100)
    at com.liferay.portal.kernel.portlet.PortletFilterUtil.doFilter(PortletFilterUtil.java:64)
    at com.liferay.portal.kernel.servlet.PortletServlet.service(PortletServlet.java:112)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
    at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(InvokerFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:749)
    at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:605)

    any clue?

    Reply
    1. Hi John,
      Sorry for late respond.
      Per my best knowledge, Source code I attached is for Liferay 6.0. In case if you are working on higher version of liferay, I would suggest to create portlet and refer my code instead of directly importing as a Liferay plugin project. I will also look it from my end.

      Feel free to ask questions / give suggestions.

      Regards
      Nilang

      Reply
  2. Hi Nilang – Nice post.

    Though I am trying same thing ( page-1-Portlet1 calls action through actionURL on Page-2-portlet2). But somehow the processAction() method doesnt get invoked. It loads that page and portlet but goes straight into render method.
    Any clue ?

    Just to mention, I have Spring MVC portlet controller and not MVCPortlet(Liferay). Does this matter ?

    //Hardik

    Reply
    1. Hi Hardik

      Welcome to Tech blog and thanks for appreciation. It matter when you are using Spring mvc portlet.You need to pass one extra parameter called ‘action’ while creating portlet URL in MultiplicationUtil.getFullPortletURL() method. The value of action parameter should be value of key of action method (in annotation) of your second portlet.

      Try this way and let me know if you still face any trouble.

      Regards
      Nilang

      Reply
      1. Hi Nilang – What do you mean by extra parameter named “action” ? As even in your MVCPortlet code I see a action parameter as below
        parameters.put(“javax.portlet.action”, new String[]{“getFactorial”});

        Should I add one more parameter ?

        My Code
        ————-
        private String getStudentDetailsURL(PortletRequest request){

        String pageFriendlyUrl=”/testaPage”;
        String portletName=”TestApp_WAR_AbcDetails”;
        String communityName=”Guest”;

        Map parameters = new HashMap(0);

        parameters.put(“javax.portlet.action”, new String[]{“myprocessAction”});
        parameters.put(“srn”, new String[]{“1122334455”});
        parameters.put(“p_p_state”, new String[]{LiferayWindowState.NORMAL.toString()});

        return UoLUtil.getFullPortletURL(request, pageFriendlyUrl, portletName, parameters, PortletRequest.ACTION_PHASE, communityName);
        }

        And myprocessAction – is the annotation of the ActionProcessing method of my spring controller as below..

        @ProcessAction(name=”myprocessAction”)
        public void myprocessAction(ActionRequest request, ActionResponse response) throws PortletException {
        logger.debug(“IN MYProcessACTION”);
        }

        BUT this is not working ! Am I missing anything ?

        Thank you
        //Hardik

        Reply
        1. Hi Hardik,
          You need to pass sth like parameters.put(“action”, new String[]{“< >”}); In case of simple MVCPortlet you have to pass “javax.portlet.action” but in case of spring mvc portlet, you need to pass simply “action”.

          Let me know if you still face trouble.

          Regards
          Nilang

          Reply
  3. Hii nilang your example is really helpful to me but can you tell me one thing when i trying to build multiplication portlet it is showing the following stacktrace

    SEVERE: Servlet Dynamic Resource Servlet threw unload() exception
    javax.servlet.ServletException: Servlet.destroy() for servlet Dynamic Resource Servlet threw exception
    at org.apache.catalina.core.StandardWrapper.unload(StandardWrapper.java:1503)
    at org.apache.catalina.core.StandardWrapper.stopInternal(StandardWrapper.java:1843)
    at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
    at org.apache.catalina.core.StandardContext.stopInternal(StandardContext.java:5614)
    at org.apache.catalina.util.LifecycleBase.stop(LifecycleBase.java:232)
    at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3947)
    at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1270)
    at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1439)
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:315)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1374)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1530)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1540)
    at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1519)
    at java.lang.Thread.run(Thread.java:722)
    Caused by: java.lang.NullPointerException
    at com.liferay.portal.kernel.servlet.PortalClassLoaderServlet.doPortalDestroy(PortalClassLoaderServlet.java:115)
    at com.liferay.portal.kernel.servlet.PortalClassLoaderServlet.portalDestroy(PortalClassLoaderServlet.java:54)
    at com.liferay.portal.kernel.servlet.PortalClassLoaderServlet.destroy(PortalClassLoaderServlet.java:40)
    at org.apache.catalina.core.StandardWrapper.unload(StandardWrapper.java:1482)
    … 15 more
    How i can solve this

    Reply
    1. Hi Mallikarjun,

      Thanks for writing here. Yes you are right. It’s known issue for time being. Recently I revamped the whole site and I need to correct the download links. I am going to do it by today itself. I will update this and let you know. Thanks for pointing this out.

      Hope you get help from articles from this blog. Feel free to ask questions / give feedback.

      Regards
      Nilang

      Reply
  4. Good Day Nilang,

    I tried downloading your code here and i’m not sure how to get the source for MultiplicationUtil.java
    May I ask how can see the source code for the utility and the source code how it was communicated from one portlet to another?

    Thanks

    Reply
    1. I have a question. I’m writing two portlets.In the portlet one (only teacher can see it),I’ll post my question. In the portlet two in the same page(Student and teacher can see) the question will show to all student. What can i do in order to see the teacher’s post (all student can see). I’m beginner please give me an advice. Thanks!

      Reply
    2. Hi Vinh Hua,

      Thanks for appreciation and welcome to Tech blog !!. From the problem statement, I understand the following things. (Let me know If I am correct).

      You want one portlet which is only viewable to only teachers and second portlet which is viewable to both Teach and Student.

      Solution:- You can place these portlets in different liferay pages. Let say you are putting portlet one(Only for Teacher) on Liferay Page-A and putting Portlet 2(Student and Teacher) on liferay page-B
      Create two roles 1)Teacher and 2)Student.
      Give Page-A permission to view for user who has role Teacher
      Give Page-B permission to view for user who has role Teacher and Student both.

      Note:- You need to create both of these role as Regular Role.

      This is simplest solution in which you don’t have to change any code. It requires only configuration change.

      Let me know if this is good. Also let me know if you need any more information.

      Regards
      Nilang I Patel

      Reply
    3. Ok.Thank you! I understand about the roles. Another problem is when I use two portlet as your post, When the teacher post a question in page A, The teacher go to page B, he can see his post, it is ok, but when I tab at the browser-> run another localhost:8080 and login with student’s account(or teacher’s account), I only see the portlet, I cannot see the question of the teacher. I would like all other new tab in browser can see the post. That is my problem!

      Reply
    4. Hi Vinh Hua,

      Can you please try open in another browser instead of same browser’s new TAB. Also can you please give all the steps that you are performing in more detail so that I can look in it.

      Regards
      Nilang I Patel

      Reply
    5. My problem:
      “Quick Note” portlet in life-ray is an example. When I post a message. I open another browser(or TAB) -> I can see the message.
      I ‘m trying to write a portlet with a text field. When I summit the text fill show. But when I TAB(or open new browser) -> I can not see the text. Some people say that I must broadcast the data for all user. I don’t know how to do it. 🙁

      Reply

Post Comment