Workflow checkpoint:

- BPM Engine Registry & Plug-in SPIs
- First JBoss JBPM based Implementation of SPIs
- Workflow Service Implementation
- Tests

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3436 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2006-07-28 22:58:02 +00:00
parent 0c35c285bd
commit 25123a4504
22 changed files with 2556 additions and 3 deletions

View File

@@ -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);
}
}

View File

@@ -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<String, WorkflowDefinitionComponent> workflowDefinitionComponents;
private Map<String, WorkflowComponent> workflowComponents;
private Map<String, TaskComponent> taskComponents;
/**
* Construct
*/
public BPMEngineRegistry()
{
workflowDefinitionComponents = new HashMap<String, WorkflowDefinitionComponent>();
workflowComponents = new HashMap<String, WorkflowComponent>();
taskComponents = new HashMap<String, TaskComponent>();
}
/**
* 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];
}
}

View File

@@ -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<WorkflowTask> 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<WorkflowTask> getPooledTasks(List<String> 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<QName, Serializable> properties, Map<QName, List<NodeRef>> add, Map<QName, List<NodeRef>> 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);
}

View File

@@ -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<QName, Serializable> 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<WorkflowInstance> getActiveWorkflows(String workflowDefinitionId);
/**
* Gets all Paths for the specified Workflow instance
*
* @param workflowId workflow instance id
* @return the list of workflow paths
*/
public List<WorkflowPath> 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<WorkflowTask> getTasksForWorkflowPath(String pathId);
}

View File

@@ -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<WorkflowDefinition> getDefinitions();
/**
* Gets a Workflow Definition by unique Id
*
* @param workflowDefinitionId the workflow definition id
* @return the deployed workflow definition
*/
public WorkflowDefinition getDefinitionById(String workflowDefinitionId);
}

View File

@@ -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<WorkflowDefinition> getDefinitions()
{
List<WorkflowDefinition> definitions = new ArrayList<WorkflowDefinition>(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<QName, Serializable> 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<WorkflowInstance> 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<WorkflowPath> 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<WorkflowTask> 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<WorkflowTask> getAssignedTasks(String authority, WorkflowTaskState state)
{
List<WorkflowTask> tasks = new ArrayList<WorkflowTask>(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<WorkflowTask> getPooledTasks(String authority)
{
// TODO: Expand authorities to include associated groups (and parent groups)
List<String> authorities = new ArrayList<String>();
authorities.add(authority);
List<WorkflowTask> tasks = new ArrayList<WorkflowTask>(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<QName, Serializable> properties, Map<QName, List<NodeRef>> add, Map<QName, List<NodeRef>> 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;
}
}

View File

@@ -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<WorkflowDefinition> workflowDefs = workflowService.getDefinitions();
assertNotNull(workflowDefs);
assertTrue(workflowDefs.size() > 0);
}
public void testStartWorkflow()
{
List<WorkflowDefinition> 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);
}
}

View File

@@ -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<WorkflowDefinition> getDefinitions()
{
try
{
return (List<WorkflowDefinition>)jbpmTemplate.execute(new JbpmCallback()
{
public Object doInJbpm(JbpmContext context)
{
GraphSession graphSession = context.getGraphSession();
List<ProcessDefinition> processDefs = (List<ProcessDefinition>)graphSession.findLatestProcessDefinitions();
List<WorkflowDefinition> workflowDefs = new ArrayList<WorkflowDefinition>(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<QName, Serializable> 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<WorkflowInstance> getActiveWorkflows(final String workflowDefinitionId)
{
try
{
return (List<WorkflowInstance>) jbpmTemplate.execute(new JbpmCallback()
{
public Object doInJbpm(JbpmContext context)
{
GraphSession graphSession = context.getGraphSession();
List<ProcessInstance> processInstances = graphSession.findProcessInstances(getJbpmId(workflowDefinitionId));
List<WorkflowInstance> workflowInstances = new ArrayList<WorkflowInstance>(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<WorkflowPath> getWorkflowPaths(final String workflowId)
{
try
{
return (List<WorkflowPath>) 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<Token> tokens = processInstance.findAllTokens();
List<WorkflowPath> paths = new ArrayList<WorkflowPath>(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<WorkflowTask> getTasksForWorkflowPath(final String pathId)
{
try
{
return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback()
{
public List<WorkflowTask> doInJbpm(JbpmContext context)
{
// retrieve tasks at specified workflow path
GraphSession graphSession = context.getGraphSession();
Token token = getWorkflowToken(graphSession, pathId);
TaskMgmtSession taskSession = context.getTaskMgmtSession();
List<TaskInstance> tasks = taskSession.findTaskInstancesByToken(token.getId());
List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(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<WorkflowTask> getAssignedTasks(final String authority, final WorkflowTaskState state)
{
try
{
return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback()
{
public List<WorkflowTask> doInJbpm(JbpmContext context)
{
// retrieve tasks assigned to authority
TaskMgmtSession taskSession = context.getTaskMgmtSession();
List<TaskInstance> tasks = taskSession.findTaskInstances(authority);
List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(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<WorkflowTask> getPooledTasks(final List<String> authorities)
{
try
{
return (List<WorkflowTask>) jbpmTemplate.execute(new JbpmCallback()
{
public List<WorkflowTask> doInJbpm(JbpmContext context)
{
// retrieve pooled tasks for specified authorities
TaskMgmtSession taskSession = context.getTaskMgmtSession();
List<TaskInstance> tasks = taskSession.findPooledTaskInstances(authorities);
List<WorkflowTask> workflowTasks = new ArrayList<WorkflowTask>(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<QName, Serializable> properties, Map<QName, List<NodeRef>> add, Map<QName, List<NodeRef>> 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<QName, Serializable> 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<QName, Serializable> 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<Transition>)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;
}
}
}

View File

@@ -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<WorkflowDefinition> 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<WorkflowInstance> 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<WorkflowInstance> instances = workflowComponent.getActiveWorkflows(workflowDef.id);
assertNotNull(instances);
assertEquals(1, instances.size());
List<WorkflowPath> 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<WorkflowInstance> 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<WorkflowInstance> instances2 = workflowComponent.getActiveWorkflows(workflowDef.id);
assertNotNull(instances2);
assertEquals(0, instances2.size());
}
public void testSignal()
{
WorkflowDefinition workflowDef = getTestDefinition();
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
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<QName, Serializable> parameters = new HashMap<QName, Serializable>();
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<WorkflowTask> completedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.COMPLETED);
assertNotNull(completedTasks);
assertEquals(0, completedTasks.size());
List<WorkflowTask> 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<QName, Serializable> parameters = new HashMap<QName, Serializable>();
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
assertNotNull(path);
assertNotNull(path);
List<WorkflowTask> 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<WorkflowDefinition> workflowDefs = workflowDefinitionComponent.getDefinitions();
for (WorkflowDefinition workflowDef : workflowDefs)
{
if (workflowDef.name.equals("Review and Approve"))
{
return workflowDef;
}
}
fail("Test Workflow Definition not found");
return null;
}
}