`
Tyler_Zhou
  • 浏览: 216972 次
  • 性别: Icon_minigender_1
  • 来自: 湖北->上海
社区版块
存档分类
最新评论

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 的新特性及开发示例已经全部介绍完毕。


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


  • 大小: 48.7 KB
  • 大小: 25.4 KB
  • 大小: 13 KB
  • 大小: 84.9 KB
  • 大小: 40.4 KB
  • 大小: 42.3 KB
  • 大小: 13.8 KB
  • 大小: 49.4 KB
分享到:
评论

相关推荐

    JSR286 portlet新特性

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

    JSR286 portlet开发标准

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

    portlet 规范和API(jsr 168/286)

    3. Portlet过滤器:类似于Servlet过滤器,portlet过滤器可以在portlet处理请求和响应之前进行拦截和处理,增强了portlet的灵活性和可扩展性。 4. 模块化部署:portlet可以通过portlet模块(Portlet WARs)进行独立...

    portlet源码案例jsr286

    JSR286,全称为Java Specification Request 286,是Java Community Process(JCP)发布的一个标准,它定义了portlet API的第二版,用于增强portlet的交互性和可扩展性。 在portlet开发中,JSR286带来了以下几个核心...

    JSR286规范与API手册

    2. **事件模型**:JSR286提供了一种新的事件模型,portlet可以通过发布和订阅事件与其他portlet通信。这种通信机制增强了portlet之间的协作和数据共享。 3. **安全增强**:规范增强了portlet的安全性,包括对...

    JSR286PortletAPI

    IBM内部JSR Portlet详细介绍,想了解IBM web sphere portlet的可以下载看看。

    JSR286规范及API、源码

    JSR286,全称为Java Specification Request 286,是Java Portlet API的第二版,也被称为Portlet 2.0 API。这个规范主要针对Web应用程序中的portlet开发,提供了一套标准接口和框架,使得portlet可以在不同的portlet...

    JSR268门户标准

    JSR286的新特性不仅提升了portlet的功能性,也为开发人员带来了更大的灵活性和控制权。例如,资源服务允许portlet根据用户的操作动态地加载资源;事件机制使得portlet之间的通信变得更加容易;共享呈现参数加强了...

    portlet-2-spec (JSR 286)

    Portlet 2.0(JSR 286)规范在原有基础上进行了一系列重要的改进和扩展,包括但不限于事件处理、资源共享、过滤器机制以及缓存策略等方面。这些改进不仅提升了Portlet应用程序的功能性和用户体验,还加强了门户平台...

    Portlet 2.0 新特性介绍(全)

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

    基于JSR168的portlet精彩范例

    基于JSR168的portlet精彩范例

    The_Java_Portlet_Specification(JSR168规范英文版)

    - **Portlet模式和窗口状态**:Portlet可以根据用户的操作处于不同的模式(如查看模式、编辑模式等),并且每个模式都有特定的视图。 - **Portlet偏好设置**:允许用户自定义Portlet的行为和外观,如选择显示的语言...

    portlet学习资料

    Portlet技术是构建企业级Web应用的重要组成部分,特别是在门户(portal)环境中,它允许开发者创建可重用、可组合的Web组件。JSR168(Java Portlet Specification 1.0)是Java社区进程发布的一项标准,为portlet的...

    比较-JSR-168-Java-Portlet-规范与-IBM-Portlet-API.docx

    2. **portlet过滤器**:IBM Portlet API可能提供更高级别的过滤机制,允许更精细的控制内容处理和过滤。 3. **portlet会话管理**:IBM的实现可能具有不同的会话管理策略,以适应企业级门户的复杂需求。 **示例...

    Struts2 JSR168 Portlet的开发

    Struts2 JSR168 Portlet的开发是构建在Java Portlet规范(JSR168)基础上,结合流行的MVC框架Struts2来创建适用于企业级门户平台的应用组件。这种开发方式允许开发者利用Struts2的强大功能,如Action、Interceptor、...

    portlet源码,portlet Filter

    在JSR 286中,portlet过滤器通过实现`PortletFilter`接口来创建。过滤器可以应用于整个portlet实例,也可以针对特定的portlet模式(如VIEW、EDIT或HELP)。通过在`portlet.xml`配置文件中定义过滤器链,可以控制过滤...

    在RAD7中开发JSR168 Portlet

    【在 RAD7 中开发 JSR168 Portlet】是一个关于使用IBM Rational Application Developer (RAD) 7.0.0.3版本开发遵循JSR168标准的portlet的教程。JSR168(Java Portlet API 1.0)是Java Community Process发布的一个...

    JSR 168 Portlet Project Creator 插件jar包

    使用这个插件,开发者可以轻松创建一个新的portlet项目,其中包括必要的目录结构和配置文件,如portlet.xml、web.xml等。然后,开发者可以在Eclipse的环境中编写portlet的Java代码,实现portlet的渲染逻辑、事件处理...

Global site tag (gtag.js) - Google Analytics