
com.effektif.workflow.impl.WorkflowEngineImpl Maven / Gradle / Ivy
/*
* Copyright 2014 Effektif GmbH.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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 com.effektif.workflow.impl;
import com.effektif.workflow.api.Configuration;
import com.effektif.workflow.api.WorkflowEngine;
import com.effektif.workflow.api.model.*;
import com.effektif.workflow.api.query.WorkflowInstanceQuery;
import com.effektif.workflow.api.query.WorkflowQuery;
import com.effektif.workflow.api.workflow.ExecutableWorkflow;
import com.effektif.workflow.api.workflowinstance.WorkflowInstance;
import com.effektif.workflow.impl.configuration.Brewable;
import com.effektif.workflow.impl.configuration.Brewery;
import com.effektif.workflow.impl.data.DataTypeService;
import com.effektif.workflow.impl.job.Job;
import com.effektif.workflow.impl.util.Exceptions;
import com.effektif.workflow.impl.util.Time;
import com.effektif.workflow.impl.workflow.ActivityImpl;
import com.effektif.workflow.impl.workflow.TransitionImpl;
import com.effektif.workflow.impl.workflow.WorkflowImpl;
import com.effektif.workflow.impl.workflowinstance.ActivityInstanceImpl;
import com.effektif.workflow.impl.workflowinstance.LockImpl;
import com.effektif.workflow.impl.workflowinstance.ScopeInstanceImpl;
import com.effektif.workflow.impl.workflowinstance.WorkflowInstanceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author Tom Baeyens
*/
public class WorkflowEngineImpl implements WorkflowEngine, Brewable {
public static final Logger log = LoggerFactory.getLogger(WorkflowEngine.class);
public String id;
public ExecutorService executorService;
public WorkflowCache workflowCache;
public WorkflowStore workflowStore;
public WorkflowInstanceStore workflowInstanceStore;
public Configuration configuration;
public List workflowExecutionListeners;
public DataTypeService dataTypeService;
@Override
public void brew(Brewery brewery) {
this.id = brewery.get(WorkflowEngineConfiguration.class).getWorkflowEngineId();
this.configuration = brewery.get(Configuration.class);
this.executorService = brewery.get(ExecutorService.class);
this.workflowCache = brewery.get(WorkflowCache.class);
this.workflowStore = brewery.get(WorkflowStore.class);
this.workflowInstanceStore = brewery.get(WorkflowInstanceStore.class);
this.dataTypeService = brewery.get(DataTypeService.class);
}
public void startup() {
}
public void shutdown() {
executorService.shutdown();
}
/// Workflow methods ////////////////////////////////////////////////////////////
@Override
public Deployment deployWorkflow(ExecutableWorkflow workflow) {
if (log.isDebugEnabled()) {
log.debug("Deploying workflow");
}
WorkflowParser parser = new WorkflowParser(configuration);
parser.parse(workflow);
if (!parser.hasErrors()) {
WorkflowImpl workflowImpl = parser.getWorkflow();
WorkflowId workflowId;
if (workflow.getId()==null) {
workflowId = workflowStore.generateWorkflowId();
workflow.setId(workflowId);
}
workflow.setCreateTime(Time.now());
workflowImpl.id = workflow.getId();
workflowStore.insertWorkflow(workflow);
if (workflowImpl.trigger!=null) {
workflowImpl.trigger.published(workflowImpl);
}
workflowCache.put(workflowImpl);
}
return new Deployment(workflow.getId(), parser.getIssues());
}
@Override
public List findWorkflows(WorkflowQuery workflowQuery) {
return workflowStore.findWorkflows(workflowQuery);
}
@Override
public void deleteWorkflows(WorkflowQuery workflowQuery) {
workflowStore.deleteWorkflows(workflowQuery);
}
public WorkflowInstance start(TriggerInstance triggerInstance) {
WorkflowInstanceImpl workflowInstance = startInitialize(triggerInstance);
return startExecute(workflowInstance);
}
/** first part of starting a new workflow instance: creating the workflow instance and applying the trigger data */
public WorkflowInstanceImpl startInitialize(TriggerInstance triggerInstance) {
WorkflowId workflowId = getLatestWorkflowId(triggerInstance);
WorkflowImpl workflow = getWorkflowImpl(workflowId);
LockImpl lock = new LockImpl();
lock.setTime(Time.now());
lock.setOwner(getId());
WorkflowInstanceId workflowInstanceId = triggerInstance.getWorkflowInstanceId();
if (workflowInstanceId==null) {
workflowInstanceId = workflowInstanceStore.generateWorkflowInstanceId();
}
WorkflowInstanceImpl workflowInstance = new WorkflowInstanceImpl(
configuration,
workflow,
workflowInstanceId,
triggerInstance,
lock,
triggerInstance.getTransientData());
if (log.isDebugEnabled()) log.debug("Created "+workflowInstance);
if (workflow.trigger!=null) {
workflow.trigger.applyTriggerData(workflowInstance, triggerInstance);
} else {
workflowInstance.setVariableValues(triggerInstance);
}
notifyWorkflowInstanceStarted(workflowInstance);
return workflowInstance;
}
/** Second part of starting a new workflow instance: executing the start activities. */
public WorkflowInstance startExecute(WorkflowInstanceImpl workflowInstance) {
WorkflowImpl workflow = workflowInstance.workflow;
if (log.isDebugEnabled()) log.debug("Starting "+workflowInstance);
if (workflow.startActivities!=null) {
for (ActivityImpl startActivityDefinition: workflow.startActivities) {
if (workflowInstance.startActivityIds == null
|| workflowInstance.startActivityIds.contains(startActivityDefinition.getId())) {
workflowInstance.execute(startActivityDefinition);
}
}
} else {
workflowInstance.endAndPropagateToParent();
}
notifyInsert(workflowInstance);
workflowInstanceStore.insertWorkflowInstance(workflowInstance);
workflowInstance.executeWork();
return workflowInstance.toWorkflowInstance();
}
public WorkflowId getLatestWorkflowId(TriggerInstance triggerInstance) {
WorkflowId workflowId = triggerInstance.getWorkflowId();
if (workflowId==null) {
if (triggerInstance.getSourceWorkflowId()!=null) {
workflowId = workflowStore.findLatestWorkflowIdBySource(triggerInstance.getSourceWorkflowId());
if (workflowId==null) throw new RuntimeException("No workflow found for source '"+triggerInstance.getSourceWorkflowId()+"'");
} else {
throw new RuntimeException("No workflow specified");
}
}
return workflowId;
}
@Override
public WorkflowInstance send(Message message) {
WorkflowInstanceImpl workflowInstance = lockWorkflowInstanceWithRetry(message.getWorkflowInstanceId());
return send(message, workflowInstance);
}
/***
* To manually move a workflowInstance from the current activityInstance to the specified activityInstance.
* Any "work" in between will not be executed! Will probably be used during testing of your workflows...
* Note: If your process contains a parallel gateway, and you move one of the two "instances" to an activity after the
* merge-parallel gateway, things would get messy.... Because of that, the move does not allow this, it checks for #open activityInstances <= 1
* Sub-processes are not taken into account ie, propagateToParent is not called.
* @return WorkflowInstance is the to-activity was found and the move was executed, null otherwise.
*/
public WorkflowInstance moveImpl(WorkflowInstanceImpl workflowInstanceImpl, String activityInstanceId, String newActivityId) {
if(workflowInstanceImpl.lock == null) throw new RuntimeException("WorkflowInstance not locked!");
if (log.isDebugEnabled()) log.debug("Moving workflowInstance to activityId: " + newActivityId);
try {
if (workflowInstanceImpl.activityInstances == null) {
log.debug("ActivityInstances == null, returning without doing something.");
return null;
}
ActivityInstanceImpl activityInstanceImpl = null;
int openActCount = 0;
if (activityInstanceId == null) {
for (ActivityInstanceImpl activityInstance : workflowInstanceImpl.activityInstances) {
if (!activityInstance.isEnded()) {
activityInstanceImpl = activityInstance;
openActCount++;
}
}
} else {
activityInstanceImpl = workflowInstanceImpl.findActivityInstanceByActivityId(activityInstanceId);
}
if (openActCount > 1)
throw new RuntimeException("Move cannot be called on a workflowInstance with more than one open activityInstance. " +
"Probably this workflowInstance is part of a paralell process...");
if (workflowInstanceImpl.jobs != null) {
Iterator jobIterator = workflowInstanceImpl.jobs.iterator();
while (jobIterator.hasNext()) {
Job job = jobIterator.next();
if (job.getActivityInstanceId() != null && activityInstanceImpl != null
&& job.getActivityInstanceId().equals(activityInstanceImpl.getId())) {
jobIterator.remove();
}
}
}
ActivityImpl activityImpl = workflowInstanceImpl.workflow.findActivityByIdLocal(newActivityId);
if (activityImpl == null) throw new RuntimeException("To-activityId not found!");
if (activityInstanceImpl != null && !activityInstanceImpl.isEnded()) activityInstanceImpl.end();
if (workflowInstanceImpl.isEnded()) {
workflowInstanceImpl.setEnd(null);
workflowInstanceImpl.duration = 0L;
}
workflowInstanceImpl.execute(activityImpl);
workflowInstanceImpl.executeWork();
return workflowInstanceImpl.toWorkflowInstance();
} finally {
workflowInstanceStore.unlockWorkflowInstance(workflowInstanceImpl);
}
}
@Override
public WorkflowInstance move(WorkflowInstanceId workflowInstanceId, String activityInstanceId, String newActivityId) {
WorkflowInstanceImpl workflowInstance = lockWorkflowInstanceWithRetry(workflowInstanceId);
return moveImpl(workflowInstance, activityInstanceId, newActivityId);
}
@Override
public WorkflowInstance move(WorkflowInstanceId workflowInstanceId, String newActivityId) {
return move(workflowInstanceId, null, newActivityId);
}
public WorkflowInstance send(Message message, WorkflowInstanceImpl workflowInstance) {
Map transientData = message.getTransientData();
if (transientData !=null) {
for (String key: transientData.keySet()) {
workflowInstance.setTransientProperty(key, transientData.get(key));
}
}
String activityInstanceId = message.getActivityInstanceId();
ActivityInstanceImpl activityInstance = workflowInstance.findActivityInstance(activityInstanceId);
if (activityInstance==null) {
workflowInstanceStore.unlockWorkflowInstance(workflowInstance);
throw new RuntimeException("Activity instance "+activityInstanceId+" not in workflow instance");
}
if (log.isDebugEnabled())
log.debug("Signalling "+activityInstance);
ActivityImpl activity = activityInstance.getActivity();
activity.activityType.message(activityInstance, message);
workflowInstance.executeWork();
return workflowInstance.toWorkflowInstance();
}
@Override
public WorkflowInstance cancel(WorkflowInstanceId workflowInstanceId) {
WorkflowInstanceImpl workflowInstance = lockWorkflowInstanceWithRetry(workflowInstanceId);
workflowInstance.cancel();
return workflowInstance.toWorkflowInstance();
}
@Override
public void deleteWorkflowInstances(WorkflowInstanceQuery query) {
workflowInstanceStore.deleteWorkflowInstances(query);
}
@Override
public List findWorkflowInstances(WorkflowInstanceQuery query) {
List workflowInstances = workflowInstanceStore.findWorkflowInstances(query);
return WorkflowInstanceImpl.toWorkflowInstances(workflowInstances);
}
/** retrieves the executable form of the workflow using the workflow cache */
public WorkflowImpl getWorkflowImpl(WorkflowId workflowId) {
WorkflowImpl workflowImpl = workflowCache.get(workflowId);
if (workflowImpl==null) {
ExecutableWorkflow workflow = workflowStore.loadWorkflowById(workflowId);
if (workflow != null) {
WorkflowParser parser = new WorkflowParser(configuration);
workflowImpl = parser.parse(workflow);
workflowCache.put(workflowImpl);
}
}
return workflowImpl;
}
public WorkflowInstanceImpl lockWorkflowInstanceWithRetry(
final WorkflowInstanceId workflowInstanceId) {
Retry retry = new Retry() {
@Override
public WorkflowInstanceImpl tryOnce() {
return workflowInstanceStore.lockWorkflowInstance(workflowInstanceId);
}
@Override
protected void failedWaitingForRetry() {
if (log.isDebugEnabled()) {
log.debug("Locking workflow instance "+workflowInstanceId+" failed... retrying in "+wait+" millis");
}
}
@Override
protected void interrupted() {
if (log.isDebugEnabled()) {
log.debug("Waiting for workflow instance lock was interrupted");
}
}
@Override
protected void failedPermanent() {
throw new RuntimeException("Couldn't lock workflow instance " + workflowInstanceId);
}
};
return retry.tryManyTimes();
}
public String getId() {
return id;
}
public ExecutorService getExecutorService() {
return executorService;
}
public WorkflowCache getProcessDefinitionCache() {
return workflowCache;
}
public WorkflowStore getWorkflowStore() {
return workflowStore;
}
public WorkflowInstanceStore getWorkflowInstanceStore() {
return workflowInstanceStore;
}
public void addWorkflowExecutionListener(WorkflowExecutionListener workflowExecutionListener) {
if (workflowExecutionListeners==null) {
workflowExecutionListeners = new ArrayList<>();
}
workflowExecutionListeners.add(workflowExecutionListener);
}
public void removeWorkflowExecutionListener(WorkflowExecutionListener workflowExecutionListener) {
if (workflowExecutionListeners!=null) {
workflowExecutionListeners.remove(workflowExecutionListener);
if (workflowExecutionListeners.isEmpty()) {
workflowExecutionListeners = null;
}
}
}
public List getWorkflowExecutionListeners() {
return workflowExecutionListeners;
}
public void setWorkflowExecutionListeners(List workflowExecutionListeners) {
this.workflowExecutionListeners = workflowExecutionListeners;
}
public void notifyWorkflowInstanceStarted(WorkflowInstanceImpl workflowInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.starting(workflowInstance);
}
}
}
public void notifyWorkflowInstanceEnded(WorkflowInstanceImpl workflowInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.ended(workflowInstance);
}
}
}
public boolean notifyActivityInstanceStarted(ActivityInstanceImpl activityInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
if (!workflowExecutionListener.starting(activityInstance)) {
return false;
}
}
}
return true;
}
public void notifyUnlocked(WorkflowInstanceImpl workflowInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.unlocked(workflowInstance);
}
}
}
public void notifyFlush(WorkflowInstanceImpl workflowInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.flush(workflowInstance);
}
}
}
public void notifyActivityInstanceEnded(ActivityInstanceImpl activityInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.ended(activityInstance);
}
}
}
public void notifyTransitionTaken(ActivityInstanceImpl activityInstanceFrom, TransitionImpl transition, ActivityInstanceImpl activityInstanceTo) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.transitioning(activityInstanceFrom, transition, activityInstanceTo);
}
}
}
public void notifyInsert(WorkflowInstanceImpl workflowInstance) {
if (workflowExecutionListeners!=null) {
for (WorkflowExecutionListener workflowExecutionListener: workflowExecutionListeners) {
workflowExecutionListener.insert(workflowInstance);
}
}
}
public VariableValues getVariableValues(WorkflowInstanceId workflowInstanceId) {
return getVariableValues(workflowInstanceId, null);
}
public VariableValues getVariableValues(WorkflowInstanceId workflowInstanceId, String activityInstanceId) {
WorkflowInstanceImpl workflowInstance = workflowInstanceStore.getWorkflowInstanceImplById(workflowInstanceId);
ScopeInstanceImpl scopeInstance = getScopeInstance(workflowInstance, activityInstanceId);
VariableValues variableValues = new VariableValues();
scopeInstance.collectVariableValues(variableValues);
return variableValues;
}
public void setVariableValues(WorkflowInstanceId workflowInstanceId, VariableValues variableValues) {
setVariableValues(workflowInstanceId, null, variableValues);
}
public void setVariableValues(WorkflowInstanceId workflowInstanceId, String activityInstanceId, VariableValues variableValues) {
if (workflowInstanceId==null || variableValues==null) {
return;
}
WorkflowInstanceImpl workflowInstance = lockWorkflowInstanceWithRetry(workflowInstanceId);
ScopeInstanceImpl scopeInstance = getScopeInstance(workflowInstance, activityInstanceId);
if (scopeInstance==null) {
workflowInstanceStore.unlockWorkflowInstance(workflowInstance);
throw new RuntimeException("Workflow instance "+workflowInstanceId+" didn't contain active activityInstanceId "+activityInstanceId);
}
Map values = variableValues!=null ? variableValues.getValues() : null;
if (values!=null) {
for (String variableId : values.keySet()) {
TypedValue typedValue = values.get(variableId);
Object value = typedValue.getValue();
scopeInstance.setVariableValue(variableId, value);
}
}
workflowInstanceStore.flushAndUnlock(workflowInstance);
}
public void setVariableValue(WorkflowInstanceId workflowInstanceId, String activityInstanceId, String variableId, Object value) {
WorkflowInstanceImpl workflowInstance = lockWorkflowInstanceWithRetry(workflowInstanceId);
ScopeInstanceImpl scopeInstance = getScopeInstance(workflowInstance, activityInstanceId);
if (scopeInstance==null) {
workflowInstanceStore.unlockWorkflowInstance(workflowInstance);
throw new RuntimeException("Workflow instance "+workflowInstanceId+" didn't contain active activityInstanceId "+activityInstanceId);
}
scopeInstance.setVariableValue(variableId, value);
workflowInstanceStore.flushAndUnlock(workflowInstance);
}
protected ScopeInstanceImpl getScopeInstance(WorkflowInstanceImpl workflowInstance, String activityInstanceId) {
ScopeInstanceImpl scopeInstance = workflowInstance;
if (activityInstanceId!=null) {
scopeInstance = workflowInstance.findActivityInstance(activityInstanceId);
Exceptions.checkNotNull(scopeInstance);
}
return scopeInstance;
}
public void executeAsync(Runnable asyncWork) {
executorService.execute(asyncWork);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy