Human Task Manager

See Also – Human Task Manager:

The Human Task Manager can be accessed through an API.

Work Baskets

With the release of Feature Pack 1 for WPS 7.0, we saw the arrival of a new capability called Work Baskets. To understand Work Baskets, first let us review the nature of a standard human task. When a Human Task definition is created, a set of IBM defined roles for that task exist. For each role, the developer can define which users are to be associated with those roles. Let us consider the most commonly used role which is called Potential Owner.

Imagine we have a task definition called TaskDefinition1. When that task definition is created, we may assign Alice and Bob as potential owners. What this means is that when an instance of the task is created from the definition, at runtime, either Alice or Bob can claim and work upon the task. Charlie, who is not named in the task definition can not work on the task.

This is the basic model of Human Task authorization in WPS before the introduction of Work Baskets. Even though Work Baskets now exist, the original model still holds true. Work Baskets is an additional model that can be used if desired or, conversely, be completely ignored.

Let's use an analogy to understand Human Task Work Baskets. Imagine fictitious workers in an office. Each worker sits at their desk to perform their job. When they are ready for some work to do they stand up from their chair and walk to a communal area where there are "work baskets".

Each work basket has a "name" associated with it. This name describes the type of work that can be found in the folders in that basket. Examples of work basket names at an office may be:


Depending on the job role of a worker, the worker can take work from the appropriate basket. To make the story more interesting, imagine that there is a security guard beside the work baskets. In order for a worker to see work in a basket, he must tell the guard who he is. The guard then checks the employee's name against a list of people who are authorized to take work from the basket. Beside each basket there is a clipboard with names on it. If the worker's name is not in the list, he doesn't get to work with the folders.

Work Basket Roles


Work Basket Business Space widgets

Work Baskets are created from the Work Baskets list widget. The Actions pull-down has a Create option to create a new Work Basket.

When selected, the companion Business Space Widget called Work Basket Information will be updated (via Widget to Widget events) to allow the user to define the settings for the new basket.

The settings of a Work Basket are broken down into three categories:

General

Identifier – An identifier for the work basket

Name – A name for the work basket

Description – A description of the work basket

Type


Owner – The owner of the work basket

Task list – Unknown ???

Access

In the Access category, we get to name a set of users and groups and then associate the authorities of this work basket against those entities. We can define two sets of attributes … those for the work basket roles and those for the task roles.

Distribution Targets

In the last category known as Distribution targets, we get to list the potential other work baskets to which tasks in this work basket can be routed.

Human Task Definition for Work Baskets

In the Human Task template definition, the name of the work basket associated with the task can be supplied. Either a hard-coded work basket name or a variable whose value will be the work basket name can be supplied.

Technical Notes

The default Query Table for work baskets is called “WORK_BASKET”

Maybe IBM.DEFAULTALLWORKBASKETSLST

To obtain a list of all work baskets, the following REST request can be used:

/rest/bpm/htm/v1/workBaskets/query

queryTableName=IBM.DEFAULTWORKBASLETSLST

admin=true

The attributes available in the response includes:


A new attribute has been added to a task instance called “workBasketName”

See Also – Work Baskets:

Business Categories

Human Tasks can have attributes associated with them called Business Categories. This allows tasks to be categorized for efficient selection and filtering.

Parallel Tasks

When a Human Task is created, we normally think of that task as having one owner and it not completing until that single owner completes it. In WPS 7 and beyond, a feature was added which IBM called "Parallel Tasks". The idea behind this is that a Human Task can be created (just as normal) but the Human Task is defined as being parallel in its template definition in WID. When an instance is created, instead of just a single owner being able to claim it, all potential owners get to claim the same task. Each potential owner sees their own copy of the task and get to work upon it. When each potential owner completes the task then the original task is flagged as completed and the caller of the task gets to move onwards. Although interesting, there are two more facets to this feature that really bring it to life.

The first is that not all of the child parallel tasks need to be completed before the parent is allowed to completed. An early completion configuration may be defined such that the parent task completes when only some of the child tasks have been completed. If the parent is allowed to complete and there are still other child tasks, these are immediately terminated.

The second important feature is how the configuration of the final response of the parent task is determined. With a simple Human Task, the response from the sole user that claimed the task is the final response but with parallel tasks, there are many different responses from many different users. As such, the ultimate final response will be an aggregate (accumulation) of the responses from all the different users. The decisions of how the aggregations are formed is under the control of the Human Task template designer.

During the creation of a new Human Task template, a panel is shown asking if this task is "single" or "parallel". If single is selected, it is standard human task with one final owner. If Parallel is selected, then the function described in this section becomes active.

Completion Selection

When a task is defined as parallel, when the Potential Owners selection is selected, the Properties view contains a Completion tab. Within this area the attributes that define the completion criteria for the parent task are defined. This is where we specify under what circumstances the parent can complete before all the children have completed.

Completion can be configured based on either a time interval from when the parent task was created or from examination of the child tasks that have completed. The later is the one that we will focus on as it is most interesting.

The condition that causes completion of the parent task can be specified by either a custom XPath expression or by using one of the templates supplied by IBM. The supplied IBM templates are:


Work Items

When a Human Task instance is created, a set of potential owners must be determined. This results in a set of "work items" being created. A work item is a relationship between a person and a human task to be worked upon. One way to think of a work item is that it has an attribute called "who" which is the identity of the staff that can work on that item. The "who" attribute can take one of three possible values:


When the Human Task is created, a set of such work items are associated with the task. This means that there could be more than one work item which gives us a result that a task can be associated with a combination of users and/or a combination of groups.

Authorization and Staff Resolution

When a human task instance is created, we want to restrict the set of people who are allowed to work on such a task. This means that we need to determine (resolve) a set of staff members who will be granted the privilege of working upon or owning a task. This is termed staff resolution.

Let us now introduce a concept called "People Assignment Criteria". In previous versions of WPS this was known as staff verbs. Imagine you fall sick and need some medical attention. You want someone to look at you and make a diagnosis. The person that should perform this role can't be just anyone … they must fulfill a requirement. Specifically, they should be doctors. If you fall ill on an airplane, the flight attendants ask if there is a doctor on board. What they are doing is executing a rule looking for the correct people to fulfill the task.

In WPS, this is implemented by parameterized queries that can be executed against staff repositories that will return a set of userids that can perform a human task. Generically, executing a staff query that results in a set of people is called a staff resolution. The staff query is executed against a staff repository. In reality, the staff repository is commonly an LDAP directory.

See Also:

Staff Resolution Criteria (aka Verbs)

Each staff query has associated with it:


WPS provides some pre-built staff queries:


Verb Descriptions

This verb retrieves the members of a group where the group is identified by an attribute (not by a name). The attributes of a group that can be matched are:


Group Members

This verb queries a group and adds the set of all users in that group into the result set. It is important to realize that this verb expands the group set into members when the query is executed. This means that if the members of the group are subsequently changed by an administrator, the result set will still consist of the original expanded group which may be different than what one would expect if the group were expanded later. This query results in a work item for every possible member of the group and hence if the group is large, it may not be the preferred solution. See the group verb for maintaining a group name until the last possible moment.

|| |GroupName|The name of the group to query for members| |AlternativeGroupName1|An optional group name to query for members that will be added to the result set| |AlternativeGroupName2|An optional group name to query for members that will be added to the result set| |IncludeSubgroups||

For LDAP, the default implementation searches for objects of type groupOfNames and from these returns their member attributes. Each member injects a new user into the result set. This object is not the only possible grouping mechanism and groupOfUniqueNames may be desired in which case the XSLT file should be modified.

<sldap:staffQueries threshold="$Threshold"> <sldap:usersOfGroup groupDN='GroupName DN'> <sldap:resultObject objectclass='$DefaultGroupClass' /> <sldap:resultAttribute name="$DefaultGroupClassMemberAttribute" destination="intermediate" /> </sldap:resultObject> <sldap:resultObject> <sldap:resultAttribute name="$DefaultUserIdAttribute" destination="UserID"/> <sldap:resultObject> </sldap:usersOfGroup> </sldap:staffQueries>

Dynamic user selection

A BPEL process can programmatically assign a set of users to a Human Task. This is known as dynamic user selection as the result is never a static definition but rather an algorithmically generated user set dynamically selected at runtime.

One way to achieve this is to use the verb Users by user ID. This can take as a parameter a variable which is of type array of Strings.

Staff Resolution plugins

Plugins for WPS are available that determine staff sets by contacting directory services. These plugins are specific to the directory type they service.

When a developer builds a staff query, that query is described in abstract terms. For example, "Members of a group". However, the execution of a request against a particular provider is provider specific. The query to return members of a group is one thing for LDAP but another way for VMM. The staff resolution plugins "even out" this disparity. However, the abstract staff query request must be transformed into the correct parameters for the staff resolution plugin. What happens is that the staff query is passed through an XSLT transform prior to being passed to the staff resolution plugin.

See Also:

Custom Human Task staff resolutions

Control files that define the verb sets available for a particular provider can be found in an IBM supplied JAR file. In the directory C:\Program Files\IBM\SDPShared\plugins, a file can be found called:

com.ibm.wbit.tel.ui_<version>.jar

This file can be opened with a ZIP editor. In the JAR file, a folder can be found at /xml. This folder contains a series of XML files that describe the people assignment criteria available in the WID editor.

A copy of the XML file to be modified can be extracted from the ZIP. This XML file contains the definitions of the people selection criteria available for use. To start working with a modified version, add a copy of that file into your module that you are building. Once done, in WID, go to the Windows > Preferences dialog and navigate to:

Business Integration > Human Task Editor > People Directory

Edit the people directory that you are working with and change the People Assignment Criteria to point to the copy of the file:

This file contains a series of verb definitions where each verb is a people assignment criteria.

The format of an entry is as follows:

<vs:DefineVerb name='name'> <vs:Description>description</vs:Description> <vs:Mandatory> <vs:Parameter> <vs:Name>name</vs:Name> <vs:type>type</vs:Type> <vs:hint>hint</vs:Hint> … <vs:hint>hint</vs:Hint> </vs:Parameter> </vs:Mandatory> <vs:Optional> <vs:Parameter> <vs:Name>name</vs:Name> <vs:type>type</vs:Type> <vs:hint>hint</vs:Hint> … <vs:hint>hint</vs:Hint> </vs:Parameter> </vs:Optional> </vs:DefineVerb>

The DefineVerb tag has a name which is what is shown in the pull down list in the WID editor. The following Description tag defines the text shown in the description area in WID.

The mandatory and optional sections defines zero or more parameters that can be supplied. A parameter is described by its name and type. The type must be taken from one of the following types:


Optional hints for the parameters can also be defined. These hints are available using entry assist and simply provide pre-entered values if desired.

Here is an example:

This is generated from:

<vs:DefineVerb name='My New Verb'> <vs:Description>My Description. My text for my New Verb goes here. </vs:Description> <vs:Mandatory> <vs:Parameter> <vs:Name>MyParm1</vs:Name> <vs:Type>xsd:string</vs:Type> <vs:Hint>XYZ</vs:Hint> <vs:Hint>ABC</vs:Hint> </vs:Parameter> </vs:Mandatory> <vs:Optional> <vs:Parameter> <vs:Name>MyParm2</vs:Name> <vs:Type>xsd:string</vs:Type> <vs:Hint>PQR</vs:Hint> <vs:Hint>MNO</vs:Hint> </vs:Parameter> </vs:Optional> </vs:DefineVerb>

When a user has entered data in the WID editor, that data for the verb is saved in the Human Task definition file (a .tel file). The format of the entry in this file is:

<staff:verb> <staff:name>name</staff:name> <staff:parameter id='parmName'>value</staff:parameter> … <staff:parameter id='parmName'>value</staff:parameter> </staff:verb>

The staff prefix is bound to the namespace:

xmlns:staff="http://www.ibm.com/schemas/workflow/wswf/plugins/staff"

In a ".tel" file, the prefix may be "tel" but the namespace is the same.

From this we see that the XML file that describes the verbs is a template that is used solely by the WID editor to describe what the user can enter. Once entered, it becomes data for the Human Task.

During deployment, the human task entries must be mapped or converted to a format suitable for the staff resolution plugin. This is achieved with an XSLT mapping file. Creation of new verbs requires new XSLT mapping files to accommodate the new verbs. The original files should never be modified, instead new copies of these files should be taken and changed as needed.

Staff resolution plugin language – The Staff Query

At the conclusion of all of these shenanigans, a request is made to a staff resolution plugin to determine the set of users that are to be allowed to work on the human task. That request is made by sending an XML document. Loosely, this looks as follows:

<staff:staffQueries threshold="integer"> staff query element * </staff:staffQueries>

Here is a more complex example:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sldap:staffQueries xmlns:sldap="http://www.ibm.com/schemas/workflow/wswf/plugins/staff/ldap" xmlns:staff="http://www.ibm.com/schemas/workflow/wswf/plugins/staff" xmlns:xalan="http://xml.apache.org/xslt" threshold="1000000"> <sldap:usersOfGroup groupDN="cn=g1,cn=myRealm,o=k1" recursive="yes"> <sldap:resultObject objectclass="groupOfNames" usage="recursive"> <sldap:resultAttribute name="member" destination="intermediate" /> </sldap:resultObject> <sldap:resultObject objectclass="inetOrgPerson" usage="simple"> <sldap:resultAttribute name="uid" desatination="userID" /> <sldap:resultAttribute name="mail" destination="eMailAddress" /> <sldap:resultAttribute name="preferredLanguage" destination="preferredLocale" /> </sldap:resultObject> </sldap:usersOfGroup> </sldap:staffQueries>

With the exception of <everybody/>, <groupID/> and <nobody/> the staffQueries document can contain a variety of children. Each child will add one or more users to the resulting set of users.

The staff query elements define the core rules. The defined rules are:

everybody


There are others but those are specific to the type of repository against which the request is being made. Multiple queries can be executed within a single staff query and the resulting set of authorized people is the union of all the individual queries.

Simple staff queries.

everybody

<staff:staffQueries> <staff:everybody/> </staff:staffQueries>

Selects everybody. No other queries are allowed in conjunction with this request. This makes sense as everybody really does mean everybody.

groupID

<staff:staffQueries> <staff:groupID name="group name"/> </staff:staffQueries>

Selects a named group.

nobody

<staff:staffQueries> <staff:nobody/> </staff:staffQueries>

Selects nobody.

remove

<staff:staffQueries> <staff:remove value="user ID to remove"/> </staff:staffQueries>

Removes a named userid from the unioned set of userids that have already been built.

userID

<staff:staffQueries> <staff:userID name="valid WebSphere user ID"/> </staff:staffQueries>

A named userid.

User registry queries

<sur:user .../>

<sur:usersOfGroup .../>

<sur:search .../>

LDAP queries

When working with the LDAP staff resolution plugin, the following additional functions are available:


user

<sldap:user dn="distinguished name of the user" attribute="attribute to evaluate" objectclass="LDAP objectclass of the queried object"/>

The user query is used to lookup an explicit LDAP entry by explicit DN. The "dn" attribute is required and acts as the lookup key in the directory. The "objectclass" attribute is required and names the class of object that is to be returned/found. The "attribute" attribute defines the LDAP entry attribute who's value is to be used for the returned user identity.

usersOfGroup

<sldap:usersOfGroup groupDN="distinguished name of the group" recursive="yes"|"no"> attribute evaluation specification 1+ </sldap:usersOfGroup>

<sldap:search baseDN="search root" filter="valid LDAP filter" searchScope="subtreeScope" | "onelevelScope" | "objectScope" recursive="yes"|"no"> attribute evaluation specification 1+ </sldap:search>

The baseDN is an optional attribute that supplies an LDAP subtree for the start of the search. If omitted, the baseDN of the plugin configuration will be use. The "filter" attribute is mandatory and used to constrain the search results returned.

attribute

*** DEPRECATED *** - replaced by resultObject

As seen by the last two queries, LDAP entries can be returned. An LDAP entry is composed of a variety of attributes. Somewhere we need to supply the information as to which of the entries attributes should be used and for what purpose. This is where the next tag comes in to play.

<sldap:attribute name="attribute name" objectclass="LDAP object class" usage="simple|recursive" />

"name" is the required attribute that names the LDAP attribute to be used. "objectclass" is the required attribute that names the object class associated with the entry.

resultObject

This is a replacement for the old <sldap:attribute> instruction.

<sldap:resultObject objectclass="LDAP object class" usage="simple|recursive"> <sldap:resultAttribute name="attribute name" destination="userID|eMailAddress|preferredLocale|intermediate" />* </sldap:resultObect>

What this does is look for results that match the LDAP object and, extracts the named attribute value and sets that to be either the userid, email address or locale for that result. If the usage type is recursive, then the destination can be intermediate. What this seems to mean is that the results are DNs that are themselves retrieved and the rules re-applied.

intermediateResult

<sldap:intermediateResult name="variable name" threshold="positive nonzero integer number" query of type user, usersOfGroup or search </sldap:intermediateResult>

"name" is a mandatory parameter and is a variable name. It will be used to temporarily hold the results of the query. This variable can then be used in subsequent queries.

intersect

<sldap:intersect value="variable name"> </sldap:intersect>

This processing instruction expects a variable as input. The variable contains a list of users/identities. When the instruction is reached, an intersection set operation is performed against the identities in the variable and the identities built so far by previous instructions (the result set). The set produced from this intersection becomes the new current result set.

Examples

LDAP group lookup

<sldap:staffQueriesxmlns:sldap="http://www.ibm.com/schemas/workflow/wswf/plugins/staff/ldap" xmlns:staff="http://www.ibm.com/schemas/workflow/wswf/plugins/staff" xmlns:xalan="http://xml.apache.org/xslt"> <sldap:usersOfGroup groupDN="cn=nurses,cn=myRealm,o=k1" recursive="no"> <sldap:resultObject objectclass="groupOfNames" usage="recursive"> <sldap:resultAttribute destination="intermediate" name="member" /> </sldap:resultObject> <sldap:resultObject objectclass="inetOrgPerson" usage="simple"> <sldap:resultAttribute destination="userID" name="uid" /> <sldap:resultAttribute destination="eMailAddress" name="mail" /> <sldap:resultAttribute destination="preferredLocale" name="preferredLanguage" /> </sldap:resultObject> </sldap:usersOfGroup> </sldap:staffQueries>

XSLT Mapping to Staff Resolution plugin language

If you have followed the story so far you will have found that when a staff query is entered in WID, it is transformed into a verb format in the human task .tel file that looks as follows:

<staff:verb> <staff:name>name</staff:name> <staff:parameter id='parmName'>value</staff:parameter> … <staff:parameter id='parmName'>value</staff:parameter> </staff:verb>

This encoding of the staff query is generic. It is not the encoding expected by any one of the IBM supplied staff resolution plugins. During deployment, this format of XML has to be transformed/mapped into the staff resolution plugin language which is also an XML document. WPS does this by executing an XSLT transformation against the verb format XML using an XSLT file that is specific to the Staff Resolution plugin.

These XSLT files can be found in the directory:

<WPS Install>/ProcessChoreographer/Staff

The files currently supplied include:


Testing of these XSL files can be performed in WID. We can run an XML document through an XSLT and view the resulting output XML.

Here is an example of a transformed query. First, the original verb XML file:

<?xml version="1.0" encoding="UTF-8"?> <staff:verb xmlns:staff="http://www.ibm.com/schemas/workflow/wswf/plugins/staff" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <staff:name>Group Members</staff:name> <staff:parameter id="GroupName">cn=g1,cn=myRealm,o=k1</staff:parameter> <staff:parameter id="IncludeSubgroups">true</staff:parameter> </staff:verb>

and the result after having been passed through an XSLT transform:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <sldap:staffQueries xmlns:sldap="http://www.ibm.com/schemas/workflow/wswf/plugins/staff/ldap" xmlns:staff="http://www.ibm.com/schemas/workflow/wswf/plugins/staff" xmlns:xalan="http://xml.apache.org/xslt" threshold="1000000"> <sldap:usersOfGroup groupDN="cn=g1,cn=myRealm,o=k1" recursive="yes"> <sldap:resultObject objectclass="groupOfNames" usage="recursive"> <sldap:resultAttribute name="member" destination="intermediate" /> </sldap:resultObject> <sldap:resultObject objectclass="inetOrgPerson" usage="simple"> <sldap:resultAttribute name="uid" desatination="userID" /> <sldap:resultAttribute name="mail" destination="eMailAddress" /> <sldap:resultAttribute name="preferredLanguage" destination="preferredLocale" /> </sldap:resultObject> </sldap:usersOfGroup> </sldap:staffQueries>

If a new verb is introduced, the XSLT must be changed to accommodate that new verb. This means the designer must have skills to code and test XSLT as well as knowledge of the design of the existing XSLT program.

The design of IBM's supplied XSLT is roughly as follows.

A template is provided which matches of "/staff:verb". This will find the <verb> elements in the input XML document. The name child element of the verb is saved in a variable called $verb.

Next there is a large <xsl:choose> block which is a switch on the name of the verb. For each verb, there is a call to a template.

For example, if we have defined a new verb called "Supervisors", then an example verb XML might look like:

<staff:verb> <staff:name>Supervisors</staff:name> <staff:parameter id='parmName'>value</staff:parameter> … <staff:parameter id='parmName'>value</staff:parameter> </staff:verb>

In the master template of the XSLT, we might then add the following:

<xsl:choose> … <xsl:when test="$verb='Supervisors'"> <xsl:call-template name="Supervisors" /> </xsl:when> … </xsl:choose>

A matching template must also be added to the file.

<xsl:template name="Supervisors"> <sldap:staffQueries> ... </sldap:staffQueries> </xsl:template>

The addition of a completely new staff resolution entry can be done as follows:

Launch the WAS admin console. Navigate to Resources > People Directory.

Select the type of directory provider to be enhanced (eg. LDAP People Directory Provider).

The output of the transformation can be seen in WAS trace with the trace flag:

com.ibm.ws.staffsupport.*=all

It should be remembered that the XSLT transformation happens once during deployment. The result of the transformation is then kept by the WPS runtime. This means that we don't have to perform an expensive XSLT transformation every time we need a staff resolution. It is tempting to place conditional logic in the XSLT that we think will execute at runtime for every task but this is not how the architecture works and will lead to unexpected results. Please keep in mind that the XSL transform happens only during application deployment and at no other time.

Programming tips

If the value of a Verb parameter determines whether or not to perform processing, consider the following recipe:

<xsl:variable name="ParmName"> <xsl:value-of select="staff:parameter[@id='ParmName']"/> </xsl:variable> <xsl:if test="$ParmName!= ''"> <!-- Do something --> </xsl:if>

When testing/developing, always test the transformations using the WID XSLT engine. Do not try an debug an XSLT in place in the Human Task engine. It may not report XSLT errors.

After having changed an XSLT file, re-start the WPS server. It appears that is required in order for the resolution to be tried again.

Caching of staff resolutions

When a staff resolution is executed, its results are cached such that if a subsequent query is made, the relatively expensive LDAP lookup need not occur again and the previous result reused. Obviously this can result in stale data being used. HTM provides the ability to clear that cache either manually (immediately) through the admin console or at periodic scheduled times through a component known as the refresh demon.

Through the admin console, the path is:

Servers > WebSphere Application Servers > server > "Business Integration" > "Business Process Choreographer" > Human Task Manager

The runtime tab contains an option to refresh

Tracing Staff resolution

Switching on the WAS trace flag com.ibm.task.*=all will produce Human Task trace in the trace output data. An additional trace flag that might also be useful is

com.ibm.ws.staffsupport.*=all

This results in a combined trace flag of:

com.ibm.ws.staffsupport.*=all:com.ibm.task.*=all

Looking for trace records that start:

[5/24/10 14:59:45:603 CDT] 00000040 TraceHTM > com.ibm.task.staff.StaffQueryManager.getStaffResult(StaffQueryManager.java:1662) ENTRY StaffQuery =

will show the XML passed as input to the XSLT transform for the staff resolution plugin.

If the staff resolution system is LDAP, then it can occasionally be useful to examine the requests and responses flowing to and from the LDAP server. TDS has a an audit log where one can see requests being sent to it but there is no obvious way to see the responses being returned. In addition, other LDAP providers may be used that you are not familiar with. A solution to tracing the LDAP flows is to use the package called SLAMD. As part of this package, a tool called ldap-decoder is provided. This tool acts as a proxy/interceptor between an LDAP client and an LDAP server and displays LDAP queries and responses.

Here is a quick cheat sheet on using ldap-decoder. First, understand that it is a DOS command window tool and hence must be run from the DOS prompt. It sits between a real LDAP provider and a client (such as WPS) that is making LDAP requests. When WPS makes a query against LDAP, it is really making the request to ldap-provider. Ldap-decoder then forwards the request to the real LDAP provider which in turn sends a response back to the ldap-decoder tool. This in turn sends the response back to WPS. This means that ldap-decoder sees both the requests to LDAP and the responses returned from LDAP. Ldap-decoder knows how to parse the LDAP data stream (which is binary) and displays the requests and corresponding responses in the DOS console window.

The ldap-decoder tool can be found in the tools sub folder of the slamd installation directory. Before starting the tool, edit the file called set-java-home.bat and change the JAVA_HOME environment variable to point to a Java runtime.

To run ldap-decoder, execute the following:

ldap-decoder -h LDAPProviderHostname -p LDAPProviderPort -L LocalListenPort

Ldap-decoder will then start listening on the LocalListenPort and when it receives a request, it will be forwarded to the LDAPProviderHostname at port LDAPProviderPort. With ldap-decoder running, got to human task provider properties and change the URL where the LDAP provider goes to perform lookups. Point this to the ldap-decoder tool's port.

