Hello World,一个简单的JSR 168 portlet

来源:互联网 发布:手机调音器软件 编辑:程序博客网 时间:2024/05/22 05:18

Hello World,一个简单的JSR 168 portlet

developerWorks

级别: 初级

杨江 (yjiang@cn.ibm.com), 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:anoncvs@cvs.apache.org:/home/cvspublic login
password: anoncvs
cvs -d :pserver:anoncvs@cvs.apache.org:/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.zip21 KBHTTP关于下载方法的信息

参考资料

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 的技术支持工作。您可以通过 yjiang@cn.ibm.com 和他联系。

原创粉丝点击