/*
* 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.mongo;
import com.effektif.mongo.WorkflowInstanceFields.*;
import com.effektif.workflow.api.Configuration;
import com.effektif.workflow.api.model.WorkflowId;
import com.effektif.workflow.api.model.WorkflowInstanceId;
import com.effektif.workflow.api.query.WorkflowInstanceQuery;
import com.effektif.workflow.api.types.DataType;
import com.effektif.workflow.api.workflow.Extensible;
import com.effektif.workflow.api.workflowinstance.VariableInstance;
import com.effektif.workflow.api.workflowinstance.WorkflowInstance;
import com.effektif.workflow.impl.WorkflowEngineImpl;
import com.effektif.workflow.impl.WorkflowInstanceStore;
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.ScopeImpl;
import com.effektif.workflow.impl.workflow.VariableImpl;
import com.effektif.workflow.impl.workflow.WorkflowImpl;
import com.effektif.workflow.impl.workflowinstance.*;
import com.mongodb.*;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import java.util.*;
import static com.effektif.mongo.ActivityInstanceFields.*;
import static com.effektif.mongo.ActivityInstanceFields.DURATION;
import static com.effektif.mongo.ActivityInstanceFields.END;
import static com.effektif.mongo.ActivityInstanceFields.END_STATE;
import static com.effektif.mongo.ActivityInstanceFields.START;
import static com.effektif.mongo.MongoDb._ID;
import static com.effektif.mongo.MongoHelper.*;
import static com.effektif.mongo.WorkflowInstanceFields.*;
public class MongoWorkflowInstanceStore implements WorkflowInstanceStore, Brewable {
public static final Logger log = MongoDb.log;
protected Configuration configuration;
protected WorkflowEngineImpl workflowEngine;
protected MongoCollection workflowInstancesCollection;
protected MongoJobStore mongoJobsStore;
protected boolean storeWorkflowIdsAsStrings;
protected DataTypeService dataTypeService;
protected MongoObjectMapper mongoMapper;
@Override
public void brew(Brewery brewery) {
MongoConfiguration mongoConfiguration = brewery.get(MongoConfiguration.class);
MongoDb mongoDb = brewery.get(MongoDb.class);
this.configuration = brewery.get(MongoConfiguration.class);
this.workflowEngine = brewery.get(WorkflowEngineImpl.class);
this.workflowInstancesCollection = mongoDb.createCollection(mongoConfiguration.workflowInstancesCollectionName);
this.storeWorkflowIdsAsStrings = mongoConfiguration.getStoreWorkflowIdsAsString();
this.mongoJobsStore = brewery.get(MongoJobStore.class);
this.dataTypeService = brewery.get(DataTypeService.class);
this.mongoMapper = brewery.get(MongoObjectMapper.class);
}
@Override
public WorkflowInstanceId generateWorkflowInstanceId() {
return new WorkflowInstanceId(new ObjectId().toString());
}
@Override
public void insertWorkflowInstance(WorkflowInstanceImpl workflowInstance) {
BasicDBObject dbWorkflowInstance = writeWorkflowInstance(workflowInstance);
workflowInstancesCollection.insert("insert-workflow-instance", dbWorkflowInstance);
workflowInstance.trackUpdates(false);
}
@Override
public void flush(WorkflowInstanceImpl workflowInstance) {
if (log.isDebugEnabled()) log.debug("Flushing workflow instance...");
WorkflowInstanceUpdates updates = workflowInstance.getUpdates();
DBObject query = BasicDBObjectBuilder.start()
.add(_ID, new ObjectId(workflowInstance.id.getInternal()))
// I don't recall what this line was for... if you re-add it, please add a comment to explain
// .add(LOCK, writeLock(workflowInstance.lock))
.get();
BasicDBObject sets = new BasicDBObject();
BasicDBObject unsets = new BasicDBObject();
BasicDBObject update = new BasicDBObject();
if (updates.isEndChanged) {
// if (log.isDebugEnabled()) log.debug(" Workflow instance ended");
if (workflowInstance.end != null) {
sets.append(END, workflowInstance.end.toDate());
sets.append(DURATION, workflowInstance.duration);
}
else {
unsets.append(END, 1);
unsets.append(DURATION, 1);
}
}
if (updates.isEndStateChanged) {
sets.append(END_STATE, workflowInstance.getEndState());
}
// MongoDB can't combine updates of array elements together with
// adding elements to that array. That's why we overwrite the whole
// activity instance array when an update happened in there.
// We do archive the ended (and joined) activity instances into a separate collection
// that doesn't have to be loaded.
if (updates.isActivityInstancesChanged) {
BasicDBList dbActivityInstances = writeActiveActivityInstances(workflowInstance.activityInstances);
sets.append(ACTIVITY_INSTANCES, dbActivityInstances);
}
if (updates.isVariableInstancesChanged) {
writeVariableInstances(sets, workflowInstance);
}
if (updates.isWorkChanged) {
List<String> work = writeWork(workflowInstance.work);
if (work!=null) {
sets.put(WORK, work);
} else {
unsets.put(WORK, 1);
}
}
if (updates.isAsyncWorkChanged) {
List<String> workAsync = writeWork(workflowInstance.workAsync);
if (workAsync!=null) {
sets.put(WORK_ASYNC, workAsync);
} else {
unsets.put(WORK_ASYNC, 1);
}
}
if (updates.isNextActivityInstanceIdChanged) {
sets.put(NEXT_ACTIVITY_INSTANCE_ID, workflowInstance.nextActivityInstanceId);
}
if (updates.isNextVariableInstanceIdChanged) {
sets.put(NEXT_VARIABLE_INSTANCE_ID, workflowInstance.nextVariableInstanceId);
}
if (updates.isLockChanged) {
// a lock is only removed
unsets.put(LOCK, 1);
}
if (updates.isJobsChanged) {
List<BasicDBObject> dbJobs = writeJobs(workflowInstance.jobs);
if (dbJobs!=null) {
sets.put(JOBS, dbJobs);
} else {
unsets.put(JOBS, 1);
}
}
if (updates.isPropertiesChanged) {
if (workflowInstance.properties != null && workflowInstance.properties.size() > 0)
sets.append(PROPERTIES, new BasicDBObject(workflowInstance.getProperties()));
else
unsets.append(PROPERTIES, 1);
}
if (!sets.isEmpty()) {
update.append("$set", sets);
}
if (!unsets.isEmpty()) {
update.append("$unset", unsets);
}
if (!update.isEmpty()) {
workflowInstancesCollection.update("flush-workflow-instance", query, update, false, false);
}
// reset the update tracking as all changes have been saved
workflowInstance.trackUpdates(false);
}
@Override
public void flushAndUnlock(WorkflowInstanceImpl workflowInstance) {
workflowInstance.removeLock();
flush(workflowInstance);
workflowInstance.notifyUnlockListeners();
}
@Override
public List<WorkflowInstanceImpl> findWorkflowInstances(WorkflowInstanceQuery query) {
BasicDBObject dbQuery = createDbQuery(query);
return findWorkflowInstances(dbQuery);
}
public List<WorkflowInstanceImpl> findWorkflowInstances(BasicDBObject dbQuery) {
DBCursor workflowInstanceCursor = workflowInstancesCollection.find("find-workflow-instance-impls", dbQuery);
List<WorkflowInstanceImpl> workflowInstances = new ArrayList<>();
while (workflowInstanceCursor.hasNext()) {
BasicDBObject dbWorkflowInstance = (BasicDBObject) workflowInstanceCursor.next();
WorkflowInstanceImpl workflowInstance = readWorkflowInstanceImpl(dbWorkflowInstance);
workflowInstances.add(workflowInstance);
}
return workflowInstances;
}
@Override
public void deleteWorkflowInstances(WorkflowInstanceQuery workflowInstanceQuery) {
BasicDBObject query = createDbQuery(workflowInstanceQuery);
workflowInstancesCollection.remove("delete-workflow-instances", query);
}
@Override
public void deleteAllWorkflowInstances() {
workflowInstancesCollection.remove("delete-workflow-instances-unchecked", new BasicDBObject(), false);
}
protected BasicDBObject createDbQuery(WorkflowInstanceQuery query) {
if (query == null) {
query = new WorkflowInstanceQuery();
}
BasicDBObject dbQuery = new BasicDBObject();
if (query.getWorkflowInstanceId() != null) {
dbQuery.append(_ID, new ObjectId(query.getWorkflowInstanceId().getInternal()));
}
if (query.getActivityId() != null) {
dbQuery.append(ACTIVITY_INSTANCES
, new BasicDBObject("$elemMatch", new BasicDBObject(ACTIVITY_ID, query.getActivityId())
.append(WORK_STATE, new BasicDBObject("$exists", true))));
}
if (query.getLockedBefore() != null) {
dbQuery.append(LOCK + "." + Lock.TIME, new BasicDBObject("$lt", query.getLockedBefore().toDate()));
}
return dbQuery;
}
public void saveWorkflowInstance(BasicDBObject dbWorkflowInstance) {
workflowInstancesCollection.save("save-workfow-instance", dbWorkflowInstance);
}
@Override
public WorkflowInstanceImpl getWorkflowInstanceImplById(WorkflowInstanceId workflowInstanceId) {
if (workflowInstanceId==null) {
return null;
}
DBObject query = createLockQuery();
query.put(_ID, new ObjectId(workflowInstanceId.getInternal()));
BasicDBObject dbWorkflowInstance = workflowInstancesCollection.findOne("get-workflow-instance", query);
if (dbWorkflowInstance==null) {
return null;
}
return readWorkflowInstanceImpl(dbWorkflowInstance);
}
@Override
public WorkflowInstanceImpl lockWorkflowInstance(WorkflowInstanceId workflowInstanceId) {
Exceptions.checkNotNullParameter(workflowInstanceId, "workflowInstanceId");
DBObject query = createLockQuery();
query.put(_ID, new ObjectId(workflowInstanceId.getInternal()));
DBObject update = createLockUpdate();
DBObject retrieveFields = new BasicDBObject()
.append(ARCHIVED_ACTIVITY_INSTANCES, false);
BasicDBObject dbWorkflowInstance = workflowInstancesCollection.findAndModify("lock-workflow-instance", query, update, retrieveFields);
if (dbWorkflowInstance==null) {
return null;
}
WorkflowInstanceImpl workflowInstance = readWorkflowInstanceImpl(dbWorkflowInstance);
workflowInstance.trackUpdates(false);
return workflowInstance;
}
@Override
public void unlockWorkflowInstance(WorkflowInstanceImpl workflowInstance) {
if (workflowInstance!=null) {
ObjectId workflowInstanceId = new ObjectId(workflowInstance.id.getInternal());
// @formatter:off
workflowInstancesCollection.update("unlock-workflow-instance",
new Query()
._id(workflowInstanceId)
.get(),
new Update()
.unset(LOCK)
.get());
// @formatter:off
workflowInstance.notifyUnlockListeners();
}
}
public DBObject createLockQuery() {
return BasicDBObjectBuilder.start()
.push(LOCK)
.add("$exists", false)
.pop()
.get();
}
public DBObject createLockUpdate() {
return BasicDBObjectBuilder.start()
.push("$set")
.push(LOCK)
.add(Lock.TIME, Time.now().toDate())
.add(Lock.OWNER, workflowEngine.getId())
.pop()
.pop()
.get();
}
@Override
public WorkflowInstanceImpl lockWorkflowInstanceWithJobsDue() {
DBObject query = createLockQuery();
query.put(JobFields.DONE, new BasicDBObject("$exists", false));
query.put(JOBS + "." + JobFields.DUE_DATE, new BasicDBObject("$lte", Time.now().toDate()));
DBObject update = createLockUpdate();
DBObject retrieveFields = new BasicDBObject()
.append(ARCHIVED_ACTIVITY_INSTANCES, false);
BasicDBObject dbWorkflowInstance = workflowInstancesCollection.findAndModify("lock-workflow-instance", query, update, retrieveFields, new BasicDBObject(START, 1), false, true, false);
if (dbWorkflowInstance==null) {
return null;
}
WorkflowInstanceImpl workflowInstance = readWorkflowInstanceImpl(dbWorkflowInstance);
workflowInstance.trackUpdates(false);
return workflowInstance;
}
public BasicDBObject writeWorkflowInstance(WorkflowInstanceImpl workflowInstance) {
BasicDBObject dbWorkflowInstance = mongoMapper.write(workflowInstance.toWorkflowInstance(true));
if (storeWorkflowIdsAsStrings) {
writeString(dbWorkflowInstance, WORKFLOW_ID, workflowInstance.workflow.id.getInternal());
}
writeLongOpt(dbWorkflowInstance, NEXT_ACTIVITY_INSTANCE_ID, workflowInstance.nextActivityInstanceId);
writeLongOpt(dbWorkflowInstance, NEXT_VARIABLE_INSTANCE_ID, workflowInstance.nextVariableInstanceId);
writeObjectOpt(dbWorkflowInstance, WORK, writeWork(workflowInstance.work));
writeObjectOpt(dbWorkflowInstance, WORK_ASYNC, writeWork(workflowInstance.workAsync));
writeObjectOpt(dbWorkflowInstance, JOBS, writeJobs(workflowInstance.jobs));
writeObjectOpt(dbWorkflowInstance, LOCK, writeLock(workflowInstance.lock));
return dbWorkflowInstance;
}
protected List<String> writeWork(Queue<ActivityInstanceImpl> workQueue) {
List<String> workActivityInstanceIds = null;
if (workQueue!=null && !workQueue.isEmpty()) {
workActivityInstanceIds = new ArrayList<>();
for (ActivityInstanceImpl workActivityInstance: workQueue) {
workActivityInstanceIds.add(workActivityInstance.id);
}
}
return workActivityInstanceIds;
}
public WorkflowInstance readWorkflowInstance(BasicDBObject dbWorkflowInstance) {
return mongoMapper.read(dbWorkflowInstance, WorkflowInstance.class);
}
public WorkflowInstanceImpl readWorkflowInstanceImpl(BasicDBObject dbWorkflowInstance) {
if (dbWorkflowInstance==null) {
return null;
}
WorkflowInstanceImpl workflowInstance = new WorkflowInstanceImpl();
workflowInstance.id = readWorkflowInstanceId(dbWorkflowInstance, _ID);
workflowInstance.businessKey = readString(dbWorkflowInstance, BUSINESS_KEY);
Object workflowIdObject = readObject(dbWorkflowInstance, WORKFLOW_ID);
// workflowId is ObjectId in the MongoConfiguration
// workflowId is String in the MongoMemoryConfiguration
// The code is written to work dynamically (and not according to the
// configuration field storeWorkflowIdsAsStrings) because the test
// suite cleanup might encounter workflow instances created by the other engine
WorkflowId workflowId = new WorkflowId(workflowIdObject.toString());
WorkflowImpl workflow = workflowEngine.getWorkflowImpl(workflowId);
if (workflow==null) {
throw new RuntimeException("No workflow for instance "+workflowInstance.id);
}
workflowInstance.workflow = workflow;
workflowInstance.workflowInstance = workflowInstance;
workflowInstance.scope = workflow;
workflowInstance.configuration = configuration;
workflowInstance.callingWorkflowInstanceId = readWorkflowInstanceId(dbWorkflowInstance, CALLING_WORKFLOW_INSTANCE_ID);
workflowInstance.callingActivityInstanceId = readString(dbWorkflowInstance, CALLING_ACTIVITY_INSTANCE_ID);
workflowInstance.nextActivityInstanceId = readLong(dbWorkflowInstance, NEXT_ACTIVITY_INSTANCE_ID);
workflowInstance.nextVariableInstanceId = readLong(dbWorkflowInstance, NEXT_VARIABLE_INSTANCE_ID);
workflowInstance.lock = readLock((BasicDBObject) dbWorkflowInstance.get(LOCK));
workflowInstance.jobs = readJobs(readList(dbWorkflowInstance, JOBS));
Map<ActivityInstanceImpl, String> allActivityIds = new HashMap<>();
readScopeImpl(workflowInstance, dbWorkflowInstance, allActivityIds);
resolveActivityReferences(workflowInstance, workflow, allActivityIds);
workflowInstance.work = readWork(dbWorkflowInstance, WORK, workflowInstance);
workflowInstance.workAsync = readWork(dbWorkflowInstance, WORK_ASYNC, workflowInstance);
workflowInstance.properties = readObjectMap(dbWorkflowInstance, PROPERTIES);
workflowInstance.setProperty(ORGANIZATION_ID, readObject(dbWorkflowInstance, ORGANIZATION_ID));
copyProperties(dbWorkflowInstance, workflowInstance);
return workflowInstance;
}
/**
* Reads database fields (that do not have Java fields) and copies them to workflow instance properties.
* This makes it possible to write non-standard fields to the database and read them from properties.
*/
private void copyProperties(BasicDBObject dbWorkflowInstance, WorkflowInstanceImpl workflowInstance) {
if (dbWorkflowInstance == null || workflowInstance == null) {
return;
}
Set<String> invalidPropertyKeys = Extensible.getInvalidPropertyKeys(WorkflowInstance.class);
// Map<String,?> mappedBeanFields = mongoMapper.write(workflowInstance.toWorkflowInstance());
for (String fieldName : dbWorkflowInstance.keySet()) {
boolean property = !invalidPropertyKeys.contains(fieldName);
if (property) {
workflowInstance.setProperty(fieldName, dbWorkflowInstance.get(fieldName));
}
}
}
protected void readScopeImpl(ScopeInstanceImpl scopeInstance, BasicDBObject dbScopeInstance, Map<ActivityInstanceImpl, String> allActivityIds) {
scopeInstance.start = readTime(dbScopeInstance, START);
scopeInstance.end = readTime(dbScopeInstance, END);
scopeInstance.endState = readString(dbScopeInstance, END_STATE);
scopeInstance.duration = readLong(dbScopeInstance, DURATION);
readActivityInstances(scopeInstance, dbScopeInstance, allActivityIds);
readVariableInstances(scopeInstance, dbScopeInstance);
}
protected void readActivityInstances(ScopeInstanceImpl scopeInstance, BasicDBObject dbScopeInstance, Map<ActivityInstanceImpl, String> allActivityIds) {
Map<Object, ActivityInstanceImpl> allActivityInstances = new LinkedHashMap<>();
List<BasicDBObject> dbActivityInstances = readList(dbScopeInstance, ACTIVITY_INSTANCES);
if (dbActivityInstances!=null) {
for (BasicDBObject dbActivityInstance: dbActivityInstances) {
ActivityInstanceImpl activityInstance = readActivityInstance(scopeInstance, dbActivityInstance, allActivityIds);
allActivityInstances.put(activityInstance.id, activityInstance);
String activityId = readString(dbActivityInstance, ACTIVITY_ID);
allActivityIds.put(activityInstance, activityId);
scopeInstance.addActivityInstance(activityInstance);
}
}
}
protected ActivityInstanceImpl readActivityInstance(ScopeInstanceImpl parent, BasicDBObject dbActivityInstance, Map<ActivityInstanceImpl, String> allActivityIds) {
ActivityInstanceImpl activityInstance = new ActivityInstanceImpl();
activityInstance.id = readId(dbActivityInstance, ID);
activityInstance.calledWorkflowInstanceId = readWorkflowInstanceId(dbActivityInstance, CALLED_WORKFLOW_INSTANCE_ID);
activityInstance.workState = readString(dbActivityInstance, WORK_STATE);
activityInstance.configuration = configuration;
activityInstance.parent = parent;
activityInstance.workflow = parent.workflow;
activityInstance.workflowInstance = parent.workflowInstance;
readScopeImpl(activityInstance, dbActivityInstance, allActivityIds);
return activityInstance;
}
protected void resolveActivityReferences(ScopeInstanceImpl scopeInstance, ScopeImpl scope, Map<ActivityInstanceImpl, String> allActivityIds) {
if (scopeInstance.activityInstances!=null) {
for (ActivityInstanceImpl activityInstance : scopeInstance.activityInstances) {
String activityId = allActivityIds.get(activityInstance);
ActivityImpl activity = scope.findActivityByIdLocal(activityId);
activityInstance.activity = activity;
activityInstance.scope = activity;
ScopeImpl nestedScope = activity.isMultiInstance() ? activity.parent : activity;
resolveActivityReferences(activityInstance, nestedScope, allActivityIds);
}
}
}
@SuppressWarnings("unchecked")
protected Queue<ActivityInstanceImpl> readWork(BasicDBObject dbWorkflowInstance, String fieldName, WorkflowInstanceImpl workflowInstance) {
Queue<ActivityInstanceImpl> workQueue = null;
List<String> workActivityInstanceIds = (List<String>) dbWorkflowInstance.get(fieldName);
if (workActivityInstanceIds!=null) {
workQueue = new LinkedList<>();
for (String workActivityInstanceId: workActivityInstanceIds) {
ActivityInstanceImpl workActivityInstance = workflowInstance.findActivityInstance(workActivityInstanceId);
workQueue.add(workActivityInstance);
}
}
return workQueue;
}
private void readVariableInstances(ScopeInstanceImpl parent, BasicDBObject dbWorkflowInstance) {
List<BasicDBObject> dbVariableInstances = readList(dbWorkflowInstance, VARIABLE_INSTANCES);
if (dbVariableInstances!=null && !dbVariableInstances.isEmpty()) {
for (BasicDBObject dbVariableInstance: dbVariableInstances) {
VariableInstance variableInstance = mongoMapper.read(dbVariableInstance, VariableInstance.class);
VariableInstanceImpl variableInstanceImpl = new VariableInstanceImpl();
variableInstanceImpl.id = variableInstance.getId();
String variableId = variableInstance.getVariableId();
variableInstanceImpl.variable = findVariableByIdRecurseParents(parent.scope, variableId);
if (variableInstanceImpl.variable!=null) {
variableInstanceImpl.type = variableInstanceImpl.variable.type;
} else {
variableInstanceImpl.variable = new VariableImpl();
DataType type = variableInstance.getType();
if (type!=null) {
variableInstanceImpl.type = dataTypeService.createDataType(type);
}
}
variableInstanceImpl.value = variableInstance.getValue();
variableInstanceImpl.configuration = configuration;
variableInstanceImpl.workflowInstance = parent.workflowInstance;
variableInstanceImpl.parent = parent;
variableInstanceImpl.workflow = parent.workflow;
parent.addVariableInstance(variableInstanceImpl);
}
}
}
protected VariableImpl findVariableByIdRecurseParents(ScopeImpl scope, String variableId) {
if (scope==null) {
return null;
}
VariableImpl variable = scope.findVariableByIdLocal(variableId);
if (variable!=null) {
return variable;
}
return findVariableByIdRecurseParents(scope.parent, variableId);
}
protected BasicDBObject writeLock(LockImpl lock) {
if (lock==null) {
return null;
}
BasicDBObject dbLock = new BasicDBObject();
writeTimeOpt(dbLock, Lock.TIME, lock.time);
writeObjectOpt(dbLock, Lock.OWNER, lock.owner);
return dbLock;
}
protected LockImpl readLock(BasicDBObject dbLock) {
if (dbLock==null) {
return null;
}
LockImpl lock = new LockImpl();
lock.owner = readString(dbLock, Lock.OWNER);
lock.time = readTime(dbLock, Lock.TIME);
return lock;
}
/** writes the given activityInstances to db format, preserving the hierarchy and including the workState. */
protected BasicDBList writeActiveActivityInstances(List<ActivityInstanceImpl> activityInstances) {
if (activityInstances==null || activityInstances.isEmpty()) {
return null;
}
BasicDBList dbActivityInstances = new BasicDBList();
for (ActivityInstanceImpl activityInstance: activityInstances) {
BasicDBObject dbActivityInstance = mongoMapper.write(activityInstance.toActivityInstance(true));
dbActivityInstances.add(dbActivityInstance);
}
return dbActivityInstances;
}
/** recursively removes the archivable activities from the scopeInstance, serializes them to DB format and adds them to the dbArchivedActivityInstances as a flat list */
protected void collectArchivedActivities(ScopeInstanceImpl scopeInstance, BasicDBList dbArchivedActivityInstances) {
if (scopeInstance.activityInstances!=null) {
List<ActivityInstanceImpl> activeActivityInstances = new ArrayList<>();
for (ActivityInstanceImpl activityInstance: scopeInstance.activityInstances) {
if (activityInstance.workState!=null) { // null means ready to be archived
activeActivityInstances.add(activityInstance);
} else {
activityInstance.activityInstances = null;
BasicDBObject dbActivity = mongoMapper.write(activityInstance.toActivityInstance());
String parentId = (activityInstance.parent.isWorkflowInstance() ? null : ((ActivityInstanceImpl) activityInstance.parent).id);
writeString(dbActivity, PARENT, parentId);
dbArchivedActivityInstances.add(dbActivity);
}
collectArchivedActivities(activityInstance, dbArchivedActivityInstances);
}
scopeInstance.activityInstances = activeActivityInstances;
}
}
protected void writeVariableInstances(BasicDBObject dbScope, ScopeInstanceImpl scope) {
if (scope.variableInstances!=null) {
for (VariableInstanceImpl variableInstanceImpl: scope.variableInstances) {
VariableInstance variableInstance = variableInstanceImpl.toVariableInstance();
BasicDBObject dbVariable = mongoMapper.write(variableInstance);
writeListElementOpt(dbScope, VARIABLE_INSTANCES, dbVariable);
}
}
}
protected List<BasicDBObject> writeJobs(List<Job> jobs) {
if (jobs==null || jobs.isEmpty()) {
return null;
}
List<BasicDBObject> dbJobs = new ArrayList<BasicDBObject>();
for (Job job: jobs) {
BasicDBObject dbJob = mongoJobsStore.writeJob(job);
dbJobs.add(dbJob);
}
return dbJobs;
}
protected List<Job> readJobs(List<BasicDBObject> dbJobs) {
if (dbJobs==null || dbJobs.isEmpty()) {
return null;
}
List<Job> jobs = new ArrayList<>();
for (BasicDBObject dbJob: dbJobs) {
Job job = mongoJobsStore.readJob(dbJob);
jobs.add(job);
}
return jobs;
}
public LinkedHashMap<WorkflowInstanceId, WorkflowInstanceImpl> findWorkflowInstanceMap(Collection<ObjectId> workflowInstanceIds) {
LinkedHashMap<WorkflowInstanceId, WorkflowInstanceImpl> workflowInstanceMap = new LinkedHashMap<>();
if (workflowInstanceIds!=null && !workflowInstanceIds.isEmpty()) {
Query query = new Query()._ids(workflowInstanceIds);
DBCursor workflowInstanceCursor = workflowInstancesCollection.find("find-workflow-instance", query.get());
while (workflowInstanceCursor.hasNext()) {
BasicDBObject dbWorkflowInstance = (BasicDBObject) workflowInstanceCursor.next();
WorkflowInstanceImpl workflowInstance = readWorkflowInstanceImpl(dbWorkflowInstance);
workflowInstanceMap.put(workflowInstance.getId(), workflowInstance);
}
}
return workflowInstanceMap;
}
public MongoCollection getWorkflowInstancesCollection() {
return workflowInstancesCollection;
}
}