Skip to main content

Enable generic Web services interfaces for Business Process Choreographer

Extend Business Process Choreographer APIs to .NET clients

Eric Erpenbach (eerpenb@us.ibm.com), Advisory Software Engineer, IBM
Eric Erpenbach is a Software Engineer at IBM in Rochester, Minnesota. He focuses on the WebSphere Process Integration family of products.
Anh-Khoa Phan (anhkhoa@us.ibm.com), Staff Software Engineer, IBM
Anh-Khoa Phan is a Software Engineer at IBM in Rochester, Minnesota. He currently works on WebSphere Business Integration.

Summary:  In this article, you'll learn how to extend the IBM® WebSphere® Process Server Business Process Choreographer Human Task Manager and Business Flow Manager APIs, typically available only for Java™ 2, Extended Edition (J2EE) clients, to Web services clients, such as Microsoft™ .NET™ clients.

Date:  12 Apr 2006
Level:  Advanced
Activity:  441 views

Overview

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
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
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
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:

  1. Review the BFM and HTM APIs
  2. Define the interface
  3. Create the implementation and Web Service binding
  4. Build a Microsoft .NET client

Review the BFM and HTM APIs

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:

  1. 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)

  2. 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)

  3. 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)

Define the interface

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:

  1. 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.
  2. 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


  3. 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
    This information must be included in the input message, or request, sent to the interface and then passed to the APIs.
  4. 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.
  5. 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:
    1. Specify anyType for the replyHandlerWrapper.

      Figure 6. createAndStartTask operation with message parts to pass information for creating a human task


    2. 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


    3. 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 anyType for the type of the response message part. Also add a fault message.

      Figure 8. Messages set for claimTask operation


    4. 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


    5. 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 selectClause and whereClause. 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.
    6. 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 Task in your library. Note the namespace value because you'll use it in the Java component implementation.
    7. 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


    8. 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 QueryResultTask and 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


    9. 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


    10. Now define the request and response message parts in the operations. For the getTaskByID operation, specify a single string for the ID for the request and single part of type Task for the response. Also include a fault message for any error situations.

      Figure 14. Operation getTaskByID to retrieve a human task by a specific ID


    11. 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 the selectClause because a definite result set is simpler for clients to use. For the response, use the QueryResultTask. Also include a fault message for any error situations.

      Figure 15. Operation getTasks to retrieve a group of human tasks by a custom search


    12. 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


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.

  1. 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


  2. 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.
    	}
    			


  3. To implement this component properly add the following basic information:
    1. 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");
            }
      } 
      


    2. Add the appropriate private attributes to the class:
      private LocalHumanTaskManagerHome taskHome;
      
      private BOFactory factory;
      

  4. 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 queryTaskTemplates API 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);
    
    


  5. Use the createInputMessage API to retrieve the input message for the tasktemplate. The input message is of type ClientObjectWrapper and is a wrapper object to the actual input message template from HTM.
    input = task.createInputMessage(taskTemplates[0].getID());
    			


  6. If the template is not found for the value passed as a parameter, a fault message should be returned. Implement this by throwing a ServiceBusinessException in the catch block to the inner try statement.
    } 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());
    }
    			}
    			


  7. 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();
    }
    			


  8. 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 inputMessage parameter of the method. The name of the DataObject is not known when inputMessage is anyType. 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());
    		}
    


  9. 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 inputTask parameter. 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());
       }
    


  10. 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 claimTask and completeTaskWithMessage are similar, and are available in the projects in the Download section.

To enable these operations to retrieve tasks, do the following:

  1. For the getTaskByID query, use the query API on the HTM to retrieve the correct attributes. In the selectCause specify all of the attributes that will be passed back in the response task. The whereClause should 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);
    


  2. When the query call returns, the columns of the single result can be placed in a task object. Create a separate buildTask to prepare the result.
    while (resultSetArray[0].next()) {
    		taskfound = true;
    		result = buildTask(resultSetArray);
    	}
    


  3. In the buildTask method, 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 appropriate get methods to retrieve the values, as shown. The implementation for the getTasks and getTaskIDs methods 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) {
   }

  1. Save the class.

The component is now complete, and you can generate a Web service binding for it by completing the following steps:

  1. 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


  2. Click Yes in the resulting message to automatically generate a WSDL file.

    Figure 20. Confirmation message


  3. 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.

  1. Select Window => Show View => Other.
  2. In the list of views, expand Basic and select Navigator, then click OK.
  3. Find the generated EJB project, which will have a name based on the name of the module holding the component.
  4. 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.)
  5. In the EJB Deployment Descriptor editor, select the References tab.

    Figure 23. List of objects that may have references defined


  6. Select Module and click Add.
  7. Select EJB reference and click Next.

    Figure 24. Create an EJB reference


  8. In the Add EJB Reference window, do the following:
    1. For Name, enter ejb/LocalHumanTaskManagerHome.
    2. For Ref Type, select Local.
    3. Select Enterprise Beans not in the workspace.
    4. In the Local home field, specify com.ibm.task.api.LocalHumanTaskManagerHome.
    5. In the Local field, specify com.ibm.task.api.LocalHumanTaskManager.

      Figure 25. Reference settings


    6. Click Next, then Finish.


    Figure 26. Completed reference


  9. For the WebSphere Binding information, enter com/ibm/task/api/HumanTaskManagerHome in the JNDI name field.

    Figure 27. WebSphere Binding setting for reference


  10. 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.

  1. 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 cleanseClip operation described in the Clean.wsdl file. The cleanseClip operation has both a request and response message, both of type of ClipBG. See the details here.

  2. To correctly generate the .NET classes, you need to define the cleanseClip request message in a separate XSD file. This is because of Microsoft .Net's limited support of XSD includes. Copy the cleanseClip definition 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 cleanseClip operation response message was not of the same type (ClipBG), you would need to create another XSD file with a separate definition for the response.

  3. As mentioned earlier, the cleaseClip operations are of type ClipBG. If you look at the ClipBG definition 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>

  1. 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.
  2. 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.
  3. Execute the xsd.exe and specify the /classes and /o options.
    "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.
  4. 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


  5. Add the BusinessGraph.cs to the project by selecting Project => Add Existing Item to bring the .NET classes into your client application.
  6. 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.
  7. 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 the soap:address value.
    <soap:address
    location="http://localhost:9080/BFMHTMFacadeWeb/sca/
    HumanTaskManagerComponentExport"/>
    


  8. Add ?wsdl to the URL and update the host and port values before using.
  9. 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


  10. 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


  11. 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 anyType objects to actual object definitions at runtime.
    1. In the Solution explorer view under Web Reference, expand Reference.map and open the Reference.cs file.
    2. Locate the public partial class definitions for createAndStartTask, claimTaskResponse, and completeTask. 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 a get/set for 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;
              	}
      	}
          }
      


    3. Replace the keyword type object with ClipBG on the attribute public ClipBG inputMessageField;. Also replace the keyword type object with ClipBG for the get/set on the inputMessageField.
      [System.Xml.Serialization.XmlElementAttribute
         (Form=System.Xml.Schema.		
         XmlSchemaForm.Unqualified, IsNullable=true)]
            public ClipBG inputMessage {
               get {
                  return this.inputMessageField;
               }
            set {
               this.inputMessageField = value;
            }
      


    4. Be sure to update all inputMessag attributes and get/set methods for all classes.
    5. 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 anyType business objects.

  12. 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


  13. 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
        {
    


  14. Create a proxy object to call the Web service.
    // Proxy for the HTM Web Service
      client.HumanTaskManagerComponent1Export1_
      HumanTaskManagerHttpService service = new
      client.HumanTaskManagerComponent1Export1_
      HumanTaskManagerHttpService();
    


  15. 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();                
    }
    


  16. 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;
    


  17. 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.";
              }
    
    


  18. 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 the claimTaskResponse class, 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";
    }
    


  19. 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 in bg) 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";
    }
    


  20. 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:

  1. 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; 
    }
    


  2. 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 the printTask method, 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.";
          }
    }
    


  3. The getTasks operation is used for retrieving a group of tasks. To call the getTasks operation, 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 getTaskIDs methods 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.

Summary

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.



Downloads

DescriptionNameSizeDownload method
Facade application projectBFMHTMFacade.zip54KBFTP|HTTP
Microsoft Visual Studio 2005 projectHTMClient.zip169KBFTP|HTTP
Web service application sampleMyTestProcessApp.ear123KBFTP|HTTP
Human task application sampleBFMHTMFacadeApp.ear130KBFTP|HTTP

Information about download methods


Resources

Learn

Get products and technologies

  • Build your next development project with IBM trial software, available for download directly from developerWorks.

Discuss

About the authors

Eric Erpenbach is a Software Engineer at IBM in Rochester, Minnesota. He focuses on the WebSphere Process Integration family of products.

Anh-Khoa Phan is a Software Engineer at IBM in Rochester, Minnesota. He currently works on WebSphere Business Integration.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, SOA and Web services
ArticleID=107841
ArticleTitle=Enable generic Web services interfaces for Business Process Choreographer
publish-date=04122006
author1-email=eerpenb@us.ibm.com
author1-email-cc=crothemi@us.ibm.com
author2-email=anhkhoa@us.ibm.com
author2-email-cc=crothemi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers