Workflow:

1) Support for localisation of all Workflow definitions
2) Consolidate on id, title & description fields for all Workflow API objects
3) Add WorkflowTransition object to Workflow APIs
4) Fix up damage of above changes (web client etc)

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3528 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2006-08-16 18:07:27 +00:00
parent 640195064a
commit 7c670da1b0
18 changed files with 239 additions and 46 deletions

View File

@@ -96,7 +96,7 @@ public interface TaskComponent
* @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);
public WorkflowTask endTask(String taskId, String transitionId);
}

View File

@@ -133,7 +133,7 @@ public interface WorkflowComponent
* @param transition the transition to follow (or null, for the default transition)
* @return the updated workflow path
*/
public WorkflowPath signal(String pathId, String transition);
public WorkflowPath signal(String pathId, String transitionId);
/**
* Gets all Tasks associated with the specified path

View File

@@ -157,7 +157,7 @@ public class WorkflowDeployer implements ApplicationListener
WorkflowDefinition def = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype);
if (logger.isInfoEnabled())
{
logger.info("Workflow deployer: Deployed process definition '" + def.name + "' (version " + def.version + ") from '" + location + "'");
logger.info("Workflow deployer: Deployed process definition '" + def.title + "' (version " + def.version + ") from '" + location + "'");
}
}
}

View File

@@ -28,6 +28,7 @@ import java.util.Set;
import java.util.Map.Entry;
import java.util.zip.ZipInputStream;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -52,6 +53,7 @@ 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.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.hibernate.Query;
@@ -68,6 +70,7 @@ 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.springframework.util.StringUtils;
import org.springmodules.workflow.jbpm31.JbpmCallback;
import org.springmodules.workflow.jbpm31.JbpmTemplate;
@@ -101,6 +104,11 @@ public class JBPMEngine extends BPMEngine
"and ti.isOpen = false " +
"and ti.end is not null";
// I18N labels
private final static String TITLE_LABEL = "title";
private final static String DESC_LABEL = "description";
private final static String DEFAULT_TRANSITION_LABEL = "bpm_businessprocessmodel.transition";
/**
* Sets the JBPM Template used for accessing JBoss JBPM in the correct context
@@ -1196,6 +1204,22 @@ public class JBPMEngine extends BPMEngine
return authorities;
}
/**
* Get an I18N Label for a workflow item
*
* @param displayId message resource id lookup
* @param labelKey label to lookup (title or description)
* @param defaultLabel default value if not found in message resource bundle
* @return the label
*/
private String getLabel(String displayId, String labelKey, String defaultLabel)
{
String key = StringUtils.replace(displayId, ":", "_");
key += "." + labelKey;
String label = I18NUtil.getMessage(key);
return (label == null) ? defaultLabel : label;
}
//
// Workflow Data Object Creation...
@@ -1226,8 +1250,11 @@ public class JBPMEngine extends BPMEngine
@SuppressWarnings("unchecked")
protected WorkflowNode createWorkflowNode(Node node)
{
String name = node.getName();
String processName = node.getProcessDefinition().getName();
WorkflowNode workflowNode = new WorkflowNode();
workflowNode.name = node.getName();
workflowNode.title = getLabel(processName + ".node." + name, TITLE_LABEL, name);
workflowNode.description = getLabel(processName + ".node." + name, DESC_LABEL, workflowNode.title);
if (node instanceof HibernateProxy)
{
Node realNode = (Node)((HibernateProxy)node).getHibernateLazyInitializer().getImplementation();
@@ -1240,18 +1267,45 @@ public class JBPMEngine extends BPMEngine
// 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 == null) ? 0 : transitions.size()];
workflowNode.transitions = new WorkflowTransition[(transitions == null) ? 0 : transitions.size()];
if (transitions != null)
{
int i = 0;
for (Transition transition : (List<Transition>)transitions)
{
workflowNode.transitions[i++] = transition.getName();
workflowNode.transitions[i++] = createWorkflowTransition(transition);
}
}
return workflowNode;
}
/**
* Create a Workflow Transition
*
* @param transition JBoss JBPM Transition
* @return Workflow Transition
*/
protected WorkflowTransition createWorkflowTransition(Transition transition)
{
WorkflowTransition workflowTransition = new WorkflowTransition();
workflowTransition.id = transition.getName();
Node node = transition.getFrom();
workflowTransition.isDefault = node.getDefaultLeavingTransition().equals(transition);
if (workflowTransition.id.length() == 0)
{
workflowTransition.title = getLabel(DEFAULT_TRANSITION_LABEL, TITLE_LABEL, workflowTransition.id);
workflowTransition.description = getLabel(DEFAULT_TRANSITION_LABEL, DESC_LABEL, workflowTransition.title);
}
else
{
String nodeName = node.getName();
String processName = node.getProcessDefinition().getName();
workflowTransition.title = getLabel(processName + ".node." + nodeName + ".transition." + workflowTransition.id, TITLE_LABEL, workflowTransition.id);
workflowTransition.description = getLabel(processName + ".node." + nodeName + ".transition." + workflowTransition.id, DESC_LABEL, workflowTransition.title);
}
return workflowTransition;
}
/**
* Creates a Workflow Instance
*
@@ -1276,15 +1330,16 @@ public class JBPMEngine extends BPMEngine
protected WorkflowDefinition createWorkflowDefinition(ProcessDefinition definition)
{
WorkflowDefinition workflowDef = new WorkflowDefinition();
String name = definition.getName();
workflowDef.title = getLabel(name + ".workflow", TITLE_LABEL, name);
workflowDef.description = getLabel(name + ".workflow", DESC_LABEL, workflowDef.title);
workflowDef.id = createGlobalId(new Long(definition.getId()).toString());
workflowDef.version = new Integer(definition.getVersion()).toString();
workflowDef.name = definition.getName();
Task startTask = definition.getTaskMgmtDefinition().getStartTask();
if (startTask != null)
{
workflowDef.startTaskDefinition = createWorkflowTaskDefinition(startTask);
}
return workflowDef;
}
@@ -1298,11 +1353,27 @@ public class JBPMEngine extends BPMEngine
{
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());
workflowTask.properties = getTaskProperties(task);
String name = task.getName();
String processName = task.getTask().getProcessDefinition().getName();
workflowTask.title = getLabel(processName + ".node." + name, TITLE_LABEL, name);
if (workflowTask.title == null)
{
workflowTask.title = workflowTask.definition.metadata.getTitle();
if (workflowTask.title == null)
{
workflowTask.title = name;
}
}
workflowTask.description = getLabel(processName + ".node." + name, DESC_LABEL, workflowTask.title);
if (workflowTask.description == null)
{
String description = workflowTask.definition.metadata.getDescription();
workflowTask.description = (description == null) ? workflowTask.title : description;
}
return workflowTask;
}

View File

@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.workflow.BPMEngineRegistry;
@@ -58,19 +59,22 @@ public class JBPMEngineTest extends BaseSpringTest
NodeRef testNodeRef;
//@Override
@Override
protected void onSetUpInTransaction() throws Exception
{
BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry");
workflowComponent = registry.getWorkflowComponent("jbpm");
taskComponent = registry.getTaskComponent("jbpm");
// deploy test process messages
I18NUtil.registerResourceBundle("org/alfresco/repo/workflow/jbpm/test-messages");
// deploy test process definition
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/test_processdefinition.xml");
assertFalse(workflowComponent.isDefinitionDeployed(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML));
testWorkflowDef = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
assertNotNull(testWorkflowDef);
assertEquals("Test", testWorkflowDef.name);
assertEquals("Test", testWorkflowDef.title);
assertEquals("1", testWorkflowDef.version);
assertTrue(workflowComponent.isDefinitionDeployed(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML));
@@ -93,7 +97,7 @@ public class JBPMEngineTest extends BaseSpringTest
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/test_processdefinition.xml");
testWorkflowDef = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
assertNotNull(testWorkflowDef);
assertEquals("Test", testWorkflowDef.name);
assertEquals("Test", testWorkflowDef.title);
assertEquals("2", testWorkflowDef.version);
}
@@ -295,7 +299,7 @@ public class JBPMEngineTest extends BaseSpringTest
WorkflowDefinition workflowDef = getTestDefinition();
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, null);
assertNotNull(path);
WorkflowPath updatedPath = workflowComponent.signal(path.id, path.node.transitions[1]);
WorkflowPath updatedPath = workflowComponent.signal(path.id, path.node.transitions[1].id);
assertNotNull(updatedPath);
}
@@ -312,15 +316,17 @@ public class JBPMEngineTest extends BaseSpringTest
List<WorkflowTask> tasks = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks);
assertEquals(1, tasks.size());
WorkflowTask updatedTask = taskComponent.endTask(tasks.get(0).id, path.node.transitions[0]);
WorkflowTask updatedTask = taskComponent.endTask(tasks.get(0).id, path.node.transitions[0].id);
assertNotNull(updatedTask);
List<WorkflowTask> completedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.COMPLETED);
List<WorkflowTask> completedTasks = taskComponent.getAssignedTasks("System", WorkflowTaskState.COMPLETED);
assertNotNull(completedTasks);
assertEquals(0, completedTasks.size());
completedTasks = filterTasksByWorkflowInstance(completedTasks, path.instance.id);
assertEquals(1, completedTasks.size());
List<WorkflowTask> assignedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
assertNotNull(assignedTasks);
assignedTasks = filterTasksByWorkflowInstance(assignedTasks, path.instance.id);
assertEquals(1, assignedTasks.size());
assertEquals("Review", assignedTasks.get(0).name);
assertEquals("Review", assignedTasks.get(0).title);
}
@@ -342,6 +348,7 @@ public class JBPMEngineTest extends BaseSpringTest
assertEquals(WorkflowTaskState.COMPLETED, updatedTask.state);
List<WorkflowTask> completedTasks = taskComponent.getAssignedTasks("System", WorkflowTaskState.COMPLETED);
assertNotNull(completedTasks);
completedTasks = filterTasksByWorkflowInstance(completedTasks, path.instance.id);
assertEquals(1, completedTasks.size());
assertEquals(WorkflowTaskState.COMPLETED, completedTasks.get(0).state);
}
@@ -361,7 +368,7 @@ public class JBPMEngineTest extends BaseSpringTest
assertEquals(1, tasks1.size());
WorkflowTask getTask = taskComponent.getTaskById(tasks1.get(0).id);
assertNotNull(getTask);
assertEquals(getTask.name, tasks1.get(0).name);
assertEquals(getTask.title, tasks1.get(0).title);
}
@@ -394,7 +401,7 @@ public class JBPMEngineTest extends BaseSpringTest
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
WorkflowTask updatedTask = taskComponent.endTask(tasks1.get(0).id, path.node.transitions[0]);
WorkflowTask updatedTask = taskComponent.endTask(tasks1.get(0).id, path.node.transitions[0].id);
assertNotNull(updatedTask);
}
@@ -409,4 +416,25 @@ public class JBPMEngineTest extends BaseSpringTest
return testWorkflowDef;
}
/**
* Filter task list by workflow instance
*
* @param tasks
* @param processInstanceId
* @return
*/
private List<WorkflowTask> filterTasksByWorkflowInstance(List<WorkflowTask> tasks, String workflowInstanceId)
{
List<WorkflowTask> filteredTasks = new ArrayList<WorkflowTask>();
for (WorkflowTask task : tasks)
{
if (task.path.instance.id.equals(workflowInstanceId))
{
filteredTasks.add(task);
}
}
return filteredTasks;
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="Review and Approve">
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:review">
<swimlane name="Initiator"></swimlane>
<start-state name="start">
<task name="wf:submitReviewTask" swimlane="Initiator" blocking="true">
@@ -14,7 +14,7 @@
<swimlane name="Reviewer">
<assignment actor-id="#{reviewer}"></assignment>
</swimlane>
<task-node name="Review">
<task-node name="review">
<task name="Review" duedate="1 business day" blocking="true" swimlane="Reviewer">
<controller>
<variable name="comment" access="read,write,required"></variable>

View File

@@ -0,0 +1,14 @@
test.workflow.title=Test
test.workflow.description=Workflow for testing purposes
test.node.start.title=Start
test.node.start.description=The Start
test.node.review.title=Review
test.node.review.description=The Review
test.node.end.title=End
test.node.end.description=The End
test.node.start.transition.review=Review
test.task.submit.title=Submit Review Title
test.task.submit.description=Submit Review Description

View File

@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="Test">
<swimlane name="Initiator"></swimlane>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="test">
<swimlane name="initiator"></swimlane>
<start-state name="start">
<task name="Submit" swimlane="Initiator">
<task name="submit" swimlane="initiator">
<controller>
<variable name="reviewer" access="write,required" />
<variable name="testNode" access="write,required" />
</controller>
</task>
<transition name="" to="Review">
<transition name="" to="review">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
<expression>
@@ -23,13 +24,14 @@
</script>
</action>
</transition>
<transition name="end" to="end">
</transition>
<transition name="end" to="end"/>
</start-state>
<swimlane name="Reviewer">
<swimlane name="reviewer">
<assignment actor-id="#{reviewer}"></assignment>
</swimlane>
<task-node name="Review">
<task-node name="review">
<event type="node-enter">
<script>
System.out.println("the reviewer is " + reviewer);
@@ -37,12 +39,14 @@
System.out.println("scriptResult = " + scriptResult);
</script>
</event>
<task name="Review" duedate="1 business day" blocking="true" swimlane="Reviewer">
<task name="review" duedate="1 business day" blocking="true" swimlane="reviewer">
<controller>
<variable name="comment" access="read,write,required"></variable>
</controller>
</task>
<transition name="" to="end"></transition>
</task-node>
<end-state name="end"></end-state>
</process-definition>

View File

@@ -30,8 +30,11 @@ public class WorkflowDefinition
/** Workflow Definition version */
public String version;
/** Workflow Definition name */
public String name;
/** Workflow Definition Title (Localised) */
public String title;
/** Workflow Definition Description (Localised) */
public String description;
/** Task Definition for Workflow Start Task (Optional) */
public WorkflowTaskDefinition startTaskDefinition;
@@ -42,6 +45,6 @@ public class WorkflowDefinition
*/
public String toString()
{
return "WorkflowDefinition[id=" + id + ",version=" + version + ",name=" + name + ",startTask=" + startTaskDefinition.toString() + "]";
return "WorkflowDefinition[id=" + id + ",version=" + version + ",title=" + title + ",startTask=" + startTaskDefinition.toString() + "]";
}
}

View File

@@ -26,9 +26,12 @@ package org.alfresco.service.cmr.workflow;
*/
public class WorkflowNode
{
/** Name of the Workflow Node */
public String name;
/** Workflow Node Title (Localised) */
public String title;
/** Workflow Node Description (Localised) */
public String description;
/** Type of the Workflow Node (typically this is BPM engine specific - informational only */
public String type;
@@ -36,7 +39,7 @@ public class WorkflowNode
public boolean isTaskNode;
/** The transitions leaving this node (or null, if none) */
public String[] transitions;
public WorkflowTransition[] transitions;
/* (non-Javadoc)
@@ -50,6 +53,6 @@ public class WorkflowNode
transitionsArray += ((i == 0) ? "" : ",") + "'" + transitions[i] + "'";
}
transitionsArray += "}";
return "WorkflowNode[name=" + name + ",type=" + type + ",transitions=" + transitionsArray + "]";
return "WorkflowNode[title=" + title + ",type=" + type + ",transitions=" + transitionsArray + "]";
}
}

View File

@@ -151,7 +151,7 @@ public interface WorkflowService
* @param transition the transition to follow (or null, for the default transition)
* @return the updated workflow path
*/
public WorkflowPath signal(String pathId, String transition);
public WorkflowPath signal(String pathId, String transitionId);
/**
* Gets all Tasks associated with the specified path
@@ -209,7 +209,7 @@ public interface WorkflowService
* @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);
public WorkflowTask endTask(String taskId, String transitionId);
/**
* Create a Workflow Package (a container of content to route through the Workflow).

View File

@@ -25,7 +25,7 @@ import org.alfresco.service.namespace.QName;
/**
* Workflow Task Data Object
*
* Represents a human-oriented task within an "in-fligth" workflow instance
* Represents a human-oriented task within an "in-flight" workflow instance
*
* @author davidc
*/
@@ -34,8 +34,11 @@ public class WorkflowTask
/** Unique id of Task */
public String id;
/** Name of Task */
public String name;
/** Task Title (Localised) */
public String title;
/** Task Description (Localised) */
public String description;
/** Task State */
public WorkflowTaskState state;
@@ -55,7 +58,6 @@ public class WorkflowTask
public String toString()
{
String propCount = (properties == null) ? "null" : "" + properties.size();
return "WorkflowTask[id=" + id + ",name=" + name + ",state=" + state + ",props=" + propCount + ",def=" + definition + ",path=" + path.toString() + "]";
return "WorkflowTask[id=" + id + ",title=" + title + ",state=" + state + ",props=" + propCount + ",def=" + definition + ",path=" + path.toString() + "]";
}
}

View File

@@ -32,7 +32,6 @@ public class WorkflowTaskDefinition
/** Unique id of Workflow Task Definition */
public String id;
// TODO: Convert to TaskDefinition (derived from TypeDefinition)
/** Task Metadata */
public TypeDefinition metadata;

View File

@@ -0,0 +1,46 @@
/*
* 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 Transition.
*
* @author davidc
*/
public class WorkflowTransition
{
/** Transition Id */
public String id;
/** Transition Title (Localised) */
public String title;
/** Transition Description (Localised) */
public String description;
/** Is this the default transition */
public boolean isDefault;
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString()
{
return "WorkflowTransition[id=" + id + ",title=" + title + "]";
}
}