/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jbpm.process.instance; import org.drools.core.SessionConfiguration; import org.drools.core.command.impl.ExecutableCommand; import org.drools.core.command.impl.RegistryContext; import org.drools.core.common.InternalKnowledgeRuntime; import org.drools.core.common.InternalWorkingMemory; import org.drools.core.common.WorkingMemoryAction; import org.drools.core.definitions.rule.impl.RuleImpl; import org.drools.core.event.ProcessEventSupport; import org.drools.core.impl.InternalKnowledgeBase; import org.drools.core.marshalling.impl.MarshallerReaderContext; import org.drools.core.marshalling.impl.MarshallerWriteContext; import org.drools.core.marshalling.impl.ProtobufMessages.ActionQueue.Action; import org.drools.core.phreak.PropagationEntry; import org.drools.core.time.TimeUtils; import org.drools.core.time.TimerService; import org.drools.core.time.impl.CommandServiceTimerJobFactoryManager; import org.drools.core.time.impl.CronExpression; import org.drools.core.time.impl.ThreadSafeTrackableTimeJobFactoryManager; import org.jbpm.process.core.event.EventFilter; import org.jbpm.process.core.event.EventTransformer; import org.jbpm.process.core.event.EventTypeFilter; import org.jbpm.process.core.timer.BusinessCalendar; import org.jbpm.process.core.timer.DateTimeUtils; import org.jbpm.process.core.timer.Timer; import org.jbpm.process.instance.event.SignalManager; import org.jbpm.process.instance.event.SignalManagerFactory; import org.jbpm.process.instance.timer.TimerInstance; import org.jbpm.process.instance.timer.TimerManager; import org.jbpm.ruleflow.core.RuleFlowProcess; import org.jbpm.workflow.core.node.EventTrigger; import org.jbpm.workflow.core.node.StartNode; import org.jbpm.workflow.core.node.Trigger; import org.kie.api.KieBase; import org.kie.api.definition.process.Node; import org.kie.api.definition.process.Process; import org.kie.api.event.process.ProcessEventListener; import org.kie.api.event.rule.DefaultAgendaEventListener; import org.kie.api.event.rule.MatchCreatedEvent; import org.kie.api.event.rule.RuleFlowGroupDeactivatedEvent; import org.kie.api.runtime.Context; import org.kie.api.runtime.EnvironmentName; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.manager.RuntimeEngine; import org.kie.api.runtime.manager.RuntimeManager; import org.kie.api.runtime.process.EventListener; import org.kie.api.runtime.process.ProcessInstance; import org.kie.api.runtime.process.WorkItemManager; import org.kie.internal.process.CorrelationKey; import org.kie.internal.runtime.StatefulKnowledgeSession; import org.kie.internal.runtime.manager.context.CaseContext; import org.kie.internal.runtime.manager.context.ProcessInstanceIdContext; import org.kie.internal.utils.CompositeClassLoader; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class ProcessRuntimeImpl implements InternalProcessRuntime { private InternalKnowledgeRuntime kruntime; private ProcessInstanceManager processInstanceManager; private SignalManager signalManager; private TimerManager timerManager; private ProcessEventSupport processEventSupport; public ProcessRuntimeImpl(InternalKnowledgeRuntime kruntime) { this.kruntime = kruntime; TimerService timerService = kruntime.getTimerService(); if ( !(timerService.getTimerJobFactoryManager() instanceof CommandServiceTimerJobFactoryManager) ) { timerService.setTimerJobFactoryManager( new ThreadSafeTrackableTimeJobFactoryManager() ); } ((CompositeClassLoader) getRootClassLoader()).addClassLoader( getClass().getClassLoader() ); initProcessInstanceManager(); initSignalManager(); timerManager = new TimerManager(kruntime, kruntime.getTimerService()); processEventSupport = new ProcessEventSupport(); if (isActive()) { initProcessEventListeners(); initStartTimers(); } initProcessActivationListener(); } public void initStartTimers() { KieBase kbase = kruntime.getKieBase(); Collection<Process> processes = kbase.getProcesses(); for (Process process : processes) { RuleFlowProcess p = (RuleFlowProcess) process; List<StartNode> startNodes = p.getTimerStart(); if (startNodes != null && !startNodes.isEmpty()) { kruntime.queueWorkingMemoryAction(new RegisterStartTimerAction(p.getId(), startNodes, this.timerManager)); } } } public ProcessRuntimeImpl(InternalWorkingMemory workingMemory) { TimerService timerService = workingMemory.getTimerService(); if ( !(timerService.getTimerJobFactoryManager() instanceof CommandServiceTimerJobFactoryManager) ) { timerService.setTimerJobFactoryManager( new ThreadSafeTrackableTimeJobFactoryManager() ); } this.kruntime = (InternalKnowledgeRuntime) workingMemory.getKnowledgeRuntime(); initProcessInstanceManager(); initSignalManager(); timerManager = new TimerManager(kruntime, kruntime.getTimerService()); processEventSupport = new ProcessEventSupport(); if (isActive()) { initProcessEventListeners(); initStartTimers(); } initProcessActivationListener(); } private void initProcessInstanceManager() { String processInstanceManagerClass = ((SessionConfiguration) kruntime.getSessionConfiguration()).getProcessInstanceManagerFactory(); try { processInstanceManager = ((ProcessInstanceManagerFactory) loadClass(processInstanceManagerClass).newInstance()) .createProcessInstanceManager(kruntime); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private void initSignalManager() { String signalManagerClass = ((SessionConfiguration) kruntime.getSessionConfiguration()).getSignalManagerFactory(); try { signalManager = ((SignalManagerFactory) loadClass(signalManagerClass).newInstance()) .createSignalManager(kruntime); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private Class<?> loadClass(String className) { try { return getRootClassLoader().loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } private ClassLoader getRootClassLoader() { KieBase kbase = ((InternalKnowledgeBase) kruntime.getKieBase()); if (kbase != null) { return ((InternalKnowledgeBase) kbase).getRootClassLoader(); } CompositeClassLoader result = new CompositeClassLoader(); result.addClassLoader(Thread.currentThread().getContextClassLoader()); return result; } public ProcessInstance startProcess(final String processId) { return startProcess(processId, null); } public ProcessInstance startProcess(String processId, Map<String, Object> parameters) { return startProcess(processId, parameters, null); } public ProcessInstance startProcess(String processId, Map<String, Object> parameters, String trigger) { ProcessInstance processInstance = createProcessInstance(processId, parameters); if ( processInstance != null ) { // start process instance return startProcessInstance(processInstance.getId(), trigger); } return null; } public ProcessInstance createProcessInstance(String processId, Map<String, Object> parameters) { return createProcessInstance(processId, null, parameters); } public ProcessInstance startProcessInstance(long processInstanceId, String trigger) { try { kruntime.startOperation(); ProcessInstance processInstance = getProcessInstance(processInstanceId); getProcessEventSupport().fireBeforeProcessStarted( processInstance, kruntime ); ((org.jbpm.process.instance.ProcessInstance) processInstance).start(trigger); getProcessEventSupport().fireAfterProcessStarted( processInstance, kruntime ); return processInstance; } finally { kruntime.endOperation(); } } public ProcessInstance startProcessInstance(long processInstanceId) { return startProcessInstance(processInstanceId, null); } @Override public ProcessInstance startProcess(String processId, CorrelationKey correlationKey, Map<String, Object> parameters) { ProcessInstance processInstance = createProcessInstance(processId, correlationKey, parameters); if ( processInstance != null ) { return startProcessInstance(processInstance.getId()); } return null; } @Override public ProcessInstance createProcessInstance(String processId, CorrelationKey correlationKey, Map<String, Object> parameters) { try { kruntime.startOperation(); final Process process = kruntime.getKieBase().getProcess( processId ); if ( process == null ) { throw new IllegalArgumentException( "Unknown process ID: " + processId ); } return startProcess( process, correlationKey, parameters ); } finally { kruntime.endOperation(); } } @Override public ProcessInstance getProcessInstance(CorrelationKey correlationKey) { return processInstanceManager.getProcessInstance(correlationKey); } private org.jbpm.process.instance.ProcessInstance startProcess(final Process process, CorrelationKey correlationKey, Map<String, Object> parameters) { ProcessInstanceFactory conf = ProcessInstanceFactoryRegistry.INSTANCE.getProcessInstanceFactory( process ); if ( conf == null ) { throw new IllegalArgumentException( "Illegal process type: " + process.getClass() ); } return conf.createProcessInstance( process, correlationKey, kruntime, parameters ); } public ProcessInstanceManager getProcessInstanceManager() { return processInstanceManager; } public TimerManager getTimerManager() { return timerManager; } public SignalManager getSignalManager() { return signalManager; } public Collection<ProcessInstance> getProcessInstances() { return processInstanceManager.getProcessInstances(); } public ProcessInstance getProcessInstance(long id) { return getProcessInstance( id, false ); } public ProcessInstance getProcessInstance(long id, boolean readOnly) { return processInstanceManager.getProcessInstance( id, readOnly ); } public void removeProcessInstance(ProcessInstance processInstance) { processInstanceManager.removeProcessInstance( processInstance ); } public void initProcessEventListeners() { for ( Process process : kruntime.getKieBase().getProcesses() ) { initProcessEventListener(process); } } public void removeProcessEventListeners() { for ( Process process : kruntime.getKieBase().getProcesses() ) { removeProcessEventListener(process); } } private void removeProcessEventListener(Process process) { if (process instanceof RuleFlowProcess) { String type = (String) ((RuleFlowProcess) process).getRuntimeMetaData().get("StartProcessEventType"); StartProcessEventListener listener = (StartProcessEventListener) ((RuleFlowProcess) process).getRuntimeMetaData().get("StartProcessEventListener"); if (type != null && listener != null) { signalManager.removeEventListener(type, listener); } } } private void initProcessEventListener(Process process) { if ( process instanceof RuleFlowProcess ) { for (Node node : ((RuleFlowProcess) process).getNodes()) { if (node instanceof StartNode) { StartNode startNode = (StartNode) node; if (startNode != null) { List<Trigger> triggers = startNode.getTriggers(); if ( triggers != null ) { for ( Trigger trigger : triggers ) { if ( trigger instanceof EventTrigger ) { final List<EventFilter> filters = ((EventTrigger) trigger).getEventFilters(); String type = null; for ( EventFilter filter : filters ) { if ( filter instanceof EventTypeFilter ) { type = ((EventTypeFilter) filter).getType(); } } StartProcessEventListener listener = new StartProcessEventListener( process.getId(), filters, trigger.getInMappings(), startNode.getEventTransformer()); signalManager.addEventListener( type, listener ); ((RuleFlowProcess) process).getRuntimeMetaData().put("StartProcessEventType", type); ((RuleFlowProcess) process).getRuntimeMetaData().put("StartProcessEventListener", listener); } } } } } } } } public ProcessEventSupport getProcessEventSupport() { return processEventSupport; } public void addEventListener(final ProcessEventListener listener) { this.processEventSupport.addEventListener( listener ); } public void removeEventListener(final ProcessEventListener listener) { this.processEventSupport.removeEventListener( listener ); } public List<ProcessEventListener> getProcessEventListeners() { return processEventSupport.getEventListeners(); } private class StartProcessEventListener implements EventListener { private String processId; private List<EventFilter> eventFilters; private Map<String, String> inMappings; private EventTransformer eventTransformer; public StartProcessEventListener(String processId, List<EventFilter> eventFilters, Map<String, String> inMappings, EventTransformer eventTransformer) { this.processId = processId; this.eventFilters = eventFilters; this.inMappings = inMappings; this.eventTransformer = eventTransformer; } public String[] getEventTypes() { return null; } public void signalEvent(final String type, Object event) { for ( EventFilter filter : eventFilters ) { if ( !filter.acceptsEvent( type, event ) ) { return; } } if (eventTransformer != null) { event = eventTransformer.transformEvent(event); } Map<String, Object> params = null; if ( inMappings != null && !inMappings.isEmpty() ) { params = new HashMap<String, Object>(); if (inMappings.size() == 1) { params.put( inMappings.keySet().iterator().next(), event ); } else { for ( Map.Entry<String, String> entry : inMappings.entrySet() ) { if ( "event".equals( entry.getValue() ) ) { params.put( entry.getKey(), event ); } else { params.put( entry.getKey(), entry.getValue() ); } } } } RuntimeManager manager = (RuntimeManager) kruntime.getEnvironment().get(EnvironmentName.RUNTIME_MANAGER); if (manager != null) { org.kie.api.runtime.manager.Context<?> context = ProcessInstanceIdContext.get(); String caseId = (String) kruntime.getEnvironment().get(EnvironmentName.CASE_ID); if (caseId != null) { context = CaseContext.get(caseId); } RuntimeEngine runtime = manager.getRuntimeEngine(context); KieSession ksession = runtime.getKieSession(); ksession.execute(new StartProcessWithTypeCommand(processId, params, type)); } else { startProcess( processId, params, type ); } } } private void initProcessActivationListener() { kruntime.addEventListener(new DefaultAgendaEventListener() { public void matchCreated(MatchCreatedEvent event) { String ruleFlowGroup = ((RuleImpl) event.getMatch().getRule()).getRuleFlowGroup(); if ( "DROOLS_SYSTEM".equals( ruleFlowGroup ) ) { // new activations of the rule associate with a state node // signal process instances of that state node String ruleName = event.getMatch().getRule().getName(); if ( ruleName.startsWith( "RuleFlowStateNode-" )) { int index = ruleName.indexOf( '-', 18 ); index = ruleName.indexOf( '-', index + 1 ); String eventType = ruleName.substring( 0, index ); kruntime.queueWorkingMemoryAction(new SignalManagerSignalAction(eventType, event)); } else if (ruleName.startsWith( "RuleFlowStateEventSubProcess-" ) || ruleName.startsWith( "RuleFlowStateEvent-" ) || ruleName.startsWith( "RuleFlow-Milestone-" ) || ruleName.startsWith( "RuleFlow-AdHocComplete-" )) { kruntime.queueWorkingMemoryAction(new SignalManagerSignalAction(ruleName, event)); } } } }); kruntime.addEventListener(new DefaultAgendaEventListener() { public void afterRuleFlowGroupDeactivated(final RuleFlowGroupDeactivatedEvent event) { if (kruntime instanceof StatefulKnowledgeSession) { signalManager.signalEvent( "RuleFlowGroup_" + event.getRuleFlowGroup().getName() + "_" + ((StatefulKnowledgeSession) kruntime).getIdentifier(), null ); } else { signalManager.signalEvent( "RuleFlowGroup_" + event.getRuleFlowGroup().getName(), null ); } } } ); } private class StartProcessWithTypeCommand implements ExecutableCommand<Void> { private static final long serialVersionUID = -8890906804846111698L; private String processId; private Map<String, Object> params; private String type; private StartProcessWithTypeCommand(String processId, Map<String, Object> params, String type) { this.processId = processId; this.params = params; this.type = type; } @Override public Void execute(Context context ) { KieSession ksession = ((RegistryContext) context).lookup( KieSession.class ); ((ProcessRuntimeImpl)((InternalKnowledgeRuntime)ksession).getProcessRuntime()).startProcess( processId, params, type ); return null; } } public void abortProcessInstance(long processInstanceId) { ProcessInstance processInstance = getProcessInstance(processInstanceId); if ( processInstance == null ) { throw new IllegalArgumentException( "Could not find process instance for id " + processInstanceId ); } ((org.jbpm.process.instance.ProcessInstance) processInstance).setState( ProcessInstance.STATE_ABORTED ); } public WorkItemManager getWorkItemManager() { return kruntime.getWorkItemManager(); } public void signalEvent(String type, Object event) { signalManager.signalEvent(type, event); } public void signalEvent(String type, Object event, long processInstanceId) { signalManager.signalEvent(processInstanceId, type, event); } public void setProcessEventSupport(ProcessEventSupport processEventSupport) { this.processEventSupport = processEventSupport; } public void dispose() { this.processEventSupport.reset(); this.timerManager.dispose(); kruntime = null; } public void clearProcessInstances() { this.processInstanceManager.clearProcessInstances(); } public void clearProcessInstancesState() { this.processInstanceManager.clearProcessInstancesState(); } public boolean isActive() { Boolean active = (Boolean) kruntime.getEnvironment().get("Active"); if (active == null) { return true; } return active.booleanValue(); } public static class RegisterStartTimerAction extends PropagationEntry.AbstractPropagationEntry implements WorkingMemoryAction { private List<StartNode> startNodes; private String processId; private TimerManager timerManager; public RegisterStartTimerAction(String processId, List<StartNode> startNodes, TimerManager timerManager) { this.processId = processId; this.startNodes = startNodes; this.timerManager = timerManager; } public RegisterStartTimerAction(MarshallerReaderContext context) { } @Override public void execute(InternalWorkingMemory workingMemory) { initTimer(workingMemory.getKnowledgeRuntime()); } @Override public void execute(InternalKnowledgeRuntime kruntime) { initTimer(kruntime); } @Override public Action serialize(MarshallerWriteContext context) throws IOException { return null; } private void initTimer(InternalKnowledgeRuntime kruntime) { for (StartNode startNode : startNodes) { if (startNode != null && startNode.getTimer() != null) { TimerInstance timerInstance = null; if (startNode.getTimer().getDelay() != null && CronExpression.isValidExpression(startNode.getTimer().getDelay())) { timerInstance = new TimerInstance(); timerInstance.setCronExpression(startNode.getTimer().getDelay()); } else { timerInstance = createTimerInstance(startNode.getTimer(), kruntime); } timerManager.registerTimer(timerInstance, processId, null); } } } protected TimerInstance createTimerInstance(Timer timer, InternalKnowledgeRuntime kruntime) { TimerInstance timerInstance = new TimerInstance(); if (kruntime != null && kruntime.getEnvironment().get("jbpm.business.calendar") != null){ BusinessCalendar businessCalendar = (BusinessCalendar) kruntime.getEnvironment().get("jbpm.business.calendar"); String delay = timer.getDelay(); timerInstance.setDelay(businessCalendar.calculateBusinessTimeAsDuration(delay)); if (timer.getPeriod() == null) { timerInstance.setPeriod(0); } else { String period = timer.getPeriod(); timerInstance.setPeriod(businessCalendar.calculateBusinessTimeAsDuration(period)); } } else { configureTimerInstance(timer, timerInstance); } timerInstance.setTimerId(timer.getId()); return timerInstance; } private void configureTimerInstance(Timer timer, TimerInstance timerInstance) { long duration = -1; switch (timer.getTimeType()) { case Timer.TIME_CYCLE: // when using ISO date/time period is not set long[] repeatValues = DateTimeUtils.parseRepeatableDateTime(timer.getDelay()); if (repeatValues.length == 3) { int parsedReapedCount = (int)repeatValues[0]; if (parsedReapedCount > -1) { timerInstance.setRepeatLimit(parsedReapedCount+1); } timerInstance.setDelay(repeatValues[1]); timerInstance.setPeriod(repeatValues[2]); } else { timerInstance.setDelay(repeatValues[0]); try { long period = DateTimeUtils.parseTimeString(timer.getPeriod()); timerInstance.setPeriod(period); } catch (RuntimeException e) { timerInstance.setPeriod(repeatValues[0]); } } break; case Timer.TIME_DURATION: duration = DateTimeUtils.parseDuration(timer.getDelay()); timerInstance.setDelay(duration); timerInstance.setPeriod(0); break; case Timer.TIME_DATE: duration = DateTimeUtils.parseDateAsDuration(timer.getDate()); timerInstance.setDelay(duration); timerInstance.setPeriod(0); break; default: break; } } private long resolveValue(String s) { return TimeUtils.parseTimeString(s); } } public class SignalManagerSignalAction extends PropagationEntry.AbstractPropagationEntry implements WorkingMemoryAction { private String type; private Object event; public SignalManagerSignalAction(String type, Object event) { this.type = type; this.event = event; } public SignalManagerSignalAction(MarshallerReaderContext context) throws IOException, ClassNotFoundException { type = context.readUTF(); if (context.readBoolean()) { event = context.readObject(); } } public void execute(InternalWorkingMemory workingMemory) { signalEvent(type, event); } public void execute(InternalKnowledgeRuntime kruntime) { signalEvent(type, event); } public Action serialize(MarshallerWriteContext context) throws IOException { return null; } } }