2
votes

EDIT: Edited title to reflect new findings. It doesn't matter what type of portlet the second portlet is. See general behavior below:

  1. I have a Struts2 portlet A on the page, its default "index" action is pageA1.
  2. I click a link on A1 and go to pageA2.
  3. I refresh the page and the portlet A still shows pageA2.
  4. There are other portlets on the page. I choose any one portlet B and click a link on B1.
  5. Portlet B navigates to B2. Meanwhile, Liferay refreshes the other portlets on the page, including Struts2 portlet A.
  6. Expected result after portlet A refresh is pageA2, however the page shown is pageA1!

Symptoms:

I added some logging to the struts action to display the parameters passed. During normal navigation (i.e. I'm just using the struts2 portlet A by itself), the parameters struts.portlet.action and struts.portlet.mode are displayed. However, if an auto-refresh occurs on struts2 portlet A due to another portlet B's render/action phase, those parameters don't seem to be passed to struts2, and thus the portlet A defaults back to its index pageA1, instead of the page shown before the refresh (pageA2).

Does this mean that since there is no struts.portlet.action param detected by struts2, it will call the default action (in this case, i set it to "index"->pageA1)?


Old Details

I have a project set up that has two portlets, both using the Struts2 framework. Both portlets are actually quite similar to each other, but their code exists in different packages, and in struts.xml, their Actions are defined in separate modules with their own namespaces. They are still part of one project, though, and are packaged together in a single WAR file.

I deploy the WAR to Liferay, and add both portlets to a single page. The following behavior occurs when I use the two portlets:

  1. Click a link on Portlet A.
  2. Portlet A loads into screen A2.
  3. Click a link on Portlet B.
  4. Portlet B loads into screen B2.
  5. However, as a side effect, Portlet A refreshes and the screen shown is its "index" page (instead of screen A2).

Is this the expected behavior, or is there anything else I should do to make this particular setup work in a single portal page?

EDIT: The links I am clicking are renderURLs (generated using the s:url tag). The second page of both portlets contain forms, which I am not sure is of any significance.

I've added some simple logging, and based on it, I've discovered that on every page refresh, both portlets are being rendered twice. I don't think that's a natural behavior.

Here is my struts.xml if it's of any use:

<package name="portletA" extends="struts-portlet-default" namespace="/portletA">

    <action name="index" class="my.a.DisplayFirstPageAction">
        <result name="success">/pageA1.jsp</result>
    </action>

    <action name="displayForm" class="my.a.DisplaySecondPageAction">
        <result name="input">/pageA2.jsp</result>
    </action>
</package>
<package name="portletB" extends="struts-portlet-default" namespace="/portletB">

    <action name="index" class="my.b.DisplayFirstPageAction">
        <result name="success">/pageB1.jsp</result>
    </action>

    <action name="displayForm" class="my.b.DisplaySecondPageAction">
        <result name="input">/pageB2.jsp</result>
    </action>
</package>

On both portlets, pageA1.jsp has a link that calls the struts action "displayForm". The FirstPageAction's execute method returns SUCCESS, while the SecondPageAction's execute method returns INPUT.

I thought it might be because I don't have a "success" result in my second action (my execute() method returns "input" since I have a form in the page). Adding a result=success tag doesn't help, though.

Thinking out loud, if I click on A1's link while B2 is rendered, B2's render/execute action should be called, but it seems like B1's render/execute action is called instead.

1

1 Answers

1
votes

Check http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/understanding-the-two-phases-of-portlet-execution, specifically the paragraphs bellow:

The portlet specification defines two phases for every request of a portlet, to allow the portal to differentiate when an action is being performed (and should not be repeated) and when the content is being produced (rendered):

  • Action phase: The action phase can only be invoked for one portlet at a time and is usually the result of an user interaction with the portlet. In this phase the portlet can change its status, for instance changing the user preferences of the portlet. It is also recommended that any inserts and modifications in the database or operations that should not be repeated are performed in this phase.
  • Render phase: The render phase is always invoked for all portlets in the page after the action phase (which may or not exist). This includes the portlet that also had executed its action phase. It's important to note that the order in which the render phase of the portlets in a page gets executedis not guaranteed by the portlet specification. Liferay has an extension to the specification through the element render-weight in liferay-portlet.xml. Portlets with a higher render weight will be rendered before those with a lower value.

[...]

There are three types of URLs that can be generated by a portlet:

  • renderURL: this is the type of URL that we have used so far. It invokes a portlet using only its render phase.
  • actionURL: this type of URL tells the portlet that it should execute its action phase before rendering all the portlets in the page.
  • resourceURL: this type of URL can be used to retrieve images, XML, JSON or any other type of resource. It is often used to generate images or other media types dynamically. It is very useful also to make AJAX requests to the server. The key difference of this URL type in comparison to the other two is that the portlet has full control of the data that will be sent in response.

So I guess you are using an actionURL to go to page A2 instead of a renderURL.