将Java Web应用转换为适应性 Portlet的方法
摘要
本文介绍如何将在门户外运行的 Web 应用转换为在 AquaLogic User Interaction 内使用的适应性 portlet。本文概述使用 Java Server Faces 或 Struts 等 Web 应用框架时遇到的问题和一种解决这些问题的新工具 — Java Portlet Toolkit。本文还将探讨 Java Portlet Toolkit 附带的一些其它工具,如 PortletBean 和特定于门户的 JSP 标签。
简介:Java Web 应用与 Java Portlet 应用
自从 Java servlet 和 Java Server Pages (JSF) 规范问世后,人们就为更简易地编写、配置和自定义 Java Web 应用进行了多次尝试。大多数应用都采用了“Model 2”架构,在这种架构中,请求通过控制 servlet 路由到各个 JSP 页面,并显示和修改 servlet 请求和会话附带的 JavaBean 中的数据。此架构采用 Smalltalk “模型-视图-控制器”设计模型,极大程度地简化了 Java Web 应用的开发。一些Web 应用框架,如Struts 和 Java Server Faces,允许开发人员配置请求路由,JavaBean 管理,验证及其它常用 Web 应用任务,而不必将这些功能硬编码到 JSP 或 Java 类中,从而进一步简化了 Web 应用开发。
Struts 和 JSF 等框架非常适合单机 Web 应用,因为这些框架能执行大部分请求处理、验证和导航的工作,使开发人员有时间定义应用所需的业务逻辑。遗憾的是,由于这些框架依赖于表单 post 和重定向来管理应用导航,其结构通常不适合在门户应用中使用。此外,由于框架没有唯一标识单个 Web 应用范围以外的页面元素的功能,大多数框架都很难转换到多 portlet 环境(在这种环境中,一个 Web 应用可能会有多个实例作为 portlet 显示在页面上)。
理想情况下,应该有一种方式可以转换 Struts、JSF 或任何 JSP/servlet 应用,使它们既可以在 portlet 中使用也可以作为单机 Web 应用使用,而不必对应用代码或配置文件进行更改。所幸的是,Plumtree Java Portlet Tools 库的开发版正好可以使开发人员达成此目的。此外,该库还为 EDK 功能提供 JavaBean 包装器。EDK 是一组工具库,允许开发人员访问请求头中编码的 portlet 信息和执行远程服务任务,例如,从 Collaboration 和 Search 等门户应用访问数据。Java Portlet Toolkit EDK 包装器 bean 允许开发人员使用 JSP 2.0 Expression Language 来获取和设置 portlet 属性、设置和首选项。最后,它还包括一套自定义标签,为访问随处刷新和 PCC 事件处理等适应性 portlet 技术提供简化访问,使开发人员在不必编写任何 JavaScript 的情况下就能执行复杂的 DHTML 操作。
本文的读者应熟悉 Java、Java servlets、JSP 和 JSP 标签库技术。希望将简单 Java 应用转换为适应性 portlet 的开发人员可只阅读 Servlet Filter PTPortletFilter这一节,跳过讨论打包在Java Portlet Tools 库中的附加特性的其他章节。希望充分利用适应性 portlet 技术的开发人员则应阅读整篇文章。
Servlet Filter 和 PTPortletFilter
Java Servlet 2.3 规范包含一个名为 Filter 的新类。Filter 是在 Java servlet 应用的 web.xml 文件中配置的,在 servlet 请求和响应发送到 JSP 和 servlet 类之前或之后都可以截取和修改它们。Java Portlet Tool 提供一种特殊的 Servlet Filter,称为 PTPortletFilter,该类截取从 Web 应用发送的 HTML 并重写,使它可包括在 Plumtree portlet 中。此重写过程会执行以下几种操作:
- 将 portlet ID 附加在 <form> 标签 ID 后面,更改标签 ID,使每个 portlet 的标签 ID 都是唯一的。如果未找到表单 ID 属性,筛选器将创建一个唯一的 ID。
- 删除或重写不属于 portlet 的标签,如 <body> 或 <link> 标签。
- 保留被重写的标签的基本属性。例如,保留所有在 <body> 标签上设置的背景颜色或 onload 事件处理程序。
- 将适应性 portlet JavaScript 附加到 portlet 后面,并将 portlet 内容包装在具有唯一 ID 的 <div> 标签中,使 portlet 能够执行随处刷新请求。
- 更改所有表单提交按钮以执行随处刷新表单 post。
- 更改指向 Web 应用资源的所有超级链接,以执行随处刷新 get 请求。
PTPortletFilter 通过将以下 XML 添加到 Web 应用的 web.xml 配置文件来声明:
<filter> <filter-name>Adaptive Portlet Filter</filter-name> <filter-class>com.plumtree.remote.filter.PTPortletFilter</filter-class> <init-param> <param-name>filter.config.file</param-name> <param-value>C:/portlettools/apps/carstore/conf/custom-filter.xml</param-value> </init-param> <init-param> <param-name>filter.log.file</param-name> <param-value>C:/portlettools/apps/carstore/logs/jpt.log</param-value> </init-param> <init-param> <param-name>filter.log.level</param-name> <param-value>debug</param-value> </init-param> <init-param> <param-name>jsxml.version</param-name> <param-value>177643</param-value> </init-param> <init-param> <param-name>portal.imageserver.alternate</param-name> <param-value>https://plumtree/imageserver</param-value> </init-param> </filter> <filter-mapping> <filter-name>Adaptive Portlet Filter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
所有筛选器初始化参数都是可选的,如果您不想执行任何筛选器日志记录或自定义操作,则只需添加 filter 类和 URL 映射即可。对于较复杂的门户配置,您可能需要添加备用的imageserver 或日志记录初始化参数。PTPortletFilter 识别的初始化参数如下:
参数 | 描述 | 默认值 |
---|---|---|
filter.config.file | 指向定义嵌套操作树的 XML 文件,PTPortletFilter 根据 Web 应用的响应 HTML 执行这些嵌套操作。Java Portlet Tools 筛选器提供一个已针对 JSF 应用测试过的 sample filter.xml。此样本文件对大多数 Java Web 应用框架都可用;但它是可以配置的,如有必要,还允许开发人员添加新的 HTML 重写操作。有关如何自定义此 XML 文件的详细说明,请参阅附录 A:PTPortletFilter 配置 XML。此参数应仅在执行自定义筛选器操作时指定。 | portlet-tools.jar 中包含的默认 filter.xml 文件。 |
filter.log.file | 此参数指定筛选器重写日志记录信息的文件的绝对路径。 | stdout |
filter.log.level | 筛选器执行的日志记录的级别。可能的值包括:debug、info、warning、severe、error 和 off。 | warning |
jsxml.version | Plumtree JSXML JavaScript 组件的版本,该版本使随处刷新在 Plumtree Portal 中成为可能。此值是指 Web 应用将优先使用的 JSXML JavaScript 库的版本。JSXML 是 G6 Scripting Framework 底层的 AJAX 框架。 | JSXML 的最新版本 |
portal.imageserver.alternate | 允许开发人员在加入 JSXML JavaScript 组件时使用指定 URL 连接到imageserver。如果门户的imageserver有安全限制,阻止 Web 应用直接访问它,则必须使用此参数。 | – |
content.type | 设置 HTTP 响应的内容类型 | text/html;charset=UTF-8 |
character.encoding | 设置 HTTP 响应中使用的字符集。 | UTF-8 |
有关 PTPortletFilter 如何重写响应 HTML 的示例,请看表单中的一个简单提交按钮:
<form id="myform" action="next.jsp"> <input type="submit" name="action" value="Submit" onchange="alert('Saving info...')"> ... </form>
如果不作修改,此按钮将导致整个门户页提交,并只对单击了按钮的 portlet 返回内容,从而使该 portlet 接收门户页的全部内容。但是,PTPortletFilter 会重写标签,以使用随处刷新:
<form id="myform_203" action="http://host/portal/server.pt/gateway/PTARGS_0_16_203_0_0_43/http%3B/app/next.jsp"> <input type="button" name="action" value="Submit" onchange="alert('Saving info...'); postback_203(document.getElementById('myform_203'), document.getElementById('pt-portlet-java-203'), {'action':'Submit'});"> ... </form>
指向 Web 应用资源(且目的是刷新当前页)的超级链接将被重写,从而使它们能够执行随处刷新请求。例如,在以下链接中:
<a href="next.jsp">Link in this window</a> <a href="next.jsp" target="new">Link in another window</a> <a href="http://www.plumtree.com">Link to another site</a>
只有第一个链接会导致门户出现问题。像上面的表单示例一样,单击此链接会使该 portlet 接收整个门户页的内容。只有此链接会被重写,以执行随处刷新请求。
<a onclick="linkback_203('http://host/portal/server.pt/gateway/PTARGS_0_16_203_0_0_43/http%3B/app/next.jsp', document.getElementById('pt-portlet-java-203'); return false;" href="#">Link in this window</a> <a href="http://host/portal/server.pt/gateway/PTARGS_0_16_203_0_0_43/http%3B/app/next.jsp" target="new">Link in another window</a> <a href="http://www.plumtree.com">Link to another site</a>
最后,Web 应用中按计划提交表单的所有 JavaScript 都会被重写,以执行随处刷新。所有引用表单的 JavaScript 都会被重写,以根据含有追加的 portlet ID 的唯一 ID 引用表单。
当 Web 应用被视为单机 Web 应用而非 portlet 中的远程 Web 服务时,PTPortletFilter 会被禁用。如果通过门户的网关运行 Web 应用中的某个页面并在 portlet 中显示,则 PTPortletFilter 将按原样显示该 Web 应用的 HTML 内容。
PortletBean
Java Portlet Tools 提供一种专门的 JavaBean, 供 JSP scriptlets 和 JSP 表达式语言使用。有关 JSP 表达式语言如何工作的详细信息,请参阅 Sun 的 JSP 文档。PortletBean 是 EDK 和客户端 portlet API 功能的 JavaBean 包装器。它为以下各项提供属性的 getter 和 setter 方法:
- Portlet、Community和页面 ID,imageserver URL,stylesheet URL 等
- Portlet 首选项设置(例如 Admin、Community 和 User)
- 为触发 PCC 事件或设置会话首选项等操作生成适应性 portlets JavaScript
此 bean 使用属性名“portlet”自动连接到通过 PTPortletFilter 的每个 ServletRequest。开发人员可通过 PortletBean 引用 EDK 方法值,而不必在 JSP 的 scriptlet 中构建 IPortletContext 和 IPortletRequest 对象。这看似只是 JavaBean 语法中已有的包装功能,但在 Java Web 应用中,PortletBean 可以用来实现强大的功能。例如,假设 JSF Web 应用开发人员想要创建一个个人资料 portlet,该 portlet 可以设置用户首选项并触发 PCC 事件,通知其它已设置首选项的portlet。JSP 页中的标记将为如下所示:
<f:view> <h:form> <b>Personal Profile Information:</b><br> <h:panelGrid columns="2"> <h:outputText value="Name:"/> <h:inputText value="#{portlet.userSettings.name}"/> <h:outputText value="Age:"/> <h:inputText value="#{portlet.userSettings.age}"/> <h:outputText value="Height:"/> <h:inputText value="#{portlet.userSettings.height}"/> <h:outputText value="Weight:"/> <h:inputText value="#{portlet.userSettings.weight}"/> <h:outputText value="Nationality:"/> <h:inputText value="#{portlet.userSettings.nationality}"/> </h:panelGrid> <h:commandButton action="success" value="Submit" onclick="#{portlet.raiseEventJS['submitinfo']}"/> </h:form> </f:view>
这一简单的 JSP 将使用户首选项值显示为 <input type="text"> 标签的值并在提交表单后即设置成功。这被称为值绑定(value binding),它使 JSF 应用自动从 servlet 请求中设置 bean 属性值,而无需开发人员编写任何请求处理代码。如果没有 PortletBean,则 JSF 应用必须创建 IPortletContext、IPortletRequest 和 IPortletResponse 对象,检索每个设置的设置值并使用 JSP scriptlet 将其插入到输入标签中,最后在服务器端的 handler 类中处理请求,而该类必须从请求获取每个设置值并将其手动设置到 IPortletResponse。此请求处理代码毫无疑问将是硬骗码,如果需要更多首选项设置时,将很难修改。
另请注意 <h:commandButton> 标签。此标签有一个 onclick 属性值,该值触发可被门户页面中其它 portlet 中的注册 PCC 事件处理程序捕捉的 JavaScript PCC 事件。在本例中,PortletBean 属性解析为:
onclick="document.PCC.RaiseEvent ('urn:/plumtree.com/adaptiveportlets', 'submitinfo');"
有关使用 PortletBean EDK 属性或使用 PortletBean 来生成客户端 JavaScript 的详细信息,请参见:附录 B:PortletBean API。
Java Portlet Tools 标签
JSP 规范允许开发人员定制自定义标签库,当 JSP 评估时,这些标签库可以生成自定义 UI 组件显示在 Web 应用中。Java Portlet Tools 库提供一组自定义标签,使开发人员能利用客户端适应性 portlet 技术。通过在 JSP 页添加 taglib 指令,即可将自定义标签库添加到 JSP 页面中,例如:
<html> <head> <title>Car Store</title> </head> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://plumtree.com/javaportlettools" prefix="portlet" %> <body> ... </body> </html>
http://plumtree.com/javaportlettools URI 用于唯一地标识 Plumtree Java Portlet Tools 标签库。在此库中,您可找到以下标签。(注意:单击下面左边栏中的链接后,可能需要刷新浏览器才能访问登录页面。)
<portlet:group> | 用于在共享名称空间中将多个 portlet 分组的标签。此名称空间允许 portlet 触发 PCC 事件或响应来自 portlet 组中其它 portlet 的 PCC 事件,同时允许 portlet 获取和设置 portlet 组可访问的会话首选项。 |
<portlet:raiseEvent> | 触发 PCC 事件的标签,该事件可被其它 portlet 捕捉。 |
<portlet:onEvent> | 定义 PCC 事件处理程序的标签。该标签会侦听特定 PCC 事件并调用包含在标签中的处理程序函数或自定义 JavaScript。 |
<portlet:clickOnEvent> | PCC 事件处理程序标签,在捕获事件时会单击其标签之间的任何元素。 |
<portlet:refreshOnEvent> | 在捕获事件时会对 portlet 内容进行随处刷新的 PCC 事件处理程序标签。刷新 URL 设置为标签的“url”属性。此事件处理程序标签可以刷新 portlet 内容或将随处刷新响应对象发送到处理程序函数。 |
<portlet:formRefreshOnEvent> | 在捕获事件时会使用随处刷新 post 提交表单的 PCC 事件处理程序标签。此事件处理程序标签可以刷新 portlet 内容或将随处刷新响应对象发送到处理程序函数。 |
<portlet:adaptivePortlet> | 用来将适应性 portlet JavaScript 包含到 JSP 页中的特殊标签。它会复制脚本的 include 和 PTPortletFilter 的 PortletBean 附件(但不是 HTML 筛选),用于 PTPortletFilter 不处理的 JSP 或不应由筛选器处理的 Web 应用。 |
<portlet:unfiltered> | 这些标签内的所有响应内容都会被 ResponseFilter 忽略。 |
<portlet:getSessionPref> | 生成从 PPC 上检索会话首选项值的 JavaScript。 |
<portlet:setSessionPref> | 生成在 PPC 上设置会话首选项值的 JavaScript。 |
有些标签与 PortletBean 提供的功能重复。例如,在一个 JSF 应用中,开发人员可以通过三种不同的方式触发 PCC 事件:
<h:outputLink onclick="#{portlet.raiseEventJS['myevent']}"> <h:outputLink onclick="<%= ((PortletBean) request. getAttribute("name")).raiseEventJS("myevent") %>"> <a onclick="<portlet:raiseEvent eventname="myevent"/>">
功能重复的原因是,JSP 表达式语言只能在 JSP 2.0 标签的自定义标签属性中起作用,而 scriptlets 读写太麻烦。许多 Web 应用框架特别阻止用户在其 JSP 中依赖 scriptlet。Java Portlet Tools 提供的自定义标签允许开发人员在没有或不能使用表达式语言的情况下避免使用 scriptlet。其它标签,如 <portlet:onEvent> 标签,不适合作为 PortletBean 属性使用,因为它们会生成大量复杂的 JavaScript 和 HTML,或者因为它们不是用来在其它标签中作为属性值使用的。开发人员应参考“附录 C:常见问题”以了解何时使用自定义标签和 PortletBean。
总结
Java Portlet Toolkit 使 Web 应用能够轻松转换为 portlet。只要在 Web 应用的 web.xml 配置文件加上筛选器,Java Portlet Toolkit 就会重写 Web 应用 HTML,使之能在门户环境中有效运行。此筛选器可根据特定 Web 应用的需求方便地进行配置。此外,Java Portlet Toolkit 还提供一组 JavaBean 和 JSP 标签,使 Java Web 应用的开发和对门户资源的访问更为简单。
参考
- Java Portlet Tools 二进制和源码文件
- J2EE 1.4 教程
- Java Portlet Tools 安装指南
- Java Portlet Tools API 文档
- Java Portlet Tools 标签库文档
- 样本 PTPortletFilter 配置 XML
附录 A: PTPortletFilter 配置 XML
PTPortletFilter 事实上是一种更通用的 ResponseFilter 类的子类,该类也执行 HTTP 响应文本(不一定是 HTML)筛选,并且不依赖 HttpServletRequest 的网关。PTPortletFilter 逐行修改 HTML,意味着它一次只执行一行,而不是整个 HTML 响应。但当 HTML 标签跨越多行时例外,例如:
<a href="myapp/chooseLocale.faces" onclick="alert('Choose Locale');"> Choose Locale</a>
在本例中,PTPortletFilter 会将标签所跨越的所有行当作单个 HTML 行进行筛眩
PTPortletFilter 在启动时会加载一个 XML 配置文件,该文件定义 PTPortletFilter 对被筛选的 Web 应用的响应 HTML所执行的筛选操作。除以上配置文件中定义的筛选操作外,PTPortletFilter 还执行几项其它功能:
- 修改 HTML,使对 <form> 标签的数组引用转换为具体的 ID 引用。
- 将 PortletBean 附加到每个 HttpServletRequest 对象。
- 以有用的值(如portlet ID 和管理首选项)填充令牌名称/值对映射(Map),用于筛选操作。
筛选器的配置 XML 定义了两组可对响应 HTML执行操作的对象:FilterAppender 和 FilterOperation。
FilterAppender
FilterAppender 是将 HTML 附加到响应 HTML开头和末尾部分的 Java 类。由于此操作只对每次请求执行一次,因此与使用FilterOperation 在响应的开头和末尾插入 HTML相比,定义一个类来执行此步骤更有效率,这是由于FilterOperation 会对响应 HTML 的每一行执行操作。Appender 是通过引用生成 appender 的类在配置 XML 文件中进行声明的。
<httpfilter> <appenders> <appender class="com.plumtree.remote.filter.appender.AdaptivePortletAppender"/> </appenders> <operations> ... </operations> </httpfilter>
在实例化时,Appender 都调用同一 init() 函数,该函数取 PTPortletFilter 的 FilterConfig 作为参数。这样,appender 就可以访问在定义筛选器的 web.xml 中设置的所有初始化参数。
PTPortletFilter 在没有 appender 类的情况下也可运行,但我们定义了一个有用的 appender,它允许 Java Web 应用框架在门户环境内部能运行:AdaptivePortletAppender。此appender附加了JSXML JavaScript组件,它允许从Web应用程序中发出随处刷新请求。AdaptivePortletAppender 使用 JSRegistry 与 imageserver 联系,并生成应包含在页面中的JavaScript文件清单,以实例化客户端 JavaScript 中的 JSXML 对象。此外,AdaptivePortletAppender 还添加了几个简化的功能,使随处刷新请求更容易生成。最后,appender 将 Web 应用生成的内容包装在具有唯一 ID pt-portlet-java-[portletID] 的 <div> 标签中。每次用户启动随处刷新请求时,该唯一 ID 就用作目标 HTML 要素;而此 <div> 标签中的内容将被响应取代。
FilterOperation
PTPortletFilter 配置 XML 定义的第二种对象是 FilterOperation 子类。每个 FilterOperation 子类代表一个将对文本的每一行执行的特定文本处理操作。FilterOperation 都构建在操作树中,使特定操作只有在其父操作成功时才会发生。以下是操作 XML 的例子:
<operations> <grabtag> <tag>title</tag> <token>TITLE</token> </grabtag> <match> <pattern></ body[^>]*></pattern> <flags>i</flags> <operations> <grabattribute> <tag>body</tag> <attribute>onload</attribute> <token>BODYONLOAD</token> </grabattribute> <grabattribute> <tag>body</tag> <attribute>bgcolor</attribute> <token>BODYBGCOLOR</token> </grabattribute> <if> <token>BODYBGCOLOR</token> <exists/> <operations> <insert> <before><body[^>]*></before> <markup><div color=@BODYBGCOLOR@></markup> </insert> </operations> </if> <replace> <pattern><(/ )body</pattern> <with><?div</with> <flags>i</flags> </replace> </operations> </match> ... </operations>
提供了几种类型的 FilterOperation。筛选器操作通过调用 excecute 方法执行,该方法使用两个自变量:来自 HTTP 响应 HTML 的文本的下一行文本,以及包含名称/值对的映射,这些名称/值对可用来替换找到的令牌。在执行该操作的过程中,可能会向令牌“映射”里添加新的值或删除一些值,文本行也可能在传递到树中的下一操作之前被更改。令牌是通过以“@”字符包围令牌值名称来定界的,如 @TOKEN@。令牌可用于各种筛选器操作值,也可以用作存储处理响应 HTML 时收集的数据的变量;例如,可创建一个令牌来存储有关响应 HTML 是否使用了 <title> 标签。还可以对这些令牌变量执行条件测试,以确定对文本行执行操作的筛选器操作流。
操作分为三类:文本操作、条件测试和令牌设置。
文本操作
以下操作可对传递到操作的文本执行,并且可将该文本的变更版本传递到下一操作中。(注意:单击下表左边栏中的链接后,可能需要刷新才能访问登录页面。)
<replace> | ReplaceOperation 操作使用正则表达式模式来匹配和替换响应 HTML 行中的文本。ReplaceOperation 支持正则表达式后向引用和同时在模式字符串和替换字符串中使用令牌。 |
<insert> | InsertOperation 可将文本插入 HTML 行的任何位置或文本有编号的行的前面。响应 HTML 开头和末尾的特殊行号值是“first”和“last”。InsertOperation 支持同时在设置的任何模式字符串和要插入的字符串中使用令牌。 |
<delete> | DeleteOperation 可用来从响应中删除一行文本。一般只有在达到某种条件时才执行删除操作。 |
条件操作
以下操作在确定是否对文本行执行了子操作中起着监视作用。条件操作对响应文本行和令牌值执行条件测试。如果通过条件测试,则执行条件操作的子操作。
<match> | MatchOperation 会尝试使用正则表达式模式匹配响应文本行。匹配模式的任何子操作都会对在响应文本行中找到的每个匹配项执行,而不是对整个文本行本身执行。MatchOperation 支持后向引用和在正则表达式模式中使用令牌。 |
<if> | 测试文本行或令牌值是否符合逻辑测试条件。IfOperation 支持的测试有“exists”、“contains”和“equals”。如果 IfOperation 测试成功,将对整个文件行执行一遍 IfOperation 的子操作。 |
令牌设置操作
以下操作可以响应文本行中提取值,并将这些值设置为令牌映射中的变量。
<set> | 将令牌值设置到令牌“映射”中。SetOperation 设置的值可包括令牌值。 |
<grab> | GrabOperation 从与正则表达式模式匹配的文本行中取第一个字符串段并将其设置到令牌“映射”中。 |
<grabtag> | 此操作将抓取不跨越多行的标签的内部 HTML。例如,该操作可能会取文档头中 <title> 标签之间的值。 |
<grabattribute> | GrabAttributeOperation 取标签的属性值并将其设置到令牌“映射”中。 |
<graball> | 取整个文本行并将其设置到令牌“映射”中。 |
<delete> | 从令牌“映射”中删除令牌值。 |
其它操作
<echo> | 一旦执行此操作,将回送标签的内容。如果日志记录级别设置为“info”或“debug”,则回送的消息将仅显示在日志中。回送消息可能包含令牌值。 |
创建自定义筛选器操作
如果开发人员想对以上操作列表中未提到的响应 HTML 执行某种操作,PTPortletFilter 支持使用自定义 FilterOperation 对象。要创建自定义操作,开发人员必须创建 FilterOperation 类的子类并实现 execute() 方法。此外,开发人员可能要覆盖该类的 init() 方法,以对操作执行任何初始化。
要在 PTPortletFilter 配置 XML 文件中声明自定义操作,开发人员必须将以下 XML 添加到文件中:
<httpfilter> <operations> ... <operation class="com.plumtree.remote.fitler.operations.MyCustomOperation"> <property name="property1">some value</property> <property name="property2">some other value</property> <operations> <set> <token>foo</token> <value>bar</value> </set> </operations> </operation> ... </operations> </httpfilter>
请注意,FilterOperation 的所有子类都支持内建的子操作,以上示例添加了自定义操作 MyCustomOperation 的 <set> 操作。但是,因为子操作的执行通常依赖于父操作的成功执行,如果创建自定义 FilterOperation 的开发人员想要对响应文本执行子操作,他们必须在 execute() 方法中调用 executeSubOperations() 方法。
标准令牌值
传递到 FilterOperation.execute() 方法中的令牌“映射”中包含几个由 PTPortletFilter 自动设置的令牌值。此外,还可以对 PTPortletFilter 进行设置,使特定设置类型(Admin、Community、Portlet 和 User 等)的所有值都可包括到令牌“映射”中。这样,筛选器操作可以引用作为被 PTPortletFilter 转换的 portlet 的部分首选项的令牌值。
标准令牌值包括:
@LINE@ | 筛选器操作正在处理的当前行号。 |
@TEXT@ | 当前正在处理的响应 HTML 行的原始文本。 |
@RESULT@ | 对传递到其中的文本(通常是来自 HTTP 响应的已修改文本行)执行的最后操作的结果。 |
@PORTLETID@ | 当前 portlet 的 ID |
@COMMUNITYID@ | 如果有,则为当前社区的 ID。 |
@PAGEID@ | 如果有,则为当前页的 ID。 |
@IMAGESERVER@ | URI 或门户 imageserver |
@STYLESHEET@ | 门户 stylesheet 的 URL。 |
此外,开发人员还可以在 PTPortletFilter 的 web.xml 声明中设置初始化参数,如果参数设置为 true,则将特定设置类型的所有首选项包括到令牌“映射”中。这样可以使 portlet 设置控制 Web 应用的筛选方式。例如,可以设计一组筛选器操作,这些操作只有在令牌“映射”中找到特定首选项时才执行。这些初始化参数包括:
admin.settings.tokens | 如果设置为 true,则将 Admin 设置包括到令牌“映射”中。 |
community.settings.tokens | 如果设置为 true,则将 Community 设置包括到令牌“映射”中。 |
communityportlet.settings.tokens | 如果设置为 true,则将 CommunityPortlet 设置包括到令牌“映射”中。 |
portlet.settings.tokens | 如果设置为 true,则将 Portlet 设置包括到令牌“映射”中。 |
user.settings.tokens | 如果设置为 true,则将 User 设置包括到令牌“映射”中。 |
userinfo.settings.tokens | 如果设置为 true,则将 UserInfo 设置包括到令牌“映射”中。 |
附录 B:PortletBean API
PortletBean 为编写专在 portlet 上运行的 Java Web 应用提供一套有用的工具。PortletBean 的 API 模拟 EDK IPortletRequest 接口和即将推出的 Plumtree 门户中发布的客户端 “PTPortlet” 对象 JavaScript。PortletBean 以属性形式自动附加到每个 HttpServletRequest。要从 JSP 或 servlet 访问 PortletBean ,开发人员可以使用以下静态方法:
PortletBean bean = PTPortletFilter.getPortlet(request);
虽然这种访问 PortletBean 的方法适用于 JSP scriptlet,但不适用于从 JSP 表达式语言中访问 PortletBean。在标准 JSP 表达式语言中,可使用以下语法引用 PortletBean:
<jsp:useBean id="portlet" scope="request" class="com.plumtree.remote.filter.bean.PortletBean"/> ... <html:link href="#" onclick="${portlet.raiseEventJS['testevent']}">Click here</html:link> 或者 <html:link href="#" onclick="${requestScope.portlet.raiseEventJS['testevent']}">Click here</html:link>
最后,Java Server Faces与扩展的表达式语言包装在一起,可自动解析请求、会话或应用程序上下文中的JavaBean。
<h:outputLink value="#" onclick="#{portlet.raiseEventJS['testevent']}> <f:verbatim>Click here</f:verbatim><</h:outputLink>
用于 EDK 功能的 PortletBean API
以下方法用作符合JavaBean的EDK功能包装器,该功能由 IPortletContext、IPortletRequest 和 IPortletResponse。如前所述,使用 EDK 功能的 JavaBean 包装器的主要优势是允许在 JSP 表达式语言中使用 PortletBean。(注意:单击下面表格左边栏的链接后,可能需要刷新浏览器才能访问登录页面。)
以下函数是获取 portlet 属性值的标准 getter/setter 函数:
- String getImageServerURI()
- String getStylesheetURL()
- String getHostPageURI()
- int getPortletID()
- int getCommunityID()
- int getPageID()
- String getPortalUUID()
- TimeZone getTimeZone()
- Boolean getIsGatewayed()
- Boolean getIsInCommunity()
除标准 getter 和 setter 外,PortletBean 还允许开发人员为 portlet 获取各种用来存储 portlet 首选项值的设置集合(Admin、Community、Portlet、CommunityPortlet、User 和 UserInfo)。在 EDK 中,这些设置值以打破标准 getter/setter 设计模式的方式进行检索和设置。具体来说,就是必须从 IPortletRequest 对象检索 portlet 设置并在 IPortletResponse 对象上进行设置。遗憾的是这种方式使得我们很难在 JSP 表达式语言中无缝地获取和设置 portlet 设置。要解决此问题,需创建一个 SettingsBean 对象,以提供 java.util.Map 接口来获取设置值并将这些值设置到特定设置集合中。SettingsBean 会扩展“映射”并覆盖标准的 put(Object key, Object value) 方法,使输入到“映射”中的项实际上设置在 IPortletResponse 上。以下方法允许用户访问每个 portlet 设置类型的 SettingsBean 对象:
- Map getAdminSettings()
- Map getCommunitySettings()
- Map getPortletSettings()
- Map getCommunityPortletSettings()
- Map getUserSettings()
- Map getUserInfoSettings()
SettingsBean 在 Java Server Faces 应用中尤其有用。将设置投射为“映射”可使 JSF 组件值绑定获取和设置不同的设置。简单地说,值绑定是具有 JavaBean 属性值(或本例中的“映射”条目)的 UI 组件的值之间的显式连接。例如,以下 JSF 代码:
<h:inputText value=" #{portlet.adminSettings['my.pref.value']}">
确保显示时,标签生成的文本输入字段会显示自动填充到文本字段的 Admin 设置值“my.pref.value”。当文本字段所在的表单发送到服务器后,值绑定会自动从请求获取提交的文本字段值并将其设置为“my.pref.value” Admin 设置的值。
用于客户端功能的 PortletBean API
PortletBean 还提供了生成客户端JavaScript的方法,这些JavaScript可包含在 HTML 标签处理程序或内嵌于JSP 的脚本中。PortletBean 生成的脚本使得访问适应性 portlet 功能更为简单,从而方便地创建动态的多 portlet 应用。就当前版本而言,PortletBean API 支持三种类型的适应性 portlet 操作:触发 PCC 事件,获取会话首选项和设置会话首选项。PCC 事件是JavaScript事件,它由 portlet 触发并由在客户端 PCC 对象上注册的事件处理程序侦听。这些事件侦听程序可位于触发事件的 portlet 或者其它 portlet 中。
为 PortletBean 生成 API 的 JavaScript 具有以 JSP 表达式语句和 JSP scriptlet 生成 JavaScript 的方法。因此,开发人员可使以下类型的调用以 JSP scriptlet 生成 JavaScript:
<%= portlet.raiseEventJS("eventname") %> <%= portlet.raiseEventJS("mynamespace", "eventname") %> <%= portlet.raiseEventJS("mynamespace", "eventname", javaMap) %> <%= portlet.getSessionPrefJS("prefname") %> <%= portlet.getSessionPrefJS("namespace","prefname") %> <%= portlet.setSessionPrefJS("prefname","value") %> <%= portlet.setSessionPrefJS("namespace","prefname","value") %>
并使其生成以下 JavaScript:
document.PCC.RaiseEvent('urn://plumtree.com/adaptiveportlets', 'eventname') document.PCC.RaiseEvent('mynamespace', 'eventname') document.PCC.RaiseEvent('mynamespace', 'eventname', {key:'value', key2:'value2'}) document.PCC.GetSessionState('urn://plumtree.com/adaptiveportlets', 'prefname') document.PCC.GetSessionState('mynamespace', 'prefname') document.PCC.SetSessionState('urn://plumtree.com/adaptiveportlets', 'prefname', 'value') document.PCC.SetSessionState('mynamespace', 'prefname', 'value')
PortletBean 也具有生成属性的 JavaScript,可执行与以上 scriptlet 相同的功能,但使用的是 JSP 表达式语言。以下表达式语句也可生成上述 JavaScript:
${portlet.raiseEventJS['eventname']} ${portlet.raiseEventJS['mynamespace, eventname']} ${portlet.raiseEventJS['mynamespace, eventname, {key:value, key2:value2}']} ${portlet.getSessionPrefJS['prefname']} ${portlet.getSessionPrefJS['mynamespace, prefname']} ${portlet.setSessionPrefJS['prefname, value']} ${portlet.setSessionPrefJS['mynamespace, prefname, value']}
PortletBean 生成此 JavaScript 的方法是使用 JSP 表达式语言的属性生成机制。从技术上来说,表达式语言语句 ${portlet.getSessionPrefJS['mynamespace, prefname']} 读作“从包含在 PortletBean 中的 GetSessionPrefJS JavaBean 中获取名为‘mynamespace, prefname’的属性”。PortletBean 将‘mynamespace, prefname’属性名称字符串解析为 Java 对象自变量数组,而不是根据属性名称直接查找。例如,先将脚本字符串‘mynamespace, eventname, {key:value, key2:value2}’转换为具有两个字符串的对象数组和一个 Java 映射。然后将这些 Java 对象转换为相应 JavaScript 字符串以包含在客户端 JavaScript 的 document.PCC.RaiseEvent() 方法中。
这些参数字符串的语法规则如下:
- 除了对字符串定界的符号外,参数字符串中不能使用单引号或双引号。例如,portlet.raiseEventJS['foo"hello"bar'] 是无效语句。
- 以花括号 ({}) 括起来的参数表示 JavaScript 关联数组,主要是关键字/值对散列。括号内的对以逗号分隔。关键字和值以字符“:”分隔,如 {key:value, key2:value2}。所有值都假定为字符串。
- 以方括号([])括起来的参数表示 JavaScript 数组。数组中的元素以逗号分隔,如 [first,second,third]。所有元素都假定为字符串。
- 以圆括号括起来的参数表示 JavaScript 对象引用。圆括号内的字符串将按原样显示,从而正确地引用页面上的 JavaScript 对象。例如,表达式语句 ${portlet.raiseEvent['ns, evtname, (window.event)']} 将生成以下 JavaScript:document.PCC.RaiseEvent('ns', 'evtname', window.event)。如果有圆括号,则生成的 JavaScript 为:document.PCC.RaiseEvent('ns','evtname','window.event'),只是简单地将字符串“window.event”作为事件对象而不是实际的 JavaScript 事件对象来传递。
- 所有其它参数均视为字符串
有关 PortletBean 生成的 JavaScript,必须注意一点:如果使用 <portlet:group> 标签来设置共享名称空间,并且没有指定名称空间自变量,则该名称空间将自动插入到生成的 JavaScript 中。将来的 Java Portlet Tools 版本将添加更多适应性 portlet JavaScript 生成器,为执行随处刷新和其它高级适应性 portlet 功能生成客户端 API JavaScript 方法。
本文地址:http://www.45fan.com/dnjc/69871.html