Hello World,一個簡單的JSR 168 portlet

Hello World,一個簡單的JSR 168 portlet

developerWorks

級 別: 初級

楊 江 ([email protected] ), IBM Innovation Center for Business Partners 技術顧問, IBM

2004 年 2 月 01 日

本文以Hello World爲例,介紹如何使用JSR 168 API編寫portlet,打包成portlet應用,部署portlet應用到portlet容器上。最後給出JSR 168的參考實現-Apache pluto的安裝配置。

在過去三年中,企業面臨着信息、流程的整合問題,"企業門戶"和"企業應用集成"一時間成了IT業內熱門的課題。國內外 IT廠商和開源組織順時而動,開發了各種企業門戶服務器。單是Java陣營,IBM公司發佈了WebSphere Portal Server 1.2/2.1/4.x/5.x, Apache Software Foundation推出了JetSpeed、BEA、Oracle、SAP、Sun也推出了各自的Portal服務器。這些Portal服務器各自提供 不同的Java API給應用系統開發商開發Portlet,應用系統開發人員不得不爲不同的Portal服務器使用互不通用的API開發功能相同的Portlet。人們 翹首以待,希望有一天能象編寫Java Servlet那樣,使用一種API編寫能運行在大多數Portal服務器上的Portlet應用。

經過近兩年時間的漫長等待,2003年10月7日,Java Community Process(JCP)發佈了JSR168: Portlet Specification 1.0的最終版本。該規範包含如下內容(參見 參考3)
定義了portlet運行環境 - portlet容器
定義了portlet容器和portlet之間的API
提供了portlet存儲持久性和非持久性數據的機制
提供了portlet包含servlet和JSP的機制
定義了portlet打包,方便部署
保證了portlet在JSR 168門戶中的二進制移植
能夠以WSRP協議把JSR 168 portlet作爲遠程portlet運行。

JSR 168規範獲得了業內的廣泛支持,JSR 168專家組包括主要的Portal廠商,包括Apache、BEA、 IBM、 Oracle、 Sun等公司和組織。IBM在Apache以開放源碼項目的方式提供了該規範的參考實現pluto,並在WebSphere Portal Server 5.0.2中提供了JSR 168的支持。

本文以Hello World爲例,介紹如何使用JSR 168 API編寫portlet,打包成portlet應用 源 碼包下載 ,部署portlet應用到portlet容器上。最後給出JSR 168的參考實現-Apache pluto的安裝配置。

現在讓我們開始吧。

一、 創建項目的目錄結構

 

portlet項目的最基本的幾個目錄是:
HelloWorld/JavaSource 放置Java源代碼
HelloWorld/WebContent/Web-INF/classes 放置Java Class文件
HelloWorld/WebContent/Web-INF/lib放置jar文件,比如jstl.jar 、standard.jar (JSTL - JSP Standard Tag Library及Apache的JSTL的實現)
HelloWorld/WebContent/Web-INF/tld 放置taglib定義文件,比如portlet.tld或者portlet.tld(portlet JSP tag)這些目錄下面的jar文件和tld文件可以從安裝好的pluto中找到。





回頁首


二、創建Portlet Java代碼

 

下面是HelloWorldPortlet.java的代碼。相關連的另外兩個Java源文件,在本文末尾有 源 碼包下載 的鏈接。

注意:

1.import語句,這裏使用的全部是java或者javax標準類庫,說明這個portlet代碼應該是可以運行在支 持相應標準的服務器上面。

2.對於一些常量,使用了public static final修飾符。有助於提供java代碼的性能。

3.processAction方法是Portlet的核心方法之一,例子代碼在這裏處理jsp中FORM表單提交的數 據,並把得到的數據放到一個Java Bean中,該Java Bean又被放到PortletSession中供jsp文件調用。
proccessAction處理完畢後,portlet引擎會運行portlet的doView方法。doView方法根據邏 輯、輸入數據或者配置,調用不同的jsp文件進行數據展示。

