iWidgets and Business Space

June 17, 2010

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.

Advertisements

6 Responses to “iWidgets and Business Space”

  1. drdamour said

    Regarding the race condition. the iWidget runtimes don’t expect any intercommunication between widgets prior to every widget being loaded on the page. The widget is known to be loaded when the onLoad event handler returns.

    What you really want to do is fire your event in the onView handler (or the onModeChanged handler, if you don’t like the shortcut handler onView or you’re not using View as your default mode). At this point the runtime can transmit events between widgets.

    something to note, because onLoad is a synchronous method, if you need to load any asynchronous resources prior to your widget being marked “ready” you are mostly SOL

  2. djna said

    Thanks for the comment. I can see that your alternatives will work. Are you saying that the addOnLoad() approach I took still leaves a race condition?

    • drdamour said

      it might, i’m not super guru on dojo so i don’t know when that page level event fires, but it’s probably dependent on the method used to load the widgets. if the load order changed, you’d be in some trouble.

      onload is meant only to load resources you required. onView/onModeChange is where you’re supposed to perform your functionality (in general). you should avoid the concept of a “one-time run” scenario within your widgets (which you may be relying on by putting code in onload and hoping it gets called only once)

      I find it best to stick to the iWidget api whenever possible, then move to the BSpace api, and as a last resort use dojo’s apis.

  3. Vishal Mehta said

    I tried firing Event using following method this.iContext.iEvents.fireEvent(“Receive Role”, “JSON”, payload) but I don’t see any event fired. Even though my code is surrounded with try catch block but don’t see any exception. I have also checked the BS wiring.

    • djna said

      Hi Vishal, sorry to hear that you’re having a problem. I’m afraid that I don’t have much of an idea as to what’s wrong here, for me it al “just worked.”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: