WebSphere Portal 編寫 portlet

本節向您介紹有關創建 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,服務 JSP jspserverportlet.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>[email protected]</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 stuff

public 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 目錄中找到。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章