Portal之Portlet如何与容器交互
转载:
Portlet运行在Portlet容器中,由Portlet容器在特定的时间点调用Portlet的方法,那么Portlet应该有哪些方法,这些方法在什么时候被Portlet容器调用呢?本文对此做一个简单的介绍.
Portlet必须实现javax.portlet.Portlet接口,不过在实际的开发中,一般都是实现GenericPortlet,因为GenericPortlet已经提供了一些默认的实现方法.
Portlet和容器的关系如下图所示:Portlet运行在Portlet容器中,负责响应portlet容器的request,对不同的request调用不同的方法,然后生成"html片段",portal server负责将不同的portlet生成的"html片段"组合成一个完整的html页面.(图片来源于Portlet in Action)
和普通http request不同的是,发送到portlet的request的类型不一样,下面是request类型的介绍.
render:当有需要显示内容的时候,容器会向portlet发render request.
action:当有需要portlet完成某个特定业务(比如更新数据库)的时候,容器会向portlet发action request,每一个action request伴随着一个render request.
resource:当有需要resource请求的时候,容器会向portlet发resource request.
event:当有event请求的时候,容器会向portlet发eventrequest.
request 类型有render和action之分的原因是,对于Portal页面上的每一个portlet,其中某一个portlet发生内容发更新后,所有的其他portlet也都需要重新"render",调用响应render request的方法.因为其他portlet用来做显示的数据源有可能被刚刚做更新的那个portlet更新了,但是其他portlet的响应action request的方法是不用被调用的.
下面通过一个实例来说明render,action request由Portlet的那个方法来响应.
1 public class LifeCyclePortlet extends GenericPortlet { 2 private String defaultVal; 3 public void init() { 4 defaultVal = getPortletConfig().getInitParameter("defaultVal"); 5 System.out.println("******** LifeCyclePortlet init ********"); 6 } 7 public void init(PortletConfig config) throws PortletException { 8 System.out.println("******** LifeCyclePortlet init(PortletConfig config) ********"); 9 super.init(config); 10 } 11 protected void doEdit(RenderRequest request, RenderResponse response) throws PortletException, 12 IOException { 13 getPortletContext().getRequestDispatcher("/WEB-INF/jsp/preferences.jsp").include(request, 14 response); 15 System.out.println("******** LifeCyclePortlet doEdit ********"); 16 } 17 protected void doHelp(RenderRequest request, RenderResponse response) throws PortletException, 18 IOException { 19 getPortletContext().getRequestDispatcher("/WEB-INF/jsp/help.jsp") 20 .include(request, response); 21 System.out.println("******** LifeCyclePortlet doHelp ********"); 22 } 23 protected void doView(RenderRequest request, RenderResponse response) throws PortletException, 24 IOException { 25 System.out.println("LifeCyclePortlet doView " + request.getParameter("reset")); 26 27 if ("success".equalsIgnoreCase((String) request.getAttribute("actionStatus"))) { 28 PortletURL homeUrl = response.createRenderURL(); 29 request.setAttribute("homeUrl", homeUrl); 30 getPortletContext().getRequestDispatcher("/WEB-INF/jsp/success.jsp").include(request, 31 response); 32 return; 33 } 34 PortletURL actionUrl = response.createActionURL(); 35 PortletURL resetRenderUrl = response.createRenderURL(); 36 37 request.setAttribute("actionUrl", actionUrl); 38 request.setAttribute("resetRenderUrl", resetRenderUrl); 39 40 if (!"error".equalsIgnoreCase((String) request.getAttribute("actionStatus"))) { 41 if ("yes".equalsIgnoreCase(request.getParameter("reset"))) { 42 defaultVal = ""; 43 } else { 44 defaultVal = getPortletConfig().getInitParameter("defaultVal"); 45 } 46 request.setAttribute("someVal", defaultVal); 47 } 48 getPortletContext().getRequestDispatcher("/WEB-INF/jsp/inputForm.jsp").include(request, 49 response); 50 } 51 52 public void processAction(ActionRequest request, ActionResponse response) 53 throws PortletException, IOException { 54 System.out.println("******** LifeCyclePortlet processAction ********"); 55 56 String val = request.getParameter("someVal"); 57 request.setAttribute("val", val); 58 59 if (val == null || val.trim().equals("")) { 60 ResourceBundle bundle = getPortletConfig().getResourceBundle(request.getLocale()); 61 request.setAttribute("errorMsg", bundle.getString("val.errorMsg.missing")); 62 request.setAttribute("actionStatus", "error"); 63 } else { 64 request.setAttribute("actionStatus", "success"); 65 } 66 } 67 public void render(RenderRequest request, RenderResponse response) throws PortletException, 68 IOException { 69 System.out 70 .println("LifeCyclePortlet render(RenderRequest request, RenderResponse response) " 71 + request.getParameter("reset")); 72 super.render(request, response); 73 }
#对上面代码的一个简单解释,以portlet继承了GenericPortlet为例,
当容器初始话的时候,调用init(PortletConfig config)和init()方法.
当有render request到达portlet的时候,render(RenderRequest request, RenderResponse response)方法被调用,根据render的mode不同,会调用doView,doEdit或者doHelp方法.render mode有view,edit,help三种.如下面LifeRay截图,显示A:portlet的"主页面"就是view mode,需要配置preferences(B)为edit mode,需要Help(C)为help mode.
当有action request的时候,调用processAction方法.也可通过Java注解@RenderMode(name = "EDIT"), @ProcessAction(name = "xxxAction")的方式来告诉容器render和action应该有那个方法来响应,这里不做介绍.
如果直接实现Portlet接口的话,只有init(PortletConfig config),render(RenderRequest request, RenderResponse response),processAction(ActionRequest request, ActionResponse response),destroy()四个方法,本例中实现的是GenericPortlet,它对Portlet的方法提供了默认的实现,如render方法会根据render request的mode决定是调用doView,doEdit还是doHelp方法等等.
下面是Portlet用到的显示内容的jsp:
1 help.jsp: 2 <table width="200px"> 3 <tr> 4 <td> 5 This is the help page 6 </td> 7 </tr> 8 </table> 9 preference.jsp: 10 <table width="200px"> 11 <tr> 12 <td> 13 This is the preference page for editing options 14 </td> 15 </tr> 16 </table> 17 inputForm.jsp: 18 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 19 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> 20 <%@ page contentType="text/html" isELIgnored="false" %> 21 <fmt:setBundle basename="content.Language-ext"/> 22 <form action="<c:out value='${requestScope.actionUrl}'/>" method="POST"> 23 <table width="200px"> 24 <tr> 25 <td colspan="2"> 26 <font color="#FF0000"><c:out 27 value="${requestScope.errorMsg}"/></font> 28 </td> 29 </tr> 30 31 <tr> 32 <td><font color="#FF0000"><b>*</b></font> <fmt:message key="label.val"/></td> 33 <td><input type="text" name="someVal" value="${requestScope.someVal}"></input></td> 34 </tr> 35 <tr> 36 <td> </td> 37 </tr> 38 <tr align="center"> 39 <td colspan="2"> 40 <input type="submit"/> 41 42 <a href="<c:out value='${requestScope.resetRenderUrl}'/>&reset=yes"> 43 <b><fmt:message key="label.reset"/></b> 44 </a> 45 </td> 46 </tr> 47 </table> 48 </form> 49 success.jsp: 50 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 51 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> 52 <%@ page contentType="text/html" isELIgnored="false" %> 53 <fmt:setBundle basename="content.Language-ext"/> 54 <table> 55 <tr> 56 <td colspan="2">action processed successfully</td> 57 </tr> 58 <tr> 59 <td align="right"><b>inputed value is </b></td> 60 <td><c:out value="${requestScope.val}"/></td> 61 </tr> 62 <tr> 63 <td colspan="2"> 64 <a href="<c:out value='${requestScope.homeUrl}'/>"> 65 <b><fmt:message key="label.home"/></b> 66 </a> 67 </td> 68 </tr> 69 </table>
#help.jsp是在上图中,当点击 "Help"的时候用来显示信息.对应doHelp方法中的Portlet中getPortletContext().getRequestDispatcher("/WEB-INF/jsp/help.jsp")代码.
#preferences.jsp是在上图中,当点击 "Preferences.jsp"的时候用来显示信息.
#inputForm.jsp用来做为Portlet的"主页面",portlet第一次显示的时候会由这个jsp来显示内容.
<form action="<c:out value='${requestScope.actionUrl}'/>" method="POST">的作用指定了form提交时候的action request,这个action url是portlet的java代码PortletURL actionUrl = response.createActionURL();生成的.提交的时候会调用portlet的processAction方法.Portlet URL和普通HTTP URL不同的地方在于,它在普通HTTP URL的基础上加上了一个参数来标识要访问的是哪一个个Portlet,如下面的p_p_id=HelloWorldPortlet_WAR_helloWorld_INSTANCE_MrP9,加上了一个参数用来标识request的类型,如下面的p_p_mode=view,这个URL有Portlet容器解析,就知道了用哪一个portlet的哪一个方法来为一个特定的URL服务了.注意,在不同的Portlet容器中,Portlet URL的参数和值是不同的,下面是LifeRay中的例子.
1 http://localhost:8080/web/guest/home?p_p_id=HelloWorldPortlet_WAR_h 2 elloWorld_INSTANCE_MrP9&p_p_lifecycle=1&p_p_state=normal&p_p_mo 3 de=view&p_p_col_id=column- 4 1&p_p_col_count=1&_HelloWorldPortlet_WAR_helloWorld_INSTANCE_MrP9_action=someAction
整个项目的structure如下:完整的代码可以从http://download.csdn.net/detail/kkdelta/4084606下载.