JBoss.orgCommunity Documentation

Chapter 17. Remote API

17.1. Remote Java API
17.1.1. Remote REST Java API Client Configuration
17.1.2. Remote JMS Java API Client Configuration
17.1.3. Remote CommandWebService Java API Client Configuration
17.1.4. Supported methods
17.2. REST
17.2.1. Runtime calls
17.2.2. Task calls
17.2.3. Deployment Calls
17.2.4. Deployment call details
17.2.5. Execute calls
17.2.6. REST summary
17.3. REST Query API
17.3.1. URL layout
17.3.2. Query Parameters
17.3.3. Parameter Table
17.3.4. Parameter examples
17.3.5. Query Output Format
17.4. JMS
17.4.1. JMS Queue setup
17.4.2. Using the remote Java API
17.4.3. Example JMS usage
17.5. Additional Information
17.5.1. REST Serialization: JAXB or JSON
17.5.2. Sending and receiving user class instances
17.5.3. Including the deployment id
17.5.4. REST Pagination
17.5.5. REST Map query parameters
17.5.6. REST Number query parameters
17.5.7. Runtime strategies

The workbench contains an execution server (for executing processes and tasks), which also allows you to invoke various process and task related operations through a remote API. As a result, you can setup your process engine "as a service" and integrate this into your applications easily by doing remote requests and/or sending the necessary triggers to the execution server whenever necessary (without the need to embed or manage this as part of your application).

Both a REST and JMS based service are available (which you can use directly), and a Java remote client allows you to invoke these operations using the existing KieSession and TaskService interfaces (you also use for local interaction), making remote integration as easy as if you were interacting with a local process engine.

The Remote Java API provides KieSession, TaskService and AuditService interfaces to the JMS and REST APIs.

The interface implementations provided by the Remote Java API take care of the underlying logic needed to communicate with the JMS or REST APIs. In other words, these implementations will allow you to interact with a remote workbench instance (i.e. KIE workbench or the jBPM Console) via known interfaces such as the KieSession or TaskService interface, without having to deal with the underlying transport and serialization details.

The first step in interacting with the remote runtime is to use a RemoteRuntimeEngineFactory static newRestBuilder(), newJmsBuilder() or newCommandWebServiceClientBuilder() to create a builder instance. Use the new builder instance to configure and to create a RuntimeEngine instance to interact with the server.

Each of the REST, JMS or WebService RemoteClientBuilder instances exposes different methods that allow the configuration of properties like the base URL of the REST API, JMS queue locations or timeout while waiting for responses.

The Remote Java API provides clients, not "instances"

While the KieSession, TaskSerivce and AuditService instances provided by the Remote Java API may "look" and "feel" like local instances of the same interfaces, please make sure to remember that these instances are only wrappers around a REST or jMS client that interacts with a remote REST or JMS API.

 

This means that if a requested operation fails on the server, the Remote Java API client instance on the client side will throw a RuntimeException indicating that the REST call failed. This is different from the behaviour of a "real" (or local) instance of a KieSession, TaskSerivce and AuditService instance because the exception the local instances will throw will relate to how the operation failed. Operations on a Remote Java API client instance that would normally throw other exceptions (such as the TaskService.claim(taskId, userId) operation when called by a user who is not a potential owner), will now throw a RuntimeException instead when the requested operation fails on the server.

 

Also, while local instances require different handling (such as having to dispose of a KieSession), client instances provided by the Remote Java API hold no state and thus do not require any special handling.

 

Finally, the instances returned by the client KieSession and TaskService instances (for example, process instances or task summaries) are not the same (internal) objects as used by the core engines. Instead, these returned instances are simple data transfer objects (DTOs) that implement the same interfaces but are designed to only return the associated data. Modifying or casting these returned instances to an internal implementation class will not succeed.

Each builder has a number of different (required or optional) methods to configure a client RuntimeEngine instance.

The following example illustrates how the Remote Java API can be used with the REST API.

