Do you want to add a new Struts action to Liferay portal or override existing Struts actions? Struts action hooks let you do just that.
Let’s consider the interfaces used for Struts actions. There are two:
com.liferay.portal.kernel.struts.StrutsAction
com.liferay.portal.kernel.struts.StrutsPortletAction
The StrutsAction
interface is for regular Struts actions from the portal, like
/c/portal/update_email_address
. The StrutsPortletAction
interface is used
for similar Struts actions, but from portlets.
Struts actions are defined as classes, and they’re all connected in a
struts-config.xml
file. A struts-config.xml
for Liferay portal running on
Apache Tomcat is in the
liferay-portal-[version]/tomcat-[version]/webapps/ROOT/WEB-INF
directory. The
struts-config.xml
file links actions to specific JSP pages. Each action
performs a specific task and then returns a forward, an object containing a
name and path. The forward defines what page the user goes to after the action
completes. When a user submits a form that maps to one of these actions, the
action class is loaded, executed, and returns a forward.
A Struts action hook can wrap or override existing Struts actions or create a
new Struts path; we’ll demonstrate both here. We’ll override the Struts actions
in the struts-config.xml
using a Struts action hook to point to a custom
class, then we’ll create a new Struts path: /c/portal/sample
and
navigate to it. Let’s get started!
First, let’s override the login portlet’s Struts action using the example-hook we created earlier in the chapter.
Here’s the current action in your portal’s struts-config.xml
file:
<action path="/login/login"
type="com.liferay.portlet.login.action.LoginAction">
<forward
name="portlet.login.login"
path="portlet.login.login"
/>
<forward
name="portlet.login.login_redirect"
path="portlet.login.login_redirect"
/>
</action>
-
Navigate to your
example-hook/docroot/WEB-INF
folder and openliferay-hook.xml
. -
Insert the following code before the closing
</hook>
tag:<struts-action> <struts-action-path>/portal/sample</struts-action-path> <struts-action-impl> com.liferay.sample.hook.action.ExampleStrutsAction </struts-action-impl> </struts-action> <struts-action> <struts-action-path>/login/login</struts-action-path> <struts-action-impl> com.liferay.sample.hook.action.ExampleStrutsPortletAction </struts-action-impl> </struts-action>
-
Create a new package
com.liferay.sample.hook.action
in yourexample-hook/docroot/WEB-INF/src
folder. -
In your new package, create a class named ExampleStrutsPortletAction, which will wrap the login portlet Struts action. Insert the following code:
package com.liferay.sample.hook.action; import com.liferay.portal.kernel.struts.BaseStrutsPortletAction; import com.liferay.portal.kernel.struts.StrutsPortletAction; import com.liferay.portal.theme.ThemeDisplay; import com.liferay.portal.kernel.util.WebKeys; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletConfig; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; public class ExampleStrutsPortletAction extends BaseStrutsPortletAction { public void processAction( StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse) throws Exception { ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY); Long currentuser = themeDisplay.getUserId(); if (currentuser != null) { System.out.println("Wrapped /login/ action2"); } originalStrutsPortletAction.processAction( originalStrutsPortletAction, portletConfig, actionRequest, actionResponse); } public String render( StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, RenderRequest renderRequest, RenderResponse renderResponse) throws Exception { System.out.println("Wrapped /login/ action"); return originalStrutsPortletAction.render( null, portletConfig, renderRequest, renderResponse); } public void serveResource( StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws Exception { originalStrutsPortletAction.serveResource( originalStrutsPortletAction, portletConfig, resourceRequest, resourceResponse); } }
-
Create a new class named
ExampleStrutsAction
in thecom.liferay.sample.hook.action
package. It will implement your new portal Struts action. Insert the following code:package com.liferay.sample.hook.action; import com.liferay.portal.kernel.struts.BaseStrutsAction; import com.liferay.portal.kernel.util.ParamUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ExampleStrutsAction extends BaseStrutsAction { public String execute( HttpServletRequest request, HttpServletResponse response) throws Exception { String name = ParamUtil.get(request, "name", "World"); request.setAttribute("name", name); return "/portal/sample.jsp"; } }
We’ve overridden the execute(HttpServletRequest, HttpServletResponse)
method
of BaseStrutsAction
, but not the execute(StrutsAction, HttpServletRequest, HttpServletResponse)
method. The original Struts action’s execute()
method
is ignored. That’s fine for our example.
Best Practice: When overriding an existing Struts action, it’s usually best
to override the method that takes the original Struts action handle as a
parameter and execute that original Struts action. Think of the original action
as a servlet filter or aspect. If you override the method that takes the
original action handle as a parameter and don’t explicitly execute it, the
original action won’t be executed. If you override the execute
method that
does not take the original action as a parameter, you are ignoring the
original action and it won’t be executed.
That’s it for overriding the Struts actions! Now Let’s get our new Struts path working.
-
Create
sample.jsp
in theexample-hook/docroot/META-INF/custom_jsps/html/portal
directory. Insert the following code:<% String name = (String)request.getAttribute("name"); %> Hello <%= name %>!
-
Add
/portal/sample
to your portal’s list of paths that don’t require authentication by copying your existingauth.public.paths
property assignment from your portal’sportal.properties
into yourportal-ext.properties
file and adding/portal/sample
to the end of the value list. It looks similar to the assignment below:auth.public.paths=\ /asset/get_categories,\ ... /wiki/rss,\ /portal/sample
-
Restart your portal server.
Congratulations! Your Struts action hook plugin is complete! Now when you access the Sign In portlet, this message prints to your console:
Wrapped /login/ action
When you actually log in, this message prints to your console:
Wrapped /login/ action2
Wrapped /login/ action
Both custom Struts actions are executed via your Struts action hook!
Try your new Struts path by accessing it from your browser (e.g.
http://localhost:8080/c/portal/sample
).
Let’s continue our hooks expedition by overriding a portal service.