diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 0da6b3f999..c03d087d7b 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -216,7 +216,12 @@ /${spaces.company_home.childname}/${spaces.dictionary.childname} alfresco/bootstrap/customWebClientExtensionSpace.xml - + + + /${spaces.company_home.childname}/${spaces.dictionary.childname} + alfresco/bootstrap/customWorkflowDefsSpace.acp + + diff --git a/config/alfresco/bootstrap/customWorkflowDefsSpace.acp b/config/alfresco/bootstrap/customWorkflowDefsSpace.acp new file mode 100644 index 0000000000..e0750a5037 Binary files /dev/null and b/config/alfresco/bootstrap/customWorkflowDefsSpace.acp differ diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 64df8f28c0..cc0251fb47 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -174,4 +174,6 @@ patch.AVMProperties.result=Properties were moved. patch.customModels.description=Adds Models space to Data Dictionary. patch.customMessages.description=Adds Messages space to Data Dictionary. -patch.customWebClientExtension.description=Adds Web Client Extension space to Data Dictionary. +patch.customWebClientExtension.description=Adds Web Client Extension space to Data Dictionary. + +patch.customWorkflowDefs.description=Adds Workflow Definitions space to Data Dictionary. diff --git a/config/alfresco/model/bpmModel.xml b/config/alfresco/model/bpmModel.xml index a8ac800697..7911884dbf 100644 --- a/config/alfresco/model/bpmModel.xml +++ b/config/alfresco/model/bpmModel.xml @@ -269,6 +269,32 @@ + + + + + + + Workflow Process Definition + cm:content + + + Workflow Name + d:text + + + Workflow Engine Id + d:text + true + jbpm + + + Workflow Deployed + d:boolean + false + + + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index d00b1b5825..dca8aef4fe 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -1017,4 +1017,25 @@ + + patch.customWorkflowDefs + patch.customWorkflowDefs.description + 0 + 105 + 106 + + + + + + /${spaces.company_home.childname}/${spaces.dictionary.childname}/app:workflow_defs + + + + /${spaces.company_home.childname}/${spaces.dictionary.childname} + alfresco/bootstrap/customWorkflowDefsSpace.acp + + + + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 6b17d748b2..ebff9289bb 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=105 +version.schema=106 diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index 2194d39088..6a7e9d07e3 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -21,6 +21,8 @@ + + @@ -106,5 +108,12 @@ - + + + + + + + + diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java b/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java new file mode 100644 index 0000000000..b1abbe05f0 --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.workflow; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.ContentServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Workflow Definition type behaviour. + * + * @author JanV + */ +public class WorkflowDefinitionType implements ContentServicePolicies.OnContentUpdatePolicy, + NodeServicePolicies.OnUpdatePropertiesPolicy, + NodeServicePolicies.BeforeDeleteNodePolicy +{ + // logger + private static Log logger = LogFactory.getLog(WorkflowDefinitionType.class); + + /** The node service */ + private NodeService nodeService; + + /** The policy component */ + private PolicyComponent policyComponent; + + /** The workflow service */ + private WorkflowService workflowService; + + + + /** + * Set the node service + * + * @param nodeService the node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the policy component + * + * @param policyComponent the policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Set the workflow service + * + * @param workflowService the workflow service + */ + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } + + + /** + * The initialise method + */ + public void init() + { + // Register interest in the onContentUpdate policy for the workflow definition type + policyComponent.bindClassBehaviour( + ContentServicePolicies.ON_CONTENT_UPDATE, + WorkflowModel.TYPE_WORKFLOW_DEF, + new JavaBehaviour(this, "onContentUpdate")); + + // Register interest in the onPropertyUpdate policy for the workflow definition type + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), + WorkflowModel.TYPE_WORKFLOW_DEF, + new JavaBehaviour(this, "onUpdateProperties")); + + // Register interest in the node delete policy + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + WorkflowModel.TYPE_WORKFLOW_DEF, + new JavaBehaviour(this, "beforeDeleteNode")); + } + + /** + * On content update behaviour implementation + * + * @param nodeRef the node reference whose content has been updated + */ + public void onContentUpdate(NodeRef nodeRef, boolean newContent) + { + deploy(nodeRef); + } + + /** + * On update properties behaviour implementation + * + * @param nodeRef the node reference + * @param before the values of the properties before update + * @param after the values of the properties after the update + */ + public void onUpdateProperties( + NodeRef nodeRef, + Map before, + Map after) + { + Boolean beforeValue = (Boolean)before.get(WorkflowModel.PROP_WORKFLOW_DEF_DEPLOYED); + Boolean afterValue = (Boolean)after.get(WorkflowModel.PROP_WORKFLOW_DEF_DEPLOYED); + + if (afterValue != null && + (beforeValue == null || (beforeValue != null && afterValue != null && beforeValue.equals(afterValue) == false))) + { + if (afterValue.booleanValue() == true) + { + deploy(nodeRef); + } + else + { + undeploy(nodeRef); + } + } + else if (afterValue == null && beforeValue != null) + { + // Undeploy the definition since the value has been cleared + undeploy(nodeRef); + } + + } + + public void beforeDeleteNode(NodeRef nodeRef) + { + // Ignore if the node is a working copy + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == false) + { + undeploy(nodeRef); + } + } + + private void deploy(NodeRef nodeRef) + { + // deploy / re-deploy + WorkflowDeployment deployment = workflowService.deployDefinition(nodeRef); + + if (deployment != null) + { + WorkflowDefinition def = deployment.definition; + + // Update the meta data for the model + Map props = nodeService.getProperties(nodeRef); + + props.put(WorkflowModel.PROP_WORKFLOW_DEF_NAME, def.getName()); + + // TODO - ability to return and handle deployment problems / warnings + if (deployment.problems.length > 0) + { + for (String problem : deployment.problems) + { + logger.warn(problem); + } + } + + nodeService.setProperties(nodeRef, props); + } + } + + private void undeploy(NodeRef nodeRef) + { + String defName = (String)nodeService.getProperty(nodeRef, WorkflowModel.PROP_WORKFLOW_DEF_NAME); + if (defName != null) + { + // Undeploy the workflow definition - all versions in JBPM + List defs = workflowService.getAllDefinitionsByName(defName); + for (WorkflowDefinition def: defs) + { + logger.info("Undeploying workflow '" + defName + "' ..."); + workflowService.undeployDefinition(def.getId()); + logger.info("... undeployed '" + def.getId() + "' v" + def.getVersion()); + } + } + } +} diff --git a/source/java/org/alfresco/repo/workflow/WorkflowModel.java b/source/java/org/alfresco/repo/workflow/WorkflowModel.java index 030a359f5f..b914a2d362 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowModel.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowModel.java @@ -74,4 +74,10 @@ public interface WorkflowModel static final QName PROP_WORKFLOW_DEFINITION_NAME = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDefinitionName"); static final QName PROP_WORKFLOW_INSTANCE_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowInstanceId"); + // workflow definition + static final QName TYPE_WORKFLOW_DEF = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDefinition"); + static final QName PROP_WORKFLOW_DEF_ENGINE_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "engineId"); + static final QName PROP_WORKFLOW_DEF_NAME = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "definitionName"); + static final QName PROP_WORKFLOW_DEF_DEPLOYED = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "definitionDeployed"); + } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index 9b84b16ba8..33678171a1 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -32,7 +32,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.workflow.WorkflowDefinition; @@ -66,6 +70,8 @@ public class WorkflowServiceImpl implements WorkflowService private AuthorityService authorityService; private BPMEngineRegistry registry; private WorkflowPackageComponent workflowPackageComponent; + private NodeService nodeService; + private ContentService contentService; /** @@ -98,6 +104,26 @@ public class WorkflowServiceImpl implements WorkflowService this.workflowPackageComponent = workflowPackageComponent; } + /** + * Sets the Node Service + * + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Sets the Content Service + * + * @param contentService + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + /* (non-Javadoc) * @see org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java.lang.String, java.io.InputStream, java.lang.String) @@ -132,8 +158,15 @@ public class WorkflowServiceImpl implements WorkflowService */ public WorkflowDeployment deployDefinition(NodeRef definitionContent) { - // TODO - throw new UnsupportedOperationException(); + if (! nodeService.getType(definitionContent).equals(WorkflowModel.TYPE_WORKFLOW_DEF)) + { + throw new WorkflowException("Node " + definitionContent + " is not of type 'bpm:workflowDefinition'"); + } + + String engineId = (String)nodeService.getProperty(definitionContent, WorkflowModel.PROP_WORKFLOW_DEF_ENGINE_ID); + ContentReader contentReader = contentService.getReader(definitionContent, ContentModel.PROP_CONTENT); + + return deployDefinition(engineId, contentReader.getContentInputStream(), contentReader.getMimetype()); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java index e1d83a3b15..0243edae7d 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java @@ -321,7 +321,7 @@ public class JBPMEngine extends BPMEngine } catch(JbpmException e) { - throw new WorkflowException("Failed to deploy workflow definition", e); + throw new WorkflowException("Failed to deploy workflow definition - " + e.getMessage(), e); } } @@ -380,7 +380,7 @@ public class JBPMEngine extends BPMEngine } catch(JbpmException e) { - throw new WorkflowException("Failed to undeploy workflow definition", e); + throw new WorkflowException("Failed to undeploy workflow definition - " + e.getMessage(), e); } } @@ -1801,8 +1801,13 @@ public class JBPMEngine extends BPMEngine throw new JbpmException("Failed to parse process definition from jBPM xml stream", e); } } + + else + { + throw new JbpmException("Failed to parse process definition - unsupported mime type '" + mimetype + "'"); + } - if (tenantService.isEnabled()) + if ((compiledDef != null) && tenantService.isEnabled()) { compiledDef.def.setName(tenantService.getName(compiledDef.def.getName())); }