IBM Business Process Management products, and increasingly other IBM products, present user Interfaces in an extensible Web 2.0 Framework known as Business Space. The UI allows users to create their own views from visual components (widgets) supplied for each product. So for example, in WebSphere Process Server, there are widgets for presenting lists of tasks and working with the tasks. The widgets can be “wired” to each other so that actions in one widget can pass events to another.

The widgets are developed in JavaScript using the dojo framework, and conform to the iWidget specification, which predefines certain life-cycle events that the widget must support. You can develop you own widgets to be be used in conjunction with out of the box widgets.

I’ve been working to create a custom widget to be used in conjunction with the ECM Widgets delivered with IBM Filenet 4.5.1. This is using a version of Business Space consistent with that found in WID/WPS 6.2. This article concerns some wrinkles I came across. You should note that creating custom widgets in later versions of Business Space is rather easier than in these versions: in WID v7 there is tooling for creating iWidgets and a much simpler deployment model.

Widgets and Endpoints

In order to make custom widgets available for use you create an XML files containing catalogue entries. Placing these XML files in

<profile>/BusinessSpace/registryData

Will cause BusinessSpace to add corresponding entries to the iWidget pallette in the UI. It seems that different Business Space environments have subtly different requirements for the contents of this file. In my case, I omitted one stanza, and when deploying to the FileNet environment my widgets were not being recognised. It seems that the following file format works across my WPS and FileNet test environments.

Example Registry File

<?xml version="1.0" encoding="UTF-8"?>
<!– START NON-TRANSLATABLE –>
<tns:BusinessSpaceRegistry xmlns:tns="
http://com.ibm.bspace/BusinessSpaceRegistry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://com.ibm.bspace/BusinessSpaceRegistry BusinessSpaceRegistry.xsd ">
<!– END NON-TRANSLATABLE –>

  <tns:Endpoint>
       <tns:id>com.xyz.bspace.rootId</tns:id>
       <tns:type>com.xyz.bspace.rootId</tns:type>
       <tns:version>1.0.0.0</tns:version>
       <tns:url>XyzRoleWidget</tns:url>
        <tns:description></tns:description>
  </tns:Endpoint>

  <!– START NON-TRANSLATABLE –>
  <tns:Category>
    <tns:id>{com.xyz.bspace}Xyz</tns:id>
    <tns:name>Xyz Custom</tns:name>
    <tns:description>Custom Widgets for Xyz</tns:description>
    <tns:tooltip>Xyz</tns:tooltip>
    <tns:localeInfo>
    <!– END NON-TRANSLATABLE –>
      <tns:locale>en_US</tns:locale>
      <tns:name>Xyz Custom</tns:name>
      <tns:description>Custom Widgets for Xyz</tns:description>
      <tns:tooltip>Xyz</tns:tooltip>
    <!– START NON-TRANSLATABLE –>
    </tns:localeInfo>
    <tns:order>5</tns:order>
  </tns:Category>
  <!– END NON-TRANSLATABLE –>

  <!– START NON-TRANSLATABLE –>
  <tns:Widget>
    <tns:id>{com.xyz.bspace}Role</tns:id>
    <tns:version>1.0.0.0</tns:version>
    <tns:name>Role Selection</tns:name>
    <tns:type>{com.ibm.bspace}iWidget</tns:type>
    <tns:description>Role Selection and Event Emission</tns:description>
    <tns:tooltip>Role Select</tns:tooltip>
    <tns:categoryId>{com.xyz.bspace}Xyz</tns:categoryId>
    <tns:widgetEndpointId>com.xyz.bspace.rootId</tns:widgetEndpointId>
    <tns:url>/iwidget/role.xml</tns:url>
    <tns:helpUrl></tns:helpUrl>
    <tns:iconUrl>images/generic_iWidget.gif</tns:iconUrl>
       <tns:owner>IBM</tns:owner>
    <tns:email>TBD</tns:email>
    <tns:serviceEndpointRef required="true">
      <tns:name>serviceUrlRoot</tns:name>
      <tns:refId>com.xyz.bspace.rootId</tns:refId>
      <tns:refVersion>1.0.0.0</tns:refVersion>
    </tns:serviceEndpointRef>
    <tns:localeInfo>
    <!– END NON-TRANSLATABLE –>
      <tns:locale>en_US</tns:locale>
      <tns:name>Role Selection</tns:name>
      <tns:description>Role Selection Widget</tns:description>
      <tns:tooltip>Role Select</tns:tooltip>
    <!– START NON-TRANSLATABLE –>
    </tns:localeInfo>
  </tns:Widget>
  <!– END NON-TRANSLATABLE –>