See Also – Custom staff resolution:


Worked Examples

This section contains some detailed worked examples illustrating how to create custom verbs.

Group and User List intersection

Consider a group that defines the set of all building inspectors in Dallas. Now consider a list of all inspectors in Texas that are certified to work on high-rise buildings. This list will include inspectors in Houston and San Antonio. If we want to assign work to a a building inspector in Dallas that is certified to work on high-rise buildings, we need a staff resolution expression that can build the intersection between a named group of users and a supplied list of users.

When we design such a solution, first we give some thought to how it would appear to an end user. It would show up in WID in the Human Task editor as a new type of selectable Verb for staff resolution.

We decide to call the new verb "Group an Users Intersection".

For most verbs, there are defined parameters and this is no exception. We see that we need two parameters:

With our knowledge of what must be done to achieve this task, we build an XML fragment that is to be made known to WID to allow us select the verb. A suitable XML fragment would be as follows:

<vs:DefineVerb name="Group and Users Intersection"> <vs:Description>Result set is the intersection between named users and a group</vs:Description> <vs:Mandatory> <vs:Parameter> <vs:Name>GroupName</vs:Name> <vs:Type>xsd:string</vs:Type> <vs:Hint></vs:Hint> </vs:Parameter> <vs:Parameter> <vs:Name>Users</vs:Name> <vs:Type>xsd:string</vs:Type> <vs:Hint></vs:Hint> </vs:Parameter> </vs:Mandatory> <vs:Optional></vs:Optional> </vs:DefineVerb>

This says that there is a new verb called Group and Users Intersection which has two mandatory parameters called GroupName and Users both of data type string.

This XML fragment should be added to the existing XML for the other verbs and made known to WID.

The next thing to do is to modify the XSLT file used to build the staff query language.

<xsl:template name="GroupAndUsersIntersection"> <xsl:variable name="Users"> <xsl:value-of select="staff:parameter[@id='Users']"/> </xsl:variable> <sldap:staffQueries> <sldap:usersOfGroup recursive="no"> <xsl:attribute name="groupDN"> <xsl:value-of select="staff:parameter[@id='GroupName']"/> </xsl:attribute> <!-- the object class can be either groupOfNames or groupOfUniqueNames --> <sldap:resultObject objectclass="groupOfUniqueNames" usage="recursive"> <!-- The name must be either member or uniqueMember depending on the Object Class being used --> <sldap:resultAttribute destination="intermediate" name="uniqueMember" /> </sldap:resultObject> <sldap:resultObject objectclass="inetOrgPerson" usage="simple"> <sldap:resultAttribute destination="userID" name="uid" /> <sldap:resultAttribute destination="eMailAddress" name="mail" /> <sldap:resultAttribute destination="preferredLocale" name="preferredLanguage" /> </sldap:resultObject> </sldap:usersOfGroup> <sldap:intersect> <xsl:attribute name="value"> <xsl:value-of select="$Users"/> </xsl:attribute> </sldap:intersect> </sldap:staffQueries> </xsl:template>

Group and Group intersection

In this example we are going to create a verb for group and group intersection. The result set will be the set of users in both groups.

The WID definition looks as follows:

<vs:DefineVerb name="Multiple Group Intersection"> <vs:Description>Result set is the intersection between multiple groups</vs:Description> <vs:Mandatory> <vs:Parameter> <vs:Name>FirstGroupName</vs:Name> <vs:Type>xsd:string</vs:Type> <vs:Hint></vs:Hint> </vs:Parameter> <vs:Parameter> <vs:Name>SecondGroupName</vs:Name> <vs:Type>xsd:string</vs:Type> <vs:Hint></vs:Hint> </vs:Parameter> </vs:Mandatory> <vs:Optional></vs:Optional> </vs:DefineVerb>

The corresponding XSLT looks as follows:

<xsl:template name="MultipleGroupIntersection"> <xsl:variable name="Group1"> <xsl:value-of select="staff:parameter[@id='FirstGroupName']"/> </xsl:variable> <xsl:variable name="Group2"> <xsl:value-of select="staff:parameter[@id='SecondGroupName']"/> </xsl:variable> <sldap:staffQueries> <sldap:usersOfGroup recursive="no"> <xsl:attribute name="groupDN"> <xsl:value-of select="$Group1"/> </xsl:attribute> <!-- the object class can be either groupOfNames or groupOfUniqueNames → <sldap:resultObject objectclass="groupOfUniqueNames" usage="recursive"> <!-- The name must be either member or uniqueMember depending on the Object Class being used → <sldap:resultAttribute destination="intermediate" name="uniqueMember" /> </sldap:resultObject> <sldap:resultObject objectclass="inetOrgPerson" usage="simple"> <sldap:resultAttribute destination="userID" name="uid" /> <sldap:resultAttribute destination="eMailAddress" name="mail" /> <sldap:resultAttribute destination="preferredLocale" name="preferredLanguage" /> </sldap:resultObject> </sldap:usersOfGroup> <sldap:intermediateResult> <xsl:attribute name="name">group2Users</xsl:attribute> <sldap:usersOfGroup recursive="no"> <xsl:attribute name="groupDN"> <xsl:value-of select="$Group2"/> </xsl:attribute> <!-- the object class can be either groupOfNames or groupOfUniqueNames → <sldap:resultObject objectclass="groupOfUniqueNames" usage="recursive"> <!-- The name must be either member or uniqueMember depending on the Object Class being used → <sldap:resultAttribute destination="intermediate" name="uniqueMember" /> </sldap:resultObject> <sldap:resultObject objectclass="inetOrgPerson" usage="simple"> <sldap:resultAttribute destination="intermediate" name="uid" /> </sldap:resultObject> </sldap:usersOfGroup> </sldap:intermediateResult> <sldap:intersect value="%group2Users%"> </sldap:intersect> </sldap:staffQueries> </xsl:template>

