User Interfaces

Business Space

Business Space is a UI framework designed for end users to interact with BPM functions. Business Space is a specialization of the IBM Lotus Mashups technologies. Business Space is built from a set of one or more "spaces". Each space contains one or more pages. Each page contains one or more widgets that are laid out on the page.

Each BPM product installed adds one or more Business Space widgets to the catalog of available widgets.

Events

Widgets can communicate with each other through the publish/subscribe of events between them. When a widget is registered with Business Space, its registration describes which events it can publish to and which events it can subscribe upon. When a widget is added to a page, it can then be wired to other widgets. One widget will act as the publisher of the event, and the other act as the subscriber. Two widgets can only be wired together if they support the same type of event.

See Also – Business Space

Flex Custom Widgets in Business Space - Using Flex within Business Space

References – Business Space


Custom Widgets

The widgets provided by IBM for business space are not the only widgets possible. You can create and use your own widgets to augment the existing functions. There are a number of steps and piece-parts required to build a new widget.

A widget is configured to Business Space by providing an XML configuration file that corresponds to the iWidget specification. Contained within this file are a number of properties that are used by the Business Space runtime to control how the widget behaves and is displayed.

In addition to the visual characteristics of the widget, we should realize that a widget can send events to other widgets as well as received events from other widgets. This is a form of publish and subscribe. The XML document also holds the information on what a widget can send to other widgets and can receive from other widgets.

An example iWidget XML control file may look as follows:

<?xml version="1.0" encoding="UTF-8" ?> <iw:iwidget id="iWidgetEditorTests1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget" supportedModes="view" mode="view" lang="en" name="Hello"> <iw:content mode="view"> <![CDATA[ <div>Hello World</div> ]]> </iw:content> </iw:iwidget>

A deep understanding is recommend of the structure and semantics of this control file. IBM provides a rich function GUI editor that hides the majority of the details allowing values to be entered in a much more pleasing manner. See iWidget editor.

In addition to the iWidget XML control file, there is a second file which is a Business Space specific Catalog XML file. The purpose of this file is to tell Business Space about the nature of the widget so that Business Space can present this widget in its catalogs of available widgets.

Finally there is one more XML file of importance to us when building custom Business Space widgets. This file is the Endpoint Registration XML file. The purpose of this file is to provide information to the widget about where it should connect to at runtime in order to communicate with a WPS server instance. Business Space widgets typically make REST style communication calls to the WPS server.

iWidget Mode

One of the core concepts of an iWidget is its mode. Think of the mode of a widget as being a "state" that the widget as a whole may be in. Depending on the value of the mode, the widget can render itself in different ways. For example, if a widget is in view mode, then it might be displaying business data. If the same widget is in configuration mode, it might be displaying configuration data for itself such as which database to read data from.

A widget can be told its mode ... which is another way of saying how the widget is to represent itself for a particular task. As an example, setting its mode to:


Business space defines these three modes of operation.

iWidget Structure

The iWidgets supplied for Process Server can be found in the directory <ProcessServer>/BusinessSpace/widgets. These serve as a useful source of reference and should be examined to see how IBM has built its own widgets.

What follows next is a breakdown on the logical content of the iWidget XML documents. Note that these conform to the iWidget specification v2.0 which is very different from the previous version that was used in previous product releases.

The XML control file begins with an


iwidget

The <iw:iwidget> element is the root container in the document used to describe the iWidget. It has many possible attributes for its configuration.

Attributes:


Example

<iw:iwidget xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget" iScope="com.kolban.AppStarter" id="appStarter" name="appStarter" supportedModes="view">

resource

<iw:resource id="{resourceName}" src="{uri}" version="{version}" mimeType="{mimeType}" callback=""/>

Attributes:


Example

<iw:resource src="appStarter.js" />

eventDescription

Attributes:


event


content

This tag contains the content that will be displayed/loaded in the Widget. This is usually HTML. The <iw:content> element is a child of the <iw:iwidget> element.

Attributes:

iWidget Modules/Data Structures


Managed Item Set

The ManagedItemSet is part of the iWidget specification. It defines data that is managed by the widget itself. This includes saving the data for later retrieval. This can be used for setting the configuration/properties of the widget so that later it can be restored.

The ManagedItemSet for an iWidget can be retrieved from the widget's iContext using the getiWidgetAttributes() function.

ManagedItemSet getiWidgetAttributes();

For example,

var myAttributes = this.iContext.getiWidgetAttributes();

Although the iWidget spec shows that the setItemValue function can take an object to save, reality is that there is a limitation of the iWidget container. To help with this, we should encode the object that we want to save as a string and save the string value as the attribute. In JavaScript, we can employ the JSON encoding and leverage the dojo.toJson function to encode a JavaScript object. When we retrieve the value, we can use the dojo.fromJson function to get the JavaScript object back again.

For example:

var x = new Object(); x.a = "Hello"; x.b = "World"; var myAttributes = this.iContext.getiWidgetAttributes(); myAttributes.setItemValue("test", dojo.toJson(x), false); myAttributes.save(); // Retrieve … var y = dojo.fromJson(myAttributes.getItemValue("test"));

Also note that saving attributes is recommended when the widget is in 'edit' mode. The actual persisting of these attributes back to the server only occurs when the widget goes from the edit mode back to view mode.

Functions on ManagedItemSet

ManagedItemSet setItemValue(in String itemName, in Object value, in boolean readOnly /*optional*/); Object getItemValue(in String itemName); ManagedItemSet removeItem(in String itemName); String[] getAllNames(); boolean isReadOnly(in String itemName); null save(in Function callbackFn /*optional*/); boolean addListener(in Function listener); boolean removeListener(in Function listener); ManagedItemSet clone();


function(in String managedItemSetName, in boolean success);

addListener This method returns a boolean indicating whether or not the request to add a listener to changes in iWidget v1.0 Specification the ItemSet was successful. Note that the signature for such a listener is:

module { null listener(in iEvent ev); }

removeListener This method returns a boolean indicating whether or not the request to remove a listener was successful.

clone This method returns a new ManagedItemSet that is a duplicate of the current ManagedItemSet. While this method does provide for cloning all the values within the ManagedItemSet, in general this will only clone the data fields for complex Objects, both type information and any embedded logic will most likely be lost.

iWidget iContext

ManagedItemSet getiWidgetAttributes(); ManagedItemSet getUserProfile(); ManagedItemSet getiDescriptor(); ItemSet getItemSet(in String name, in Boolean private); Object iScope(); String processMarkup(in String markup);

null processiWidgets(in DOMNode node); Element getElementById(in String id); Element[] getElementByClass(in String className); Element getRootElement(); null requires(in String requiredItem, in String version /*optional*/, in String uri, in Function callbackFn /*optional*/); iEvents iEvents; IO io; xml xml; String widgetId;


getiDescriptor This method returns the ManagedItemSet that provides access to attributes that both the iContext and the iWidget need to understand. If there is no ManagedItemSet related to the iWidget's descriptive items, this method MUST create an empty set and return it.

getItemSet This method returns an ItemSet corresponding to the requested name. If it does not already exist, an ItemSet will be created and associated with the supplied name. If a new ItemSet is created, the "private" parameter controls whether or not the ItemSet can be exposed to other components. If the ItemSet already exists, the "private" parameter is ignored. If access to the desired ItemSet is denied or the ItemSet cannot be created, null is returned.


function(requiredItem, uri, resourceHandle /* when appropriate */)

The following names refer to optional functionality defined here:

io

xml

iEvents This field contains an object that provides access to event services in a manner allowing the iWidgets on the page to interact in a loosely coupled manner while also providing control to whomever is defining the overall page/application. Types related to eventing are defined in a separate section below.

io This optional field contains an object that provides access to io services in a manner allowing the page as a whole to remain consistent, particularly in the face of server-side coordination between related application components. See also: iWidget IO.

xml This optional field is a placeholder for future definitions providing explicit support for XML-oriented processing. This version of the specification provides no such definitions, but they are expected in future versions. Extensions supported by more advanced iContexts SHOULD follow the pattern used for the iEvents and IO portions of these definitions. This allows iWidgets to determine the support for a "foo" extension defined by a group named "bar" using a construct of the form:

var fooSupported = (iContext._bar & iContext._bar.foo);

widgetId This field appears to be the Dojo Dijit ID for the Dijit Widget

Constants

