级别: 中级
Hongqing Song , WebSphere 顾问, IBM
Richard Scott , 架构师,eServer Solutions Enablement, IBM Dallas
2005 年 9 月 1 日
本文描述了两种用于将信息从遗留 Web 页传递到 Portlet 的方法。接收 Portlet 既可以是 JSR 168 Portlet ,又可以是 IBM API Portlet。文中的下载部分包含用于两种场景的示例代码,可供您安装和部署。示例演示了如何使用静态链接(包含预定义数据)和动态字段(承载运行时用户输入)。针对 IBM API Portlet 的解决方案在 IBM WebSphere Portal V5.0.X 和 V5.1.X. The solut 中都可以工作。针对 JSR Portlet 的解决方案只能用于 WebSphere Portal V5.1.x。
引言
IBM® WebSphere® Portal V5.1.0.1 引入了跨页面连接,作为 JSR 168 Portlet 间和 IBM API Portlet 间页面到页面通信的方法。然而,我们先前的文章(请参阅参考资料)的读者以及目前的一些项目的需求指出,需要解决另一种页面到页面通信的问题:即遗留 Web 页(不在门户服务器中运行的页面)和门户页面上的 Portlet 间的通信。遗留 Web 页有多种格式,包括静态 HTML、CGI、ASP 或 JSP 和 Servlet。
因为 IBM API Portlet 是 Servlet 类的子类,所以它可以直接访问 HTTP 参数(即 HTML 表单中的 URL 参数和数据)。然而,JSR 168 Portlet 只能模拟 Servlet API,而不能直接访问 HTTP 参数。因此,针对 IBM API Portlet 和 JSR 168 Portlet 的解决方案是不同的。
本文是为需要集成遗留 Web 应用程序和 WebSphere Portal 应用程序的架构师和开发人员准备的。读者应该具备 Portlet 开发和 Web 应用程序开发的基本经验。要获得关于 Portlet 开发的概述,请参阅 IBM Rational Application Developer V6 Portlet Application Development and Portal Tools(请参阅参考资料)。
场景介绍
在我们的前两篇文章中,Portlet 间的页面到页面通信和 WebSphere Portal V5.1.0.1 中的 JSR 168 Portlet 间的页面到页面通信,我们使用“快速搜索 (Quick search)”示例演示了用于这样的场景的推荐解决方案。本文使用相同的示例来演示我们用于遗留 Web 页到 Portlet 通信场景的解决方案。
WebSphere Portal 是一种新的 Web 技术,在许多企业中可能都存在现有的遗留 Web 应用程序。将遗留 Web 应用程序转换成 WebSphere Portal 应用程序需要花费大量的时间、资源和资金。因此,许多 WebSphere Portal 应用程序需要与遗留 Web 应用程序共存和集成。这两种应用程序可能运行在相同或不同的服务器环境中。本文介绍的解决方案在这两种情况下都可以工作。
假定一家公司开发了一个非常好的搜索引擎,并且希望其遗留的 Web 站点页面能够利用这个新的搜索引擎。为了提供该功能,您需要支持现有的遗留 Web 页和公司门户中的 Portlet 间的页面到页面通信。
图 1 展示了遗留站点上的 QuickSearch 页。它可以是一个独立的页面,也可以是遗留站点的页面上的一个有用部分。
| 动态 |
用户输入搜索文本,再单击“Submit”按钮,然后 SearchResult portlet 显示搜索结果。 |
| 静态 |
用户单击“Search resources for WebSphere Portal”(一个静态链接,它将 WebSphere Portal 预定义位搜索文本 URL 参数),然后 SearchResult Portlet 显示搜索结果。 |
图 1. 遗留 Web 站点上的 QuickSearch 页面

图 2 显示用户在 QuickSearch 遗留页面输入字符串“User input data”并单击“Submit”按钮之后的 SearchResult Portlet。SearchResult Portlet 接收该字符串,并且在句子“The search text you entered was XXX”的末尾显示它。
图 2. 带有动态搜索功能的 SearchResult Portlet

图 3 展示了用户在 QuickSearch 遗留页面上单击 Search resources for WebSphere Portal 链接之后的 SearchResult Portlet。SearchResult Portlet 接收 WebSphere Portal 作为 URL 参数,并将其作为句子的一部分进行显示。
图 3. 带有静态搜索功能的 SearchResult Portlet

以下产品用于开发和测试这些示例 Portlet:
- IBM WebSphere Portal V5.1.0.1
- IBM Rational Application Developer for WebSphere V6.0(带有 WebSphere Portal V5.1 测试环境)
- Microsoft Internet Explorer V6.0
- Netscape v7.1
- Mozilla Firefox V1.0
所有的源代码都包含在下载文件 p2p_legacy_code.zip 中。
场景 1. IBM API Portlet
因为 IBM API Portlet 是 Servlet 的子类,所以它可以直接访问 HTTP 参数。这样您便可以使用 URL 映射和 HTTP 参数实现遗留 Web 页和 IBM API Portlet 间的通信。
清单 1 展示了本场景中与使用 IBM API 实现的 SearchResult Portlet 通信的 QuickSearch Web 页的 HTML 源代码。包含 SearchResult Portlet 的门户网页的 URL 为
http://localhost:9081/wps/portal/search_result
|
,其中 search_result 是 SearchResult 门户网页的 URL 映射名称。
创建该页面的 URL 映射名称有两个原因:
- 对于用户,它比 WebSphere Portal 生成的缺省 URL 要友好得多。缺省的 URL 难以理解,并且与计算机有关(对于同一页面,不同的计算机生成不同的 URL)。以下 URL 是 WebSphere Portal 为搜索结果页面生成的 URL 的示例:
http://localhost:9081/wps/portal/!ut/p/kcxml/04_Sj9SPykssy0xPLMnMz0vM0Y_QjzK
LN4g3cQHJgFiWQBaYdoWJGJtCRBBqHOECvh75uan63voB-gW5oaER5Y6KAIhg
H-A!/delta/base64xml/L3dJdyEvUUd3QndNQSEvNElVRS82XzBfOUQ!
|
- 将门户应用程序从临时服务器移到生产服务器更加容易,因为您可以将同一 URL 映射名称用于不同服务器上的页面。但您无法控制 WebSphere Portal 生成的 URL。
请参见 WebSphere Portal Information Center(参考资料中提供了相关地址),了解如何创建 URL 映射。在创建了 URL 映射之后,您可以使用 URL http://localhost:9081/wps/portal/search_result 指向该页面。
在清单 1 中您将看到,数据 p2psampel5_search_text 将作为 HTTP 参数提交给该门户页面。
清单 1. p2p_ibm_portlet.html(用于与 IBM API Portlet 通信的 QuickSearch Web 页的源代码)
<html>
<title>Quick search</title>
<body>
<form method="get" action="http://localhost:9081/wps/portal/search_result">
<table>
<tr><td colspan="2">Please enter search text</td></tr>
<tr>
<td><input type="text" name="p2psample5_search_text"/></td>
<td><input type="Submit" value="Submit"/></td
</tr>
</table>
</form>
<a href="http://localhost:9081/wps/portal/search_result?p2psample5_search_text=WebSphere%20Portal">
Search resources for WebSphere Portal
</a>
</body>
</html>
|
清单 2 展示了 SearchResult Portlet(它是一个 IBM API Portlet)的源代码段。该 Portlet 直接从遗留页面检索 HTTP 参数。
清单 2. SearchResultPortlet 的 doView() 方法
public void doView(PortletRequest request, PortletResponse response)
throws PortletException, IOException
{
...
// Retrieves the URL parameter submitted from the legacy Web page.
String p2psample5_search_text = request.getParameter("p2psample5_search_text");
// Make a view mode bean
SearchResultPortletViewBean viewBean = new SearchResultPortletViewBean();
viewBean.setSearchText(p2psample5_search_text);
request.setAttribute(VIEW_BEAN, viewBean);
// Invoke the JSP to render
getPortletConfig().getContext().include(VIEW_JSP, request, response);
...
}
|
要部署此场景的示例代码,请执行以下步骤:
- 下载压缩文件
p2p_legacy_code.zip。
- 将 p2p_ibm_portlet.html 放在 Web 服务器上。如果使用的是 IBM HTTP 服务器,则将该文件放在以下目录中:
{$httpServer_home}/htdocs/en_US/p2p,其中 {$httpServe_home} 是 IBM HTTP 服务器的安装目录。
- 启动 WebSphere Portal。
- 安装 P2PSample5.war。
- 在 My Portal 下,创建一个名为
Search result 的标签。
- 在 Search result 下,创建一个名为
P2PSample 的页面。
- 在 Search result 页面上添加 SearchResult Portlet。
- 创建一个名为“search_result”的 URL 映射,并将其映射到 My portal-->Search result-->P2PSample。
- 将
Search result 页面设置为公共,或启用 WebSphere Portal 的安全性。(请参考 WebSphere Portal Information Center)。
- 如果搜索页面受到保护,并且 WebSphere Portal 安全性未启用,则示例将不能工作。缺省情况下,安装 WebSphere Portal 5.X 时不启用安全性。
- 要测试示例,请打开浏览器并输入以下 URL:
http://localhost/p2p/p2p_ibm_portlet.html
- 单击“Search resources for WebSphere Portal”链接,或输入一些搜索文本并单击 Submit 按钮。
如果搜索结果页面是公共的,则您可以直接看到它。然而,如果搜索结果页面受到保护并且启用了 WebSphere Portal 安全性,则您需要输入用户名和密码。然后,您可以看到搜索结果页面以及从遗留 Web 页提交的数据。
场景 2:JSR 168 Portlet
因为 JSR Portlet 只模拟 Servlet API,所以它无法直接访问 HTTP 参数。因而上面的解决方案对 JSR 168 Portlet 不起作用。但是 WebSphere Portal V5.1 有内置的类 WebSphere Portal System Programming Interface (SPI),开发人员可以使用该类来创建指向门户页面的 URL,并且将数据传递到该页面上的 JSR Portlet。请参阅参考资料,了解关于如何访问 SPI Javadoc 的信息。
重要:URL 的总长度不超过 2048 个字符。
图 1 展示了我们针对 JSR 168 Portlet 的解决方案。具体过程如下:
- 用户输入搜索文本并单击“Submit”。另外,用户可以单击 QuickSearch 页面上的静态链接。还有一种可选的方式是,将数据以 HTTP 参数的形式发送到 P2PHandler。
- P2PHandler 获取从 Web 页发送的 HTTP 参数。它将这些参数发送到使用 Model SPI 创建的 P2pUtil.jar 中的 Java 类。
- P2PUtil.jar 中的 Java 类创建指向 SearchResult 门户页面的 URL。从遗留 Web 页发送的参数作为呈现参数包含在该 URL 中。
- P2PHandler 获取该 URL 并重定向到 SearchResult 门户页面。SearchResult Portlet 检索呈现参数,然后执行搜索。
图 4:场景 2 的组件图

