编写 portlet

来源:互联网 发布:北京师范大网络教育 编辑:程序博客网 时间:2024/05/16 20:29

本节向您介绍有关创建 portlet 的概念,从一个彻底修改过的简单的 portlet 开始。使用有关 Portlet API 的节和 Javadoc 作为参考。

如果您熟悉为 WebSphere Portal Family 版本 2.1 产品开发 portlet,应该可以注意到 Portlet API 已作了显著改进。请参阅迁移 portlet 以获取更多信息。

创建简单 portlet 实例

WebSphere Portal 包括可在 wp_root/install 目录中找到的下列预定义的 portlet 集合。

Portlet文件名
文件服务器 portlet,提供静态 HTML 文件fileserverportlet.war
Servlet 调用器 portlet,将 servlet 作为 portlet 调用ServletInvoker.war
JSP portlet,服务 JSPjspserverportlet.war
CSV 查看器,显示数据以逗号分隔值的格式排列的文件csv.war
RSS portlet,显示提供使用丰富站点摘要格式的数据的远程 URL。xslt.war
OCS 查看器,显示“打开内容辛迪加”通道供给。ocsviewer.war

这些 portlet 提供了基本功能(例如提供静态 HTML 服务或动态 JSP 文件服务),这样不必编写代码就可以把这些功能集成到您的门户网站中。可以通过使用门户网站的管理来创建和部署这些 portlet 任意数量的实例。请参阅 Portlet 管理以获取更多信息。

使用 portlet 文件服务器服务于静态内容

portlet 文件服务器可以在它到 portlet 窗口的路径下显示任何 HTML 文件。缺省情况下,它显示位于 WAR 文件的 FileServerPortlet/html 目录中的 test.html。要其它内容服务,可将 HTML 文件添加到 portlet 文件服务器的路径下并且在门户网站管理中使用 Portlet 管理修改它的 url 参数至新建的文件中。url 参数的根指向 was_root/installedApps/portlet_id.ear/FileServer.war 目录,此目录的部署中 portlet_id 是创建的唯一标识。可以创建多个 portlet 实例,每个实例从各自的路径服务不同的内容。

portlet 样本

提供了一组样本 portlet,它们演示了 Portlet API 的功能。这些 portlet 由 wp_root/dev 目录下的 bookmark_samplets.zip 文件提供。这些样本 portlet 包含以下文件:

bookmark0.war演示如何获取、设置和除去某一具体 portlet 的变量。然而,这个 portlet 不产生任何输出。
bookmark1.war使用 PortletResponse 写输出到门户网站。
bookmark2.war实现侦听器(例如,在页面中的 portlet 标记之前插入 HTML 输出)。
bookmark3.war从 PortletConfig 检索参数和从 PortletData 与 PortletSetting 检索属性。
bookmark4.war从资源束获取本地化的文本。
bookmark5.war包含用于 portlet 的视图方式的 JSP。
bookmark6.war包含用于编辑方式和实现 ActionListener 的 JSP,以便用户添加书签。

下面几节描述如何创建一个简单的 Hello World portlet,以及如何编译、封装和部署这个 portlet。没有为 Hello World 提供文件样本;您必须使用文本中提供的示例来创建它。然而,在 wp_root/dev 目录下提供了 HelloWorld2.war,它演示了如何使用 JSP 来提供 portlet 标记。

设置 portlet 开发环境

在尝试本节所讨论的任何类和样本之前,您应该设置一个让编写、编译和测试 portlet 各任务更易于实现的环境。 WebSphere Portal 产品软件包包含下列开发工具:

  • WebSphere Studio Application Developer 版本 4.02
    支持电子商务应用程序终端到终端的开发、测试和部署。
  • 门户网站工具箱
    门户网站工具箱插入到 Application Developer 中并为创建 portlet 应用程序项目和创建基于 PortletAdapter 和 MVCPortlet 类的 portlet 以及基于模板的 portlet(ServletInvokerPortletJSPPortlet XSLTPortlet)提供向导。

    确保将您的系统 PATH 环境变量设置为使用 JDK 1.3.0,这是 WebSphere Application Server 所使用的 JDK。需要使用该 JDK 级别来编译类文件以便在 portlet 中使用。

除了开发工作站之外,还应设置门户网站服务器以便通过安装下列组件来发布和测试您的 portlet 应用程序项目:

  • WebSphere Portal(开发选项)
  • WebSphere Application Server - 高级版,单服务器

Hello World portlet

Hello World portlet 提供编写您的第一个 portlet 的介绍。Hello World 扩展 AbstractPortlet 帮助器类,并提供一个 portlet 必需的最少方法。它使用 PortletResponse 提供到门户网站页面的简单输出。

示例:Hello World portlet


package com.ibm.wps.samples.HelloWorld;import org.apache.jetspeed.portlet.*;import org.apache.jetspeed.portlets.*;import java.io.*;public class HelloWorld extends AbstractPortlet{  public void init(PortletConfig portletConfig) throws UnavailableException  {    super.init( portletConfig );  }  public void service( PortletRequest portletRequest,                         PortletResponse portletResponse)         throws PortletException, IOException  {    PrintWriter writer = portletResponse.getWriter();         writer.println("<p>Hello Portal World!</p>");  }}

编译 Java 源代码

使用 WebSphere Application Server 提供的 JDK 编译 Java 源文件。编译 Java 源之前,为编译器设置 CLASSPATH 以便查找 portlet 使用的任何 portlet 软件包的 JAR 文件。下列 JAR 文件总是应该在 CLASSPATH 中设置以便编译:

   was_root/lib/app/portlet-api.jar;   was_root/lib/app/wpsportlets.jar;   was_root/lib/app/wps.jar;

其中 was_root 是 WebSphere Application Server 的安装目录。此外,如果需要 servlet 功能的任何类,添加下列:

   was_root/lib/j2ee.jar;   was_root/lib/websphere.jar;

然后,使用到 Java portlet 源代码的全限定路径来编译 portlet。

  javac -classpath %CLASSPATH% com.ibm.wps.samples.HelloWorld.java

要在编译之后测试 Hello World,必须先把它封装成一个 WAR 文件,并把它安装到门户网站服务器。然而,第一步是把 portlet 封装成 JAR 文件格式。要创建名为 HelloWorld.jar 的 JAR 文件,输入以下命令:

   jar -cf HelloWorld.jar HelloWorld.class

请参阅 JDK 文档,以获取有关 JAR 命令的更多信息。

封装和部署 portlet

完成开发和测试 portlet 之后,portlet 便已准备好以 Web 应用程序归档WAR 文件的形式部署到门户网站服务器。您还需要把您的 portlet 封装成一个 WAR 文件以便在门户网站服务器中测试它。

WAR 文件格式包含组成单个 portlet 的 Java 类和资源。资源可以是图像、JSP 文件或包含翻译的消息文本的属性文件。除了 portlet 代码和资源以外,WAR 文件还包含 Web 应用程序部署描述符(web.xml)和 portlet 部署描述符(portlet.xml),该文件包含门户网站服务器安装和配置 portlet 所需的信息。将 portlet 的类、资源和描述性信息封装到单个文件,使得 portlet 的分发和部署更容易。

WebSphere Portal 包含一个用于安装、卸载和更新 portlet 的管理 portlet。在 WAR 文件中包含 portlet 的好处是可以动态下载和安装。门户网站管理员可以从因特网下载 WAR 文件,然后使用门户网站管理界面把该 portlet 安装到 WebSphere Portal。该 portlet 已为使用准备就绪,不需要重新启动服务器。

要在 WAR 文件中封装 portlet,请按照这些步骤操作:

  1. 创建部署描述符
  2. WAR 文件目录结构排列 portlet 文件。
  3. 使用 JAR 实用程序来封装文件

Hello World 的部署描述符

以下样本可与 Hello World portlet 一起封装。请参阅部署描述符以获取有关这个主题的完整信息。

Web 应用程序部署描述符:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"           "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd"><web-app id="WebApp_504848313">  <display-name>Hello World Portlet Application - Portlet Sample #1</display-name>    <servlet id="Servlet_439329280">        <servlet-name>HelloWorld</servlet-name>        <servlet-class>com.ibm.wps.samples.HelloWorld</servlet-class>      </servlet>          <servlet-mapping id="ServletMapping_439329280">        <servlet-name>HelloWorld</servlet-name>        <url-pattern>/HelloWorld/*</url-pattern>      </servlet-mapping></web-app>

Portlet 部署描述符:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE portlet-app-def PUBLIC "-//IBM//DTD Portlet Application 1.1//EN"                                 "portlet_1.1.dtd"><portlet-app-def>  <portlet-app uid="504848313">    <portlet-app-name>Hello World Portlet Application - Portlet Sample #1</portlet-app-name>    <portlet href="WEB-INF/web.xml#Servlet_439329280" id="Portlet_439329280">        <portlet-name>HelloWorld</portlet-name>  <cache>        <expires>0</expires>        <shared>no</shared>  </cache>      <allows> <minimized/> </allows>      <supports>      <markup name="html">        <view/>        </markup>      </supports>    </portlet> </portlet-app>  <concrete-portlet-app uid="640682430">    <portlet-app-name>Concrete Hello World Portlet Application - Portlet Sample #1</portlet-app-name>      <context-param>        <param-name>Portlet Master</param-name>        <param-value>yourid@yourdomnain.com</param-value>      </context-param>      <concrete-portlet href="Portlet_439329280">        <portlet-name>HelloWorld</portlet-name>        <default-locale>en</default-locale>         <language locale="en_US">           <title>Hello World - Sample Portlet #1</title>           <title-short>Hello-World</title-short>           <description>Hello World - Sample Portlet #1</description>           <keywords>portlet hello world</keywords>         </language>       </concrete-portlet>    </concrete-portlet-app></portlet-app-def>