See also – Staff Resolution:

StaffQueryResultPostProcessorPlugin

When a Human Task is reach and a set of users for a task is determined, the resulting set of users is known as a Staff Query Result. When the query for the users is completed but before the result is used, a custom user exit can be called which is supplied the result set that will be used. This exit can modify the result set to add or remove users. This means that the user set can be completely controlled by application logic.

Before we discuss the plugin and its interface, let us first examine the StaffQueryResult interface.

This object represents a result from a staff query. The result can be one of four possible types:


An instance of a StaffQueryResult can hold data for one of these types. A StaffQueryResult can not name a group and a set of users. To determine what type of StaffQueryResult is present, the method called getResultType() is available. This returns one off:


If the type is Userids, then the userid data can be retrieved from the StaffQueryResult with one of either of the two methods:

Which ever one is called first prevents the other from being used (odd semantics that). The returned data structures (the Collection or the Map) can be modified and will reflect changes to the StaffQueryResult. Another way of saying this is that the returned data structures are references to the real data represented by the StaffQueryResult. The entries in these data collections are of type UserData which is a wrapper of three fields: The UserName, the Locale and the EmailAddress.

Instances of UserData objects and StaffQueryResult objects are created from a StaffQueryResultFactory instance.

See Also – Post Processing Plugins:

Replacement Expressions

At runtime, parameters to verbs can be taken from the context in which the process is running. This allows variables to be named in the verb definitions and the content of those variables will be used to supply data to the verb evaluations.

Here are some simple examples:


Stand-alone Expressions

The following table lists all expressions, that are available for stand-alone human tasks (i.e. tasks that are not embedded inline in a process). These expressions can be used inside descriptions as well as staff assignments. The expressions must be enclosed in '%' characters.

|| |htm:task.originator|task originator name| |htm:task.owner|task owner name| |htm:task.starter|task starter name| |htm:task.administrators|list of task administrators| |htm:task.potentialOwners|list of potential task owners| |htm:task.editors|list of task editors| |htm:task.readers|list of task readers| |htm:task.potentialStarters|list of potential task starters|

Staff Variables

|| |htm:property.CustomProp|value of task's custom property 'CustomProp'| |htm:task.displayName|default task display name| |htm:task.description|default task description| |htm:task.instanceID|task instance id| |htm:task.BPCExplorerURL|BPC Explorer URL to task/escalation details| |htm:input.[part][\XPath]|data from task's input message| |htm:output.[part][\XPath]|data from task's output message|

The htm:input notation may be difficult to get right at the first try. If you open the Interface in the WSDL editor (not the usual Interface editor) then you can find all the pieces of the expression

(click for full size)

The corresponding replacement expression is then:

%htm:input.requestContentCommentParameters\request/user%

Note that the syntax here is easy to mistype. The XPath expression can be arbitrarily complex. We have seen reports that for some styles of wrapped data it is easiest to do a tree search by beginning the XPath expression with '//', so this expression is usable instead of previous example:

%htm:input.requestContentCommentParameters\//request/user%

Another, and possible simpler recipe seems to be:

%htm:input.<operationName>Parameters\<operationParameterName>/<fieldName>%

Escalation variables

|| |htm:escalation.description|default escalation description| |htm:escalation.expectedTaskState|escalation's expected task state| |htm:escalation.instanceID|escalation instance id| |htm:escalation(escalationName).receivers|escalation receivers|

Inline expressions

The following tables illustrate the expressions available to in-line human tasks.

Process variables

|| |wf:variable.VariableName.[part][\XPath]|'VariableName[part][\XPath]' is passed to BFM to resolve the variable/expression. Note: Recent testing seems to be showing that this syntax may no longer be valid. It seems that simply naming the variable in % characters is all this required. For example %myStringVar% or %myBO\myfield% works.| |wf:property.customPropertyName|The 'customPropertyName' is passed to the BFM to resolve the custom property.|

Inline staff variables

|| |wf:process.starter|The starter of the process.| |wf:process.administrators|The administrators of the process.| |wf:process.readers|The readers of the process.| |wf:activity(activityName).potentialOwners|The potential owners of either the current activity or the activity named in brackets| |wf:activity(activityName).owner|The owner of either the activity named in brackets| |wf:activity(activityName).editors|The editors of either the current activity or the activity named in brackets| |wf:activity(activityName).readers|The readers of either the current activity or the activity named in brackets|

Note that inline staff variables which refer to sibling activities can only be resolved, if the referred activity is also an inline human task and is already started at the point in time when the staff resolution happens.

