WebSphere Process Server Version 6 supports application integration, BPEL business processes, human tasks, and other features for Service-Oriented Architecture applications. Service Component Architecture (SCA) provides a component description model and gives you the ability to expose these components as services available over different protocols, while the Business Object framework provides a data model for the components. With SCA and Business Objects, business processes become services with operations that start processes and send messages to existing business processes. Human tasks can also be services with operations that create human tasks. These operations allow interaction with a specific service interface of a business process or human task, but in some situations, you may need a more generic interface to interact with business processes or human tasks. Information like the state of a business process or human task, when a human task is going to expire, or which activities have completed in a business process are not available through service interfaces, nor are operations like transferring a work item for a human task from one user to another or terminating a business process.
Figure 1. Interaction options for specific business processes and human task SCA components
The Business Flow Manager (BFM) and Human Task Manager (HTM) components of Business Process Choreographer offer a full set of J2EE APIs for working with business processes and human tasks. With these APIs, you can start business processes, send events to existing business processes, terminate and deleting business processes, and more. With the APIs, you can also create, claim, complete, transfer, and delete human tasks. For both business processes and human tasks, you can use a query method to search for specific business process and human task instances based on search criteria. However, although these APIs are quite robust, they are limited to J2EE clients.
Figure 2. Interaction options for business processes and human tasks by J2EE clients
The good news is that you can expose these APIs through additional channels using the SCA programming model and the APIs. Using a Java class defined as a SCA component, acting as a facade, you can implement the APIs in methods that you define through an interface that can be published over a variety of protocols, including SOAP/HTTP. The methods you expose as services also handle the conversion from the BFM and HTM-specific data types to Business Objects and from Business Objects to the BFM and HTM specific data types. This means that you can extend the full capabilities of BFM and HTM to a whole new set of clients, including Microsoft .NET clients.
Figure 3. New interaction options for accessing generic process and task APIs as services
In this article, you'll learn how to do extend the BFM and HTML capabilities to .NET clients by completing the following steps:
- Review the BFM and HTM APIs
- Define the interface
- Create the implementation and Web Service binding
- Build a Microsoft .NET client
You should have a general understanding of the capabilities of the BFM and HTM APIs, because you'll want to expose certain methods or combination of methods as services. You can't expose the methods directly as services because of the mapping of the API parameters to the message parts of the interfaces. When you consider which APIs to make available as services, think about what type of business operation you want to perform, and whether you want to combine multiple APIs under a single method to be exposed as a single operation.
For this exercise, we'll expose the following business operations as services, which means we'll have to make a number of HTM API calls:
- Create and start a task
- Claim a task
- Complete a task
- Query a task by ID
- Query for a collection of tasks
- Query for a collection of task IDs
You'll find the Javadoc for the HTM API at <$WPS_Install_Dir>\web\apidocs\com\ibm\task\api\ or <$WID_Install_Dir>\runtimes\bi_v6\web\apidocs\com\ibm\task\api\. The Javadoc for BFM is at <$WPS_Install_Dir>\web\apidocs\com\ibm\bpe\api\ or <$WID_Install_Dir>\runtimes\bi_v6\web\apidocs\com\ibm\bpe\api\.
To create and start a task, do the following:
- Get the task template from the HTM. This API returns an array of query templates, but if you specify the proper
whereClause, it returns a single template:LocalHumanTaskManager.queryTaskTemplates
((java.lang.String whereClause, java.lang.String orderByClause, java.lang.Integer threshold, java.util.TimeZone timeZone)
- Once you have the task template, create the input message for the task in preparation for passing the correct information into the task when it is started. This API returns an empty input message object:
LocalHumanTaskManager.
createInputMessage(TKIID tkiid)
- Now you can create and start a task. This API returns a TKIID, which is the unique identifier of the task:
LocalHumanTaskManager.
createAndStartTask(java.lang.String name, java.lang. String namespace, ClientObjectWrapper input, ReplyHandlerWrapper replyHandler)
Once you've chosen the business operations and determined which APIs need to be called, you can define the interface. To define the interface, do the following:
- In WebSphere Integration Developer, create a library project. This project will hold all interfaces and business objects for easy sharing inside WebSphere Integration Developer, as well as by clients using the interfaces and data objects to call the services.
- Create the interface and then create request/response operations for creating and starting a task, claiming a task, and completing a task, as shown in Figures 4 and 5.
Figure 4. Interface with operations to expose to the Web service clients to call the HTM
Figure 5. createAndStartTask operation with generic messages
- Next, define the request, response, and fault messages for the services in the interface definition. When considering the business objects to define, look at the type of information needed to call the BFM or HTM APIs.
You need the following information for each of the three APIs (queryTaskTemplates, createInputMessage, createAndStartTask) that are used to create and start a task:
- Task template name
- Task template namespace
- Input message, which is the business data that matches the input message for the human task
- Reply handler wrapper for sending the response to a specific listener
- In the interface editor, add an input part for each one of these values. The input message type can be the same as the message type that matches the input message for the actual task, or you can specify
anyType. Specifying the actual message type that matches the task, limits this operation to a specific task. - Create business objects for each task you want to create and start through a service. If you use
anyType, you can use the operation for tasks with different message types without needing a separate definition for creating and starting a task:- Specify
anyTypefor thereplyHandlerWrapper.
Figure 6. createAndStartTask operation with message parts to pass information for creating a human task
- For the response message, create a single part with the TKIID of the task created. You can also add a fault message with a single part for errors that occur while creating the task.
Figure 7. createAndStartTask operation with output and fault messages defined
- The claimTask API requires only the ID of the task to claim. It returns the input message of the task. For the claimTask operation, add a single part for the request and an single part for the output. For the output that will be the input message of the task, you can again choose to use a specific message type or a generic type. If you use a specific type, the operation will be able to claim only specific task types. With a generic type, you can claim any type of task. Use
anyTypefor the type of the response message part. Also add a fault message.
Figure 8. Messages set for claimTask operation
- For the completeTask operation, you need to use multiple APIs, which require the ID and output message of the task. Add two parts for the request. The APIs don't generate a response message. You can add your own response as a confirmation, but for this exercise, we won't do that. However, you should add a fault message to report any errors that may occur.
Figure 9. Operations and messages fully defined for creating, claiming and completing a human task
- Next you need to add operations for querying for a task by a specific ID, querying for tasks based on a specific search string, and querying for multiple task IDs based on a specific search string. To create these additional operations, you'll perform steps similar to the steps for defining operations with the appropriate message parts in the HTM APIs. There is a query API that allows for specific statements to be specified based on a
selectClauseandwhereClause. The return value is a set of values. You can define operations that perform specific queries, and provide flexibility.
Figure 10. Operations for querying for tasks
For the request and response messages, the only difference in the definitions is the use of business objects in the responses. - When retrieving a task by ID or specific string, numerous attributes are available. Using a business object definition for a task and its attributes makes for a simple and re-usable interface definition. A business object definition of the task should include all attributes which you would like to pass back to a client issuing the query request. Check out the pre-defined TASK view in the IBM WebSphere Business Process Management Version 6.0 Information Center for a complete list of task attributes that can be returned. Many of these attributes are needed only in a WebSphere Process Server environment, so you don't need to define them in the business object. Create a business object named
Taskin your library. Note the namespace value because you'll use it in the Java component implementation. - Create attributes with the appropriate types for the business object. This business object is used in the operation for retrieving a task by specific ID and retrieving a set of tasks based on a specific search string.
Figure 11. Task business object
- You can also create a business object for the response from the query that returns a set of tasks based upon a specific query. The WebSphere Integration Developer interface editor does not allow for an array object to be specified in a message part. In order to work around this limitation, you can use a business object that includes the array. Create a business object named
QueryResultTaskand add an attribute that is an array of tasks.
Figure 12. Business object with an array of tasks in order to return a result set of human tasks
- For the query operation to retrieve a set of IDs, create a business object named
QueryResultTaskID, which has an array of strings for the IDs.
Figure 13. Business object with an array of strings to return a result set of human tasks IDs
- Now define the request and response message parts in the operations. For the
getTaskByIDoperation, specify a single string for the ID for the request and single part of typeTaskfor the response. Also include a fault message for any error situations.
Figure 14. Operation getTaskByID to retrieve a human task by a specific ID
- For the getTasks operation, define parts that specify the query to be executed. You'll need parts for the
whereClauses, and you can also add parts for the ability to skip objects in the result set (skiptuples), as well as limit the number of tasks returned (threshold). Itapos;s better not to create a parameter for theselectClausebecause a definite result set is simpler for clients to use. For the response, use theQueryResultTask. Also include a fault message for any error situations.
Figure 15. Operation getTasks to retrieve a group of human tasks by a custom search
- For the getTaskIDs operation, again efine parts to specify the query to be executed. For the response, use the QueryResultTaskID. Also include a fault message for any error situations.
Figure 16. Operation getTaskByID to retrieve a task by a specific ID
- Specify
Create the Java implementation and Web service binding
Now that the interface is defined, you can create the Java component and call the appropriate HTM APIs to create and start a task. To do this, create a module and establish a dependency to the library using the Dependency editor. This makes the interface and business objects available for components in the module.
- Open the Assembly editor for the module. Drag the interface from the library and drop it in the Assembly editor. Select Component with No Implementation Type. Rename the component, if you want.
Figure 17. Assembly editor with the unimplemented skeleton component definition
- Implement the component as a Java component by right-clicking on the component and selecting Generate Implementation => Java.
Figure 18. Assembly editor with the skeleton Java component
When you implement the component, the icon changes and a Java class is created with a method that matches the name of the operation defined in the interface. Notice how the method has parameters that match the parts of the operation:
public String createAndStartTask(String taskName, String taskNamespace, Object inputMessage, Object replyHandlerWrapper) throws ServiceBusinessException { //TODO Needs to be implemented. }
- To implement this component properly add the following basic information:
- In the constructor of the class, add a method
initHTM, which initializes the HTM and the Business Object factory. This method is called from the other methods in the class.
private void initHTM() { try { InitialContext initialContext = new InitialContext(); taskHome = (LocalHumanTaskManagerHome) initialContext .lookup("java:comp/env/ejb/LocalHumanTaskManagerHome"); } catch (NamingException e) { StringBuffer s = new StringBuffer( "Lookup for Human Task Manager local interface (EJB) failed"); System.out.println(s); e.printStackTrace(); throw new ServiceBusinessException("s"); } }
- Add the appropriate private attributes to the class:
private LocalHumanTaskManagerHome taskHome; private BOFactory factory;
- In the constructor of the class, add a method
- To implement the method for creating and starting a task, begin by obtaining a handle to the HTM. Then build the query statement that is passed to the
queryTaskTemplatesAPI to retrieve the template from the HTM.
DataObject result = null; DataObject myMessage = null; ClientObjectWrapper input = null; TaskTemplate[] taskTemplates; try { initHTM(); LocalHumanTaskManager task = taskHome.create(); try { StringBuffer whereClause = new StringBuffer(); whereClause.append("TASK_TEMPL.NAME = '"); whereClause.append(taskName); whereClause.append("' AND TASK_TEMPL.NAMESPACE = '"); whereClause.append(taskNamespace); whereClause.append("'"); taskTemplates = task.queryTaskTemplates(whereClause.toString(), "TASK_TEMPL.NAME", new Integer(1), null);
- Use the
createInputMessageAPI to retrieve the input message for the tasktemplate. The input message is of typeClientObjectWrapperand is a wrapper object to the actual input message template from HTM.
input = task.createInputMessage(taskTemplates[0].getID());
- If the template is not found for the value passed as a parameter, a fault message should be returned. Implement this by throwing a
ServiceBusinessExceptionin thecatchblock to the innertrystatement.
} catch (NullPointerException npe) { StringBuffer s = new StringBuffer(); s.append("HTMF0002E: Task "); s.append(taskName); s.append(" can not be found deployed on server. Verify the task name."); System.out.println(s.toString()); npe.printStackTrace(); throw new ServiceBusinessException(s.toString()); } }
- To place the application data from the inputTask parameter in the input message for the task, you need to set a reference to the input message to a variable of type
DataObject. Because the input message is a wrapper and not a DataObject, you can't set it with the DataObject APIs.
if (input.getObject() != null ... input.getObject() instanceof DataObject) { myMessage = (DataObject) input.getObject(); }
- You can set the input message for the task with the application data passed in to the parameter. The application data is located in the
inputMessageparameter of the method. The name of theDataObjectis not known wheninputMessageisanyType. To set the input message of the task with the application data, you must get the name. You can find the name using the property feature of a DataObject. (If the interface to the task contains multiple parts and is not defined by a Business Object, additional programming would be required. In this example, a fault message is returned.) With the name, the application data is set on the reference.
if (inputMessage != null && inputMessage instanceof DataObject) { Type type1 = myMessage.getType(); java.util.List propList1 = type1.getProperties(); if (propList1.size() == 1) { Property prop1 = (Property) propList1.get(0); myMessage.set(prop1.getName(), (DataObject) inputMessage); } else { StringBuffer s = new StringBuffer(); s.append("HTMF0003E: "); s.append("The input messsage is null or is a primative part which is not supported at this time."); System.out.println(s.toString()); throw new ServiceBusinessException(s.toString()); }
- Now you can create and start the task with the createAndStartTask API, passing in the application data that you've set, as well as the template name and namespace. You could also set the reply context based on the attribute in the
inputTaskparameter. For this example, we have set it to null. The result of the method call is to return the ID of the task.
TKIID tkiid = task.createAndStartTask(taskName, taskNamespace,input, null); return tkiid.toString(); } else { StringBuffer s = new StringBuffer(); s.append("HTMF0004E: "); s.append("Messages received with primitive types are supported at this time."); System.out.println(s.toString()); throw new ServiceBusinessException(s.toString()); }
- Catch any exceptions that might occur. Differentiating between task exceptions and other exceptions helps the
client determine the type of exception. In either case, you should create a fault message and throw it in a
ServiceBusinessException.
} catch (TaskException e) { StringBuffer s = new StringBuffer(); s.append("HTMF0001E: "); s.append("Error occured within HumanTaskManagerComponent. Check server for more details."); System.out.println(s.toString()); throw new ServiceBusinessException(s.toString()); } catch (CreateException e) { StringBuffer s = new StringBuffer(); s.append("HTMF0005E: "); s.append("Error occured with retrieving HumanTaskManager in HumanTaskManagerComponent. Check availability of HumanTaskManager."); System.out.println(s.toString()); throw new ServiceBusinessException(s.toString()); }
The implementations for the
claimTaskandcompleteTaskWithMessageare similar, and are available in the projects in the Download section.
To enable these operations to retrieve tasks, do the following:
- For the
getTaskByIDquery, use the query API on the HTM to retrieve the correct attributes. In theselectCausespecify all of the attributes that will be passed back in the response task. ThewhereClauseshould use the task ID that is passed as a parameter.
public DataObject getTaskByID(String tkiid) throws ServiceBusinessException { DataObject result = null; boolean taskfound = false; try { initHTM(); LocalHumanTaskManager task = taskHome.create(); QueryResultSet[] resultSetArray = new QueryResultSet[1]; String selectClause = "DISTINCT TASK.ACTIVATED,TASK.COMPLETED,TASK.DUE,TASK.EXPIRES, TASK.FIRST_ACTIVATED,TASK.KIND,TASK.LAST_MODIFIED, TASK.LAST_STATE_CHANGE,TASK.NAME,TASK.NAME_SPACE, TASK.ORIGINATOR, TASK.OWNER, TASK.PRIORITY, TASK.STARTER,TASK.STARTED,TASK.STATE,TASK.TYPE, TASK.IS_ESCALATED,TASK.IS_INLINE,TASK.SUSPENDED,TASK. SUPPORT_AUTOCLAIM,TASK.SUPPORT_CLAIM_SUSP, TASK.SUPPORT_DELEGATION, TASK.TKIID"; String whereClause = "TASK.TKIID = ID('" + tkiid + "')"; resultSetArray[0] = task .query(selectClause, whereClause, null, new Integer(1), null);
- When the query call returns, the columns of the single result can be placed in a task object. Create a separate
buildTaskto prepare the result.
while (resultSetArray[0].next()) { taskfound = true; result = buildTask(resultSetArray); }
- In the
buildTaskmethod, the task business object definition you created is used to instantiate the value. For any of the values in the result set that are null, the exception is caught and the element is not added to the task object. This prevents processing of empty elements. The values in the result set are of different types and you'll need to use the appropriategetmethods to retrieve the values, as shown. The implementation for thegetTasksandgetTaskIDsmethods is similar, and is available in the projects in the Download section.
private DataObject buildTask(QueryResultSet resultSet[]) {
DataObject result = factory.create(
"http://BFMHTMLibrary/com/ibm/websphere/htm/task", "Task");
try {
result.setDate("activationTime",resultSet[0].getTimestamp(1).getTime());
} catch (NullPointerException npe) {}
try {
result.setDate("completionTime",
resultSet[0].getTimestamp(2).getTime());
} catch (NullPointerException npe) {}
// ...
// Additional set statements for remaining columns
// ...
try {
result.setString("tkiid",
(resultSet[0].getOID(24)).toString());
} catch (NullPointerException npe) {
}
|
- Save the class.
The component is now complete, and you can generate a Web service binding for it by completing the following steps:
- In the Assembly editor, right-click on the component and select Export => Web Service Binding.
Figure 19. Option to expose the Java component as a Web Service
- Click Yes in the resulting message to automatically generate a WSDL file.
Figure 20. Confirmation message
- In the Select Transport window, select soap/http and click OK.
Figure 21. Select transport
The binding is generated.
Figure 22. Fully defined Web service binding
The service is nearly complete. All that remains is to add the EJB reference for the Java component to reach the HTM EJB. WebSphere Integration Developer does not have a direct way of adding this reference for components, so you'll need to use J2EE tools.
- Select Window => Show View => Other.
- In the list of views, expand Basic and select Navigator, then click OK.
- Find the generated EJB project, which will have a name based on the name of the module holding the component.
- Expand <Module Name> => ejbModule => META-INF and open ejb-jar.xml. (If you open the file in a text editor, you may need to enable advanced J2EE capabilities under Workbench => Preferences.)
- In the EJB Deployment Descriptor editor, select the References tab.
Figure 23. List of objects that may have references defined
- Select Module and click Add.
- Select EJB reference and click Next.
Figure 24. Create an EJB reference
- In the Add EJB Reference window, do the following:
- For Name, enter
ejb/LocalHumanTaskManagerHome. - For Ref Type, select Local.
- Select Enterprise Beans not in the workspace.
- In the Local home field, specify
com.ibm.task.api.LocalHumanTaskManagerHome. - In the Local field, specify
com.ibm.task.api.LocalHumanTaskManager.
Figure 25. Reference settings
- Click Next, then Finish.
Figure 26. Completed reference
- For Name, enter
- For the WebSphere Binding information, enter
com/ibm/task/api/HumanTaskManagerHomein the JNDI name field.
Figure 27. WebSphere Binding setting for reference
- Save and close the file. Note that If you make any changes in the Assembly editor, you may need to recreate this value because the builders in WebSphere Integration Developer will recreate the EJB project in preparation for deployment.
The HTM service is complete. Once you deploy it and the application containing a human task template, the service is ready to receive requests from Web service clients to create and start tasks. A complete sample that implements these operations in the component is included in the Download section.
Build a .NET Client for the HTM Web service
Note: For the purposes of this article, we've used Microsoft Visual Studio 2005 Professional Edition (8.0.50727.42, RTM.050727-4200) and Microsoft .NET Framework 2.0.50727 with WinFix runtime and extensions (January Community Technology Preview).
You can create a .NET client to call the Web service and interact with human tasks using a variety of products. For a .NET client to call the Web service, you'll need to know the location of the WSDL file. If you want the interface to the Web service to be flexible and usable for any human task with a parameter type of anyType for the business object, you also need the business object definition or WSDL for the human tasks. Each human task is defined with an interface. The interface contains messages whose parts pass business information or business objects. In order for a .NET client to correctly pass the business object to match the format of the business object on the interface, you must also use the definition of the business object and interface.
You need to convert the interface and business objects to classes that a .NET application can use. You'll find the interfaces and business objects for human tasks in a Library module, which allows for easy sharing between modules and components. You can easily export a Library module from WebSphere Integration Developer as a ZIP file. In this example, the Library with the interface and business objects is in the CleansePublish.zip file.
- Extract CleansePublish.zip to a directory and navigate to \CleansePublishLibrary\CleansePublishLibrary\com\clipsandtacks\scm\.
Figure 28. Contents of library containing human task interface (WSDL and XSD) information
There are a number of WSDL interface and XSD files. For this example, we defined the human task with the
cleanseClipoperation described in the Clean.wsdl file. ThecleanseClipoperation has both a request and response message, both of type ofClipBG. See the details here. - To correctly generate the .NET classes, you need to define the
cleanseCliprequest message in a separate XSD file. This is because of Microsoft .Net's limited support of XSD includes. Copy thecleanseClipdefinition into a new file named cleanseClip.xsd. Include the schema and import information, and don't forget to add the closing schema tag.
<xsd:schema <xsd:schema targetNamespace="http://CleansePublishLibrary/com/clipsandtacks/scm/Clean" xmlns:bons1="http://CleansePublishLibrary/com/clipsandtacks/scm" xmlns:tns="http://CleansePublishLibrary/com/clipsandtacks/scm/Clean" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:import namespace="http://CleansePublishLibrary/com/clipsandtacks/scm" schemaLocation="../../.. /xsd-includes/http.CleansePublishLibrary.com.clipsandtacks.scm.xsd"/> <xsd:element name="cleanseClip"> <xsd:complexType> <xsd:sequence> <xsd:element name="inClipBG" nillable="true"type="bons1:ClipBG"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>
Note: if the definition for the
cleanseClipoperation response message was not of the same type (ClipBG), you would need to create another XSD file with a separate definition for the response. - As mentioned earlier, the
cleaseClipoperations are of typeClipBG. If you look at theClipBGdefinition in the ClipBG.xsd file, you'll find that it's based on the Business Graph definition (<xsd:import namespace="http://www.ibm.com/xmlns/prod/websphere/bo/6.0.0"/>), which is supported by WebSphere Process Server and is designed for specific integration applications. You'll need a copy of the Business Graph definition to correctly generate the .NET classes. Create a copy, called BusinessGraph.xsd, with the following contents. You can also get a copy of the definition at <$WebSphere Integration Developer Install Directory>\wstools\eclipse\plugins\com.ibm.ws.bo_6.0.0\xsd\BusinessGraph.xsd.
<schema
targetNamespace="http://www.ibm.com/xmlns/prod/websphere/bo/6.0.0"
xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<complexType name="BusinessGraph" abstract="true">
<sequence>
<any minOccurs="0" maxOccurs="unbounded"
namespace="http://www.w3.org/2001/XMLSchema" processContents="lax"/>
<element name="package" type="anyType" minOccurs="0" maxOccurs="unbounded"/>
<!--http://www.eclipse.org/emf/2002/Ecore#//EPackage-->
<element name="changeSummary" type="anyType" minOccurs="0"/>
<!--ChangeSummaryType-->
<element name="properties" type="anyType" minOccurs="0"/>
<!--DataObject-->
<element name="eventSummary" type="anyType" minOccurs="0" maxOccurs="unbounded"/>
<!--ReferenceToContainmentMapEntry-->
</sequence>
<anyAttribute namespace="##other" processContents="lax"/>
</complexType>
</schema>
|
- Place the copy of the definition in the same directory as the other XSD files. Note: If the interface of the human task does not have messages containing parts that are of the Business Graph type, you don't need the BusinessGraph.xsd file.
- You can use the XML Schemas/DataTypes support utility (xsd.exe), included with Microsoft Visual Studio or Microsoft Windows SDK, to generate the .NET class definitions from the interfaces and business objects. When executing the utility, you must specify the cleanseClip, BusinessGraph, and ClipBG XSD files.
- Execute the xsd.exe and specify the
/classesand/ooptions.
"C:\Program Files\Microsoft Visual Studio\SDK\v2.0\Bin\xsd.exe" C:\temp\CleansePublishLibrary\CleansePublishLibrary\com \clipsandtacks\scm\cleanseClip.xsd C:\temp\CleansePublishLibrary\CleansePublishLibrary\com \clipsandtacks\scm\ClipBG.xsd C:\temp\CleansePublishLibrary\CleansePublishLibrary \com\clipsandtacks\scm\BusinessGraph.xsd /classes /o:c:\temp\
A single BusinesGraph.cs file is generated at the location you specify (c:\temp\, in this example) that contains the class definitions for the different business objects. - Now you can create your .NET client application. In Windows Visual Studio 2005, create a new Visual C# Windows or Windows (WinFX) project.
Figure 29. Project Options in Visual Studio
- Add the BusinessGraph.cs to the project by selecting Project => Add Existing Item to bring the .NET classes into your client application.
- Besides the specific classes for the human tasks, you also need to generate the classes for the Web service. If the service is online, the easiest way to bring the classes into a project is to create a Web reference. (If the Web service is not online and you have a copy of the WSDL interface, you can specify the local copy or use the wsdl.exe utility included in Microsoft Windows SDK to generate the classes for the Web service.) Select Project => Add Web Reference.
- For the URL, enter
http://<hostname>:<port>/BFMHTMFacadeWeb/sca/HumanTaskManagerComponentExportWS?wsdl, where hostname is the system WebSphere Process Server is running on and port is the server accepting SOAP/HTTP requests. You can find the port value by opening the generated WSDL file for the Web services export in WebSphere Integration Developer. In the BFMHTMFacade module, expand Web Services Port and open the WSDL file with a text editor or WSDL editor (not Interface editor). Near the bottom, look for thesoap:addressvalue.
<soap:address location="http://localhost:9080/BFMHTMFacadeWeb/sca/ HumanTaskManagerComponentExport"/>
- Add
?wsdlto the URL and update the host and port values before using. - The service returns a summary of the operations found on the interface. Change the Web reference name to a something meaningful (in this case, HTMReference), because this is used in the class definitions.
Figure 29. Web Service lookup results
- Select the option in the Solution Explorer to show all files. You'll see the different parts of the Web services interface listed under HTMReference:
Figure 30. Web service reference information and generated artifacts
- Before you build the actual client application, you need to modify the generated classes of the Web service to support the appropriate business objects that will be passed for the human task for this client. This step is required because of current limitations in Microsoft .NET's support for dynamically converting
anyTypeobjects to actual object definitions at runtime.- In the Solution explorer view under Web Reference, expand Reference.map and open the Reference.cs file.
- Locate the public partial class definitions for
createAndStartTask,claimTaskResponse, andcompleteTask. Each one of these classes has an attribute for the message part (inputMessage), which holds the specific business object (ClipBG) of the task. Each one also has aget/setfor the attribute.
/// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.42")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)] public partial class createAndStartTask { private string taskNameField; private string taskNamespaceField; private object inputMessageField; private object replyHandlerWrapperField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute (Form=System.Xml.Schema. XmlSchemaForm.Unqualified, IsNullable=true)] public string taskName { get { return this.taskNameField; } set { this.taskNameField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute (Form=System.Xml.Schema. XmlSchemaForm.Unqualified, IsNullable=true)] public string taskNamespace { get { return this.taskNamespaceField; } set { this.taskNamespaceField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute (Form=System.Xml.Schema. XmlSchemaForm.Unqualified, IsNullable=true)] public object inputMessage { get { return this.inputMessageField; } set { this.inputMessageField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute (Form=System.Xml.Schema. XmlSchemaForm.Unqualified, IsNullable=true)] public object replyHandlerWrapper { get { return this.replyHandlerWrapperField; } set { this.replyHandlerWrapperField = value; } } }
- Replace the keyword type object with ClipBG on the attribute
public ClipBG inputMessageField;. Also replace the keyword type object with ClipBG for theget/seton theinputMessageField.
[System.Xml.Serialization.XmlElementAttribute (Form=System.Xml.Schema. XmlSchemaForm.Unqualified, IsNullable=true)] public ClipBG inputMessage { get { return this.inputMessageField; } set { this.inputMessageField = value; }
- Be sure to update all
inputMessagattributes andget/setmethods for all classes. - Save and close the file.
In this example, the same object type is used throughout the interface. For interfaces where you're using different business objects on the request and response, you'll need to modify the appropriate values in other classes, too. Without these modifications, the .NET runtime will be unable to correctly handle
anyTypebusiness objects. - You can build any type of .NET application to call the Web service and work with human tasks. Create a simple application, which allows you to specify the URL of the service and the operation to be called and then invoked by clicking a button. We won't go over the full details of building the user interface in this example.
Figure 31. Custom client for calling human task web service
- The implementation behind the user interface to call the appropriate operation is fairly straightforward. In the included namespace area, you can simplify the implementation by specifying an alias for
HTMClient.HTMReference.
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using client = HTMClient.HTMReference; namespace HTMClient { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window {
- Create a proxy object to call the Web service.
// Proxy for the HTM Web Service client.HumanTaskManagerComponent1Export1_ HumanTaskManagerHttpService service = new client.HumanTaskManagerComponent1Export1_ HumanTaskManagerHttpService();
- You can declare objects that will be used for the messages sent to and received from the different operations as shown:
// Creating the data constructs required by the service client.TKIID id = new client.TKIID(); // The default ClipBG used ClipBG bg = new ClipBG(); Clip clip = new Clip(); public Window1() { InitializeComponent(); }
- In the method that is mapped to the button-click event, you can check the combo box for the selected value. First retrieve the URL that was specified in the first textbox and set this in the proxy. The proxy has a default value set for the URL that was set when the Web reference was created and the classes for the Web service were generated. This URL value can be updated at any time, which allows for changes to be made without regenerating the classes.
private void button1_Click(object sender, EventArgs e) { service.Url = textBox1.Text;
- To create a task, you must first create an object, or task, that represents the operation. Next you need to set the information about the task to create and start, such as the task name and task namespace. You must also set the business data for the task, represented by
bg, after a number of attributes in the clip object have been set. Finally the operation to create and start the task is called and the response, which is an object containing the task ID, is stored.
try { if (comboBox1.Text.Equals("Create Task")) { client.createAndStartTask task = new client.createAndStartTask(); task.taskName = "SimpleTask"; task.taskNamespace = "http://MyTestProcess/com/acme/task"; clip.color = "red"; clip.clipID = "0001"; bg.Clip = clip; task.inputMessage = bg; id = (service.createAndStartTask(task)).tkiid; textBox2.Text = "Task [" + id + "] CREATED successfully."; }
- Once you've created a task, it can be claimed using the same ID. The claim operation is called with the response stored in another variable (
bg). Because of the changes made to theclaimTaskResponseclass, the response is correctly placed in an object (bg) of the correct type (ClipBG).
else if (comboBox1.Text.Equals("Claim Task")) { client.claimTask claim = new client.claimTask(); claim.tkiid = id; bg = service.claimTask(claim).inputMessage; textBox2.Text += "\r\nTask [" + id + "] CLAIMED successfully"; }
- The claimed task can be completed after it is claimed. First an object (
completeTaskMessage) representing the request is created. It is then set with the business information (stored inbg) before set in the request message. The ID of the task to complete is also set in the request. Finally the operation to complete the task is invoked, sending the request message.
else if (comboBox1.Text.Equals("Complete Task")) { client.completeTaskWithMessage completeTaskMessage = new client.completeTaskWithMessage(); completeTaskMessage.inputMessage = bg; completeTaskMessage.tkiid = id; service.completeTaskWithMessage(completeTaskMessage); textBox2.Text += "\r\nTask [" + id + "] COMPLETED successfully"; }
- Add a catch block to report any failures.
catch (Exception excep) { textBox2.Text += "\r\nError executing {" + comboBox1.Text + "] : " + excep.ToString(); } } }
You can create similar methods for the operations that return information about tasks:
- First you may want to implement a method that can be used for working with the task objects retrieved.
private void printTask(client.Task taskReturned) { textBox2.Text += "\r\n----- Task retrieved -----"; textBox2.Text += "\r\nactivationTime = " + taskReturned.activationTime.ToString(); textBox2.Text += "\r\ncompletionTime = " + taskReturned.completionTime.ToString(); textBox2.Text += "\r\ndueTime = " + taskReturned.dueTime.ToString(); //... // Additional statements for remaining valuess //... textBox2.Text += "\r\ntkiid= " + taskReturned.tkiid; }
- To retrieve a specific task, first create an object (
getTaskByIDMessage) that represents the request. Then set the task ID to retrieve. Finally invoke the operation to retrieve the task. You can then process the result, using theprintTaskmethod, in this case.
else if (comboBox1.Text.Equals("Query Specific Task")) { if (textBox3 != null) { client.getTaskByID getTaskByIDMessage = new client.getTaskByID(); getTaskByIDMessage.tkiid = textBox3.Text; client.getTaskByIDResponse taskReturned = service.getTaskByID(getTaskByIDMessage); printTask(taskReturned.task); } else { textBox2.Text += "\r\nTask ID field is empty. Specify a value."; } }
- The
getTasksoperation is used for retrieving a group of tasks. To call thegetTasksoperation, create the object (getTaskByIDMessage) for the request. Then you can set the different parts of the request. You can set these parts to values entered by the user or, as in this case, you can set them to empty values that will return all tasks. Finally, you can invoke the operation to retrieve the tasks and iterate through the result set in order to get information about each task.
else if (comboBox1.Text.Equals("Query All Tasks")) { client.getTasks getTasksMessage = new client.getTasks(); getTasksMessage.whereClause = ""; getTasksMessage.orderBy = ""; client.getTasksResponse queryResult = service.getTasks(getTasksMessage); for (int index = 0; index < queryResult.resultSet.Length;index++) { textBox2.Text += "\r\n----- Task " + index + " -----"; printTask(queryResult.resultSet[index]); } }
The implementation for the
getTaskIDsmethods is very similar, and is available, along with the full application, in the Download section.
The client application is now complete and can call the HTM Web service running on WebSphere Process Server to interact with the simple human task that is defined with the cleanseClip operation and is also running on WebSphere Process Server. You can easily build similar clients for other human tasks with different interfaces.
In this article, you've learned how to extend Business Process Choreographer APIs, typically available only for Java™ 2, Extended Edition (J2EE) clients, to Microsoft™ .NET™ clients. You can use the strategy and steps in this article to expose Human Task Manager or Business Flow Manager operations to additional Web service clients.
| Description | Name | Size | Download method |
|---|---|---|---|
| Facade application project | BFMHTMFacade.zip | 54KB | FTP |
| Microsoft Visual Studio 2005 project | HTMClient.zip | 169KB | FTP |
| Web service application sample | MyTestProcessApp.ear | 123KB | FTP |
| Human task application sample | BFMHTMFacadeApp.ear | 130KB | FTP |
Information about download methods
Learn
-
WebSphere Business Integration zone: Get the latest WebSphere Business integration technical resources and downloads.
-
WebSphere Web services zone: Get the latest WebSphere Web Services technical resources and information
- WebSphere Business Process Management Version 6.0 Information Center: Get complete product information on WebSphere Business Process Management, including WebSphere Process Server.
-
Service Data Objects (SDO) specification
-
IBM Education Assistant: IBM WebSphere Business Process Management
Get products and technologies
- Build your next development project with IBM trial software, available for download directly from developerWorks.
Discuss
-
developerWorks blogs: Get involved in the developerWorks community.
Comments (Undergoing maintenance)