WAR 文件目录结构

封装您的 portlet 之前,您必须按照此处描述的目录结构排列类文件和资源。portlet 应用程序是作为结构化的目录层次结构而存在。

/
portlet 文件结构的根目录。
/images
portlet 必需的任何图像的位置。
/WEB-INF
所有受保护资源的位置。/WEB-INF 目录用于存储封装 portlet 所需的 portlet 描述符文档、所有运行时可执行的 JAR 文件以及类。

portlet 信息目录不是应用程序公用文档树的部件。驻留在 /WEB-INF 中的文件并不直接服务客户机。

/WEB-INF/lib
存储 portlet JAR 文件的位置。
/jsp
JSP 文件的位置。
/WEB-INF/classes
portlet 类文件的位置。个别的类文件应该存储到 /WEB-INF/classes 下反映类软件包的目录结构中。例如,软件包 com.ibm.wps.samples.HelloWorld 中类 HelloWorld.class 应该存储到 /WEB-INF/classes/com/ibm/wps/samples/HelloWorld.class .
/META-INF
manifest 文件的位置,manifest.mf。manifest 在标准 JAR 文件格式中为 Java 1.3 规范所定义。/META-INF 目录中的内容不是服务于客户机的。
多语言视图选择

WebSphere Portal 支持门户网站和 portlet 的国际化。那些包含的大部分文本的 JSP(例如,帮助 JSP)可以直接翻译。这意味着文本包含在 JSP 而不是资源束中。对于不使用资源束的 JSP,您需要把它存储到相应的本地化位置。当 portlet 为输出 portlet 的内容使用 JSP 时,门户网站将基于目标浏览器和语言及国家或地区的目标浏览器的设置搜索和选择正确的 JSP。要使用 PortletContext.include() 函数把 JSP 包含进 portlet:

   getPortletConfig().getContext().include(path, portletRequest, portletResponse);
要使 portlet 能进行语言选择,必须使用下列目录结构把 portlet 的 JSP 封装到 WAR 文件中:

WEB-INF/jsp/markup_type /language _country_variant /jspname.jsp

其中:...

路径
开发者定义的路径。路径不能包含 mime-type/language_country_variant。include() 方法已经定位了也在这些目录中的正确的 JSP。
markup_type
htmlwmlchtml
language
是 JSP 的语言,例如enjade
country
是 JSP 的国家或地区,例如 US、UK 或 CA。
variant
是指不同的浏览器,例如 IE5 或 NS4。

例如,如果客户机使用的是语言特性设置为英语(美国)的 Internet Explorer 5,方法 include(/mypath/mytemplate.jsp, portletRequest, portletResponse) 让门户网站服务器按以下次序查找 JSP。

  1. /html/mypath/ie5/en_US/mytemplate.jsp
  2. /html/mypath/ie5/en/mytemplate.jsp
  3. /html/mypath/ie5/mytemplate.jsp
  4. /html/mypath/en_US/mytemplate.jsp
  5. /html/mypath/en/mytemplate.jsp
  6. /html/mypath/mytemplate.jsp
  7. /html/en_US/mytemplate.jsp
  8. /html/en/mytemplate.jsp
  9. /html/mytemplate.jsp
  10. /mytemplate.jsp

把 portlet 和资源封装进 WAR 文件

任何 JAR 实用程序都可用于构建 WAR 文件。下面是如何使用 WebSphere Application Server 提供的 JAR 实用程序的示例。


  • 要创建名称为 MailPortlet.par 的 WAR 文件并包含 /WEB-INF/images 目录中的所有文件:
       jar -cf HelloWorld.war images WEB-INF 
  • 要使用修改后的 portlet 描述符更新现有的 WAR 文件 HelloWorld.war
       jar -uf HelloWorld.war WEB-INF/portlet.xml
  • 要从 WAR 文件 HelloWorld.war 抽取 portlet 描述符:
       jar -xf HelloWorld.war WEB-INF/portlet.xml 
  • 要从现有的 WAR 文件 HelloWorld.war 抽取所有的文件:
       jar -xf HelloWorld.war  

创建 WAR 文件之后,可按 portlet 管理所述将它安装到 WebSphere Portal。

为使 portlet 应用程序和复杂的 portlet 部署容易,提供了 XML 访问为安装而调用的 portlet 配置文件。使用 portlet 配置文件允许指定位置和页面放置您的 portlet、主题和外观、支持标记和客户机,以及其它设置。这对于使用消息传递的 portlet 特别有用,因为这些 portlet 必须放置在相同的页面上。要获取更多关于 XML 访问的信息,请参阅 XML 输入和输出XML 中门户网站配置的表示法

