开发 JSR 168 portlet

为了查看如何使用 Pluto 作为 portlet 的 JSR 168 兼容性测试平台,需要一个 portlet 来进行测试。在此练习中,您将创建一个简单的 portlet,它根据用户的输入,把文本框中的内容转换为全是大写字符或全是小写字符

开发 JSR 168 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

开发 JSR 168 portlet

现在必须为 portlet 应用程序输入布局信息 —— 一个非常不重要的任务,因为在 portlet 应用程序中只有一个 portlet。正如在图 5 中可以看到的,您告知 Pluto portlet 有一行和一列,然后单击 Submit 向服务器提交此选择:


图 5.为 portlet 应用程序输入页面布局信息

开发 JSR 168 portlet

接下来,必须定义 portlet 出现在 portlet 应用程序页面布局的何处。通过把 portlet 应用程序中的所有 portlet 映射到刚才定义的行和列来完成此任务。因为只有一个 portlet 需要部署并且您先前选择了一行和一列的页面布局,所以在该位置输入 ChangeCase portlet 并单击 Submit,如图 6 所示:


图 6. 映射 portlet 的位置
开发 JSR 168 portlet

为了部署 portlet,您可以选择重新启动 Pluto 或者热部署 包含 portlet 的 portlet 应用程序,如图 7 所示:


图 7. 热部署 portlet

开发 JSR 168 portlet

一个具有 portlet 应用程序名称(Change Case)的链接出现在侧栏。单击该超链接,现在将看到 portlet 应用程序,其中包含 portlet,如图 8 所示。这时应该与 Change Case portlet 交互并确保它的功能与预期的一样。如果与预期的一样,您就可以确定此 portlet 与 JSR 168 兼容。


图 8. 部署到 Pluto 的 Change Case portlet

开发 JSR 168 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 工具。

你可能感兴趣的:(jsp,Web,xml,IBM,websphere)