Liferay 7.2 - MVCActionCommand Override

Liferay 7.2 - MVCActionCommand Override

Overview

This article describes how override Liferay's OOTB action commands.

Why do you need it?

You may need it to inject custom functionality before or after original MVCActionCommand execution: read additional data from request, save it to database, etc.
For developers familiar with Liferay 6.2 - this is a replacement for Struts Action hooks. Most of Struts Actions have been replaced with MVCCommands in the Liferay internal code - that's why extension point has been changed also.

MVCActionCommand implementation

1. Find MVCActionCommand, which you need to customize.
We'll customize user creation process. Thus, the target one will be: com.liferay.users.admin.web.internal.portlet.action.EditUserMVCActionCommand

2.  Check mvc.command.name and javax.portlet.name properties.
For our class they are:
"javax.portlet.name=" + UsersAdminPortletKeys.MY_ORGANIZATIONS,
"javax.portlet.name=" + UsersAdminPortletKeys.USERS_ADMIN,
"mvc.command.name=/users_admin/edit_user"
, which is:
 "javax.portlet.name=com_liferay_users_admin_web_portlet_MyOrganizationsPortlet",
 "javax.portlet.name=com_liferay_users_admin_web_portlet_UsersAdminPortlet",
 "mvc.command.name=/users_admin/edit_user"
if we skip the constants class.

3. Create a service module with custom MVCActionCommand (CustomEditUserMVCActionCommand here).
  • make it extend com.liferay.portal.kernel.portlet.bridges.mvc.BaseMVCActionCommand class and override com.lifedev.portlet.action.CustomEditUserMVCActionCommand.doProcessAction method;
  • copy OSGi properties above to the created class;
  • add service.ranking property to prioritize component over the original one: "service.ranking:Integer=100";
  • inject the original MVCActionCommand (specifying target component name):
    @Reference(target = "(component.name=com.liferay.users.admin.web.internal.portlet.action.EditUserMVCActionCommand)")
    private MVCActionCommand mvcActionCommand;
    
  • invoke original mvcActionCommand, and inject custom logic either before or after invocation.
Here is the result class sample:
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.portlet.bridges.mvc.BaseMVCActionCommand;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCActionCommand;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;

@Component(
        property = {
                "javax.portlet.name=com_liferay_users_admin_web_portlet_MyOrganizationsPortlet",
                "javax.portlet.name=com_liferay_users_admin_web_portlet_UsersAdminPortlet",
                "mvc.command.name=/users_admin/edit_user",
                "service.ranking:Integer=100"
        },
        service = MVCActionCommand.class
)
public class CustomEditUserMVCActionCommand extends BaseMVCActionCommand {

    @Override
    protected void doProcessAction(ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {

        _log.info("CustomEditUserMVCActionCommand, before original MVCActionCommand.");
        //todo: add pre- customization

        mvcActionCommand.processAction(actionRequest, actionResponse);

        _log.info("CustomEditUserMVCActionCommand, after original MVCActionCommand.");
        //todo: add post- customization
    }

    @Reference(target = "(component.name=com.liferay.users.admin.web.internal.portlet.action.EditUserMVCActionCommand)")
    private MVCActionCommand mvcActionCommand;

    private static Log _log  = LogFactoryUtil.getLog(CustomEditUserMVCActionCommand.class);
}
You can inject your business-logic instead of TODOs above.

Dependencies in build.gradle file:
dependencies {
    compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
    compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
    compileOnly group: "javax.portlet", name: "portlet-api", version: "3.0.0"
    compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1"
}
OSGi metadata in bnd.bnd, sample:
Bundle-Name: LifeDev MVC Command Override
Bundle-SymbolicName: com.lifedev.portlet.action
Bundle-Version: 1.0.0
Deploy created module, and verify, that your custom MVCActionCommand is invoked together with the original one. 
Enjoy 😏

Comments

Popular posts from this blog

Liferay Search Container Example

Installing Blade CLI from Command Line

Liferay Keycloak integration