iContext.constants.mode.VIEW = "view" iContext.constants.mode.EDIT = "edit" iContext.constants.mode.HELP = "help" iContext.constants.ATTRIBUTES = "attributes" iContext.constants.IDESCRIPTOR = "idescriptor" iContext.constants.USERPROFILE = "userprofile" iContext.constants.keys.SHIFT = 1 iContext.constants.keys.ALT = 2 iContext.constants.keys.CTRL = 4 iContext.constants.keys.META = 8 iContext.constants.keys.CAPSLOCK = 16


iEvents

module iEvents { null publishedEvents(in iEventDescription eventDesc[]); null handledEvents(in iEventDescription eventDesc[]); null fireEvent( in String name, /* event name, preferrably a serialized QName */ in String type, /* optional reference to type, preferrably a serialized QName */ in Object payload /* optional ... the event's data */); }


io

XMLHttpRequest XMLHttpRequest(); URI rewriteURI(in URI uri); XMLHttpRequest request(in requestVerb, in URI uri, in Function callbackFn, in String message /*optional*/, in [{headerName, value}] requestHeader /*optional*/);


Notes

Getting the URL for the widget

The URL for the widget can be obtained using:

var rootString = io.rewriteURL("");

Misc

After changing the Business Space Registry, much of the documentation says to execute an expensive restart of the server. Experience so far seems to be showing that simply stopping and then restarting the application called IBM_BSPACE_WIDGETS appears to be sufficient.

iWidget editor

In the latest versions of ID and RAD, there is a an iWidget editor built into the development tooling. The iWidget editor is documented in the RAD InfoCenter. This editor visualizes the iWidget XML description file in a custom editor that is designed to show the values of an iWidget. In addition, an iWidget control file can be created as a new artifact. To create a new iWidget, select New from a Web project. You must be in the Web Perspective. In the list of available artifacts, the iWidget entry can be found:

If the entry does not show up in the list, choose it from the New artifact list. It can be found in the Web folder.

This launches a wizard in which details of the new iWidget can be entered:

In the editor, the iWidget has a drop-down selection for the type of iWidget. The selections available are:


Once created, a new XML artifact appears in the project. To open this file in the iWidget editor, select Open With → Other and select iWidget Editor

Here is an example of an iWidget file opened in the editor with some data entered:

BSpaceWidgetRegistry

getWidgetRegistry() Returns the business space registry for the widget that contains the instance of this class.

getWidgetRegistryServiceURI() Returns the service URI that returns all the widget registries.

getWidgetRootURI() Returns the URI to the location of the widget definition XML.

getWidgetInfoByWidgetId(widgetId) Undocumented.

getServiceEndpoint(key) Returns the endpoint that matches the given key.

getServiceURLRoot() Returns the service URI root of the business space.

isInBSpace() Returns true if this widget is hosted in business space. False otherwise.

getDefinitionXMLPath() Returns the path to this widget's definition XML.

BSpaceGeneralHelper


Registering Widgets

In WPS 7.0, a new wsadmin task was introduced that performs the installation actions more elegantly than previous releases. The command is called AdminTask.installBusinessSpaceWidgets.

The syntax for the new WSAdmin task is as follows:

AdminTask.installBusinessSpaceWidgets('[ -nodeName *nodeName *-serverName *serverName *-widgets pathToWidgetZipFile]')

The input to this file is a manually created ZIP file. To create the ZIP file, first create a new folder. In that folder, create the following sub-folders:

ear Contains the EAR file for the Widget Web Project. This EAR contains the iWidget XML file and the JavaScript amongst possible others.

catalog Contains the catalog_nameOfWidget.xml file. The structure and content of this file is described at The Catalog File.

endpoints Contains the endpoint XML file

help (optional)

Once done, ZIP up the data so that these folders are the immediate children in the ZIP. This is the ZIP file to be passed to the installBusinessSpaceWidgets script in the -widgets parameter.

The effect of running this command will be to install the EAR contained within the ZIP as well as register the new Business Space widget. Experience shows that although the EAR application is installed, it is not automatically started. It must be started before the widget can be used in Business Space.

An almost identical command is available to remove (un-install) a previously installed widget. Like the install script, the un-install script takes the ZIP file as input.

AdminTask.uninstallBusinessSpaceWidgets('[ -nodeName *nodeName *-serverName *serverName *-widgets pathToWidgetZipFile]')

Updating the information about the widget in WPS can also be achieved through scripting:

AdminTask.updateBusinessSpaceWidgets('[ -nodeName *nodeName *-serverName *serverName *-catalogs pathToCatalogXMLFile]')

The same command can be used to update the endpoints XML file:

AdminTask.updateBusinessSpaceWidgets('[ -nodeName *nodeName *-serverName *serverName *-endpoints pathToEndpointsXMLFile]')

After installing or updating a widget (at least in test), restart the server.

When installing a widget, the catalog file is copied into:

<Profile>/BusinessSpace/Node/Server/mm.runtime.prof/config

Deleting the file from this location will also delete the widget. The file called catalog_default.xml should also be edited to remove the associated includes.

Similarly, a directory called:

<Profile>/BusinessSpace/Node/Server/mm.runtime.prof/endpoints

contains the endpoint files.

The Catalog File

The XML Catalog file that is used to register a widget does not appear to be any known industry standard. It appears to be IBM specific and very technical in nature at that. No publicly understood documentation is known to exist that describes this mysterious content. By examination (and guesswork), the Catalog XML file seems to contain the following information.

In WPS v7.0, a new catalog structure was designed that differs from that of previous releases. The name of the XML file should be:

catalog_nameOfWidget.xml

Documentation on this is currently poor. Its general structure appears to be:

<catalog id="???"> <resource-type>Catalog</resource-type> <category name="???"> <title>Text</title> <description>Text</description> <entry id="{namespace}name" unique-name="{namespace}name"> <title>Text</title> <description>Text</description> <definition>Path???</definition> <preview>Path???</preview> <icon>Path???</icon> <previewThumbnail>Path???</previewThumbnail> <shortDescription>Text</shortDescription> <metadata name="com.ibm.bspace.version">1.0.0.0</metadata> <metadata name="com.ibm.bspace.owner">IBM</metadata> <metadata name="com.ibm.bspace.serviceEndpointRefs"> [{ "name": "serviceUrlRoot", "required": "false", "refId": "endpoint://{namespace}name", "refVersion": "1.0.0.0" }] </metadata> </entry> <category> </catalog>

The text entries in the XML Catalog can be NLS encoded using the following format:

<nls-string lang="en">Human Readable Text</nls-string>

The fields in the Catalog structure are as follows:



It is also used in the category selection in a couple of places: and

catalog/category/entry/description The description shows up in the category selection

catalog/category/entry/definition This entry points to the path of the iWidget XML file available from a URL. This links together the Catalog data and the iWidget XML description data. It is from here that the runtime can obtain the iWidget XML description document.



Registry Endpoints XML

The endpoint registry XML example looks as follows:

<?xml version="1.0" encoding="UTF-8"?> <BusinessSpaceRegistry xmlns="http://com.ibm.bspace/BusinessSpaceRegistry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://com.ibm.bspace/BusinessSpaceRegistry BusinessSpaceRegistry.xsd "> <Endpoint> <id>{namespace}name</id> <type>{namespace}name</type> <version>1.0.0.0</version> <url>/WebPath/</url> <description>Description</description> </Endpoint> </BusinessSpaceRegistry>

BusinessSpaceRegistry/Endpoint/id The ID of this endpoint information. This is what is references in the Registry Widget XML in the tag serviceEndpointRef/refId.


JavaScript for a new widget

When a widget is loaded by Business Space, that widget usually supplies some JavaScript code to be executed. As the Business Space widget is loaded and used, callbacks are executed into this code. It is this code that controls the user interactions and makes REST calls to back-end servers and updates the HTML of the page to display or change content. JavaScript does not natively have similar concepts to Java of package names and class names. Dojo provides a similar mapping using the two Dojo methods called dojo.provide and dojo.declare. Using these methods provides the ability to declare the Java equivalent of a Class type in a package. This is used with custom Widgets in the iWidget specification. In the iWidget definition, there is an attribute called iScope. This takes the name of the declared JavaScript class. When an instance of a Business Space widget is created on the page, a corresponding instance of the JavaScript class object is also created.

dojo.provide("com.kolban.SampleWidget"); dojo.declare("com.kolban.SampleWidget", [dijit._Widget], { "onLoad": function() { dojo.mixin(this, new com.ibm.bspace.common.util.widget.BSpaceCommonUtilityLoader()); this.require("com.ibm.bspace.common.util.widget.BSpaceGeneralHelper"); }, "onUnload": function() {}, "onReload": function() {}, "onRefreshNeeded": function() {}, "onSizeChanged": function() {} });

Functions

The JavaScript written to implement the widget contains callback functions that are called by the Business Space framework. The names of these functions are architected. What follows is a brief description of the different callbacks available.


In addition, for each of the modes, a callback function is available for when the mode is selected:

onview

onedit

Modes

A widget can be in any one of a number of modes. The common modes are view and edit. To switch mode, the iContext event called onModeChanged can be invoked.

this.iContext.iEvents.fireEvent("onModeChanged", null, "{newMode: 'view'}");

HTML rewriting

It is possible that there is HTML re-writing going on in the environment. I seem to see that the widget.xml contains:

_IWID_

is being replaced with a UUID which I believe to be the iWidget ID (what ever that means). This same widgetID can be obtained from iContext.widgetId.

Debugging iWidget JavaScript

Assuming you are running the iWidget in FireFox with FireBug installed, adding the JavaScript code statement:

debugger;

causes a breakpoint to be reached which stops the execution and throws you into the FireBug JavaScript debugger.

Adding the JavaScript:

console.log("Text");

caused the text to be logged to the FireBug console.

Event handling

A widget can send (publish) and receive (handle) events from other widgets. IBM's supplied Business Space widgets both publish and handle events. This means that custom widgets can be wired together with the IBM widgets to react or cause reaction. In order for two widgets to be wired together, one needs to publish an event interface and the other needs to handle the exact same event interface. The configuration that describes published and handled events is performed in the iWidget XML document. There are two tags of interest. These are:

eventDescription

event

The eventDescription contains:

<iw:eventDescription id="{eventDescName}" payloadType="{payloadType}" description="{description}" lang="{locale}" title="{title}"> <!-- one per locale --> <iw:alt description="{description}" title="{title}" lang="{locale}" /> </iw:eventDescription>

id This appears to be a unique ID for the eventDescription. It must match the value used in the eventDescName in the event tag.

payLoadType This describes the type of data that can be incoming.

lang A locale (eg. "en")

description A textual description of the event

title Unknown. Although this shows up in the spec, it does not show up in the iWidget editor.

The event contains

<iw:event id="{eventName}" eventDescName="{eventDescName}" published="{boolean}" handled="{boolean}" handlerItemName="{attributeValue}" onEvent="{handlerName}" />

id The identity of the event. This must match between event sender and receiver.

To send an event one uses the method called:

iContext.iEvents.fireEvent()

The function defined in the onEvent attribute of the event tag accepts a single parameter defined as follows:

module iEvent { String name; String type; Object payload; String source; }


When the JavaScript function named in the <iw:event onEvent=...> entry is called, it is passed a JavaScript Object as a parameter. This object contains at least the following:

The iWidget specification provides a set of predefined events that callback into the JavaScript code without event specifications in the XML file having to be coded. These are:


{ "newMode": value }

Called when the mode of the widget has changed

{ "newWidth": value, "newHeight": *value *}


Dojo Level Workarounds

The level of Dojo distributed with Business Space is back-level compared to the latest possible Dojo release. At the time of writing, the level of Dojo supplied is 1.4.3. Many of the expected Dojo functions simply aren't there. Here are some of them (but by no means all) and some suggested workaround:

None at this time.

Debugging and Problem determination

If after building a custom widget, things are not working as expected, here are some tips to follow to see what might be going wrong:

In the catalog file, there is an entry called <definition>. This defines a WEB path to the iWidget xml file. Open a browser and attempt to access this file. For example:

http://localhost:9080/PathToWidgetXMLFile

After making changes to the definitions, consider restarting the Business Space server. When changes actually take effect is not completely known.

When logging JavaScript objects, consider using the FireBug command:

console.debug(object)

This will log the object to the console in an expanded form that allows one to interrogate the object's contents very easily.

While developing Custom Widgets, changes are frequently made to the code files. If a browser caches code, then re-testing can be a challenge. In FireFox 3, the Function Key 5 (F5) causes a re-load of the current page bypassing any cached files.

Assuming you are running the iWidget in FireFox with FireBug installed, adding the JavaScript code statement:

debugger;

causes a breakpoint to be fired when reached which stops the execution and throws you into the FireBug JavaScript debugger.

If the widget appears but there is no flex content, check that the expected SWF file is the one named.

See Also:


Custom Widget Walk through

In this section we will walk through the creation of a trivial custom widget from beginning to end to demonstrate the creation of such an entity. It assumes familiarity with the previous topics

We create a new Dynamic Web based project to hold our new widget. We call the project MyTestWidget and have it associated with a deployable EAR called MyTestWidgetEAR. This project will host the JavaScript and iWidget XML file.

Now we need to create the JavaScript file that will be called to handle events.

dojo.provide("com.sample.MyTestWidget"); dojo.declare("com.sample.MyTestWidget", null, { onLoad: function () { console.log("onLoad called"); }, onUnload: function() { console.log("onUnload called!"); }, onview: function() { console.log("onview"); }, onedit: function() { console.log("onEdit called!"); }, onReload: function() { console.log("onReload called"); }, onRefreshNeeded: function() { console.log("onRefreshNeeded called!"); console.log("Root: " + this.getWidgetRootURI()); console.log("here: " + this.iContext.io.rewriteURI("")); }, onSizeChanged: function(event){ console.log("onSizeChanged called"); console.log("New sizes: width=" + event.payload.newWidth + " height=" + event.payload.newHeight); }, /** * Sample Event handler … */ handleEvent: function(event) { console.log("handleEvent called! : " + event.payload); } });

Create the iWidget XML file. Right click in the Web Content folder of the new project and select create New > Other. From the Web folder of the New dialog, find and select iWidget.

The name of the new iWidget should be MyTestWidget.

Open the newly created iWidget XML file with the iWidget editor. This may have to be done with the Open > Other and then select the editor explicitly.

Give values to some of the iWidget attributes such as name and iScope. The iScope parameter MUST has the Dijit name of the widget. Save the result and close the editor.

Here is the data contained in the iWidget XML file:

<?xml version="1.0" encoding="UTF-8" ?> <iw:iwidget id="MyTestWidget" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget" supportedModes="view" lang="en" iScope="com.sample.MyTestWidget"> <iw:content mode="view"> <![CDATA[ <div>Hello World</div> ]]> </iw:content> <iw:resource src="MyTestWidget.js"/> </iw:iwidget>

Create a General Project called MyTestWidgetPackage. This project will be used to hold Business Space widget installation and packaging artifacts.

In the new project, create three simple folders called:

catalog

ear

endpoints

The result should look as follows:

In the catalog folder, create a file called catalog_MyTestWidget.xml and in the folder called endpoints create a file called endpoints.xml. These should be created as simple files.

Copy the following XML fragment into the content of the file catalog_MyTestWidget.xml. This fragment is a template for what we need and will be further edited.

<?xml version="1.0" encoding="UTF-8"?> <catalog id="myWidget"> <resource-type>Catalog</resource-type> <category name="myWidget"> <title> <nls-string lang="en">My Widget</nls-string> </title> <description> <nls-string lang="en">My Widget description.</nls-string> </description> <entry id="{mynamespace}myWidget" unique-name="{mynamespace}myWidget"> <title> <nls-string lang="en">My Widget Title 2</nls-string> </title> <description> <nls-string lang="en">My Widget Description 2</nls-string> </description> <definition>/WebRoot/myWidget.xml</definition> <preview>/WebRoot/images/preview_myWidget.gif</preview> <icon>/WebRoot/images/icon_myWidget.gif</icon> <previewThumbnail>/WebRoot/images/thumb_myWidget.gif</previewThumbnail> <shortDescription> <nls-string lang="en">My Short Description</nls-string> </shortDescription> <metadata name="com.ibm.bspace.version">1.0.0.0</metadata> <metadata name="com.ibm.bspace.owner">IBM</metadata> <metadata name="com.ibm.bspace.serviceEndpointRefs"> [{ "name": "serviceUrlRoot", "required": "false", "refId": "endpoint://{mynamespace}myWidget", "refVersion":"1.0.0.0" }] </metadata> </entry> </category> </catalog>

Change the following:

All references to myWidget to be MyTestWidget

All references to WebRoot to be MyTestWidget

The result will be:

<?xml version="1.0" encoding="UTF-8"?> <catalog id="MyTestWidget"> <resource-type>Catalog</resource-type> <category name="MyTestWidget"> <title> <nls-string lang="en">My Test Widget</nls-string> </title> <description> <nls-string lang="en">My Test Widget description.</nls-string> </description> <entry id="{mynamespace}MyTestWidget" unique-name="{mynamespace}MyTestWidget"> <title> <nls-string lang="en">My Test Widget Title 2</nls-string> </title> <description> <nls-string lang="en">My Test Widget Description 2</nls-string> </description> <definition>/MyTestWidget/MyTestWidget.xml</definition> <preview>/MyTestWidget/images/preview_myWidget.gif</preview> <icon>/MyTestWidget/images/icon_myWidget.gif</icon> <previewThumbnail>/MyTestWidget/images/thumb_myWidget.gif</previewThumbnail> <shortDescription> <nls-string lang="en">My MyTestWidget Short Description</nls-string> </shortDescription> <metadata name="com.ibm.bspace.version">1.0.0.0</metadata> <metadata name="com.ibm.bspace.owner">IBM</metadata> <metadata name="com.ibm.bspace.serviceEndpointRefs"> [{ "name": "serviceUrlRoot", "required": "false", "refId": "endpoint://{mynamespace}MyTestWidget", "refVersion":"1.0.0.0" }] </metadata> </entry> </category> </catalog>

Copy the following XML fragment into the endpoints.xml file:

<?xml version="1.0" encoding="UTF-8"?> <BusinessSpaceRegistry xmlns="http://com.ibm.bspace/BusinessSpaceRegistry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://com.ibm.bspace/BusinessSpaceRegistry BusinessSpaceRegistry.xsd "> <Endpoint> <id>{mynamespace}myWidget</id> <type>{mynamespace}myWidget</type> <version>1.0.0.0</version> <url>/WebRoot/</url> <description>Location of MyWidget</description> </Endpoint> </BusinessSpaceRegistry>

Change all occurrences of myWidget to MyTestWidget and the WebRoot to also be MyTestWidget. The result will be:

<?xml version="1.0" encoding="UTF-8"?> <BusinessSpaceRegistry xmlns="http://com.ibm.bspace/BusinessSpaceRegistry" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://com.ibm.bspace/BusinessSpaceRegistry BusinessSpaceRegistry.xsd "> <Endpoint> <id>{mynamespace}MyTestWidget</id> <type>{mynamespace}MyTestWidget</type> <version>1.0.0.0</version> <url>/MyTestWidget/</url> <description>Location of MyWidget</description> </Endpoint> </BusinessSpaceRegistry>

Now we want to generate the EAR file for the MyTestWidget Web project and save the result in the file system underneath the ear folder in the packaging project.

Refreshing the packaging project now shows that the EAR is contained in the ear folder:

Generate a ZIP file containing just these folders and their contents. This can be done through the ID Export option. Remember to select "Create only selected directories" to ensure that no extra directories are created that we do not want.

If examined in a ZIP tool, the following would be shown:

Once the ZIP file has been created, the Widget is ready to be installed. Use the wsadmin script to install the widget. Make sure that the script is pointing to the ZIP file that was just created. See Registering Widgets. After installation has been completed, restart the WPS server to ensure that all changes have taken effect. Once the server has been restarted, Business Space can be launched. In the page editing section, we will now see a new widget that can be added to a page:

Once added, we can see it in place within the Business Space environment:

It isn't a very exciting widget … but it is indeed a custom widget.

Using RAD 8.0.3 to create an iWidget

Mashup Center

See also:

Multiple Browser instances and BPM

When developing or working with BPM solutions, it is not uncommon to want to work with multiple browsers open on your desktop to a variety of BPM applications. Such applications can include:


In a secure WAS environment, each of these applications requires you to authenticate with the target WAS server before the application can be used. Unfortunately, this can cause a problem. To understand this, let us examine how WAS authentication through a browser works.

When you login to WAS and provide a user name and password, that data is sent to WAS only once. WAS validates that the user name and password match and generates a security token. This token is probably no more than a long sequence of characters. When the browser subsequently interacts with WAS, the token is passed back from the browser back to WAS. On seeing this token, WAS now knows who you are and trusts the previous authentication. This means that the userid and password only ever flowed to WAS once. All transmissions between the browser and WAS occur over the encrypted HTTPS (SSL) transport and hence the content of the token is never exposed on the network and can't be sniffed and replayed by other users.

When the token is originally generated by WAS and sent back to the browser, the token is saved locally by the browser in the form of a cookie. This cookie containing the token is what is sent back to WAS when the browser makes subsequent calls back to WAS.

So far ... no problems.

Now imagine bringing up two browser windows or tabs running in the same browser. If we sign-on to a WAS based application on one window, a token is created and saved in the local cookie store. If we sign-on to WAS in the second window, again a token is created and saved in the local cookie store. The problem is ... is that there is only one cookie store shared by all browser windows/instances. So if you sign-in as user "A" in one browser instance and then sign-in as user "B" in a second browser instance, the token/cookie for user "A" is replaced with the token/cookie for user "B".

From a users perspective, this manifests itself as either getting unexpected results when working with WAS applications in different browser windows or else we get additional requests to authenticate as the cookies are constantly being expired and refreshed.

Fortunately, there is a solution.

When we use the FireFox browser, each instance of the browser can have its own private profile. Think of this as a complete set of configurations including the storage area where cookies are saved. If we want to have multiple browsers up and running then we can launch multiple instances of FireFox, each with its own profile.

The FireFox program has two command line flags that should be supplied:

-ProfileManager

-no-remote

The first parameter causes FireFox to display the ProfileManager dialog which allows us to create and select the profile to be used.

The second parameter forces FireFox to use a second profile even if an existing instance of FireFox is already running. Without this flag, a second profile will be ignored and FireFox will use the profile in effect for the first instance started.

The ProfileManager dialog is shown next:

Use the Create Profile button to create as many profiles as desired. To start FireFox with a specific profile, select it and click the Start Firefox button.

Note that multiple tabs in a single instance of FireFox will always share the same profile so utilize multiple profiles and hence multiple browser instances to keep them separate.

Business Space Supplied Widgets

Business Space comes with a variety of Widgets. As other BPM products are installed, so too are additional sets of Widgets.

Business Configuration

Business Rules

Human Task Management

The Human Task Management category of Business Space contains the following widgets:


Escalations List

Human Workflow Diagram

A BPEL process can contain a set of Human Tasks. The Human Workflow Diagram widget allows us to visualize the human tasks within instances of BPEL processes and see where we are within the process with respect to the human tasks being executed. It dynamically examines the process associated with a selected task and draws a picture of where that task is within the process relative to other tasks.

The following illustrates an example of the Human Workflow Diagram.

The widget responds to the following events:


An example wiring of this widget would be to a Tasks List widget with:

Tasks List (Item Selected) → Human Workflow Diagram (Open)

My Team's Tasks

My Work Organizer

Process Definitions List

Processes List

Task Definitions List

Task Information

Tasks List

|| |Action Requested|| |Focus Changed|| |Items Selected|| |Task Released|| |Task Delegated|| |Task Terminated|| |Task Deleted|| |Task Claimed|| |Escalation Started||

Problem Determination

Solution Administration

Solution Operation

User Management

Viewers

Web Viewer

The Web Site viewer widget displays the contents of a web site. The web site displayed can be configured in the settings for the widget or can be passed in via an event definition.

Script Adapter

The Script Adapter widget can be wired to receive events published by any other widget. When it receives an event, it can then forward that event onwards to any other wired widget. The value of this is two-fold. First, it can be used during widget development for debugging. When one widget publishes an event, the fact that the event was published and its payload can be logged by the Script Adapter. Secondly, JavaScript source code can be supplied as a configuration parameter to the Script Adapter widget. This JavaScript can operate against the payload data and potentially massage or transform its content. The passed in data is available in a variable called payload. Payload will be a String. If it contains JSON encoded data, then the data will have to be expanded. An example of this is:

var myObject = eval('(' + payload + ')');

Visual Step

JSPs

See Also:

WebSphere Portal

See also:

Liferay

Liferay is an Open Source Portal environment.

Installing Liferay on WAS

Liferay can be installed on WAS v8 environment. Since we are working with IBM BPM, chances are good that our familiarity with WAS v8 will be higher than other Java EE platforms.

We start our installation by creating a new WAS profile. I'll assume you know how to do that. Create the profile with no special augmentations.

Download the Liferay package without a bundled Java EE environment. This can be found in the Additional Files page at the Liferay web site:

A Zip file called "liferay-portal-depenencies" is supplied which contains JAR files that need to be added to the WAS class path. As of writing, these are:


These should be copied to <WAS>/lib/ext

Now we start the WAS Server. Experience has shown that the default max heap size of 256MB is not enough. I increased to 512MB. Once started, bring up the WAS Admin console and install the Liferay WAR file. Accept all the default options. The WAR is pretty large, it make take a few moments to upload and then parse for installation.

Using the Liferay Tomcat bundle