package com.ibm.spc;
import java.io.*;
import javax.portlet.*;
/**
*
* A sample portlet based on GenericPortlet
*
*/
public class HelloWorldPortlet extends GenericPortlet {
public static final String JSP_FOLDER = "/com_ibm_spc/jsp/"; // JSP folder name
public static final String VIEW_JSP = "HelloWorldPortletView"; // JSP file name to be rendered on the view mode
public static final String VIEW_BEAN = "HelloWorldPortletBean"; // Bean name for the view mode request
public static final String SAY_HELLO_ACTION = "Say_Hellow_Action"; // Action name for submit form
public static final String YOUR_NAME = "YourName"; // Parameter name for the text input
/**
* Serve up the <code>view</code> mode.
*
* @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
*/
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
// Invoke the JSP to render
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP));
rd.include(request,response);
}
/**
* Process an action request.
*
* @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse)
*/
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException {
if( request.getParameter(SAY_HELLO_ACTION) != null ) {
// Make a session bean
PortletSession session = request.getPortletSession();
HelloWorldPortletBean viewBean = new HelloWorldPortletBean();
session.setAttribute(VIEW_BEAN, viewBean);

System.out.println("debug HelloWorld " + request.getParameter(YOUR_NAME));

// Set form text in the view bean
viewBean.setFormText(request.getParameter(YOUR_NAME));
}
}
/**
* Returns JSP file path.
*
* @param request Render request
* @param jspFile JSP file name
* @return JSP file path
*/
private static String getJspFilePath(RenderRequest request, String jspFile) {
String markup = request.getProperty("wps.markup");
if( markup == null )
markup = getMarkup(request.getResponseContentType());
return JSP_FOLDER+markup+"/"+jspFile+"."+getJspExtension(markup);
}

/**
* Convert MIME type to markup name.
*
* @param contentType MIME type
* @return Markup name
*/
private static String getMarkup(String contentType) {
if( "text/vnd.wap.wml".equals(contentType) )
return "wml";
return "html";
}
/**
* Returns the file extension for the JSP file
*
* @param markupName Markup name
* @return JSP extension
*/
private static String getJspExtension(String markupName) {
return "jsp";
}
}





回頁首


三、創建JSP

 

jsp文件中首先聲明它不需要創建新的HTTP Session,返回頁面的內容是html頁面。然後import聲明需要引用標準java類庫java.util,javax.portlet,以及我 們自己的類庫com.ibm.spc。接着聲明使用portlet標記庫。<portlet:defineObjects/>使用 portlet標記庫的標記defineObjects,定義了jsp中要使用3個變量:

RenderRequest renderRequest
RenderResponse renderResponse
PortletConfig portletConfig

<%@ page session="false" import="java.util.*,javax.portlet.*,com.ibm.spc.*" %>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>

接下來,從<portlet:defineObjects/>語句定義的變量renderRequest當中 獲取PortletSession,進而得到session當中保存的數據並顯示在JSP頁面上。

 <%
PortletSession session = renderRequest.getPortletSession();
HelloWorldPortletBean bean = (HelloWorldPortletBean)session.getAttribute(HelloWorldPortlet.VIEW_BEAN);
%>
if (bean != null) {
String formText = bean.getFormText();
if( formText.length()>0 ) {
%>
Hello <%=formText%>.
<%
}
}
%>


最後部分是使用portlet標記庫的另一個標記actionURL產生一個URL指向當前頁面中的這個portlet, 生成的URL能夠觸發當前portlet的action請求,或者說這個URL能夠觸發當前portlet的processAction方法。

  <FORM method="POST" action="<portlet:actionURL/>">
<LABEL for="<%=HelloWorldPortlet.YOUR_NAME%>">Please input your name here, </LABEL><BR>
<INPUT name="<%=HelloWorldPortlet.YOUR_NAME%>" type="text"/>
<INPUT name="<%=HelloWorldPortlet.SAY_HELLO_ACTION%>" type="submit" value="Submit"/>
</FORM>






回頁首


四、編譯portlet

 

編寫好portlet的java代碼,現在我們可以把它編譯成二進制class文件。

下面的腳本中使用JAVA_HOME環境變量指向WebSphere Application Server 5.0.2中的IBM JDK 1.3.1。

