/* * Copyright (c) NASK, NCSC * * This file is part of HoneySpider Network 2.1. * * This is a free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package pl.nask.hsn2.workflow.engine; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.activiti.engine.impl.pvm.PvmProcessDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.nask.hsn2.bus.api.BusManager; import pl.nask.hsn2.bus.operations.JobStatus; import pl.nask.hsn2.bus.operations.TaskErrorReasonType; import pl.nask.hsn2.framework.bus.FrameworkBus; import pl.nask.hsn2.framework.suppressor.JobSuppressorHelper; import pl.nask.hsn2.framework.suppressor.SingleThreadTasksSuppressor; import pl.nask.hsn2.framework.workflow.engine.ProcessBasedWorkflowDescriptor; import pl.nask.hsn2.framework.workflow.engine.WorkflowDescriptor; import pl.nask.hsn2.framework.workflow.engine.WorkflowEngine; import pl.nask.hsn2.framework.workflow.engine.WorkflowEngineException; import pl.nask.hsn2.framework.workflow.engine.WorkflowNotDeployedException; import pl.nask.hsn2.framework.workflow.job.MapWorkflowJobRepository; import pl.nask.hsn2.framework.workflow.job.WorkflowJob; import pl.nask.hsn2.framework.workflow.job.WorkflowJobInfo; import pl.nask.hsn2.framework.workflow.job.WorkflowJobRepository; import pl.nask.hsn2.framework.workflow.job.WorkflowJobRepositoryException; import pl.nask.hsn2.suppressor.JobSuppressorHelperImpl; import pl.nask.hsn2.utils.FileIdGenerator; import pl.nask.hsn2.utils.IdGenerator; public final class ActivitiWorkflowEngine implements WorkflowEngine { private static final Logger LOGGER = LoggerFactory.getLogger(ActivitiWorkflowEngine.class); private WorkflowJobRepository jobRepository; private final int tasksSuppressorThreshold; private final SingleThreadTasksSuppressor suppressor; public ActivitiWorkflowEngine(IdGenerator jobIdGenerator, SingleThreadTasksSuppressor suppressor, int tasksSuppressorThreshold) { this.jobRepository = new MapWorkflowJobRepository(jobIdGenerator); this.tasksSuppressorThreshold = tasksSuppressorThreshold; this.suppressor = suppressor; suppressor.start(); } public ActivitiWorkflowEngine(String jobSeqDir, SingleThreadTasksSuppressor suppressor, int tasksSuppressorThreshold) { FileIdGenerator idGenerator = new FileIdGenerator(); idGenerator.setSequenceFile(jobSeqDir, "jobId.seq"); this.jobRepository = new MapWorkflowJobRepository(idGenerator); this.tasksSuppressorThreshold = tasksSuppressorThreshold; this.suppressor = suppressor; } private long startJob(WorkflowJob job) throws WorkflowEngineException { long id = jobRepository.add(job); JobSuppressorHelper jobSuppressorHelper = null; if (suppressor.isEnabled()) { jobSuppressorHelper = new JobSuppressorHelperImpl(id, tasksSuppressorThreshold, suppressor); } job.start(id, jobSuppressorHelper); return id; } @Override public void taskAccepted(long jobId, int taskId) { LOGGER.debug("Got TaskAccepted (jobId={}, taskId={})", new Object[] { jobId, taskId }); try { WorkflowJob job = getJob(jobId); job.markTaskAsAccepted(taskId); } catch (WorkflowJobRepositoryException e) { LOGGER.warn("Got TaskAccepted for non existant job (jobId={}, taskId={})", jobId, taskId); ((FrameworkBus) BusManager.getBus()).jobFinishedReminder(jobId, null, taskId); } catch (Exception e) { LOGGER.warn("Error marking task {} in job {} as accepted", taskId, jobId); LOGGER.warn(e.getMessage(), e); } } @Override public void taskCompleted(long jobId, int requestId, Set<Long> newObjects) { LOGGER.debug("Got TaskCompleted (jobId={}, taskId={}, newObjects={})", new Object[] { jobId, requestId, newObjects }); try { WorkflowJob job = getJob(jobId); job.markTaskAsCompleted(requestId, newObjects); } catch (WorkflowJobRepositoryException e) { LOGGER.warn("Got TaskCompleted for non existant job, ignore (jobId={}, taskId={})", jobId, requestId); ((FrameworkBus) BusManager.getBus()).jobFinishedReminder(jobId, null, requestId); } } protected WorkflowJob getJob(long jobId) throws WorkflowJobRepositoryException { WorkflowJob job = jobRepository.get(jobId); if (job == null) { throw new WorkflowJobRepositoryException("No job with id=" + jobId); } return job; } @Override public List<WorkflowJobInfo> getJobs() { try { return jobRepository.getJobs(); } catch (WorkflowJobRepositoryException e) { LOGGER.error("Cannot get list of jobs from jobs repository.", e); return new ArrayList<WorkflowJobInfo>(); } } @Override public void taskError(long jobId, int requestId, TaskErrorReasonType reason, String description) { LOGGER.debug("Got TaskError (jobId={}, taskId={}, reason={}, errorMsg={})", new Object[] { jobId, requestId, reason, description }); try { WorkflowJob job = getJob(jobId); // TODO: policy dependent, should be configurable (by the workflow?) job.markTaskAsFailed(requestId, reason, description); } catch (WorkflowJobRepositoryException e) { LOGGER.warn("Got TaskError for non existant job, ignore (jobId={}, taskId={})", jobId, requestId); ((FrameworkBus) BusManager.getBus()).jobFinishedReminder(jobId, null, requestId); } } @Override public void resume(long jobId) { try { WorkflowJob job = getJob(jobId); job.resume(); } catch (WorkflowJobRepositoryException e) { LOGGER.error("Cannot resume job.", e); } } @Override public WorkflowJobInfo getJobInfo(long jobId) { WorkflowJobInfo job = null; try { job = jobRepository.get(jobId); } catch (WorkflowJobRepositoryException e) { LOGGER.error("Cannot find job id.", e); } return job; } @Override public long startJob(WorkflowDescriptor desc, String processName, Map<String, Properties> jobParameters) throws WorkflowEngineException { @SuppressWarnings("unchecked") ProcessBasedWorkflowDescriptor<PvmProcessDefinition> descriptor = (ProcessBasedWorkflowDescriptor<PvmProcessDefinition>) desc; if (!descriptor.isDeployed()) { throw new WorkflowNotDeployedException(descriptor.getId()); } else { PvmProcessDefinition def = descriptor.getProcessDefinition(processName); if (def == null) { throw new IllegalStateException("process " + processName + " not found in workflow " + descriptor.getId()); } WorkflowJob job = new ActivitiJob(def, descriptor, jobParameters); return startJob(job); } } @Override public long startJob(WorkflowDescriptor descriptor, String processName) throws WorkflowEngineException { return startJob(descriptor, processName, null); } @Override public long startJob(WorkflowDescriptor descriptor) throws WorkflowEngineException { return startJob(descriptor, "main", null); } @Override public int getJobsCount(JobStatus status) { int i = 0; for (WorkflowJobInfo info : this.getJobs()) { if (info.getStatus().equals(status)) { i++; } } return i; } @Override public void cancelJob(long jobId) { try { jobRepository.get(jobId).cancel(); } catch (WorkflowJobRepositoryException e) { LOGGER.error("Cannot find job id.", e); } } }