JBoss.orgCommunity Documentation

Chapter 17. Remote API

17.1. Remote Java API
17.1.1. The REST Remote Java RuntimeEngine Factory
17.1.2. The JMS Remote Java RuntimeEngine Factory
17.1.3. Supported methods
17.2. REST
17.2.1. Runtime calls
17.2.2. History calls
17.2.3. Task calls
17.2.4. Deployment calls
17.2.5. Deployment call details
17.2.6. Execute calls
17.2.7. Additional Information
17.2.8. REST summary
17.3. JMS
17.3.1. JMS Queue setup
17.3.2. Using the remote Java API
17.3.3. Serialization issues
17.3.4. Example JMS usage

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 AuditLogService 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 create either the RemoteRestRuntimeFactory or RemoteJmsRuntimeEngineFactory, both of which are instances of the RemoteRuntimeEngineFactory interface.

The configuration for the Remote Java API is done when creating the RemoteRuntimeEngineFactory instance: there are a number of different constructors for both the JMS and REST impelementations that allow the configuration of such things as the base URL of the REST API, JMS queue location or timeout while waiting for responses.

Once the factory instances have been created, there are a couple of methods that can then be used to instantiate the client instance that you want to use:

The RemoteRestRuntimeFactory has 4 constructor methods available. Besides an "everything" constructor method which provides all of the options provided below, there are also the following 3 constructors:

RemoteRestRuntimeFactory constructor method parameters

Simple constructor

Constructor with timeout parameter

Constructor with form-based authorization parameter

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

public void startProcessAndHandleTaskViaRestRemoteJavaAPI(URL instanceUrl, 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
  RemoteRuntimeEngineFactory restSessionFactory 
    = new RemoteRestRuntimeFactory(deploymentId, instanceUrl, user, password);
  // Create KieSession and TaskService instances and use them
  RemoteRuntimeEngine engine = restSessionFactory.newRuntimeEngine();
  KieSession ksession = engine.getKieSession();
  TaskService taskService = engine.getTaskService();
  // Each opertion 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. 
  ProcessInstance processInstance 
    = ksession.startProcess("com.burns.reactor.maintenance.cycle");
  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);
}

The Remote JMS Java RuntimeEngine works precisely the same as the REST variant, except that it has different constructors.

The RemoteJmsRuntimeEngineFactory constructors can be grouped into 3 types. The list below specifies the main arguments to each group type.

Configuration using the server URL

Configuration using only the URL of the server where the jBPM Console or Kie Workbench instance is running, is the most straightforward. However, this is only possible when the jBPM console or KIE Workbench instance is running on Wildfly or JBoss AS, or JBoss EAP.

The following table describes the parameters used when using an InitialContext to configure a RemoteJmsRuntimeEngineFactory instance:


Configuration using an InitialContext instance

When configuring the RemoteJmsRuntimeEngineFactory with an InitialContext instance as a parameter, it's necessary to retrieve the (remote) InitialContext instance first from the remote server. The following code illustrates how to do this.

