There are many place where you can place MVEL expressions. For example in human task subject field:
#{variable}
kcontext
is available within the Script Task scope, it's an instance of ProcessContext
See: ProcessContext.java
Set a variable:
kcontext.setVariable("customer", customer);
Get variable:
result = (Boolean) kcontext.getVariable("result");
System.out.println(">>> exit: " + kcontext.getNodeInstance().getNodeName());
The task assignments are able to perform variable mapping using the MVEL notation.
E.g.:
The following interface provides the full access to the variable:
org.kie.api.runtime.process.WorkflowProcessInstance
This is a code sample:
WorkflowProcessInstance processInstance = (WorkflowProcessInstance) runtimeEngine.getKieSession().getProcessInstance(processInstanceId);
processInstance.getVariable(name);
processInstance.setVariable(name, value);
The file package-names-white-list
can be used to declare visible packages.
When you declare a package others become invisible to the BC.
The use of this file allows a developer to narrow down the group of facts that are loaded and are therefore, visible. This helps in speeding up the loading of these facts while creating new rules. This file is created automatically on the creation of a new project in the root directory, along with the pom.xml and project.imports project files. For existing projects, you may create this file manually.
In order to understand the process actual logic at runtime, you can leverage the event listeners. There is an already good implementation really useful for debugging purposes.
-
Open the deployment descriptors and add an event listener entry:
- Identifier:
org.drools.core.event.DebugProcessEventListener
- Resolver:
reflection
- Parameters: none
- Identifier:
Otherwise, if you prefer have a proper debug session with your favourite java IDE
-
Start the server in debug mode:
./standalone.sh --debug
-
attach the IDE to the remote debugging port 8787.
-
place your breakpoints and run the process
You can debug the process placing breakpoints on the event listeners. Another (simpler) option is to leverage a simple utility class, e.g.:
public static void debug(ProcessContext kcontext) {
System.out.println("Util.debug()");
}
The designer place the method call in the "on entry" script of the task that he want to analyse (or "on exit" one).
The designer sets a breakpoint in that method and when the method is reached he can inspect and the kcontext
information.
The latter technique has the drawback of being quite intrusive, because it requires an update of the process model.
Warning: the process runs in a transaction context with a default time out of 120 seconds, so if the debugging activities last more than that, you should incur in a runtime exception.
To configure JVM remote debugging on a JBoss container, add the following configuration to the JAVA_OPTS_APPEND environment variable of the container:
-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n
CRC snippet:
spec:
environment: rhpam-authoring
objects:
servers:
- jvm:
javaOptsAppend: -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n
Retrieve the list of running pods:
oc get pods
Port-forward the a local port to the debugging port of the container, e.g.:
oc port-forward ${pod-name} 8787:8787
LocalKieContainerMain.class.getClassLoader()
In order to serialize LocalDateTime you can configure the related property in this way:
@JsonSerialize(using=LocalDateTimeSerializer.class)
@JsonDeserialize(using=LocalDateTimeDeserializer.class)
@JsonFormat(shape = Shape.STRING)
private java.time.LocalDateTime startTime;
In order to debug the java code in the snippet you need to access to the generated code.
There is a system property that force the runtime to dump the code:
-Ddrools.dump.dir=some/root/dir
Constructor
XStream xStream = new XStream();
xStream.fromXML(xml, this);
String
public String toString() {
XStream xStream = new XStream();
return xStream.toXML(this);
}
<properties>
<version.bpms>6.4.0.Final-redhat-6</version.bpms>
</properties>
(...)
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-services-ejb-client</artifactId>
<version>${bpm.version}</version>
<scope>runtime</scope>
</dependency>
To start a process from within a process instance you can use code like this (not sure you can use this from within a rule, never tried this):
RuntimeManager rm =
RuntimeManagerRegistry.get().getManager(deploymentId);
RuntimeEngine engine = rm.getRuntimeEngine(EmptyContext.get());
KieSession ksession = engine.getKieSession();
try {
ksession.startProcess(processId, paramMap);
} finally {
rm.disposeRuntimeEngine(engine);
}
Inside a kieserver:
KieServerImpl kieServer = KieServerLocator.getInstance();
KieServerExtension ext = ((KieServerImpl)kieServer).getServerRegistry().getServerExtension("jBPM");
ProcessService processService = (ProcessService) ext.getServices().stream().filter(service -> service instanceof ProcessService).findFirst().get();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("eventId", eventId);
processService.startProcess(deploymentId ,processId, paramMap);
Inside a WorkItemHandler, runtimeManager
is provided at initialization time:
RuntimeEngine runtimeEngine = runtimeManager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession kieSession = runtimeEngine.getKieSession();
Map<String, Object> startParams = new HashMap<>();
startParams.put("businessId", "id-"+n);
kieSession.startProcess(processName, startParams );
Warning: When PER_PROCESS_INSTANCE
strategy in place, DON'T reuse the runtime engine, but get a new one for each new process to start.
Remind to free up the resources:
runtimeManager.disposeRuntimeEngine(runtimeEngine);
Retrieve the runtimeEngine when PER_PROCESS_INSTANCE
strategy in place:
Context<?> context = new ProcessInstanceIdContext(parentProcessInstanceId);
RuntimeEngine runtimeEngine = runtimeManager.getRuntimeEngine(context);
Import dependencies using provided
for the scope.
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
Optional tag should avoid the propagation of the dependency in chain. It's useful in case the code is in the Business Central.
KieRuntime kruntime = ((ProcessInstance) getProcessInstance()).getKnowledgeRuntime();
RuntimeManager manager = (RuntimeManager) kruntime.getEnvironment().get(EnvironmentName.RUNTIME_MANAGER);
Leveraging kcontext
:
String deploymentId = (String) kcontext.getKieRuntime().getEnvironment().get(EnvironmentName.DEPLOYMENT_ID);
Example
kcontext.getKnowledgeRuntime().insert( application );
RuleFlowProcessInstance processInstance = (...)
InternalProcessRuntime processRuntime = (InternalProcessRuntime) processInstance.getKnowledgeRuntime().getProcessRuntime();
SignalManager signalManager = processRuntime.getSignalManager();
retryHandlingListener.register(signalManager);
signalManager.addEventListener("test", listener);
It's possible to ask a process to sent a signal when it complete:
RuleFlowProcessInstance processInstance = (...)
piImpl.setSignalCompletion(true);
Conventionally, it sends a signal with the following SignalRef: processInstanceCompleted:#{processInstanceId}
.
The event payload is the RuleFlowProcessInstance
, where it's possible to retrieve all the information on the closing instance.
During the development of BRMS 6.3 and 6.4, the engine was redesigned to better coordinate different threads (both created by user and internal to the engine itself) accessing a KieSession. In particular, they simplified the concurrency model of the engine, with the introduction of that state machine, and this allow to both increase performances in multithreaded environments and be more confident about the robustness of the engine in heavily concurrent use cases.
KieBase and KieSession have been designed to be thread-safe. Dynamic changes to a KieBase are now coordinated with the state machines of the KieSessions formerly created by the same KieBase, so also incremental compilation can be considered thread-safe.
KieSessions can be shared between different threads.
It's better to avoid the execution of long and blocking operations inside the RHS but this is not related with thread-safety. This only related to performance reasons, because also the invocation of a RHS is blocking and synchronous, so having a particularly slow RHS will have the effect of slowing down the whole engine.
KieSession is single threaded (v7 introduces multi-threading capabilities). So any blocking operation in the RHS blocks the full session and all the rule evaluation in that session. So blocking operations in the RHS essentially kills performance of your rules engine.
since version 6 timers can be configured with valid ISO8601 date format that supports both one shot timers and repeatable timers. Timers can be defined as date and time representation, time duration or repeating intervals
- Date - 2013-12-24T20:00:00.000+02:00 - fires exactly at Christmas Eve at 8PM
- Duration - PT1S - fires once after 1 second
- Repeatable intervals - R/PT1S - fires every second, no limit, alternatively R5/PT1S will fire 5 times every second
P3Y6M4DT12H30M5S
represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".P2D
represents 2 days
Wikipedia duration standard format
Service Registry must be used in a kie-server (CDI not allowed):
Dependency:
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-services-api</artifactId>
<scope>provided</scope>
</dependency>
Usage example:
RuntimeDataService runtimeDataService = (RuntimeDataService) ServiceRegistry.get()
.service(ServiceRegistry.RUNTIME_DATA_SERVICE);
UserTaskAdminService userTaskAdminService = (UserTaskAdminService) ServiceRegistry.get()
.service(ServiceRegistry.USER_TASK_ADMIN_SERVICE);
QueryService queryService = (QueryService) ServiceRegistry.get().service(ServiceRegistry.QUERY_SERVICE);
RuleFlowProcessInstance processInstance = (RuleFlowProcessInstance) event.getProcessInstance();
processInstance.getNodeInstances()
.forEach(node -> {
if (node instanceof StateBasedNodeInstance)
((StateBasedNodeInstance) node).triggerCompleted();;
});
H2:
select processid, nid, nodetype, nodename, count(nid) as total_hits, avg(execution_time) as averageExecutionTime, min(execution_time) as minExecutionTime, max(execution_time) as maxExecutionTime
from (
select processid, max(log_date) as lastLog, processinstanceid as piid, nodeinstanceid as niid, nodeid as nid, nodetype, nodename, ( min(log_date) - max(log_date)) as execution_time
from NodeInstanceLog group by processinstanceid, nodeinstanceid order by lastLog
) group by nid
Oracle:
select processid, nid, nodetype, nodename, count(nid) as total_hits, avg(execution_time) as averageExecutionTime, min(execution_time) as minExecutionTime, max(execution_time) as maxExecutionTime
from (
select processid, max(log_date) as lastLog, processinstanceid as piid, nodeinstanceid as niid, nodeid as nid, nodetype, nodename, ( min(to_number(TO_CHAR(log_date,'YYYYMMDDHHmmss'))) - max(to_number(TO_CHAR(log_date,'YYYYMMDDHHmmss'))) ) as execution_time
from c##rhpam.NodeInstanceLog
group by processid, processinstanceid, nodeinstanceid, nodeid, nodetype, nodename order by lastLog
) group by processid, nid, nodetype, nodename
Retrieve the kieContainer from the classpath
private static KieServices ks = KieServices.Factory.get();
private static KieContainer kieContainer = ks.getKieClasspathContainer(BKM.class.getClassLoader());
singleton with a counter:
import java.util.concurrent.atomic.AtomicInteger;
public class SingletonSample {
private static SingletonSample SINGLETON = new SingletonSample();
private AtomicInteger counter = new AtomicInteger();
public static SingletonSample get() {
return SINGLETON;
}
public int count() {
return counter.incrementAndGet();
}
}