package com.constellio.model.services.workflows;
import java.util.List;
import org.joda.time.LocalDateTime;
import com.constellio.data.utils.TimeProvider;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.WorkflowTask;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchema;
import com.constellio.model.entities.schemas.MetadataSchemaTypes;
import com.constellio.model.entities.workflows.definitions.AllUsersSelector;
import com.constellio.model.entities.workflows.definitions.BPMNProperty;
import com.constellio.model.entities.workflows.definitions.WorkflowDefinition;
import com.constellio.model.entities.workflows.definitions.WorkflowRouting;
import com.constellio.model.entities.workflows.definitions.WorkflowRoutingDestination;
import com.constellio.model.entities.workflows.definitions.WorkflowServiceTask;
import com.constellio.model.entities.workflows.definitions.WorkflowUserTask;
import com.constellio.model.entities.workflows.execution.WorkflowExecution;
import com.constellio.model.services.collections.CollectionsListManager;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.records.RecordServicesException;
import com.constellio.model.services.records.RecordUtils;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.tasks.TaskServices;
import com.constellio.model.services.workflows.WorkflowExecutorRuntimeException.WorkflowExecutorRuntimeException_InvalidTaskId;
import com.constellio.model.services.workflows.WorkflowExecutorRuntimeException.WorkflowExecutorRuntimeException_InvalidTransaction;
import com.constellio.model.services.workflows.config.WorkflowsConfigManager;
import com.constellio.model.services.workflows.execution.WorkflowExecutionService;
public class WorkflowExecutor {
private final WorkflowExecutionService workflowExecutionService;
private final WorkflowsConfigManager workflowsConfigManager;
private final TaskServices taskServices;
private final CollectionsListManager collectionsListManager;
private final MetadataSchemasManager metadataSchemasManager;
private final RecordServices recordServices;
private final ModelLayerFactory modelLayerFactory;
public WorkflowExecutor(ModelLayerFactory modelLayerFactory) {
this.workflowExecutionService = modelLayerFactory.newWorkflowExecutionService();
this.workflowsConfigManager = modelLayerFactory.getWorkflowsConfigManager();
this.taskServices = modelLayerFactory.newTaskServices();
this.collectionsListManager = modelLayerFactory.getCollectionsListManager();
this.metadataSchemasManager = modelLayerFactory.getMetadataSchemasManager();
this.recordServices = modelLayerFactory.newRecordServices();
this.modelLayerFactory = modelLayerFactory;
}
public void handleWaitingWorkflows() {
for (String collection : collectionsListManager.getCollections()) {
for (WorkflowExecution workflow : workflowExecutionService.getNextWorkflowWaitingForSystemProcessing(collection)) {
WorkflowDefinition workflowDefinition = workflowsConfigManager
.getWorkflowDefinition(collection, workflow.getWorkflowDefinitionId());
handleWaitingWorkflow(workflow, workflowDefinition);
}
}
}
public void handleWaitingWorkflow(WorkflowExecution workflowExecution, WorkflowDefinition workflowDefinition) {
String currentTaskId = workflowExecution.getCurrentTaskId();
com.constellio.model.entities.workflows.definitions.WorkflowTask task = workflowDefinition.getTask(currentTaskId);
if (WorkflowRoutingDestination.DESTINATION_END.equals(currentTaskId)) {
workflowExecutionService.remove(workflowExecution);
} else if (task != null) {
if (task instanceof WorkflowUserTask) {
if (workflowExecution.getVariables().containsKey(TaskServices.TASK_DONE)) {
workflowExecution.getVariables().remove(TaskServices.TASK_DONE);
workflowExecutionService.addUpdateWorkflowExecution(workflowExecution);
workflowExecution.markCurrentTaskAsDoneAndStartNextTask(null, TimeProvider.getLocalDateTime(),
task.getSingleDestination());
handleWaitingWorkflow(workflowExecution, workflowDefinition);
} else {
WorkflowUserTask userTask = (WorkflowUserTask) task;
prepareUserTask(userTask, workflowExecution);
workflowExecutionService.addUpdateWorkflowExecution(workflowExecution);
workflowExecutionService
.markAsNotWaitingForSystem(workflowExecution.getCollection(), workflowExecution.getId());
}
} else if (task instanceof WorkflowServiceTask) {
WorkflowServiceTask serviceTask = (WorkflowServiceTask) task;
serviceTask.getAction().execute(workflowExecution, modelLayerFactory);
workflowExecution.markCurrentTaskAsDoneAndStartNextTask(null, TimeProvider.getLocalDateTime(),
task.getSingleDestination());
handleWaitingWorkflow(workflowExecution, workflowDefinition);
}
} else {
WorkflowRouting routing = workflowDefinition.getRouting(currentTaskId);
if (routing != null) {
String destination = routing.getDestination(workflowExecution);
workflowExecution.markCurrentTaskAsDoneAndStartNextTask(null, TimeProvider.getLocalDateTime(), destination);
handleWaitingWorkflow(workflowExecution, workflowDefinition);
} else {
throw new WorkflowExecutorRuntimeException_InvalidTaskId(currentTaskId);
}
}
}
public void prepareUserTask(WorkflowUserTask userTask, WorkflowExecution workflowExecution) {
MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(workflowExecution.getCollection());
MetadataSchema schema = types.getSchema(userTask.getTaskSchema());
Record newTaskRecord = recordServices.newRecordWithSchema(schema);
WorkflowTask task = taskServices.newRelativeTask(newTaskRecord, types);
fillTaskMetadatas(task, userTask, schema, workflowExecution);
Transaction transaction = new Transaction(newTaskRecord).setSkippingRequiredValuesValidation(true);
try {
recordServices.execute(transaction);
} catch (RecordServicesException e) {
throw new WorkflowExecutorRuntimeException_InvalidTransaction(e);
}
}
public void fillTaskMetadatas(WorkflowTask newTask, WorkflowUserTask userTask, MetadataSchema schema,
WorkflowExecution workflowExecution) {
setAssignCandidates(newTask, userTask.getUserSelector(), workflowExecution);
setDueDate(newTask, userTask.getDueDateInDays());
newTask.setWorkflowId(workflowExecution.getId());
newTask.setWorkflowRecordIds(workflowExecution.getRecordIds());
for (BPMNProperty property : userTask.getFields()) {
setMetadataBasedOnBPMNFields(newTask, property, schema, workflowExecution);
}
}
public void setAssignCandidates(WorkflowTask newTask, AllUsersSelector userSelector, WorkflowExecution execution) {
List<String> userIds = new RecordUtils()
.toWrappedRecordIdsList(userSelector.getCandidateUsers(execution, modelLayerFactory));
newTask.setAssignCandidates(userIds);
}
public void setDueDate(WorkflowTask newTask, int dueDateInDays) {
newTask.setDueDate(new LocalDateTime().plusDays(dueDateInDays));
}
public void setMetadataBasedOnBPMNFields(WorkflowTask newTask, BPMNProperty bpmnField, MetadataSchema schema,
WorkflowExecution workflowExecution) {
Metadata propertyMetadata = schema.getMetadata(bpmnField.getFieldId());
if (bpmnField.getExpressionValue() != null) {
newTask.set(propertyMetadata.getLocalCode(), bpmnField.getExpressionValue());
} else if (bpmnField.getVariableCode() != null) {
newTask.set(propertyMetadata.getLocalCode(), workflowExecution.getVariable(bpmnField.getVariableCode()));
}
}
}