为了查看如何使用 Pluto 作为 portlet 的 JSR 168 兼容性测试平台,需要一个 portlet 来进行测试。在此练习中,您将创建一个简单的 portlet,它根据用户的输入,把文本框中的内容转换为全是大写字符或全是小写字符
我们从查看清单 3 所示的 portlet.xml 文件开始。portlet.xml 文件是包含关于 WAR 文件中捆绑的 portlet 的配置细节的描述符文件。
清单 3. portlet.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
id="com.ibm.changecase.ChangeCasePortlet">
<portlet>
<portlet-name>ChangeCase</portlet-name>
<display-name>Change Case Portlet</display-name>
<portlet-class>com.ibm.changecase.ChangeCasePortlet</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>ChangeCasePortlet</title>
</portlet-info>
</portlet>
</portlet-app>
清单 3 中的 <portlet-app> 标记定义了 XML 模式定义和 portlet 应用程序 的 ID。一个 portlet 应用程序可以包含零个或多个 portlet。使用 <portlet> 标记来定义 portlet 应用程序中的单个 portlet:
<portlet-name> —— 提供一个名称,在内部或由程序使用该名称来引用 portlet。
<display-name> —— portlet 的缩写名,用来在 GUI 工具中显示 portlet 名称,它随 portlet 容器的不同而不同。
<portlet-class> —— 充当 portlet 控制器的类。
<supports> —— 这些标记定义 portlet 支持的 portlet 模式和 mime 类型。
<title> —— 可以在 portlet.xml 中定义 portlet 的首选标题。但是,如何使用该标题取决于 portlet 容器。
清单 4 显示的是 portlet.xml 中引用的 com.ibm.changecase.ChangeCasePortlet portlet 类。此类必须实现 javax.portlet.Portlet 接口,但幸运的是,您不必直接实现 Portlet 接口。JSR 168 为 javax.portlet.Portlet 接口定义了一个称为 javax.portlet.GenericPortlet 类的默认实现。com.ibm.changecase.ChangeCasePortlet 类继承 GenericPortlet 类。
清单 4. ChangeCasePortlet 类
package com.ibm.changecase;
import java.io.*;
import javax.portlet.*;
/**
*
* A sample portlet based on GenericPortlet
*
*/
public class ChangeCasePortlet extends GenericPortlet {
private static String VIEW_JSP = "/view.jsp";
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletContext context = getPortletConfig().getPortletContext();
context.getRequestDispatcher(VIEW_JSP).include(request, response);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
//Do Action Handling here.
}
}
注意重写 doView() 和 processAction() 方法的选择。每当出现 portlet 操作时都会调用 processAction() 方法。当用户处于 portlet 的视图模式时调用 doView() 方法。JSR 168 支持其他的模式,例如帮助模式和编辑模式。但是,如果回过头看一下 清单 3,在 <supports> 部分可以看到 portlet 只支持视图模式。
现在仔细地看一下清单 4 中的 doView() 方法。与 Java servlet 编码中一样,portlet 编码中也经常使用模型-视图-控制器 (MVC) 设计模式。因此,在 portlet 中,把表示的职责转交给了 view.jsp。或者,也可以使用 prinltn 逻辑在 doView() 方法中实现视图。
response.getWriter().println("<p>Hello World</p>()");
这种方法的问题是,图形用户界面的设计者需要具有 portlet 技术的知识来编写 doView() 方法。JSP 开发人员从复杂的 Java 开发中解放出来,能够集中精力开发前端界面。清单 5 显示的是 view.jsp:
清单 5. view.jsp
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%>
<portlet:defineObjects />
<%String textBox = renderRequest.getParameter("textBox");
if (textBox == null)
textBox = "";
String caseString = renderRequest.getParameter("case");
boolean isUpperCase = true;
if ((caseString != null) && (caseString.equals("false"))) {
isUpperCase = false;
}
String errorMessage = renderRequest.getParameter("errorMessage");%>
<%if (errorMessage != null) {%>
<p><%=errorMessage%></p>
<%}%>
<FORM name="<portlet:namespace/>caseform" action="<portlet:actionURL/>">
<INPUT type="text" name="textBox" size="20" value="<%=textBox%>">
<p><INPUT type="radio" name="case" value="lowercase"
<%if (!isUpperCase) {%> checked="checked" <%}%>>To Lowercase</p>
<p><INPUT type="radio" name="case" value="uppercase"
<%if (isUpperCase) {%> checked="checked" <%}%>>To Uppercase</p>
<INPUT type="submit" name="<portlet:namespace/>submitCase"
value="Change Case"></FORM>
首先,注意在 view.jsp 中定义 portlet 标记库。这是 JSP 解析器识别 portlet 标记所必需的。您使用的第一个 portlet 标记是 <portlet:defineObjects/>。此标记允许访问 renderRequest、renderResponse 和 portletConfig 对象。使用 renderRequest 对象使您具有访问 requestParameters 的权利。portlet 类通过 request 参数向 view JSP 传递值。
接下来,在 view.jsp 中创建一个向 portlet 类发送表单数据的表单。为了发送表单数据,必须创建一个 actionURL,它使 ChangeCasePortlet portlet 类的 processAction() 方法被调用。使用 <portlet:actionURL/> 标记创建 actionURL。 注意,在 view.jsp 中将文本框和单选按钮的值设置为服务器传回 JSP 的值。因此,view.jsp 负责处理请求输入和显示 portlet 的响应。
单击表单的 Submit 按钮会调用 portlet 的 processAction() 方法,如清单 6 所示。processAction() 从 view.jsp 接收一个 ActionRequest 对象作为输入。
清单 6. processAction 方法
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
String newCase = request.getParameter("case");
String textBox = request.getParameter("textBox");
String errorMessage = null;
boolean isUpperCase = true;
if ((newCase!=null) && (newCase.equals(ChangeCaseConstants.LOWER_CASE)))
isUpperCase = false;
else if ((newCase == null) || (newCase==ChangeCaseConstants.UPPER_CASE))
errorMessage = "Error no case selected! Select a case.";
if (textBox !=null) {
if (isUpperCase)
textBox = textBox.toUpperCase();
else
textBox = textBox.toLowerCase();
response.setRenderParameter("textBox", textBox);
} else
errorMessage = "Error, text in the text box is invalid";
response.setRenderParameter("case", Boolean.toString(isUpperCase));
if (errorMessage != null) {
response.setRenderParameter("errorMessage",errorMessage);
}
}
ActionRequest 对象包含输入到表单中的数据。为了检索表单数据,可使用 getParameter() 方法。在 processAction() 方法中,也要执行业务逻辑,确定用户是想要大写形式还是小写形式的输出。根据该逻辑,把输入的文本转换成想要的大小写形式并发送给用户。使用 setRenderParameter() 方法把数据发送给视图。
编译并打包 JSR 168 portlet
现在已经开发了 portlet,需要把它转换成已编译的形式,并为了部署到 Pluto 将它打包。首先,确保 portlet-api-1.0.jar 在 CLASSPATH 中。然后使用 javac 编译器编译 ChangeCaseConstants.java 和 ChangeCasePortlet.java:
javac ChangeCaseConstants.java
javac ChangeCasePortlet.java
接着需要为 WAR 文件创建所需要的文件夹结构,WAR 文件是归档文件,通过它把 portlet 部署到 portlet 容器。把刚才编译的两个类放在 classes\com\ibm\changecase 目录中。
为了构建 WAR 文件,需要以下的目录结构,如清单 7 所示:
清单 7. 用于部署的目录结构
changeCaseWAR\
META-INF
MANIFEST.MF
WEB-INF
classes
com
ibm
changecase
ChangeCaseConstants.class
ChangeCasePortlet.class
lib
tld
portlet.tld
portlet.xml
web.xml
index.html
view.jsp
注意,清单 7 引入了 4 个我们还没有讨论过的文件:
portlet.tld:这是 portlet 标记库。如果回想一下,您曾经用 <portlet:defineObjects /> 之类的引用在整个 JSP 中都用到过 portlet 标记库。portlet 标记库在 Apache-SVN 代码库中可以得到(参见 参考资料)。
MANIFEST.MF:因为这里不存在外部依赖,所以这个清单文件除了 Manifest-Version: 1.0 之外什么也不包含。
index.html:如果由于某些原因上下文根被直接访问,则会显示 index.html。在 index.html 中可以有任何正确格式的 HTML。
web.xml(如清单 8 所示):它定义包含单个 portlet 的 Web 应用程序。
清单 8. 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 id="WebApp_ID">
<display-name>CasePortlet</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<taglib>
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>tld/portlet.tld</taglib-location>
</taglib>
<taglib id="PortletTLD">
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>/WEB-INF/tld/std-portlet.tld</taglib-location>
</taglib>
</web-app>
一旦生成了 MANIFEST.MF、portlet.tld、web.xml 和 index.html,就可以使用 JAR 实用工具将清单 7 所示的结构归档到一个 WAR 文件中:
C:\temp>jar -cvf changeCase.war
结果是一个叫做 changeCase.war 文件,用于部署到 JSR 168 兼容的门户。
确保 portlet 是 JSR 168 兼容的
现在使用 Apache Pluto 来查明 portlet 是否能通过 JSR168 兼容性的最终测试。
确保 Apache Pluto 启动并运行,然后导航到 Pluto 主页(http://localhost:8080/pluto/portal)。单击侧栏上的 Admin。应该看到 Deploy War portlet 显示在管理页面上,如图 4 所示。单击 Browse 并导航到在前一节中放置在一起的 changeCase.war 文件的位置,然后单击 Submit。
图 4. Pluto 的 Deploy War portlet
现在必须为 portlet 应用程序输入布局信息 —— 一个非常不重要的任务,因为在 portlet 应用程序中只有一个 portlet。正如在图 5 中可以看到的,您告知 Pluto portlet 有一行和一列,然后单击 Submit 向服务器提交此选择:
图 5.为 portlet 应用程序输入页面布局信息
接下来,必须定义 portlet 出现在 portlet 应用程序页面布局的何处。通过把 portlet 应用程序中的所有 portlet 映射到刚才定义的行和列来完成此任务。因为只有一个 portlet 需要部署并且您先前选择了一行和一列的页面布局,所以在该位置输入 ChangeCase portlet 并单击 Submit,如图 6 所示:
图 6. 映射 portlet 的位置
为了部署 portlet,您可以选择重新启动 Pluto 或者热部署 包含 portlet 的 portlet 应用程序,如图 7 所示:
图 7. 热部署 portlet
一个具有 portlet 应用程序名称(Change Case)的链接出现在侧栏。单击该超链接,现在将看到 portlet 应用程序,其中包含 portlet,如图 8 所示。这时应该与 Change Case portlet 交互并确保它的功能与预期的一样。如果与预期的一样,您就可以确定此 portlet 与 JSR 168 兼容。
图 8. 部署到 Pluto 的 Change Case portlet
Change Case Portlet 将能够运行在任何支持 JSR 168 portlet 标准的 Portlet 容器中。
参考资料
学习
您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
“JSR 168 - An Introduction to the Portlet Specification”:更多地了解 JSR 168。
Apache Pluto:访问 Pluto Web 站点。
JSR 168: Portlet Specification 和 SR 286: Portlet Specification 2.0:由 Java Community Process 开发和维护的 portlet 规范。
“使用 WebSphere Portal V5.1 在 IBM Portlet 和 JSR 168 Portlet 间共享信息”(Stefan Hepper 和 Jerry Zheng,developerWorks,2006 年 2 月):编写一个自定义的 portlet 服务以允许 IBM portlet 和 JSR 168 portlet 共享作为属性的信息。
“使用 WebSphere Portal V5.1 在 JSR 168 Portlet 中缓存数据”(Stefan Hepper 和 Jerry Zheng,developerWorks,2005 年 8 月):学习如何在 JSR 168 portlet 中缓存数据以避免不必要的后端请求。
“将 WorldClock portlet 从 IBM Portlet API 转换到 JSR 168 portlet API”(Franziska Paeffgen 和 Birga Rick,developerWorks,2004 年 12 月):了解如何将最为 IBM WebSphere Portal 专有 Portlet API 开发的 portlet 转换为使用 JSR 168 标准 portlet API 的 portlet。
Verifying Apache HTTP Server Releases:有关验证来自 Apache Web 站点的下载文件的更多信息。
Java 技术专区:关于 Java 编程的各个方面的数百篇文章。
获得产品和技术
Apache Pluto:下载 Pluto。
portlet 标记库:从 Apache-SVN 代码库下载 portlet 标记。
Apache Jetspeed:一个开放源码的 portlet 容器。
Java 5.0 SDK:Pluto 需要 Java 5。
IBM 的 WebSphere Portal Server Version 5.1:支持 JSR 168 标准的商业解决方案。
GnuPG:一个免费的 PGP 工具。