腳本中使用CP變量指向Tomcat 4.1中帶的Servlet 2.3類庫,以及pluto的JSR 168 portlet類庫。腳本最後的動作是編譯HelloWorld portlet,並把編譯好的class文件放到WebContent/WEB-INF/classes目錄。

注意:

WebSphere Portal Server 5.0.2中使用的WebSphere Application Server 企業版5.0.2;Tomcat 4.1使用的JDK 1.3.1。

WebSphere Application Server 5.1中的JDK是1.4.1版本。

set JAVA_HOME=C:/WebSphere/AppServer/java
set PATH=%JAVA_HOME%/bin
set tomcat.home.pluto=e:/ApacheSoftwareFoundation/Tomcat4.1
set CP=.
rem Servlet 2.3 API jar file
set CP=%CP%;%tomcat.home.pluto%/common/lib/servlet.jar
rem JSR 168 API jar file
set CP=%CP%;%tomcat.home.pluto%/shared/lib/portlet-api.jar
rem Specify where to place generated class files
set target_path=../WebContent/WEB-INF/classes
cd JavaSource
javac -classpath %CP% -d %target_path% com/ibm/spc/*.java





回頁首


五、 創建Web應用的部署描述文件

 

Portlet應用也是一個J2EE Web應用,擁有一個Web應用部署描述文件web.xml。web.xml文件中taglib標記部分是關於Portlet Tag Library的定義,在Portlet 應用的jsp文件中可以使用這種Tag Lib。

下面代碼片斷聲明使用uri是 http://java.sun.com/portlet 的tag lib,tag lib的前綴是portlet。關於Portlet Tag Library請參考Java Portlet Specification。

注意:2004年2月的pluto中portlet部署程序中不能分析處理web.xml文件中welcome-file 的標記,相信Apache會在後繼的版本中修正這個問題。解決辦法是,或者從web.xml文件中去除有關的tag;或者修改pluto代碼,爲 servletdefinitionmapping.xml文件添加welcome-file標記,爲 org.apache.pluto.portalImpl.om.servlet.impl. WebApplicationDefinitionImpl java類添加一個字段來解決這個問題。

<?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">
<display-name>HelloWorld Web Application</display-name>
<taglib id="PortletTLD">
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>/WEB-INF/tld/portlet.tld</taglib-location>
</taglib>
</web-app>





回頁首


六、 創建Portlet部署描述文件

 

每個Portlet應用除了Web應用部署描述文件web.xml外,還有一個Portlet部署描述文件 - portlet.xml。該文件中包括該Portlet Application中一個或者多個portlet的定義。

下面的portlet.xml文件中首先是<portlet-app/>,其中引用了sun公司的關於 portlet描述文件的名字空間的定義文件portlet-app_1_0.xsd。然後是各個<portlet/>定義,包括名字和描述 信息,國際化的名字和描述信息,portlet的class類名,portlet的初始化參數、國際化用戶界面中使用的資源文件。HelloWorld Portlet有一個初始化參數wps.markup,在我們的portlet代碼中使用 renderRequest.getProperty("wps.markup")獲得這個初始化參數的值。





回頁首


七、 創建war文件

 

我們使用JDK的命令jar把class文件、jsp文件、jar包、JSP標記庫、web部署描述文件 web.xml、portlet部署描述文件portlet.xml等打包成web archive文件。

set JAVA_HOME=C:/WebSphere/AppServer/java
set PATH=%JAVA_HOME%/bin
cd WebContent
jar cf ../build/HelloWorld.war .





回頁首


八、 在pluto上面部署portlet應用

 

pluto提供了部署打包成war格式portlet應用的命令行工具。名叫Deploy的部署工具會做兩件事 情:

1. 分析portlet應用war文件中的web.xml文件和portlet.xml文件,修改web.xml以添加和pluto運行環境相關的 servlet定義和servlet運行參數

2. 把portlet作爲Web應用部署到Tomcat服務器上面

在我們的例子中,Deploy工具修改web.xml文件,添加一個名叫HelloWorldPortlet的 servlet定義,併爲這個servlet添加了portlet-guid的參數,參數值HelloWorld.HelloWorldPortlet。 其中<servlet-name>值來源於portlet.xml文件中<portlet-name>標記,portlet- guid參數的值是war文件的前面部分和portlet.xml文件中<portlet-name>標記的值的組合。

        <servlet>
<servlet-name>HelloWorldPortlet</servlet-name>
<display-name>HelloWorldPortlet Wrapper</display-name>
<description>Automated generated Portlet Wrapper</description>
<servlet-class>org.apache.pluto.core.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-guid</param-name>
<param-value>HelloWorld.HelloWorldPortlet</param-value>
</init-param>
<init-param>
<param-name>portlet-class</param-name>
<param-value>com.ibm.spc.HelloWorldPortlet</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldPortlet</servlet-name>
<url-pattern>/HelloWorldPortlet/*</url-pattern>
</servlet-mapping>


Deploy的語法是:

deploy <TOMCAT-webapps-directory> <TOMCAT-pluto-webmodule-name> <web-archive>


<build-container-dir> [-debug] [-addToEntityReg <app-id> [<portlet-id>:<portlet-name>]+]



下面的例子腳本中,Tomcat所有Web應用都部署在TOMCAT-webapps-directory目錄下面,比 如%tomcat.home%/webapps;<TOMCAT-pluto-webmodule-name>參數是pluto web應用的名稱,比如 pluto;web-archive參數是我們要部署的portlet應用打包文件, D:/workspace/JSR168Portlet/HelloWorld/build/HelloWorld.war;<build- container-dir>是portlet部署工具的工作路徑,比如當前路徑,Deploy工具會在相對於工作路徑的路徑下面找尋相關文件。

set JAVA_HOME=C:/WebSphere/AppServer/java
set PATH=%JAVA_HOME%/bin
set tomcat.home.pluto=e:/ApacheSoftwareFoundation/Tomcat4.1
set CP=.
set CP=%CP%;%tomcat.home.pluto%/common/lib/servlet.jar
set CP=%CP%;%tomcat.home.pluto%/shared/lib/portlet-api.jar
set CP=%CP%;%tomcat.home.pluto%/common/endorsed/xercesImpl.jar
set CP=%CP%;%tomcat.home.pluto%/webapps/pluto/WEB-INF/lib/castor-0.9.5.jar;
set CP=%CP%;%tomcat.home.pluto%/common/endorsed/xmlParserAPIs.jar
set CP=%CP%;%tomcat.home.pluto%/shared/lib/pluto-1.0.jar
cd %tomcat.home.pluto%/webapps/pluto/WEB-INF/classes
set tomcat.home=e:/ApacheSoftwareFoundation/Tomcat4.1
java -classpath %CP% org.apache.pluto.portalImpl.Deploy %tomcat.home%/webapps pluto


D:/workspace/JSR168Portlet/HelloWorld/build/HelloWorld.war . -debug



注意:2004年2月份的pluto部署以後,可能缺少一個文件,具體路徑和文件名稱是 tomcat.home/webapps/pluto/WEB-INF/portal/src/webapp/WEB-INF/tld /portlet.tld。





回頁首


九、 註冊portlet到pluto容器中

 

修改%tomcat.home.pluto%/webapps/pluto/WEB-INF/data /portletentityregistry.xml文件,加入我們的portlet的註冊信息。其中application屬 性<definition-id>指向HelloWorld應用的war文件名稱的前半部分,portlet屬性definition-id 指向HelloWorld應用的web.xml文件中portlet-guid的值。這樣,portlet容器pluto就能夠根據這個註冊文件找到相應 web應用的servlet了。

        <application id="5">
<definition-id>HelloWorld</definition-id>
<portlet id="1">
<definition-id>HelloWorld.HelloWorldPortlet</definition-id>
<preferences>
<pref-name>TestName4</pref-name>
<pref-value>TestValue4</pref-value>
<read-only>true</read-only>
</preferences>
</portlet>
</application>






回頁首


十、 把portlet放到我們的測試頁面中

 

修改%tomcat.home.pluto%/webapps/pluto/WEB-INF/data/ pageregistry.xml文件,添加一段fragment,把我們的portlet註冊到頁面上。其中名爲portlet的參數的值是 portletentityregistry.xml文件中註冊的portlet的應用id和portlet id的組合。

                        <fragment name="col1" type="column">
<fragment name="p3" type="portlet">
<property name="portlet" value="5.1"/>
</fragment>
</fragment>


現在重新啓動Tomcat,我們終於看到盼望已久的JSR 168 portlet了。

在頁面中輸入IBM Innovation Center,你將看到





回頁首


十一、 pluto的安裝

 

根據JSR 168 Request,IBM在Apache以開放源碼的方式提供了JSR 168的參考實現。

安裝Apache pluto,我們需要
1)Maven 1.0-beta-10或者更高版本
2)JDK 1.3或者更高
3)Servlet 2.3引擎
Tomcat 4.1.18-LE w/JDK 1.4
或者
Tomcat 4.1.24 w/ JDK 1.3
4)CVS Client for windows
本人使用的測試環境是IBM JDK 1.3.1, jakarta-tomcat-4.1.29, v. 1.0-rc1-SNAPSHOT。

我們需要使用cvs客戶端以匿名方式下載pluto代碼。注意:cvs客戶端不能使用代理,必須用直接的internet 連接。

cvs -d :pserver:[email protected]:/home/cvspublic login
password: anoncvs
cvs -d :pserver:[email protected]:/home/cvspublic checkout jakarta-pluto

安裝好JDK、maven、Tomcat以後,進入pluto源碼目錄,拷貝 build.properties.sample 文件爲build.properties文件。在build.properties文件中指定tomcat安裝路徑,注意windows平臺上面或者使用 /或者是//作爲路徑的分割符。如果你在公司防火牆的後面或者其他原因必須通過代理服務器訪問internet的話,那麼需要指定代理服務器參數 maven.proxy.host和maven.proxy.port。

tomcat.home.pluto=e://ApacheSoftwareFoundation//Tomcat4.1
maven.proxy.host = proxyhostname
maven.proxy.port = 8080

現在,我們執行maven命令就可以開始pluto的安裝了。Maven將安裝pluto到Tomcat服務器上面,安裝 名爲testsuite的測試用portlet應用到Tomcat上,配置pluto以運行testsuite中的例子portlet。

啓動tomcat以後,通過http://localhost:8080/pluto/portal/就可以訪問到 pluto的演示頁面了。

MAVEN_HOME=E:/ApacheSoftwareFoundation/Maven1.0-rc1
Set PATH=%MAVEN_HOME%/bin;%path%
maven fullDeployment





回頁首


後記

 

相信JSR 168標準會象Servlet標準那樣得到應用服務器廠商和廣大應用開發商的大力支持。上面的HelloWorld portlet現在可以運行在Apache pluto上面和IBM WebSphere Portal Server 5.0.2上面,相信也能運行在其他支持該標準的Portal服務器上。






回頁首


下載

名字 大小 下載方法
HelloWorld.zip 21 KB HTTP
關於下載方法的信息


參考資料

1) JSR(Java Standardization Request) 168

2)Pluto reference implementation: http://jakarta.apache.org/pluto

3)"Introducing the Portlet Specification," Stefan Hepper and Stephan Hesmer (JavaWorld):

Part 1: Get your feet wet with the specification's underlying terms and concepts (August 2003)

Part 2: The Portlet API's reference implementation reveals its secrets (September 2003)

4) IBM JSR 168 Tech preview

5) IBM Portal Toolkit 5.0.2 :IBM WebSphere Studio 5.0的plugin, 支持JSR 168 portlet的開發環境

6) 比 較JSR 168 Java Portlet 規範和IBM Portlet API



關於作者

楊江 ,IBM Innovation Center for Business Partners 技術顧問,從事 Lotus、WebSphere 相關產品線,如 WebSphere Portal、Lotus Sametime、Lotus Domino/Notes 的技術支持工作。您可以通過 [email protected] 和他聯繫。

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