When you develop portlets, you often need to pass data between them. If you use portlet messaging, the portlets must be within the same portlet application, which is sometimes too restrictive. The newest technology for inter-portlet communication is cooperative portlets, also known as Click-to-Action (hereafter referred to as C2A).
C2A supports messaging even when the portlets are in different portlet applications, and it promotes reusability. In IBM® WebSphere®Portal V5 (hereafter called WebSphere Portal), C2A has a new feature which lets you pass complex data types between portlets. Using this feature, you can carry multiple properties across the portlets. For example, prior to V5, you would have passed only the user id to another portlet, and then you would get the actual information about the user from a data store, even though you had all the data in the source portlet. Or, you could have accomplished this by using a String Delimiter and parsed it, but that is just too cumbersome. Using this new feature in V5, you can pass all the data easily, using a bean or Hashtable.
This article is intended for developers who already have experience developing portlets for WebSphere Portal. You also need to have some knowledge of Web Services Definition Language (WSDL). After reading this article, you should be able to pass objects between portlets.
The following software was used in testing this solution.
- IBM WebSphere Portal Server Version 5.0.2
- Portal fix PQ85663
Consider the following use-case:
- A page on a portal has two portlets: a shopping cart portlet and an address-book portlet.
- A user enters the portal to do some shopping.
- The user has previously listed several addresses in the address-book portlet.
- The user has selected an item to purchase, and added it to the shopping cart.
- When the user is ready to check-out, he or she wants to pick that address from the address-book portlet and send it to the shopping cart portlet, instead of having to re-enter it.
To enable a portlet for communication you:
- Create a WSDL file for the target portlet.
- Copy a required library to your portlet.
- Add code in the source portlet to generate the C2A markup.
The rest of the article describes how to each of these tasks.
Here is sample Web Services Definition Language code for the shopping cart portlet (target portlet).
Listing 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>
Let's look at the key components of this WSDL code.
<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 is the field that the source portlet would use in order to transmit a
message. Every portlet which is registered to receive this type of
is registering to receive. Currently, in WebSphere Portal V5, the service does not validate the sub-elements. But including them improves the readability.
<portlet:action name="addressDetails" caption="Address.Details" description="Get Address"/>
action name is the name with which the target portlet receives an action, within its
<input> <portlet:param name="addressInfo" caption="address.info" class="java.util.Hashtable" partname="address" boundTo="request-attribute"/> </input>
<input> The name in the input block is the name with which the portlet could
receive the message. The
boundTo field specifies the transport in
which the message is delivered to the receiver. It could be
request-attribute , or
Because, in this case, you are going to transmit a non-String value, use the
request-attribute , or
session as the way of transporting the message instead of the
default request parameter, which cannot store a non-string value.
class="java.util.Hashtable" attribute specifies the name of the class which C2A uses for matching the message, in addition to the message type (AddressType). This has to be specified in the source portlet when generating the markup using C2A. By default, it assumes String.
<output > The output parameter is not important if you are not wiring the portlets. However, if you are going to wire the portlets, you should have an output parameter defined so that the wiring tool can know that this portlet can send certain types of messages to other portlets and it can wire them. You see the output parameter in the address-book portlet WSDL.
For a given operation you can have one (only) input block and any number of output blocks.
To summarize, the receiving portlet would get the message in the actionPerformed method with the action name addressDetails and the value is stored in the request attribute with the name addressInfo.
To provide your portlets with access to the C2A libraries, you need to copy the
the WebSphere Portal product library,
<WP_INSTALL_ROOT>\pb\lib to your portlet's
See Enabling portlets for cooperation
for more information.
- Using the taglibs provided with c2a
You can use this approach if you want to include the links in JSP and you can take the default way that the tag formats the data. However, this way will not work if you are trying to pass complex data type because the tags do not take an object reference.
- Using the programmatic
You could use the PropertyBrokerService to generate the artifacts programmatically, as described in the InfoCenter.
Using the programmatic approach is useful if you would like to:
- Format the data in complex situations.
- Generate the markup within your Java code.
- Pass complex data types.
For more information about using the programmatic approach, see Using Cooperative Portlets in WebSphere Portal V5.
To generate the markup, you should be able to use the
method in the attached portlet in most cases without any modifications.
Listing 2. Code snippet for generating the markup
//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.getActionTriggerMarkup(portletRequest, portletResponse, propValue, true); String onClickMarkup = shm.getOnClickMarkup(); String showActionsMarkup = shm.getShowActionsMarkup(); boolean isWired = shm.isWired();
onClick event for your HTML anchor tag.
isWired boolean value indicates if the portlet is wired using the Portlet Wiring portlet.
If the portlet is wired, then it does not display the pop-up when clicked; instead, it automatically calls the
Here is an example of what's generated in showActionsMarkup:
Here is an example of what's generated in onClickMarkup
window.c2a_showMenu(window.c2aMenu2, true, false, true, false, event);
Tip: If you are planning to use portlet wiring define an output parameter in the source portlet's WSDL for each data type supported by the source portlet.
The Address book portlet includes a
DummyC2A.wsdl so that it can add an
output parameter that says it is broadcasting a message of type
AddressType. The name of the message in the output
parameter is important because you must use it when you are generating the markup.
In this example, the
Dummy.wsdl for the address-book portlet includes an
output parameter with the name
Therefore, you must use
addressInfo_out when you set the name on the property.
So far you have seen how to pass data using a Hashtable. You could also create and use a bean class for passing the data. In oder to do that,you create a bean class, package it in a JAR file, and put it in
<WP_INSTALL_ROOT>\shared\app, which is visible to all portlet applications.
The image below shows the rendered portlet.
Screenshot of the portlet in action - Regular
The call to invoke
broker.getActionTriggerMarkup() won't work within Struts actions because this method only works in the render phase of the portal and the struts action is executed in the event phase. You could get around it by extending the Struts portlet and overriding
doView(). However, you could call it from within a JSP, which is called in the render phase. A Struts version of the portlet is included in the download.
Listing 3 shows the difference in the WSDL file for Struts. It includes the declaration of a type attribute and the name of the struts action which receives the message.
Listing 3. WSDL difference for Struts
<portlet:action type="struts" name="/ReceiveAddress.do" caption="Address.Details" description="Get.Address"/>
The figure below shows a regular portlet and a Struts portlet sharing data with each other. Both the portlets are included as part of the download.
Sharing data between a Struts portlet and a regular portlet
You have seen how to pass data between portlets using C2A with a Hashtable. This is powerful and helpful when you are passing an object between portlets. Now that you have seen how you could do it using a Hashtable, you could extend this to use a custom bean of your own to pass data.
One final note: even though this is a nice feature, you should be cautious about using this mechanism in the wrong circumstances. If you are using the session as a transport mechanism, and if the object size is big, you may run into performance issues. Also, the solution may be more overhead than you need. If the requirement is just to pass a String, then just pass a String.
WebSphere Portal V5 InfoCenter
Portlet development guide
Enabling portlets for C2A
WebSphere Portal zone