/* * 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.framework.core; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.activiti.engine.impl.pvm.PvmProcessDefinition; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.nask.hsn2.bus.operations.JobStatus; import pl.nask.hsn2.bus.operations.TaskErrorReasonType; import pl.nask.hsn2.framework.workflow.engine.ProcessBasedWorkflowDescriptor; import pl.nask.hsn2.framework.workflow.engine.WorkflowAlreadyDeployedException; import pl.nask.hsn2.framework.workflow.engine.WorkflowAlreadyRegisteredException; import pl.nask.hsn2.framework.workflow.engine.WorkflowDescriptor; import pl.nask.hsn2.framework.workflow.engine.WorkflowDescriptorManager; 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.engine.WorkflowNotRegisteredException; import pl.nask.hsn2.framework.workflow.hwl.Workflow; import pl.nask.hsn2.framework.workflow.job.WorkflowJobInfo; import pl.nask.hsn2.framework.workflow.repository.WorkflowRepoException; import pl.nask.hsn2.framework.workflow.repository.WorkflowRepository; import pl.nask.hsn2.framework.workflow.repository.WorkflowVersionInfo; import pl.nask.hsn2.workflow.parser.HWLParser; import pl.nask.hsn2.workflow.parser.WorkflowParseException; import pl.nask.hsn2.workflow.parser.WorkflowParser; import pl.nask.hsn2.workflow.parser.WorkflowSyntaxException; import pl.nask.hsn2.workflow.parser.WorkflowValidationException; import pl.nask.hsn2.workflow.parser.WorkflowValidator; public final class WorkflowManager { private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowManager.class); private static final WorkflowManager INSTANCE = new WorkflowManager(); private WorkflowParser parser = new HWLParser(); private WorkflowDescriptorManager<? extends WorkflowDescriptor> workflowDefinitionManager; private WorkflowEngine workflowEngine; private WorkflowRepository repository; private WorkflowValidator validator; private int maximumRunningJobLimit = 0; // unlimited by default private WorkflowManager() { } public static WorkflowManager getInstance() { if (INSTANCE.workflowEngine == null) { throw new IllegalStateException("WorkflowManager not configured properly: workflowEngine is missing"); } if (INSTANCE.workflowDefinitionManager == null) { throw new IllegalStateException("WorkflowManager not configured properly: workflowDefinitionManager is missing"); } if (INSTANCE.validator == null) { throw new IllegalStateException("WorkflowManager not configured properly: validator is not initialized (setKnownServiceNames was not called)"); } if (INSTANCE.repository == null) { throw new IllegalStateException("WorkflowManager not configured properly: repository is not initialized"); } return INSTANCE; } /** * Initializes workflow validator * * @param serviceNames */ public static void setKnownServiceNames(String[] serviceNames) { INSTANCE.validator = new WorkflowValidator(serviceNames.clone()); } /** * Starts a job (workflow process instance) for the given workflowId * * @param workflowName * @param workflowConfig * @return id of the started job * @throws WorkflowNotDeployedException * @throws WorkflowRepoException */ public long runJob(String workflowName, Map<String, Properties> workflowConfig, String workflowVersion) throws WorkflowEngineException, WorkflowRepoException { String version = getCurrentVersionIfEmpty(workflowName, workflowVersion); WorkflowDescriptor w = workflowDefinitionManager.get(version); if (w == null) { w = deployWorkflow(workflowName, version); } return workflowEngine.startJob(w, "main", workflowConfig); } public String getCurrentVersionIfEmpty(String workflowName, String workflowVersion) throws WorkflowRepoException { if (workflowVersion != null && !"".equals(workflowVersion)){ return workflowVersion; } List<WorkflowVersionInfo> versions = repository.getVersions(workflowName); if (versions == null || versions.isEmpty()) { throw new WorkflowRepoException("Workflow cannot be found: " + workflowName); } else { return versions.get(0).getVersion(); } } public WorkflowVersionInfo getWorkflowVersionInfo(String workflowName, String workflowVersion) throws WorkflowRepoException { List<WorkflowVersionInfo> versions = repository.getVersions(workflowName); if (workflowVersion == null || "".equals(workflowVersion)) { // Is there is any version of the workflow? if (versions == null || versions.isEmpty()) { throw new WorkflowRepoException("Workflow cannot be found: " + workflowName); } // take HEAD return versions.get(0); } else { for (WorkflowVersionInfo v : versions) { if (workflowVersion.equals(v.getVersion())) { return v; } } throw new WorkflowRepoException( new StringBuffer("Version '") .append(workflowVersion).append("' ") .append("not found for workflow name '") .append(workflowName).append("'.").toString()); } } private WorkflowDescriptor deployWorkflow(String workflowName, String version) throws WorkflowNotDeployedException { InputStream is = null; try { is = repository.getWorkflowFile(workflowName, version); Workflow workflow = parser.parse(is); WorkflowDescriptor desc = workflowDefinitionManager.createDescritor(version, workflowName, workflow); workflowDefinitionManager.registerWorkflow(desc); validator.validateAll(workflow); workflowDefinitionManager.deploy(version); return desc; } catch (WorkflowRepoException e) { LOGGER.error("Couldn't retrieve workflow from the repository, name={}, version={}", workflowName, version); throw new WorkflowNotDeployedException(workflowName, e); } catch (WorkflowSyntaxException e) { LOGGER.error("Workflow syntax invalid: {}, name={}, version={}", new Object[]{e.getMessage(), workflowName, version}); throw new WorkflowNotDeployedException("Workflow syntax invalid:" + workflowName, e); } catch (WorkflowParseException e) { LOGGER.error("Workflow parse error: {}, name={}, version={}", new Object[]{e.getMessage(), workflowName, version}); throw new WorkflowNotDeployedException("Workflow parse error:" + workflowName, e); } catch (WorkflowValidationException e) { LOGGER.error("Workflow validation error: {}, name={}, version={}", new Object[] {e.getMessage(), workflowName, version}); throw new WorkflowNotDeployedException("Workflow validation error:" + workflowName, e); } catch (WorkflowAlreadyRegisteredException e) { throw new WorkflowNotDeployedException(workflowName, e); } catch (WorkflowAlreadyDeployedException e) { throw new WorkflowNotDeployedException(workflowName, e); } catch (WorkflowNotRegisteredException e) { throw new WorkflowNotDeployedException(workflowName, e); } finally { IOUtils.closeQuietly(is); } } public List<WorkflowDescriptor> getWorkflowDefinitions(boolean enabledOnly) throws WorkflowRepoException { Set<String> names = new TreeSet<>(); List<WorkflowDescriptor> descriptors = new ArrayList<>(workflowDefinitionManager.getWorkflowDefinitions(enabledOnly)); for (WorkflowDescriptor descriptor : descriptors) { names.add(descriptor.getName()); } if (!enabledOnly) { List<String> workflowNames = repository.listWorkflowNames(); names.addAll(workflowNames); } List<WorkflowDescriptor> workflows = new ArrayList<>(); for (String name : names) { ProcessBasedWorkflowDescriptor<PvmProcessDefinition> workflow = new ProcessBasedWorkflowDescriptor<PvmProcessDefinition>(null, name, null); workflows.add(workflow); } return workflows; } public WorkflowDescriptor getWorkflowDefinition( String workflowName, String workflowVersion) throws WorkflowRepoException { String version = getCurrentVersionIfEmpty(workflowName, workflowVersion); WorkflowDescriptor w = workflowDefinitionManager.get(version); if (w != null) { return w; } InputStream is = null; Workflow workflow = null; try { is = repository.getWorkflowFile(workflowName, version); workflow = parser.parse(is); } catch (WorkflowSyntaxException e) { // not important, in this case workflow will be null } catch (WorkflowParseException e) { // not important, in this case workflow will be null } finally { IOUtils.closeQuietly(is); } return new ProcessBasedWorkflowDescriptor<PvmProcessDefinition>(version, workflowName, workflow); } public void taskAccepted(long jobId, int requestId) { workflowEngine.taskAccepted(jobId, requestId); } public void taskCompleted(long jobId, int requestId, Set<Long> newObjects) { workflowEngine.taskCompleted(jobId, requestId, newObjects); } public List<WorkflowJobInfo> getWorkflowJobs() { return workflowEngine.getJobs(); } public WorkflowJobInfo getJobInfo(long jobId) { return workflowEngine.getJobInfo(jobId); } public void taskError(long jobId, int requestId, TaskErrorReasonType reason, String description) { workflowEngine.taskError(jobId, requestId, reason, description); } public static void setWorkflowDefinitionManager(WorkflowDescriptorManager<? extends WorkflowDescriptor> mgr) { INSTANCE.workflowDefinitionManager = mgr; } public static void setWorkflowRepository(WorkflowRepository repo) { INSTANCE.repository = repo; } public static void setWorkflowEngine(WorkflowEngine engine) { INSTANCE.workflowEngine = engine; } public static void setMaximumRunningJobLimit(int maximumRunningJobLimit) { INSTANCE.maximumRunningJobLimit = maximumRunningJobLimit; } public void resume(long jobId) { workflowEngine.resume(jobId); } public String uploadWorkflow(String name, InputStream inputStream, boolean override) throws WorkflowRepoException { if (!override) { // check if the repo contains any version of the workflow List<WorkflowVersionInfo> versions = repository.getVersions(name); if (!versions.isEmpty()) { throw new IllegalArgumentException("Cannot override workflow with name: " + name); } } WorkflowVersionInfo info = repository.saveWorkflow(name, inputStream); return info.getVersion(); } public List<WorkflowVersionInfo> getWorkflowHistory(String workflowName) throws WorkflowRepoException { return repository.getVersions(workflowName); } public InputStream downloadWorkflow(String workflowName, String version) throws WorkflowRepoException{ return repository.getWorkflowFile(workflowName, getCurrentVersionIfEmpty(workflowName, version)); } public boolean isMaximumProcessingJobsLimitExeeded() { if (this.maximumRunningJobLimit > 0 && workflowEngine.getJobsCount(JobStatus.PROCESSING)>=this.maximumRunningJobLimit) { return true; } return false; } public void jobCancel(long id){ workflowEngine.cancelJob(id); } }