JSR 286 Portlet 的新特性,第 3 部分: Portlet 过滤器和 Portlet 窗口


  在 本系列 的 第 1 部分 简要回顾了JSR 168 Portlet,并对 JSR 286 Portlet 的新增特性做了详细的介绍,第 2 部分 和第 3 部分将通过在 Apache Pluto 2.0 平台上开发和部署 Portlet 应用程序, 向读者介绍 JSR 286 Portlet 新特性的使用方法。本文将介绍 JSR 286 Portlet 的 Portlet 过滤器和 Portlet 窗口应用程序开发。


  关于本系列


   本系列 专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学完本系列后,您将了解到相对于 JSR 168 Portlet,JSR 286 Portlet 究竟提供了哪些增强功能, 以及这些新增特性在实际开发中的应用。


  本系列的 第 1 部分 简单回顾了 JSR 168 Portlet, 并列出了 JSR 286 Portlet 的新增内容。第 2 部分 和第 3 部分将通过在 Apache Pluto 2.0 平台上开发和部署 Portlet 应用程序,向读者介绍 JSR 286 Portlet 新特性的使用方法。


  关于本文


  本文承接 第 2 部分,继续介绍 JSR 286 Portlet 的 Portlet 过滤器和 Portlet 窗口应用程序开发。阅读本文之前,您应当对 JSR 168 Portlet 有所了解,并阅读了本系列的 第 1 部分 和 第 2 部分。


  Portlet 过滤器


  通过 第 1 部分 的介绍,我们知道 Portlet 过滤器分为:


  Action 过滤器


  Render 过滤器


  Resource 过滤器


  Event 过滤器


   我们将首先对这四种 Portlet 过滤器的开发使用流程分别单独进行介绍,然后将这四种 Portlet 过滤器综合起来进行更进一步的开发,最后通过和 Servlet 过滤器的结合使用,使读者明白 Portlet 过滤器和 Servlet 过滤器的关系和区别。


  Action 过滤器


  新建 Java 类 TestActionFilter:


  清单 1. TestActionFilter.java 文件


package com.ibm.samples.jsr286.filters; 
 
import ... 
 
public class TestActionFilter implements ActionFilter { 
 
  private static Log log = LogFactory.getLog(TestActionFilter.class); 
 
  private FilterConfig filterConfig; 
 
  public void init(FilterConfig filterConfig) throws PortletException { 
    log.info("action filter [" + filterConfig.getFilterName() 
        + "] is initialized."); 
    this.filterConfig = filterConfig; 
  } 
 
  public void destroy() { 
    log.info("action filter [" + filterConfig.getFilterName() 
        + "] is destroyed."); 
  } 
 
  public void doFilter(ActionRequest actionRequest, 
      ActionResponse actionResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("action filter [" + filterConfig.getFilterName() 
        + "] is called."); 
    filterChain.doFilter(actionRequest, actionResponse); 
  } 
}  


   这个程序的主要作用就是在 Action Filter 初始化、过滤器调用,销毁的时候分别打印相应的信息。清单 1 中 filterChain.doFilter(actionRequest, actionResponse) 需要读者特别注意,这行代码保证了过滤器链的传递,删去这行代码,则过滤器链将在该过滤器执行结束后终结。


  编辑 portlet.xml 文件,加入如下片断:


  清单 2. Action 过滤器定义


<portlet-app ...> 
  ... 
  <filter> 
    <display-name>TestActionFilter</display-name> 
    <filter-name>TestActionFilter</filter-name> 
    <filter-class>com.ibm.samples.jsr286.filters.TestActionFilter</filter-class> 
    <lifecycle>ACTION_PHASE</lifecycle> 
  </filter> 
  ... 
</portlet-app> 

 


  定义 Action 过滤器映射,可以影射到具体某个 Portlet, 或者根据模式匹配到一组 Portlet:


  清单 3. Action 过滤器映射


... 
<filter-mapping> 
  <filter-name>TestActionFilter</filter-name> 
  <portlet-name>*</portlet-name> 
</filter-mapping> 
... 

 


  在清单 3 的定义中,我们声明 TestActionFilter 对所有 Portlet 的 processAction 调用进行拦截。


   重启 Web 应用程序并将 TestPortlet 部署到 "Test JSR 286 Portlet Page" 页面, 并将该页面其它所有 Portlet 移除,输入数据,点击 Submit 按钮触发 processAction 调用,Eclipse Console 出现如下输出:


  清单 4. Action 过滤器调用结果


... 
2008-3-16 22:35:20 com.ibm.samples.jsr286.filters.TestActionFilter init 
信息: action filter [TestActionFilter] is initialized. 
2008-3-16 22:35:24 com.ibm.samples.jsr286.filters.TestActionFilter doFilter 
信息: action filter [TestActionFilter] is called. 
2008-3-16 22:35:24 com.ibm.samples.jsr286.filters.TestActionFilter destroy 
信息: action filter [TestActionFilter] is destroyed. 
... 

 

  从上面的信息可以看出,对于 Portlet 的每次 processAction 调用,Action Filter 都要经历一个初始化、过滤方法 doFilter 调用、销毁的全过程。读者可以多次实验证实这一点。


  Render 过滤器


  新建 Java 类 TestRenderFilter:


  清单 5. TestRenderFilter.java 文件


package com.ibm.samples.jsr286.filters; 
 
import ... 
 
public class TestRenderFilter implements RenderFilter { 
 
  private static Log log = LogFactory.getLog(TestRenderFilter.class); 
 
  private FilterConfig filterConfig; 
 
  public void init(FilterConfig filterConfig) throws PortletException { 
    log.info("render filter [" + filterConfig.getFilterName() 
        + "] is initialized."); 
    this.filterConfig = filterConfig; 
  } 
 