<!– START NON-TRANSLATABLE –>
</tns:BusinessSpaceRegistry>
<!– END NON-TRANSLATABLE –>

The key entry here is the Endpoint entry. It is possible to place this in a separate endpoints file – many examples have xxxWidgets.xml and xxxEndpoints.xml – but it seems also to be possible to combine the entries in a single file. We discovered that if the endpoint entry is missing, in a FileNet environment the Pallette entry is not displayed. Curiously, in my WPS environment, the endpoint seems to be optional.

ECM Events

Many online examples of event emission use code such as this:

         var payload = {"name": data};
         this.iContext.iEvents.fireEvent("Receive Role", null, payload);

When firing event across to an ECM Widget we discovered that it was necessary to specify that second parameter, which is the type of the payload.

        this.iContext.iEvents.fireEvent("Receive Role", “JSON”, payload);

ECM inBasket

That got the event sent, and we wired the ECM inBasket to receive the event. Our intention was to allow the user pick a role and have that transmitted to the inBasket, but there was one more piece ta having that take effect: you also need to correctly configure the inBasket. In the configuration panel of the inBasket you can select a chosen role, if you do that then events are ignored. So instead you must select no role (an empty entry at the end of the list) in the inBasket configuration. With that done the events are delivered to the inBasket and we get the desired effect.

It’s all in the … Timing

Having got the payload nicely transferred there just one more problem. What happens when the page is first displayed? If the user has previously selected a role we want to make that the default. So I have used a cookie to record the current selection and so in my onLoad method I retrieve it:

     this.currentRole = dojo.cookie("XyzItems.currentRole");

Clearly, we want that current value to be transmitted to the inBasket so I also explicitly fire an event across:

     var payload = {"name": ""+ role};
     this.iContext.iEvents.fireEvent("Receive Role", "JSON", payload);

And in my test environment this works just fine. To my annoyance when deployed to a UAT environment the widget does not even load! That leads to two important learning points.

Make sure Exceptions are Handled

After some head-scratching I found that fireEvent() was throwing an exception and as my onLoad() method had no exception handling the exception was causing onLoad() to fail. Hence my widget didn’t complete its initialisation.

So Lesson Number One (obvious, so why did I forget to do it?) Don’t forget to have suitable Exception Handling.

But that’s not the end: why did we get an exception at all? In the test environment it was fine, why not in UAT?

Don’t do too much in onLoad

The exception was complaining that the receiving widget didn’t have an appropriate event handler. My inBasket doesn’t have an event handler? But there it is in the code! In my test environment it obviously does, it works!

Here we see a clasic little race-condition. Until the inBasket is properly initialised the implementation class may not be available. In UAT clearly I had a rather different set of performance characteristics. My code, running in the onLoad() method of my wiget was assuming that all event recipients were ready to receive events. Manifetsly, that’s not guaranteed while onLoad() is executing.

So what to do? Well this problem is nicely solved in the dojo/iWidget environemnt: It is possible to install a second callback to be executed when the whole page is initialised. You add this code in your onLoad() method:

     dojo.addOnLoad(this, "_onReallyLoaded");

and then fire the event from the _onReallyLoaded() mthod.