Big honkin' merge from head. Sheesh!

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3617 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park
2006-08-27 01:01:30 +00:00
parent e2c66899cc
commit 8031cc6574
322 changed files with 20776 additions and 6550 deletions

View File

@@ -0,0 +1,96 @@
/*
* 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 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,193 @@
/*
* 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 Component
* - Task Component
*
* @author davidc
*/
public class BPMEngineRegistry
{
/** ID seperator used in global Ids */
private static final String ID_SEPERATOR = "$";
private static final String ID_SEPERATOR_REGEX = "\\$";
/** Logging support */
private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow");
private Map<String, WorkflowComponent> workflowComponents;
private Map<String, TaskComponent> taskComponents;
/**
* Construct
*/
public BPMEngineRegistry()
{
workflowComponents = new HashMap<String, WorkflowComponent>();
taskComponents = new HashMap<String, TaskComponent>();
}
/**
* 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_REGEX);
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,135 @@
/*
* 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.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
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.WorkflowService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* Simple workflow action executor
*
* @author Roy Wetherall
*/
public class StartWorkflowActionExecuter extends ActionExecuterAbstractBase
{
public static final String NAME = "start-workflow";
public static final String PARAM_WORKFLOW_NAME = "workflowName";
// action dependencies
private NamespaceService namespaceService;
private WorkflowService workflowService;
private NodeService nodeService;
/**
* @param namespaceService
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param nodeService
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param workflowService
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
/* (non-Javadoc)
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#getAdhocPropertiesAllowed()
*/
@Override
protected boolean getAdhocPropertiesAllowed()
{
return true;
}
/* (non-Javadoc)
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
paramList.add(new ParameterDefinitionImpl(PARAM_WORKFLOW_NAME, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_WORKFLOW_NAME)));
// TODO: Start Task Template parameter
}
/* (non-Javadoc)
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
protected void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef)
{
// retrieve workflow definition
String workflowName = (String)ruleAction.getParameterValue(PARAM_WORKFLOW_NAME);
WorkflowDefinition def = workflowService.getDefinitionByName(workflowName);
// create workflow package to contain actioned upon node
NodeRef workflowPackage = (NodeRef)ruleAction.getParameterValue(WorkflowModel.ASSOC_PACKAGE.toPrefixString(namespaceService));
workflowPackage = workflowService.createPackage(workflowPackage);
ChildAssociationRef childAssoc = nodeService.getPrimaryParent(actionedUponNodeRef);
nodeService.addChild(workflowPackage, actionedUponNodeRef, ContentModel.ASSOC_CONTAINS, childAssoc.getQName());
// build map of workflow start task parameters
Map<String, Serializable> paramValues = ruleAction.getParameterValues();
Map<QName, Serializable> workflowParameters = new HashMap<QName, Serializable>();
workflowParameters.put(WorkflowModel.ASSOC_PACKAGE, workflowPackage);
for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
{
if (!entry.getKey().equals(PARAM_WORKFLOW_NAME))
{
QName qname = QName.createQName(entry.getKey(), namespaceService);
Serializable value = entry.getValue();
workflowParameters.put(qname, value);
}
}
// start the workflow
workflowService.startWorkflow(def.id, workflowParameters);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.Date;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
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.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.GUID;
/**
* Add features action execution test
*
* @author Roy Wetherall
*/
public class StartWorkflowActionExecuterTest extends BaseSpringTest
{
private NodeService nodeService;
private NamespaceService namespaceService;
private PersonService personService;
private NodeRef rootNodeRef;
private NodeRef nodeRef;
private StartWorkflowActionExecuter executer;
/**
* Called at the begining of all tests
*/
@Override
protected void onSetUpInTransaction() throws Exception
{
this.nodeService = (NodeService)this.applicationContext.getBean("nodeService");
this.namespaceService = (NamespaceService)this.applicationContext.getBean("namespaceService");
this.personService = (PersonService)this.applicationContext.getBean("personService");
AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
// Create the store and get the root node
rootNodeRef = nodeService.getRootNode(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "spacesStore"));
this.nodeRef = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}testnode"),
ContentModel.TYPE_CONTENT).getChildRef();
// Get the executer instance
this.executer = (StartWorkflowActionExecuter)this.applicationContext.getBean(StartWorkflowActionExecuter.NAME);
}
/**
* Test execution
*/
public void testExecution()
{
// Execute the action
ActionImpl action = new ActionImpl(null, GUID.generate(), StartWorkflowActionExecuter.NAME, null);
action.setParameterValue(StartWorkflowActionExecuter.PARAM_WORKFLOW_NAME, "jbpm://wf:review");
action.setParameterValue(WorkflowModel.PROP_REVIEW_DUE_DATE.toPrefixString(namespaceService), new Date());
NodeRef reviewer = personService.getPerson("admin");
action.setParameterValue(WorkflowModel.ASSOC_REVIEWER.toPrefixString(namespaceService), reviewer);
executer.execute(action, this.nodeRef);
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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);
/**
* Start the specified Task
*
* Note: this is an optional task operation. It may be used to track
* when work started on a task as well as resume a suspended task.
*
* @param taskId the task to start
* @return the updated task
*/
public WorkflowTask startTask(String taskId);
/**
* Suspend the specified Task
*
* @param taskId
* @return the update task
*/
public WorkflowTask suspendTask(String taskId);
/**
* 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 transitionId);
}

View File

@@ -0,0 +1,156 @@
/*
* 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.io.Serializable;
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;
import org.alfresco.service.namespace.QName;
/**
* SPI to be implemented by a BPM Engine that provides Workflow instance management.
*
* @author davidc
*/
public interface WorkflowComponent
{
//
// Workflow Definition Support
//
/**
* Deploy a Workflow Definition
*
* @param workflowDefinition the content object containing the definition
* @param mimetype (optional) the mime type of the workflow definition
* @return workflow deployment descriptor
*/
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype);
/**
* Is the specified Workflow Definition already deployed?
*
* Note: the notion of "already deployed" may differ between bpm engines. For example,
* different versions of the same process may be considered equal.
*
* @param workflowDefinition the definition to check
* @param mimetype the mimetype of the definition
* @return true => already deployed
*/
public boolean isDefinitionDeployed(InputStream workflowDefinition, String mimetype);
/**
* 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);
/**
* Gets a Workflow Definition by unique name
*
* @param workflowName workflow name e.g. jbpm://review
* @return the deployed workflow definition
*/
public WorkflowDefinition getDefinitionByName(String workflowName);
//
// Workflow Instance Support
//
/**
* 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 transitionId);
/**
* 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,191 @@
/*
* 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 java.util.Properties;
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.WorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.ClassPathResource;
/**
* Alfresco bootstrap Process deployment.
*
* @author davidc
*/
public class WorkflowDeployer implements ApplicationListener
{
// Logging support
private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow");
// Workflow Definition Properties (used in setWorkflowDefinitions)
public static final String ENGINE_ID = "engineId";
public static final String LOCATION = "location";
public static final String MIMETYPE = "mimetype";
public static final String REDEPLOY = "redeploy";
// Dependencies
private TransactionService transactionService;
private WorkflowService workflowService;
private AuthenticationComponent authenticationComponent;
private List<Properties> workflowDefinitions;
/**
* Sets the Transaction Service
*
* @param userTransaction the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Sets the namespace service
*
* @param namespaceService the namespace service
*/
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
/**
* Set the authentication component
*
* @param authenticationComponent
*/
public void setAuthenticationComponent(AuthenticationComponent authenticationComponent)
{
this.authenticationComponent = authenticationComponent;
}
/**
* Sets the Workflow Definitions
*
* @param workflowDefinitions
*/
public void setWorkflowDefinitions(List<Properties> workflowDefinitions)
{
this.workflowDefinitions = workflowDefinitions;
}
/**
* Deploy the Workflow Definitions
*/
public void deploy()
{
if (transactionService == null)
{
throw new ImporterException("Transaction Service must be provided");
}
if (authenticationComponent == null)
{
throw new ImporterException("Authentication Component must be provided");
}
if (workflowService == null)
{
throw new ImporterException("Workflow Service must be provided");
}
UserTransaction userTransaction = transactionService.getUserTransaction();
authenticationComponent.setSystemUserAsCurrentUser();
try
{
userTransaction.begin();
// bootstrap the workflow definitions
if (workflowDefinitions != null)
{
for (Properties workflowDefinition : workflowDefinitions)
{
// retrieve workflow specification
String engineId = workflowDefinition.getProperty(ENGINE_ID);
if (engineId == null || engineId.length() == 0)
{
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
{
WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype);
if (logger.isInfoEnabled())
logger.info("Workflow deployer: Deployed process definition '" + deployment.definition.title + "' (version " + deployment.definition.version + ") from '" + location + "' with " + deployment.problems.length + " problems");
}
}
}
userTransaction.commit();
}
catch(Throwable e)
{
// rollback the transaction
try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {}
try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {}
throw new AlfrescoRuntimeException("Workflow deployment failed", e);
}
finally
{
authenticationComponent.clearCurrentSecurityContext();
}
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ApplicationEvent event)
{
if (event instanceof ContextRefreshedEvent)
{
deploy();
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* Workflow Model Constants
*/
public interface WorkflowModel
{
//
// Base Business Process Management Definitions
//
// task constants
static final QName TYPE_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "task");
static final QName PROP_TASK_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "taskId");
static final QName PROP_START_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "startDate");
static final QName PROP_DUE_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "dueDate");
static final QName PROP_COMPLETION_DATE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "completionDate");
static final QName PROP_PRIORITY = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "priority");
static final QName PROP_STATUS = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "status");
static final QName PROP_PERCENT_COMPLETE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "percentComplete");
static final QName PROP_COMPLETED_ITEMS = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "completedItems");
static final QName ASSOC_POOLED_ACTORS = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "pooledActors");
// workflow task contstants
static final QName TYPE_WORKFLOW_TASK = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowTask");
static final QName PROP_CONTEXT = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "context");
static final QName PROP_OUTCOME = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "outcome");
static final QName PROP_PACKAGE_ACTION_GROUP = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "packageActionGroup");
static final QName PROP_PACKAGE_ITEM_ACTION_GROUP = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "packageItemActionGroup");
static final QName ASSOC_PACKAGE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "package");
// workflow package
static final QName ASPECT_WORKFLOW_PACKAGE = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowPackage");
static final QName PROP_WORKFLOW_DEFINITION_ID = QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "workflowDefinitionId");
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 Models
//
// review & approve
static final QName TYPE_SUBMITREVIEW_TASK = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "submitReviewTask");
static final QName PROP_REVIEW_PRIORITY = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewPriority");
static final QName PROP_REVIEW_DUE_DATE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewDueDate");
static final QName ASSOC_REVIEWER = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "reviewer");
}