  public void destroy() { 
    log.info("render filter [" + filterConfig.getFilterName() 
        + "] is destroyed."); 
  } 
 
  public void doFilter(RenderRequest renderRequest, 
      RenderResponse renderResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("render filter [" + filterConfig.getFilterName() 
        + "] is called."); 
    filterChain.doFilter(renderRequest, renderResponse); 
  } 
} 

 

  这个程序的主要作用就是在 Render Filter 初始化、过滤器调用,销毁的时候分别打印相应的信息。和 Action Filter 一样,读者同样需要注意过滤链传递的问题。


  编辑 portlet.xml 文件,加入如下片断:


  清单 6. Render 过滤器定义


<portlet-app ...> 
  ... 
  <filter> 
    <display-name>TestRenderFilter</display-name> 
    <filter-name>TestRenderFilter</filter-name> 
    <filter-class>com.ibm.samples.jsr286.filters.TestRenderFilter</filter-class> 
    <lifecycle>RENDER_PHASE</lifecycle> 
  </filter> 
  ... 
</portlet-app> 

 


  定义 Render 过滤器映射,可以影射到具体某个 Portlet, 或者根据模式匹配到一组 Portlet:


  清单 7. Render 过滤器映射


... 
<filter-mapping> 
  <filter-name>TestRenderFilter</filter-name> 
  <portlet-name>*</portlet-name> 
</filter-mapping> 
... 

 


  在清单 7 的定义中,我们声明 TestRenderFilter 对所有 Portlet 的 render 调用进行拦截。


  重启 Web 应用程序并将多个 Portlet 部署到 "Test JSR 286 Portlet Page"页面, 访问该页面,Eclipse Console 出现多个如下输出:


  清单 8. Render 过滤器调用结果


... 
2008-3-16 22:53:11 com.ibm.samples.jsr286.filters.TestRenderFilter init 
信息: render filter [TestRenderFilter] is initialized. 
2008-3-16 22:53:11 com.ibm.samples.jsr286.filters.TestRenderFilter doFilter 
信息: render filter [TestRenderFilter] is called. 
2008-3-16 22:53:11 com.ibm.samples.jsr286.filters.TestRenderFilter destroy 
信息: render filter [TestRenderFilter] is destroyed. 
... 

 

  从上面的信息可以看出,对于 Portlet 的每次 render 调用,Render Filter 都要经历一个初始化、过滤方法 doFilter 调用、销毁的全过程。以上信息出现的次数与部署到页面上的 Portlet 个数相同,意味着 Portlet 过滤器的拦截是分别针对每个 Portlet 进行的。


  Resource 过滤器


  新建 Java 类 TestResourceFilter


  清单 9. TestResourceFilter.java 文件


package com.ibm.samples.jsr286.filters; 
 
import ... 
 
public class TestResourceFilter implements ResourceFilter { 
 
  private static Log log = LogFactory.getLog(TestResourceFilter.class); 
 
  private FilterConfig filterConfig; 
 
  public void init(FilterConfig filterConfig) throws PortletException { 
    log.info("resource filter [" + filterConfig.getFilterName() 
        + "] is initialized."); 
    this.filterConfig = filterConfig; 
  } 
 
  public void destroy() { 
    log.info("resource filter [" + filterConfig.getFilterName() 
        + "] is destroyed."); 
  } 
 
  public void doFilter(ResourceRequest resourceRequest, 
      ResourceResponse resourceResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("resource filter [" + filterConfig.getFilterName() 
        + "] is called."); 
    filterChain.doFilter(resourceRequest, resourceResponse); 
  } 
} 

 

  这个程序的主要作用就是在 Resource Filter 初始化、过滤器调用,销毁的时候分别打印相应的信息,读者同样需要注意过滤链传递的问题。


  编辑 portlet.xml 文件,加入如下片断:


  清单 10. Resource 过滤器定义


<portlet-app ...> 
  ... 
  <filter> 
    <display-name>TestResourceFilter</display-name> 
    <filter-name>TestResourceFilter</filter-name> 
    <filter-class>com.ibm.samples.jsr286.filters.TestResourceFilter</filter-class> 
    <lifecycle>RESOURCE_PHASE</lifecycle> 
  </filter> 
  ... 
</portlet-app> 

 


  定义 Resource 过滤器映射,可以影射到具体某个 Portlet, 或者根据模式匹配到一组 Portlet:


  清单 11. Resource 过滤器映射


... 
<filter-mapping> 
  <filter-name>TestResourceFilter</filter-name> 
  <portlet-name>*</portlet-name> 
</filter-mapping> 
... 

 


  在清单 11 的定义中,我们声明 TestResourceFilter 对所有 Portlet 的 serveResource 调用进行拦截。


   重启 Web 应用程序并将 TestPortlet 部署到 "Test JSR 286 Portlet Page"页面, 访问该页面, 点击超链接“Click me to request Resource URL”请求资源,Eclipse Console 出现如下输出:


  清单 12. Resource 过滤器调用结果


... 
2008-3-17 13:21:03 com.ibm.samples.jsr286.filters.TestResourceFilter init 
信息: resource filter [TestResourceFilter] is initialized. 
2008-3-17 13:21:03 com.ibm.samples.jsr286.filters.TestResourceFilter doFilter 
信息: resource filter [TestResourceFilter] is called. 
2008-3-17 13:21:05 com.ibm.samples.jsr286.filters.TestResourceFilter destroy 
信息: resource filter [TestResourceFilter] is destroyed. 
... 

 

  从上面的信息可以看出,对于 Portlet 的每次 serveResource 调用,Resource Filter 都要经历一个初始化、过滤方法 doFilter 调用、销毁的全过程。


  Event 过滤器


  新建 Java 类 TestEventFilter


  清单 13. TestEventFilter.java 文件


package com.ibm.samples.jsr286.filters; 
 
import ... 
 
public class TestEventFilter implements EventFilter { 
 
  private static Log log = LogFactory.getLog(TestEventFilter.class); 
 
  private FilterConfig filterConfig; 
 
  public void init(FilterConfig filterConfig) throws PortletException { 
    log.info("event filter [" + filterConfig.getFilterName() 
        + "] is initialized."); 
    this.filterConfig = filterConfig; 
  } 
 
  public void destroy() { 
    log.info("event filter [" + filterConfig.getFilterName() 
        + "] is destroyed."); 
  } 
 
  public void doFilter(EventRequest eventRequest, 
      EventResponse eventResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("event filter [" + filterConfig.getFilterName() 
        + "] is called."); 
    Event event = eventRequest.getEvent(); 
    log.info("event name: " + event.getName()); 
    log.info("event qname: " + event.getQName()); 
    log.info("event value: " + event.getValue().toString()); 
    filterChain.doFilter(eventRequest, eventResponse); 
  } 
 
}

 

  这个程序的主要作用就是在 Event Filter 初始化、销毁的时候分别打印相应的信息,在 doFilter 方法中,截获事件的名称、QName 和 事件值,读者同样需要注意过滤链传递的问题。


  编辑 portlet.xml 文件,加入如下片断:


  清单 14. Event 过滤器定义


<portlet-app ...> 
  ... 
  <filter> 
    <display-name>TestEventFilter</display-name> 
    <filter-name>TestEventFilter</filter-name> 
    <filter-class>com.ibm.samples.jsr286.filters.TestEventFilter</filter-class> 
    <lifecycle>EVENT_PHASE</lifecycle> 
  </filter> 
  ... 
</portlet-app> 

 


  定义 Event 过滤器映射,可以影射到具体某个 Portlet, 或者根据模式匹配到一组 Portlet:


  清单 15. Event 过滤器映射


... 
<filter-mapping> 
  <filter-name>TestEventFilter</filter-name> 
  <portlet-name>*</portlet-name> 
</filter-mapping> 
... 

 


  在清单 15 的定义中,我们声明 TestEventFilter 对所有 Portlet 的 processEvent 调用进行拦截。


   重启 Web 应用程序并将 TestSimpleEventSenderPortlet、TestSimpleEventReceiverPortlet、 TestComplexEventSenderPortlet、TestComplexEventReceiverPortlet 部署到 "Test JSR 286 Portlet Page" 页面, 访问该页面,点击 TestSimpleEventSenderPortlet 按钮,Eclipse Console 出现如下输出:


  清单 16. Event 过滤器调用结果


... 
2008-3-17 19:51:35 com.ibm.samples.jsr286.filters.TestEventFilter init 
信息: event filter [TestEventFilter] is initialized. 
2008-3-17 19:51:35 com.ibm.samples.jsr286.filters.TestEventFilter doFilter 
信息: event filter [TestEventFilter] is called. 
2008-3-17 19:51:35 com.ibm.samples.jsr286.filters.TestEventFilter doFilter 
信息: event name: simple-event 
2008-3-17 19:51:35 com.ibm.samples.jsr286.filters.TestEventFilter doFilter 
信息: event qname: {http://cn.ibm.com/}simple-event 
2008-3-17 19:51:35 com.ibm.samples.jsr286.filters.TestEventFilter doFilter 
信息: event value: simple-event is sent by TestSimpleEventSenderPortlet 
2008-3-17 19:51:35 com.ibm.samples.jsr286.filters.TestEventFilter destroy 
信息: event filter [TestEventFilter] is destroyed. 
... 

 


  从上面的信息可以看出,对于 Portlet 的每次发送事件行为,Event Filter 都要经历一个初始化、过滤方法 doFilter 调用、销毁的全过程。从清单 16 也可以看到过滤器捕获到的事件信息。


  发送复杂事件的过滤器结果捕获读者可以自行测试。


  综合使用 Portlet 过滤器


  Portlet 的四种过滤器可以集成到一个类中去实现,只要该类实现了上述四个接口即可。以下为类 TestAllPhaseFilter 的类图:


  图 1. TestAllPhaseFilter 的继承关系




 

  TestAllPhaseFilter 实现代码如清单 17 所示:


  清单 17. TestAllPhaseFilter.java 文件


package com.ibm.samples.jsr286.filters; 
 
import ... 
 
public class TestAllPhaseFilter implements ActionFilter, RenderFilter, 
    ResourceFilter, EventFilter { 
 
  private static Log log = LogFactory.getLog(TestAllPhaseFilter.class); 
 
  private FilterConfig filterConfig; 
 
  public void init(FilterConfig filterConfig) throws PortletException { 
    log.info("filter [" + filterConfig.getFilterName() 
        + "] is initialized."); 
    this.filterConfig = filterConfig; 
  } 
 
  public void destroy() { 
    log.info("filter [" + filterConfig.getFilterName() + "] is destroyed."); 
  } 
 
  public void doFilter(ActionRequest actionRequest, 
      ActionResponse actionResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("filter [" + filterConfig.getFilterName() 
        + "] for action is called."); 
    filterChain.doFilter(actionRequest, actionResponse); 
  } 
 
  public void doFilter(RenderRequest renderRequest, 
      RenderResponse renderResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("filter [" + filterConfig.getFilterName() 
        + "] for render is called."); 
    PrintWriter out = renderResponse.getWriter(); 
    out.println( 
    "<font color=red>Portlet filter test for writing to output stream.</font>"); 
    filterChain.doFilter(renderRequest, renderResponse); 
  } 
 
  public void doFilter(ResourceRequest resourceRequest, 
      ResourceResponse resourceResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("filter [" + filterConfig.getFilterName() 
        + "] for resouce is called."); 
    log.info("Resource ID:" + resourceRequest.getResourceID()); 
    PrintWriter out = resourceResponse.getWriter(); 
    out.println( 
    "<font color=red>Portlet filter test for writing to output stream.</font>"); 
    filterChain.doFilter(resourceRequest, resourceResponse); 
  } 
 
  public void doFilter(EventRequest eventRequest, 
      EventResponse eventResponse, FilterChain filterChain) 
      throws IOException, PortletException { 
    log.info("filter [" + filterConfig.getFilterName() 
        + "] for event is called."); 
    Event event = eventRequest.getEvent(); 
    log.info("event name: " + event.getName()); 
    log.info("event qname: " + event.getQName()); 
    log.info("event value: " + event.getValue().toString()); 
    filterChain.doFilter(eventRequest, eventResponse); 
  } 
} 

 

 

  编辑 portlet.xml 文件,加入如下片断:


  清单 18. TestAllPhaseFilter的配置


... 
  <filter> 
    <display-name>TestAllPhaseFilter</display-name> 
    <filter-name>TestAllPhaseFilter</filter-name> 
    <filter-class>com.ibm.samples.jsr286.filters.TestAllPhaseFilter</filter-class> 
    <lifecycle>ACTION_PHASE</lifecycle> 
    <lifecycle>RENDER_PHASE</lifecycle> 
    <lifecycle>RESOURCE_PHASE</lifecycle> 
    <lifecycle>EVENT_PHASE</lifecycle> 
  </filter> 
... 
  <filter-mapping> 
    <filter-name>TestAllPhaseFilter</filter-name> 
    <portlet-name>*</portlet-name> 
  </filter-mapping> 
...  

 

 


   这部分声明代表该过滤器支持 portlet 四个阶段 action、render、resource, event 的过滤拦截,且对所有 portlet 均起作用。具体执行结果请读者自行验证。读者可以从中体会到 portlet 过滤器的拦截和输出流中写入数据的功能。


  同 Servlet 过滤器一样,Portlet 过滤器同样存在过滤器链,但这个过滤器链是特定于 Portlet 的某个阶段的,如图 2 所示:


  图 2. Portlet 过滤器链




 

  其中图 2 中的两个 Portlet 过滤器组成了过滤器链( Portlet Filter Chain),Portlet 请求先流经 Portlet 过滤器链,最后到达 Portlet, Portlet 响应同样也流经 Portlet 过滤器链到达 Portal 页面聚集内容显示。


  TestAllPhaseFilter 支持 Portlet 四个阶段的响应,在 Portlet 特定的阶段,可以分别和 1 - 4 节中的 Portlet 过滤器形成过滤器链,读者可以从日志输出的 Porltet 过滤器调用次序证明这一点。


  和 Servlet 过滤器的结合


   从本系列 第 1 部分 的图 3 所示,我们已经得知,Servlet 过滤器是一个门户级过滤器,它是接收和修改客户端请求的第一个组件,同时也是修改对客户端的响应的最后一个组件。Servlet 过滤器比 Portlet 过滤器的优先级别要高,容器将首先进行 Servlet 过滤,其次是 Portlet 过滤。


  为了在 Portal 容器中测试 Servlet 过滤器,我们最好能自己将 Portal 容器构建起来。幸运的是,Apache Pluto 使用 Servlet 实现了轻量级的 Portal 容器,我们很容易就可以做到这一点。


  使用 Pluto 构建自己的门户


  将 ${TOMCAT_HOME}\PlutoDomain\ 下的 pluto-portal-2.0.0-SNAPSHOT.war 解压缩。目录结构如图 3 所示:


  图 3. pluto-portal-2.0.0-SNAPSHOT 目录结构




 

  在 Eclipse 新建动态 web 项目 myportal, 运行时选择 Tomcat 6,Servlet 版本选择 2.3。


  将 jsr286portles 项目中 WebContent\META-INF\ 目录下的 context.xml 文件复制到 myportal 项目的 WebContent\META-INF\ 目录下。


  将 pluto-portal-2.0.0-SNAPSHOT\ 目录下的以下目录和文件拷贝到 myportal 项目的 WebContent\ 目录下。


  images 目录


  pluto.css 文件


  pluto.js 文件


  portlet-spec-1.0.css 文件


  将 pluto-portal-2.0.0-SNAPSHOT\WEB-INF\ 目录下的以下目录和文件拷贝到 myportal 项目的 WebContent\WEB-INF 目录下。


  lib 目录


  tld 目录


  themes 目录


  pluto-portal-driver-services-config.xml 文件


  将 pluto-portal-2.0.0-SNAPSHOT\WEB-INF\classes\ToolTips.properties 文件复制到 myportal 项目的 src 目录下。


  编辑 myportal 项目的 web.xml 文件,内容如清单 19 所示:


  清单 19. web.xml 文件

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
  "http://java.sun.com/dtd/web-app_2_3.dtd"> 
<web-app> 
  <display-name>myportal</display-name> 
 
  <context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value> 
      /WEB-INF/pluto-portal-driver-services-config.xml 
    </param-value> 
  </context-param> 
 
  <listener> 
    <listener-class> 
      org.springframework.web.context.ContextLoaderListener 
    </listener-class> 
  </listener> 
 
  <listener> 
    <listener-class> 
      org.apache.pluto.driver.PortalStartupListener 
    </listener-class> 
  </listener> 
 
  <servlet> 
    <servlet-name>plutoPortalDriver</servlet-name> 
    <servlet-class> 
      org.apache.pluto.driver.PortalDriverServlet 
    </servlet-class> 
  </servlet> 
 
  <servlet-mapping> 
    <servlet-name>plutoPortalDriver</servlet-name> 
    <url-pattern>/portal/*</url-pattern> 
  </servlet-mapping> 
 
  <taglib> 
    <taglib-uri>http://portals.apache.org/pluto</taglib-uri> 
    <taglib-location>/WEB-INF/tld/pluto.tld</taglib-location> 
  </taglib> 
</web-app> 

 

  在 myportal 项目的 WEB-INF 目录下新建 pluto-portal-driver-config.xml 文件,该文件为 pluto portal 容器的门户配置文件。内容如清单 20 所示:


  清单 20. pluto-portal-driver-config.xml 文件


<?xml version="1.0" encoding="UTF-8"?> 
<pluto-portal-driver> 
 
  <portal-name>pluto-portal-driver</portal-name> 
  <portal-version>2.0.0-SNAPSHOT</portal-version> 
  <container-name>Pluto Portal Driver</container-name> 
 
  <supports> 
    <portlet-mode>view</portlet-mode> 
    <portlet-mode>edit</portlet-mode> 
    <portlet-mode>help</portlet-mode> 
    <portlet-mode>config</portlet-mode> 
 
    <window-state>normal</window-state> 
    <window-state>maximized</window-state> 
    <window-state>minimized</window-state> 
  </supports> 
 
  <render-config default="JSR 286 test page"> 
    <page name="JSR 286 test page" 
      uri="/WEB-INF/themes/pluto-default-theme.jsp"> 
      <portlet context="/jsr286portlets" 
        name="TestPortlet" /> 
      <portlet context="/jsr286portlets" 
        name="TestSimpleEventSenderPortlet"/> 
      <portlet context="/jsr286portlets" 
        name="TestSimpleEventReceiverPortlet"/> 
      <portlet context="/jsr286portlets" 
        name="TestComplexEventSenderPortlet"/> 
      <portlet context="/jsr286portlets" 
        name="TestComplexEventReceiverPortlet"/> 
      <portlet context="/jsr286portlets" 
        name="TestPublicRenderParameterPortlet"/> 
    </page> 
  </render-config> 
 
</pluto-portal-driver> 

 

  在 Eclipse 的 Servers 视图中将 myportal 项目添加到 tomcat 服务器中,启动 tomcat。 使用浏览器访问 http://localhost:8080/myportal/portal/,呈现一个包含我们之前生成的所有 portlet 的一个 portal 页面,如图 4 所示:


  图 4. 自行构建的 portal 容器 myportal




 

  

      创建 Servlet 过滤器


  在 myportal 项目中新建 Java 类 TestServletFilter, 如清单 21 所示:


  清单 21. pluto-portal-driver-config.xml 文件


package com.ibm.samples.servlet.filters; 
 
import ... 
 
public class TestServletFilter implements Filter { 
 
  private static Log log = LogFactory.getLog(TestServletFilter.class); 
 
  private FilterConfig filterConfig; 
 
  public void init(FilterConfig filterConfig) throws ServletException { 
    log.info("servlet filter [" + filterConfig.getFilterName() 
        + "] is initialized."); 
    this.filterConfig = filterConfig; 
  } 
 
  public void destroy() { 
    log.info("servlet filter [" + filterConfig.getFilterName() 
        + "] is destroyed."); 
  } 
 
  public void doFilter(ServletRequest servletRequest, 
      ServletResponse servletResponse, FilterChain filterChain) 
      throws IOException, ServletException { 
    log.info("serlvet filter [" + filterConfig.getFilterName() 
        + "] is called."); 
    filterChain.doFilter(servletRequest, servletResponse); 
  } 
 
} 

 

  配置 Servlet 过滤器


  编辑 myportal 项目的 web.xml 文件,增加如清单 22 的配置内容:


  清单 22. Servlet 过滤器配置片断


  ... 
  <filter> 
    <filter-name>TestServletFilter1</filter-name> 
    <filter-class> 
      com.ibm.samples.servlet.filters.TestServletFilter 
    </filter-class> 
  </filter> 
 
  <filter> 
    <filter-name>TestServletFilter2</filter-name> 
    <filter-class> 
      com.ibm.samples.servlet.filters.TestServletFilter 
    </filter-class> 
  </filter> 
 
  <filter-mapping> 
    <filter-name>TestServletFilter1</filter-name> 
    <url-pattern>/portal/*</url-pattern> 
  </filter-mapping> 
 
  <filter-mapping> 
    <filter-name>TestServletFilter2</filter-name> 
    <url-pattern>/portal/*</url-pattern> 
  </filter-mapping> 
  ...  


 


  可以看到,清单 22 中使用java 类 TestServletFilter 共配置了两个 Servlet 过滤器,均对 portal 进行过滤。


  Servlet 和 Porlet 过滤器链


   通过日志分析,我们可以得到如图 5 所示的过滤器链,该图体现了一个 HTTP 请求所流经的 Servlet 过滤器链和 Portlet 过滤器链。请注意图中用不同颜色的线条表示 Portlet 过滤器针对的 Portlet 的四个不同的阶段,这四个阶段的过滤器操作是分别独立发生的,而不是并行发生的。


  图 5. Servlet 过滤器链和 Portlet 过滤器链




 


  读者可以从日志输出注意到 Servlet 过滤器和 Portlet 过滤器的以下区别和联系:


  Servlet 过滤器在 Web 应用启动时初始化,在 Web 应用关闭时销毁;Portlet 过滤器在访问相应 Portlet 的时候初始化,在 Portlet 访问结束的时候销毁。


  Servlet 过滤器在访问所有 portlet 的聚合体 - portal 页面的时候调用,并在所有的 portlet 过滤器调用之前调用。


   Servlet 过滤器过滤的对象是整个 Portal 页面或者一个完整的资源, Portlet 过滤器过滤的对象是 Portal 页面上的组成元素 Portlet 小部件,当然 Portlet 过滤器也可以通过 Resource Filter 过滤一个单独的资源。


  Servlet 过滤器只可以对 Servlet 唯一的生命阶段 Servlet 响应阶段进行过滤,Portlet 过滤器可以分别对 Portlet 的操作、呈现、事件、资源这四个不同的生命阶段进行过滤。


  Portlet 窗口


   PortletRequest 新增了一个方法 getWindowID(),可以获得 Portlet 的窗口 ID,这个 ID 是由容器生成的。在 Portal 容器中布局同一个 Portlet 多次的情况下,windowID 可以用来区分同一个 Portlet 的不同窗口,从而可以使这些 Portlet 窗口缓存并呈现不同的数据。


  在本节中,将提供一个示例,在同一个 portlet 的不同窗口中,根据 Portlet 窗口 ID 的不同,呈现不同的数据。


  准备数据


  本例中我们使用 xml 文件提供两个联系人数据,在工程 jsr286portlets 的 src 目录下, 新建 address.xml 文件,内容如清单 23 所示:


  清单 23. address.xml 文件


<?xml version="1.0" encoding="UTF-8"?> 
<addressbooks> 
  <addressbook> 
    <name>Yan Zhi Dong</name> 
    <address>Beijing</address> 
    <telphone>010-12345678</telphone> 
    <mobile>13512345678</mobile> 
    <email>yanzhid@cn.ibm.com</email> 
  </addressbook> 
  <addressbook> 
    <name>Liu Xu Jin</name> 
    <address>Tianjin</address> 
    <telphone>010-87654321</telphone> 
    <mobile>13887654321</mobile> 
    <email>liuxujin@cn.ibm.com</email> 
  </addressbook> 
</addressbooks> 

 


  将 XML 数据映射为 Java Bean


  本例使用 Apache Commons 库的 Digester 包解析 XML 内容并且封装到 Java Bean 中。


  从 http://commons.apache.org/ 查找并下载 commons-digester、commons-collections、commons-beanutils 包,并将 jar 文件复制到 WEB-INF\lib 目录下。


  按照 Digester 包的要求,在 src 目录下新建一个规则文件 address-rule.xml, 定义 XML 文件到 Java Bean 的映射规则。如清单 24 所示:


  清单 24. address-rule.xml 文件


<?xml version="1.0" encoding="UTF-8"?> 
<digester-rules> 
  <pattern value="addressbooks/addressbook"> 
    <object-create-rule 
      classname="com.ibm.samples.bean.AddressBook" /> 
    <set-next-rule methodname="add" paramtype="java.lang.Object" /> 
    <set-properties-rule /> 
    <bean-property-setter-rule pattern="name" /> 
    <bean-property-setter-rule pattern="address" /> 
    <bean-property-setter-rule pattern="telphone" /> 
    <bean-property-setter-rule pattern="mobile" /> 
    <bean-property-setter-rule pattern="email" /> 
  </pattern> 
</digester-rules> 

 

  新建 Java 类 AddressBook, 该类为所映射的 Java Bean, 如清单 25 所示:


  清单 25. AddressBook.java 文件


package com.ibm.samples.bean; 
 
import java.io.Serializable; 
 
public class AddressBook implements Serializable { 
 
  private static final long serialVersionUID = 5490046055431517141L; 
 
  private String name; 
  private String address; 
  private String telphone; 
  private String mobile; 
  private String email; 
 
  ...//setters and getters 
} 

 


  应用 portlet 窗口


  新建 Portlet 类 TestWindowPortlet, 该类继承了 GenericPortlet,实现了 VIEW 和 EDIT 模式。如清单 26 所示:


  清单 26. TestWindowPortlet.java 文件


package com.ibm.samples.jsr286.portlets; 
 
import ... 
 
public class TestWindowPortlet extends GenericPortlet { 
  private static Log log = LogFactory.getLog(TestWindowPortlet.class); 
 
  @Override 
  protected void doEdit(RenderRequest request, RenderResponse response) 
      throws PortletException, IOException { 
    PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher( 
        "/WEB-INF/jsp/TestWindowPortletEdit.jsp"); 
    rd.include(request, response); 
  } 
 
  @Override 
  protected void doView(RenderRequest request, RenderResponse response) 
      throws PortletException, IOException { 
    String windowID = request.getWindowID(); 
    request.setAttribute("windowID", windowID); 
    AddressBook addressbook = (AddressBook) getPortletContext() 
        .getAttribute(request.getWindowID()); 
    request.setAttribute("addressbook", addressbook); 
    PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher( 
        "/WEB-INF/jsp/TestWindowPortletView.jsp"); 
    rd.include(request, response); 
  } 
 
  @Override 
  public void processAction(ActionRequest request, ActionResponse response) 
      throws PortletException, IOException { 
    List<AddressBook> addressbooks = (List<AddressBook>) getPortletContext() 
        .getAttribute("addressbooks"); 
    if (addressbooks == null) { 
      addressbooks = new ArrayList<AddressBook>(); 
      URL rule = getClass().getResource("/address-rule.xml"); 
      Digester digester = DigesterLoader.createDigester(rule); 
      digester.push(addressbooks); 
      InputStream inputStream = getClass().getResourceAsStream( 
          "/address.xml"); 
      try { 
        digester.parse(inputStream); 
      } catch (SAXException e) { 
        log.error(this, e); 
      } 
      getPortletContext().setAttribute("addressbooks", addressbooks); 
    } 
    String strIndex = request.getParameter("index"); 
    int index = Integer.parseInt(strIndex); 
    AddressBook addressbook = addressbooks.get(index); 
    getPortletContext().setAttribute(request.getWindowID(), addressbook); 
    response.setPortletMode(PortletMode.VIEW); 
  } 
} 

 


  注意 processAction 方法,根据 Portlet 的窗口 ID, 在方法的末尾将特定的 AddressBook 对象和特定的 portlet 窗口关联起来,并将这种关联放在 Portlet 上下文中(也可以视作缓存)。


  相应的 JSP 文件,如清单 27 和 清单 28:


  清单 27. TestWindowPortletEdit.jsp 文件


<%@ page language="java" contentType="text/html; charset=UTF-8" 
  pageEncoding="UTF-8"%> 
<%@taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0"%> 
<portlet:defineObjects /> 
<form action="<portlet:actionURL/>"> 
<table> 
  <tr> 
    <td>Please select one item of the following:</td> 
  </tr> 
  <tr> 
    <td><input type="radio" name="index" value="0" />Yan Zhi Dong</td> 
  </tr> 
  <tr> 
    <td><input type="radio" name="index" value="1" />Liu Xu Jin</td> 
  </tr> 
  <tr> 
    <td><input type="submit" value="Submit" /></td> 
  </tr> 
</table> 
</form> 

 

  

  清单 28. TestWindowPortletView.jsp 文件


<%@ page language="java" contentType="text/html; charset=UTF-8" 
  pageEncoding="UTF-8"%> 
<%@taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0"%> 
<portlet:defineObjects /> 
<table> 
  <tr> 
    <td>WindowID:</td> 
    <td>${windowID}</td> 
  </tr> 
  <tr> 
    <td>Name:</td> 
    <td>${addressbook.name}</td> 
  </tr> 
  <tr> 
    <td>Address:</td> 
    <td>${addressbook.address}</td> 
  </tr> 
  <tr> 
    <td>Telphone:</td> 
    <td>${addressbook.telphone}</td> 
  </tr> 
  <tr> 
    <td>Mobile:</td> 
    <td>${addressbook.mobile}</td> 
  </tr> 
  <tr> 
    <td>Email:</td> 
    <td>${addressbook.email}</td> 
  </tr> 
</table> 

 

  在 portlet.xml 和 web.xml 文件中增加相应的 Portlet 定义 TestWindowPortlet ,并设定该 portlet 支持 VIEW 和 EDIT 模式。


  新建 test 页面,并将 TestWindowPortlet 添加到 test 页面两次,如图 6 所示:


  图 6. TestWindowPortlet 的两个窗口




 


  在 EDIT 模式下,分别选择不同的选项,然后提交,如图 7:


  图 7. EDIT 模式下选择不同的选项




 

  最后两个窗口可以显示不同的数据,如图 8 所示:


  图 8. TestWindowPortlet 的两个窗口分别显示不同的数据




 

  关于本示例的引申


   在该示例中,我们使用 XML 文件存储数据。在实际的生产环境中,数据来源可能是一个关系数据库,甚至是一个远程调用(比如 Web Services)。在这种情况下,如果数据的更新周期比较长,就可以通过数据的缓存提高性能。在同一个 portal 页面中,如果存在同一个 portlet 的不同窗口副本,就可以根据 Portlet 的窗口 ID,把从数据库调用或者远程调用获得的数据缓存到 Portal Server 本地,并且和 Portlet 的特定窗口关联起来,以减少数据库访问或者远程调用的开销,提高系统的性能。本例中使用 Portlet 上下文缓存数据,实际生产过程中可以使用某些缓存产品,如 Ehcache 等。


  小结


  本部分通过示例代码介绍了 JSR 286 Portlet 过滤器和 Portlet 窗口的实际开发使用步骤。至此,JSR 286 Portlet 的新特性及开发示例已经全部介绍完毕。


附件:本文示例源代码或素材下载


JSR286规范与API手册 JSR286规范与API手册 JSR286 Portlet 门户 portal portlet2.0开发必备 立即下载

相关推荐

portlet 规范和API(jsr 168/286)

目前最全的资源 jsr168 jsr286 中英文都有 还有API

JSR 286

 新规范的一些主要功能及特性:资源服务:一种新的通过 portlet 呈现资源的方式。事件:通过发送事件和接收事件来实现 portlet 之间的通信。Portlet 过滤器:与 servlet 过滤器类似,根据 Portlet 请求和响应动态的呈现内容的变换。存在以下四种类型的 portlet 过滤器: Action 过滤器Render 过滤器Res

yanghw0510的专栏 1103

固定资产报废的损失鉴定怎么做?

在社会生产的日常中,企业经常会遇到固定资产报废的损失鉴定,那么怎么去做鉴定?哪些情况下能申请固定资产报废损失鉴定呢? 今天,华碧鉴定就和大家说一说固定资产报废损失鉴定那些事儿。 符合下列条件之一的固定资产可申请报废: 1、使用年限过长,功能丧失,完全失去使用价值,或不能使用并无修复价值的; 2、产品技术落后,质量差,耗能高,效率低,已属淘汰且不适于继续使用,或技术指标已达不到使用要求的; 3、严重损坏,无法修复的或虽能修复,但累计修理费已接近或超过市场价值的; 4、主要附件损坏,无.

weixin_50362670的博客 2116

JSR 286 Portlet 的新特性,第 2 部分: 资源服务、事件与共享呈现参数

关于本系列本系列 专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学完本系列后,您将了解到相对于 JSR 168 Portlet,JSR 286 Portlet 究竟提供了哪些增强功能, 以及这些新增特性在实际开发中的应用。本系列的 第 1 部分 简单回顾了 JSR 168 Portlet, 并列出了 JSR

844

在Kintex-7 FPGA上实现皮秒级时间测量:手把手教你搭建CARRY4进位链TDC(附源码与约束)

本文详细介绍了在Kintex-7 FPGA上利用CARRY4进位链实现皮秒级时间测量(TDC)的完整方案。从CARRY4的物理特性分析到硬件架构设计,再到校准方法与精度提升技巧,手把手教你构建测量精度达53ps的TDC系统,并附源码与约束文件,助力激光雷达、高能物理等精密计时应用开发。

weixin_42550052的博客 444

【MMDetection3.0实战】从零构建自定义数据集训练流程

本文详细介绍了使用MMDetection3.0框架从零开始构建自定义目标检测数据集的完整训练流程。内容涵盖环境配置、COCO格式数据准备、核心代码改造、模型配置与训练策略调优,以及最终的模型测试与验证,帮助开发者快速上手并训练自己的检测模型。

l6m7n8的博客 314

portlet 2.0_Java Portlet规范V2.0(JSR 286)中有哪些新功能?

Portlet是组件化的面向用户的应用程序,可创建某种标记。 该标记旨在与其他标记片段聚合到一个更大的应用程序中,例如,如图1所示的门户页面。 图1.一个样本门户页面 因此,可以将Portlet视为基于用户界面(UI)的服务,这些服务采用面向服务的体系结构(SOA)的方式一直到用户界面。 Java Portlet规范的第一个版本Java规范要求(JSR)168在2003年的Java...

cusi77914的博客 709

ESP32-IDF HTTP服务器实战:从零搭建Web控制界面

本文详细介绍了如何在ESP32平台上使用ESP-IDF框架从零搭建HTTP服务器,并创建Web控制界面。内容涵盖环境配置、服务器基础设置、Web页面处理、GPIO控制接口实现及AJAX异步请求处理,帮助开发者快速掌握物联网设备Web控制开发。

efc123456的博客 1063

JSR286 Portlet开发:Portlet之间的Events通信

开发工具:Jdeveloper 11.1.1.7 1.创建Portlet Application a.选择File->New,在新建向导中选择WebCenter Portal - Portlet Producer Application,如下图: b.在向导中输入Application Name,如下图所示: c.其他配置默认即可,点击【Finish】完成。 2.

CQMKING的专栏 974

存储关键技术·RAID技术·RAID2.0·华为RAID2.0+·华为服务器·云计算

一.RAID基本概念 常见的RAID级别组合RAID级别 二.RAID的数据组织形式 三.RAID的数据保护功能 方法一:镜像方法二:奇偶校验算法(XOR) 四.RAID热备和重构 1.热备(Hot Spare)的定义2.热备的分类3.热备的工作原理 五.RAID 2.0技术 1.RAID 2.02.RAID 2.0+3.华为RAID 2.0+ 六.RAID 2.0+块虚拟化技术 1.什么会出现RAID 2.0+技术?2.什么是RAID 2.0?3.RAID 2.0+的原理是什么?

qq_47480932的博客 3320

优化Cartographer重定位速度:从子图筛选到参数调优的完整思路

本文详细介绍了优化Cartographer重定位速度的完整方法,从子图筛选到参数调优,帮助工程师在已知初始位姿的前提下将重定位耗时降低50%以上。重点分析了性能瓶颈、子图筛选策略、匹配算法选择及环境自适应的参数优化策略,并通过实战案例展示了显著效果提升。

weixin_28704565的博客 464

JSR 286 Portlet 的新特性,第 1 部分: Portlet 2.0 新特性介绍

关于本文本文假定读者熟知 JSR 168 Portlet,并对 J2EE 基本常识有一定了解。本文主要以理论的方式向读者介绍 JSR 286 Portlet 的以下新增特性: 资源服务 事件 共享呈现参数 Portlet 过滤器 Portlet 窗口 Portlet 和 JSR 168Portlet 是部署在容器内用来生

762

【NLP】第十章:位置编码Positional Encoding

前面讲RNN时一般都会有一个ht的输出,这个输出就是样本与样本之间的信息,而且RNN是时序循环的,所以RNN本身的结构,在物理上,就自带词的前后顺序信息的。比如词never,不管never本身的词向量是什么,只要你在序列中排第一个位置,你的位置向量就是[0,1,01],你排第三个位置,你的位置向量就是[0.9,-0,4,0.2,1.0]。也所以,你可以理解为:embedding后的词嵌入,PE按照每个样本在sequence中的位置,把embedding后的词嵌入给扭转后,才送入attention的。

因为热爱所以坚持 1918

JSR 286 Portlet 的新特性: Portlet 2.0 新特性介绍

关于本文本文假定读者熟知 JSR 168 Portlet,并对 J2EE 基本常识有一定了解。本文主要以理论的方式向读者介绍 JSR 286 Portlet 的以下新增特性:资源服务事件共享呈现参数Portlet 过滤器Portlet 窗口回页首Portlet

jianpingdu2009的专栏 1218

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

JSR 286 Portlet 的新特性: Portlet 2.0 新特性介绍(portlet协作、事件机制、数据共享)

  本文假定读者熟知 JSR 168 Portlet,并对 J2EE 基本常识有一定了解。本文主要以理论的方式向读者介绍 JSR 286 Portlet 的以下新增特性:  资源服务  事件  共享呈现参数  Portlet 过滤器  Portlet 窗口  Portlet 和 JSR 168  Portlet 是部署在容器内用来生成动态内容的 Web 组件,与 ser

jianpingdu2009的专栏 1078

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Portlet 2.0 新特性介绍(全)

第一部分 Portlet 2.0 新特性介绍======================================================================  关于本系列  本系列文章专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学习完本系列后,您将了解相对于JSR 168 Portle...

caoming51021的专栏 1417

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

JSR 286 Portlet 的新特性 第 1 部分: Portlet 2.0 新特性介绍

关于本系列 本系列文章专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学习完本系列后,您将了解相对于 JSR 168 Portlet,JSR 286 Portlet 究竟提供了哪些增强功能, 以及这些新增特性在实际开发中的应用。 第 1 部分将简单回顾 JSR 168 Portlet, 并列出了 JSR 286

墨者侠客 657
上一篇: JSR 286 Portlet 的新特性,第 2 部分: 资源服务、事件与共享呈现参数
iteye_4621
博客等级 码龄8年 1粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值