生成标记

在第一个示例中,portlet 通过使用 Java PrintWriter 提供标记。多数标记使用 JSP 生成。当 portlet 必须变换 XML 源时,对此有一个例外。在这种情况下,portlet 可使用 XSLT 生成标记。

使用 JSP 生成标记

将 portlet 标记从 portlet 的主功能分隔开最简单的方法之一是使用 JSP。下面是用于 Hello World2 样本的编辑页面的 JSP。分隔的视图或帮助 JSP 将存在以便为支持附加的 portlet 方式提供用户界面。

当编写您的 JSP 时有几点注意事项:

  1. 为保持门户网站外观的一致性,请使用 portlet 的样式表中的 portlet 类规范。
  2. 确保包含 WebSphere Portal 的 JSP 标记库以获取管理您的 portlet 的名称空间所需的功能。此外,使用 <portletAPI:encodeNamespace> 标记来设置请求参数名称。请参阅使用 Portlet API 标记以获取更多信息。
  3. 在本示例中,Java String 对象从 portlet 的 doEdit() 方法被传递给 JSP。对于更复杂的数据传递,可能需要创建单独的对象(数据视图 bean 或散列表)来包含显示所需的数据。

示例:用于 Hello World2 的编辑方式的 JSP


<%@ taglib uri="/WEB-INF/tld/portlet.tld" prefix="portletAPI" %><portletAPI:init /><jsp:useBean id="saveURI" class="java.lang.String" scope="request" /> <jsp:useBean id="cancelURI" class="java.lang.String" scope="request" /> <jsp:useBean id="userName" class="java.lang.String" scope="request" /> <!-- build table for edit screen --> <div CLASS="wpsEditBack"> <span class="wpsEditHead">Configure Hello World Portlet</SPAN> <br> <form method="post" name="form" action="<%= saveURI %>"> <table WIDTH="100%" CELLSPACING="0" CELLPADDING="0" BORDER="0">   <tr>     <td align="right" class="wpsEditText">Enter string to display:</TD>      <td><input class="wpsEditField" size="20" type="text"                 name="<%=portletResponse.encodeNamespace("userName")%>"                 value=<%= userName %>>     </td>   </tr>   <!-- Empty row -->   <tr>    <td> </td>  </tr>   <tr>    <td class="wpsButtonText">       <input type="submit" name="save" value="Save" >       <input type="button" value="Cancel"              onClick="window.location.href='<%= cancelURI %>'" >     </td>   </tr> </table></form> </div> 

为多个设备生成标记

WebSphere Portal 的主要功能之一是它对多种设备的支持能力。WebSphere Portal 支持 PC 浏览器、i-mode 和 WAP 电话,本产品将来的版本还会支持其它设备类型。支持多种设备的挑战是根据浏览器的特征以不同的方式来输出内容。某个浏览器可接受 HTML 4.0;另一个可接受 WML;某个 WAP 电话可显示四行文字、每行 25 个字符;而另一种电话可能会有自己 PDA 风格的界面。

以下示例演示了一种方法,在生成 portlet 的标记之前,先选择与当前设备相关的标记类型。提供了 doMarkupOutput() 方法,调用这个方法来处理对于每种 portlet 方式的输出的请求。从 PortletRequest 获取的 Client 对象标识当前设备所需的标记语言。

示例:添加方法以处理不同的标记


...{  public void init (PortletConfig portletConfig) throws UnavailableException  {    super.init( portletConfig );  // Call super to do further init  }  public void doView( PortletRequest request, PortletResponse response )         throws PortletException, IOException  {    doMarkupOutput( request, response, "View" );  }  public void doHelp( PortletRequest request, PortletResponse response )         throws PortletException, IOException  {    doMarkupOutput( request, response, "Help" );  }  public void doEdit( PortletRequest request, PortletResponse response )         throws PortletException, IOException  {    doMarkupOutput( request, response, "Edit" );  }  public void doMarkupOutput( PortletRequest request,                              PortletResponse response,                              String portletModeString ) throws PortletException, IOException  {    String   markup = request.getClient().getMarkupName();    PrintWriter writer = response.getWriter();    //-------------------------------------------------------------------------------------    // Check the client device to determine the type of markup to generate    //-------------------------------------------------------------------------------------    if( markup.equalsIgnoreCase("HTML") )          // Standard HTML: text/html    {      writer.println( "<p>Hello Portal... The Portlet Mode is: "+portletModeString+" </p>" );    }    else if( markup.equalsIgnoreCase("WML") )      // WML: text/wml    {      writer.println( "<card id=/"hello/"><P>Hello Portal... " );      writer.println( "The Portlet Mode is: "+portletModeString+" </P></card>" );    }    else if( markup.equalsIgnoreCase("CHTML") )    // Compact HTML: text/html    {      writer.println( "<P>Hello Portal... The Portlet Mode is: "+portletModeString+" </P>" );    }        else // Unrecognized Markup Type Error: Throw An Exception    {      throw( new PortletException( "Unknown Markup Type") );    }  }}

使用 MVCPortlet 类

WebSphere Portal 提供 MVCPortlet 类作为 com.ibm.wps.portlets 软件包的一部分。发送输出前,扩展该类的 Portlet 不必检查客户机标记。相反,他们为每个标记类型提供控制器类。Web 应用程序描述符中的 servlet 类标记定义扩展 MVCPortlet 的空的调试文件类。控制器类被定义为初始化参数,如下例所示。

  <servlet-class>com.mycompany.myportlet.myMVCportlet</servlet-class>          <init-param>    <param-name>controller.html</param-name>    <param-value>com.mycompany.myportlet.HTMLController</param-value>          </init-param>                   <init-param>    <param-name>controller.wml</param-name>    <param-value>com.mycompany.myportlet.WMLController</param-value>          </init-param>                   <init-param>    <param-name>controller.chtml</param-name>    <param-value>com.mycompany.myportlet.CHTMLController</param-value>          </init-param>         

控制器类扩展 AbstractPortletController 并包含 portlet 代码,如 doView()、doEdit() 方法或 ActionListener(若有必要)。应用程序开发者允许您从创建 Portlet 项目向导创建 MVCPortlet。

使用持久性

使用 PortletData 对象保存、检索或删除 portlet 数据到持久性 portlet 数据。只有当 portlet 处于编辑方式下时,portlet 才可在 PortletData 对象中存储值。如果 portlet 在组页面上,则 PortletData 中保存的信息对所有 portlet 的用户都是可用的。Portlet 通过调用 PortletRequest 对象的 getData() 方法来检索对 PortletData 实例的引用。

以下 BookmarkPortlet.javabookmark6.war 的示例中,在用户在编辑页面上输入到书签的 URL 后,setAttribute() 和 store() 方法把信息保存到 PortletData。然而,首先要从 PortletData 检索用户的 URL_COUNT,以便更新 URL_COUNT。

   PortletData data = event.getRequest().getData();   String count = (String) data.getAttribute(URL_COUNT);   int i = 0;   if (count != null)    {       i = Integer.parseInt(count);    }    i++;    data.setAttribute(NAME_PREFIX + i, name);    data.setAttribute(URL_PREFIX  + i, url);    data.setAttribute(URL_COUNT      , Integer.toString(i));    try    {       data.store();    }    catch (IOException e)    {    throw new PortletException (e);    }         

只有 Java String 类型的数据才可保存到 PortletData 对象。

注:位于缺省门户网站页面的 portlet(在用户登录前)不能访问 PortletData。要防止缺省页面上出现不希望的错误,通知管理员不要在缺省页面上包含此 portlet,或者在尝试访问其属性之前测试 PortletData 的可用性。

Hello World2 样本中也使用了 PortletData,使用户能编辑问候语并将其保存为持久状态。此外,PortletContext 用于调用 JSP 以输出查看和编辑标记。

  1. 要显示的缺省问候语是从 Web 应用程序部署描述符获取的。下面演示了把初始化参数 defaultHelloString 设置为“Hello!”的描述符。.

    示例:Hello World2 的 Web 应用程序描述符


    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE web-app    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"           "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">   <web-app id="WebApp_3">      <display-name>HelloPortlet</display-name>      <servlet id="Servlet_1">         <servlet-name>Hello World2</servlet-name>         <servlet-class>com.ibm.wps.samplets.helloworld.HelloWorld2</servlet-class>          <init-param>              <param-name>defaultHelloString</param-name>              <param-value>Hello!</param-value>          </init-param>               </servlet>            <servlet-mapping id="ServletMapping_1">         <servlet-name>Hello World2</servlet-name>         <url-pattern>/Hello World2/*</url-pattern>      </servlet-mapping>   </web-app>  

    <init-param> 标记设置的配置数据是只读的,且为每个用户和每个 portlet 派生出来的具体的 portlet 维护。如果需要允许每个具体的 portlet 有不同的配置,则应该在 portlet 部署描述符的 <concrete-portlet> 标记中设置数据。

  2. doView() 方法在此 portlet 的标准显示之前接收控制。访问 PortletData 对象以获取要显示的字符串。如果用户尚未指定要显示的字符串,则将使用缺省字符串。该字符串存储在 PortletRequest 对象中,使它可用于为这个 portlet 生成查看标记的 JSP(viewJSP)。
  3. doEdit() 方法在为这个 portlet 显示编辑页面之前接收控制。一个返回 URI 被创建并使用 PortletRequest 对象传递给编辑方式的 JSP。保存操作包含在返回 URI 中,以便为这个 portlet 调用 ActionListener。门户网站在处理保存操作时把控制传递给 ActionListenerActionListener 可以在持久存储器中保留用户输入的“编辑”信息。请参阅操作事件以获取有关 ActionListener 和 portlet 操作的更多信息。

    HelloActionListener 的代码显示为下列 portlet 代码。

示例:从 PortletConfig 保存和检索数据


package com.ibm.wps.samplets.helloworld;import org.apache.jetspeed.portlet.DefaultPortletAction;import org.apache.jetspeed.portlet.*;import org.apache.jetspeed.portlet.event.*;import org.apache.jetspeed.portlets.*;import java.io.*;import java.util.*;public class HelloWorld2 extends AbstractPortlet implements ActionListener{    // Since there is a single instance of the portlet, only use instance variables    // for immutable values that are the same for all users of the portlet    private final static String viewJSP = "/WEB-INF/helloworld/html/HelloWorldView.jsp";    private final static String editJSP = "/WEB-INF/helloworld/html/HelloWorldEdit.jsp";    private String defaultString;   public void init (PortletConfig portletConfig) throws UnavailableException    {    super.init( portletConfig );        if ( getPortletLog().isDebugEnabled() ) {            getPortletLog().debug("HelloWorld: init called");        }        // The default Hello String is obtained from the portlet configuration parameters        defaultString = portletConfig.getInitParameter("defaultHelloString");    }  public void doView( PortletRequest request, PortletResponse response )         throws PortletException, IOException    {        //Get the user's name to display from persistent storage        PortletData portletData = request.getData();        String stringToDisplay = (String) portletData.getAttribute("userName");        // If this is the first time the user has accessed this portlet, then        // no display string will be found for this user in persistent storage        if (stringToDisplay == null) {            stringToDisplay = defaultString;    // set default string        }        // Add the display string to the portlet request to make it accessible by the view JSP        request.setAttribute("userName", stringToDisplay);       // Get a context for the current session for invoking the JSP        PortletContext context = getPortletConfig().getContext();        context.include(viewJSP, request, response);    }    public void doEdit(PortletRequest portletRequest, PortletResponse portletResponse )         throws PortletException, IOException    {        // Create the return URI for the edit page        PortletURI returnURI = portletResponse.createReturnURI();        // Preserve the Cancel URI in the request to make it accessible by the edit JSP        portletRequest.setAttribute("cancelURI", returnURI.toString());        // For the "Save" button the return URI must include the "Save" action        // so the Action Listener for this portlet will be invoked        PortletAction saveAction = new DefaultPortletAction("save");        returnURI.addAction(saveAction);        // Preserve the Save URI in the request to make it accessible by the edit JSP        portletRequest.setAttribute("saveURI", returnURI.toString());        //Get the user's name to display from persistent storage        String stringToDisplay = (String)portletRequest.getData().getAttribute("userName");        if (stringToDisplay == null) {            stringToDisplay = defaultString;    // none found, set default string        }        // Add the display string to the request to make it accessible by the edit JSP        // as an inital value of the input field on the edit form        portletRequest.setAttribute("userName", stringToDisplay);       // Get a context for the current session for invoking the JSP        PortletContext context = getPortletConfig().getContext();        context.include(editJSP, portletRequest, portletResponse);      }  public void actionPerformed(ActionEvent event) {        DefaultPortletAction action = (DefaultPortletAction)event.getAction();    HelloWorld2 helloPortlet = (HelloWorld2)event.getPortlet();        PortletLog log = helloPortlet.getPortletLog();        // If this is a save action, then see if the user specified a name        if (action!=null) {            if (action.getName().equals("save")) {                PortletRequest request = event.getRequest();                PortletData portData = request.getData();                String userName = request.getParameter("userName");        try {                // Save the name specified by the user                if (userName != null) {                    portData.setAttribute("userName", userName);                        portData.store();          }                } catch (AccessDeniedException ade) {                    } catch (IOException ioe) {          log.error( "<i><b>Couldn't write the user date to persistence because an I/O Error occurred.</b></i>" );        }      }    }  }}

如前面所述,Hello World2 portlet 实现了 ActionListener 以处理保存操作。用户在编辑页面上输入名称,ActionListener 的 actionPerformed() 方法从 PortletRequest 对象获取特定用户的字符串,以存储到用户的持久存储器。在返回到 portlet 的 doView() 方法之前,ActionListener 被调用,这样如果用户输入名称失败,则 ActionListener 可强制 portlet 保留在编辑方式,等待来自用户的输入。

portlet 消息传递

Portlet API 支持一张页面上的 portlet 之间的消息传递。例如,如果四个 portlets(Left、Right、Top、Bottom)是称为 Sides 的 portlet 应用程序一部分,则 portlet Left 能向 portlet Right、Top 和 Bottom 发送信息(只要它们在用户的相同页面上)。下列条件应用于发送和接收消息的 portlet。

  • portlet 必须在相同的页面上。可以使用 XML 访问为 portlet 应用程序创建 portlet 配置文件,该 portlet 应用程序在安装时间内在页面上配置 portlet。请参阅门户网站配置界面以获取更多信息。
  • portlet 必须为相同 portlet 应用程序的一部分。该条件仅应用于实现 PortletMessage 接口的 portlet。扩展 DefaultPortletMessage 的 portlet 不必是相同 portlet 应用程序的成员。
  • 作为 Web 服务部署的 portlet 无法发送和接收消息。

通常,消息是从 portlet 的操作侦听器发送的,并由另一个 portlet 的消息侦听器接收。用户在一个 portlet 中执行操作。捕捉和处理操作事件。根据该操作结果,portlet 可使用 PortletContext 对象的 send() 方法把消息发送到其它 portlet。

示例:发送消息的 ActionListener


...    public void actionPerformed (ActionEvent event) {...      if (action.getName().equals("browse")) {           log.debug("BookmarkActionListener - browse action");                String url = (String) action.getParameters().get("url");           log.debug("BookmarkPortletActionListener - opening link: " + url);...         try {              portlet.getConfig().getContext().send(null, new DefaultPortletMessage(url));         }         catch (AccessDeniedException ade) {              log.error("BookmarkPortletActionListener - unable to send message /"url=" + url + "/" - AccessDenied");         }      }...}

在该样本中,在书签 portlet 中已经定义“浏览”操作。

   DefaultPortletAction browseAction = new org.apache.jetspeed.portlets.DefaultPortletAction("browse");   browseAction.addParameter("url", url);   PortletURI portletURI = response.createReturnURI();   portletURI.addAction(browseAction);   String actionURI = portletURI.toString();   

send() 方法采用下列自变量:

portletName
接收该请求的 portlet 名称。在上述示例中,将消息发送到 null,这意味着它在相同的 portlet 应用程序中对所有 portlet 广播。要将消息发送到特定的 portlet,指定 portlet 的名称,就象在 portlet 部署描述符中的 <portlet-name> 标记所定义的那样。
消息
要发送的消息。消息必须是实现那个接口的 PortletMessage 对象或任何子类。在上述示例中,消息在 DefaultPortletMessage 对象中例示,包含从所执行操作的 url 字符串。

正在接收的 portlet 有一个消息侦听器,它使用消息事件的 getMessage() 方法检索消息。

示例:接收消息的 MessageListener


    public void messageReceived (MessageEvent event)     throws PortletException    {        PortletMessage msg = event.getMessage();        if (msg instanceof DefaultPortletMessage) {            String url = ((DefaultPortletMessage)msg).getText();            PortletAdapter portlet = (PortletAdapter)event.getPortlet();            portlet.getPortletLog().debug("BookmarkPortletMessageListener messageReceived");                PortletRequest request = event.getRequest();PortletSession session = request.getSession();            session.setAttribute("url",url);         }    }}

由于 MessageListener 将已接收的消息设置为会话属性,正在接收的 portlet 必须从会话获取“url”参数。

要获取有关操作事件和消息事件的更多信息,请参阅 Portlet 事件

消息和跟踪记录日志

Portlet 可以把消息和跟踪信息写到日志文件,该文件在 wp_root/log/ 目录中维护。该日志文件帮助门户网站管理员审查 portlet 错误和特殊情况并帮助 portlet 开发者测试和调试 portlet。Portlet API 提供 PortletLog 类,它有把消息和跟踪信息写到日志的方法:

debug()
把跟踪信息写到 wps_[timestamp].log
info()
把信息性消息写到 wps_[timestamp].log
error()
把错误消息写到 wps_[timestamp].log
warn()
把警告消息写到 wps_[timestamp].log

[timestamp] 的格式如下:

   year.month.date-hour.minute.second    

例如:wps_2002.03.08-14.00.00.log 是 2002 年 3 月 8 日下午 2:00 写入的。

如果您在一个方法中多次访问 portlet 日志,建议把日志引用指定到变量,例如:

  private PortletLog myLogRef = getPortletLog(); 

由于日志操作是代价高昂的,PortletLog 提供了确定是否要为给定级别启用日志的方法。仅在日志正在跟踪那个级别的消息时,您的 portlet 才写到给定级别的日志。例如:

  if( getPortletLog().isDebugEnabled() )  {    myLogRef.debug("Warning, Portlet Resources are low!");  }  

要获取有关 WebSphere Portal 中的日志的更多的信息,请参阅日志管理

刷新 portlet 高速缓存

portlet 高速缓存保持 portlet 的完整输出。结果,在用户更改 portlet 状态时,门户网站服务器没有调用 portlet 的 service() 或 doView() 方法。当前 portlet 的高速缓存条目应该为无效时,getLastModified() 方法使 portlet 开发者能通知容器,因此,应该刷新 portlet 的内容。您能使用 WindowListeners 设置新的时间戳记并返回 getLastModified 中的时间戳记。下例显示书签高速缓存其输出的书签 portlet 的部件,但如果窗口状态更改为提供其它输出,则需要立即更改其内容。

首先,在 portlet 部署描述符中,注册 WindowListener 和所支持的 portlet 状态并启用高速缓存。

getLastModified() 示例:Portlet 部署描述符


  <listener>    <listener-class type="window">      com.mycompany.portlets.bookmark.BookmarkPortletWindowListener    </listener-class>  </listener>  <cache>       <expires>-1</expires>    <shared>NO</shared>  </cache>  <allows>    <maximized/>    <minimized/>  </allows>

下一步,WindowListener 为 portlet 会话对 LAST_MODIFIED 属性设置时间戳记。

getLastModified() 示例:WindowListener


package com.mycompany.portlets.bookmark;import org.apache.jetspeed.portlets.*;import org.apache.jetspeed.portlet.*;Import org.apache.jetspeed.portlet.event.*;Import java.io.IOException;                     //Java stuffpublic class BookmarkPortletWindowListener extends WindowAdapter {  public void windowMaximized (WindowEvent event) throws PortletException  {    setLastModified(event);  }  public void windowRestored (WindowEvent event) throws PortletException  {    setLastModified(event);  }  private void setLastModified(WindowEvent event) {    PortletSession session = event.getRequest().getSession(false);        if (session != null) {      session.setAttribute(BookmarkPortlet.LAST_MODIFIED, new Long(System.currentTimeMillis()));    }  }  public void windowDetached (WindowEvent event) throws PortletException  {  }}

最终,当发出请求时,portlet 的 getLastModified() 方法返回时间戳记。

getLastModified() 示例:BookmarkPortlet


    public long getLastModified(PortletRequest request) {        PortletSession session = request.getSession(false);        if (session != null) {            Long lastModified = (Long) session.getAttribute(LAST_MODIFIED);            if (lastModified != null) {                return lastModified.longValue();            }        }        return -1;    }

输出并行 portlet

缺省情况下,门户网站服务器并行输出到 portlet 到页面(使用分隔处理线程)。管理员可以设置用于 portlet 输出的一定数量的线程或关闭该能力,使得可以使用单线程串行输出 portlet。这些设置在 JetspeedResources.properties 文件中。请参阅管理门户网站下的 portlet 并行输出以获取更多信息。

还在 portlet 级别设置 portlet 输出,缺省情况下它是关闭的。要为 portlet 设置并行 portlet 输出,请设置 portlet 部署描述符的 <concrete-portlet> 标记中的该配置参数。

      <config-param>         <param-name>parallel</param-name>         <param-value>true</param-value>      </config-param> 

portlet.xml 中设置该参数帮助管理员避免部署 portlet 之后才进行设置。

个性化 portlet

WebSphere Personalization 使客户能轻松构建 Web 站点,使站点内容与站点访问者相匹配。尽管定制允许用户设置他们自己的首选项或确定查看什么内容,门户网站供应商(开发者、管理员)使用个性化来确定基于用户的特性显示什么内容。

所有个性化解决方案都有用户概念、内容,以及匹配技术。WebSphere Portal 提供用户概念,它是作为带用户概要文件的 User 类来实现。对于每个安装的门户网站内容是特定的,并可以包含客户数据存储、旧的数据库、预订内容供应商及其它。对于匹配技术,WebSphere Personzalization 提供一个启用基于规则的个性化的资源引擎和规则引擎。结合到一起这些组件允许您使用内容与用户相匹配的个性化规则来开发 portlet。

考虑一个为采购办公室用品提供 portlet 的内部网门户网站的示例。超过 50 美元的订单必须由经理批准。仅当 portlet 用户是经理时,portlet 需要显示一个启动批准表单的按钮。在这种情况下,可以把按钮作为内容点添加到 portlet 的视图 JSP。内容点就是您要显示个性化内容的 web 页面的位置。然后创建把该点映射到 portlet 用户的规则,例如:

当用户是经理时,显示批准按钮

WebSphere Studio Application Developer V4.03 用于在 portlet JSP 中创建内容点。Personalization 工作区用于开发在基于用户特性的点中显示内容的规则。关于使用个性化 portlet 开发的更多详细说明,可以在门户网站服务器的 wp_root/dev 目录中找到。


相关信息