public void startProcessAndHandleTaskViaRestRemoteJavaAPI(URL serverRestUrl, String deploymentId, String user, String password) {
        // the serverRestUrl should contain a URL similar to "http://localhost:8080/jbpm-console/"
        
        // Setup the factory class with the necessarry information to communicate with the REST services
        RuntimeEngine engine = RemoteRuntimeEngineFactory.newRestBuilder()
        .addUrl(serverRestUrl)
        .addTimeout(5)
        .addDeploymentId(deploymentId)
        .addUserName(user)
        .addPassword(password)
        // if you're sending custom class parameters, make sure that
        // the remote client instance knows about them!
        .addExtraJaxbClasses(MyType.class)
        .build();
        
        // Create KieSession and TaskService instances and use them
        KieSession ksession = engine.getKieSession();
        TaskService taskService = engine.getTaskService();
        
        // Each operation on a KieSession, TaskService or AuditLogService (client) instance
        // sends a request for the operation to the server side and waits for the response
        // If something goes wrong on the server side, the client will throw an exception.
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("paramName", new MyType("name", 23));
        ProcessInstance processInstance
        = ksession.startProcess("com.burns.reactor.maintenance.cycle", params);
        long procId = processInstance.getId();
        
        String taskUserId = user;
        taskService = engine.getTaskService();
        List<TaskSummary> tasks = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK");
        
        long taskId = -1;
        for (TaskSummary task : tasks) {
        if (task.getProcessInstanceId() == procId) {
        taskId = task.getId();
        }
        }
        
        if (taskId == -1) {
        throw new IllegalStateException("Unable to find task for " + user +
        " in process instance " + procId);
        }
        
        taskService.start(taskId, taskUserId);
        
        // resultData can also just be null
        Map<String, Object> resultData = new HashMap<String, Object>();
        taskService.complete(taskId, taskUserId, resultData);
        }

When configuring the remote JMS client, you must choose one of the following ways to configure the JMS connection:

In addition, if you are doing an operation on a task via the remote JMS client (and are not using the disableTaskSecurity() method), then you must also configure SSL. The following methods (described in more detail below) are available for this:

Each builder has a number of different (required or optional) methods to configure a client RuntimeEngine instance.

Remote JMS Runtime Engine Builder methods

addConnectionFactory(ConnectionFactory connectionFactory) when

Add a ConnectionFactory used to create JMS session to send and receive messages

addDeploymentId(String deploymentId) when

Set the deployment id of the deployment

addExtraJaxbClasses(Class…​ extraJaxbClasses ) when

Add extra classes to the client for when user-defined class instances are passed as parameters to client methods

When passing instances of user-defined classes in a Remote Java API call, it’s important to use this method first to add the classes so that the class instances can be serialized correctly.

addHostName(String hostname) when

Set the host name for the server that the client is making a JMS connection with

addJbossServerHostName(String hostname)

Set the host name of the JBoss EAP server that the client is making a JMS connection with

After using this method, no other configuration is needed with regards to the server. However, additional server parameters (host name, port) may be needed when also configuring SSL.

Make sure that the EAP version-appropriate org.jboss.naming:jboss-naming dependency is available on the classpath when doing this

addJmsConnectorPort(int port) when

Set the port used for the JMS connection connection with the server

addKeystoreLocation(String keystorePath) when

Set the location (path) of the keystore

addKeystorePassword(String keystorePassword) when

Set the password for the keystore

addKieSessionQueue(Queue ksessionQueue) when

Pass the javax.jms.Queue instance representing the KIE.SESSION queue used to receive process instance requests from the client

addPassword(String password) always
Set the password of the user connecting to the server
addProcessInstanceId(long process) when

Set the process instance id of the deployment

addRemoteInitialContext(InitialContext remoteInitialContext)

Set the remote InitialContext instance from the remote application server, which is then used to retrieve the ConnectionFactory and Queue instances

After using this method, no other configuration is needed with regards to the server. However, additional server parameters (host name, port) may be needed when also configuring SSL.

addResponseQueue(Queue responseQueue) when

Pass the javax.jms.Queue instance representing the KIE.RESPONSE queue used to send responses to the client

addTaskServiceQueue(Queue taskServiceQueue) when

Pass the javax.jms.Queue instance representing the KIE.TASK queue used to receive task operation requests to the client

addTimeout(int timeoutInSeconds)
Set the timeout for the JMS message. The default is 5 seconds.
addTruststoreLocation(String truststorePath) when

Set the location (path) of the keystore

addTruststoreLocation(String truststorePassword) when

Set the password for the keystore

addUserName(String userName) always

Set the name of the user connecting to the server

This is also the user whose permissions will be used when doing any task operations

clearJaxbClasses()
Clears the list of (user-defined) Classes that the client instance should know about
disableTaskSecurity()
Enables configuration of a client that can send task-related operation requests without having to use SSL. If this option is not used and a task operation is called, the client will throw an exception.
useKeystoreAsTruststore()
Uses the same file (location) and password for the truststore configuration when configuring SSL.

The following example illustrates how to configure a Remote Java API JMS client using a remote InitialContext instance along with SSL. In this case, the same file is being used as both the client’s keystore (the client’s identifying keys and certificates) and as the client’s truststore (the client’s list of trusted certificates from other parties, in this case, the server).

public void startProcessAndHandleTaskViaJmsRemoteJavaAPISsl(String hostNameOrIpAdress, int jmsSslConnectorPort,
        String deploymentId, String user, String password,
        String keyTrustStoreLocation, String keyTrustStorePassword,
        String processId) {
        
        InitialContext remoteInitialContext = getRemoteInitialContext();
        
        RuntimeEngine engine = RemoteRuntimeEngineFactory.newJmsBuilder()
        .addDeploymentId(deploymentId)
        .addRemoteInitialContext(remoteInitialContext)
        .addUserName(user)
        .addPassword(password)
        .addHostName(hostNameOrIpAdress)
        .addJmsConnectorPort(jmsSslConnectorPort)
        .useKeystoreAsTruststore()
        .addKeystoreLocation(keyTrustStoreLocation)
        .addKeystorePassword(keyTrustStorePassword)
        .build();
        
        // create JMS request
        KieSession ksession = engine.getKieSession();
        ProcessInstance processInstance = ksession.startProcess(processId);
        long procInstId = processInstance.getId();
        logger.debug("Started process instance: " + procInstId );
        
        TaskService taskService = engine.getTaskService();
        List<TaskSummary> taskSumList
        = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK");
        TaskSummary taskSum = null;
        for( TaskSummary taskSumElem : taskSumList ) {
        if( taskSumElem.getProcessInstanceId().equals(procInstId) ) {
        taskSum = taskSumElem;
        }
        }
        long taskId = taskSum.getId();
        logger.debug("Found task " + taskId);
        
        // get other info from task if you want to
        Task task = taskService.getTaskById(taskId);
        logger.debug("Retrieved task " + taskId );
        
        taskService.start(taskId, user);
        
        Map<String, Object> resultData = new HashMap<String, Object>();
        // insert results for task in resultData
        taskService.complete(taskId, user, resultData);
        logger.debug("Completed task " + taskId );
        }

Starting with this release, a simple webservice has been added to the remote API.

As mentioned above, the Remote Java API provides client-like instances of the RuntimeEngine, KieSession, TaskService and AuditService interfaces.

This means that while many of the methods in those interfaces are available, some are not. The following tables lists the methods which are available. Methods not listed in the below, will throw an UnsupportedOperationException explaining that the called method is not available.







REST API calls to the execution server allow you to remotely manage processes and tasks and retrieve various dynamic information from the execution server. The majority of the calls are synchronous, which means that the call will only finish once the requested operation has completed on the server. The exceptions to this are the deployment POST calls, which will return the status of the request while the actual operation requested will asynchronously execute.

When using Java code to interface with the REST API, the classes used in POST operations or otherwise returned by various operations can be found in the (org.kie.remote:)kie-services-client JAR.

This section lists REST calls that interface process instances.

The deploymentId component of the REST calls below must conform to the following regular expression:

[\w\.-]+(:[\w\.-]+){2,2}(:[\w\.-]*){0,2}

For more information about the composition of the deployment id, see the Deployment Calls section.

[POST] /runtime/{deploymentId}/process/{processDefId}/start

[POST] rest/runtime/{deploymentId}/process/{processDefId}/startform

[GET] /runtime/{deploymentId}/process/instance/{procInstId}

[POST] /runtime/{deploymentId}/process/instance/{procInstId+}/abort

[POST] /runtime/{deploymentId}/process/instance/{procInstId}/signal

[GET] /runtime/{deploymentId}/process/instance/{procInstId}/variable/{varName}

[POST] /runtime/{deploymentId}/signal

[GET] /runtime/{deploymentId}/workitem/{workItemId}

[POST] /runtime/{deploymentId}/workitem/{workItemId}/complete

[POST] /runtime/{deploymentId}/workitem/{workItemId: [0-9-]+}/abort

[POST] /history/clear

[GET] /history/instances

[GET] /history/instance/{procInstId}

[GET] /history/instance/{procInstId}/child

[GET] /history/instance/{procInstId}/node

[GET] /history/instance/{procInstId}/variable

[GET] /history/instance/{procInstId}/node/{nodeId}

[GET] /history/instance/{procInstId}/variable/{varId}

[GET] /history/process/{processDefId}

[GET] /history/variable/{varId}

[GET] /history/variable/{varId}/value/{value}

[GET] /history/variable/{varId}/instances

[GET] /history/variable/{varId}/value/{value}/instances

[GET] /runtime/{deploymentId}/history/variable/{varId}

[GET] /runtime/{deploymentId}/history/variable/{varId}/value/{value}

[GET] /runtime/{deploymentId}/history/variable/{varId}/instances

[GET] /runtime/{deploymentId}/history/variable/{varId}/value/{value}/instances

The following section describes the three different types of task calls: * Task REST operations that mirror the TaskService interface, allowing the user to interact with the remote TaskService instance * The Task query REST operation, that allows users to query for Task instances * Other Task REST operations that retrieve information

Task operation authorizations. Task REST operations use the user information (used to authorize and authenticate the HTTP call) to check whether or not the requested operations can happen. This also applies to REST calls that retrieve information, such as the task query operation. REST calls that request information will only return information about tasks that the user is allowed to see.

With regards to retrieving information, only users associated with a task may retrieve information about the task. However, the authorizations of progress and other modifications of task information are more complex. See the Task Permissions section in the Task Service documentation for more infomration.

Note

Given that many users have expressed the wish for a "super-task-user" that can execute task REST operations on all tasks, regardless of the users associated with the task, there are now plans to implement that feature. However, so far for the 6.x releases, this feature is not available.

All of the task operation calls described in this section use the user (id) used in the REST basic authorization as input for the user parameter in the specific call.

Some of the operations take an optional lanaguage query parameter. If this parameter is not given as a element of the URL itself, the default value of “en-UK” is used.

The taskId component of the REST calls below must conform to the following regex:

[0-9]+

[POST] /task/{taskId}/activate

[POST] /task/{taskId}/claim

[POST] /task/{taskId}/claimnextavailable

[POST] /task/{taskId}/complete - Completes a task - Returns a JaxbGenericResponse with the status of the operation - Parameters: Takes map query parameters, which are the "results" input for the complete operation

[POST] /task/{taskId}/delegate

[POST] /task/{taskId}/exit

[POST] /task/{taskId}/fail

[POST] /task/{taskId}/forward

[POST] /task/{taskId}/nominate

[POST] /task/{taskId}/release

[POST] /task/{taskId}/resume

[POST] /task/{taskId}/skip

[POST] /task/{taskId}/start

[POST] /task/{taskId}/stop - Stops a task - Returns a JaxbGenericResponse with the status of the operation

[POST] /task/{taskId}/suspend

[GET] /task/{taskId}/showTaskForm

 

Example 17.1. Query usage

This call retrieves the task summaries of all tasks that have a work item id of 3, 4, or 5. If you specify the same parameter multiple times, the query will select tasks that match any of that parameter.

http://server:port/rest/task/query?workItemId=3&workItemId=4&workItemId=5

The next call will retrieve any task summaries for which the task id is 27 and for which the work item id is 11. Specifying different parameters will result in a set of tasks that match both (all) parameters.

`http://server:port/rest/task/query?workItemId=11&taskId=27`

The next call will retrieve any task summaries for which the task id is 27 or the work item id is 11. While these are different parameters, the union parameter is being used here so that the union of the two queries (the work item id query and the task id query) is returned.

http://server:port/rest/task/query?workItemId=11&taskId=27&union=true`

The next call will retrieve any task summaries for which the status is Created and the potential owner of the task is Bob. Note that the letter case for the status parameter value is case-'in’sensitve.

http://server:port/rest/task/query?status=creAted&potentialOwner=Bob`

The next call will return any task summaries for which the status is Created and the potential owner of the task is bob. Note that the potential owner parameter is case-'sensitive'. bob is not the same user id as Bob!

http://server:port/rest/task/query?status=created&potentialOwner=bob`

The next call will return the intersection of the set of task summaries for which the process instance is 201, the potential owner is bob and for which the status is Created or Ready.

http://server:port/rest/task/query?status=created&status=ready&potentialOwner=bob&processInstanceId=201

That means that the task summaries that have the following characteristics would be included:

  • process instance id 201, potential owner bob, status Ready
  • process instance id 201, potential owner bob, status Created

And that following task summaries will not be included:

  • process instance id 183, potential owner bob, status Created
  • process instance id 201, potential owner '`mary`, status Ready
  • process instance id 201, potential owner bob, status `Complete`

The calls described in this section allow users to manage deployments. Deployments are in fact KieModule JARs which can be deployed or undeployed, either via the UI or via the REST calls described below. Configuration options, such as the runtime strategy, should be specified when deploying the deployment: the configuration of a deployment can not be changed after it has already been deployed.

The above deploymentId regular expression describes an expression that contains the following elements, separated from eachother by a : character:

In a more formal sense, the deploymentId component of the REST calls below must conform to the following regex:

`[\w\.-]+(:[\w\.-]+){2,2}(:[\w\.-]*){0,2}`

This regular expression is explained as follows:


This [\w\.-] element occurs at least 3 times and at most 5 times, separated by a : character each time.


There are 2 operations that can be used to modify the status of a deployment:

These POST deployment calls are both asynchronous, which means that the information returned by the POST request does not reflect the eventual final status of the operation itself.

Important

As noted above, both the /deploy and /undeploy operations are asynchronous REST operations. Successfull requests to these URLs will return the status 202 upon the request completion. RFC 2616 defines the 202 status as meaning the following:

 

RFC 2616: "the request has been accepted for processing, but the processing has not been completed."

 

This means the following:

  1. While the request may have been accepted "successfully", the operation itself (deploying or undeploying the deployment unit) may actually fail.
  2. Furthermore, information about deployments, such as that retrieved by calling the GET operations described below, are snapshots and the information (including the status of the deployment unit) may have changed by the time the user client receives the answer to the GET request.

[GET] /deployment/

[GET] /deployment/ {deploymentId}

[POST] /deployment/{deploymentId}/deploy

[POST] /deployment/{deploymentId}/undeploy

While there is a /runtime/{id}/execute and a task/execute method, both will take all types of commands. This is possible because execute takes a JaxbCommandsRequest object, which contains a list of (org.kie.api.command.)Command objects. The JaxbCommandsRequest has fields to store the proper deploymentId and processInstanceId information.

Of course, if you send a request with a command that needs this information (deploymentId, for example) and don’t fill the deploymentId in, the request will fail.

The following /rest/execute call can be used to start a process (with process id ‘evaluation’ in the project with deployment id ‘org.jbpm:Evaluation:1.0’) and two parameters (parameter employee equal to ‘krisv’ and reason equal to ‘Yearly performance evaluation’).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<command-request>
  <deployment-id>org.jbpm:Evaluation:1.0</deployment-id>
  <ver>6.2.0.1</ver>
  <user>krisv</user>
  <start-process processId="evaluation">
    <parameter>
      <item key="reason">
        <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Yearly performance evaluation</value>
      </item>
      <item key="employee">
        <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">krisv</value>
      </item>
    </parameter>
  </start-process>
</command-request>

Note that the request should also contain the following HTTP headers:

  • A Content-Type header with the value of application/xml
  • A Authorization header with basic authentication information, as specificed by RFC2616 (see link).

The response will contain information about the process instance that was just started:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <command-response>
    <deployment-id>org.jbpm:Evaluation:1.0</deployment-id>
    <ver>6.2.0.1</ver>
    <process-instance index="0">
    <process-id>evaluation</process-id>
    <id>15</id>
    <state>1</state>
    <parentProcessInstanceId>0</parentProcessInstanceId>
    <command-name>StartProcessCommand</command-name>
  </process-instance>
</command-response>

The /execute operation also supports sending user-defined class instances as parameters in the command. This relies on JAXB for serialization and deserialization. To be able to deserialize the custom class on the server side, a "Kie-Deployment-Id" header must also be set to the deployment id of the project.

For example, when starting a process or completing a task, a user typically passes additional parameters (process variable values or the result data for the completed task). These values are then either primitives (Strings, ints, etc.) or user-defined classes that were created using the data modeler in the workbench, added directly to the deployed project or part of a dependency to the deployment (project).

The following request starts a process which contains a custom TestObject class (with two fields) as a parameter.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<command-request>
  <deployment-id>demo:testproject:1.0</deployment-id>
  <ver>6.2.0.1</ver>
  <user>krisv</user>
  <start-process processId="testproject.testprocess">
    <parameter>
      <item key="testobject">
        <value xsi:type="testObject" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <field1>1</field1>
          <field2>2</field2>
        </value>
      </item>
    </parameter>
  </start-process>
</command-request>

Just as in the basic example above, both a Content-Type and Authorization header should be set in the request.

The 3 headers that therefore need to be set in the requst are the following:

  • A Content-Type header with the value of application/xml
  • A Authorization header with basic authentication information, as specificed by RFC2616 (see link).
  • A Kie-Deployment-Id header with the value of the deployment id containing the class definitions of any parameters sent with the command .

The URL templates in the table below are relative to the one of the following URLs:

The REST Query API allows users of the jBPM console and the KIE workbench (as well as products based on these applications) to "richly" query tasks, variables and process instances.

In the documentation below,

When you submit a REST call to the query operation, your URL will look something like this:

http://localhost:8080/business-central/rest/query/runtime/process/processId=org.process.frombulator&piid=29

A query containing multiple different query parameters will search for the intersection of the given parameters.

However, many of the query parameters described below can be entered multiple times: when multiple values are given for the same query parameter, the query will then search for any results that match one or more of the values entered.


The "task or process" column describes whether or not a query parameter can be used with the task and/or the process instance query operations.


[1] The date operations take strings with a specific date format as their values: yy-MM-dd_HH:mm:ss. However, users can also submit only part of the date:

  • Submitting only the date (yy-MM-dd) means that a time of 00:00:00 is used (the beginning of the day).
  • Submitting only the time (HH:mm:ss) means that the current date is used.

For the format used, see the SimpleDateFormat documentation.

[2] The var query parameter is used differently than other parameters. If you want to specify both the variable id and value of a variable (as opposed to just the variable id), then you can do it by using the var query parameter. The syntax is var_<variable-id>=<variable-value>


[3] The varreggex (or shortened version vr) parameter works similarly to the var query parameter. However, the value part of the query parameter can be a regular expression.

[4] By default, only the information from most recent (last) variable instance logs is retrieved. However, users can also retrieve all variable instance logs (that match the given criteria) by using this parameter.

The process instance query returns a JaxbQueryProcessInstanceResult instance.

The task query returns a JaxbQueryTaskResult instance.

Results are structured as follows:

A JaxbQueryProcessInstanceInfo object contains:

  • a process instance object
  • a list of 0 or more variable objects

A JaxbQueryTaskInfo info object contains:

  • the process instance id
  • a list of 0 or more task summary obejcts
  • a list of 0 or more variable objects

The Java Message Service (JMS) is an API that allows Java Enterprise components to communicate with each other asynchronously and reliably.

Operations on the runtime engine and tasks can be done via the JMS API exposed by the jBPM console and KIE workbench. However, it’s not possible to manage deployments or the knowledge base via this JMS API.

Unlike the REST API, it is possible to send a batch of commands to the JMS API that will all be processed in one request after which the responses to the commands will be collected and return in one response message.

The following is a rather long example that shows how to use the JMS API. The numbers ("callouts") along the side of the example refer to notes below that explain particular parts of the example. It’s supplied for those advanced users that do not wish to use the jBPM Remote Java API.

The jBPM Remote Java API, described here, will otherwise take care of all of the logic shown below.

package org.kie.remote.client.documentation.jms;
      
      import static org.kie.services.client.serialization.SerializationConstants.DEPLOYMENT_ID_PROPERTY_NAME;
      import static org.kie.services.client.serialization.SerializationConstants.SERIALIZATION_TYPE_PROPERTY_NAME;
      import static org.kie.services.shared.ServicesVersion.VERSION;
      
      import java.util.Collections;
      import java.util.List;
      import java.util.Set;
      import java.util.UUID;
      
      import javax.jms.Connection;
      import javax.jms.ConnectionFactory;
      import javax.jms.JMSException;
      import javax.jms.Message;
      import javax.jms.MessageConsumer;
      import javax.jms.MessageProducer;
      import javax.jms.Queue;
      import javax.jms.Session;
      import javax.jms.TextMessage;
      import javax.naming.InitialContext;
      import javax.naming.NamingException;
      
      import org.kie.api.command.Command;  1
      import org.kie.api.runtime.process.ProcessInstance;
      import org.kie.api.task.model.TaskSummary;
      import org.kie.remote.client.api.RemoteRuntimeEngineFactory;  2
      import org.kie.remote.client.api.exception.MissingRequiredInfoException;
      import org.kie.remote.client.api.exception.RemoteApiException;
      import org.kie.remote.client.api.exception.RemoteCommunicationException;
      import org.kie.remote.client.jaxb.ClientJaxbSerializationProvider;
      import org.kie.remote.client.jaxb.JaxbCommandsRequest;
      import org.kie.remote.client.jaxb.JaxbCommandsResponse;
      import org.kie.remote.jaxb.gen.AuditCommand; 3
      import org.kie.remote.jaxb.gen.GetTaskAssignedAsPotentialOwnerCommand;
      import org.kie.remote.jaxb.gen.StartProcessCommand;
      import org.kie.remote.jaxb.gen.TaskCommand;
      import org.kie.services.client.serialization.JaxbSerializationProvider;
      import org.kie.services.client.serialization.SerializationException;
      import org.kie.services.client.serialization.SerializationProvider;
      import org.kie.services.client.serialization.jaxb.impl.JaxbCommandResponse;
      import org.kie.services.client.serialization.jaxb.rest.JaxbExceptionResponse;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      public class SendJmsExample {
      
      protected static final Logger logger = LoggerFactory.getLogger(SendJmsExample.class);
      
      public void sendCommandsViaJms( String user, String password, String connectionUser, String connectionPassword,
      String deploymentId, String processId, String hostName ) {
      
      /**
      * JMS setup
      */
      // Get JNDI context from server
      InitialContext context = RemoteRuntimeEngineFactory.getRemoteJbossInitialContext(hostName, connectionUser, connectionPassword);
      
      // Create JMS connection
      ConnectionFactory connectionFactory;
      try {
      connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
      } catch( NamingException ne ) {
      throw new RuntimeException("Unable to lookup JMS connection factory.", ne);
      }
      
      // Setup queues
      Queue sessionQueue, taskQueue, sendQueue, responseQueue;
      try {
      sendQueue = sessionQueue = (Queue) context.lookup("jms/queue/KIE.SESSION");
      taskQueue = (Queue) context.lookup("jms/queue/KIE.TASK");
      responseQueue = (Queue) context.lookup("jms/queue/KIE.RESPONSE");
      } catch( NamingException ne ) {
      throw new RuntimeException("Unable to lookup send or response queue", ne);
      }
      
      /**
      * Command preparation
      */
      StartProcessCommand startProcCmd = new StartProcessCommand();
      startProcCmd.setProcessId(processId);
      
      /**
      * Send command via JMS and receive response
      */
      SerializationProvider serializationProvider = ClientJaxbSerializationProvider.newInstance();
      ProcessInstance procInst = (ProcessInstance) sendJmsCommand(startProcCmd,
      connectionUser, connectionPassword,
      user, password, deploymentId, null,
      connectionFactory, sendQueue, responseQueue,
      serializationProvider, Collections.EMPTY_SET, JaxbSerializationProvider.JMS_SERIALIZATION_TYPE,
      5 * 1000);
      
      /**
      * Command preparation
      */
      GetTaskAssignedAsPotentialOwnerCommand gtaapoCmd = new GetTaskAssignedAsPotentialOwnerCommand();
      gtaapoCmd.setUserId(user);
      
      // Send command request
      Long processInstanceId = null; // needed if you're doing an operation on a PER_PROCESS_INSTANCE deployment
      
      /**
      * Send command via JMS and receive response
      */
      @SuppressWarnings("unchecked")
      List<TaskSummary> taskSumList = (List<TaskSummary>) sendJmsCommand(gtaapoCmd,
      connectionUser, connectionPassword,
      user, password, deploymentId, processInstanceId,
      connectionFactory, sendQueue, responseQueue,
      serializationProvider, Collections.EMPTY_SET, JaxbSerializationProvider.JMS_SERIALIZATION_TYPE,
      5 * 1000);
      
      long taskId = taskSumList.get(0).getId();
      }
      
      // @formatter:off
      public static Object sendJmsCommand( Command command,
      String connUser, String connPassword,
      String userName, String password, String deploymentId, Long processInstanceId,
      ConnectionFactory factory, Queue sendQueue, Queue responseQueue,
      SerializationProvider serializationProvider, Set<Class<?>> extraJaxbClasses, int serializationType,
      long timeoutInMillisecs ) {
      // @formatter:on
      
      if( deploymentId == null && !(command instanceof TaskCommand || command instanceof AuditCommand) ) {
      throw new MissingRequiredInfoException("A deployment id is required when sending commands involving the KieSession.");
      }
      JaxbCommandsRequest req; 4
      if( command instanceof AuditCommand ) {
      req = new JaxbCommandsRequest(command);
      } else {
      req = new JaxbCommandsRequest(deploymentId, command);
      }
      
      req.setProcessInstanceId(processInstanceId);
      req.setUser(userName);
      req.setVersion(VERSION);
      
      Connection connection = null;
      Session session = null;
      JaxbCommandsResponse cmdResponse = null;
      String corrId = UUID.randomUUID().toString();
      String selector = "JMSCorrelationID = '" + corrId + "'";
      try {
      
      // setup
      MessageProducer producer;
      MessageConsumer consumer;
      try {
      if( password != null ) {
      connection = factory.createConnection(connUser, connPassword);
      } else {
      connection = factory.createConnection();
      }
      session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      
      producer = session.createProducer(sendQueue);
      consumer = session.createConsumer(responseQueue, selector);
      
      connection.start();
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to setup a JMS connection.", jmse);
      }
      
      // Create msg
      TextMessage textMsg; 5
      try {
      
      // serialize request
      String xmlStr = serializationProvider.serialize(req); 6
      textMsg = session.createTextMessage(xmlStr);
      
      // set properties
      // 1. corr id
      textMsg.setJMSCorrelationID(corrId);
      // 2. serialization info
      textMsg.setIntProperty(SERIALIZATION_TYPE_PROPERTY_NAME, serializationType);
      if( extraJaxbClasses != null && !extraJaxbClasses.isEmpty() ) {
      if( deploymentId == null ) {
      throw new MissingRequiredInfoException(
      "Deserialization of parameter classes requires a deployment id, which has not been configured.");
      }
      textMsg.setStringProperty(DEPLOYMENT_ID_PROPERTY_NAME, deploymentId);
      }
      // 3. user/pass for task operations
      boolean isTaskCommand = (command instanceof TaskCommand);
      if( isTaskCommand ) {
      if( userName == null ) {
      throw new RemoteCommunicationException(
      "A user name is required when sending task operation requests via JMS");
      }
      if( password == null ) {
      throw new RemoteCommunicationException(
      "A password is required when sending task operation requests via JMS");
      }
      textMsg.setStringProperty("username", userName);
      textMsg.setStringProperty("password", password);
      }
      // 4. process instance id
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to create and fill a JMS message.", jmse);
      } catch( SerializationException se ) {
      throw new RemoteCommunicationException("Unable to deserialze JMS message.", se.getCause());
      }
      
      // send
      try {
      producer.send(textMsg);
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to send a JMS message.", jmse);
      }
      
      // receive
      Message response;
      try {
      response = consumer.receive(timeoutInMillisecs);
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to receive or retrieve the JMS response.", jmse);
      }
      
      if( response == null ) {
      logger.warn("Response is empty");
      return null;
      }
      // extract response
      assert response != null: "Response is empty.";
      try {
      String xmlStr = ((TextMessage) response).getText();
      cmdResponse = (JaxbCommandsResponse) serializationProvider.deserialize(xmlStr); 7
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to extract " + JaxbCommandsResponse.class.getSimpleName()
      + " instance from JMS response.", jmse);
      } catch( SerializationException se ) {
      throw new RemoteCommunicationException("Unable to extract " + JaxbCommandsResponse.class.getSimpleName()
      + " instance from JMS response.", se.getCause());
      }
      assert cmdResponse != null: "Jaxb Cmd Response was null!";
      } finally {
      if( connection != null ) {
      try {
      connection.close();
      if( session != null ) {
      session.close();
      }
      } catch( JMSException jmse ) {
      logger.warn("Unable to close connection or session!", jmse);
      }
      }
      }
      
      String version = cmdResponse.getVersion();
      if( version == null ) {
      version = "pre-6.0.3";
      }
      if( !version.equals(VERSION) ) {
      logger.info("Response received from server version [{}] while client is version [{}]! This may cause problems.",
      version, VERSION);
      }
      
      List<JaxbCommandResponse<?>> responses = cmdResponse.getResponses();
      if( responses.size() > 0 ) {
      JaxbCommandResponse<?> response = responses.get(0);
      if( response instanceof JaxbExceptionResponse ) {
      JaxbExceptionResponse exceptionResponse = (JaxbExceptionResponse) response;
      throw new RemoteApiException(exceptionResponse.getMessage());
      } else {
      return response.getResult();
      }
      } else {
      assert responses.size() == 0: "There should only be 1 response, not " + responses.size() + ", returned by a command!";
      return null;
      }
      }
      }

1 2 3

These classes can all be found in the kie-api, kie-services-client and kie-services-jaxb JARs. For this example, the only dependency needed is org.kie.remote:kie-remote-client, which transitively depends on both the org.kie:kie-api and org.kie.remote:kie-remote-jaxb artifacts.

4

The JaxbCommandsRequest instance is the "holder" object in which you can place all of the commands you want to execute in a particular request. By using the JaxbCommandsRequest.getCommands() method, you can retrieve the list of commands in order to add more commands to the request. A deployment id is required for command request messages that deal with business processes. Command request messages that only contain human task-related commands do not require a deployment id.

5

Note that the JMS message sent to the remote JMS API must be constructed as follows:

  • It must be a JMS text message (javax.jms.TextMessage).
  • It must contain a serialized instance of a JaxbCommandsRequest, added to the message as a UTF string
  • It must have a filled JMS Correlation ID property.
  • It must have an int property with the name of "serialization" set to an acceptable value (only 0 at the moment).

6 7

The same serialization mechanism used to serialize the request message will be used to serialize the response message.

Except for the Execute calls, all other REST calls described below can use either JAXB or JSON.

All REST calls, unless otherwise specified, use JAXB serialization.

When using JSON, make sure to add the JSON media type ("application/json") to the ACCEPT header of your REST call.

Sometimes, users may wish to pass instances of their own classes as parameters to commands sent in a REST or Webservice request or JMS message. In order to do this, there are a number of requirements.

Some of the REST calls below return lists of information. The results of these operations can be paginated, which means that the lists can be split up and returned according to the parameters sent by the user.

For example, if the REST call parameters indicate that page 2 with page size 10 should be returned for the results, then results 10 to (and including) 19 will be returned.

The first page is always page 1 (as opposed to page "0").


If both a "long" pagination parameter and its synonym are used, then only the value from the "long" variant is used. For example, if the page is given with a value of 11 and the p parameter is given with a value of 37, then the value of the page parameter, 11 , will be used and the p parameter will be ignored.

For the following operations, pagination is always used. See above for the default values used.