Chapter 19. Intelligent Process Server Java Client API Overview
19.1. Client Configuration
You need to declare a configuration object and set server communication aspects, such as the protocol (REST or JMS), credentials and the payload format (XStream, JAXB or JSON). For additional example, follow the Hello World project.
Client Configuration
import org.kie.server.api.marshalling.MarshallingFormat; import org.kie.server.client.KieServicesClient; import org.kie.server.client.KieServicesConfiguration; import org.kie.server.client.KieServicesFactory; public class DecisionServerTest { private static final String URL = "http://localhost:8080/kie-server/services/rest/server"; private static final String USER = "kieserver"; private static final String PASSWORD = "kieserver1!"; private static final MarshallingFormat FORMAT = MarshallingFormat.JSON; private KieServicesConfiguration conf; private KieServicesClient kieServicesClient; @Before public void initialize() { conf = KieServicesFactory.newRestConfiguration(URL, USER, PASSWORD); //If you use custom classes, such as Obj.class, add them to the configuration Set<Class<?>> extraClassList = new HashSet<Class<?>>(); extraClassList.add(Obj.class); conf.addExtraClasses(extraClassList); conf.setMarshallingFormat(FORMAT); kieServicesClient = KieServicesFactory.newKieServicesClient(conf); } }
JMS Client Configuration
import java.util.Properties; import javax.jms.ConnectionFactory; import javax.jms.Queue; import javax.naming.Context; import javax.naming.InitialContext; import org.junit.Test; import org.kie.server.client.KieServicesClient; import org.kie.server.client.KieServicesConfiguration; import org.kie.server.client.KieServicesFactory; public class DecisionServerTest { private static final String REMOTING_URL = new String("remote://localhost:4447"); private static final String USER = "kieserver"; private static final String PASSWORD = "kieserver1!"; private static final String INITIAL_CONTEXT_FACTORY = new String("org.jboss.naming.remote.client.InitialContextFactory"); private static final String CONNECTION_FACTORY = new String("jms/RemoteConnectionFactory"); private static final String REQUEST_QUEUE_JNDI = new String("jms/queue/KIE.SERVER.REQUEST"); private static final String RESPONSE_QUEUE_JNDI = new String("jms/queue/KIE.SERVER.RESPONSE"); private KieServicesConfiguration conf; private KieServicesClient kieServicesClient; @Test public void testJms() throws Exception { final Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY); env.put(Context.PROVIDER_URL, REMOTING_URL); env.put(Context.SECURITY_PRINCIPAL, USER); env.put(Context.SECURITY_CREDENTIALS, PASSWORD); InitialContext context = new InitialContext(env); Queue requestQueue = (Queue) context.lookup(REQUEST_QUEUE_JNDI); Queue responseQueue = (Queue) context.lookup(RESPONSE_QUEUE_JNDI); ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup(CONNECTION_FACTORY); conf = KieServicesFactory.newJMSConfiguration(connectionFactory, requestQueue, responseQueue, USER, PASSWORD); //If you use custom classes, such as Obj.class, add them to the configuration Set<Class<?>> extraClassList = new HashSet<Class<?>>(); extraClassList.add(Obj.class); conf.addExtraClasses(extraClassList); kieServicesClient = KieServicesFactory.newKieServicesClient(conf); } }
Note that you must assign the the guest
role to the user kieserver
. Additionally, you must declare JMS dependency:
<dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-jms-client-bom</artifactId> <version>7.5.7.Final-redhat-3</version> <type>pom</type> </dependency>
19.1.1. JMS Interaction Patterns
Since version 6.4 of Red Hat JBoss BPM Suite, Intelligent Process Server Client integration with JMS has been enhanced by several interaction patterns. Available interaction patterns are:
- Request reply: the default option that blocks the client until a response is received, making the JMS integration synchronous. Request reply is not suitable for a JMS transactional delivery.
- Fire and forget: an option for one-way integration. Suitable, for example, for notifications invoked by integration with the Intelligent Process Server. Fire and forget is convenient for a transactional JMS delivery. Messages are delivered to the server only if the transaction that invoked the server client was committed successfully.
- Asynchronous with callback: with this option, the client is not blocked after sending a message to Intelligent Process Server. Responses can be received asynchronously. This option can be used for the transactional JMS delivery.
You can set the response handlers either globally (when a KieServicesConfiguration
is created) or individually on different client instances (such as RuleServiceClient
, ProcessServicesClient
, and others) during runtime.
Whereas fire and forget and request reply patterns do not require any additional configuration, you need to configure the callback if you use the asynchronous pattern. The Intelligent Process Server client includes a built-in callback (BlockingResponseCallback
) that provides support using a blocking queue. The callback is configured to receive a single message at a time by default. Therefore, each client interaction contains a single message (request) and a single response. You can change the size of the queue to make it possible to receive multiple messages.
To create a custom callback, implement the org.kie.server.client.jms.ResponseCallback
interface.
Intelligent Process Server client is not thread-safe when switching response handlers. Change of a handler can affect all the threads which are using the same client instance. It is recommended to use separate client instances in case of dynamic changes of the handler. You can maintain a set of clients where each client uses a dedicated response handler. Depending on which handler is required, choose a respective client.
For example, having two clients, the first client (with the fire and forget pattern) can be used for starting processes and the second client (with the request reply pattern) can be used for querying user tasks.
Example 19.1. Global JMS Configuration
InitialContext context = ...; Queue requestQueue = (Queue) context.lookup("jms/queue/KIE.SERVER.REQUEST"); Queue responseQueue = (Queue) context.lookup("jms/queue/KIE.SERVER.RESPONSE"); ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory"); KieServicesConfiguration jmsConfiguration = KieServicesFactory.newJMSConfiguration(connectionFactory, requestQueue, responseQueue, "user", "password"); // Set your response handler globally here. jmsConfiguration.setResponseHandler(new FireAndForgetResponseHandler());
Example 19.2. Per Client JMS Configuration
ProcessServiceClient processClient = client.getServicesClient(ProcessServicesClient.class); // Change response handler for processClient. The other clients are not affected. processClient.setResponseHandler(new FireAndForgetResponseHandler());
In case you are using asynchronous or fire and forget response handlers, you can turn on JMS transactions in KieServicesConfiguration
. If you do so, use a transaction-aware connection factory: XAConnectionFactory
.
JMS transactions are supported only on Red Hat JBoss Enterprise Application Platform. JMS transactions are not tested on Oracle WebLogic Server and IBM WebSphere Application Server.
19.2. Server Response
Service responses are represented by the org.kie.server.api.model.ServiceResponse<T>
object, where T
represents the payload type. It has the following attributes:
-
String message
: returns the response message. -
ResponseType type
: returns eitherSUCCESS
orFAILURE
. -
T result
: returns the requested object.
Example 19.3. Hello World Server Response
import org.kie.server.api.model.ServiceResponse; import org.kie.server.client.RuleServicesClient; RuleServicesClient ruleClient = client.getServicesClient(RuleServicesClient.class); ServiceResponse<ExecutionResults> response = ruleClient.executeCommandsWithResults(container, batchCommand); // Retrieve result with identifier output-object Object result = response.getResult().getValue("output-object");
A service response is retrieved only if you are using the request reply response handler. In case of asynchronous or fire and forget response handlers, all remote calls always return null
.
19.3. Inserting and Executing Commands
To insert commands, use the org.kie.api.command.KieCommands
class. To instantiate the KieCommands
class, use org.kie.api.KieServices.get().getCommands()
. If you want to add multiple commands, use the BatchExecutionCommand
wrapper.
Example 19.4. Inserting and Executing Commands
import org.kie.api.command.Command; import org.kie.api.command.KieCommands; import org.kie.server.api.model.ServiceResponse; import org.kie.server.client.RuleServicesClient; import org.kie.server.client.KieServicesClient; import org.kie.api.KieServices; import java.util.Arrays; ... public void executeCommands() { String containerId = "hello"; System.out.println("== Sending commands to the server =="); RuleServicesClient rulesClient = kieServicesClient.getServicesClient(RuleServicesClient.class); KieCommands commandsFactory = KieServices.Factory.get().getCommands(); Command<?> insert = commandsFactory.newInsert("Some String OBJ"); Command<?> fireAllRules = commandsFactory.newFireAllRules(); Command<?> batchCommand = commandsFactory.newBatchExecution(Arrays.asList(insert, fireAllRules)); ServiceResponse<ExecutionResults> executeResponse = rulesClient.executeCommandsWithResults(containerId, batchCommand); if(executeResponse.getType() == ResponseType.SUCCESS) { System.out.println("Commands executed with success!"); // Retrieve result with identifier output-object Object result = executeResponse.getResult().getValue("output-object"); } else { System.out.println("Error executing rules. Message: "); System.out.println(executeResponse.getMsg()); } }
Add the org.drools:drools-compiler
dependency into your pom.xml
file. See the Supported Components Versions section of Red Hat JBoss BPM Suite Installation Guide to add a correct version.
<dependency> <groupId>org.drools-redhat</groupId> <artifactId>drools-compiler</artifactId> <version>6.5.0.Final-redhat-2</version> </dependency>
See Embedded jBPM Engine Dependencies for a list of further Maven dependencies.
19.4. Listing Server Capabilities
From version 6.2, Intelligent Process Server supports the business process execution. To find out server capabilities, use the org.kie.server.api.model.KieServerInfo
object.
KieServicesClient
requires the server capability information to correctly produce service clients (see Section 19.7, “Available Intelligent Process Server Clients”). You can specify the capabilities globally in KieServicesConfiguration
, otherwise they are automatically retrieved from the server.
Regardless of which response handler is globally specified, KieServicesClient
uses synchronous request response handler to retrieve the server capabilities. However, you cannot make synchronous calls when JMS transactions are enabled. To do so, you need to set the server capabilities in KieServicesConfiguration
.
Example 19.5. Server Capabilities
public void listCapabilities() { KieServerInfo serverInfo = kieServicesClient.getServerInfo().getResult(); System.out.print("Server capabilities:"); for (String capability : serverInfo.getCapabilities()) { System.out.print(" " + capability); } System.out.println(); }
19.5. Listing Containers
Containers are represented by the org.kie.server.api.model.KieContainerResource
object. The list of resources is represented by the org.kie.server.api.model.KieContainerResourceList
object.
Example 19.6. Print a List of Containers
public void listContainers() { KieContainerResourceList containersList = kieServicesClient.listContainers().getResult(); List<KieContainerResource> kieContainers = containersList.getContainers(); System.out.println("Available containers: "); for (KieContainerResource container : kieContainers) { System.out.println("\t" + container.getContainerId() + " (" + container.getReleaseId() + ")"); } }
When obtaining the list of containers, you can optionally filter the result using an instance of the org.kie.server.api.model.KieContainerResourceFilter
class, which is passed to the org.kie.server.client.KieServicesClient.listContainers()
method.
Example 19.7. Filter Containers in Java Client API
public void listContainersWithFilter() { // The following filter will match only containers with the ReleaseId // "org.example:container:1.0.0.Final" and status FAILED KieContainerResourceFilter filter = new KieContainerResourceFilter.Builder() .releaseId("org.example", "container", "1.0.0.Final") .status(KieContainerStatus.FAILED) .build(); // using previously created KieServicesClient.... KieContainerResourceList containersList = kieServicesClient.listContainers(filter).getResult(); List<KieContainerResource> kieContainers = containersList.getContainers(); System.out.println("Available containers: "); for (KieContainerResource container : kieContainers) { System.out.println("\t" + container.getContainerId() + " (" + container.getReleaseId() + ")"); } }
19.6. Handling Containers
You can use the Intelligent Process Server Java client to create and dispose containers. If you dispose a container, ServiceResponse
will be returned with void
payload. If you create a container, KieContainerResource
object will be returned.
Disposing and Creating a Container
public void disposeAndCreateContainer() { System.out.println("== Disposing and creating containers =="); List<KieContainerResource> kieContainers = kieServicesClient.listContainers().getResult().getContainers(); if (kieContainers.size() == 0) { System.out.println("No containers available..."); return; } KieContainerResource container = kieContainers.get(0); String containerId = container.getContainerId(); ServiceResponse<Void> responseDispose = kieServicesClient.disposeContainer(containerId); if (responseDispose.getType() == ResponseType.FAILURE) { System.out.println("Error disposing " + containerId + ". Message: "); System.out.println(responseDispose.getMsg()); return; } System.out.println("Success Disposing container " + containerId); System.out.println("Trying to recreate the container..."); ServiceResponse<KieContainerResource> createResponse = kieServicesClient.createContainer(containerId, container); if(createResponse.getType() == ResponseType.FAILURE) { System.out.println("Error creating " + containerId + ". Message: "); System.out.println(responseDispose.getMsg()); return; } System.out.println("Container recreated with success!"); }
A conversation between a client and a specific Intelligent Process Server container in a clustered environment is secured by a unique conversationID
. The conversationID
is transferred using the X-KIE-ConversationId
REST header. If you update the container, unset the previous conversationID
. Use KieServiesClient.completeConversation()
to unset the conversationID
for Java API.
19.7. Available Intelligent Process Server Clients
KieServicesClient
serves also as an entry point for other clients with the ability to perform various operations, such as Red Hat JBoss BRMS commands and manage processes. Following services are available in the org.kie.server.client
package:
- JobServicesClient is used to schedule, cancel, requeue, and get job requests.
- ProcessServicesClient is used to start, signal, and abort processes or work items.
- QueryServicesClient is used to query processes, process nodes, and process variables.
- RuleServicesClient is used to send commands to the server to perform rule-related operations (for example insert objects into the working memory, fire rules, …).
The org.kie.server.client.RuleServicesClient.executeCommands()
API call was deprecated in version 6.3. The new org.kie.server.client.RuleServicesClient.executeCommandsWithResults()
API returns execution results for objects that have been unmarshalled.
- UserTaskServicesClient is used to perform all user-task operations (start, claim, cancel a task) and query tasks by specified field (process instances id, user, …)
- UIServicesClient is used to get String representation of forms (XML or JSON) and of the process image (SVG).
- SolverServicesClient is used to perform all Business Resource Planner operations, such as getting the solver state and the best solution, or disposing of a solver.
The getServicesClient
method provides access to any of these clients:
RuleServicesClient rulesClient = kieServicesClient.getServicesClient(RuleServicesClient.class);
19.8. Listing Available Business Processes
Use QueryClient
to list available process definitions. QueryClient
methods use pagination, therefore in addition to the query you make, you must provide the current page and the number of results per page. In the provided example, the query starts on page 0 and lists the first 1000 results.
List Processes
public void listProcesses() { System.out.println("== Listing Business Processes =="); QueryServicesClient queryClient = kieServicesClient.getServicesClient(QueryServicesClient.class); List<ProcessDefinition> findProcessesByContainerId = queryClient.findProcessesByContainerId("rewards", 0, 1000); for (ProcessDefinition def : findProcessesByContainerId) { System.out.println(def.getName() + " - " + def.getId() + " v" + def.getVersion()); } }
19.9. Starting a Business Processes
Use the ProcessServicesClient
client to start your process. Ensure that any custom classes you require for your process are added into the KieServicesConfiguration
object, using the addExtraClasses()
method. To start a process using the Java Client API, see the following example:
import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import org.kie.server.api.marshalling.MarshallingFormat; import org.kie.server.client.KieServicesClient; import org.kie.server.client.KieServicesConfiguration; import org.kie.server.client.KieServicesFactory; import org.kie.server.client.ProcessServicesClient; ... public static void startProcess() { //Client configuration setup KieServicesConfiguration config = KieServicesFactory.newRestConfiguration(SERVER_URL, LOGIN, PASSWORD); //Add custom classes, such as Obj.class, to the configuration Set<Class<?>> extraClassList = new HashSet<Class<?>>(); extraClassList.add(Obj.class); config.addExtraClasses(extraClassList); config.setMarshallingFormat(MarshallingFormat.JSON); // ProcessServicesClient setup KieServicesClient client = KieServicesFactory.newKieServicesClient(config); ProcessServicesClient processServicesClient = client.getServicesClient(ProcessServicesClient.class); // Create an instance of the custom class Obj obj = new Obj(); obj.setOk("ok"); Map<String, Object> variables = new HashMap<String, Object>(); variables.put("test", obj); // Start the process with custom class processServicesClient.startProcess(CONTAINER, processId, variables); }
19.10. QueryDefinition for Intelligent Process Server Using Java Client API
QueryDefinition
is a feature used to execute advanced queries. For more information about advanced queries, see Section 16.7, “Advanced Queries for the Intelligent Process Server”. To register and execute query definitions using the Java Client API, see the following example:
Registering and Executing Query Definitions with QueryServicesClient
// client setup KieServicesConfiguration conf = KieServicesFactory.newRestConfiguration(SERVER_URL, LOGIN, PASSWORD); KieServicesClient client = KieServicesFactory.newKieServicesClient(conf); // get the query services client QueryServicesClient queryClient = client.getServicesClient(QueryServicesClient.class); // building the query QueryDefinition queryDefinition = QueryDefinition.builder().name(QUERY_NAME) .expression("select * from Task t") .source("java:jboss/datasources/ExampleDS") .target("TASK").build(); // two queries cannot have the same name queryClient.unregisterQuery(QUERY_NAME); // register the query queryClient.registerQuery(queryDefinition); // execute the query with parameters: query name, mapping type (to map the fields to an object), page number, page size and return type List<TaskInstance> query = queryClient.query(QUERY_NAME, QueryServicesClient.QUERY_MAP_TASK, 0, 100, TaskInstance.class); // read the result for (TaskInstance taskInstance : query) { System.out.println(taskInstance); }
Note that target
instructs QueryService
to apply default filters. Alternatively, you can set filter parameters manually. Target
has the following values:
public enum Target { PROCESS, TASK, BA_TASK, PO_TASK, JOBS, CUSTOM; }