diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml
index e536595323..2fe31c568d 100644
--- a/config/alfresco/public-services-context.xml
+++ b/config/alfresco/public-services-context.xml
@@ -934,5 +934,41 @@
+
+
+
+
+
+
+ org.alfresco.service.cmr.workflow.WorkflowService
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${server.transaction.mode.default}
+
+
+
+
+
+
+ org.alfresco.service.cmr.workflow.WorkflowService
+
+
+ Workflow Service
+
+
diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml
index 1c49209e27..010c82854e 100644
--- a/config/alfresco/workflow-context.xml
+++ b/config/alfresco/workflow-context.xml
@@ -3,7 +3,29 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -12,14 +34,19 @@
-
-
+
+
+
+
+
+
+
diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java
index d7e846d429..eb018bc77a 100644
--- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java
+++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java
@@ -44,6 +44,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.cmr.view.ExporterService;
import org.alfresco.service.cmr.view.ImporterService;
+import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
@@ -313,4 +314,12 @@ public class ServiceDescriptorRegistry
{
return (ScriptService)getService(SCRIPT_SERVICE);
}
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.ServiceRegistry#getWorkflowService()
+ */
+ public WorkflowService getWorkflowService()
+ {
+ return (WorkflowService)getService(WORKFLOW_SERVICE);
+ }
}
diff --git a/source/java/org/alfresco/repo/workflow/BPMEngine.java b/source/java/org/alfresco/repo/workflow/BPMEngine.java
new file mode 100644
index 0000000000..7083475f0c
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/BPMEngine.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import org.alfresco.service.cmr.workflow.WorkflowException;
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * Base functionality for a plug-in BPM Engine
+ *
+ * @author davidc
+ */
+public class BPMEngine implements InitializingBean
+{
+ private BPMEngineRegistry registry;
+ private String engineId;
+
+
+ /**
+ * Sets the BPM Engine Registry
+ *
+ * @param registry the registry
+ */
+ public void setBPMEngineRegistry(BPMEngineRegistry registry)
+ {
+ this.registry = registry;
+ }
+
+ /**
+ * Sets the BPM Engine Id
+ *
+ * @param engineId the id
+ */
+ public void setEngineId(String engineId)
+ {
+ this.engineId = engineId;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+ */
+ public void afterPropertiesSet() throws Exception
+ {
+ if (engineId == null || engineId.length() == 0)
+ {
+ throw new WorkflowException("Engine Id not specified");
+ }
+
+ if (this instanceof WorkflowDefinitionComponent)
+ {
+ registry.registerWorkflowDefinitionComponent(engineId, (WorkflowDefinitionComponent)this);
+ }
+ if (this instanceof WorkflowComponent)
+ {
+ registry.registerWorkflowComponent(engineId, (WorkflowComponent)this);
+ }
+ if (this instanceof TaskComponent)
+ {
+ registry.registerTaskComponent(engineId, (TaskComponent)this);
+ }
+ }
+
+ /**
+ * Construct a global Id for use outside of the engine
+ *
+ * @param localId the local engine id
+ * @return the global id
+ */
+ protected String createGlobalId(String localId)
+ {
+ return BPMEngineRegistry.createGlobalId(engineId, localId);
+ }
+
+ /**
+ * Construct a local Id from a global Id
+ *
+ * @param globalId the global id
+ * @return the local id
+ */
+ protected String createLocalId(String globalId)
+ {
+ return BPMEngineRegistry.getLocalId(globalId);
+ }
+}
diff --git a/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java b/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java
new file mode 100644
index 0000000000..c2024b0a6d
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.alfresco.service.cmr.workflow.WorkflowException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * BPM Engine Registry
+ *
+ * Responsible for managing the list of registered BPM Engines for the
+ * following components:
+ *
+ * - Workflow Definition Component
+ * - Workflow Component
+ * - Task Component
+ *
+ * @author davidc
+ */
+public class BPMEngineRegistry
+{
+ /** ID seperator used in global Ids */
+ private static final String ID_SEPERATOR = "://";
+
+ /** Logging support */
+ private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow");
+
+ private Map workflowDefinitionComponents;
+ private Map workflowComponents;
+ private Map taskComponents;
+
+
+ /**
+ * Construct
+ */
+ public BPMEngineRegistry()
+ {
+ workflowDefinitionComponents = new HashMap();
+ workflowComponents = new HashMap();
+ taskComponents = new HashMap();
+ }
+
+ /**
+ * Register a BPM Engine Workflow Definition Component
+ *
+ * @param engineId engine id
+ * @param engine implementing engine
+ */
+ public void registerWorkflowDefinitionComponent(String engineId, WorkflowDefinitionComponent engine)
+ {
+ if (workflowDefinitionComponents.containsKey(engineId))
+ {
+ throw new WorkflowException("Workflow Definition Component already registered for engine id '" + engineId + "'");
+ }
+ workflowDefinitionComponents.put(engineId, engine);
+
+ if (logger.isInfoEnabled())
+ logger.info("Registered Workflow Definition Component '" + engineId + "' (" + engine.getClass() + ")");
+ }
+
+ /**
+ * Gets all registered Workflow Definition Components
+ *
+ * @return array of engine ids
+ */
+ public String[] getWorkflowDefinitionComponents()
+ {
+ return workflowDefinitionComponents.keySet().toArray(new String[workflowDefinitionComponents.keySet().size()]);
+ }
+
+ /**
+ * Gets a specific BPM Engine Workflow Definition Component
+ *
+ * @param engineId engine id
+ * @return the Workflow Definition Component
+ */
+ public WorkflowDefinitionComponent getWorkflowDefinitionComponent(String engineId)
+ {
+ return workflowDefinitionComponents.get(engineId);
+ }
+
+ /**
+ * Register a BPM Engine Workflow Component
+ *
+ * @param engineId engine id
+ * @param engine implementing engine
+ */
+ public void registerWorkflowComponent(String engineId, WorkflowComponent engine)
+ {
+ if (workflowComponents.containsKey(engineId))
+ {
+ throw new WorkflowException("Workflow Component already registered for engine id '" + engineId + "'");
+ }
+ workflowComponents.put(engineId, engine);
+
+ if (logger.isInfoEnabled())
+ logger.info("Registered Workflow Component '" + engineId + "' (" + engine.getClass() + ")");
+ }
+
+ /**
+ * Gets all registered Workflow Components
+ *
+ * @return array of engine ids
+ */
+ public String[] getWorkflowComponents()
+ {
+ return workflowComponents.keySet().toArray(new String[workflowComponents.keySet().size()]);
+ }
+
+ /**
+ * Gets a specific BPM Engine Workflow Component
+ *
+ * @param engineId engine id
+ * @return the Workflow Component
+ */
+ public WorkflowComponent getWorkflowComponent(String engineId)
+ {
+ return workflowComponents.get(engineId);
+ }
+
+ /**
+ * Register a BPM Engine Task Component
+ *
+ * @param engineId engine id
+ * @param engine implementing engine
+ */
+ public void registerTaskComponent(String engineId, TaskComponent engine)
+ {
+ if (taskComponents.containsKey(engineId))
+ {
+ throw new WorkflowException("Task Component already registered for engine id '" + engineId + "'");
+ }
+ taskComponents.put(engineId, engine);
+
+ if (logger.isInfoEnabled())
+ logger.info("Registered Task Component '" + engineId + "' (" + engine.getClass() + ")");
+ }
+
+ /**
+ * Gets all registered Task Components
+ *
+ * @return array of engine ids
+ */
+ public String[] getTaskComponents()
+ {
+ return taskComponents.keySet().toArray(new String[taskComponents.keySet().size()]);
+ }
+
+ /**
+ * Gets a specific BPM Engine Task Component
+ *
+ * @param engineId engine id
+ * @return the Workflow Component
+ */
+ public TaskComponent getTaskComponent(String engineId)
+ {
+ return taskComponents.get(engineId);
+ }
+
+
+ //
+ // BPM Engine Id support
+ //
+
+ /**
+ * Construct a global Id
+ *
+ * @param engineId engine id
+ * @param localId engine local id
+ * @return the global id
+ */
+ public static String createGlobalId(String engineId, String localId)
+ {
+ return engineId + ID_SEPERATOR + localId;
+ }
+
+ /**
+ * Break apart a global id into its engine and local ids
+ *
+ * @param globalId the global id
+ * @return array containing engine id and global id in that order
+ */
+ public static String[] getGlobalIdParts(String globalId)
+ {
+ String[] parts = globalId.split(ID_SEPERATOR);
+ if (parts.length != 2)
+ {
+ throw new WorkflowException("Invalid Global Id '" + globalId + "'");
+ }
+ return parts;
+ }
+
+ /**
+ * Get the engine id from a global id
+ *
+ * @param globalId the global id
+ * @return the engine id
+ */
+ public static String getEngineId(String globalId)
+ {
+ return getGlobalIdParts(globalId)[0];
+ }
+
+ /**
+ * Get the local id from a global id
+ *
+ * @param globalId the global id
+ * @return the local id
+ */
+ public static String getLocalId(String globalId)
+ {
+ return getGlobalIdParts(globalId)[1];
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/workflow/TaskComponent.java b/source/java/org/alfresco/repo/workflow/TaskComponent.java
new file mode 100644
index 0000000000..8fabe54b78
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/TaskComponent.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.cmr.workflow.WorkflowTaskState;
+import org.alfresco.service.namespace.QName;
+
+
+/**
+ * SPI to be implemented by a BPM Engine that provides Task management.
+ *
+ * @author davidc
+ */
+public interface TaskComponent
+{
+
+ /**
+ * Gets a Task by unique Id
+ *
+ * @param taskId the task id
+ * @return the task
+ */
+ public WorkflowTask getTaskById(String taskId);
+
+ /**
+ * Gets all tasks assigned to the specified authority
+ *
+ * @param authority the authority
+ * @param state filter by specified workflow task state
+ * @return the list of assigned tasks
+ */
+ public List getAssignedTasks(String authority, WorkflowTaskState state);
+
+ /**
+ * Gets the pooled tasks available to the specified authority
+ *
+ * @param authority the authority
+ * @return the list of pooled tasks
+ */
+ public List getPooledTasks(List authorities);
+
+ /**
+ * Update the Properties and Associations of a Task
+ *
+ * @param taskId the task id to update
+ * @param properties the map of properties to set on the task (or null, if none to set)
+ * @param add the map of items to associate with the task (or null, if none to add)
+ * @param remove the map of items to dis-associate with the task (or null, if none to remove)
+ * @return the update task
+ */
+ public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove);
+
+ /**
+ * End the Task (i.e. complete the task)
+ *
+ * @param taskId the task id to end
+ * @param transition the task transition to take on completion (or null, for the default transition)
+ * @return the updated task
+ */
+ public WorkflowTask endTask(String taskId, String transition);
+
+}
+
diff --git a/source/java/org/alfresco/repo/workflow/WorkflowComponent.java b/source/java/org/alfresco/repo/workflow/WorkflowComponent.java
new file mode 100644
index 0000000000..a0f742e866
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/WorkflowComponent.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.service.cmr.workflow.WorkflowInstance;
+import org.alfresco.service.cmr.workflow.WorkflowPath;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.namespace.QName;
+
+
+/**
+ * SPI to be implemented by a BPM Engine that provides Workflow instance management.
+ *
+ * @author davidc
+ */
+public interface WorkflowComponent
+{
+
+ /**
+ * Start a Workflow Instance
+ *
+ * @param workflowDefinitionId the workflow definition id
+ * @param parameters the initial set of parameters used to populate the "Start Task" properties
+ * @return the initial workflow path
+ */
+ public WorkflowPath startWorkflow(String workflowDefinitionId, Map parameters);
+
+ /**
+ * Gets all "in-flight" workflow instances of the specified Workflow Definition
+ *
+ * @param workflowDefinitionId the workflow definition id
+ * @return the list of "in-fligth" workflow instances
+ */
+ public List getActiveWorkflows(String workflowDefinitionId);
+
+ /**
+ * Gets all Paths for the specified Workflow instance
+ *
+ * @param workflowId workflow instance id
+ * @return the list of workflow paths
+ */
+ public List getWorkflowPaths(String workflowId);
+
+ /**
+ * Cancel an "in-fligth" Workflow instance
+ *
+ * @param workflowId the workflow instance to cancel
+ * @return an updated representation of the workflow instance
+ */
+ public WorkflowInstance cancelWorkflow(String workflowId);
+
+ /**
+ * Signal the transition from one Workflow Node to another within an "in-flight"
+ * process.
+ *
+ * @param pathId the workflow path to signal on
+ * @param transition the transition to follow (or null, for the default transition)
+ * @return the updated workflow path
+ */
+ public WorkflowPath signal(String pathId, String transition);
+
+ /**
+ * Gets all Tasks associated with the specified path
+ *
+ * @param pathId the path id
+ * @return the list of associated tasks
+ */
+ public List getTasksForWorkflowPath(String pathId);
+
+}
+
diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDefinitionComponent.java b/source/java/org/alfresco/repo/workflow/WorkflowDefinitionComponent.java
new file mode 100644
index 0000000000..5fd9ef849b
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/WorkflowDefinitionComponent.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.alfresco.service.cmr.workflow.WorkflowDefinition;
+
+
+/**
+ * SPI to be implemented by a BPM Engine that provides Workflow Definition management.
+ *
+ * @author davidc
+ */
+public interface WorkflowDefinitionComponent
+{
+
+ /**
+ * Deploy a Workflow Definition
+ *
+ * @param workflowDefinition the content object containing the definition
+ * @return workflow definition
+ */
+ public WorkflowDefinition deployDefinition(InputStream workflowDefinition);
+
+ /**
+ * Undeploy an exisiting Workflow Definition
+ *
+ * TODO: Determine behaviour when "in-flight" workflow instances exist
+ *
+ * @param workflowDefinitionId the id of the definition to undeploy
+ */
+ public void undeployDefinition(String workflowDefinitionId);
+
+ /**
+ * Gets all deployed Workflow Definitions
+ *
+ * @return the deployed workflow definitions
+ */
+ public List getDefinitions();
+
+ /**
+ * Gets a Workflow Definition by unique Id
+ *
+ * @param workflowDefinitionId the workflow definition id
+ * @return the deployed workflow definition
+ */
+ public WorkflowDefinition getDefinitionById(String workflowDefinitionId);
+
+}
+
diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java
new file mode 100644
index 0000000000..c7b10a776c
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.workflow.WorkflowDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowException;
+import org.alfresco.service.cmr.workflow.WorkflowInstance;
+import org.alfresco.service.cmr.workflow.WorkflowPath;
+import org.alfresco.service.cmr.workflow.WorkflowService;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.cmr.workflow.WorkflowTaskState;
+import org.alfresco.service.namespace.QName;
+
+
+/**
+ * Default Alfresco Workflow Service whose implementation is backed by registered
+ * BPM Engine plug-in components.
+ *
+ * @author davidc
+ */
+public class WorkflowServiceImpl implements WorkflowService
+{
+ private BPMEngineRegistry registry;
+
+
+ /**
+ * Sets the BPM Engine Registry
+ *
+ * @param registry bpm engine registry
+ */
+ public void setBPMEngineRegistry(BPMEngineRegistry registry)
+ {
+ this.registry = registry;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(org.alfresco.service.cmr.repository.NodeRef)
+ */
+ public WorkflowDefinition deployDefinition(NodeRef definitionContent)
+ {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#undeployDefinition(java.lang.String)
+ */
+ public void undeployDefinition(String processDefinitionId)
+ {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitions()
+ */
+ public List getDefinitions()
+ {
+ List definitions = new ArrayList(10);
+ String[] ids = registry.getWorkflowDefinitionComponents();
+ for (String id: ids)
+ {
+ WorkflowDefinitionComponent component = registry.getWorkflowDefinitionComponent(id);
+ definitions.addAll(component.getDefinitions());
+ }
+ return Collections.unmodifiableList(definitions);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionById(java.lang.String)
+ */
+ public WorkflowDefinition getDefinitionById(String workflowDefinitionId)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId);
+ WorkflowDefinitionComponent component = getWorkflowDefinitionComponent(engineId);
+ return component.getDefinitionById(workflowDefinitionId);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#startWorkflow(java.lang.String, java.util.Map)
+ */
+ public WorkflowPath startWorkflow(String workflowDefinitionId, Map parameters)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId);
+ WorkflowComponent component = getWorkflowComponent(engineId);
+ return component.startWorkflow(workflowDefinitionId, parameters);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#startWorkflowFromTemplate(org.alfresco.service.cmr.repository.NodeRef)
+ */
+ public WorkflowPath startWorkflowFromTemplate(NodeRef templateDefinition)
+ {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getActiveWorkflows(java.lang.String)
+ */
+ public List getActiveWorkflows(String workflowDefinitionId)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId);
+ WorkflowComponent component = getWorkflowComponent(engineId);
+ return component.getActiveWorkflows(workflowDefinitionId);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getWorkflowPaths(java.lang.String)
+ */
+ public List getWorkflowPaths(String workflowId)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(workflowId);
+ WorkflowComponent component = getWorkflowComponent(engineId);
+ return component.getWorkflowPaths(workflowId);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#cancelWorkflow(java.lang.String)
+ */
+ public WorkflowInstance cancelWorkflow(String workflowId)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(workflowId);
+ WorkflowComponent component = getWorkflowComponent(engineId);
+ return component.cancelWorkflow(workflowId);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#signal(java.lang.String, java.lang.String)
+ */
+ public WorkflowPath signal(String pathId, String transition)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(pathId);
+ WorkflowComponent component = getWorkflowComponent(engineId);
+ return component.signal(pathId, transition);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getTasksForWorkflowPath(java.lang.String)
+ */
+ public List getTasksForWorkflowPath(String pathId)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(pathId);
+ WorkflowComponent component = getWorkflowComponent(engineId);
+ return component.getTasksForWorkflowPath(pathId);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getAssignedTasks(java.lang.String, org.alfresco.service.cmr.workflow.WorkflowTaskState)
+ */
+ public List getAssignedTasks(String authority, WorkflowTaskState state)
+ {
+ List tasks = new ArrayList(10);
+ String[] ids = registry.getTaskComponents();
+ for (String id: ids)
+ {
+ TaskComponent component = registry.getTaskComponent(id);
+ tasks.addAll(component.getAssignedTasks(authority, state));
+ }
+ return Collections.unmodifiableList(tasks);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getPooledTasks(java.lang.String)
+ */
+ public List getPooledTasks(String authority)
+ {
+ // TODO: Expand authorities to include associated groups (and parent groups)
+ List authorities = new ArrayList();
+ authorities.add(authority);
+
+ List tasks = new ArrayList(10);
+ String[] ids = registry.getTaskComponents();
+ for (String id: ids)
+ {
+ TaskComponent component = registry.getTaskComponent(id);
+ tasks.addAll(component.getPooledTasks(authorities));
+ }
+ return Collections.unmodifiableList(tasks);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#updateTask(java.lang.String, java.util.Map, java.util.Map, java.util.Map)
+ */
+ public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(taskId);
+ TaskComponent component = getTaskComponent(engineId);
+ return component.updateTask(taskId, properties, add, remove);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#endTask(java.lang.String, java.lang.String)
+ */
+ public WorkflowTask endTask(String taskId, String transition)
+ {
+ String engineId = BPMEngineRegistry.getEngineId(taskId);
+ TaskComponent component = getTaskComponent(engineId);
+ return component.endTask(taskId, transition);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.service.cmr.workflow.WorkflowService#getTaskById(java.lang.String)
+ */
+ public WorkflowTask getTaskById(String taskId)
+ {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Gets the Workflow Definition Component registered against the specified BPM Engine Id
+ *
+ * @param engineId engine id
+ */
+ private WorkflowDefinitionComponent getWorkflowDefinitionComponent(String engineId)
+ {
+ WorkflowDefinitionComponent component = registry.getWorkflowDefinitionComponent(engineId);
+ if (component == null)
+ {
+ throw new WorkflowException("Workflow Definition Component for engine id '" + engineId + "' is not registered");
+ }
+ return component;
+ }
+
+ /**
+ * Gets the Workflow Component registered against the specified BPM Engine Id
+ *
+ * @param engineId engine id
+ */
+ private WorkflowComponent getWorkflowComponent(String engineId)
+ {
+ WorkflowComponent component = registry.getWorkflowComponent(engineId);
+ if (component == null)
+ {
+ throw new WorkflowException("Workflow Component for engine id '" + engineId + "' is not registered");
+ }
+ return component;
+ }
+
+ /**
+ * Gets the Task Component registered against the specified BPM Engine Id
+ *
+ * @param engineId engine id
+ */
+ private TaskComponent getTaskComponent(String engineId)
+ {
+ TaskComponent component = registry.getTaskComponent(engineId);
+ if (component == null)
+ {
+ throw new WorkflowException("Task Component for engine id '" + engineId + "' is not registered");
+ }
+ return component;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java
new file mode 100644
index 0000000000..c41b546332
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImplTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow;
+
+import java.util.List;
+
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.workflow.WorkflowDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowPath;
+import org.alfresco.service.cmr.workflow.WorkflowService;
+import org.alfresco.util.BaseSpringTest;
+
+
+/**
+ * Workflow Service Implementation Tests
+ *
+ * @author davidc
+ */
+public class WorkflowServiceImplTest extends BaseSpringTest
+{
+ WorkflowService workflowService;
+
+ //@Override
+ protected void onSetUpInTransaction() throws Exception
+ {
+ workflowService = (WorkflowService)applicationContext.getBean(ServiceRegistry.WORKFLOW_SERVICE.getLocalName());
+ }
+
+ public void testGetWorkflowDefinitions()
+ {
+ List workflowDefs = workflowService.getDefinitions();
+ assertNotNull(workflowDefs);
+ assertTrue(workflowDefs.size() > 0);
+ }
+
+ public void testStartWorkflow()
+ {
+ List workflowDefs = workflowService.getDefinitions();
+ assertNotNull(workflowDefs);
+ assertTrue(workflowDefs.size() > 0);
+ WorkflowDefinition workflowDef = workflowDefs.get(0);
+ WorkflowPath path = workflowService.startWorkflow(workflowDef.id, null);
+ assertNotNull(path);
+ assertTrue(path.active);
+ assertNotNull(path.node);
+ assertNotNull(path.instance);
+ assertEquals(workflowDef.id, path.instance.definition.id);
+ }
+}
diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
new file mode 100644
index 0000000000..8f58734b9c
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow.jbpm;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.workflow.BPMEngine;
+import org.alfresco.repo.workflow.TaskComponent;
+import org.alfresco.repo.workflow.WorkflowComponent;
+import org.alfresco.repo.workflow.WorkflowDefinitionComponent;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.workflow.WorkflowDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowException;
+import org.alfresco.service.cmr.workflow.WorkflowInstance;
+import org.alfresco.service.cmr.workflow.WorkflowNode;
+import org.alfresco.service.cmr.workflow.WorkflowPath;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowTaskState;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.hibernate.proxy.HibernateProxy;
+import org.jbpm.JbpmContext;
+import org.jbpm.JbpmException;
+import org.jbpm.context.exe.ContextInstance;
+import org.jbpm.db.GraphSession;
+import org.jbpm.db.TaskMgmtSession;
+import org.jbpm.graph.def.Node;
+import org.jbpm.graph.def.ProcessDefinition;
+import org.jbpm.graph.def.Transition;
+import org.jbpm.graph.exe.ProcessInstance;
+import org.jbpm.graph.exe.Token;
+import org.jbpm.taskmgmt.def.Task;
+import org.jbpm.taskmgmt.exe.TaskInstance;
+import org.springmodules.workflow.jbpm31.JbpmCallback;
+import org.springmodules.workflow.jbpm31.JbpmTemplate;
+
+
+/**
+ * JBoss JBPM based implementation of:
+ *
+ * Workflow Definition Component
+ * Workflow Component
+ * Task Component
+ *
+ * @author davidc
+ */
+public class JBPMEngine extends BPMEngine
+ implements WorkflowDefinitionComponent, WorkflowComponent, TaskComponent
+{
+ // Implementation dependencies
+ protected DictionaryService dictionaryService;
+ protected NamespaceService namespaceService;
+ private JbpmTemplate jbpmTemplate;
+
+ /**
+ * Sets the JBPM Template used for accessing JBoss JBPM in the correct context
+ *
+ * @param jbpmTemplate
+ */
+ public void setJBPMTemplate(JbpmTemplate jbpmTemplate)
+ {
+ this.jbpmTemplate = jbpmTemplate;
+ }
+
+ /**
+ * Sets the Dictionary Service
+ *
+ * @param dictionaryService
+ */
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ /**
+ * Sets the Namespace Service
+ *
+ * @param namespaceService
+ */
+ public void setNamespaceService(NamespaceService namespaceService)
+ {
+ this.namespaceService = namespaceService;
+ }
+
+
+ //
+ // Workflow Definition...
+ //
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#deployDefinition(java.io.InputStream)
+ */
+ public WorkflowDefinition deployDefinition(InputStream workflowDefinition)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#undeployDefinition(java.lang.String)
+ */
+ public void undeployDefinition(String workflowDefinitionId)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitions()
+ */
+ @SuppressWarnings("unchecked")
+ public List getDefinitions()
+ {
+ try
+ {
+ return (List)jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ GraphSession graphSession = context.getGraphSession();
+ List processDefs = (List)graphSession.findLatestProcessDefinitions();
+ List workflowDefs = new ArrayList(processDefs.size());
+ for (ProcessDefinition processDef : processDefs)
+ {
+ WorkflowDefinition workflowDef = createWorkflowDefinition(processDef);
+ workflowDefs.add(workflowDef);
+ }
+ return workflowDefs;
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to retrieve workflow definitions", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitionById(java.lang.String)
+ */
+ public WorkflowDefinition getDefinitionById(String workflowDefinitionId)
+ {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+
+ //
+ // Workflow Instance Management...
+ //
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowComponent#startWorkflow(java.lang.String, java.util.Map)
+ */
+ @SuppressWarnings("unchecked")
+ public WorkflowPath startWorkflow(final String workflowDefinitionId, final Map parameters)
+ {
+ try
+ {
+ return (WorkflowPath) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ // initialise jBPM actor (for any processes that wish to record the initiator)
+ context.setActorId(AuthenticationUtil.getCurrentUserName());
+
+ // construct a new process
+ GraphSession graphSession = context.getGraphSession();
+ ProcessDefinition processDefinition = graphSession.loadProcessDefinition(getJbpmId(workflowDefinitionId));
+ ProcessInstance processInstance = new ProcessInstance(processDefinition);
+ Token token = processInstance.getRootToken();
+
+ // create the start task if one exists
+ Task startTask = processInstance.getTaskMgmtInstance().getTaskMgmtDefinition().getStartTask();
+ if (startTask != null)
+ {
+ TaskInstance taskInstance = processInstance.getTaskMgmtInstance().createStartTaskInstance();
+ setTaskProperties(taskInstance, parameters);
+ token = taskInstance.getToken();
+ }
+
+ // Save the process instance along with the task instance
+ context.save(processInstance);
+ return createWorkflowPath(token);
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to start workflow " + workflowDefinitionId, e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowComponent#getActiveWorkflows(java.lang.String)
+ */
+ @SuppressWarnings("unchecked")
+ public List getActiveWorkflows(final String workflowDefinitionId)
+ {
+ try
+ {
+ return (List) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ GraphSession graphSession = context.getGraphSession();
+ List processInstances = graphSession.findProcessInstances(getJbpmId(workflowDefinitionId));
+ List workflowInstances = new ArrayList(processInstances.size());
+ for (ProcessInstance processInstance : processInstances)
+ {
+ if (!processInstance.hasEnded())
+ {
+ WorkflowInstance workflowInstance = createWorkflowInstance(processInstance);
+ workflowInstances.add(workflowInstance);
+ }
+ }
+ return workflowInstances;
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to retrieve workflow instances for definition '" + workflowDefinitionId + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowComponent#getWorkflowPaths(java.lang.String)
+ */
+ @SuppressWarnings("unchecked")
+ public List getWorkflowPaths(final String workflowId)
+ {
+ try
+ {
+ return (List) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ // retrieve process instance
+ GraphSession graphSession = context.getGraphSession();
+ ProcessInstance processInstance = graphSession.loadProcessInstance(getJbpmId(workflowId));
+
+ // convert jBPM tokens to workflow posisitons
+ List tokens = processInstance.findAllTokens();
+ List paths = new ArrayList(tokens.size());
+ for (Token token : tokens)
+ {
+ if (!token.hasEnded())
+ {
+ WorkflowPath path = createWorkflowPath(token);
+ paths.add(path);
+ }
+ }
+
+ return paths;
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to retrieve workflow paths for workflow instance '" + workflowId + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowComponent#cancelWorkflow(java.lang.String)
+ */
+ public WorkflowInstance cancelWorkflow(final String workflowId)
+ {
+ try
+ {
+ return (WorkflowInstance) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ // retrieve and cancel process instance
+ GraphSession graphSession = context.getGraphSession();
+ ProcessInstance processInstance = graphSession.loadProcessInstance(getJbpmId(workflowId));
+ processInstance.end();
+
+ // save the process instance along with the task instance
+ context.save(processInstance);
+ return createWorkflowInstance(processInstance);
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to cancel workflow instance '" + workflowId + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowComponent#signal(java.lang.String, java.lang.String)
+ */
+ public WorkflowPath signal(final String pathId, final String transition)
+ {
+ try
+ {
+ return (WorkflowPath) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ // retrieve jBPM token for workflow position
+ GraphSession graphSession = context.getGraphSession();
+ Token token = getWorkflowToken(graphSession, pathId);
+
+ // signal the transition
+ if (transition == null)
+ {
+ token.signal();
+ }
+ else
+ {
+ Node node = token.getNode();
+ if (!node.hasLeavingTransition(transition))
+ {
+ throw new WorkflowException("Transition '" + transition + "' is invalid for Workflow path '" + pathId + "'");
+ }
+ token.signal(transition);
+ }
+
+ // save
+ ProcessInstance processInstance = token.getProcessInstance();
+ context.save(processInstance);
+
+ // return new workflow path
+ return createWorkflowPath(token);
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to signal transition '" + transition + "' from workflow path '" + pathId + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.WorkflowComponent#getTasksForWorkflowPath(java.lang.String)
+ */
+ @SuppressWarnings("unchecked")
+ public List getTasksForWorkflowPath(final String pathId)
+ {
+ try
+ {
+ return (List) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public List doInJbpm(JbpmContext context)
+ {
+ // retrieve tasks at specified workflow path
+ GraphSession graphSession = context.getGraphSession();
+ Token token = getWorkflowToken(graphSession, pathId);
+ TaskMgmtSession taskSession = context.getTaskMgmtSession();
+ List tasks = taskSession.findTaskInstancesByToken(token.getId());
+ List workflowTasks = new ArrayList(tasks.size());
+ for (TaskInstance task : tasks)
+ {
+ WorkflowTask workflowTask = createWorkflowTask(task);
+ workflowTasks.add(workflowTask);
+ }
+ return workflowTasks;
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to retrieve tasks assigned at Workflow path '" + pathId + "'", e);
+ }
+ }
+
+
+ //
+ // Task Management ...
+ //
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#getAssignedTasks(java.lang.String, org.alfresco.service.cmr.workflow.WorkflowTaskState)
+ */
+ @SuppressWarnings("unchecked")
+ public List getAssignedTasks(final String authority, final WorkflowTaskState state)
+ {
+ try
+ {
+ return (List) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public List doInJbpm(JbpmContext context)
+ {
+ // retrieve tasks assigned to authority
+ TaskMgmtSession taskSession = context.getTaskMgmtSession();
+ List tasks = taskSession.findTaskInstances(authority);
+ List workflowTasks = new ArrayList(tasks.size());
+ for (TaskInstance task : tasks)
+ {
+ if (getWorkflowTaskState(task).equals(state))
+ {
+ WorkflowTask workflowTask = createWorkflowTask(task);
+ workflowTasks.add(workflowTask);
+ }
+ }
+ return workflowTasks;
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to retrieve tasks assigned to authority '" + authority + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#getPooledTasks(java.util.List)
+ */
+ @SuppressWarnings("unchecked")
+ public List getPooledTasks(final List authorities)
+ {
+ try
+ {
+ return (List) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public List doInJbpm(JbpmContext context)
+ {
+ // retrieve pooled tasks for specified authorities
+ TaskMgmtSession taskSession = context.getTaskMgmtSession();
+ List tasks = taskSession.findPooledTaskInstances(authorities);
+ List workflowTasks = new ArrayList(tasks.size());
+ for (TaskInstance task : tasks)
+ {
+ WorkflowTask workflowTask = createWorkflowTask(task);
+ workflowTasks.add(workflowTask);
+ }
+ return workflowTasks;
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to retrieve pooled tasks for authorities '" + authorities + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#updateTask(java.lang.String, java.util.Map, java.util.Map, java.util.Map)
+ */
+ public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove)
+ {
+ // TODO
+ throw new UnsupportedOperationException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#endTask(java.lang.String, java.lang.String)
+ */
+ public WorkflowTask endTask(final String taskId, final String transition)
+ {
+ try
+ {
+ return (WorkflowTask) jbpmTemplate.execute(new JbpmCallback()
+ {
+ public Object doInJbpm(JbpmContext context)
+ {
+ // retrieve task
+ TaskMgmtSession taskSession = context.getTaskMgmtSession();
+ TaskInstance taskInstance = taskSession.loadTaskInstance(getJbpmId(taskId));
+
+ // signal the transition on the task
+ if (transition == null)
+ {
+ taskInstance.end();
+ }
+ else
+ {
+ Node node = taskInstance.getTask().getTaskNode();
+ if (node.getLeavingTransition(transition) == null)
+ {
+ throw new WorkflowException("Transition '" + transition + "' is invalid for Workflow task '" + taskId + "'");
+ }
+ taskInstance.end(transition);
+ }
+
+ // save
+ ProcessInstance processInstance = taskInstance.getToken().getProcessInstance();
+ context.save(processInstance);
+
+ // note: the ending of a task may not have signalled (i.e. more than one task exists at
+ // this node)
+ return createWorkflowTask(taskInstance);
+ }
+ });
+ }
+ catch(JbpmException e)
+ {
+ throw new WorkflowException("Failed to signal transition '" + transition + "' from workflow task '" + taskId + "'", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.workflow.TaskComponent#getTaskById(java.lang.String)
+ */
+ public WorkflowTask getTaskById(String taskId)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Sets Properties of Task
+ *
+ * @param instance task instance
+ * @param properties properties to set
+ */
+ protected void setTaskProperties(TaskInstance instance, Map properties)
+ {
+ if (properties == null)
+ {
+ return;
+ }
+
+ // TODO: Use Dictionary to drive mapping
+
+ // TODO: Determine if NodeRefs and collection of NodeRefs need to be converted to String
+
+ ContextInstance context = instance.getContextInstance();
+ for (Entry entry : properties.entrySet())
+ {
+ String name = null;
+ QName qname = entry.getKey();
+ if (qname.getNamespaceURI().equals(NamespaceService.DEFAULT_URI))
+ {
+ name = qname.getLocalName();
+ }
+ else
+ {
+ name = qname.toPrefixString(namespaceService);
+ }
+ context.setVariable(name, entry.getValue());
+ }
+ }
+
+
+ //
+ // Helpers...
+ //
+
+
+ /**
+ * Get JBoss JBPM Id from Engine Global Id
+ *
+ * @param id global id
+ * @return JBoss JBPM Id
+ */
+ protected long getJbpmId(String id)
+ {
+ try
+ {
+ String theLong = createLocalId(id);
+ return new Long(theLong);
+ }
+ catch(NumberFormatException e)
+ {
+ throw new WorkflowException("Format of id '" + id + "' is invalid", e);
+ }
+ }
+
+ /**
+ * Get the JBoss JBPM Token for the Workflow Path
+ *
+ * @param session JBoss JBPM Graph Session
+ * @param pathId workflow path id
+ * @return JBoss JBPM Token
+ */
+ protected Token getWorkflowToken(GraphSession session, String pathId)
+ {
+ // extract process id and token path within process
+ String[] path = pathId.split("::");
+ if (path.length != 2)
+ {
+ throw new WorkflowException("Invalid workflow path '" + pathId + "'");
+ }
+
+ // retrieve jBPM token for workflow position
+ ProcessInstance processInstance = session.loadProcessInstance(getJbpmId(path[0]));
+ Token token = processInstance.findToken(path[1]);
+ if (token == null)
+ {
+ throw new WorkflowException("Workflow path '" + pathId + "' does not exist");
+ }
+
+ return token;
+ }
+
+
+ //
+ // Workflow Data Object Creation...
+ //
+
+ /**
+ * Creates a Workflow Path
+ *
+ * @param token JBoss JBPM Token
+ * @return Workflow Path
+ */
+ protected WorkflowPath createWorkflowPath(Token token)
+ {
+ WorkflowPath path = new WorkflowPath();
+ path.id = createGlobalId(token.getProcessInstance().getId() + "::" + token.getFullName());
+ path.instance = createWorkflowInstance(token.getProcessInstance());
+ path.node = createWorkflowNode(token.getNode());
+ path.active = !token.hasEnded();
+ return path;
+ }
+
+ /**
+ * Creates a Workflow Node
+ *
+ * @param node JBoss JBPM Node
+ * @return Workflow Node
+ */
+ @SuppressWarnings("unchecked")
+ protected WorkflowNode createWorkflowNode(Node node)
+ {
+ WorkflowNode workflowNode = new WorkflowNode();
+ workflowNode.name = node.getName();
+ if (node instanceof HibernateProxy)
+ {
+ Node realNode = (Node)((HibernateProxy)node).getHibernateLazyInitializer().getImplementation();
+ workflowNode.type = realNode.getClass().getSimpleName();
+ }
+ else
+ {
+ workflowNode.type = node.getClass().getSimpleName();
+ }
+ // TODO: Is there a formal way of determing if task node?
+ workflowNode.isTaskNode = workflowNode.type.equals("TaskNode");
+ List transitions = node.getLeavingTransitions();
+ workflowNode.transitions = new String[transitions.size()];
+ int i = 0;
+ for (Transition transition : (List)transitions)
+ {
+ workflowNode.transitions[i++] = transition.getName();
+ }
+ return workflowNode;
+ }
+
+ /**
+ * Creates a Workflow Instance
+ *
+ * @param instance JBoss JBPM Process Instance
+ * @return Workflow instance
+ */
+ protected WorkflowInstance createWorkflowInstance(ProcessInstance instance)
+ {
+ WorkflowInstance workflowInstance = new WorkflowInstance();
+ workflowInstance.id = createGlobalId(new Long(instance.getId()).toString());
+ workflowInstance.definition = createWorkflowDefinition(instance.getProcessDefinition());
+ workflowInstance.active = !instance.hasEnded();
+ return workflowInstance;
+ }
+
+ /**
+ * Creates a Workflow Definition
+ *
+ * @param definition JBoss Process Definition
+ * @return Workflow Definition
+ */
+ protected WorkflowDefinition createWorkflowDefinition(ProcessDefinition definition)
+ {
+ WorkflowDefinition workflowDef = new WorkflowDefinition();
+ workflowDef.id = createGlobalId(new Long(definition.getId()).toString());
+ workflowDef.name = definition.getName();
+ Task startTask = definition.getTaskMgmtDefinition().getStartTask();
+ if (startTask != null)
+ {
+ workflowDef.startTaskDefinition = createWorkflowTaskDefinition(startTask);
+ }
+
+ return workflowDef;
+ }
+
+ /**
+ * Creates a Workflow Task
+ *
+ * @param task JBoss Task Instance
+ * @return Workflow Task
+ */
+ protected WorkflowTask createWorkflowTask(TaskInstance task)
+ {
+ WorkflowTask workflowTask = new WorkflowTask();
+ workflowTask.id = createGlobalId(new Long(task.getId()).toString());
+ workflowTask.name = task.getName();
+ workflowTask.path = createWorkflowPath(task.getToken());
+ workflowTask.state = getWorkflowTaskState(task);
+ workflowTask.definition = createWorkflowTaskDefinition(task.getTask());
+
+ // TODO: Properties and Associations
+
+ return workflowTask;
+ }
+
+ /**
+ * Creates a Workflow Task Definition
+ *
+ * @param task JBoss JBPM Task
+ * @return Workflow Task Definition
+ */
+ protected WorkflowTaskDefinition createWorkflowTaskDefinition(Task task)
+ {
+ // TODO: Extend jBPM task instance to include dictionary definition qname
+ WorkflowTaskDefinition taskDef = new WorkflowTaskDefinition();
+ taskDef.id = task.getName();
+ QName typeName = QName.createQName(taskDef.id, namespaceService);
+ taskDef.metadata = dictionaryService.getType(typeName);
+ return taskDef;
+ }
+
+ /**
+ * Get the Workflow Task State for the specified JBoss JBPM Task
+ *
+ * @param task task
+ * @return task state
+ */
+ protected WorkflowTaskState getWorkflowTaskState(TaskInstance task)
+ {
+ if (task.hasEnded())
+ {
+ return WorkflowTaskState.COMPLETED;
+ }
+ else
+ {
+ return WorkflowTaskState.IN_PROGRESS;
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java
new file mode 100644
index 0000000000..2145f4b3ea
--- /dev/null
+++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.repo.workflow.jbpm;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.repo.workflow.BPMEngineRegistry;
+import org.alfresco.repo.workflow.TaskComponent;
+import org.alfresco.repo.workflow.WorkflowComponent;
+import org.alfresco.repo.workflow.WorkflowDefinitionComponent;
+import org.alfresco.service.cmr.workflow.WorkflowDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowException;
+import org.alfresco.service.cmr.workflow.WorkflowInstance;
+import org.alfresco.service.cmr.workflow.WorkflowPath;
+import org.alfresco.service.cmr.workflow.WorkflowTask;
+import org.alfresco.service.cmr.workflow.WorkflowTaskState;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.BaseSpringTest;
+
+
+/**
+ * JBPM Engine Tests
+ *
+ * @author davidc
+ */
+public class JBPMEngineTest extends BaseSpringTest
+{
+ WorkflowDefinitionComponent workflowDefinitionComponent;
+ WorkflowComponent workflowComponent;
+ TaskComponent taskComponent;
+
+
+ //@Override
+ protected void onSetUpInTransaction() throws Exception
+ {
+ BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry");
+ workflowDefinitionComponent = registry.getWorkflowDefinitionComponent("jbpm");
+ workflowComponent = registry.getWorkflowComponent("jbpm");
+ taskComponent = registry.getTaskComponent("jbpm");
+ }
+
+
+ public void testGetWorkflowDefinitions()
+ {
+ List workflowDefs = workflowDefinitionComponent.getDefinitions();
+ assertNotNull(workflowDefs);
+ assertTrue(workflowDefs.size() > 0);
+ }
+
+
+ public void testStartWorkflow()
+ {
+ try
+ {
+ @SuppressWarnings("unused") WorkflowPath path = workflowComponent.startWorkflow("norfolknchance", null);
+ fail("Failed to catch invalid definition id");
+ }
+ catch(WorkflowException e)
+ {
+ }
+
+ // TODO: Determine why process definition is loaded, even though it doesn't exist
+// try
+// {
+// @SuppressWarnings("unused") WorkflowPosition pos = workflowComponent.startProcess("1000", null);
+// fail("Failed to catch workflow definition id that does not exist");
+// }
+// catch(WorkflowException e)
+// {
+// }
+
+ WorkflowDefinition workflowDef = getTestDefinition();
+ WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, null);
+ assertNotNull(path);
+ assertTrue(path.id.endsWith("::/"));
+ assertNotNull(path.node);
+ assertNotNull(path.instance);
+ assertEquals(workflowDef.id, path.instance.definition.id);
+ }
+
+
+ public void testGetWorkflowInstances()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+ workflowComponent.startWorkflow(workflowDef.id, null);
+ workflowComponent.startWorkflow(workflowDef.id, null);
+ List instances = workflowComponent.getActiveWorkflows(workflowDef.id);
+ assertNotNull(instances);
+ assertEquals(2, instances.size());
+ for (WorkflowInstance instance : instances)
+ {
+ assertEquals(workflowDef.id, instance.definition.id);
+ }
+ }
+
+
+ public void testGetPositions()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+ workflowComponent.startWorkflow(workflowDef.id, null);
+ List instances = workflowComponent.getActiveWorkflows(workflowDef.id);
+ assertNotNull(instances);
+ assertEquals(1, instances.size());
+ List paths = workflowComponent.getWorkflowPaths(instances.get(0).id);
+ assertNotNull(paths);
+ assertEquals(1, paths.size());
+ assertEquals(instances.get(0).id, paths.get(0).instance.id);
+ assertTrue(paths.get(0).id.endsWith("::/"));
+ }
+
+
+ public void testCancelWorkflowInstance()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+ workflowComponent.startWorkflow(workflowDef.id, null);
+ List instances1 = workflowComponent.getActiveWorkflows(workflowDef.id);
+ assertNotNull(instances1);
+ assertEquals(1, instances1.size());
+ WorkflowInstance cancelledInstance = workflowComponent.cancelWorkflow(instances1.get(0).id);
+ assertNotNull(cancelledInstance);
+ assertFalse(cancelledInstance.active);
+ List instances2 = workflowComponent.getActiveWorkflows(workflowDef.id);
+ assertNotNull(instances2);
+ assertEquals(0, instances2.size());
+ }
+
+
+ public void testSignal()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+ Map parameters = new HashMap();
+ parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
+ WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
+ assertNotNull(path);
+ WorkflowPath updatedPath = workflowComponent.signal(path.id, path.node.transitions[0]);
+ assertNotNull(updatedPath);
+ }
+
+
+ public void testGetAssignedTasks()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+ Map parameters = new HashMap();
+ parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
+ WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
+ assertNotNull(path);
+ assertNotNull(path);
+ WorkflowPath updatedPath = workflowComponent.signal(path.id, path.node.transitions[0]);
+ assertNotNull(updatedPath);
+ List completedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.COMPLETED);
+ assertNotNull(completedTasks);
+ assertEquals(0, completedTasks.size());
+ List assignedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
+ assertNotNull(assignedTasks);
+ assertEquals(1, assignedTasks.size());
+ assertEquals("Review", assignedTasks.get(0).name);
+ }
+
+
+ public void testEndTask()
+ {
+ WorkflowDefinition workflowDef = getTestDefinition();
+ Map parameters = new HashMap();
+ parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
+ WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
+ assertNotNull(path);
+ assertNotNull(path);
+ List tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
+ assertNotNull(tasks1);
+ assertEquals(1, tasks1.size());
+ assertEquals(WorkflowTaskState.IN_PROGRESS, tasks1.get(0).state);
+ WorkflowTask updatedTask = taskComponent.endTask(tasks1.get(0).id, null);
+ assertNotNull(updatedTask);
+ assertEquals(WorkflowTaskState.COMPLETED, updatedTask.state);
+ }
+
+
+ /**
+ * Locate the Test Workflow Definition
+ *
+ * @return workflow definition
+ */
+ private WorkflowDefinition getTestDefinition()
+ {
+ List workflowDefs = workflowDefinitionComponent.getDefinitions();
+ for (WorkflowDefinition workflowDef : workflowDefs)
+ {
+ if (workflowDef.name.equals("Review and Approve"))
+ {
+ return workflowDef;
+ }
+ }
+ fail("Test Workflow Definition not found");
+ return null;
+ }
+
+}
diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java
index 3d26bcbf29..3fc80541da 100644
--- a/source/java/org/alfresco/service/ServiceRegistry.java
+++ b/source/java/org/alfresco/service/ServiceRegistry.java
@@ -38,6 +38,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.cmr.view.ExporterService;
import org.alfresco.service.cmr.view.ImporterService;
+import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
@@ -81,6 +82,7 @@ public interface ServiceRegistry
static final QName TEMPLATE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "TemplateService");
static final QName FILE_FOLDER_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "FileFolderService");
static final QName SCRIPT_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "ScriptService");
+ static final QName WORKFLOW_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "WorkflowService");
/**
* Get the list of services provided by the Repository
@@ -254,4 +256,10 @@ public interface ServiceRegistry
*/
@NotAuditable
ScriptService getScriptService();
+
+ /**
+ * @return the workflow service (or null if one is not provided)
+ */
+ @NotAuditable
+ WorkflowService getWorkflowService();
}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowDefinition.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowDefinition.java
new file mode 100644
index 0000000000..d427ecf63f
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowDefinition.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+
+/**
+ * Workflow Definition Data Object
+ *
+ * @author davidc
+ */
+public class WorkflowDefinition
+{
+ /** Workflow Definition unique id */
+ public String id;
+
+ /** Workflow Definition name */
+ public String name;
+
+ /** Task Definition for Workflow Start Task (Optional) */
+ public WorkflowTaskDefinition startTaskDefinition;
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "WorkflowDefinition[id=" + id + ",name=" + name + ",startTask=" + startTaskDefinition.toString() + "]";
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowException.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowException.java
new file mode 100644
index 0000000000..e776ed6e18
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Base Exception of Workflow Exceptions.
+ *
+ * @author David Caruana
+ */
+public class WorkflowException extends AlfrescoRuntimeException
+{
+ private static final long serialVersionUID = -7338963365877285084L;
+
+ public WorkflowException(String msgId)
+ {
+ super(msgId);
+ }
+
+ public WorkflowException(String msgId, Throwable cause)
+ {
+ super(msgId, cause);
+ }
+
+ public WorkflowException(String msgId, Object ... args)
+ {
+ super(msgId, args);
+ }
+
+ public WorkflowException(String msgId, Throwable cause, Object ... args)
+ {
+ super(msgId, args, cause);
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowInstance.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowInstance.java
new file mode 100644
index 0000000000..07c0dac232
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowInstance.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+
+/**
+ * Workflow Instance Data Object
+ *
+ * Represents an "in-flight" workflow.
+ *
+ * @author davidc
+ */
+public class WorkflowInstance
+{
+ /** Workflow Instance unique id */
+ public String id;
+
+ /** Is this Workflow instance still "in-flight" or has it completed? */
+ public boolean active;
+
+ /** Workflow Definition */
+ public WorkflowDefinition definition;
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "WorkflowInstance[id=" + id + ",active=" + active + ",def=" + definition.toString() + "]";
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowNode.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowNode.java
new file mode 100644
index 0000000000..ad0fdf040c
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowNode.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+
+/**
+ * Workflow Node Data Object
+ *
+ * Represents a Node within the Workflow Definition.
+ *
+ * @author davidc
+ */
+public class WorkflowNode
+{
+ /** Name of the Workflow Node */
+ public String name;
+
+ /** Type of the Workflow Node (typically this is BPM engine specific - informational only */
+ public String type;
+
+ /** Does this Workflow Node represent human interaction? */
+ public boolean isTaskNode;
+
+ /** The transitions leaving this node (or null, if none) */
+ public String[] transitions;
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ String transitionsArray = "{";
+ for (int i = 0; i < transitions.length; i++)
+ {
+ transitionsArray += ((i == 0) ? "" : ",") + "'" + transitions[i] + "'";
+ }
+ transitionsArray += "}";
+ return "WorkflowNode[name=" + name + ",type=" + type + ",transitions=" + transitionsArray + "]";
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowPath.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowPath.java
new file mode 100644
index 0000000000..c187dfd3c9
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowPath.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+
+/**
+ * Workflow Path Data Object
+ *
+ * Represents a path within an "in-flight" workflow instance.
+ *
+ * Simple workflows consists of a single "root" path. Multiple paths occur when a workflow
+ * instance branches, therefore more than one concurrent path is taken.
+ *
+ * @author davidc
+ */
+public class WorkflowPath
+{
+ /** Unique id of Workflow Path */
+ public String id;
+
+ /** Workflow Instance this path is part of */
+ public WorkflowInstance instance;
+
+ /** The Workflow Node the path is at */
+ public WorkflowNode node;
+
+ /** Is the path still active? */
+ public boolean active;
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "WorkflowPath[id=" + id + ",instance=" + instance.toString() + ",active=" + active + ",node=" + node.toString()+ "]";
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java
new file mode 100644
index 0000000000..fe573799b0
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+
+
+/**
+ * Workflow Service.
+ *
+ * Client facing API for interacting with Alfresco Workflows and Tasks.
+ *
+ * @author davidc
+ */
+public interface WorkflowService
+{
+ //
+ // Workflow Definition Management
+ //
+
+ /**
+ * Deploy a Workflow Definition to the Alfresco Repository
+ *
+ * Note: The specified content object must be of type bpm:workflowdefinition.
+ * This type describes for which BPM engine the definition is appropriate.
+ *
+ * @param workflowDefinition the content object containing the definition
+ * @return workflow definition
+ */
+ public WorkflowDefinition deployDefinition(NodeRef workflowDefinition);
+
+ /**
+ * Undeploy an exisiting Workflow Definition
+ *
+ * TODO: Determine behaviour when "in-flight" workflow instances exist
+ *
+ * @param workflowDefinitionId the id of the definition to undeploy
+ */
+ public void undeployDefinition(String workflowDefinitionId);
+
+ /**
+ * Gets all deployed Workflow Definitions
+ *
+ * @return the deployed workflow definitions
+ */
+ public List getDefinitions();
+
+ /**
+ * Gets a Workflow Definition by unique Id
+ *
+ * @param workflowDefinitionId the workflow definition id
+ * @return the deployed workflow definition
+ */
+ public WorkflowDefinition getDefinitionById(String workflowDefinitionId);
+
+
+ //
+ // Workflow Instance Management
+ //
+
+
+ /**
+ * Start a Workflow Instance
+ *
+ * @param workflowDefinitionId the workflow definition id
+ * @param parameters the initial set of parameters used to populate the "Start Task" properties
+ * @return the initial workflow path
+ */
+ public WorkflowPath startWorkflow(String workflowDefinitionId, Map parameters);
+
+ /**
+ * Start a Workflow Instance from an existing "Start Task" template node held in the
+ * Repository. The node must be of the Type as described in the Workflow Definition.
+ *
+ * @param templateDefinition the node representing the Start Task properties
+ * @return the initial workflow path
+ */
+ public WorkflowPath startWorkflowFromTemplate(NodeRef templateDefinition);
+
+ /**
+ * Gets all "in-flight" workflow instances of the specified Workflow Definition
+ *
+ * @param workflowDefinitionId the workflow definition id
+ * @return the list of "in-fligth" workflow instances
+ */
+ public List getActiveWorkflows(String workflowDefinitionId);
+
+ /**
+ * Gets all Paths for the specified Workflow instance
+ *
+ * @param workflowId workflow instance id
+ * @return the list of workflow paths
+ */
+ public List getWorkflowPaths(String workflowId);
+
+ /**
+ * Cancel an "in-fligth" Workflow instance
+ *
+ * @param workflowId the workflow instance to cancel
+ * @return an updated representation of the workflow instance
+ */
+ public WorkflowInstance cancelWorkflow(String workflowId);
+
+ /**
+ * Signal the transition from one Workflow Node to another
+ *
+ * @param pathId the workflow path to signal on
+ * @param transition the transition to follow (or null, for the default transition)
+ * @return the updated workflow path
+ */
+ public WorkflowPath signal(String pathId, String transition);
+
+ /**
+ * Gets all Tasks associated with the specified path
+ *
+ * @param pathId the path id
+ * @return the list of associated tasks
+ */
+ public List getTasksForWorkflowPath(String pathId);
+
+
+ //
+ // Task Management
+ //
+
+ /**
+ * Gets a Task by unique Id
+ *
+ * @param taskId the task id
+ * @return the task
+ */
+ public WorkflowTask getTaskById(String taskId);
+
+ /**
+ * Gets all tasks assigned to the specified authority
+ *
+ * @param authority the authority
+ * @param state filter by specified workflow task state
+ * @return the list of assigned tasks
+ */
+ public List getAssignedTasks(String authority, WorkflowTaskState state);
+
+ /**
+ * Gets the pooled tasks available to the specified authority
+ *
+ * @param authority the authority
+ * @return the list of pooled tasks
+ */
+ public List getPooledTasks(String authority);
+
+ /**
+ * Update the Properties and Associations of a Task
+ *
+ * @param taskId the task id to update
+ * @param properties the map of properties to set on the task (or null, if none to set)
+ * @param add the map of items to associate with the task (or null, if none to add)
+ * @param remove the map of items to dis-associate with the task (or null, if none to remove)
+ * @return the update task
+ */
+ public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove);
+
+ /**
+ * End the Task (i.e. complete the task)
+ *
+ * @param taskId the task id to end
+ * @param transition the task transition to take on completion (or null, for the default transition)
+ * @return the updated task
+ */
+ public WorkflowTask endTask(String taskId, String transition);
+
+
+ // todo: workflow package apis
+ // createPackage
+
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java
new file mode 100644
index 0000000000..07fe5f2411
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowTask.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+
+
+/**
+ * Workflow Task Data Object
+ *
+ * Represents a human-oriented task within an "in-fligth" workflow instance
+ *
+ * @author davidc
+ */
+public class WorkflowTask
+{
+ /** Unique id of Task */
+ public String id;
+
+ /** Name of Task */
+ public String name;
+
+ /** Task State */
+ public WorkflowTaskState state;
+
+ /** Workflow path this Task is associated with */
+ public WorkflowPath path;
+
+ /** Task Definition */
+ public WorkflowTaskDefinition definition;
+
+ /** Task Properties as described by Task Definition */
+ public Map properties;
+
+ /** Task Associations as described by Task Definition */
+ public Map> associations;
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "WorkflowTask[id=" + id + ",name=" + name + ",state=" + state + ",def=" + definition + ",path=" + path.toString() + "]";
+ }
+
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowTaskDefinition.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowTaskDefinition.java
new file mode 100644
index 0000000000..6ab275b576
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowTaskDefinition.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
+
+
+/**
+ * Workflow Task Definition Data Object.
+ *
+ * Represents meta-data for a Workflow Task. The meta-data is described in terms
+ * of the Alfresco Data Dictionary.
+ *
+ * @author davidc
+ */
+public class WorkflowTaskDefinition
+{
+ /** Unique id of Workflow Task Definition */
+ public String id;
+
+ // TODO: Convert to TaskDefinition (derived from TypeDefinition)
+ /** Task Metadata */
+ public TypeDefinition metadata;
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "WorkflowTaskDefinition[id=" + id + ",metadata=" + metadata + "]";
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowTaskState.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowTaskState.java
new file mode 100644
index 0000000000..89c6d259ec
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowTaskState.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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 org.alfresco.service.cmr.workflow;
+
+
+/**
+ * Workflow Task State
+ *
+ * Represents the high-level state of Workflow Task (in relation to "in-flight"
+ * workflow instance).
+ *
+ * A user-defined task state may be represented as Task Property (and described
+ * by the Alfresco Data Dictionary).
+ *
+ * @author davidc
+ */
+public enum WorkflowTaskState
+{
+ IN_PROGRESS,
+ COMPLETED;
+}