One of the bundles available for Liferay is an integrated Tomcat environment. After extracting the content to a folder, we can launch the Tomcat environment using:

<Liferay>/tomcat/bin/startup

Developing Liferay Portlets

First we will want to install the Liferay development environment. The full recipe for this can be found in the Liferay documentation but here are some notes that I used to get it working. First I downloaded Eclipse for Java EE 4.2.1. Once launched, I used the Eclipse in-built updated to install the Liferay IDE from the following Eclipse update site:

http://sourceforge.net/projects/lportal/files/Liferay IDE/1.6.1/updatesite/

Liferay supports the JSR-286 Portlet Specification

See also:

Lotus Forms

See Also:

Dojo

Custom Dojo development environment

At times we want to work with the Dojo generated by WID. WID places these files in a Web Project for deployment to the WPS server for subsequent retrieval by Business Space. If we want to modify the generated HTML files, we find that we are constantly redeploying the Web project so that the changes become visible. During development of UIs, we constantly find ourselves wishing to make changes so this update/publish sequence can be annoying. Fortunately, there is an elegant solution.

When the web project containing the HTML is published to WPS, the files for the HTML can be accessed from the file system under the WPS profile directory. Changes made to these files are immediately picked up.

In the following walk-through, assume that the Web Project hosting the Dojo/HTML files is called DojoHolder and that an example HTML file is called MyHTML.html. DojoHolder is hosted by the EAR called DojoHolderApp.

If we navigate to:

{WPS Profiles}/{Profile Name}/installedApps/{Cell Name}/DojoHolderApp.ear/DojoHolder.war

we will find the files in the DojoHolder Web Project. Editing these will provide real-time changes to the files as served by WPS.

Rather than editing these files with notepad or some similar editor, we ideally want to edit them with a JavaScript editor such as is found in WID. It has been found that if we create a new WID Static Web project we can then link in these file system files to the web project and edit within WID. This Web Project will never be published and is only a wrapper to allow us access to the files.

See also:


See also:


Flex and Business Space

Flex is UI technology from Adobe that allows a developer to create fantastic user interfaces very easily. Flex is the combination of:


Widgets that use Flex

Here is a list of widgets that are known to use Flex. There may very well be others. This list is useful if reverse engineering is needed to see how a technique was done.

Flex Custom Widgets in Business Space

Business Space is the BPM framework for hosting “Widgets” that allow end users to interact with IBM’s BPM products.

Business Space provides the ability to create new customer written widgets that can add or replace functions for end user interaction. These are new widgets are generally called ‘custom widgets’. The creation of a general custom Business Space widget is described in detail at Custom Widgets.

The goal of this section is to provide additional knowledge on creating end user interfaces using Flex and that will be hosted as Custom Widgets in a Business Space environment.

Using Flex components, a user interface can very quickly be created. The next question is how can this be visualized in the IBM Business Space?

The answer to this is to understand that a Business Space Custom Widget is a combination of the following technologies:


To create a custom widget, the developer must be aware of all of these technologies and more. This is a steep learning curve.

Fortunately, there is a relatively easy solution.

What if we could create a Business Space custom widget that could generically host a Flex application? If designed and implemented properly, the task of creating a new custom widget utilizing Flex would solely be that of building a Flex application solution and the custom widget could simply be told which Flex application to load. This would dramatically reduce the burden of implementation and allow the UI designers to focus exclusively in their area of comfort and expertise … that being Flex.

At a high level, this solution would look as follows:

A Business Space user will open the web page to the Business Space environment and either add a new widget or see existing widgets on their page. These widgets are written in the combination of JavaScript and Dojo. The widget will insert an Adobe Flash Player object into the page using HTML and the flash player will then load the Flex application. The Flex application can communicate directly with the BPM environment using REST or other communication calls as well as interact with the Widget that loaded it.

Again, it needs to be stressed, that the designer of the user interface need only concentrate on their logical function and not be aware at all of the Widget and Business Space interactions. All they see is the clean world of Flex programming.

We will call such a generic Flex wrapper for Business Space by the name BSFlex.

Building a new Custom Widget for Flex

The construction of a new Custom Widget needs WID for its design and configuration. Create a new Dynamic Web project. In that Dynamic Web Project, add the following supplied files into the WebContent folder:

After adding the files to the project, the file structure should look as follows:

The files that were added were:


Now we need to rename these files. Let us assume that our new widget is going to be called “MyTest”

The mapping of file names becomes:

|| |images/Icon_160x125.png|unchanged| |images/Icon_20x20.png|unchanged| |images/Icon_64x48.png|unchanged| |BSpaceCommonUtilityLoader.js|unchanged| |Flex.js|MyTest.js| |Flex_iWidget.xml|MyTest_iWidget.xml|

The resulting contents of the project after renaming looks like:

Next we must edit the content of some of these files.

The files that must be edited are:

MyTest.js

MyTest_iWidget.xml

Within these files, the places to be changed are marked with

** CHANGE **

The change is to replace the word “flex” or “Flex” with “myTest” or “MyTest”.

Copy the XXXRegistryEndpoints.xml and XXXRegistryWidgets.xml file to the WPS <Profile>/BusinessSpace/registryData folder/directory.

Stop and restart the WAS application called BusinessSpaceManager.

From the Flex perspective, an Adobe Flex Builder project has been supplied which contains the necessary functions for the framework to execute. Rename the FlexWidget.mxml file to be the same name as the target widget.

Environment of the Flex application

When the Flex application has been loaded, it is passed some properties from the Business Space widget. These properties can be used by the Flex programmer to communicate with BPM and Business Space.

The properties passed in can be found in the Application.application.parameters object

|| |Property|Type|Description| |iWidgetId|String|The ID of the Dijit widget in the Business Space| |widgetBaseUri|String|The URL of the Web application associated with the Business Space widget| |baseUri|String|Protocol/host/port to the server hosting business space|

Widget Instance Configuration

Every Business Space widget has configuration data available to it. The BSFlex wrapper needs to be informed when the user has selected configuration in Business Space.

In our Flex application, we must register a callback from Business Space which is informed when a configure request has been issued. The following code should be added that is invoked early:

ExternalInterface.addCallback("doConfigure", doConfigure);

The function registered is called (with no parameters) when configuration is requested because the user has selected Configure from the menu for the widget. The name of the callback must be doConfigure. When the doConfigure function in Flex is called, it should visualize the configuration to allow the user to change properties. This is the next section.

Our BSFlex wrapper provides an object called <local:Configuration>. This visual component loads and saves data from the Business Space persistent store.

Custom configuration for the BS Widget is achieved through modification of this widget to display the configuration desired.

The exposed (public) information for this object is:

function loadAttributes(): void

Load the attributes for this instance of the widget.

var attributes:Object

This variable holds the attributes of the widget. This object can be used globally to get the values saved for this widget during configuration. The object should never be written to outside of the context of a configuration edit.

event: endConfigure

Event issued when the configuration has completed (Apply or Cancel pressed).

The basic visual looks as follows:

When the Apply button is pressed, the values of the attributes object are saved back to Business Space. If the Cancel button is pressed, nothing is saved back to the server.

When either Apply or Cancel are pressed, an event is generated called endConfigure that flags the configuration as over.

It is anticipated that this component will be included in a ViewStack container and shown when configuration of the Widget is requested. When the endConfigure event is received, the widget will be hidden from the stack.

Example:

Imagine that our widget has three attributes called “a”, “b” and “c”. The Configuration.mxml Flex source file would be modified to present visualization of these values. The data for the values must be held off the “attributes” variable as this is the data that is loaded and saved when the widget is used. The “attributes” variable is public from this flex file and hence can be read and written to globally. From a Flex programmer’s perspective, the only customization needed is to code the function called setAttributes() which is also in Configuration.mxml. This function should populate the attributes object from the current settings of the configuration visuals.

Making REST requests

In many cases, the Flex BS Widget will want to make REST requests back to the server to send and receive information. This is easily achieved in Flex through the HTTPService object.

Here is an example:

import mx.rpc.http.HTTPService; import mx.rpc.AsyncToken; import mx.rpc.events.ResultEvent; var h1:HTTPService = new HTTPService(); var baseUri:String = Application.application.parameters["baseUri"]; h1.url = baseUri + "/rest/bpm/monitor/models"; var asyncToken:AsyncToken = h1.send(); asyncToken.addResponder(new mx.rpc.Responder( function(result:ResultEvent): void { var respData:Object = JSON.decode(String(result.result)); // Do something … return; }, function(fault:FaultEvent): void { trace("Fault caught: " + ObjectUtil.toString(fault)); }));

Although this may look a little scary at first, it can be quickly understood. First we create an instance of the HTTPService object which knows how to make a REST request. Next we get the base URI that will be the server target of the request. We then append to this the target specific data and set this to be the url property on the HTTPService object. We then send the request which returns an asyncToken object. To this we add the callback function that will be invoked when the response is available.

When using REST, we often find that we want to subvert browser security, thankfully that is handled by the Ajax Network Proxy supplied with Business Space. This can be reached at:

/mum/proxy

For example:

http://localhost:9080/mum/proxy/http/www.google.com:123

Prior to 7.5.1, the Proxy was wide open meaning that any redirections were possible. From 7.5.1 onwards, Proxy configuration was tightened. The proxy configuration file is called "proxy-config.xml" and can be found at:

<Profile Name>/BusinessSpace/<nodeName>/<serverName>/mm.runtime.prof/config

After making these changes, the AdminTask.updateBlobConfig wsadmin command must be executed. Details of this can be found in the BPM InfoCenter. An example would be:

AdminTask.updateBlobConfig('[-serverName server1 -nodeName win7-x64Node01 -propertyFileName "C:\IBM\WebSphere\AppServer\profiles\ProcCtr01\BusinessSpace\win7-x64Node01\server1\mm.runtime.prof\config\proxy-config.xml" -prefix "Mashups_"]') AdminConfig.save()

To switch off security for the Proxy, an entry such as the following may be added to proxy-config.xml:

<proxy:policy url="*" acf="none" basic-auth-support="true"> <proxy:actions> <proxy:method>GET</proxy:method> <proxy:method>POST</proxy:method> <proxy:method>PUT</proxy:method> <proxy:method>DELETE</proxy:method> </proxy:actions> <proxy:headers> <proxy:header>Cache-Control</proxy:header> <proxy:header>Pragma</proxy:header> <proxy:header>User-Agent</proxy:header> <proxy:header>Accept*</proxy:header> <proxy:header>Content*</proxy:header> <proxy:header>X-Method-Override</proxy:header> <proxy:header>X-HTTP-Method-Override</proxy:header> <proxy:header>If-Match</proxy:header> <proxy:header>If-None-Match</proxy:header> <proxy:header>If-Modified-Since</proxy:header> <proxy:header>If-Unmodified-Since</proxy:header> <proxy:header>Slug</proxy:header> <proxy:header>SOAPAction</proxy:header> </proxy:headers> <proxy:cookies> <proxy:cookie>LtpaToken</proxy:cookie> <proxy:cookie>LtpaToken2</proxy:cookie> <proxy:cookie>JSESSIONID</proxy:cookie> </proxy:cookies> </proxy:policy>

If you are using the Chrome web browser, it has a command line option called "--disable-web-security" that switches off web security (Same Origin Policy) for that instance of the browser. This is useful for development but should never be used or suggested for production. In later versions of Chrome, we must also ass "--user-data-dir".

You will know it is working because you will see a warning message:

See also:

Handling returned XML

Some of the REST requests return XML data. By default, this XML is parsed into an Object. If we want the pure XML text, we need to set the resultFormat property of the HTTPService object to “text”. It has been found that the XML returned contains carriage-return/line-feed characters (i.e “\r\n”). To remove the carriage-return, the following AS3 can be used:

var data1:String = String(result.result); var p1:RegExp = new RegExp("\r", "g"); data1 = data1.replace(p1, "");

Cross domain security

A Flex application that wishes to call a service in a machine other than that from which the Flex application was loaded will be disallowed by default. This protects the Flex sandbox from breaking security protocols. For development testing, create a file called crossdomain.xml and have it available for download from the machine that the REST request is targeted to. The content of the file should look as follows:

<?xml version="1.0"?> <cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy>

Be aware that this will allow ANY Flex application to connect to the server. This is usually fine for testing. Before setting up cross domain security, first test to see if the application works without it. For BPM apps it seems to work fine probably due to explicit authentication.

Problems with PUT and DELETE requests

Unfortunately, Flex’s HTTPService object disallows both PUT and DELETE requests. What this means is that a complete section of WPS REST interfaces appear to be inaccessible. Fortunately, WPS claims to accept the X-Method-Override capability.

|| |Bug

The problem with this is that it appears not to actually work. PMR 79493,004,000 has been raised to address the problem. As of 2009-07-02 this has been partially fixed with an APAR available from support called IZ52208. This problem no longer exists in the latest versions of WPS with the latest fixpacks applied.|

An example of this would be

h1.method = "POST"; h1.headers = {"X-Method-Override": "PUT"};

Widget to Widget Event Handling

Event handling is the idea that a Business Space widget can be a source or destination of events. An event is simply the transmission of some data from one widget to another. The data can be anything that the Flex programmer chooses to transmit.

Sending Events

A Flex application can send an event directly to another widget. The way to achieve this is illustrated in the following code fragment:

var iWidgetId:String = FlexGlobals.topLevelApplication.parameters.iWidgetId; ExternalInterface.call("_" + iWidgetId + "_iContext.iEvents.publishEvent", "eventName", "parms");

Old technique

Sending an event is accomplished in two parts. First the flex application will make a call back to the JavaScript wrapper:

var iWidgetId:String = FlexGlobals.topLevelApplication.parameters.iWidgetId; ExternalInterface.call("_" + iWidgetId + "_iContext.iScope().funcName", parms);

In the JavaScript file, we need to code something like the following to actually execute the event publish.

this.iContext.iEvents.publishEvent(eventName, payload);

This needs to be wrapped in the function that the Flex application will invoke through the ExternalInterface call.

eventPublish_markerClicked: function(payload) { this.iContext.iEvents.publishEvent("com.kolban.map.MarkerClicked", payload); }

Convention has us declare the function with the name:

eventPublish_<event name>

The iWidget.xml file for the custom widget must be modified to declare that the widget is capable of publishing events. Code similar to the following should be added:

<iw:eventDescription id="com.sample.MyEventDescription" payloadType="JSON" lang="en" description="My Event Description" /> <iw:event id="com.sample.MyEvent" description="com.sample.MyEventDescription" published="true" />

The name of the event to be published is found in the iw:event@id location.

Unfortunately, we can't call the fireEvent wrapper function directly. This is because the function is defined on the JavaScript object that represents the widget and not on the page itself. In WPS 7.0, when a Business Space widget is on a page, the JavaScript object that represents that widget is hooked of the root of the HTML DOM tree. It appears to have a name of

_iWidgetId_IContext

This object has a function upon it called iScope(). This returns the JavaScript object that we wish to access.

From this a Flex application can call the functions in that widget directly. From within Flex, we can then call functions on that object as shown in the following example:

var iWidgetId:String = FlexGlobals.topLevelApplication.parameters.iWidgetId; ExternalInterface.call("_" + iWidgetId + "_iContext.iScope().testFunc", "Hello World");

The iWidgetId can be passed into flex from the JavaScript variable

this.iContext.io.id

Receiving Events

Receiving events sent from another Widget is a little trickier.

In the receiving Flex application, a function needs to be created with the signature:

private function <name>(payload:Object) : void

This is the function that is to be called when an event transmitted by a partner widget has been received. It is in this function that the Flex code for processing the event will be written.

This function needs to be exposed to the surrounding JavaScript with a call to the Flex provided class called ExternalInterface such as:

ExternalInterface.addCallback("<name>", name);

This will allow the surrounding JavaScript to call the method when an event arrives. A suitable place for adding this code is in the init() method.

In the iWidget XML file, the fact that this widget can now subscribe to incoming events must also be registered:

<iw:eventDescription id="com.kolban.map.MapInputDesc" description="MapInput Desc1" lang="en" payloadType="JSON"> </iw:eventDescription> <iw:event id="com.kolban.map.MapInput" description="com.kolban.map.MapInputDesc" handled="true" published="false" onEvent="eventSubscribe_mapInput"/>

In the JavaScript for the iWidget, a callback handler must be defined:

eventSubscribe_mapInput: function(event) { var swfId = this.iContext.widgetId + "_" + this.widgetName; if (dojo.isIE) { window[swfId].eventSubscribe_mapInput(event.payload); } else { document[swfId].eventSubscribe_mapInput(event.payload); } }

Working with WPS Human Task / Process Data

WebSphere Process Server has the notion of Business Objects. Both a BPEL process and a Human Task can take one or more Business Objects as input and return one or more Business Objects as outputs. On occasion (and especially with Human Tasks) we may wish the input to be presented to the user and have a response returned to the process or task (we will focus now on just tasks, but the same applies to processes).

WPS internally represents a Business Object definition as an XML Schema and a Business Object instance as an XML document corresponding to that schema. When we make REST calls to WPS to get Human Task input data, we are returned an instance of an XML document. If we want to send new data to the Human Task as output data for that task, we would supply an XML document.

Through the REST APIs we can ask for an instance of task data. The following code fragment illustrates how this can be done:

h1.url = "http://localhost:9080/rest/bpm/htm/v1/task/" + tkiid + "/input"; h1.method = "GET"; h1.resultFormat = HTTPService.RESULT_FORMAT_E4X; var asyncToken:AsyncToken = h1.send(); asyncToken.addResponder(new mx.rpc.Responder( function(result:ResultEvent) : void { var myXML:XML = XML(result.result); // Do something with the XML }, function(fault:FaultEvent) : void { trace("Got a fault!"); } ));

What we end up with is a Flex XML object. If the variable holding this data is held at the highest level and defined as bindable … for example:

[Bindable] private var myXML:XML;

Then the fields in the data can be accessed through Flex bindings.

Let us now work through an example of this. Imagine we have a Business Object that is defined as follows:

It is used in a Human Task Interface that looks like:

The XML document retrieved via a REST call to WPS might look as follows:

<?xml version="1.0" encoding="UTF-8"?> <p:mailing_._type xsi:type="p:mailing_._type" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://HTTestPerson/Mailing"> <input> <street>9200 Glenhaven</street> <city>Fort Worth</city> <state>TX</state> <zip>76182</zip> </input> </p:mailing_._type>

It looks pretty scary … but if we take it apart we have the following:

<root> <input> <street> <city> <state> <zip>

What we find is that the immediate children of the document are the input parameters. The tags are named after the names of the parameters. In this case the tag called input matches the parameter called input found on the Interface.

The children of the parameter tags are the data. In this case, a data type called Address which matches the BO definition.

When this is assigned to a Flex XML object, we can then address the fields contained within such as:

[Bindable] var myData:XML = … var street:String = myData.input.street; var state:String = myData.input.state;

If we then use Flex Binding, we can display a field in a TextInput Flex component with the following:

<mx:TextInput text="{myData.input.street}" />

Mapping Data types and controls

A WPS Business Object field has a finite collection of possible common scalar data types associated with it. These include:


When we look at the input and output data associated with a task, we must realize that the Business Object is serialized to and from an XML document. This then asks the question, what is valid for a given BO field type in the associated XML item?

String

Any character string is allowable.

Int

Any positive or negative numeric characters are allowable

Date

Date is more interesting. The format of the date expected in XML is “YYYY-MM-DD”. This is not the default format of a Flex DateField. DateField has a property called formatString which can be set to “YYYY-MM-DD” to generate the correct format for the XML document.

Working with repeating/array data

Working with AnyType

Performing input validation

Single User Workflow

Single User Workflow is the idea of being presented with a page, completing that page and then immediately being presented with a succeeding page without having to manually claim the task.

InitiateAndClaimFirst

In: processTemplateName

In: InputMessage

Out: AIID

Out: ActivityName

Out: InputMessage

CompleteAndClaimSuccessor

In: AIID

In: OutputMessage

Out: AIID

Out: ActivityName

Out: InputMessage

CompleteAndClaimSuccessor

CompleteAndClaimSuccessor

<End>

Validate that the format of data being sent or received matches the expected format for the HTTPService request.

Use the FireFox 3.5 FireBug tool which can show Flash related communications.

Only a large subset of the BFM and HTM APIs are available via the REST interface. If the other BFM and HTM calls are needed, we seem to be stuck. As a possible workaround, we can create our own REST based servlet that when called, makes the correct local EJB calls. This is not an easy/obvious proposition.

Elixir

References – Elixir


References – Flex Widgets


Custom Process Portals

The IBM BPM framework provides what it calls the "Process Portal". This is an IBM supplied web based application that provides users with the abilities to:


It looks as follows:

The IBM supplied Process Portal is feature rich and extremely powerful. However there is a commonly encountered problem with consumers actually using it. Simply put, it may not be the company's strategic direction for interacting with users. Imagine a clerk responsible for processing insurance claims. This clerk has been trained to use the existing user interfaces of the existing applications. It would not be surprising if multiple distinct applications could be worked upon through this one user interface. If IBM BPM is to be considered as a component in a solution, it may not be desired to use Process Portal for a variety of non-functional business reasons. From a raw technical perspective, it will work just fine … but if Process Portal were used, we have the unfortunate story of the clerk user having to switch from one application to another to perform their work. In addition, extra training will have to be supplied to the clerks to teach them how to use the new user interface. All of this adds up to expense and introduces opportunities for errors. There is low quality in saying "Enter new claim data in this application" but "View claim history in that application".

Fortunately, there is an elegant solution. The IBM BPM product provides programming interfaces (APIs) that can be used by technical programming staff to perform the tasks that are achieved through IBM's supplied Process Portal. These APIs are exposed through the REST style of remote function call. By having such an API available, consumers can build or augment their own existing user interface applications to include access to the features provided by IBM BPM. This section will continue to discuss and dive deeper into that notion.

First of all let us ask ourselves what are the key concepts that we must take into account:


See also:

Types of Custom Portal

Let us start with some simple thoughts on different types of portals. At the highest level we have two basic kinds. Those known as "thick clients" and those that are "browser based". Thick clients are native executables installed locally on consumers desktops. These are applications that may use Windows/.NET APIs, Java Swing or Java SWT (as examples). The second type of process portal are those that run within the browser environment. These are the more common types of custom portals. These can also be broken down into categories based on whether the web page (HTML) is build in the browser (Web 2.0 style) or built on the server.

Browser built HTML technologies includes:


Server built HTML includes:

JSP

JSF

Portlets (JSR168, JSR286) hosted by portal providers

Irrespective of the type of UI technology used, the high level concepts and design remain the same.

Once the task has been selected, we now choose how to display the task data itself. This will either be the Coach UI as an in-line web page perhaps using iFrame technology or else it will be custom controls/UI using the REST API to get the data for a specific task.

Generic Widgets for Custom Portals

With the idea of Custom Portals in mind, maybe we can design a framework for generic portals? This framework would be as flexible as possible and hide the majority of details from the consumer. To make the solution as flexible as possible, let us assume that we introduce the notion of a "Widget". A Widget is the user interface building block that is responsible for some function.

The Task List Widget

Here is a picture of the IBM Process Portal task list:

As we see, it is very attractive but what does it "logically" contain? If we strip away the superficial colors and styling, what is left?

The answer is that it is a visual container that lists a series of tasks that a user can work upon. This is a core thought. The concept of the Task List is a "container of tasks". It is responsible for determining a list of tasks and then showing each task. If a building block Widget of a Task List were to be created, what would it consist of?

Ignoring its visual appearance, we could define it to have:

With this minimal interface, the Task List widget would then be functional.

After a Task List requests tasks from a BPM Server, the Task List must now display each Task retrieved.

We will now assume the existence of a second type of Widget that we will call the TaskItem. The Task List will create an instance of a TaskItem for each Task that it wishes to display. Since the TaskItem is a widget, it is responsible for displaying its content. Again ignoring the visual appearance of the TaskItem, we could define it as having the following signature:

By generalizing these two functions, we now have all we need to create arbitrary visualizations of a Task List.

Here is an example visualization of a new Task List using these principles:

The Task Table Widget

When we think of a custom portal, we must consider the selection of tasks by a user. The user wishes to see these tasks in a desirable form. Given that the user may have some number of tasks available for them to work upon, one style of task list presentation is that known as the table. The table is pretty much what it sounds like. It is a rectangular array of information composed of rows and columns. Each row will represent a single task and each column will hold some attribute of that task.

The Task Table widget is an alternative to the Task List widget. It presents a table of tasks to the user and allows the user to select a task through the "Open" button.

This widget has a callback function called "onTaskSelected" which is invoked and passed an object which contains the taskId of the task that was selected.

The widget exposes a method called "refreshTasks()" which, when invoked, will retrieve the current set of tasks and refresh the table with the new information.

An example of usage would be:

|| |<div data-dojo-type="kolban.widget.TaskTable"> <script type="dojo/method"> this.refreshTasks(); </script> <script type="dojo/connect" data-dojo-event="onTaskSelected" data-dojo-args="value"> console.log("Connect called: " + value.taskId); myCoachViewer.set("taskId", value.taskId); </script> </div>|

This fragment will create the TaskTable widget, call its refreshTasks() method and, when a task is selected, call a CoachViewer to show the Coach associated with the task.

The CoachViewer widget

The CoachViewer widget is used to load and view the Coaches for an associated task. It allocates an area of the browser display and when asked to show a Coach, that area then hosts the Coach details. It has a property called "taskId" that must be set using the widget's "set()" method. When a new taskId is supplied, the widget will display the content of the associated Coach.

An example of usage would be:

|| |<div data-dojo-type="kolban.widget.CoachViewer" data-dojo-id="myCoachViewer"> </div>|

This can be combined with the TaskTable widget (see previous) to select which task to show.

The CoachViewer also has a callback function called "onCompleted" which is called when the user completes the task. This can be used to signal a refresh of the task list or some other function. The parameter passed to the callback is an object which contains:

status – The status of the completion. This may be

"completed" – to indicate that the coach completed succesfully

"error" – to indicate that an error was encountered. If the status is error, two additional properties will be available:

A property of the CoachView called "showStandby" can be set to "true" to show a "standby" overlay while the Coach is loading.

A property called "showError" is defined which is a boolean. If set to true and an error is encountered, a dialog box showing the nature of the error will be displayed. If set to false, no such dialog will be shown. In either setting, an error will be returned via the onCompleted callback.


Determining when the current Coach has completed

When writing a custom Portal, the current Coach is usually shown in an iFrame. When the Coach completes, we somehow need to tell our portal framework that this has happened. It seems that when a Coach completes, it posts a message to the DOM "window" containing the iFrame. This is the same window being used by the portal. The following code will register a callback for such an entry:

var onCompleted = function (m) { debugger; } if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', onCompleted, false); } else if (typeof window.attachEvent != 'undefined') { window.attachEvent('onmessage', onCompleted); }

A more modern solution using Dojo would be:

on(window, "message", function(m) { … });

In jQuery, we would use:

jQuery(window).on("message", function(data) {…});

The data passed in here will contain data.originalEvent.data as the JSON string.

Unfortunately, the callback is invoked on a number of occasions by different parties, not just when we expect, so we have to be careful to check the response.

The passed in "m" message contains a field called "data" which appears to contain a single string which is a JSON encoded piece of data that contains:

name – so far we have seen instances of:


taskID

applicationInstanceId

parameters – Can be array of strings. For onError it appears to be a message and a message code.

Unfortunately there is one more wrinkle. In all releases tested so far (up to and including 8.5.5), the "onCompleted" response message includes invalid JSON data. Specifically, the "taskID" property is returned as the text "undefined" with no quotes around it. This breaks JSON.parse(). As such, we can't rely on using JSON parses to parse the data and need to string grep it for what we need.

Using the Custom Portal widgets

Now that we have described the functions of the custom portal widgets, what remains is to describe how to use them.

A Widget is added to a Web Page (a source HTML file). Since the widgets are Dojo, the first thing we have to do is to add the mandatory boiler plate to load the Dojo environment:

<script type="text/javascript" src="/teamworks/script/coachNG/dojo/1.8.6/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true"> </script> <link rel="stylesheet" href="/teamworks/script/coachNG/dojo/1.8.6/dojo/resources/dojo.css" /> <link rel="stylesheet" href="/teamworks/script/coachNG/dojo/1.8.6/dijit/themes/claro/claro.css" />

The above must be added to in the <head> section of the page. The URL to Dojo is good as of IBM BPM 8.5.

Because we are using custom widgets that are not part of the default Dojo distribution, we need to tell the environment where to load those widgets from. This is achieved using the AMD technology. In addition to saying where the custom widgets are located, we also have to say which of those widgets are going to be used in the page. Again in the <head> section, we add the following:

<script type="text/javascript"> require({ packages : [ { name : 'kolban', location : '/StaticTests/kolban' } ] }); require([ "kolban/widget/ProcessStart" ]); </script>

Because we are using Dojo, we must also specify the Dojo theme that we are going to use. The HTML <body> tag should have the Dojo theme added to it:

<body class="claro">

We are now ready to include any widgets we wish, for example:

<div data-dojo-type="kolban.widget.ProcessStart" data-dojo-id="myProcessStart" > <script type="dojo/connect" data-dojo-event="onStarted" data-dojo-args="processDetails"> console.log("Process Started!"); console.dir(processDetails); </script> </div>

Putting it all together, a sample would look like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Test_ProcessStart</title> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <script type="text/javascript" src="/proxy/teamworks/script/coachNG/dojo/1.8.3/dojo/dojo.js" data-dojo-config="async: true, parseOnLoad: true"> </script> <link rel="stylesheet" href="/proxy/teamworks/script/coachNG/dojo/1.8.3/dojo/resources/dojo.css" /> <link rel="stylesheet" href="/proxy/teamworks/script/coachNG/dojo/1.8.3/dijit/themes/claro/claro.css" /> <script type="text/javascript"> require({ packages : [ { name : 'kolban', location : '/StaticTests/kolban' } ] }); require([ "kolban/widget/ProcessStart" ]); </script> </head> <body class="claro"> <h1>Test ProcessStart</h1> <p>The ProcessStart Widget presents a visible list of start-able processes. Clicking on one of these entries causes a new instance of that process to start. </p> <hr /> <div data-dojo-type="kolban.widget.ProcessStart" data-dojo-id="myProcessStart"> <script type="dojo/connect" data-dojo-event="onStarted" data-dojo-args="processDetails"> console.log("Process Started!"); console.dir(processDetails); </script> </div> <hr /> </body> </html>

Writing custom portals using Coaches

In principle, a custom portal can itself be written as a Coach.

Within a custom portal we can imagine the user being presented with a list of tasks upon which they can work. After selecting such a task, a new window might open which will show the details of the new task. The URL to be used to open the window can be found using the REST API to query upon the client settings of the taskId to be launched. This REST API returns a URL for exactly this purpose. However, there appears to be a problem (as of 8.0.1.1). When the newly opened Coach completes, it seems to cause a "refresh" or "reset" of the Coach that launched it. This means that if we write a Coach which acts as a process portal, select a task and then open that task in a new window, when that new window ends, the original process portal Coach is reset. We do not yet know of a circumvention.

Addition: 2013-10-15: It appears that if we open a new window for a Coach if we set that window's "opener" property to null, the refresh will not happen.

Apache Flex

Flex is a function rich UI development environment that runs within a browser, native on the desktop and within Mobile platforms including Android and iOS. Originally developed by Adobe, it was Open Sourced and provided to the Apache group. The latest information on Flex can be found here:

http://flex.apache.org/

There are a variety of fee and free development tools for building Flex applications. These include:


Flex for Mobile

One of the key reasons for considering Flex is for building mobile applications (i.e. those that can run on Android or iOS). Flex provides a write-once/run-anywhere model. It should be stressed that a logical "runtime" of the Adobe AIR environment is used but to all intents and purposes building such apps may be considered closer to native Apps than attempting to build HTML5 based applications.

The Flex support for Mobile appears to be first class but restricts the components that can be used. Here is a quick summary of those supported:

|| |StageWebView|Web Browser| |BusyIndicator|| |Button|| |ButtonBar|| |Callout|| |CalloutButton|| |CheckBox|| |DateSpinner|| |HSlider|| |Image|| |Label|| |List|| |RadioButton/RadioButtonGroup|| |SpinnerList|| |TextArea|| |TextInput|| |ToggleSwitch||

|| |DataGroup|| |Group|| |HGroup|| |Scroller|| |Spacer|| |TileGroup|| |VGroup||

Installing FlashDevelop

FlashDevelop is a free, Open Source development environment for Flex based applications:

http://www.flashdevelop.org/

Google Charts

Google provides a charting package. The URL for this is:

https://developers.google.com/chart/

To use this technology, we need to perform some work:

<script type="text/javascript" src="https://www.google.com/jsapi"></script>

google.load('vizualization', '1.0', {'packages': ['corechart']});

google.setOnLoadCallback(function() { … code goes here ... });

Providing data to Google Charts

All Google charts expect data encapsulated in a DataTable object. The name of this object is "google.visualization.DataTable". There are a number of ways to build and populate this object. We will touch upon some of them. For full details, see the full Google documentation.

var data = new google.visualization.DataTable(); data.addColumn('string', 'Name'); data.addColumn('number', 'age'); data.addRows(1); data.setCell(0, 0, 'Bob'); data.setCell(0, 1, 34);

Google Chart – Gauge

The Gauge control provides a "dial" or "gauge" which contains a label and a value. The value is shown as well as a needle that provides a relative indication of where the value falls:

Package: google.load('visualization', '1', {packages: ['gauge']});

var chart = new google.visualization.Gauge(node); chat.draw(data, options);

Page 54

No Comments
Back to top