级别: 初级 Varadarajan Ramamoorthy (varad@us.ibm.com), 高级 IT 专家, IBM Software Services for WebSphere
2004 年 4 月 01 日 本文将向您展示如何在 WebSphere Portal Version 5 中的 Portlet 之间传递复杂数据类型。
引言
当您开发 Portlet 时,通常需要在 Portlet 之间传递数据。如果您用到 Portlet 消息传递,那么这些 Portlet 必须处于同一个 Portlet 应用程序中,而这有时限制过多。协作 Portlet 是用于内部 Portlet 通信的最新技术,协作 Portlet 也称为一点即动(Click-to-Action)(以下称为 C2A)。
C2A 还支持位于不同 Portlet 应用程序中的 Portlet 之间的消息传递,并且它还提高了重用能力。在 IBM WebSphere Portal V5(以下简称为 WebSphere Portal)中,C2A 拥有了新的特征,能够让您在 Portlet 之间传递复杂数据类型。通过使用这一特征,您可以跨 Portlet 传送多个特性。例如,在 V5 之前,即使您在源 Portlet 中拥有所有的数据,您却仅将用户 ID 传递给另一个 Portlet,然后从数据存储器中获取关于用户的实际信息。或者您通过使用一个字符串分隔符并对其进行解析来完成同样的工作,但是这样确实太麻烦。通过使用 V5 中的这一新特征,您可以使用 Bean 或者 Hashtable 容易地传递所有的数据。
本文的预期读者是已经具有开发用于 WebSphere Portal 的 Portlet 的经验的开发人员。您还需要了解一些有关 Web 服务描述语言(Web Services Definition Language,WSDL)的知识。在阅读完本文之后,您将学会在 Portlet 之间传递对象。
要了解有关开发 Portlet 的更多信息,请参阅
WebSphere Portal V5 InfoCenter和
Portlet development guide。要了解有关 WSDL 的更多信息,请参阅
WSDL 规范。
下列软件用于测试这个解决方案:
- IBM WebSphere Portal Server Version 5.0.2
- Portal fix PQ85663
用况
考虑下列用况:
- 门户上的一个页面包含两个 Portlet:购物车 Portlet 和地址簿 Portlet。
- 用户进入门户网站购物。
- 该用户预先在地址簿 Portlet 中列出数个地址。
- 用户选择了一个要购买的物品并将它添加到购物车。
- 当用户准备结帐时,他或她想要从地址簿 Portlet 中挑选地址并将它发送到购物车 Portlet,而不需要重新输入该地址。
实现
要启用一个用于通信的 Portlet,您需要:
- 为目标 Portlet 创建一个 WSDL 文件。
- 将一个需要的库复制到您的 Portlet 中。
- 在源 Portlet 中添加代码以生成 C2A 标记。
本文余下内容将描述如何逐一完成这些任务。
为目标 Portlet 创建 WSDL 文件
以下是购物车 Portlet(目标 Portlet)的样本 WSDL 代码。
清单 1. ShoppingcartC2A.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="Message_Service"
targetNamespace="http://www.ibm.com/wps/c2a/examples/shipping"
xmlns="http://schemas.xmlsoap.org/wsdl/"V
xmlns:portlet="http://www.ibm.com/wps/c2a"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.ibm.com/wps/c2a/examples/shipping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.xmlsoap.org/wsdl/
http://schemas.xmlsoap.org/wsdl/
http://www.w3.org/2001/XMLSchema
http://www.w3.org/2001/XMLSchema.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types>
<xsd:complexType name=
"AddressType">
<xsd:all>
<xsd:element name="fullname" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:string"/>
</xsd:all>
</xsd:complexType>
</types>
<message name="addressRequest">
<part name="address" type="tns:AddressType"/>
</message>
<portType name="Message_Service">
<operation name="addressDetailsOp">
<input message="tns:addressRequest"/>
</operation>
</portType>
<binding name="addressInfoBinding" type="tns:Message_Service">
<portlet:binding/>
<operation name="addressDetailsOp">
<portlet:action name=
"addressDetails"
caption="Address.Details" description="Get Address"/>
<input>
<portlet:param name=
"addressInfo"
caption="address.info"
class="java.util.Hashtable"
partname="address"
boundTo=
"request-attribute"/>
</input>
</operation>
</binding>
</definitions>
|
让我们看一看上面的 WSDL 代码中的关键组件。
消息类型
<types>
<xsd:complexType name="AddressType">
<xsd:all>
<xsd:element name="fullname" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:string"/>
</xsd:all>
</xsd:complexType>
</types>
|
AddressType 是源 Portlet 将用于传输消息的字段。每个注册接收这种类型的消息的 Portlet 都将在源 Portlet 的 JavaScript 弹出菜单(由 C2A 生成)中列出。在本例中,AddressType 是目标 Portlet(购物车 Portlet)注册接收的消息类型。目前在 WebSphere Portal V5 中,服务没有验证子元素。但是包含这些子元素可以提高可读性。
操作名
<portlet:action name="addressDetails" caption="Address.Details" description="Get Address"/>
|
action name 是目标 Portlet 用来接收一个操作的名称,其中包含
actionPerformed 方法。
Portlet 参数
<input>
<portlet:param name="addressInfo" caption="address.info"
class="java.util.Hashtable" partname="address" boundTo="request-attribute"/>
</input>
|
<input> input 块中的名称是 Portlet 用来接收消息的名称。
boundTo 字段指定了将消息传递给接收者所使用的传输。它可以是
request-parameter (缺省值)、
request-attribute 、或
session 。由于在本例中您将传送一个非字符串的值,所以请使用
request-attribute 或
session 来代替缺省的 request-parameter(它不能存储非字符串值)作为传输消息的方式。
class="java.util.Hashtable" 属性除了指定消息类型(AddressType)之外还指定 C2A 用于与消息匹配的一个类的名称。使用 C2A 生成标记时必须在源 Portlet 中指定它的值。在缺省情况下,它假定为字符串值。
<output> 如果您没有连接 Portlet,那么 output 参数并不重要。然而,如果您将要连接 Portlet,您应该定义一个 output 参数,以使连接工具能知道该 Portlet 可以向其他 Portlet 传递某些类型的消息,并且能够连接那些 Portlet。在地址簿 Portlet 的 WSDL 文件中您会看到 output 参数。
对于一个给定的业务您可以拥有(仅仅)一个 input 块和任意数目的 output 块。
总结一下,接收方 Portlet 在操作名为 addressDetails 的 actionPerformed 方法中获取消息,并将消息值储存到名为 addressInfo 的请求属性中。
复制所需的库
要为您的 Portlet 提供对 C2A 库的访问,您需要将 WebSphere Portal 产品库的
<WP_INSTALL_ROOT>\pb\lib 文件夹中的
pbportlet.jar 文件复制到您 Portlet 的
WEB-INF\lib 文件夹中。参阅
Enabling portlets for cooperation
以了解更多相关信息。
生成标记
最后一个步骤是生成 JavaScript 以及用户点击后能够触发向目标 Portlet 传递数据的操作的链接。
您可以通过两种不同的方式来生成 JavaScript 和链接。
- 使用 C2A 提供的标记库
如果您想要在 JSP 中包含链接并且可以采用标记格式化数据的缺省方式,那么您可以使用这一方法。然而,如果您尝试传递复杂数据类型,这种方法将无法工作,这是因为那些标记没有采用对象引用。
- 使用程序化方法
正如在 InfoCenter 中所描述的,您可以使用 PropertyBrokerService 程序化地生成工件。
使用程序化方法将有助于您实现:
- 格式化复杂情况中的数据。
- 在您的 Java 代码中生成标记。
- 传递复杂数据类型。
要了解有关使用程序化方法的更多信息,请参阅
Using Cooperative Portlets in WebSphere Portal V5。
要生成标记,在大多数情况下您应该能够无需任何修改地使用附带的 Portlet 中的
C2AHelper.getSourceItemForProperty()
方法。
清单 2. 生成标记的代码片断
//get the property broker
PropertyBrokerService broker =
(PropertyBrokerService) portletContext.getService(PropertyBrokerService.class);
Property property =
PropertyFactory.createProperty(portletRequest.getPortletSettings());
//name space for the input param defined in the target's wsdl
property.setNamespace(nameSpace);
// message type you would like to set
property.setType(messageType);
// property name has to match with the output param in source portlet's wsdl for wiring
property.setName(propName);
property.setDirection(Property.OUT);
property.setClassname(propValueObj.getClass().getName());
propValue = PropertyFactory.createPropertyValue(property, propValueObj);
// gets the markup
ActionTriggerMarkup shm =
broker.getActionTriggerf(portletRequest, portletResponse, propValue, true);
String onClickMarkup = shm.getOnClickMarkup();
String showActionsMarkup = shm.getShowActionsMarkup();
boolean isWired = shm.isWired();
|
使用
onClickMarkup
作为您的 HTML 锚标记的
onClick 事件。
showActionsMarkup
文本是当用户点击由 C2A 生成的链接时执行的 JavaScript。
isWired 布尔值表示是否已使用 Portlet Wiring Portlet 来连接该 Portlet。如果该 Portlet 已连接,那么在点击它时不显示弹出窗口;取而代之,它自动调用
wired 操作。
以下是由 showActionsMarkup 生成的一个示例:
<script language="JavaScript">
function func2(){
window.c2aMenu2 = new c2a_Menu();
c2aMenu2.formElementArray0 = new Array(1)
c2aMenu2.formElementArray0[0] = "pb-action-data-4"
c2aMenu2.c2a_addMenuItem('Address Details - Struts',
'javascript:c2a_invokeMenuAction("c2a_form_5_0_2E8_/ReceiveAddress_do",
window.c2aMenu2.formElementArray0)');
c2aMenu2.formElementArray1 = new Array(1)
c2aMenu2.formElementArray1[0] = "pb-action-data-5"
c2aMenu2.c2a_addMenuItem('Address Details - Regular',
'javascript:c2a_invokeMenuAction("c2a_form_5_0_2E6_addressDetails",
window.c2aMenu2.formElementArray1)');
c2aMenu2.formElementArray2 = new Array(1)
c2aMenu2.formElementArray2[0] = "pb-action-data-6"
c2aMenu2.c2a_addMenuItem('Invoke all actions',
'javascript:c2a_invokeMenuAction("c2a_form_5_0_2E5_c2a_broadcast_action",
window.c2aMenu2.formElementArray2)');
}
</script>
|
以下是由 onClickMarkup 生成的一个示例
window.c2a_showMenu(window.c2aMenu2, true, false, true, false, event);
|
技巧:如果您计划使用 Portlet 连接,您就需要在源 Portlet 的 WSDL 中为每个源 Portlet 支持的数据类型都定义一个输出参数。
地址簿 Portlet 包含一个
DummyC2A.wsdl ,以使它能够添加一个输出参数,该输出参数说明它正在广播
AddressType 类型的消息。输出参数中的消息名称是重要的,因为您在生成标记时必须使用它。
在本例中,用于地址簿 Portlet 的
Dummy.wsdl 包含一个名为
addressInfo_out 的
AddressType 类型的输出参数。因此,当您设置该属性的名称时,您必须使用
addressInfo_out 。
property.setName("addressInfo_out"); |
使用 Bean 来传递数据
现在您已了解如何使用 Hashtable 来传递数据。您还可以创建和使用一个用于传递数据的 Bean 类。为达到此目的,您需要创建一个 Bean 类,然后将其打包到一个 JAR 文件中,再把它放到
<WP_INSTALL_ROOT>\shared\app 中,此 Bean 类对所有的 Portlet 应用程序都是可见的。
下图显示呈现的 Portlet。
运行中的 Portlet 屏幕截图--正常
在 Struts Portlet 中使用 C2A
激活
broker.getActionTriggerMarkup() 的调用在 Struts 操作中不能正常进行,因为此方法仅在 Portal 的呈现阶段起作用,而 Struts 操作是在事件阶段执行的。您可以通过扩展 Struts Portlet 并覆盖
doView() 来避开它。然而,您可以从 JSP 内部调用它,JSP 是在呈现阶段被调用的。
下载中包含了此 Portlet 的 Struts 版本。
清单 3 展示了 Struts 的 WSDL 文件的区别。它包含了一个类型属性的声明以及接收消息的 Struts 操作的名称。
清单 3. Struts 的 WSDL 区别
<portlet:action type=
"struts"
name=
"/ReceiveAddress.do"
caption="Address.Details"
description="Get.Address"/>
|
下图展示了彼此共享数据的一个正常 Portlet 和一个 Struts Portlet。下载中包含了这两个 Portlet。
在 Struts Portlet 和正常 Portlet 之间共享数据
结束语
您已经了解了如何使用带有 Hashtable 的 C2A 在 Portlet 之间传递数据。这一功能对于您在 Portlet 之间传递对象是强大而且有效的。既然您已经了解如何使用一个 Hashtable 来实现这一功能,您就能够对其进行扩展以使用您自己的自定义 Bean 来传递数据。
最后需要注意的一点是:虽然这是一个很好的特征,但您还是应该小心不要在错误的环境中使用这一机制。如果您使用 Session 作为传输机制,而且对象很大,您就有可能陷入到性能问题中。同样地,这个方案可能超出您的需求。如果需求仅仅是传递一个字符串,那么您就只传递一个字符串好了。
下载 | 名字 | 大小 | 下载方法 |
|---|
| complex_c2a.zip | 1.7M | HTTP |
参考资料
关于作者  | 
|  | Varad Ramamoorthy 是一名 IBM Software Services for WebSphere 高级顾问,在位于北卡罗莱纳州罗利的 IBM Research Triangle Park 实验室工作。 |
对本文的评价
|