- 浏览: 216972 次
- 性别:
- 来自: 湖北->上海
最新评论
-
苹果超人:
我也遇到这个问题,我想在ConfigurationAction ...
杀千刀的PortletPreferences -
flago:
Liferay 的论坛设置的默认显示多少条帖子怎么设置??
liferay中vm中如何调用java类代码 -
ofdata:
遭遇此问题
杀千刀的PortletPreferences -
yaokaiwen99:
大哥求一个简单的flexigrid在liferay中应用的例 ...
Liferay异步提交 -
gaigai511225:
你好 插件环境那个url 具体怎么写 我照您的方式写了出错 ...
Liferay异步提交
在 本系列 的 第 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 的新特性及开发示例已经全部介绍完毕。
附件:本文示例源代码或素材下载
- 1269934348_ddvip_3278.rar (1.2 MB)
- 下载次数: 33
发表评论
-
JSR 286 Portlet 的新特性,第 2 部分: 资源服务、事件与共享呈现参数
2010-05-31 18:42 2200在本系列的 第 1 部分 简要回顾了 J ... -
JSR 286 Portlet 的新特性,第 1 部分: Portlet 2.0 新特性介绍
2010-05-31 17:55 2378本系列文章专门针 ... -
Session share
2010-05-14 12:18 0Tomcat Server.xml <Servi ... -
Liferay集群负载均衡配置
2009-10-30 11:14 4882先介绍一下项目系统的环境, Liferay Version: ... -
Liferay 集群部署
2009-10-30 00:00 0十二点了,明天断续 -
Liferay 权限策略
2009-10-29 23:45 3093先上一张大家都很熟悉的图 这张图可以说是整 ... -
Liferay Portlet之间的通信
2009-10-29 19:12 5006从我做Liferay到现在,这个问题一直都存在着,直到最近我才 ... -
Liferay异步提交
2009-10-29 18:06 3395liferay的异步提交其实是非常简单的,他用的是json & ... -
Liferay异步刷新
2009-10-29 18:05 3171Liferay portlet异步刷新,分为两种情况。 ... -
准备开始整理半年来对lifeay新的学习成果
2009-10-29 18:04 1491有半年没有写过liferay的blog了,不过对life ... -
Ajax知识
2009-06-11 17:50 0<SCRIPT LANGUAGE=" ... -
改变Liferay Portal Context(让Liferay不在使用ROOT目录)
2009-05-25 22:46 1823在部署的时候如果遇到我们会遇到让liferay与其他的项目并存 ... -
velocity调用DB持久层,生成actionurl
2009-05-22 17:42 1173#set($hostelCacheService = $ser ... -
liferay 所有版本下载地址
2009-05-21 13:29 3205从1.7.5一直到现在的5.2.3,应有尽有。 htt ... -
liferay ldap配置与相关代码
2008-11-28 11:06 38201.下载LDAP server 并安装, liferay支 ... -
liferay 的加密技术(CRYPT,SHA,SSHA,MD2,MD5)
2008-11-27 15:59 4284package com.liferay.util; impo ... -
liferay openid配置与代码详解
2008-11-21 21:14 28241.liferay的openid的配置很简单的,先去 open ... -
liferay验证码认证
2008-08-27 15:26 2733生成验证码相关: /** * Copyright (c ... -
很烦,想站在阳台上大声的喊
2008-08-20 19:42 1794终于到回家了,好累, ... -
网络爬虫相关(自已记下来,怕以后忘记)
2008-08-16 13:15 1925package com.taobao.html; impor ...
相关推荐
文章专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学习完本系列后,您将了解相对于 JSR 168 Portlet,JSR 286 Portlet 究竟提供了哪些增强功能, 以及这些...
文章专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学习完本系列后,您将了解相对于 JSR 168 Portlet,JSR 286 Portlet 究竟提供了哪些增强功能, 以及这些...
3. Portlet过滤器:类似于Servlet过滤器,portlet过滤器可以在portlet处理请求和响应之前进行拦截和处理,增强了portlet的灵活性和可扩展性。 4. 模块化部署:portlet可以通过portlet模块(Portlet WARs)进行独立...
JSR286,全称为Java Specification Request 286,是Java Community Process(JCP)发布的一个标准,它定义了portlet API的第二版,用于增强portlet的交互性和可扩展性。 在portlet开发中,JSR286带来了以下几个核心...
2. **事件模型**:JSR286提供了一种新的事件模型,portlet可以通过发布和订阅事件与其他portlet通信。这种通信机制增强了portlet之间的协作和数据共享。 3. **安全增强**:规范增强了portlet的安全性,包括对...
IBM内部JSR Portlet详细介绍,想了解IBM web sphere portlet的可以下载看看。
JSR286,全称为Java Specification Request 286,是Java Portlet API的第二版,也被称为Portlet 2.0 API。这个规范主要针对Web应用程序中的portlet开发,提供了一套标准接口和框架,使得portlet可以在不同的portlet...
JSR286的新特性不仅提升了portlet的功能性,也为开发人员带来了更大的灵活性和控制权。例如,资源服务允许portlet根据用户的操作动态地加载资源;事件机制使得portlet之间的通信变得更加容易;共享呈现参数加强了...
Portlet 2.0(JSR 286)规范在原有基础上进行了一系列重要的改进和扩展,包括但不限于事件处理、资源共享、过滤器机制以及缓存策略等方面。这些改进不仅提升了Portlet应用程序的功能性和用户体验,还加强了门户平台...
系列文章专门针对具有 JSR 168 Portlet 开发基础,并且想了解 JSR 286 Portlet 新特性和开发流程的开发人员。在学习完本系列后,您将了解相对于 JSR 168 Portlet,JSR 286 Portlet 究竟提供了哪些增强功能, 以及...
基于JSR168的portlet精彩范例
- **Portlet模式和窗口状态**:Portlet可以根据用户的操作处于不同的模式(如查看模式、编辑模式等),并且每个模式都有特定的视图。 - **Portlet偏好设置**:允许用户自定义Portlet的行为和外观,如选择显示的语言...
Portlet技术是构建企业级Web应用的重要组成部分,特别是在门户(portal)环境中,它允许开发者创建可重用、可组合的Web组件。JSR168(Java Portlet Specification 1.0)是Java社区进程发布的一项标准,为portlet的...
2. **portlet过滤器**:IBM Portlet API可能提供更高级别的过滤机制,允许更精细的控制内容处理和过滤。 3. **portlet会话管理**:IBM的实现可能具有不同的会话管理策略,以适应企业级门户的复杂需求。 **示例...
Struts2 JSR168 Portlet的开发是构建在Java Portlet规范(JSR168)基础上,结合流行的MVC框架Struts2来创建适用于企业级门户平台的应用组件。这种开发方式允许开发者利用Struts2的强大功能,如Action、Interceptor、...
在JSR 286中,portlet过滤器通过实现`PortletFilter`接口来创建。过滤器可以应用于整个portlet实例,也可以针对特定的portlet模式(如VIEW、EDIT或HELP)。通过在`portlet.xml`配置文件中定义过滤器链,可以控制过滤...
【在 RAD7 中开发 JSR168 Portlet】是一个关于使用IBM Rational Application Developer (RAD) 7.0.0.3版本开发遵循JSR168标准的portlet的教程。JSR168(Java Portlet API 1.0)是Java Community Process发布的一个...
使用这个插件,开发者可以轻松创建一个新的portlet项目,其中包括必要的目录结构和配置文件,如portlet.xml、web.xml等。然后,开发者可以在Eclipse的环境中编写portlet的Java代码,实现portlet的渲染逻辑、事件处理...