View File

@@ -0,0 +1,43 @@
/*
* 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.repository.NodeRef;
/**
* Contract for managing Workflow Packages. A package is a container
* of Content that's routed through a Workflow.
*
* @author davidc
*/
public interface WorkflowPackageComponent
{
/**
* Create a Workflow Package (a container of content to route through the Workflow).
*
* If an existing container is supplied, it's supplemented with the workflow package aspect.
*
* @param container (optional) a pre-created container (e.g. folder, versioned folder or layered folder)
* @return the workflow package
*/
public NodeRef createPackage(NodeRef container);
// TODO: Support for finding packages via meta-data of WorkflowPackage aspect
}

View File

@@ -0,0 +1,210 @@
/*
* 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.ArrayList;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
/**
* Alfresco implementation of Workflow Package where the package is stored
* within the Alfresco Repository.
*
* @author davidc
*/
public class WorkflowPackageImpl implements WorkflowPackageComponent
{
private final static String PACKAGE_FOLDER = "Workflow Packages";
// service dependencies
private ImporterBootstrap bootstrap;
private SearchService searchService;
private NodeService nodeService;
private NamespaceService namespaceService;
private FileFolderService fileFolderService;
private NodeRef systemWorkflowContainer = null;
/**
* @param bootstrap the importer bootstrap for the store to place workflow items into
*/
public void setImporterBootstrap(ImporterBootstrap bootstrap)
{
this.bootstrap = bootstrap;
}
/**
* @param fileFolderService file folder service
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* @param searchService search service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param namespaceService namespace service
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowPackageComponent#createPackage(org.alfresco.service.cmr.repository.NodeRef)
*/
public NodeRef createPackage(NodeRef container)
{
// create a container, if one is not specified
if (container == null)
{
// create simple folder in workflow system folder
NodeRef system = getSystemWorkflowContainer();
// TODO: Consider structuring this folder, if number of children becomes an issue
List<String> folders = new ArrayList<String>();
folders.add(PACKAGE_FOLDER);
folders.add(GUID.generate());
FileInfo containerFolder = fileFolderService.makeFolders(system, folders, ContentModel.TYPE_FOLDER);
container = containerFolder.getNodeRef();
}
// attach workflow package
if (nodeService.hasAspect(container, WorkflowModel.ASPECT_WORKFLOW_PACKAGE))
{
throw new WorkflowException("Container '" + container + "' is already a workflow package.");
}
nodeService.addAspect(container, WorkflowModel.ASPECT_WORKFLOW_PACKAGE, null);
// return container
return container;
}
/**
* Gets the system workflow container for storing workflow related items
*
* @return the system workflow container
*/
private NodeRef getSystemWorkflowContainer()
{
if (systemWorkflowContainer == null)
{
NodeRef systemContainer = findSystemContainer();
systemWorkflowContainer = findSystemWorkflowContainer(systemContainer);
if (systemWorkflowContainer == null)
{
throw new WorkflowException("Unable to find system workflow folder - does not exist.");
}
}
return systemWorkflowContainer;
}
/**
* Finds the system workflow container
*
* @param systemContainer the system container
* @return the system workflow container
*/
private NodeRef findSystemWorkflowContainer(NodeRef systemContainer)
{
String path = bootstrap.getConfiguration().getProperty("system.workflow_container.childname");
if (path == null)
{
throw new WorkflowException("Unable to locate workflow system container - path not specified");
}
List<NodeRef> nodeRefs = searchService.selectNodes(systemContainer, path, null, namespaceService, false);
if (nodeRefs != null && nodeRefs.size() > 0)
{
systemWorkflowContainer = nodeRefs.get(0);
}
return systemWorkflowContainer;
}
/**
* Finds the system container
*
* @return the system container
*/
private NodeRef findSystemContainer()
{
String path = bootstrap.getConfiguration().getProperty("system.system_container.childname");
if (path == null)
{
throw new WorkflowException("Unable to locate system container - path not specified");
}
NodeRef root = nodeService.getRootNode(bootstrap.getStoreRef());
List<NodeRef> nodeRefs = searchService.selectNodes(root, path, null, namespaceService, false);
if (nodeRefs == null || nodeRefs.size() == 0)
{
throw new WorkflowException("Unable to locate system container - path not found");
}
return nodeRefs.get(0);
}
/**
* Creates the System Workflow Container
*
* @return the system workflow container
*/
public NodeRef createSystemWorkflowContainer()
{
NodeRef systemContainer = findSystemContainer();
NodeRef systemWorkflowContainer = findSystemWorkflowContainer(systemContainer);
if (systemWorkflowContainer == null)
{
String name = bootstrap.getConfiguration().getProperty("system.workflow_container.childname");
QName qname = QName.createQName(name, namespaceService);
ChildAssociationRef childRef = nodeService.createNode(systemContainer, ContentModel.ASSOC_CHILDREN, qname, ContentModel.TYPE_FOLDER);
systemWorkflowContainer = childRef.getChildRef();
}
return systemWorkflowContainer;
}
}

