Liferay7 BPM门户开发之17: Portlet 生命周期
2016-11-04 12:10
453 查看
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)
定制[b]Portlet类[/b]
@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的文档还太少。
相关文章推荐
- Liferay7 BPM门户开发之28: Portlet文件上传,及实体类同步更新上传
- Liferay7 BPM门户开发之40: Form表单的Action到Render的数据传递
- Liferay7 BPM门户开发之14: 通用流程实现从Servlet到Portlet (Part3)
- Liferay7 BPM门户开发之20: 理解Asset Framework
- Liferay7 BPM门户开发之19: 理解Service Builder体系
- Liferay7 BPM门户开发之29: 核心kernel.util包下面的通用帮助类ParamUtil、GetterUtil使用
- Liferay7 BPM门户开发之12:acitiviti和liferay用户权限体系集成
- Liferay7 BPM门户开发之22: Liferay7模型监听器(Model Listeners)
- Liferay7 BPM门户开发之36: 使用Portlet filters过滤器做切面AOP
- Liferay7 BPM门户开发之42: Liferay核心JSP定制扩展
- Liferay7 BPM门户开发之13: 通用流程实现从Servlet到Portlet (Part2)
- Liferay7 BPM门户开发之7: Activiti中的重要概念和主要数据库结构
- Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发
- Liferay7 BPM门户开发之45: 集成Activiti文件上传部署流程BPMN模型
- Liferay7 BPM门户开发之16: Liferay中用户\站点\组织架构\角色\用户组以及关联关系
- Liferay7 BPM门户开发之4: Activiti事件处理和监听Event handlers
- Liferay7 BPM门户开发之27: MVC Portlet插件工程开发
- Liferay7 BPM门户开发之21: 理解消息总线(Message Bus)体系
- Liferay7 BPM门户开发之26: 集成Activiti到Liferay7
- Liferay7 BPM门户开发之33: Portlet之间通信的3种方式(session、IPC Render Parameter、IPC Event、Cookies)