33 Building a Custom Worklist Client
33.1 Introduction to Building Clients for Workflow Services
When creating a Java client application to call Human Workflow service, ensure that JRF is running on the same environment as the Java client application.
To build a simple worklist application:
The typical sequence of calls when building a simple worklist application is as follows.
- Get a handle to
IWorklistServiceClient
fromWorkflowServiceClientFactory
. - Get a handle to
ITaskQueryService
fromIWorklistServiceClient
. - Authenticate a user by passing a username and password to the authenticate method on
ITaskQueryService
. Get a handle toIWorkflowContext
. - Query the list of tasks using
ITaskQueryService
. - Get a handle to
ITaskService
fromIWorklistServiceClient
. - Iterate over the list of tasks returned, performing actions on the tasks using
ITaskService
.
The code sample below demonstrates how to build a client for workflow services. A
list of all tasks assigned to jstein
is queried. A task whose outcome has not
been set is approved.
try { //Create JAVA WorflowServiceClient IWorkflowServiceClient wfSvcClient = WorkflowServiceClientFactory.getWorkflowServiceClient( WorkflowServiceClientFactory.REMOTE_CLIENT); //Get the task query service ITaskQueryService querySvc = wfSvcClient.getTaskQueryService(); //Login as jstein IWorkflowContext ctx = querySvc.authenticate("jstein","welcome1".toCharArry(),null); //Set up list of columns to query List queryColumns = new ArrayList(); queryColumns.add("TASKID"); queryColumns.add("TASKNUMBER"); queryColumns.add("TITLE"); queryColumns.add("OUTCOME"); //Query a list of tasks assigned to jstein List tasks = querySvc.queryTasks(ctx, queryColumns, null, //Do not query additional info ITaskQueryService.AssignmentFilter.MY, null, //No keywords null, //No custom predicate null, //No special ordering 0, //Do not page the query result 0); //Get the task service ITaskService taskSvc = wfSvcClient.getTaskService(); //Loop over the tasks, outputting task information, and approving any //tasks whose outcome has not been set... for(int i = 0 ; i < tasks.size() ; i ++) { Task task = (Task)tasks.get(i); int taskNumber = task.getSystemAttributes().getTaskNumber(); String title = task.getTitle(); String taskId = task.getSystemAttributes().getTaskId(); String outcome = task.getSystemAttributes().getOutcome(); if(outcome == null) { outcome = "APPROVE"; taskSvc.updateTaskOutcome(ctx,taskId,outcome); } System.out.println("Task #"+taskNumber+" ("+title+") is "+outcome); } } catch (Exception e) { //Handle any exceptions raised here... System.out.println("Caught workflow exception: "+e.getMessage()); }
33.2 Packages and Classes for Building Clients
Use the following packages and classes for building clients.
-
oracle.bpel.services.workflow.metadata.config.model
The classes in this package contain the object model for the workflow configuration in the task definition file. The
ObjectFactory
class can create objects. -
oracle.bpel.services.workflow.metadata.routingslip.model
The classes in this package contain the object model for the routing slip. The
ObjectFactory
class can create objects. -
oracle.bpel.services.workflow.metadata.taskdisplay.model
The classes in this package contain the object model for the task display. The
ObjectFactory
class can create objects. -
oracle.bpel.services.workflow.metadata.taskdefinition.model
The classes in this package contain the object model for the task definition file. The
ObjectFactory
class can create objects. -
oracle.bpel.services.workflow.client.IWorkflowServiceClient
The interface for the workflow service client.
-
oracle.bpel.services.workflow.client.WorkflowServiceClientFactory
The factory for creating the workflow service client.
-
oracle.bpel.services.workflow.metadata.ITaskMetadataService
The interface for the task metadata service.
-
oracle.bpel.services.workflow.task.ITaskService
The interface for the task service.
-
oracle.bpel.services.workflow.task.IRoutingSlipCallback
The interface for the callback class to receive callbacks during task processing.
-
oracle.bpel.services.workflow.task.IAssignmentService
The interface for the assignment service.
33.3 Workflow Service Clients
Any worklist application accesses the various workflow services through the workflow service client. The workflow service client code encapsulates all the logic required for communicating with the workflow services using different local and remote protocols. After the worklist application has an instance of the workflow service client, it does not need to consider how the client communicates with the workflow services.
The advantages of using the client are as follows:
-
Hides the complexity of the underlying connection mechanisms such as SOAP/HTTP and Enterprise JavaBeans
-
Facilitates changing from using one particular invocation mechanism to another, for example from SOAP/HTTP to remote Enterprise JavaBeans
The following class is used to create instances of the IWorkflowServiceClient
interface:
oracle.bpel.services.workflow.client.WorkflowServiceClientFactory
WorkflowServiceClientFactory
has several methods that create workflow clients. The simplest method, getWorkflowServiceClient
, takes a single parameter, the client type. The client type can be one of the following:
-
WorkflowServiceClientFactory.REMOTE_CLIENT
—The client uses a remote Enterprise JavaBeans interface to invoke workflow services located remotely from the client. -
WorkflowServiceClientFactory.SOAP_CLIENT
—The client uses SOAP to invoke web service interfaces to the workflow services, located remotely from the client.
The other factory methods enable you to specify the connection properties directly (rather than having the factory load them from the wf_client_config.xml file
), and enable you to specify a logger to log client activity.
The following enhancements to the workflow service clients are included in this release:
-
You can specify the workflow client configuration using either a JAXB object or a map, as shown in example 1 and 2 below:
Example 1
WorkflowServicesClientConfigurationType wscct = new WorkflowServicesClientConfigurationType(); List<ServerType> servers = wscct.getServer(); ServerType server = new ServerType(); server.setDefault(true); server.setName(serverName); servers.add(server); RemoteClientType rct = new RemoteClientType(); rct.setServerURL("t3://stapj73:7001"); rct.setUserName("weblogic"); rct.setPassword("weblogic")); rct.setInitialContextFactory("weblogic.jndi.WLInitialContextFactory"); rct.setParticipateInClientTransaction(false); server.setRemoteClient(rct); IWorkflowServiceClient wfSvcClient = WorkflowServiceClientFactory.getWorkflowServiceClient( WorkflowServiceClientFactory.REMOTE_CLIENT, wscct, logger);
Example 2
Map<IWorkflowServiceClientConstants.CONNECTION_PROPERTY,java.lang.String> properties = new HashMap<IWorkflowServiceClientConstants.CONNECTION_PROPERTY,java.lang.String>(); properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.MODE, IWorkflowServiceClientConstants.MODE_DYNAMIC); properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.SOAP_END_POINT_ROOT, "http://localhost:8888"); IWorkflowServiceClient client = WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.SOAP_CLIENT, properties, null);
-
Clients can optionally pass in a
java.util.logging.Logger
where the client logs messages. If no logger is specified, then the workflow service client code does not log anything. The code sample below shows how a logger can be passed to the workflow service clients:java.util.logging.Logger logger = ....; IWorkflowServiceClient client = WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT, properties, logger);
Through the factory, it is possible to get the client libraries for all the workflow services. See Table 34-1 for the clients available for each of the services.
You can obtain instances of BPMIdentityService
and BPMIdentityConfigService
by calling the getSOAPIdentityServiceClient
and getSOAPIdentityConfigServiceClient
methods on WorkflowServiceClientFactory
. You can obtain all other services through an instance of IWorkflowServiceClient
.
The client classes use the configuration file wf_client_config.xml
for the service endpoints. In the client class path, this file is in the class path directly, meaning the containing directory is in the class path. The wf_client_config.xml
file contains:
-
A section for remote clients, as shown in the code sample below:
<remoteClient>
<serverURL>t3://hostname.domain_name:7001</serverURL>
<userName>weblogic</userName>
<password>weblogic</password>
<initialContextFactory>weblogic.jndi.WLInitialContextFactory
</initialContextFactory>
<participateInClientTransaction>false</participateInClientTransaction>
</remoteClient>
-
A section for SOAP endpoints for each of the services, as shown in the code sample below:
<soapClient>
<rootEndPointURL>http://hostname.domain_name:7001</rootEndPointURL>
<identityPropagation mode="dynamic" type="saml">
<policy-references>
<policy-reference enabled="true" category="security"
uri="oracle/wss10_saml_token_client_policy"/>
</policy-references>
</identityPropagation>
</soapClient>
The workflow client configuration XML schema definition is in the wf_client_config.xsd
file.
33.3.1 The IWorkflowServiceClient Interface
The IWorkflowServiceClient
interface provides methods, summarized in Table 33-1, for obtaining handles to the various workflow services interfaces.
Table 33-1 IWorkflowServiceClient Methods
Method | Interface |
---|---|
getTaskService |
oracle.bpel.services.workflow.task.ITaskService |
getTaskQueryService |
oracle.bpel.services.workflow.query.ITaskQueryService |
getTaskReportService |
oracle.bpel.services.workflow.report.ITaskReportService |
getTaskMetadataService |
oracle.bpel.services.workflow.metadata.ITaskMetadataService |
getUserMetadataService |
oracle.bpel.services.workflow.user.IUserMetadataService |
getRuntimeConfigService |
oracle.bpel.services.workflow.runtimeconfig.IRuntimeConfigService |
getTaskEvidenceService |
oracle.bpel.services.workflow.metadata.ITaskMetadataService |
33.4 Class Paths for Clients Using SOAP
SOAP clients must have the following JAR files in their class path.
$SOA_HOME/soa/modules/oracle.bpm.client_11.1.1/
oracle.bpm.bpm-services.client.jar
oracle.bpm.bpm-services.interface.jar
oracle.bpm.client.jar
oracle.bpm.web-resources.jar
$SOA_HOME/soa/modules/oracle.bpm.project_11.1.1/
oracle.bpm.project.catalog.jar
oracle.bpm.project.draw.jar
oracle.bpm.project.jar
oracle.bpm.project.model.jar
$SOA_HOME/soa/modules/oracle.bpm.runtime_11.1.1/
oracle.bpm.bpm-services.implementation.jar
oracle.bpm.bpm-services.internal.jar
oracle.bpm.core.jar
oracle.bpm.lib.jar
oracle.bpm.metadata.jar
oracle.bpm.metadata-interface.jar
oracle.bpm.papi.jar
oracle.bpm.xml.jar
$SOA_HOME/soa/modules/oracle.soa.fabric_11.1.1/
fabric-runtime.jar
bpm-infra.jar
$SOA_HOME/soa/modules/oracle.soa.workflow_11.1.1/
bpm-services.jar
bpm-workflow-datacontrol.jar
$SOA_HOME/soa/modules/
soa-startup.jar
$MW_HOME/oracle_common/modules/oracle.webservices_11.1.1/
wsclient.jar
$MW_HOME/oracle_common/modules/oracle.jrf_11.1.1/
jrf-api.jar
$MW_HOME/wlserver_10.3/server/lib/
wlthint3client.jar
${bea.home}/wlserver/server/lib/
wlfullclient.jar
$ORACLE_HOME/soa/plugins/jdeveloper/external/
oracle.external.soa.jrf-wsclient-extended.jar
${bea.home}/oracle_common/module/clients/
com.oracle.webservices.wls.jaxws-owsm-client_12.1.3.jar
You can generate the wlfullclient.jar
file using the commands shown in the code sample below:
cd ${bea.home}/wlserver/server/lib java -jar ../../../modules/com.bea.core.jarbuilder_2.2.0.0.jar
Note:
Client applications no longer use the system\services\config
or system\services\schema
directories in the class path.
33.5 Class Paths for Clients Using Remote EJBs
Clients using remote EJBs must have the following JAR files in their class path.
-
wlfullclient.jar
-
oracle.external.soa.jrf-wsclient-extended.jar
-
wlclient.jar
-
xmlparserv2.jar
-
xml.jar
-
bpm-infra.jar
-
bpm-services.jar
-
fabric-runtime.jar
Note:
Client applications no longer use the system\services\config
or system\services\schema
directories in the class path.
33.6 Initiating a Task
Tasks can be initiated programmatically.
Set the following task attributes:
-
taskDefinitionId
-
title
-
payload
-
priority
The following task attributes are optional, but are typically set by clients:
-
creator
-
ownerUser
—Defaults tobpeladmin
if empty -
processInfo
-
identificationKey
—Tasks can be queried based on the identification key from the TaskQueryService.
33.6.1 Creating a Task
The task object model is available in the package
oracle.bpel.services.workflow.task.model
To create objects in this model, use the ObjectFactory
class.
33.6.2 Creating a Payload Element in a Task
The task payload can contain multiple payload message attributes. Since the payload is not well defined until the task definition, the Java object model for the task does not contain strong type objects for the client payload. The task payload is represented by the AnyType
Java object. The AnyType
Java object is created with an XML element whose root is payload
in the namespace
http://xmlns.oracle.com/bpel/workflow/task
The payload XML element contains all the other XML elements in it. Each XML element defines a message attribute.
The code sample below shows how to set a task payload:
import oracle.bpel.services.workflow.task.model.AnyType; import oracle.bpel.services.workflow.task.model.ObjectFactory; import oracle.bpel.services.workflow.task.model.Task; .......... Document document = //createXMLDocument Element payloadElem = document.createElementNS("http://xmlns.oracle.com/bpel/workflow/ task", "payload"); Element orderElem = document.createElementNS("http://xmlns.oracle.com/pcbpel/test/order", "order"); Element child = document.createElementNS("http://xmlns.oracle.com/pcbpel/test/order", "id"); child.appendChild(document.createTextNode("1234567")); orderElem.appendChild(child); payloadElem.appendChild(orderElem); document.appendChild(payloadElem); task.setPayloadAsElement(payloadElem);
Note:
The AnyType.getContent()
element returns an unmodifiable list of XML elements. You cannot add other message attributes to the list.
33.6.3 Initiating a Task Programmatically
The code sample below shows how to initiate a vacation request task programmatically:
// create task object ObjectFactory objectFactory = new ObjectFactory(); Task task = objectFactory.createTask(); // set title task.setTitle("Vacation request for jcooper"); // set creator task.setCreator("jcooper"); // set taskDefinitionId. taskDefinitionId is the target // namespace of the task // If namespace is used, the active version of the composite corresponding // to that of the namespace will be used. task.setTaskDefinitionId("http://xmlns.oracle.com/VacationRequest/ Project1/Humantask1"); (Your task definition ID will be different.) // create and set payload Document document = XMLUtil.createDocument(); Element payloadElem = document.createElementNS(TASK_NS, "payload"); Element vacationRequestElem = document.createElementNS(VACATION_REQUEST_NS, "VacationRequestProcessRequest"); Element creatorChild = document.createElementNS(VACATION_REQUEST_NS, "creator"); creatorChild.appendChild(document.createTextNode("jcooper")); vacationRequestElem.appendChild(creatorChild); Element fromDateChild = document.createElementNS(VACATION_REQUEST_NS, "fromDate"); fromDateChild.appendChild(document.createTextNode("2006-08-05T12:00:00")); vacationRequestElem.appendChild(fromDateChild); Element toDateChild = document.createElementNS(VACATION_REQUEST_NS, "toDate"); toDateChild.appendChild(document.createTextNode("2006-08-08T12:00:00")); vacationRequestElem.appendChild(toDateChild); Element reasonChild = document.createElementNS(VACATION_REQUEST_NS, "reason"); reasonChild.appendChild(document.createTextNode("Hunting")); vacationRequestElem.appendChild(reasonChild); payloadElem.appendChild(vacationRequestElem); document.appendChild(payloadElem); task.setPayloadAsElement(payloadElem); IWorkflowServiceClient workflowServiceClient = WorkflowServiceClientFactory.getWorkflowServiceClient (WorkflowServiceClientFactory.SOAP_CLIENT); ITaskService taskService = workflowServiceClient.getTaskService(); IInitiateTaskResponse iInitiateTaskResponse = taskService.initiateTask(task); Task retTask = iInitiateTaskResponse.getTask(); System.out.println("Initiated: " + retTask.getSystemAttributes().getTaskNumber() + " - " + retTask.getSystemAttributes().getTaskId()); return retTask;
33.7 Changing Workflow Standard View Definitions
The worklist application and the UserMetadataService
API provide methods that you can use to create, update, and delete standard views.
See User Metadata Service for more information.