public void startProcessAndTaskViaJmsRemoteJavaAPI(String serverHostName, String deploymentId, String user, String password) {

  // Setup remote JMS runtime engine factory
  InitialContext remoteInitialContext 
    = getRemoteInitialContext(serverHostName, user, password);
  int maxTimeoutSecs = 5;
  RemoteJmsRuntimeEngineFactory remoteJmsFactory 
    = new RemoteJmsRuntimeEngineFactory(deploymentId, remoteInitialContext, user, password, maxTimeoutSecs);
  // Interface with JMS api
  RuntimeEngine engine = remoteJmsFactory.newRuntimeEngine();
  KieSession ksession = engine.getKieSession();
  ProcessInstance processInstance = ksession.startProcess("com.burns.reactor.maintenance.cycle");
  long procId = processInstance.getId();
  TaskService taskService = engine.getTaskService();
  List<Long> tasks = taskService.getTasksByProcessInstanceId(procId);
  taskService.start(tasks.get(0), user);
}
private static InitialContext getRemoteInitialContext(String jbossServerHostName, String user, String password) { 
  // Configure the (JBoss AS 7/EAP 6) InitialContextFactory
  Properties initialProps = new Properties();
  initialProps.setProperty(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
  initialProps.setProperty(InitialContext.PROVIDER_URL, "remote://"+ jbossServerHostName + ":4447");
  initialProps.setProperty(InitialContext.SECURITY_PRINCIPAL, user);
  initialProps.setProperty(InitialContext.SECURITY_CREDENTIALS, password);
  for (Object keyObj : initialProps.keySet()) {
    String key = (String) keyObj;
    System.setProperty(key, (String) initialProps.get(key));
  }
  
  // Create the remote InitialContext instance
  try {
    return new InitialContext(initialProps);
  } catch (NamingException e) {
    throw new RuntimeException("Unable to create " + InitialContext.class.getSimpleName(), e);
  }
}

The following table describes the parameters used when using an InitialContext to configure a RemoteJmsRuntimeEngineFactory instance:


Configuration using ConnectionFactory and Queue instance parameters

Some users may have direct access to a ConnectionFactory and the Queue instances needed to interact with the JMS API. In this case, they can use the RemoteJmsRuntimeEngineFactory constructor that uses the following arguments:


As mentioned above, the Remote Java API provides client-like instances of the RuntimeEngine, KieSession, TaskService and AuditLogService 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 with

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

For information, see the Deployment calls section.

[POST] /runtime/{deploymentId}/process/{processDefId}/start
[POST] /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}/variables
[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

Rest calls that contain "rest/runtime/{deploymentId}/history" have been deprecated: the same functionality provided by these calls can be found in the history REST calls described in the previous sections.

[POST] /runtime/{deploymentId}/history/clear
[GET] /runtime/{deploymentId}/history/instances
[GET] /runtime/{deploymentId}/history/instance/{procInstId}
[GET] /runtime/{deploymentId}/history/instance/{procInstId}/child
[GET] /runtime/{deploymentId}/history/instance/{procInstId}/node
[GET] /runtime/{deploymentId}/history/instance/{procInstId}/variable
[GET] /runtime/{deploymentId}/history/instance/{procInstId}/node/{nodeId}
[GET] /runtime/{deploymentId}/history/instance/{procInstId}/variable/{varId}
[GET] /runtime/{deploymentId}/history/process/{processDefId}
[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 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, for the 6.0.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:

[POST] /task/{taskId}/activate
[POST] /task/{taskId}/claim
[POST] /task/{taskId}/claimnextavailable
[POST] /task/{taskId}/complete
[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
[POST] /task/{taskId}/suspend
[GET] /task/{taskId}/showTaskForm
[GET] /task/query

The /task/query operation queries all non-archived tasks based on the parameters given.

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

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

This regular expression is explained as follows:

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

Runtime commands
AbortWorkItemCommandGetProcessInstancesCommandGetGlobalCommand
CompleteWorkItemCommandSetProcessInstanceVariablesCommandGetIdCommand
GetWorkItemCommandSignalEventCommandSetGlobalCommand
AbortProcessInstanceCommandStartCorrelatedProcessCommandDeleteCommand
GetProcessIdsCommandStartProcessCommandFireAllRulesCommand
GetProcessInstanceByCorrelationKeyCommandGetVariableCommandInsertObjectCommand
GetProcessInstanceCommandGetFactCountCommandUpdateCommand
Task commands
ActivateTaskCommandFailTaskCommandGetTasksOwnedCommand
AddTaskCommandForwardTaskCommandNominateTaskCommand
CancelDeadlineCommandGetAttachmentCommandProcessSubTaskCommand
ClaimNextAvailableTaskCommandGetContentCommandReleaseTaskCommand
ClaimTaskCommandGetTaskAssignedAsBusinessAdminCommandResumeTaskCommand
CompleteTaskCommandGetTaskAssignedAsPotentialOwnerCommandSkipTaskCommand
CompositeCommandGetTaskByWorkItemIdCommandStartTaskCommand
DelegateTaskCommandGetTaskCommandStopTaskCommand
ExecuteTaskRulesCommandGetTasksByProcessInstanceIdCommandSuspendTaskCommand
ExitTaskCommandGetTasksByStatusByProcessInstanceIdCommand 
Task commands
ClearHistoryLogsCommandFindProcessInstanceCommandFindSubProcessInstancesCommand
FindActiveProcessInstancesCommandFindProcessInstancesCommandFindVariableInstancesByNameCommand
FindNodeInstancesCommandFindSubProcessInstancesCommandFindVariableInstancesCommand

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

All REST calls, unless otherwise specified, will 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 request or JMS message. In order to do this, there are a number of requirements.

  1. The user-defined class satisfy the following in order to be property serialized and deserialized by the JMS API:

  2. The class definition must be included in the deployment jar of the deployment that the JMS message content is meant for.

  3. The sender must set a “deploymentId” string property on the JMS bytes message to the name of the deploymentId. This property is necessary in order to be able to load the proper classes from the deployment itself before deserializing the message on the server side.

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.


The URL templates in the table below are relative the following URL:

Table 17.18. runtime REST calls

URL TemplateTypeDescription
/runtime/{deploymentId}/process/{procDefID}/startPOSTstart a process instance based on the Process definition (accepts query map parameters)
/runtime/{deploymentId}/process/{procDefID}/startformPOSTreturns a valid URL to the start process form to be shown on a client aplication.
/runtime/{deploymentId}/process/instance/{procInstanceID}GETreturn a process instance details
/runtime/{deploymentId}/process/instance/{procInstanceID}/abortPOSTabort the process instance
/runtime/{deploymentId}/process/instance/{procInstanceID}/signalPOSTsend a signal event to process instance (accepts query map parameters)
/runtime/{deploymentId}/process/instance/{procInstanceID}/variable/{varId}GETreturn a variable from a process instance
/runtime/{deploymentId}/signal/{signalCode}POSTsend a signal event to deployment
/runtime/{deploymentId}/workitem/{workItemID}/completePOSTcomplete a work item (accepts query map parameters)
/runtime/{deploymentId}/workitem/{workItemID}/abortPOSTabort a work item
/runtime/{deploymentId}/withvars/process/{procDefinitionID}/startPOST

start a process instance and return the process instance with its variables

Note that even if a passed variable is not defined in the underlying process definition, it is created and initialized with the passed value.

/runtime/{deploymentId}/withvars/process/instance/{procInstanceID}/GET

return a process instance with its variables

/runtime/{deploymentId}/withvars/process/instance/{procInstanceID}/signalPOST

send a signal event to the process instance (accepts query map parameters)

The following query parameters are accepted:

  • The signal parameter specifies the name of the signal to be sent

  • The event parameter specifies the (optional) value of the signal to be sent



Table 17.20. history REST calls

URL TemplateTypeDescription
/history/clear/POSTdelete all process, node and history records
/history/instancesGETreturn the list of all process instance history records
/history/instance/{procInstId}GETreturn a list of process instance history records for a process instance
/history/instance/{procInstId}/childGETreturn a list of process instance history records for the subprocesses of the process instance
/history/instance/{procInstId}/nodeGETreturn a list of node history records for a process instance
/history/instance/{procInstId}/node/{nodeId}GETreturn a list of node history records for a node in a process instance
/history/instance/{procInstId}/variableGETreturn a list of variable history records for a process instance
/history/instance/{procInstId}/variable/{variableId}GETreturn a list of variable history records for a variable in a process instance
/history/process/{procDefId}GETreturn a list of process instance history records for process instances using a given process definition
/history/variable/{varId}GETreturn a list of variable history records for a variable
/history/variable/{varId}/instancesGETreturn a list of process instance history records for process instances that contain a variable with the given variable id
/history/variable/{varId}/value/{value}GETreturn a list of variable history records for variable(s) with the given variable id and given value
/history/variable/{varId}/value/{value}/instancesGETreturn a list of process instance history records for process instances with the specified variable that contains the specified variable value


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.

// normal java imports skipped


import org.drools.core.command.runtime.process.StartProcessCommand;
import org.jbpm.services.task.commands.GetTaskAssignedAsPotentialOwnerCommand;
import org.kie.api.command.Command;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.api.task.model.TaskSummary;
(1)import org.kie.services.client.api.command.exception.RemoteCommunicationException;
import org.kie.services.client.serialization.JaxbSerializationProvider;
import org.kie.services.client.serialization.SerializationConstants;
import org.kie.services.client.serialization.SerializationException;
import org.kie.services.client.serialization.jaxb.impl.JaxbCommandResponse;
import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsRequest;
import org.kie.services.client.serialization.jaxb.impl.JaxbCommandsResponse;
import org.kie.services.client.serialization.jaxb.rest.JaxbExceptionResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DocumentationJmsExamples {
  protected static final Logger logger = LoggerFactory.getLogger(DocumentationJmsExamples.class);
  public void sendAndReceiveJmsMessage() {
    String USER = "charlie";
    String PASSWORD = "ch0c0licious";
    String DEPLOYMENT_ID = "test-project";
    String PROCESS_ID_1 = "oompa-processing";
    URL serverUrl;
    try {
      serverUrl = new URL("http://localhost:8080/jbpm-console/");
    } catch (MalformedURLException murle) {
      logger.error("Malformed URL for the server instance!", murle); 
      return;
    }
    // Create JaxbCommandsRequest instance and add commands
    Command<?> cmd = new StartProcessCommand(PROCESS_ID_1);
(5)    int oompaProcessingResultIndex = 0;
(2)    JaxbCommandsRequest req = new JaxbCommandsRequest(DEPLOYMENT_ID, cmd);
    req.getCommands().add(new GetTaskAssignedAsPotentialOwnerCommand(USER, "en-UK"));
(5)    int loompaMonitoringResultIndex = 1;
    // Get JNDI context from server
    InitialContext context = getRemoteJbossInitialContext(serverUrl, USER, PASSWORD);
      
    // 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 sendQueue, responseQueue;
    try {
      sendQueue = (Queue) context.lookup("jms/queue/KIE.SESSION");
      responseQueue = (Queue) context.lookup("jms/queue/KIE.RESPONSE");
    } catch (NamingException ne) {
      throw new RuntimeException("Unable to lookup send or response queue", ne);
    }
    // Send command request
    Long processInstanceId = null; // needed if you're doing an operation on a PER_PROCESS_INSTANCE deployment
    String humanTaskUser = USER;
    JaxbCommandsResponse cmdResponse = sendJmsCommands(
        DEPLOYMENT_ID, processInstanceId, humanTaskUser, req, 
        connectionFactory, sendQueue, responseQueue, 
        USER, PASSWORD, 5);
    // Retrieve results
    ProcessInstance oompaProcInst = null;
    List<TaskSummary> charliesTasks = null;
(6)    for (JaxbCommandResponse<?> response : cmdResponse.getResponses()) {
      if (response instanceof JaxbExceptionResponse) {
        // something went wrong on the server side
        JaxbExceptionResponse exceptionResponse = (JaxbExceptionResponse) response;
        throw new RuntimeException(exceptionResponse.getMessage());
      }
(5)      if (response.getIndex() == oompaProcessingResultIndex) {
(6)        oompaProcInst = (ProcessInstance) response.getResult();
(5)      } else if (response.getIndex() == loompaMonitoringResultIndex) {
(6)        charliesTasks = (List<TaskSummary>) response.getResult();
      }
    }
  }
  private JaxbCommandsResponse sendJmsCommands(String deploymentId, Long processInstanceId, String user, JaxbCommandsRequest req,
      ConnectionFactory factory, Queue sendQueue, Queue responseQueue, String jmsUser, String jmsPassword, int timeout) {
    req.setProcessInstanceId(processInstanceId);
    req.setUser(user);
    Connection connection = null;
    Session session = null;
    String corrId = UUID.randomUUID().toString();
    String selector = "JMSCorrelationID = '" + corrId + "'";
    JaxbCommandsResponse cmdResponses = null;
    try {
      // setup
      MessageProducer producer;
      MessageConsumer consumer;
      try {
        if (jmsPassword != null) {
          connection = factory.createConnection(jmsUser, jmsPassword);
        } 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);
      }
      JaxbSerializationProvider serializationProvider = new JaxbSerializationProvider();
      // if necessary, add user-created classes here:
      // xmlSerializer.addJaxbClasses(MyType.class, AnotherJaxbAnnotatedType.class);
      // Create msg
      BytesMessage msg;
      try {
(3)        msg = session.createBytesMessage();
        // set properties
(3)        msg.setJMSCorrelationID(corrId);
(3)        msg.setIntProperty(SerializationConstants.SERIALIZATION_TYPE_PROPERTY_NAME, JaxbSerializationProvider.JMS_SERIALIZATION_TYPE);
        Collection<Class<?>> extraJaxbClasses = serializationProvider.getExtraJaxbClasses();
        if (!extraJaxbClasses.isEmpty()) {
          String extraJaxbClassesPropertyValue = JaxbSerializationProvider
              .classSetToCommaSeperatedString(extraJaxbClasses);
          msg.setStringProperty(SerializationConstants.EXTRA_JAXB_CLASSES_PROPERTY_NAME, extraJaxbClassesPropertyValue);
          msg.setStringProperty(SerializationConstants.DEPLOYMENT_ID_PROPERTY_NAME, deploymentId);
        }
        // serialize request
(3)        String xmlStr = serializationProvider.serialize(req);
        msg.writeUTF(xmlStr);
      } 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(msg);
      } catch (JMSException jmse) {
        throw new RemoteCommunicationException("Unable to send a JMS message.", jmse);
      }
      // receive
      Message response;
      try {
        response = consumer.receive(timeout);
      } catch (JMSException jmse) {
        throw new RemoteCommunicationException("Unable to receive or retrieve the JMS response.", jmse);
      }
      if (response == null) {
        logger.warn("Response is empty, leaving");
        return null;
      }
      // extract response
      assert response != null : "Response is empty.";
      try {
        String xmlStr = ((BytesMessage) response).readUTF();
(4)        cmdResponses = (JaxbCommandsResponse) serializationProvider.deserialize(xmlStr);
      } 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 cmdResponses != null : "Jaxb Cmd Response was null!";
    } finally {
      if (connection != null) {
        try {
          connection.close();
          session.close();
        } catch (JMSException jmse) {
          logger.warn("Unable to close connection or session!", jmse);
        }
      }
    }
    return cmdResponses;
  }
  private InitialContext getRemoteJbossInitialContext(URL url, String user, String password) { 
    Properties initialProps = new Properties();
    initialProps.setProperty(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
    String jbossServerHostName = url.getHost();
    initialProps.setProperty(InitialContext.PROVIDER_URL, "remote://"+ jbossServerHostName + ":4447");
    initialProps.setProperty(InitialContext.SECURITY_PRINCIPAL, user);
    initialProps.setProperty(InitialContext.SECURITY_CREDENTIALS, password);
    for (Object keyObj : initialProps.keySet()) {
      String key = (String) keyObj;
      System.setProperty(key, (String) initialProps.get(key));
    }
    try {
      return new InitialContext(initialProps);
    } catch (NamingException e) {
      throw new RemoteCommunicationException("Unable to create " + InitialContext.class.getSimpleName(), e);
    }
  }
}

1

These classes can all be found in the kie-services-client and the kie-services-jaxb JAR.

2

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.

3

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

  • It must be a JMS byte message.

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

  • It must contain a serialized instance of a JaxbCommandsRequest, added to the message as a UTF string

4

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

5

In order to match the response to a command, to the initial command, use the index field of the returned JaxbCommandResponse instances. This index field will match the index of the initial command. Because not all commands will return a result, it's possible to send 3 commands with a command request message, and then receive a command response message that only includes one JaxbCommandResponse message with an index value of 1. That 1 then identifies it as the response to the second command.

6

Since many of the results returned by various commands are not serializable, the jBPM JMS Remote API converts these results into JAXB equivalents, all of which implement the JaxbCommandResponse interface. The JaxbCommandResponse.getResult() method then returns the JAXB equivalent to the actual result, which will conform to the interface of the result.

For example, in the code above, the StartProcessCommand returns a ProcessInstance. In order to return this object to the requester, the ProcessInstance is converted to a JaxbProcessInstanceResponse and then added as a JaxbCommandResponse to the command response message. The same applies to the List<TaskSummary> that's returned by the GetTaskAssignedAsPotentialOwnerCommand.

However, not all methods that can be called on a normal ProcessInstance can be called on the JaxbProcessInstanceResponse because the JaxbProcessInstanceResponse is simply a representation of a ProcessInstance object. This applies to various other command response as well. In particular, methods which require an active (backing) KieSession, such as ProcessInstance.getProess() or ProcessInstance.signalEvent(String type, Object event) will throw an UnsupportedOperationException.