清单 3 展示了 QuickSearch 遗留页面的 HTML 源代码。在这个场景中,QuickSearch 页面指向 p2pHandler.jsp。
清单 3. p2p_168_portlet.html(QuickSearch 遗留页面的源代码)
<html>
<title>Quick search</title>
<body>
<form method="get" action="http://localhost:9081/wps/p2p/p2pHandler.jsp">
<table>
<tr><td colspan="2">Please enter search text</td></tr>
<tr>
<td><input type="text" name="p2psample4_search_text"/></td>
<td><input type="Submit" value="Submit"/></td
</tr>
</table>
</form>
<a href="http://localhost:9081/wps/p2p/p2pHandler.jsp?p2psample4_search_text=
WebSphere%20Portal">
Search resources for WebSphere Portal
</a>
</body>
</html>
|
如清单 4 所示,P2PHandler 是使用 JSP 实现的。它检索 HTTP 参数,调用 P2PUtil.jar 中的 Java 类来构建 URL,然后重定向到 SearchResult 门户页面。
清单 4. p2pHandler.jsp
<%@ page import="p2phandler.util.P2PURLGenerator"%>
<%
// Retrieve HTTP parameter sent from legacy Web page.
String searchStr = request.getParameter("p2psample4_search_text");
try
{
// Call classes in P2PUtil.jar to create the URL.
// Parameters sent from the legacy Web page are included in the URL.
String targetURLStr = P2PURLGenerator.generateURLString(
"p2psample.SearchResult","p2psample4.win.SearchResult",
"p2psample4_search_text", searchStr);
// Redirect to the SearchResult page
response.sendRedirect(targetURLStr);
}
catch(Exception e)
{
System.err.println(e.toString());
}
%>
|
清单 5 展示了如何使用 WebSphere Portal SPI 类生成带有呈现参数的 URL:
清单 5. P2PURLGenerator.java 类
public class P2PURLGenerator
{
...
public static String generateURLString(
String pageName, // Custom unique name of the page containing the portlet
String portletName, // The unique name of the portlet window
String paramName, // The parameter name
String paramValue // The parameter value
) throws StateException, NamingException, IOException
{
final StateManager mgr = getStateManager();
// Get the URL factory
final URLAccessorFactory URLFactory =
(URLAccessorFactory) mgr.getAccessorFactory(URLAccessorFactory.class);
// Request a URL
// The 3rd argument specifies whether the URL points to the
// protected (authenticated) area
// Pass in "false" if your page is accessible for unauthenticated users
final EngineURL URL = URLFactory.newURL(new MyServerContext(), false, true,
mgr.newState(), Constants.EMPTY_COPY);
// Set the page this URL should point to
final SelectionAccessorFactory selectionFactory =
(SelectionAccessorFactory) mgr.getAccessorFactory(SelectionAccessorFactory.class);
// Request the selection controller to set the page;
// pass in the state associated with the created URL
final SelectionAccessorController selectionCtrl =
selectionFactory.getSelectionController(URL.getState());
// Set the page; you need the unique name (String) or the ObjectID of that page
selectionCtrl.setSelection(pageName);
// Dispose the accessor (avoids memory leak)
selectionCtrl.dispose();
// Set portlet render parameters
final PortletAccessorFactory portletAccessorFactory =
(PortletAccessorFactory) mgr.getAccessorFactory(PortletAccessorFactory.class);
//Get the portlet controller to set render parameters;
// pass in the state associated with rge created URL
final PortletAccessorController portletCtrl =
portletAccessorFactory.getPortletController(portletName, URL.getState());
// Get the modifiable render parameter map
final Map parameters = portletCtrl.getParameters();
// Set the render parameter
parameters.put(paramName, new String[]{paramValue});
// Dispose the accessor (avoids memory leak)
portletCtrl.dispose();
// Now convert the URL to a String; pass in your writer.
// writeDispose() writes the URL to the given writer and
// disposes of the URL afterwards.
// If you want to display this URL a multiple times pls use writeCopy().
return URL.writeDispose(new StringWriter()).toString();
}
...
}
|
上面的代码表明,您需要知道两件事才能使一个 JSR Portlet 能够与其他页面上的另一个 JSR Portlet 通信:
- 目标门户页面的唯一名称。
- 该页面上的 Portlet 窗口的唯一名称。
虽然可以在 WebSphere Portal 管理控制台上定义门户页面的唯一名称,但是您无法定义 Portlet 窗口的唯一名称。您需要编写 XML 部署文件,并使用 xmlaccess 实用工具创建这些唯一名称。
清单 6 展示了部分 p2p_create_page.xml 文件(在 p2p_legacy_code.zip 中),它创建了一个标题为 Search result 且具有唯一名称 p2psample.SearchResult 的门户页面,并且将具有唯一名称的 p2psample4.win.SearchResult 放在该页面上。
清单 6. p2p_create_page.xml
...
<content-node action="locate" objectid=
"Content.Root.My_Portal" uniquename="wps.My Portal"/>
<content-node content-parentref="Content.Root.My_Portal" action=
"update" active="true" objectid="SearchResult.page" uniquename=
"p2psample.SearchResult" type="page" skinref="undefined">
<supported-markup markup="html" update="set"/>
<localedata locale="en">
<title>Search result</title>
</localedata>
<component action="update" ordinal="100"
type="container" orientation="H">
<component action="update" ordinal="100"
type="container" orientation="V" >
<component action="update" ordinal="100"
type="control" uniquename="p2psample4.win.SearchResult">
<portletinstance action="update" objectid="SearchResult.page.portlet1"
Portletref="p2p.portlet.SearchResult"/>
</component>
</component>
</component>
</content-node>
...
|
清单 7 展示了 SearchResult Portlet 的源代码,SearchResult Portlet 是 JSR 168 Portlet。它从使用 URLGenerator 类构建的 URL 中检索呈现参数。
清单 7. SearchResultPortlet.java
public class SearchResultPortlet extends GenericPortlet
{
...
public void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
// Retrieve the HTTP parameter sent from the legacy Web page.
String p2psample4_search_text =
request.getParameter("p2psample4_search_text");
SearchResultPortletViewBean viewBean=new SearchResultPortletViewBean();
viewBean.setSearchText(p2psample4_search_text);
request.setAttribute(VIEW_BEAN, viewBean);
// 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);
}
…
}
|
要部署场景 2 的示例代码,请执行以下步骤:
- 解压缩 p2p_legacy_code.zip 文件。
- 将 p2p_168_portlet.html 复制到 Web 服务器。如果您使用的是 IBM HTTP 服务器,则将该文件放入以下文件夹中:
{$httpServer_home}/htdocs/en_US/p2p
|
,其中 {$httpServe_home} 是 IBM HTTP 服务器的安装目录。
- 将 p2pHandler.jsp 复制到以下目录:
{$was_home}/installedApps/machine_name/wps.ear/wps.war/p2p
|
- 将 P2PUtil.jar 放入以下目录:
,其中 ${wps_home} 表示 WebSphere Portal 安装目录。
- 将 P2PSample4.war 放入
${wps_home}/installableApps 目录。
- 在
${wps_home}/bin 中,创建名为 xmlaccess 的目录。然后,将 p2p_create_page.xml 复制到 ${wps_home}/bin/xmlaccess。
- 启动 WebSphere Portal。
- 打开命令提示符,转到
${wps_home}/bin/xmlaccess,然后运行以下命令(在一行上):
..\xmlaccess.bat –in p2p_create_page.xml –out result.xml –user
wpsadmin –pwd password –URL http://localhost:9081/wps/config
|
使用用于 WebSphere Portal 服务器的用户名和密码。
- 停止 WebSphere portal 并启动 WebSphere Portal。
- 将 My portal 下名为
Search result 的页面设置为公共。
- 要测试该示例,请在 Web 浏览器中打开以下 URL:
http://localhost/p2p/p2p_168_portlet.html
|
- 单击 Search resources for WebSphere Portal 链接,或者输入一些搜索文本并单击 Submit。您将看到搜索结果页面包含从遗留 QuickSearch Web 提交的数据。
结束语
使用本文提供的解决方案,您可以启用从遗留 Web 页到 Portlet 的通信。您了解了如何将 URL 映射和 HTTP 参数用于遗留 Web 页和 IBM API Portlet 间的通信。对于 JSR 168 Portlet,我们使用通过由 WebSphere Portal V5.1.X 提供的 Model SPI 处理的 HTTP 参数。另外,对于 JSR 168 Portlets,您可以使用 xmlaccess 来创建该目标门户页面的唯一名称,以及该页面上的 Portlet 窗口的唯一名称。要进行从 Portlet 到遗留 Web 页的通信,您只需使用 GET 或 POST 方法发送带有 HTTP 参数的数据即可。 |