Passing complex data types between cooperative portlets

This article shows how you can pass a complex data type between portlets in WebSphere Portal Version 5.

Varadarajan Ramamoorthy (varad@us.ibm.com), Senior IT Specialist, IBM Software Services for WebSphere

Author photoVarad Ramamoorthy is a senior consultant for IBM Software Services for WebSphere at the IBM Research Triangle Park Lab in Raleigh, North Carolina.



14 April 2004

Introduction

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.

For more information on developing portlets see the WebSphere Portal V5 InfoCenter and the Portlet development guide. For more information about WSDL, see the WSDL Specification.

The following software was used in testing this solution.

  • IBM WebSphere Portal Server Version 5.0.2
  • Portal fix PQ85663

Use case

Consider the following use-case:

  1. A page on a portal has two portlets: a shopping cart portlet and an address-book portlet.
  2. A user enters the portal to do some shopping.
  3. The user has previously listed several addresses in the address-book portlet.
  4. The user has selected an item to purchase, and added it to the shopping cart.
  5. 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.

Implementation

To enable a portlet for communication you:

  1. Create a WSDL file for the target portlet.
  2. Copy a required library to your portlet.
  3. Add code in the source portlet to generate the C2A markup.

The rest of the article describes how to each of these tasks.

Creating the WSDL file for the target portlet

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.

Message type

<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>

The 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 message would be listed in the source portlet's JavaScript pop-up menu, which is generated by C2A. In this case, the AddressType is the message type the target portlet (shopping cart portlet) is registering to receive. Currently, in WebSphere Portal V5, the service does not validate the sub-elements. But including them improves the readability.

action name

<portlet:action name="addressDetails" caption="Address.Details" description="Get Address"/>

The action name is the name with which the target portlet receives an action, within its actionPerformed method.

portlet param

<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-parameter (default), request-attribute , or session. 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.

The 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.

Copying the required library

To provide your portlets with access to the C2A libraries, you need to copy the pbportlet.jar from the WebSphere Portal product library, <WP_INSTALL_ROOT>\pb\lib to your portlet's WEB-INF\lib folder. See Enabling portlets for cooperation for more information.

Generating the markup

The last step is to generate the JavaScript and the link the user clicks to invoke an action which passes data to the target portlet.

You can generate the JavaScript and the links in two different ways.

  • 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 approach

    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:

    1. Format the data in complex situations.
    2. Generate the markup within your Java code.
    3. 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 C2AHelper.getSourceItemForProperty() 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();

Use the onClickMarkup for the onClick event for your HTML anchor tag. The showActionsMarkup text is the JavaScript that is executed when the user clicks on a link generated by C2A. The 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 wired action.

Here is an example of what's generated in 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>

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 addressInfo_out of type AddressType. Therefore, you must use addressInfo_out when you set the name on the property.

property.setName("addressInfo_out");

Using a bean to pass data

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
Screenshot of the portlet in action - Regular portlet

Using C2A in Struts portlets

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
Screenshot of the portlet in action - Struts and Regular portlet

Conclusion

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.


Download

DescriptionNameSize
Code samplecomplex_c2a.zip  ( HTTP | FTP )1.7 MB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=14598
ArticleTitle=Passing complex data types between cooperative portlets
publish-date=04142004