Getting data from Tasks

The replacement expressions allow data to be set into tasks, but what about getting data from tasks? The solution to this is the realization that even when a Human Task completes, but before the end of a process, the data associated with that task is not deleted and is available through API. Within a Java Snippet within a BPEL process, the following code can be used:

ActivityInstanceData aid = activityInstance("ActivityName");

From the ActivityInstanceData object, various properties can be retrieved such as the owner of the activity with the getOwner() method.

See Also – Replacement Expression:

Human Task User Interfaces

See Also:

Human Task API Event Handlers

WPS provides extensions that can be used to implement Human Task functionality.

An interface called APIEventHandlerPlugin5 can be implemented. A default version is already implemented called APIEventHandler which can be used to merely over-ride the functions desired.

The interface provides two methods of each function. These are prefixed with pre and post (for example: preCallTask and postCallTask). The following table lists the functions available to be caught.

|| |Function|When Called| |CallTask|CALL TASK| |CancelClaim|CANCEL CLAIM| |Claim|CLAIM| |Complete|COMPLETE| |CompleteWithFollowOnTask|COMPLETE WITH FOLLOW-ON TASK| |CompleteWithNewFollowOnTask|COMPLETE WITH NEW FOLLOW-ON TASK| |CreateAndCallTask|CREATE AND CALL TASK| |CreateAndStartTask|CREATE AND START TASK| |CreateAndStartTaskAsSubTask|CREATE AND START TASK AS SUBTASK| |CreateTask|CREATE TASK| |CreateWorkItem|CREATE WORKITEM| |DeleteTask|DELETE TASK| |DeleteWorkItem|DELETE WORKITEM| |GetTaskAndMarkRead|GET TASK AND MARK READ| |ReplaceWorkItem|REPLACE WORKITEM| |RestartTask|RESTART TASK| |ResumeTask|RESUME TASK| |SetBinaryCustomProperty|SET BINARY CUSTOM PROPERTY| |SetCustomProperty|SET CUSTOM PROPERTY| |SetFaultMessage|SET FAULT MESSAGE| |SetInputMessage|SET INPUT MESSAGE| |SetOutputMessage|SET OUTPUT MESSAGE| |SetTaskRead|SET TASK READ| |StartTask|START TASK| |StartTaskAsSubTask|START TASK AS SUBTASK| |SuspendTask|SUSPEND TASK| |SuspendTaskUntil|SUSPEND TASK UNTIL| |SuspendTaskWithCancelClaim|SUSPEND TASK WITH CANCEL CLAIM| |TerminateTask|TERMINATE TASK| |TransferToWorkBasket|TRANSFER TO WORK BASKET| |TransferWorkItem|TRANSFER WORKITEM| |TriggerEscalation|TRIGGER ESCALATION| |UpdateInactiveTask|UPDATE INACTIVE TASK| |UpdateTask|UPDATE TASK|

Experimentation seems to show that if the plugin JAR is added to the TaskContainer application as opposed to the system class path then if the TaskContainer is bounced, a new version of the plugin will be loaded. This can be useful during development.

WebSphere Process Server (WPS) provides Human Task Management through the HTM functions. When a Human Task is created, a set of eligible users is chosen for being potential owners of that task and for other roles also related to the task. This set of users is defined in the Task Template during development time in WID. At runtime, we have the option of changing the set of selected users through the concept of a custom plugin written in Java. This plugin is executed during the processing of the operation and can change the result.

To assist with the creation of such a plugin, a sample WID 6.2 Project Interchange has been written which is a skeleton/placeholder for the functions. It is provided as a companion to this document.

Once the plugin has been implemented, it must be installed into the WPS server. The InfoCenter documentation on this is vague and assumes some prerequisite knowledge on the part of the reader. The following illustrates the steps required to install a plugin.

After building the plugin and exporting it as a JAR file, this JAR needs to be registered as a WAS shared library. From within the Admin Console of WAS, create a new shared library.

Create a new shared library entry.

Give the new shared library a logical name and set the Classpath attribute to point to the absolute location on the local file system of the exported Plugin JAR file.

Next we need to tell the WPS provided Human Task Manager component to include the newly defined shared library in its own classpath. Again from the Admin Console, drill down to the Human Task Manager application.

Select the Human Task Manager application which has a name which starts with TaskContainer.....

In the References section, click on Shared library references.

Select the complete application (not the modules) by checking the box and then click the Reference shared libraries button.

In the list of available shared libraries, select the library we added earlier and add it to the list of libraries associated with the application.

The result will be that the application now references the shared library. Press OK to complete the task.

The shared library has now been associated with the task and we can complete this step with the OK button.

We have one last step ahead of us and this is to tell the Human Task Manager to use the plugin during its runtime processing. From the Admin Console, select the Application Server details and in the Business Integration > Business Process Choreographer section, select Human Task Manager.

Select Custom Properties.

In the Custom Properties for the Human Task Manager application, click on the property called Staff.PostProcessorPlugin. This will allow us to edit/change/set the value of this property.

Set the value of this property to be the logical name of the plugin. Note that this need not be the same name as the Java class that implements the plugin so take care here.

We have now completed all the necessary setup and can save all our changes.

The changes will not be visible to WPS until the next server restart so go ahead and restart the server.

No Comments
Back to top