View File

@@ -0,0 +1,330 @@
/*
* 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.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.WorkflowDeployment;
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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Default Alfresco Workflow Service whose implementation is backed by registered
* BPM Engine plug-in components.
*
* @author davidc
*/
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;
/**
* Sets the BPM Engine Registry
*
* @param registry bpm engine registry
*/
public void setBPMEngineRegistry(BPMEngineRegistry registry)
{
this.registry = registry;
}
/**
* Sets the Workflow Package Component
*
* @param workflowPackage workflow package component
*/
public void setWorkflowPackageComponent(WorkflowPackageComponent workflowPackageComponent)
{
this.workflowPackageComponent = workflowPackageComponent;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java.lang.String, java.io.InputStream, java.lang.String)
*/
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype)
{
WorkflowComponent component = getWorkflowComponent(engineId);
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)
* @see org.alfresco.service.cmr.workflow.WorkflowService#isDefinitionDeployed(java.lang.String, java.io.InputStream, java.lang.String)
*/
public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype)
{
WorkflowComponent component = getWorkflowComponent(engineId);
return component.isDefinitionDeployed(workflowDefinition, mimetype);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(org.alfresco.service.cmr.repository.NodeRef)
*/
public WorkflowDeployment deployDefinition(NodeRef definitionContent)
{
// TODO
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#undeployDefinition(java.lang.String)
*/
public void undeployDefinition(String workflowDefinitionId)
{
String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId);
WorkflowComponent component = getWorkflowComponent(engineId);
component.undeployDefinition(workflowDefinitionId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitions()
*/
public List<WorkflowDefinition> getDefinitions()
{
List<WorkflowDefinition> definitions = new ArrayList<WorkflowDefinition>(10);
String[] ids = registry.getWorkflowComponents();
for (String id: ids)
{
WorkflowComponent component = registry.getWorkflowComponent(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);
WorkflowComponent component = getWorkflowComponent(engineId);
return component.getDefinitionById(workflowDefinitionId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionByName(java.lang.String)
*/
public WorkflowDefinition getDefinitionByName(String workflowName)
{
String engineId = BPMEngineRegistry.getEngineId(workflowName);
WorkflowComponent component = getWorkflowComponent(engineId);
return component.getDefinitionByName(workflowName);
}
/* (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)
{
String engineId = BPMEngineRegistry.getEngineId(taskId);
TaskComponent component = getTaskComponent(engineId);
return component.getTaskById(taskId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#createPackage(java.lang.String, org.alfresco.service.cmr.repository.NodeRef)
*/
public NodeRef createPackage(NodeRef container)
{
return workflowPackageComponent.createPackage(container);
}
/**
* 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,75 @@
/*
* 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.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
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;
NodeService nodeService;
//@Override
protected void onSetUpInTransaction() throws Exception
{
workflowService = (WorkflowService)applicationContext.getBean(ServiceRegistry.WORKFLOW_SERVICE.getLocalName());
nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_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);
}
public void testWorkflowPackage()
{
NodeRef nodeRef = workflowService.createPackage(null);
assertNotNull(nodeRef);
assertTrue(nodeService.hasAspect(nodeRef, WorkflowModel.ASPECT_WORKFLOW_PACKAGE));
}
}

View File

@@ -0,0 +1,226 @@
/*
* 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.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ScriptService;
import org.dom4j.Element;
import org.jbpm.context.def.VariableAccess;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.Token;
import org.jbpm.jpdl.xml.JpdlXmlReader;
import org.springframework.beans.factory.BeanFactory;
import org.xml.sax.InputSource;
/**
* A jBPM Action Handler for executing Alfresco Script
*
* The configuration of this action is as follows:
* <script language="javascript">
* <expression>
* the script to execute
* </expression>
* <variable name="watcha" access="write"/>
* </script>
*
* It's exactly the same as jBPM's own script configuration.
*
* @author davidc
*/
public class AlfrescoJavaScript extends JBPMSpringActionHandler
{
private static final long serialVersionUID = -2908748080671212745L;
private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null);
private ScriptService scriptService;
private Element script;
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
scriptService = (ScriptService)factory.getBean(ServiceRegistry.SCRIPT_SERVICE.getLocalName());
}
/* (non-Javadoc)
* @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
*/
@SuppressWarnings("unchecked")
public void execute(ExecutionContext executionContext) throws Exception
{
// extract action configuration
String expression = null;
List<VariableAccess> variableAccesses = null;
// is the script specified as text only, or as explicit expression, variable elements
boolean isTextOnly = true;
Iterator<Element> iter = script.elementIterator();
while (iter.hasNext())
{
Element element = iter.next();
if (element.getNodeType() == Element.ELEMENT_NODE)
{
isTextOnly = false;
}
}
// extract script and variables
if (isTextOnly)
{
expression = script.getTextTrim();
}
else
{
variableAccesses = jpdlReader.readVariableAccesses(script);
expression = script.element("expression").getTextTrim();
}
// construct script arguments and execute
Map<String, Object> inputMap = createInputMap(executionContext, variableAccesses);
Object result = scriptService.executeScriptString(expression, inputMap);
// map script return variable to process context
VariableAccess returnVariable = getWritableVariable(variableAccesses);
if (returnVariable != null)
{
ContextInstance contextInstance = executionContext.getContextInstance();
Token token = executionContext.getToken();
contextInstance.setVariable(returnVariable.getVariableName(), result, token);
}
}
/**
* Construct map of arguments to pass to script
*
* Based on the <variable> elements of the action configuration.
*
* @param executionContext the execution context
* @param variableAccesses the variable configuration
* @return the map of script arguments
*/
@SuppressWarnings("unchecked")
public Map<String, Object> createInputMap(ExecutionContext executionContext, List<VariableAccess> variableAccesses)
{
Map<String, Object> inputMap = new HashMap<String, Object>();
// initialise process variables
Token token = executionContext.getToken();
inputMap.put("executionContext", executionContext);
inputMap.put("token", token);
if (executionContext.getNode() != null)
{
inputMap.put("node", executionContext.getNode());
}
if (executionContext.getTask() != null)
{
inputMap.put("task", executionContext.getTask());
}
if (executionContext.getTaskInstance() != null)
{
inputMap.put("taskInstance", executionContext.getTaskInstance());
}
// if no readable variableInstances are specified,
ContextInstance contextInstance = executionContext.getContextInstance();
if (!hasReadableVariable(variableAccesses))
{
// copy all the variableInstances of the context into the interpreter
Map<String, Object> variables = contextInstance.getVariables(token);
if (variables != null)
{
for (Map.Entry entry : variables.entrySet())
{
String variableName = (String) entry.getKey();
Object variableValue = entry.getValue();
inputMap.put(variableName, variableValue);
}
}
}
else
{
// copy the specified variableInstances into the interpreterz
for (VariableAccess variableAccess : variableAccesses)
{
if (variableAccess.isReadable())
{
String variableName = variableAccess.getVariableName();
String mappedName = variableAccess.getMappedName();
Object variableValue = contextInstance.getVariable(variableName, token);
inputMap.put(mappedName, variableValue);
}
}
}
return inputMap;
}
/**
* Determine if there are variables to read from the process context
*
* @param variableAccesses the variables configuration
* @return true => there are variables to read
*/
private boolean hasReadableVariable(List<VariableAccess> variableAccesses)
{
if (variableAccesses != null)
{
for (VariableAccess variableAccess : variableAccesses)
{
if (variableAccess.isReadable())
{
return true;
}
}
}
return false;
}
/**
* Determine if there is a variable to write back to the process context
*
* @param variableAccesses the variables configuration
* @return true => there is a variable to write
*/
private VariableAccess getWritableVariable(List<VariableAccess> variableAccesses)
{
if (variableAccesses != null)
{
for (VariableAccess variableAccess : variableAccesses)
{
if (variableAccess.isWritable())
{
return variableAccess;
}
}
}
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,469 @@
/*
* 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.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
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.security.authentication.AuthenticationComponent;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.ServiceRegistry;
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;
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;
import org.springframework.core.io.ClassPathResource;
/**
* JBPM Engine Tests
*
* @author davidc
*/
public class JBPMEngineTest extends BaseSpringTest
{
AuthenticationComponent authenticationComponent;
NodeService nodeService;
WorkflowComponent workflowComponent;
TaskComponent taskComponent;
WorkflowDefinition testWorkflowDef;
NodeRef testNodeRef;
@Override
protected void onSetUpInTransaction() throws Exception
{
// run as system
authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser("admin");
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));
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);
assertEquals("test", testWorkflowDef.name);
assertEquals("1", testWorkflowDef.version);
assertTrue(workflowComponent.isDefinitionDeployed(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML));
// get valid node ref
nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName());
testNodeRef = nodeService.getRootNode(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "spacesStore"));
nodeService.setProperty(testNodeRef, ContentModel.PROP_CREATED, new Date());
}
@Override
protected void onTearDownInTransaction()
{
authenticationComponent.clearCurrentSecurityContext();
}
public void testGetWorkflowDefinitions()
{
List<WorkflowDefinition> workflowDefs = workflowComponent.getDefinitions();
assertNotNull(workflowDefs);
assertTrue(workflowDefs.size() > 0);
}
public void testDeployWorkflow() throws Exception
{
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/test_processdefinition.xml");
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);
assertEquals("test", testWorkflowDef.name);
assertEquals("2", testWorkflowDef.version);
}
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 testStartWorkflowParameters()
{
WorkflowDefinition workflowDef = getTestDefinition();
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.PROP_TASK_ID, 3); // protected - shouldn't be written
params.put(WorkflowModel.PROP_DUE_DATE, new Date()); // task instance field
params.put(WorkflowModel.PROP_PRIORITY, 1); // task instance field
params.put(WorkflowModel.PROP_PERCENT_COMPLETE, 10); // context variable
params.put(QName.createQName("", "Message"), "Hello World"); // context variable outside of task definition
params.put(QName.createQName("", "Array"), new String[] { "one", "two" }); // context variable outside of task definition
params.put(QName.createQName("", "NodeRef"), new NodeRef("workspace://1/1001")); // context variable outside of task definition
params.put(ContentModel.PROP_OWNER, "admin"); // task assignment
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
assertNotNull(path);
assertTrue(path.id.endsWith("-@"));
assertNotNull(path.node);
assertNotNull(path.instance);
assertEquals(workflowDef.id, path.instance.definition.id);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
WorkflowTask task = tasks1.get(0);
assertTrue(task.properties.containsKey(WorkflowModel.PROP_TASK_ID));
assertTrue(task.properties.containsKey(WorkflowModel.PROP_DUE_DATE));
assertTrue(task.properties.containsKey(WorkflowModel.PROP_PRIORITY));
assertTrue(task.properties.containsKey(WorkflowModel.PROP_PERCENT_COMPLETE));
assertTrue(task.properties.containsKey(ContentModel.PROP_OWNER));
NodeRef initiator = path.instance.initiator;
String initiatorUsername = (String)nodeService.getProperty(initiator, ContentModel.PROP_USERNAME);
assertEquals("admin", initiatorUsername);
}
public void testUpdateTask()
{
WorkflowDefinition workflowDef = getTestDefinition();
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.PROP_TASK_ID, 3); // protected - shouldn't be written
params.put(WorkflowModel.PROP_DUE_DATE, new Date()); // task instance field
params.put(WorkflowModel.PROP_PRIORITY, 1); // task instance field
params.put(WorkflowModel.PROP_PERCENT_COMPLETE, 10); // context variable
params.put(QName.createQName("", "Message"), "Hello World"); // context variable outside of task definition
params.put(QName.createQName("", "Array"), new String[] { "one", "two" }); // context variable outside of task definition
params.put(QName.createQName("", "NodeRef"), new NodeRef("workspace://1/1001")); // context variable outside of task definition
params.put(ContentModel.PROP_OWNER, "admin"); // task assignment
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
assertNotNull(path);
assertTrue(path.id.endsWith("-@"));
assertNotNull(path.node);
assertNotNull(path.instance);
assertEquals(workflowDef.id, path.instance.definition.id);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
WorkflowTask task = tasks1.get(0);
assertTrue(task.properties.containsKey(WorkflowModel.PROP_TASK_ID));
assertTrue(task.properties.containsKey(WorkflowModel.PROP_DUE_DATE));
assertTrue(task.properties.containsKey(WorkflowModel.PROP_PRIORITY));
assertTrue(task.properties.containsKey(WorkflowModel.PROP_PERCENT_COMPLETE));
assertTrue(task.properties.containsKey(ContentModel.PROP_OWNER));
// update with null parameters
try
{
WorkflowTask taskU1 = taskComponent.updateTask(task.id, null, null, null);
assertNotNull(taskU1);
}
catch(Throwable e)
{
fail("Task update failed with null parameters");
}
// update property value
Map<QName, Serializable> updateProperties2 = new HashMap<QName, Serializable>();
updateProperties2.put(WorkflowModel.PROP_PERCENT_COMPLETE, 100);
WorkflowTask taskU2 = taskComponent.updateTask(task.id, updateProperties2, null, null);
assertEquals(100, taskU2.properties.get(WorkflowModel.PROP_PERCENT_COMPLETE));
// add to assocation
QName assocName = QName.createQName("", "TestAssoc");
List<NodeRef> toAdd = new ArrayList<NodeRef>();
toAdd.add(new NodeRef("workspace://1/1001"));
toAdd.add(new NodeRef("workspace://1/1002"));
toAdd.add(new NodeRef("workspace://1/1003"));
Map<QName, List<NodeRef>> addAssocs = new HashMap<QName, List<NodeRef>>();
addAssocs.put(assocName, toAdd);
WorkflowTask taskU3 = taskComponent.updateTask(task.id, null, addAssocs, null);
assertNotNull(taskU3.properties.get(assocName));
assertEquals(3, ((List<NodeRef>)taskU3.properties.get(assocName)).size());
// add to assocation again
List<NodeRef> toAddAgain = new ArrayList<NodeRef>();
toAddAgain.add(new NodeRef("workspace://1/1004"));
toAddAgain.add(new NodeRef("workspace://1/1005"));
Map<QName, List<NodeRef>> addAssocsAgain = new HashMap<QName, List<NodeRef>>();
addAssocsAgain.put(assocName, toAddAgain);
WorkflowTask taskU4 = taskComponent.updateTask(task.id, null, addAssocsAgain, null);
assertNotNull(taskU4.properties.get(assocName));
assertEquals(5, ((List<NodeRef>)taskU4.properties.get(assocName)).size());
// remove assocation
List<NodeRef> toRemove = new ArrayList<NodeRef>();
toRemove.add(new NodeRef("workspace://1/1002"));
toRemove.add(new NodeRef("workspace://1/1003"));
Map<QName, List<NodeRef>> removeAssocs = new HashMap<QName, List<NodeRef>>();
removeAssocs.put(assocName, toRemove);
WorkflowTask taskU5 = taskComponent.updateTask(task.id, null, null, removeAssocs);
assertNotNull(taskU5.properties.get(assocName));
assertEquals(3, ((List<NodeRef>)taskU5.properties.get(assocName)).size());
}
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();
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, null);
assertNotNull(path);
WorkflowPath updatedPath = workflowComponent.signal(path.id, path.node.transitions[1].id);
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");
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
assertNotNull(path);
assertNotNull(path);
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].id);
assertNotNull(updatedTask);
List<WorkflowTask> completedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.COMPLETED);
assertNotNull(completedTasks);
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);
}
public void testEndTask()
{
WorkflowDefinition workflowDef = getTestDefinition();
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
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);
List<WorkflowTask> completedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.COMPLETED);
assertNotNull(completedTasks);
completedTasks = filterTasksByWorkflowInstance(completedTasks, path.instance.id);
assertEquals(1, completedTasks.size());
assertEquals(WorkflowTaskState.COMPLETED, completedTasks.get(0).state);
}
public void testGetTask()
{
WorkflowDefinition workflowDef = getTestDefinition();
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
assertNotNull(path);
assertNotNull(path);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
WorkflowTask getTask = taskComponent.getTaskById(tasks1.get(0).id);
assertNotNull(getTask);
assertEquals(getTask.id, tasks1.get(0).id);
}
public void testNodeRef()
{
WorkflowDefinition workflowDef = getTestDefinition();
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "reviewer"), "admin");
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
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);
}
public void testScript()
throws IOException
{
// deploy test script definition
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/test_script.xml");
assertFalse(workflowComponent.isDefinitionDeployed(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML));
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
assertNotNull(deployment);
WorkflowDefinition workflowDef = deployment.definition;
Map<QName, Serializable> parameters = new HashMap<QName, Serializable>();
parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters);
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);
}
/**
* Locate the Test Workflow Definition
*
* @return workflow definition
*/
private WorkflowDefinition getTestDefinition()
{
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

@@ -0,0 +1,96 @@
/*
* 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.Date;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.mozilla.javascript.Scriptable;
/**
* Scriptable Node suitable for JBPM Beanshell access
*
* TODO: This implementation derives from the JavaScript Alfresco Node. At
* some point we should look to having a script-independent node with various
* script-specific sub-types (and value conversions).
*
* @author davidc
*/
public class JBPMNode extends org.alfresco.repo.jscript.Node
{
private static final long serialVersionUID = -826970280203254365L;
/**
* Construct
*
* @param nodeRef node reference
* @param services services
*/
public JBPMNode(NodeRef nodeRef, ServiceRegistry services)
{
super(nodeRef, services, null);
}
/* (non-Javadoc)
* @see org.alfresco.repo.jscript.Node#createValueConverter()
*/
@Override
protected NodeValueConverter createValueConverter()
{
return new JBPMNodeConverter();
}
/**
* Value converter for beanshell.
*/
private class JBPMNodeConverter extends org.alfresco.repo.jscript.Node.NodeValueConverter
{
@Override
public Serializable convertValueForRepo(Serializable value)
{
if (value instanceof Date)
{
return value;
}
else
{
return super.convertValueForRepo(value);
}
}
@Override
public Serializable convertValueForScript(ServiceRegistry services, Scriptable scope, QName qname, Serializable value)
{
if (value instanceof NodeRef)
{
return new JBPMNode(((NodeRef)value), services);
}
else if (value instanceof Date)
{
return value;
}
else
{
return super.convertValueForScript(services, scope, qname, value);
}
}
}
}

View File

@@ -0,0 +1,9 @@
package org.alfresco.repo.workflow.jbpm;
import java.util.ArrayList;
public class JBPMNodeList extends ArrayList<JBPMNode>
{
private static final long serialVersionUID = 1376915749912156471L;
}

View File

@@ -41,9 +41,9 @@ public class JBPMSpringTest extends BaseSpringTest
//@Override
protected void xonSetUpInTransaction() throws Exception
protected void onSetUpInTransaction() throws Exception
{
jbpmTemplate = (JbpmTemplate)applicationContext.getBean("jbpm.template");
jbpmTemplate = (JbpmTemplate)applicationContext.getBean("jbpm_template");
descriptorService = (DescriptorService)applicationContext.getBean("DescriptorService");
}
@@ -51,7 +51,7 @@ public class JBPMSpringTest extends BaseSpringTest
{
}
public void xtestHelloWorld()
public void testHelloWorld()
throws Exception
{
// Between the 3 method calls below, all data is passed via the
@@ -74,21 +74,21 @@ public class JBPMSpringTest extends BaseSpringTest
theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();
}
public void xtestStep0()
public void testStep0()
throws Exception
{
deployProcessDefinition();
setComplete();
}
public void xtestStep1()
public void testStep1()
throws Exception
{
processInstanceIsCreatedWhenUserSubmitsWebappForm();
setComplete();
}
public void xtestStep2()
public void testStep2()
throws Exception
{
theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();

View File

@@ -0,0 +1,79 @@
/*
* 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 org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.jbpm.context.exe.Converter;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
/**
* jBPM Converter for transforming Alfresco Node to string and back
*
* @author davidc
*/
public class NodeConverter implements Converter
{
private static final long serialVersionUID = 1L;
private static BeanFactoryLocator jbpmFactoryLocator = new JbpmFactoryLocator();
/* (non-Javadoc)
* @see org.jbpm.context.exe.Converter#supports(java.lang.Object)
*/
public boolean supports(Object value)
{
if (value == null)
{
return true;
}
return (value.getClass() == JBPMNode.class);
}
/* (non-Javadoc)
* @see org.jbpm.context.exe.Converter#convert(java.lang.Object)
*/
public Object convert(Object o)
{
Object converted = null;
if (o != null)
{
converted = ((JBPMNode)o).getNodeRef().toString();
}
return converted;
}
/* (non-Javadoc)
* @see org.jbpm.context.exe.Converter#revert(java.lang.Object)
*/
public Object revert(Object o)
{
Object reverted = null;
if (o != null)
{
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
reverted = new JBPMNode(new NodeRef((String)o), serviceRegistry);
}
return reverted;
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.util.ArrayList;
import java.util.List;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.jbpm.context.exe.converter.SerializableToByteArrayConverter;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
/**
* jBPM Converter for transforming Alfresco Node to string and back
*
* @author davidc
*/
public class NodeListConverter extends SerializableToByteArrayConverter
{
private static final long serialVersionUID = 1L;
private static BeanFactoryLocator jbpmFactoryLocator = new JbpmFactoryLocator();
/* (non-Javadoc)
* @see org.jbpm.context.exe.Converter#supports(java.lang.Object)
*/
public boolean supports(Object value)
{
if (value == null)
{
return true;
}
return (value.getClass() == JBPMNodeList.class);
}
/* (non-Javadoc)
* @see org.jbpm.context.exe.Converter#convert(java.lang.Object)
*/
public Object convert(Object o)
{
Object converted = null;
if (o != null)
{
JBPMNodeList nodes = (JBPMNodeList)o;
List<NodeRef> values = new ArrayList<NodeRef>(nodes.size());
for (JBPMNode node : nodes)
{
values.add(node.getNodeRef());
}
converted = super.convert(values);
}
return converted;
}
/* (non-Javadoc)
* @see org.jbpm.context.exe.Converter#revert(java.lang.Object)
*/
@SuppressWarnings("unchecked")
public Object revert(Object o)
{
Object reverted = null;
if (o != null)
{
List<NodeRef> nodeRefs = (List<NodeRef>)super.revert(o);
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
JBPMNodeList nodes = new JBPMNodeList();
for (NodeRef nodeRef : nodeRefs)
{
nodes.add(new JBPMNode(nodeRef, serviceRegistry));
}
reverted = nodes;
}
return reverted;
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.ServiceRegistry;
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.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.springframework.core.io.ClassPathResource;
/**
* JBPM Engine Tests
*
* @author davidc
*/
public class NodeListConverterTest extends BaseSpringTest
{
AuthenticationComponent authenticationComponent;
PersonService personService;
WorkflowComponent workflowComponent;
TaskComponent taskComponent;
WorkflowDefinition testWorkflowDef;
NodeRef testNodeRef;
private static String taskId = null;
@Override
protected void onSetUpInTransaction() throws Exception
{
personService = (PersonService)applicationContext.getBean("personService");
BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry");
workflowComponent = registry.getWorkflowComponent("jbpm");
taskComponent = registry.getTaskComponent("jbpm");
// deploy latest review and approve process definition
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml");
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);
// run as system
authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
// get valid node ref
NodeService nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName());
testNodeRef = nodeService.getRootNode(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "spacesStore"));
}
public void testStep1Start()
{
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.ASSOC_PACKAGE, testNodeRef);
Date reviewDueDate = new Date();
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewDueDate"), reviewDueDate);
NodeRef reviewer = personService.getPerson("admin");
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewer"), reviewer);
WorkflowPath path = workflowComponent.startWorkflow(testWorkflowDef.id, params);
assertNotNull(path);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
setComplete();
taskId = tasks1.get(0).id;
}
public void testSetNodeRefList()
{
List<NodeRef> nodeRefs = new ArrayList<NodeRef>();
nodeRefs.add(testNodeRef);
nodeRefs.add(testNodeRef);
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.PROP_COMPLETED_ITEMS, (Serializable)nodeRefs);
WorkflowTask task = taskComponent.getTaskById(taskId);
assertNull(task.properties.get(WorkflowModel.PROP_COMPLETED_ITEMS));
WorkflowTask updatedTask = taskComponent.updateTask(taskId, params, null, null);
assertNotNull(updatedTask);
assertTrue(updatedTask.properties.containsKey(WorkflowModel.PROP_COMPLETED_ITEMS));
assertEquals(2, ((List)updatedTask.properties.get(WorkflowModel.PROP_COMPLETED_ITEMS)).size());
setComplete();
}
public void testUpdateNodeRefList()
{
List<NodeRef> nodeRefs = new ArrayList<NodeRef>();
// nodeRefs.add(testNodeRef);
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.PROP_COMPLETED_ITEMS, (Serializable)nodeRefs);
// WorkflowTask task = taskComponent.getTaskById(taskId);
// assertNotNull(task);
// assertTrue(task.properties.containsKey(WorkflowModel.PROP_COMPLETED_ITEMS));
// assertEquals(2, ((List)task.properties.get(WorkflowModel.PROP_COMPLETED_ITEMS)).size());
WorkflowTask updatedTask = taskComponent.updateTask(taskId, params, null, null);
assertNotNull(updatedTask);
assertTrue(updatedTask.properties.containsKey(WorkflowModel.PROP_COMPLETED_ITEMS));
assertEquals(0, ((List)updatedTask.properties.get(WorkflowModel.PROP_COMPLETED_ITEMS)).size());
setComplete();
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.workflow.BPMEngineRegistry;
import org.alfresco.repo.workflow.TaskComponent;
import org.alfresco.repo.workflow.WorkflowComponent;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.service.ServiceRegistry;
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.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
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.QName;
import org.alfresco.util.BaseSpringTest;
import org.springframework.core.io.ClassPathResource;
/**
* Review and Approve workflow specific Tests
*
* @author davidc
*/
public class ReviewAndApproveTest extends BaseSpringTest
{
AuthenticationComponent authenticationComponent;
PersonService personService;
WorkflowComponent workflowComponent;
TaskComponent taskComponent;
WorkflowDefinition testWorkflowDef;
NodeRef testNodeRef;
@Override
protected void onSetUpInTransaction() throws Exception
{
personService = (PersonService)applicationContext.getBean("personService");
BPMEngineRegistry registry = (BPMEngineRegistry)applicationContext.getBean("bpm_engineRegistry");
workflowComponent = registry.getWorkflowComponent("jbpm");
taskComponent = registry.getTaskComponent("jbpm");
// deploy latest review and approve process definition
ClassPathResource processDef = new ClassPathResource("org/alfresco/repo/workflow/jbpm/review_and_approve_processdefinition.xml");
WorkflowDeployment deployment = workflowComponent.deployDefinition(processDef.getInputStream(), MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);
// run as system
authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
authenticationComponent.setSystemUserAsCurrentUser();
// get valid node ref
NodeService nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName());
testNodeRef = nodeService.getRootNode(new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "spacesStore"));
}
@Override
protected void onTearDownInTransaction()
{
authenticationComponent.clearCurrentSecurityContext();
}
public void testSubmitForReview()
{
WorkflowDefinition workflowDef = testWorkflowDef;
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.ASSOC_PACKAGE, testNodeRef);
Date reviewDueDate = new Date();
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewDueDate"), reviewDueDate);
NodeRef reviewer = personService.getPerson("admin");
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewer"), reviewer);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
assertNotNull(path);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
WorkflowTask task = tasks1.get(0);
assertTrue(task.properties.containsKey(WorkflowModel.ASSOC_PACKAGE));
WorkflowTask endedTask = taskComponent.endTask(task.id, null);
assertNotNull(endedTask);
assertTrue(endedTask.properties.containsKey(WorkflowModel.PROP_OUTCOME));
assertEquals("", endedTask.properties.get(WorkflowModel.PROP_OUTCOME));
List<WorkflowTask> assignedTasks = taskComponent.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS);
assertNotNull(assignedTasks);
assignedTasks = filterTasksByWorkflowInstance(assignedTasks, path.instance.id);
assertEquals(testNodeRef, assignedTasks.get(0).properties.get(WorkflowModel.ASSOC_PACKAGE));
assertEquals(reviewDueDate, assignedTasks.get(0).properties.get(WorkflowModel.PROP_DUE_DATE));
}
public void testCompletedItems()
{
WorkflowDefinition workflowDef = testWorkflowDef;
List<NodeRef> nodeRefs = new ArrayList<NodeRef>();
nodeRefs.add(testNodeRef);
nodeRefs.add(testNodeRef);
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.ASSOC_PACKAGE, testNodeRef);
params.put(WorkflowModel.PROP_COMPLETED_ITEMS, (Serializable)nodeRefs);
Date reviewDueDate = new Date();
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewDueDate"), reviewDueDate);
NodeRef reviewer = personService.getPerson("admin");
params.put(QName.createQName("http://www.alfresco.org/model/workflow/1.0", "reviewer"), reviewer);
WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, params);
assertNotNull(path);
List<WorkflowTask> tasks1 = workflowComponent.getTasksForWorkflowPath(path.id);
assertNotNull(tasks1);
assertEquals(1, tasks1.size());
WorkflowTask task = tasks1.get(0);
assertTrue(task.properties.containsKey(WorkflowModel.PROP_COMPLETED_ITEMS));
assertEquals(2, ((List)task.properties.get(WorkflowModel.PROP_COMPLETED_ITEMS)).size());
}
/**
* 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

@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
<subclass name="org.alfresco.repo.workflow.jbpm.WorkflowTaskInstance"
extends="org.jbpm.taskmgmt.exe.TaskInstance"
discriminator-value="W"/>
</hibernate-mapping>

View File

@@ -0,0 +1,109 @@
/*
* 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 org.alfresco.service.cmr.workflow.WorkflowException;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
/**
* Alfresco specific implementation of a jBPM task instance
*
* @author davidc
*/
public class WorkflowTaskInstance extends TaskInstance
{
private static final long serialVersionUID = 6824116036569411964L;
/** Alfresco JBPM Engine */
private static JBPMEngine jbpmEngine = null;
/**
* Gets the JBPM Engine instance
*
* @return JBPM Engine
*/
private JBPMEngine getJBPMEngine()
{
if (jbpmEngine == null)
{
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
jbpmEngine = (JBPMEngine)factory.getFactory().getBean("jbpm_engine");
if (jbpmEngine == null)
{
throw new WorkflowException("Failed to retrieve JBPMEngine component");
}
}
return jbpmEngine;
}
/**
* Construct
*/
public WorkflowTaskInstance()
{
super();
}
/**
* Construct
*
* @param taskName
* @param actorId
*/
public WorkflowTaskInstance(String taskName, String actorId)
{
super(taskName, actorId);
}
/**
* Construct
*
* @param taskName
*/
public WorkflowTaskInstance(String taskName)
{
super(taskName);
}
@Override
public void create(ExecutionContext executionContext)
{
super.create(executionContext);
getJBPMEngine().setDefaultTaskProperties(this);
}
@Override
public void end(Transition transition)
{
// NOTE: Set the outcome first, so it's available during the submission of
// task variables to the process context
Transition outcome = (transition == null) ? token.getNode().getDefaultLeavingTransition() : transition;
if (outcome != null)
{
getJBPMEngine().setTaskOutcome(this, outcome);
}
super.end(transition);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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 org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.TaskInstanceFactory;
import org.jbpm.taskmgmt.exe.TaskInstance;
/**
* jBPM factory for creating Alfresco derived Task Instances
*
* @author davidc
*/
public class WorkflowTaskInstanceFactory implements TaskInstanceFactory
{
private static final long serialVersionUID = -8097108150047415711L;
/* (non-Javadoc)
* @see org.jbpm.taskmgmt.TaskInstanceFactory#createTaskInstance(org.jbpm.graph.exe.ExecutionContext)
*/
public TaskInstance createTaskInstance(ExecutionContext executionContext)
{
return new WorkflowTaskInstance();
}
}

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:adhoc">
<swimlane name="initiator"/>
<start-state name="start">
<task name="wf:submitAdhocTask" swimlane="initiator">
<controller>
<variable name="assignee" access="write" mapped-name="wf:assignee"/>
<variable name="adhocduedate" access="write" mapped-name="wf:adhocDueDate"/>
<variable name="adhocpriority" access="write" mapped-name="wf:adhocPriority"/>
<variable name="adhocdescription" access="write" mapped-name="wf:adhocDescription"/>
<variable name="notify" access="write" mapped-name="wf:notifyMe"/>
<variable name="workflowpackage" access="write" mapped-name="bpm:package"/>
<variable name="workflowcontext" access="write" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="adhoc"/>
</start-state>
<swimlane name="assignee">
<assignment actor-id="#{assignee.properties['cm:userName']}"/>
</swimlane>
<task-node name="adhoc">
<event type="task-create">
<script>
taskInstance.dueDate = adhocduedate;
taskInstance.priority = adhocpriority;
</script>
</event>
<task name="wf:adhocTask" swimlane="assignee">
<controller>
<variable name="adhocdescription" access="read" mapped-name="wf:adhocDescription"/>
<variable name="workflowpackage" access="read" mapped-name="bpm:package"/>
<variable name="workflowcontext" access="read" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="completed">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
if (notify)
{
var mail = actions.create("mail");
mail.parameters.to = initiator.properties["cm:email"];
mail.parameters.subject = "Adhoc Task " + adhocdescription;
mail.parameters.from = assignee.properties["cm:email"];
mail.parameters.text = "It's done";
mail.execute(workflowpackage);
}
</script>
</action>
</transition>
</task-node>
<task-node name="completed">
<task name="wf:completedAdhocTask" swimlane="initiator">
<controller>
<variable name="adhocdescription" access="read" mapped-name="wf:adhocDescription"/>
<variable name="workflowpackage" access="read" mapped-name="bpm:package"/>
<variable name="workflowcontext" access="read" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="end"/>
</task-node>
<end-state name="end"/>
</process-definition>

View File

@@ -0,0 +1,28 @@
<jbpm-configuration>
<jbpm-context>
<service name="persistence" factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
<service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
<service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
<service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
<service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
</jbpm-context>
<!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
<string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />
<string name="resource.business.calendar" value="org/jbpm/calendar/jbpm.business.calendar.properties" />
<string name="resource.default.modules" value="org/jbpm/graph/def/jbpm.default.modules.properties" />
<string name='resource.converter' value='org/alfresco/repo/workflow/jbpm/jbpm.converter.properties' />
<string name="resource.action.types" value="org/jbpm/graph/action/action.types.xml" />
<string name="resource.node.types" value="org/jbpm/graph/node/node.types.xml" />
<string name="resource.parsers" value="org/jbpm/jpdl/par/jbpm.parsers.xml" />
<string name="resource.varmapping" value="org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml" />
<long name="jbpm.msg.wait.timout" value="5000" singleton="true" />
<int name="jbpm.byte.block.size" value="1024" singleton="true" />
<string name="mail.smtp.host" value="localhost" />
<bean name="jbpm.task.instance.factory" class="org.alfresco.repo.workflow.jbpm.WorkflowTaskInstanceFactory" singleton="true" />
<bean name="jbpm.variable.resolver" class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
<bean name="jbpm.mail.address.resolver" class="org.jbpm.identity.mail.IdentityAddressResolver" singleton="true" />
</jbpm-configuration>

View File

@@ -0,0 +1,19 @@
# this file contains the mappings between converter types
# and the char that is used in the database. this mapping
# is used by the ConverterEnumType to store the VariableInstance
# converter field. The Converters class provides singleton access
# to these converter classes.
B org.jbpm.context.exe.converter.BooleanToStringConverter
Y org.jbpm.context.exe.converter.BytesToByteArrayConverter
E org.jbpm.context.exe.converter.ByteToLongConverter
C org.jbpm.context.exe.converter.CharacterToStringConverter
A org.jbpm.context.exe.converter.DateToLongConverter
D org.jbpm.context.exe.converter.DoubleToStringConverter
F org.jbpm.context.exe.converter.FloatToStringConverter
G org.jbpm.context.exe.converter.FloatToDoubleConverter
I org.jbpm.context.exe.converter.IntegerToLongConverter
R org.jbpm.context.exe.converter.SerializableToByteArrayConverter
H org.jbpm.context.exe.converter.ShortToLongConverter
N org.alfresco.repo.workflow.jbpm.NodeConverter
L org.alfresco.repo.workflow.jbpm.NodeListConverter

View File

@@ -0,0 +1,190 @@
<!--
# This file specifies how jbpm will store variables into the database.
#
# If jbpm needs to determine how to store a variable into the database,
# the jbpm-types below or scanned in sequence as they are specified here.
# For each jbpm-type, jbpm will see if the give variable object matches
# with the matcher bean. If there is a match, the converter (optional)
# and the variable instance will be used to store and retrieve
# the variable value for the rest of its lifetime till the variable is
# deleted.
-->
<jbpm-types>
<list name="jbpm.types" singleton="true">
<!-- java.lang.String -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.String" /></field>
</bean>
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" />
</jbpm-type>
<!-- java.lang.Boolean -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Boolean" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.BooleanToStringConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" />
</jbpm-type>
<!-- java.lang.Character -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Character" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.CharacterToStringConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" />
</jbpm-type>
<!-- java.lang.Long -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Long" /></field>
</bean>
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" />
</jbpm-type>
<!-- java.lang.Byte -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Byte" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.ByteToLongConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" />
</jbpm-type>
<!-- java.lang.Short -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Short" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.ShortToLongConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" />
</jbpm-type>
<!-- java.lang.Integer -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Integer" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.IntegerToLongConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" />
</jbpm-type>
<!-- java.lang.Double -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Double" /></field>
</bean>
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.DoubleInstance" />
</jbpm-type>
<!-- java.lang.Float -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.lang.Float" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.FloatToDoubleConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.DoubleInstance" />
</jbpm-type>
<!-- java.util.Date -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="java.util.Date" /></field>
</bean>
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.DateInstance" />
</jbpm-type>
<!-- org.alfresco.repo.workflow.jbpm.JBPMNode -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="org.alfresco.repo.workflow.jbpm.JBPMNode" /></field>
</bean>
</matcher>
<converter class="org.alfresco.repo.workflow.jbpm.NodeConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" />
</jbpm-type>
<!-- org.alfresco.repo.workflow.jbpm.JBPMNodeList -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="org.alfresco.repo.workflow.jbpm.JBPMNodeList" /></field>
</bean>
</matcher>
<converter class="org.alfresco.repo.workflow.jbpm.NodeListConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.ByteArrayInstance" />
</jbpm-type>
<!-- byte[] -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
<field name="className"><string value="[B" /></field>
</bean>
</matcher>
<converter class="org.jbpm.context.exe.converter.BytesToByteArrayConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.ByteArrayInstance" />
</jbpm-type>
<!-- java.io.Serializable -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.SerializableMatcher" />
</matcher>
<converter class="org.jbpm.context.exe.converter.SerializableToByteArrayConverter" />
<variable-instance class="org.jbpm.context.exe.variableinstance.ByteArrayInstance" />
</jbpm-type>
<!-- hibernatable long id types -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.HibernateLongIdMatcher" />
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.HibernateLongInstance" />
</jbpm-type>
<!-- hibernatable string id types -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.HibernateStringIdMatcher" />
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.HibernateStringInstance" />
</jbpm-type>
<!-- hibernatable ejb3 types -->
<jbpm-type>
<matcher>
<bean class="org.jbpm.context.exe.matcher.Ejb3Matcher" />
</matcher>
<variable-instance class="org.jbpm.context.exe.variableinstance.Ejb3Instance" />
</jbpm-type>
</list>
</jbpm-types>

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:review">
<swimlane name="initiator"/>
<start-state name="start">
<task name="wf:submitReviewTask" swimlane="initiator">
<controller>
<variable name="reviewer" access="write" mapped-name="wf:reviewer"/>
<variable name="reviewduedate" access="write" mapped-name="wf:reviewDueDate"/>
<variable name="reviewpriority" access="write" mapped-name="wf:reviewPriority"/>
<variable name="package" access="write" mapped-name="bpm:package"/>
<variable name="context" access="write" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="review"/>
</start-state>
<swimlane name="reviewer">
<assignment actor-id="#{reviewer.properties['cm:userName']}"/>
</swimlane>
<task-node name="review">
<task name="wf:reviewTask" swimlane="reviewer">
<event type="task-create">
<script>
taskInstance.dueDate = reviewduedate;
taskInstance.priority = reviewpriority;
</script>
</event>
<controller>
<variable name="package" access="read,required" mapped-name="bpm:package"/>
<variable name="context" access="read,required" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="reject" to="rejected"/>
<transition name="approve" to="approved"/>
</task-node>
<task-node name="rejected">
<task name="wf:rejectedTask" swimlane="initiator">
<controller>
<variable name="package" access="read,required" mapped-name="bpm:package"/>
<variable name="context" access="read,required" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="end"/>
</task-node>
<task-node name="approved">
<task name="wf:approvedTask" swimlane="initiator">
<controller>
<variable name="package" access="read,required" mapped-name="bpm:package"/>
<variable name="context" access="read,required" mapped-name="bpm:context"/>
</controller>
</task>
<transition name="" to="end"/>
</task-node>
<end-state name="end"/>
</process-definition>

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

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="test">
<swimlane name="initiator"></swimlane>
<start-state name="start">
<task name="submit" swimlane="initiator">
<controller>
<variable name="reviewer" access="write,required" />
<variable name="testNode" access="write,required" />
</controller>
</task>
<transition name="" to="review">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
<expression>
function result()
{
return ("Initiator: " + initiator.properties["cm:userName"] + ", process instance = " + executionContext.processInstance.id + ", testNode children = " + testNode.children.length);
}
result();
</expression>
<variable name="scriptResult" access="write"/>
</script>
</action>
</transition>
<transition name="end" to="end"/>
</start-state>
<swimlane name="reviewer">
<assignment actor-id="#{reviewer}"></assignment>
</swimlane>
<task-node name="review">
<event type="node-enter">
<script>
System.out.println("the reviewer is " + reviewer);
System.out.println("node " + testNode.name + " contains " + testNode.children.length + " children");
System.out.println("scriptResult = " + scriptResult);
</script>
</event>
<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

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="test_script">
<swimlane name="initiator"></swimlane>
<start-state name="start">
<task name="submit" swimlane="initiator">
<event type="task-assign">
<script>
System.out.println("taskInstance.create: " + taskInstance.create);
</script>
</event>
<controller>
<variable name="testNode" access="write,required" />
</controller>
</task>
<transition name="" to="doit"/>
</start-state>
<node name="doit">
<event type="node-enter">
<script>
<expression>
System.out.println("testNode.created: " + testNode.properties{"cm:created"});
System.out.println("test node " + testNode.name + " contains " + testNode.children.length + " children");
</expression>
<variable name="testNode" access="read"/>
</script>
</event>
<event type="node-enter">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
<!-- following line fails as it attempts to convert properties of children to javascript objects -->
<!-- except the beanshell line above has already pre-created the children without the javascript scope -->
<!-- object -->
<!-- var result = "testNode.created: " + testNode.properties["cm:created"] + ", testNode.children.length: " + testNode.children[0].properties["cm:name"]; -->
<expression>
var result = "testNode.created: " + testNode.properties["cm:created"] + ", testNode.children.length: " + testNode.children.length;
result;
</expression>
<variable name="testNode" access="read"/>
<variable name="alfrescoScriptResult" access="write"/>
</script>
</action>
</event>
<transition name="" to="end"/>
</node>
<end-state name="end">
<event type="node-enter">
<script>
System.out.println("javascript: " + alfrescoScriptResult);
</script>
</event>
</end-state>
</process-definition>