• Liferay7 BPM门户开发之17: Portlet 生命周期


    Portlet 生命周期

    init() =〉 render() =〉 processAction() =〉 processEvent() =〉 serveResource() =〉destroy()

    • init()

        在Liferay容器部署portlet时,启动portlet实例化

        init有两个写法:

      •  public void init() throws PortletException
      •  public void init(PortletConfig config) throws PortletException

          PortletConfig对象用于读取portlet配置(定义在portlet.xml)

    • render()

        HTML的UI输出,是最常用到的方法了,一个例子

    @Override
     public void render(RenderRequest request, RenderResponse response)
       throws PortletException, IOException {
      _log.info(" 做些你自己定义的事情");
      super.render(request, response);
     }

        值得注意的是:

      • 在render()的一般最后一行,需要写这句:super.render(request,response),如果不写会不能正常工作,会造成GenericPortlet继承链的断裂(render() 在GenericPortlet中被重写);
      • Portlet不能直接访问ServletRequest和ServletResponse;
      • RenderRequest和RenderResponse是接口,继承于PortletRequest和PortletResponse;
      • 如果一个页面有多个Portlets,当每次页面刷新,所有Portlets实例的render()就会被全部调用一次;
      • 有趣的是,Portlet规范并没有一个排序的机制,去安排这些Portlets的render()顺序,这证明了Portlet的独立性,如果要定制开发时序的加载,那必须自己去实现一个GenericPortlet的子类,或者直接扩展MVCPortlet,增加一个加载队列。
    • processAction()

        action 处理,后面再详细介绍,这里只要知道ActionRequest也是一个接口继承

        

    • processEvent()

        监听时间处理

    • serveResource()

        通过resource URL处理资源

    • destroy()

        portlet卸载时的处理

        

    Portlet 容器负责的工作

    • 装载portlet类
    • 创建portlet实例
    • 初始化
    • 向portlet实例发送用户请求(request)
    • 销毁实例(当容器卸载portlet)

    Portlet和Servlet的区别

    见:

    Liferay7 BPM门户开发之15: Liferay开发体系简介

    Portlet类的层次

    • GenericPortlet实现了Portlet接口;
    • LiferayPortlet是GenericPortlet的子类,并且提供了一些额外方法;
    • MVCPortlet继承与LiferayPortlet.提供了用于MVC架构的一些方法;
    • UserCustomPortlet (用户定义portlet)继承与MVCPorrtlet;

    URLs 传递

    • Portlet在开发调试时不如servlet中那样方便直接,无法定义静态指定地址url,而是通过以下几种方式:
      •   Render URL:call render method,用于界面控制
      •   Action URL:call action method,用于服务调用
      •   Resource URL:call serve resource method,用于访问资源

    Render 处理

    首先,介绍Render URL,有四种方式:

    方式1、Portlet Tag (portlet:renderURL)

    view.jsp

    <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

    <%@page import="com.liferay.portal.kernel.portlet.LiferayPortletMode"%>
    <%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>

    <portlet:renderURL var="renderUrl" 
     windowState="<%=LiferayWindowState.NORMAL.toString() %>" copyCurrentRenderParameters="true" portletMode="<%=LiferayPortletMode.VIEW.toString()%>">
     <portlet:param name="param" value="XXXXXX"/>
    </portlet:renderURL>
     
    <a href="${renderUrl}">RenderURL Created by Portlet Tag</a>
    render()
    private static final Log _log = LogFactoryUtil.getLog(YourPortlet.class.getName());
     
     @Override
     public void render(RenderRequest request, RenderResponse response)
       throws PortletException, IOException {
      _log.info(" This is render method");
      String data = request.getParameter("param");
      String data1= ParamUtil.getString(request, "param","");
      super.render(request, response);
     }

    最终会实际产生以下URL:

    http://localhost:8080/web/portletTest/test?

    • p_p_id=renderURLbyportletTag_WAR_renderURLbyportlet
    • &p_p_lifecycle=0
    • &p_p_state=normal
    • &p_p_mode=view
    • &p_p_col_id=column-1
    • &p_p_col_count=1
    • &_renderURLbyportletTag_WAR_renderURLbyportlet_param=XXXXXX

    p_p_id 是portlet Id

    p_p_lifecycle是生命周期的当前阶段,定义是:0 – render;1 – action;2 – serve resource;

    p_p_col_id是多列布局中的当前页第几列;

    p_p_col_count是当前页的布局总列数;

    最后一个就是用户定自定url参数了,由jsp传递;

    方式2、PortletURLFactoryUtil 方式

    这种是服务端直接控制,jsp基本不用写什么逻辑,后台控比较喜欢用

    render()

    @Override
    public void render(RenderRequest request, RenderResponse response)
      throws PortletException, IOException {
       _log.info(" This is render method of RenderURLByJavaAPIPortlet");
       String data = request.getParameter("param");
       String data1= ParamUtil.getString(request, "param","");
       
       ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(WebKeys.THEME_DISPLAY);
       PortletURL renderUrl =  PortletURLFactoryUtil.create(request, themeDisplay.getPortletDisplay().getId(), themeDisplay.getPlid(), PortletRequest.RENDER_PHASE);
       renderUrl.setWindowState(LiferayWindowState.NORMAL);
       renderUrl.setPortletMode(LiferayPortletMode.VIEW);
       renderUrl.setParameter("param", "This parameter comes from Render URL generated with Java API");
       request.setAttribute("renderUrlByJavaAPI", renderUrl.toString());
       
      super.render(request, response);
     }

    其中,plid 是page layout id

    在jsp中,只用一行代码:

    <a href="${renderUrlByJavaAPI}">Render Url created by Java API</a>

    还有一种灵活方式,即可以由jsp来直接输出

    <%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>
    <liferay-theme:defineObjects/>
    
    <%
     PortletURL renderUrlFromJSP = renderResponse.createRenderURL();
     renderUrlFromJSP.setParameter("param1", "This portletULR is created with API in JSP");
     renderUrlFromJSP.setWindowState(LiferayWindowState.NORMAL);
     renderUrlFromJSP.setPortletMode(LiferayPortletMode.VIEW);
     
    %>
    <a href="<%=renderUrlFromJSP%>">Render Url created by JSP</a>

    方式3:Liferay Tag (liferay-portlet:renderURL)

     view.jsp
    
    <%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %>
    <liferay-portlet:renderURL var="openPortletURL" copyCurrentRenderParameters="true" portletMode="<%=LiferayPortletMode.VIEW.toString() %>" 
      windowState="<%=LiferayWindowState.NORMAL.toString()%>">
      <liferay-portlet:param name="param" value="This is from Liferay TAG"/>
    </liferay-portlet:renderURL>
     
    <a href="${openPortletURL}">Render Url created by Liferay TAG in JSP</a>

    render() 同第一种写法(Portlet Tag方式)

    方式4:JavaScript (by AUI)

    view.jsp
    
    <%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
    <%@page import="com.liferay.portal.kernel.portlet.LiferayPortletMode"%>
    <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
    <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
    <%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>
     
    <liferay-theme:defineObjects/>
    <portlet:defineObjects />
     
    <a id="renderURLWithJS" href=""> This render URL link is created with Javascript</a>
     
    <aui:script>
    AUI().use('liferay-portlet-url', function(A) {
    var renderUrl1 = Liferay.PortletURL.createRenderURL();
    renderUrl1.setWindowState("<%=LiferayWindowState.NORMAL.toString() %>");
    renderUrl1.setParameter("param","This value comes from Javascript");
    renderUrl1.setPortletMode("<%=LiferayPortletMode.VIEW %>");
    renderUrl1.setPortletId("<%=themeDisplay.getPortletDisplay().getId() %>");
     
    A.one("#renderURLWithJS").set('href',renderUrl1.toString());
    alert("renderUrl1 is ->"+renderUrl1.toString());
    alert(A.one("#renderURLWithJS").attr("href"));
     
    });
     
    </aui:script>
     
    这种比较不常用

    render() 同第一种写法(Portlet Tag方式)

    action 处理

    在涉及页面处理业务逻辑,或其他Portlet交互,或者Form提交Action时,就需要我们定义Action方法,actionURL 就是用来传递Action的id

    它是和jsp一对一对应的,比如jsp中:

    view.jsp
    
    <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
     
    <portlet:defineObjects />
    
    User Name is : <b> ${userName}</b>
    
    <portlet:actionURL name="actionMethod1" var="sampleActionMethodURL">
    </portlet:actionURL>
     
    <form action="${sampleActionMethodURL}" method="post">
        UserName :-<input type="text" name="userName"><br>
        <input type="submit" value="Submit"> 
    </form>
    
     
    <portlet:actionURL name="addName" var="addNameUrl"></portlet:actionURL>
     
    <a href="${addNameUrl}">Add Name</a>

    前提还要定义portlet.xml

    portlet.xml首先要定义
    
    <portlet-name>custom-liferaymvc</portlet-name>
    <display-name>Custom Liferaymvc</display-name>
    <portlet-class>com.companyname.portlet.CustomMVCPortlet</portlet-class>
    <init-param>
      <name>view-jsp</name>
      <value>/jsp/view.jsp</value>
    </init-param>

    编写一个继承MVCPortlet的类

    @ProcessAction(name="addName")
     public void addName(ActionRequest actionRequest,
       ActionResponse actionResponse) throws IOException, PortletException, PortalException, SystemException{
      actionRequest.setAttribute("userName", "Wangxin");
     }
    
    
    @ProcessAction(name="actionMethod1")
    public void sampleActionMethod(ActionRequest request, ActionResponse response)
                    throws IOException, PortletException, PortalException, SystemException{
                _log.info("This is sampleActionMethod ...");
    String userName = ParamUtil.get(actionRequest, "userName", StringPool.BLANK);
      actionRequest.setAttribute("userName", userName);
            }

    如果跟踪一下,会是自动产生以下url:

    http://localhost:8080/web/home?p_auth=e6cvA8tX
    &p_p_id=testactionmethod_WAR_testactionmethodportlet
    &p_p_lifecycle=1
    &p_p_state=normal
    &p_p_mode=view
    &p_p_col_id=column-1
    &_testactionmethod_WAR_testactionmethodportlet_javax.portlet.action=actionMethod1

    另外PortletURLFactoryUtil 是一种后台产生action url的常用方式

    java code:
    
        @Override
        public void render(RenderRequest request, RenderResponse response)
                throws PortletException, IOException {
            
              ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(WebKeys.THEME_DISPLAY);
              PortletURL actionUrl =  PortletURLFactoryUtil.create(request, themeDisplay.getPortletDisplay().getId(), themeDisplay.getPlid(), PortletRequest.ACTION_PHASE);
              actionUrl.setWindowState(LiferayWindowState.NORMAL);
              actionUrl.setPortletMode(LiferayPortletMode.VIEW);
              actionUrl.setParameter("javax.portlet.action", "actionMethodByJavaApi");
              actionUrl.setParameter("sampleParam", "This parameter comes from Action URL generated with Java API");
              request.setAttribute("actionUrlByJavaAPI", actionUrl.toString());
            super.render(request, response);
        }
     
        @ProcessAction(name="actionMethodByJavaApi")
        public void actionMethodByJavaApi(ActionRequest request, ActionResponse response)
                throws IOException, PortletException, PortalException, SystemException{
            String sampleParam = ParamUtil.get(request, "sampleParam", "defaultValue");
            _log.info("Sample Param is ::"+sampleParam);
        }
     
    
    view.jsp
    
    
    <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
    <portlet:defineObjects />
    <a href="${actionUrlByJavaAPI}">Action Url created by Java API in Portlet Class</a>
    <br>

    还有两种方式就不介绍了:
    liferay-portlet:actionURL 和 JavaScript (AUI module – Liferay.PortletURL)

    一个portlet交互的流程图

    resource 处理

    Render & action是最早被定义的规范,见portlet specification 1.0 (JSR-168).
    resource serving 被定义于portlet specification 2.0 (JSR-286)
    一句话概括serve resource做的事情:动态产生输出到客户端,负责向客户端发送动态内容
    比如:下载文件,发送一个excle报表,发送xml、json文件......,这些在Render 周期是无法实现的

    serve resource过程

    这里有比较好的介绍:
    http://www.oracle.com/technetwork/java/jsr286-2-141964.html

    7.0版的Command设计模式

    在7.0版,整个一套处理设计为注解方式了,摒弃了满屏混乱的XML配置,更加清晰直观,代码结构更好

    有三种命令接口:

    • MVCActionCommand
    • MVCRenderCommand
    • MVCResourceCommand

    其中

    BaseMVCActionCommand(class)实现=> MVCActionCommand(interface)

    定制Portlet

    @Component(
        immediate = true,
        property = {
            "com.liferay.portlet.css-class-wrapper=portlet-hello-world",
            "com.liferay.portlet.display-category=category.sample",
            "com.liferay.portlet.icon=/icons/hello_world.png",
            "com.liferay.portlet.preferences-owned-by-group=true",
            "com.liferay.portlet.private-request-attributes=false",
            "com.liferay.portlet.private-session-attributes=false",
            "com.liferay.portlet.remoteable=true",
            "com.liferay.portlet.render-weight=50",
            "com.liferay.portlet.use-default-template=true",
            "javax.portlet.display-name=Hello World",
            "javax.portlet.expiration-cache=0",
            "javax.portlet.init-param.always-display-default-configuration-icons=true",
            "javax.portlet.name=" + HelloWorldPortletKeys.HELLO_WORLD,
            "javax.portlet.resource-bundle=content.Language",
            "javax.portlet.security-role-ref=guest,power-user,user",
            "javax.portlet.supports.mime-type=text/html"
        },
        service = Portlet.class
    )
    public class HelloWorldPortlet extends MVCPortlet {
    ......
    }

    action command

    比如一个action URL用于编辑实体,指向edit_entry.jsp

    <portlet:actionURL name="/blogs/edit_entry" var="editEntryURL" />

    action URL被触发后,匹配的action类将会被处理,可以有两种方式实现action command,一是实现接口MVCActionCommand,二是直接继承BaseMVCActionCommand类,liferay官方是推荐第二种,因为已经实现了很多有用的方法。

    你的XXXXMVCActionCommand 类必需有@Componet注解,是为了OSGI容器反射得到类,例子:

    @Component(
    immediate = true,
    property = {
    "javax.portlet.name=Your_portlet_name",
    "mvc.command.name=/blogs/edit_entry"
    },
    service = MVCActionCommand.class
    )
    public class YourMVCActionCommand extends BaseMVCActionCommand {
    // your action
    }

     比如一个增加实体的方法

    public void addGuestbook(ActionRequest request, ActionResponse response)
            throws PortalException, SystemException {
    
        ServiceContext serviceContext = ServiceContextFactory.getInstance(
            Guestbook.class.getName(), request);
    
        String name = ParamUtil.getString(request, "name");
    
        try {
            _guestbookService.addGuestbook(serviceContext.getUserId(),
                    name, serviceContext);
    
            SessionMessages.add(request, "guestbookAdded");
    
        } catch (Exception e) {
            SessionErrors.add(request, e.getClass().getName());
    
            response.setRenderParameter("mvcPath",
                "/html/guestbook/edit_guestbook.jsp");
        }
    
    }

    MVC render command

    需要定义三件事情:

    • 实现MVCRenderCommand interface;
    • 在视图创建portlet render URL;
    • Component注解定义MVCRenderCommand service;

    一个最简单的render

    @Component(
        immediate = true,
        property = {
           "javax.portlet.name=" + HelloWorldPortletKeys.HELLO_WORLD,
           "javax.portlet.name=" + HelloWorldPortletKeys.HELLO_OTHER_WORLD,
           "mvc.command.name=/hello/edit_entry"
        },
        service = MVCRenderCommand.class
    )
    public class EditEntryMVCRenderCommand implements MVCRenderCommand {
    
    @Override
    public String render(
        RenderRequest renderRequest, RenderResponse renderResponse)
        throws PortletException {
    
        try {
         Blog entry = ActionUtil.getEntry(renderRequest);
        }
        catch (Exception e) {
            if (e instanceof NoSuchEntryException ||
                e instanceof PrincipalException) {
    
                SessionErrors.add(renderRequest, e.getClass());
    
                return "/hello/error.jsp";
            }
            else {
                throw new PortletException(e);
            }
        }
    
        return "/hello/edit_entry.jsp";
      }
    }

    javax.portlet.name被定义了2个,这代表它可以在2个portlet中使用。

    URL:

    <portlet:renderURL var="editEntryURL">
    <portlet:param name="mvcRenderCommandName" value="/hello/edit_entry" />
    <portlet:param name="entryId" value="<%= String.valueOf(entry.getEntryId()) %>" />
    </portlet:renderURL>

    一个较详细的例子:

    @Override
    public void render(RenderRequest renderRequest,
            RenderResponse renderResponse) throws PortletException, IOException {
    
        try {
            ServiceContext serviceContext = ServiceContextFactory.getInstance(
                    Guestbook.class.getName(), renderRequest);
    
            long groupId = serviceContext.getScopeGroupId();
    
            long guestbookId = ParamUtil.getLong(renderRequest, "guestbookId");
    
            List<Guestbook> guestbooks = _guestbookService
                    .getGuestbooks(groupId);
    
            if (guestbooks.size() == 0) {
                Guestbook guestbook = _guestbookService.addGuestbook(
                        serviceContext.getUserId(), "Main", serviceContext);
    
                guestbookId = guestbook.getGuestbookId();
    
            }
    
            if (!(guestbookId > 0)) {
                guestbookId = guestbooks.get(0).getGuestbookId();
            }
    
            renderRequest.setAttribute("guestbookId", guestbookId);
    
        } catch (Exception e) {
    
            throw new PortletException(e);
        }
    
        super.render(renderRequest, renderResponse);
    
    }

    MVC serveResource command

    @Component(
        property = {
            "javax.portlet.name=" + LoginPortletKeys.FAST_LOGIN,
            "javax.portlet.name=" + LoginPortletKeys.LOGIN,
            "mvc.command.name=/login/captcha"
        },
        service = MVCResourceCommand.class
    )
    public class CaptchaMVCResourceCommand implements MVCResourceCommand {
    
        @Override
        public boolean serveResource(
            ResourceRequest resourceRequest, ResourceResponse resourceResponse) {
    
            try {
                CaptchaUtil.serveImage(resourceRequest, resourceResponse);
    
                return false;
            }
            catch (Exception e) {
                _log.error(e, e);
    
                return true;
            }
        }
    
        private static final Log _log = LogFactoryUtil.getLog(
            CaptchaMVCResourceCommand.class);
    }

    目前7.0的文档还太少。

  • 相关阅读:
    SharePoint 2013 配置我的网站 图文引导
    关于SharePoint REST中的授权的研究
    SharePoint重置密码功能Demo
    SharePoint 沙盒解决方案 VS 场解决方案
    移动设备和SharePoint 2013
    win32
    win32
    链表复习-1
    win32
    洛谷基础算法
  • 原文地址:https://www.cnblogs.com/starcrm/p/6029771.html
Copyright © 2020-2023  润新知