From c3875d0d363368295492aecf4125a3f1ef0ff0b3 Mon Sep 17 00:00:00 2001 From: David Caruana Date: Thu, 17 Aug 2006 19:04:38 +0000 Subject: [PATCH] Workflow: 1) Add access to process definition warnings in workflow service deploy method 2) Fix nasty little bug found by Gav where jBPM beanshell access to Alfresco Node failed as it expected slightly different object types to those expected by Alfresco Javascript access to Node. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3542 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../messages/workflow-messages.properties | 2 +- .../java/org/alfresco/repo/jscript/Node.java | 39 +++++-- .../repo/workflow/WorkflowComponent.java | 5 +- .../repo/workflow/WorkflowDeployer.java | 10 +- .../repo/workflow/WorkflowServiceImpl.java | 23 +++- .../repo/workflow/jbpm/JBPMEngine.java | 102 +++++++++++++++--- .../repo/workflow/jbpm/JBPMEngineTest.java | 7 +- .../repo/workflow/jbpm/NodeConverter.java | 7 +- .../repo/workflow/jbpm/jbpm.varmapping.xml | 2 +- .../service/cmr/workflow/WorkflowService.java | 28 ++--- 10 files changed, 169 insertions(+), 56 deletions(-) diff --git a/config/alfresco/messages/workflow-messages.properties b/config/alfresco/messages/workflow-messages.properties index 99ffea3161..f488826abd 100644 --- a/config/alfresco/messages/workflow-messages.properties +++ b/config/alfresco/messages/workflow-messages.properties @@ -5,7 +5,7 @@ wf_review.workflow.title=Review & Approve wf_review.workflow.description=Send documents for approval wf_review.node.start.title=Start +wf_review.node.start.transition.review.title=Review wf_review.node.review.title=Review -wf_review.node.start.transition.review=Review wf_review.task.wf_submitReviewTask.title=Review wf_workflowmodel.type.wf_submitReviewTask.title=Review diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java index cce8c71aa7..e73c1e19fa 100644 --- a/source/java/org/alfresco/repo/jscript/Node.java +++ b/source/java/org/alfresco/repo/jscript/Node.java @@ -74,7 +74,7 @@ import org.springframework.util.StringUtils; * * @author Kevin Roast */ -public final class Node implements Serializable, Scopeable +public class Node implements Serializable, Scopeable { private static Log logger = LogFactory.getLog(Node.class); @@ -155,7 +155,6 @@ public final class Node implements Serializable, Scopeable this.nodeService = services.getNodeService(); this.imageResolver = resolver; this.scope = scope; - this.converter = new NodeValueConverter(); } /** @@ -405,7 +404,8 @@ public final class Node implements Serializable, Scopeable Serializable propValue = props.get(qname); // perform the conversion to a script safe value and store - this.properties.put(qname.toString(), converter.convertValueForScript(qname, propValue)); + + this.properties.put(qname.toString(), getValueConverter().convertValueForScript(qname, propValue)); } } @@ -891,14 +891,14 @@ public final class Node implements Serializable, Scopeable */ public void save() { - // persist properties back to the node in the DB + // persist properties back to the node in the DB Map props = new HashMap(getProperties().size()); for (String key : this.properties.keySet()) { Serializable value = (Serializable)this.properties.get(key); // perform the conversion from script wrapper object to repo serializable values - value = converter.convertValueForRepo(value); + value = getValueConverter().convertValueForRepo(value); props.put(createQName(key), value); } @@ -1209,7 +1209,7 @@ public final class Node implements Serializable, Scopeable { // get the value out for the specified key - make sure it is Serializable Object value = props.get((String)propId, props); - value = converter.convertValueForRepo((Serializable)value); + value = getValueConverter().convertValueForRepo((Serializable)value); aspectProps.put(createQName((String)propId), (Serializable)value); } } @@ -1635,12 +1635,37 @@ public final class Node implements Serializable, Scopeable // ------------------------------------------------------------------------------ // Value Conversion + + /** + * Gets the node value converter + * + * @return the node value converter + */ + protected NodeValueConverter getValueConverter() + { + if (converter == null) + { + converter = createValueConverter(); + } + return converter; + } + + + /** + * Constructs the node value converter + * + * @return the node value converter + */ + protected NodeValueConverter createValueConverter() + { + return new NodeValueConverter(); + } /** * Value converter with knowledge of Node specific value types */ - private final class NodeValueConverter extends ValueConverter + public class NodeValueConverter extends ValueConverter { /** * Convert an object from any repository serialized value to a valid script object. diff --git a/source/java/org/alfresco/repo/workflow/WorkflowComponent.java b/source/java/org/alfresco/repo/workflow/WorkflowComponent.java index e64de75a45..3ebe7fc26d 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowComponent.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowComponent.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowTask; @@ -46,9 +47,9 @@ public interface WorkflowComponent * * @param workflowDefinition the content object containing the definition * @param mimetype (optional) the mime type of the workflow definition - * @return workflow definition + * @return workflow deployment descriptor */ - public WorkflowDefinition deployDefinition(InputStream workflowDefinition, String mimetype); + public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype); /** * Is the specified Workflow Definition already deployed? diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index 5998437aeb..ac101d87f0 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -24,7 +24,7 @@ import javax.transaction.UserTransaction; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.cmr.view.ImporterException; -import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.transaction.TransactionService; @@ -148,17 +148,13 @@ public class WorkflowDeployer implements ApplicationListener if (workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype)) { if (logger.isDebugEnabled()) - { logger.debug("Workflow deployer: Definition '" + location + "' already deployed"); - } } else { - WorkflowDefinition def = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype); + WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype); if (logger.isInfoEnabled()) - { - logger.info("Workflow deployer: Deployed process definition '" + def.title + "' (version " + def.version + ") from '" + location + "'"); - } + logger.info("Workflow deployer: Deployed process definition '" + deployment.definition.title + "' (version " + deployment.definition.version + ") from '" + location + "' with " + deployment.problems.length + " problems"); } } } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index 770f8b2a0f..64ac955e93 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -25,6 +25,7 @@ import java.util.Map; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowPath; @@ -32,6 +33,8 @@ 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; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** @@ -42,6 +45,10 @@ import org.alfresco.service.namespace.QName; */ public class WorkflowServiceImpl implements WorkflowService { + // Logging support + private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow"); + + // Dependent services private BPMEngineRegistry registry; private WorkflowPackageComponent workflowPackageComponent; @@ -70,10 +77,20 @@ public class WorkflowServiceImpl implements WorkflowService /* (non-Javadoc) * @see org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java.lang.String, java.io.InputStream, java.lang.String) */ - public WorkflowDefinition deployDefinition(String engineId, InputStream workflowDefinition, String mimetype) + public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype) { WorkflowComponent component = getWorkflowComponent(engineId); - return component.deployDefinition(workflowDefinition, mimetype); + WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype); + + if (logger.isDebugEnabled() && deployment.problems.length > 0) + { + for (String problem : deployment.problems) + { + logger.debug("Workflow definition '" + deployment.definition.title + "' problem: " + problem); + } + } + + return deployment; } /* (non-Javadoc) @@ -88,7 +105,7 @@ public class WorkflowServiceImpl implements WorkflowService /* (non-Javadoc) * @see org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(org.alfresco.service.cmr.repository.NodeRef) */ - public WorkflowDefinition deployDefinition(NodeRef definitionContent) + public WorkflowDeployment deployDefinition(NodeRef definitionContent) { // TODO throw new UnsupportedOperationException(); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java index cc34a31a1c..51275d733b 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java @@ -46,6 +46,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowNode; @@ -68,11 +69,15 @@ 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.jpdl.par.ProcessArchive; +import org.jbpm.jpdl.xml.JpdlXmlReader; +import org.jbpm.jpdl.xml.Problem; 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; +import org.xml.sax.InputSource; /** @@ -178,23 +183,23 @@ public class JBPMEngine extends BPMEngine /* (non-Javadoc) * @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#deployDefinition(java.io.InputStream) */ - public WorkflowDefinition deployDefinition(final InputStream workflowDefinition, final String mimetype) + public WorkflowDeployment deployDefinition(final InputStream workflowDefinition, final String mimetype) { try { - return (WorkflowDefinition)jbpmTemplate.execute(new JbpmCallback() + return (WorkflowDeployment)jbpmTemplate.execute(new JbpmCallback() { public Object doInJbpm(JbpmContext context) { // construct process definition - ProcessDefinition def = createProcessDefinition(workflowDefinition, mimetype); + CompiledProcessDefinition compiledDef = compileProcessDefinition(workflowDefinition, mimetype); // deploy the parsed definition - context.deployProcessDefinition(def); + context.deployProcessDefinition(compiledDef.def); // return deployed definition - WorkflowDefinition workflowDef = createWorkflowDefinition(def); - return workflowDef; + WorkflowDeployment workflowDeployment = createWorkflowDeployment(compiledDef); + return workflowDeployment; } }); } @@ -217,11 +222,11 @@ public class JBPMEngine extends BPMEngine public Boolean doInJbpm(JbpmContext context) { // create process definition from input stream - ProcessDefinition processDefinition = createProcessDefinition(workflowDefinition, mimetype); + CompiledProcessDefinition processDefinition = compileProcessDefinition(workflowDefinition, mimetype); // retrieve process definition from Alfresco Repository GraphSession graphSession = context.getGraphSession(); - ProcessDefinition existingDefinition = graphSession.findLatestProcessDefinition(processDefinition.getName()); + ProcessDefinition existingDefinition = graphSession.findLatestProcessDefinition(processDefinition.def.getName()); return (existingDefinition == null) ? false : true; } }); @@ -828,6 +833,53 @@ public class JBPMEngine extends BPMEngine // Helpers... // + + /** + * Process Definition with accompanying problems + */ + private static class CompiledProcessDefinition + { + public CompiledProcessDefinition(ProcessDefinition def, List problems) + { + this.def = def; + this.problems = new String[problems.size()]; + int i = 0; + for (Problem problem : problems) + { + this.problems[i++] = problem.toString(); + } + } + + protected ProcessDefinition def; + protected String[] problems; + } + + /** + * JpdlXmlReader with access to problems encountered during compile. + * + * @author davidc + */ + private static class JBPMEngineJpdlXmlReader extends JpdlXmlReader + { + private static final long serialVersionUID = -753730152120696221L; + + public JBPMEngineJpdlXmlReader(InputStream inputStream) + { + super(new InputSource(inputStream)); + } + + /** + * Gets the problems + * + * @return problems + */ + public List getProblems() + { + return problems; + } + } + + /** * Construct a Process Definition from the provided Process Definition stream * @@ -835,10 +887,11 @@ public class JBPMEngine extends BPMEngine * @param mimetype mimetype of stream * @return process definition */ - protected ProcessDefinition createProcessDefinition(InputStream definitionStream, String mimetype) + @SuppressWarnings("unchecked") + protected CompiledProcessDefinition compileProcessDefinition(InputStream definitionStream, String mimetype) { String actualMimetype = (mimetype == null) ? MimetypeMap.MIMETYPE_ZIP : mimetype; - ProcessDefinition def = null; + CompiledProcessDefinition compiledDef = null; // parse process definition from jBPM process archive file @@ -848,7 +901,9 @@ public class JBPMEngine extends BPMEngine try { zipInputStream = new ZipInputStream(definitionStream); - def = ProcessDefinition.parseParZipInputStream(zipInputStream); + ProcessArchive reader = new ProcessArchive(zipInputStream); + ProcessDefinition def = reader.parseProcessDefinition(); + compiledDef = new CompiledProcessDefinition(def, reader.getProblems()); } catch(Exception e) { @@ -869,15 +924,17 @@ public class JBPMEngine extends BPMEngine { try { - def = ProcessDefinition.parseXmlInputStream(definitionStream); + JBPMEngineJpdlXmlReader jpdlReader = new JBPMEngineJpdlXmlReader(definitionStream); + ProcessDefinition def = jpdlReader.readProcessDefinition(); + compiledDef = new CompiledProcessDefinition(def, jpdlReader.getProblems()); } catch(Exception e) { throw new JbpmException("Failed to parse process definition from jBPM xml stream", e); } } - - return def; + + return compiledDef; } /** @@ -1152,12 +1209,13 @@ public class JBPMEngine extends BPMEngine String name = key.toPrefixString(this.namespaceService); if (value instanceof NodeRef) { - value = new org.alfresco.repo.jscript.Node((NodeRef)value, serviceRegistry, null); + value = new JBPMNode((NodeRef)value, serviceRegistry); } instance.setVariableLocally(name, value); } } + /** * Convert a list of Alfresco Authorities to a list of authority Names * @@ -1391,6 +1449,20 @@ public class JBPMEngine extends BPMEngine return taskDef; } + /** + * Creates a Workflow Deployment + * + * @param compiledDef compiled JBPM process definition + * @return workflow deployment + */ + protected WorkflowDeployment createWorkflowDeployment(CompiledProcessDefinition compiledDef) + { + WorkflowDeployment deployment = new WorkflowDeployment(); + deployment.definition = createWorkflowDefinition(compiledDef.def); + deployment.problems = compiledDef.problems; + return deployment; + } + /** * Get the Workflow Task State for the specified JBoss JBPM Task * diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java index 7f6a506c37..93146132b5 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java @@ -35,6 +35,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowPath; @@ -72,7 +73,8 @@ public class JBPMEngineTest extends BaseSpringTest // 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); + WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML); + testWorkflowDef = deployment.definition; assertNotNull(testWorkflowDef); assertEquals("Test", testWorkflowDef.title); assertEquals("1", testWorkflowDef.version); @@ -95,7 +97,8 @@ public class JBPMEngineTest extends BaseSpringTest public void testDeployWorkflow() throws Exception { ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/test_processdefinition.xml"); - testWorkflowDef = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML); + WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML); + testWorkflowDef = deployment.definition; assertNotNull(testWorkflowDef); assertEquals("Test", testWorkflowDef.title); assertEquals("2", testWorkflowDef.version); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/NodeConverter.java b/source/java/org/alfresco/repo/workflow/jbpm/NodeConverter.java index 42665f779a..8f74a775b7 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/NodeConverter.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/NodeConverter.java @@ -16,7 +16,6 @@ */ package org.alfresco.repo.workflow.jbpm; -import org.alfresco.repo.jscript.Node; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.NodeRef; import org.jbpm.context.exe.Converter; @@ -46,7 +45,7 @@ public class NodeConverter implements Converter { return true; } - return (value.getClass() == Node.class); + return (value.getClass() == JBPMNode.class); } /* (non-Javadoc) @@ -57,7 +56,7 @@ public class NodeConverter implements Converter Object converted = null; if (o != null) { - converted = ((Node)o).getNodeRef().toString(); + converted = ((JBPMNode)o).getNodeRef().toString(); } return converted; } @@ -72,7 +71,7 @@ public class NodeConverter implements Converter { BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null); ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY); - reverted = new Node(new NodeRef((String)o), serviceRegistry, null); + reverted = new JBPMNode(new NodeRef((String)o), serviceRegistry); } return reverted; } diff --git a/source/java/org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml b/source/java/org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml index 2fc5dd3e4a..fa44ad9758 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml +++ b/source/java/org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml @@ -123,7 +123,7 @@ - + diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java index 9ba617184d..fd3d55165d 100644 --- a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java +++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java @@ -43,11 +43,22 @@ public interface WorkflowService * * @param engineId the bpm engine id * @param workflowDefinition the workflow definition - * @param overwrite true => redeploy, if process definition already exists - * @return mimetype the mimetype of the workflow definition + * @param mimetype the mimetype of the workflow definition + * @return workflow deployment descriptor */ - public WorkflowDefinition deployDefinition(String engineId, InputStream workflowDefinition, String mimetype); + public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype); + /** + * 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 deployment descriptor + */ + public WorkflowDeployment deployDefinition(NodeRef workflowDefinition); + /** * Is the specified Workflow Definition already deployed? * @@ -61,17 +72,6 @@ public interface WorkflowService */ public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype); - /** - * 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 *