diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 7ac609d102..e6f6bf44c3 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -42,6 +42,15 @@ system.workflow.deployservlet.enabled=false # Sets the location for the JBPM Configuration File system.workflow.jbpm.config.location=classpath:org/alfresco/repo/workflow/jbpm/jbpm.cfg.xml +# Determines if the JBPM engine is enabled +# NOTE: The JBPM engine is used by several parts of the system so it's recommended that +# it remains enabled. Only change this setting if you have alternative solutions +# for the affected areas i.e. site invitations, publishing and WCM +system.workflow.engine.jbpm.enabled=true + +# Determines if the Activiti engine is enabled +system.workflow.engine.activiti.enabled=true + index.subsystem.name=lucene # ######################################### # diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index c1e7253624..f964351e08 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -11,6 +11,7 @@ + @@ -60,6 +61,19 @@ + + + + + + + + ${system.workflow.engine.jbpm.enabled} + + + ${system.workflow.engine.activiti.enabled} + + @@ -77,6 +91,7 @@ + diff --git a/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java b/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java index fe54d03fc1..49f6fa29f2 100644 --- a/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java +++ b/source/java/org/alfresco/repo/workflow/BPMEngineRegistry.java @@ -21,6 +21,7 @@ package org.alfresco.repo.workflow; import java.util.HashMap; import java.util.Map; +import org.alfresco.service.cmr.workflow.WorkflowAdminService; import org.alfresco.service.cmr.workflow.WorkflowException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,6 +47,8 @@ public class BPMEngineRegistry /** Logging support */ private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow"); + private WorkflowAdminService workflowAdminService; + private Map workflowComponents; private Map taskComponents; @@ -58,6 +61,16 @@ public class BPMEngineRegistry taskComponents = new HashMap(); } + /** + * Sets the workflow admin service + * + * @param workflowAdminService the workflow admin service + */ + public void setWorkflowAdminService(WorkflowAdminService workflowAdminService) + { + this.workflowAdminService = workflowAdminService; + } + /** * Register a BPM Engine Workflow Component * @@ -70,10 +83,19 @@ public class BPMEngineRegistry { 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() + ")"); + if (workflowAdminService.isEngineEnabled(engineId)) + { + workflowComponents.put(engineId, engine); + + if (logger.isDebugEnabled()) + logger.debug("Registered Workflow Component '" + engineId + "' (" + engine.getClass() + ")"); + } + else + { + if (logger.isWarnEnabled()) + logger.warn("Ignoring Workflow Component '" + engineId + "' (" + engine.getClass() + ") as the engine is disabled"); + } } /** @@ -109,10 +131,19 @@ public class BPMEngineRegistry { 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() + ")"); + if (workflowAdminService.isEngineEnabled(engineId)) + { + taskComponents.put(engineId, engine); + + if (logger.isDebugEnabled()) + logger.debug("Registered Task Component '" + engineId + "' (" + engine.getClass() + ")"); + } + else + { + if (logger.isWarnEnabled()) + logger.warn("Ignoring Task Component '" + engineId + "' (" + engine.getClass() + ") as the engine is disabled"); + } } /** diff --git a/source/java/org/alfresco/repo/workflow/WorkflowAdminServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowAdminServiceImpl.java new file mode 100644 index 0000000000..408e9eba6c --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/WorkflowAdminServiceImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.workflow; + +import org.alfresco.repo.workflow.activiti.ActivitiConstants; +import org.alfresco.repo.workflow.jbpm.JBPMEngine; +import org.alfresco.service.cmr.workflow.WorkflowAdminService; + +/** + * Default implementation of the workflow admin service. + * + * @author Gavin Cornwell + * @since 4.0 + */ +public class WorkflowAdminServiceImpl implements WorkflowAdminService +{ + private boolean jbpmEngineEnabled = true; + private boolean activitiEngineEnabled = true; + + public void setJbpmEngineEnabled(boolean jbpmEngineEnabled) + { + this.jbpmEngineEnabled = jbpmEngineEnabled; + } + + public void setActivitiEngineEnabled(boolean activitiEngineEnabled) + { + this.activitiEngineEnabled = activitiEngineEnabled; + } + + @Override + public boolean isEngineEnabled(String engineId) + { + if (JBPMEngine.ENGINE_ID.equals(engineId)) + { + return jbpmEngineEnabled; + } + else if (ActivitiConstants.ENGINE_ID.equals(engineId)) + { + return activitiEngineEnabled; + } + else + { + // if the engine id is not recognised it can't be enabled! + return false; + } + } +} diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index 1d1303154e..1b98ad6545 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -40,6 +40,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.search.SearchService; +import org.alfresco.service.cmr.workflow.WorkflowAdminService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; @@ -73,6 +74,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean // Dependencies private TransactionService transactionService; private WorkflowService workflowService; + private WorkflowAdminService workflowAdminService; private AuthenticationContext authenticationContext; private DictionaryDAO dictionaryDAO; private List workflowDefinitions; @@ -100,14 +102,24 @@ public class WorkflowDeployer extends AbstractLifecycleBean } /** - * Sets the namespace service + * Sets the workflow service * - * @param namespaceService the namespace service + * @param workflowService the workflow service */ public void setWorkflowService(WorkflowService workflowService) { this.workflowService = workflowService; } + + /** + * Sets the workflow admin service + * + * @param workflowAdminService the workflow admin service + */ + public void setWorkflowAdminService(WorkflowAdminService workflowAdminService) + { + this.workflowAdminService = workflowAdminService; + } /** * Set the authentication component @@ -223,7 +235,9 @@ public class WorkflowDeployer extends AbstractLifecycleBean } if (!transactionService.getAllowWrite()) { - logger.warn("Repository is in read-only mode; not deploying workflows."); + if (logger.isWarnEnabled()) + logger.warn("Repository is in read-only mode; not deploying workflows."); + return; } @@ -235,12 +249,12 @@ public class WorkflowDeployer extends AbstractLifecycleBean // bootstrap the workflow models and static labels (from classpath) if (models != null && resourceBundles != null && ((models.size() > 0) || (resourceBundles.size() > 0))) { - DictionaryBootstrap dictionaryBootstrap = new DictionaryBootstrap(); - dictionaryBootstrap.setDictionaryDAO(dictionaryDAO); + DictionaryBootstrap dictionaryBootstrap = new DictionaryBootstrap(); + dictionaryBootstrap.setDictionaryDAO(dictionaryDAO); dictionaryBootstrap.setTenantService(tenantService); - dictionaryBootstrap.setModels(models); - dictionaryBootstrap.setLabels(resourceBundles); - dictionaryBootstrap.bootstrap(); // also registers with dictionary + dictionaryBootstrap.setModels(models); + dictionaryBootstrap.setLabels(resourceBundles); + dictionaryBootstrap.bootstrap(); // also registers with dictionary } // bootstrap the workflow definitions (from classpath) @@ -254,25 +268,22 @@ public class WorkflowDeployer extends AbstractLifecycleBean { throw new WorkflowException("Workflow Engine Id must be provided"); } + String location = workflowDefinition.getProperty(LOCATION); if (location == null || location.length() == 0) { throw new WorkflowException("Workflow definition location must be provided"); } - Boolean redeploy = Boolean.valueOf(workflowDefinition.getProperty(REDEPLOY)); - String mimetype = workflowDefinition.getProperty(MIMETYPE); - - // retrieve input stream on workflow definition - ClassPathResource workflowResource = new ClassPathResource(location); - // deploy workflow definition - if (!redeploy && workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype)) - { - if (logger.isDebugEnabled()) - logger.debug("Workflow deployer: Definition '" + location + "' already deployed"); - } - else + if (workflowAdminService.isEngineEnabled(engineId)) { + Boolean redeploy = Boolean.valueOf(workflowDefinition.getProperty(REDEPLOY)); + String mimetype = workflowDefinition.getProperty(MIMETYPE); + + // retrieve input stream on workflow definition + ClassPathResource workflowResource = new ClassPathResource(location); + + // deploy workflow definition if (!redeploy && workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype)) { if (logger.isDebugEnabled()) @@ -280,11 +291,24 @@ public class WorkflowDeployer extends AbstractLifecycleBean } else { - WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), - mimetype, workflowResource.getFilename()); - logDeployment(location, deployment); + if (!redeploy && workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype)) + { + if (logger.isDebugEnabled()) + logger.debug("Workflow deployer: Definition '" + location + "' already deployed"); + } + else + { + WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), + mimetype, workflowResource.getFilename()); + logDeployment(location, deployment); + } } } + else + { + if (logger.isDebugEnabled()) + logger.debug("Workflow deployer: Definition '" + location + "' not deployed as the '" + engineId + "' engine is disabled"); + } } } @@ -299,9 +323,9 @@ public class WorkflowDeployer extends AbstractLifecycleBean { for (NodeRef nodeRef : nodeRefs) { - deploy(nodeRef, false); - } - } + deploy(nodeRef, false); + } + } } userTransaction.commit(); @@ -328,59 +352,66 @@ public class WorkflowDeployer extends AbstractLifecycleBean { Boolean value = (Boolean)nodeService.getProperty(nodeRef, WorkflowModel.PROP_WORKFLOW_DEF_DEPLOYED); if ((value != null) && (value.booleanValue() == true)) - { - if (!redeploy && workflowService.isDefinitionDeployed(nodeRef)) - { - if (logger.isDebugEnabled()) - { - logger.debug("Workflow deployer: Definition '" + nodeRef + "' already deployed"); + { + String engineId = (String) nodeService.getProperty(nodeRef, WorkflowModel.PROP_WORKFLOW_DEF_ENGINE_ID); + + if (workflowAdminService.isEngineEnabled(engineId)) + { + if (!redeploy && workflowService.isDefinitionDeployed(nodeRef)) + { + if (logger.isDebugEnabled()) + logger.debug("Workflow deployer: Definition '" + nodeRef + "' already deployed"); + } + else + { + // deploy / re-deploy + WorkflowDeployment deployment = workflowService.deployDefinition(nodeRef); + logDeployment(nodeRef, deployment); + if (deployment != null) + { + WorkflowDefinition def = deployment.getDefinition(); + + // 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.getProblems().length > 0) + { + for (String problem : deployment.getProblems()) + { + if (logger.isWarnEnabled()) + logger.warn(problem); + } + } + + nodeService.setProperties(nodeRef, props); + } } - } - else - { - // deploy / re-deploy - WorkflowDeployment deployment = workflowService.deployDefinition(nodeRef); - logDeployment(nodeRef, deployment); - if (deployment != null) - { - WorkflowDefinition def = deployment.getDefinition(); - - // 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.getProblems().length > 0) - { - for (String problem : deployment.getProblems()) - { - logger.warn(problem); - } - } - - nodeService.setProperties(nodeRef, props); - } - } + } + else + { + if (logger.isDebugEnabled()) + logger.debug("Workflow deployer: Definition '" + nodeRef + "' not deployed as the '" + engineId + "' engine is disabled"); + } } } else { if (logger.isDebugEnabled()) - { - logger.debug("Workflow deployer: Definition '" + nodeRef + "' not deployed since it is a working copy"); - } + logger.debug("Workflow deployer: Definition '" + nodeRef + "' not deployed since it is a working copy"); } } private void logDeployment(Object location, WorkflowDeployment deployment) { - if (logger.isInfoEnabled()) + if (logger.isDebugEnabled()) { String title = deployment.getDefinition().getTitle(); String version = deployment.getDefinition().getVersion(); int problemLength = deployment.getProblems().length; - logger.info("Workflow deployer: Deployed process definition '" + title + "' (version " + version + ") from '" + location + "' with " + problemLength + " problems"); + logger.debug("Workflow deployer: Deployed process definition '" + title + "' (version " + version + ") from '" + location + "' with " + problemLength + " problems"); } } @@ -396,24 +427,20 @@ public class WorkflowDeployer extends AbstractLifecycleBean List defs = workflowService.getAllDefinitionsByName(defName); for (WorkflowDefinition def: defs) { - if (logger.isInfoEnabled()) - { - logger.info("Undeploying workflow '" + defName + "' ..."); - } + if (logger.isDebugEnabled()) + logger.debug("Undeploying workflow '" + defName + "' ..."); + workflowService.undeployDefinition(def.getId()); - if (logger.isInfoEnabled()) - { - logger.info("... undeployed '" + def.getId() + "' v" + def.getVersion()); - } + + if (logger.isDebugEnabled()) + logger.debug("... undeployed '" + def.getId() + "' v" + def.getVersion()); } } } else { if (logger.isDebugEnabled()) - { - logger.debug("Workflow deployer: Definition '" + nodeRef + "' not undeployed since it is a working copy"); - } + logger.debug("Workflow deployer: Definition '" + nodeRef + "' not undeployed since it is a working copy"); } } @@ -424,10 +451,10 @@ public class WorkflowDeployer extends AbstractLifecycleBean AuthenticationUtil.runAs(new RunAsWork() { public Object doWork() - { + { init(); return null; - } + } }, AuthenticationUtil.getSystemUserName()); tenantAdminService.register(this); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java index 6a271b9526..9b9f664e09 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java @@ -195,6 +195,9 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine private static final String ERR_GET_COMPANY_HOME_INVALID = "jbpm.engine.get.company.home.invalid"; private static final String ERR_GET_COMPANY_HOME_MULTIPLE = "jbpm.engine.get.company.home.multiple"; + // engine ID + public static final String ENGINE_ID = "jbpm"; + /** * Sets the JBPM Template used for accessing JBoss JBPM in the correct * context diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java index 45d2e992ee..3dee83c622 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java @@ -84,8 +84,8 @@ public class JBPMEngineTest extends BaseAlfrescoSpringTest person3 = createPerson(USER3); BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry"); - workflowComponent = registry.getWorkflowComponent("jbpm"); - taskComponent = registry.getTaskComponent("jbpm"); + workflowComponent = registry.getWorkflowComponent(JBPMEngine.ENGINE_ID); + taskComponent = registry.getTaskComponent(JBPMEngine.ENGINE_ID); packageComponent = (WorkflowPackageComponent)applicationContext.getBean("workflowPackageImpl"); // deploy test process messages diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMSpringActionHandler.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMSpringActionHandler.java index 487eb526c0..dc94440456 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMSpringActionHandler.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMSpringActionHandler.java @@ -65,7 +65,7 @@ public abstract class JBPMSpringActionHandler implements ActionHandler protected String getWorkflowInstanceId(ExecutionContext context) { String id = new Long(context.getProcessInstance().getId()).toString(); - return BPMEngineRegistry.createGlobalId("jbpm", id); + return BPMEngineRegistry.createGlobalId(JBPMEngine.ENGINE_ID, id); } } diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java b/source/java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java index 43032fed85..e6b96718d9 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java @@ -112,7 +112,7 @@ public class JbpmWorkflowServiceIntegrationTest extends AbstractWorkflowServiceI @Override protected String getEngine() { - return "jbpm"; + return JBPMEngine.ENGINE_ID; } @Override diff --git a/source/java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java b/source/java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java index ef5d70063c..00c3c16864 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java @@ -67,8 +67,8 @@ public class ReviewAndApproveTest extends BaseSpringTest { personService = (PersonService)applicationContext.getBean("personService"); BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry"); - workflowComponent = registry.getWorkflowComponent("jbpm"); - taskComponent = registry.getTaskComponent("jbpm"); + workflowComponent = registry.getWorkflowComponent(JBPMEngine.ENGINE_ID); + taskComponent = registry.getTaskComponent(JBPMEngine.ENGINE_ID); // deploy latest review and approve process definition ClassPathResource processDef = new ClassPathResource("alfresco/workflow/review_processdefinition.xml"); diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowAdminService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowAdminService.java new file mode 100644 index 0000000000..cf7cacd071 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowAdminService.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.service.cmr.workflow; + +/** + * Client facing API for providing administration information about the + * {@link WorkflowService}. + * + * @author Gavin Cornwell + * @since 4.0 + */ +public interface WorkflowAdminService +{ + /** + * Determines whether the engine with the given id is enabled. + * + * @param engineId The id of a workflow engine + * @return true if the engine id is valid and is enabled + */ + public boolean isEngineEnabled(String engineId); +}