/*
* 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.memory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import com.effektif.workflow.api.model.WorkflowInstanceId;
import com.effektif.workflow.api.query.WorkflowInstanceQuery;
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.job.Job;
import com.effektif.workflow.impl.util.Lists;
import com.effektif.workflow.impl.util.Time;
import com.effektif.workflow.impl.workflowinstance.LockImpl;
import com.effektif.workflow.impl.workflowinstance.WorkflowInstanceImpl;
/**
* @author Tom Baeyens
*/
public class MemoryWorkflowInstanceStore implements WorkflowInstanceStore, Brewable {
private static final Logger log = WorkflowEngineImpl.log;
protected String workflowEngineId;
protected Map<WorkflowInstanceId, WorkflowInstanceImpl> workflowInstances;
protected Set<WorkflowInstanceId> lockedWorkflowInstanceIds;
public MemoryWorkflowInstanceStore() {
}
@Override
public void brew(Brewery brewery) {
initializeWorkflowInstances();
this.workflowEngineId = brewery.get(WorkflowEngineImpl.class).id;
}
protected void initializeWorkflowInstances() {
this.workflowInstances = new ConcurrentHashMap<>();
this.lockedWorkflowInstanceIds = Collections.newSetFromMap(new ConcurrentHashMap<WorkflowInstanceId, Boolean>());
}
@Override
public WorkflowInstanceId generateWorkflowInstanceId() {
return new WorkflowInstanceId(UUID.randomUUID().toString());
}
@Override
public void insertWorkflowInstance(WorkflowInstanceImpl workflowInstance) {
workflowInstances.put(workflowInstance.id, workflowInstance);
}
@Override
public void flush(WorkflowInstanceImpl workflowInstance) {
}
@Override
public void flushAndUnlock(WorkflowInstanceImpl workflowInstance) {
lockedWorkflowInstanceIds.remove(workflowInstance.id);
workflowInstance.removeLock();
workflowInstance.notifyUnlockListeners();
}
@Override
public void unlockWorkflowInstance(WorkflowInstanceImpl workflowInstance) {
if (workflowInstance!=null) {
workflowInstance.removeLock();
lockedWorkflowInstanceIds.remove(workflowInstance.id);
workflowInstance.notifyUnlockListeners();
}
}
@Override
public List<WorkflowInstanceImpl> findWorkflowInstances(WorkflowInstanceQuery query) {
if (query.getWorkflowInstanceId()!=null) {
WorkflowInstanceImpl workflowInstance = workflowInstances.get(query.getWorkflowInstanceId());
if (workflowInstance!=null && workflowInstance.isIncluded(query)) {
return Lists.of(workflowInstance);
} else {
return Collections.EMPTY_LIST;
}
}
List<WorkflowInstanceImpl> workflowInstances = new ArrayList<>();
Iterator<WorkflowInstanceImpl> iterator = this.workflowInstances.values().iterator();
int limit = query.getLimit()!=null ? query.getLimit() : Integer.MAX_VALUE;
while (iterator.hasNext() && workflowInstances.size()<limit) {
WorkflowInstanceImpl workflowInstance = iterator.next();
if (workflowInstance.isIncluded(query)) {
workflowInstances.add(workflowInstance);
}
}
return workflowInstances;
}
@Override
public void deleteWorkflowInstances(WorkflowInstanceQuery workflowInstanceQuery) {
for (WorkflowInstanceImpl workflowInstance: findWorkflowInstances(workflowInstanceQuery)) {
workflowInstances.remove(workflowInstance.id);
}
}
@Override
public WorkflowInstanceImpl getWorkflowInstanceImplById(WorkflowInstanceId workflowInstanceId) {
if (workflowInstanceId==null) {
return null;
}
return workflowInstances.get(workflowInstanceId);
}
@Override
public WorkflowInstanceImpl lockWorkflowInstance(WorkflowInstanceId workflowInstanceId) {
WorkflowInstanceQuery query = new WorkflowInstanceQuery()
.workflowInstanceId(workflowInstanceId);
query.setLimit(1);
List<WorkflowInstanceImpl> workflowInstances = findWorkflowInstances(query);
if (workflowInstances==null || workflowInstances.isEmpty()) {
throw new RuntimeException("Process instance doesn't exist");
}
WorkflowInstanceImpl workflowInstance = workflowInstances.get(0);
workflowInstanceId = workflowInstance.id;
lockWorkflowInstance(workflowInstance);
return workflowInstance;
}
@Override
public WorkflowInstanceImpl lockWorkflowInstanceWithJobsDue() {
Iterator<WorkflowInstanceImpl> iterator = this.workflowInstances.values().iterator();
while (iterator.hasNext()) {
WorkflowInstanceImpl workflowInstance = iterator.next();
if (workflowInstance.jobs!=null) {
for (Job job: workflowInstance.jobs) {
if (job.isDue()) {
lockWorkflowInstance(workflowInstance);
return workflowInstance;
}
}
}
}
return null;
}
public synchronized void lockWorkflowInstance(WorkflowInstanceImpl workflowInstance) {
WorkflowInstanceId workflowInstanceId = workflowInstance.getId();
if (lockedWorkflowInstanceIds.contains(workflowInstanceId)) {
throw new RuntimeException("Process instance "+workflowInstanceId+" is already locked");
}
lockedWorkflowInstanceIds.add(workflowInstanceId);
LockImpl lock = new LockImpl();
lock.setTime(Time.now());
lock.setOwner(workflowEngineId);
workflowInstance.setLock(lock);
if (log.isDebugEnabled()) {
log.debug("Locked process instance "+workflowInstanceId);
}
}
@Override
public void deleteAllWorkflowInstances() {
initializeWorkflowInstances();
}
}