[MNT-21638] Script task execution (#1210)

* [MNT-21638] Script task execution based on workflow deloyment category. Javadoc. Unit tests.
This commit is contained in:
tiagosalvado10
2022-09-23 13:24:49 +01:00
committed by GitHub
parent a77584a398
commit 125b35e11c
17 changed files with 542 additions and 139 deletions

View File

@@ -35,10 +35,11 @@ public class GetDeploymentsSanityTests extends RestTest
restClient.assertStatusCodeIs(HttpStatus.OK);
deployments.assertThat().entriesListIsNotEmpty();
deployments.getOneRandomEntry().onModel().assertThat()
.fieldsCount().is(3).and()
.fieldsCount().is(4).and()
.field("id").isNotEmpty().and()
.field("deployedAt").isNotEmpty().and()
.field("name").isNotEmpty();
.field("name").isNotEmpty().and()
.field("category").isNotEmpty();
}
}

View File

@@ -38,6 +38,7 @@ import java.util.Map;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.workflow.WorkflowDeployer;
import org.alfresco.rest.api.tests.AbstractTestFixture;
import org.alfresco.rest.api.tests.RepoService.TestNetwork;
import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse;
@@ -128,7 +129,7 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi
Deployment adhocDeployment = deploymentMap.get("adhoc.bpmn20.xml");
assertEquals(activitiDeployment.getId(), adhocDeployment.getId());
assertEquals(activitiDeployment.getCategory(), adhocDeployment.getCategory());
assertEquals(activitiDeployment.getCategory(), WorkflowDeployer.CATEGORY_FULL_ACCESS);
assertEquals(activitiDeployment.getName(), adhocDeployment.getName());
assertEquals(activitiDeployment.getDeploymentTime(), adhocDeployment.getDeployedAt());
@@ -251,7 +252,7 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi
assertNotNull(deployment);
assertEquals(activitiDeployment.getId(), deployment.getId());
assertEquals(activitiDeployment.getCategory(), deployment.getCategory());
assertEquals(activitiDeployment.getCategory(), WorkflowDeployer.CATEGORY_FULL_ACCESS);
assertEquals(activitiDeployment.getName(), deployment.getName());
assertEquals(activitiDeployment.getDeploymentTime(), deployment.getDeployedAt());

View File

@@ -269,6 +269,14 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
* @see org.alfresco.service.cmr.repository.ScriptProcessor#executeString(java.lang.String, java.util.Map)
*/
public Object executeString(String source, Map<String, Object> model)
{
return executeString(source, model, false);
}
/**
* @see org.alfresco.service.cmr.repository.ScriptProcessor#executeString(java.lang.String, java.util.Map, boolean)
*/
public Object executeString(String source, Map<String, Object> model, boolean secure)
{
try
{
@@ -283,7 +291,7 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess
{
Context.exit();
}
return executeScriptImpl(script, model, true, "string script");
return executeScriptImpl(script, model, secure, "string script");
}
catch (Throwable err)
{

View File

@@ -190,6 +190,15 @@ public class ScriptServiceImpl implements ScriptService
return executeScriptString(this.defaultScriptProcessor, script, model);
}
/**
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map, boolean)
*/
public Object executeScriptString(String script, Map<String, Object> model, boolean secure)
throws ScriptException
{
return executeScriptString(this.defaultScriptProcessor, script, model, secure);
}
/**
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map)
*/
@@ -197,12 +206,23 @@ public class ScriptServiceImpl implements ScriptService
throws ScriptException
{
ScriptProcessor scriptProcessor = lookupScriptProcessor(engine);
return executeString(scriptProcessor, script, model);
return executeString(scriptProcessor, script, model, false);
}
/**
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map, boolean)
*/
public Object executeScriptString(String engine, String script, Map<String, Object> model, boolean secure)
throws ScriptException
{
ScriptProcessor scriptProcessor = lookupScriptProcessor(engine);
return executeString(scriptProcessor, script, model, secure);
}
/**
* Execute script
*
* @param processor the script processor that will be responsible for supplied script execution
* @param location the location of the script
* @param model context model
* @return Object the result of the script
@@ -227,6 +247,7 @@ public class ScriptServiceImpl implements ScriptService
/**
* Execute script
*
* @param processor the script processor that will be responsible for supplied script execution
* @param scriptRef the script node reference
* @param contentProp the content property of the script
* @param model the context model
@@ -252,6 +273,7 @@ public class ScriptServiceImpl implements ScriptService
/**
* Execute script
*
* @param processor the script processor that will be responsible for supplied script execution
* @param location the classpath string locating the script
* @param model the context model
* @return Object the result of the script
@@ -276,11 +298,14 @@ public class ScriptServiceImpl implements ScriptService
/**
* Execute script string
*
* @param processor the script processor that will be responsible for supplied script execution
* @param script the script string
* @param model the context model
* @param secure the flag indicating if string script is considered secure (e.g., if it comes from classpath)
* if true it will have access to the full execution context, if false the script will be executed in a sandbox context
* @return Object the result of the script
*/
protected Object executeString(ScriptProcessor processor, String script, Map<String, Object> model)
protected Object executeString(ScriptProcessor processor, String script, Map<String, Object> model, boolean secure)
{
ParameterCheck.mandatoryString("script", script);
@@ -290,7 +315,7 @@ public class ScriptServiceImpl implements ScriptService
}
try
{
return processor.executeString(script, model);
return processor.executeString(script, model, secure);
}
catch (Throwable err)
{

View File

@@ -75,6 +75,18 @@ public interface WorkflowComponent
*/
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name);
/**
* Deploy a Workflow Definition
*
* @param workflowDefinition the content object containing the definition
* @param mimetype (optional) the mime type of the workflow definition
* @param name (optional) a name to represent the deployment
* @param fullAccess true if category should be defined in order to consider the deployment secure
* @return workflow deployment descriptor
* @since 4.0
*/
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name, boolean fullAccess);
/**
* Is the specified Workflow Definition already deployed?
*
@@ -87,6 +99,13 @@ public interface WorkflowComponent
*/
public boolean isDefinitionDeployed(InputStream workflowDefinition, String mimetype);
/**
* Sets the deployment category if applicable to allow the workflow to have full access
*
* @param workflowDefinition the definition to check
*/
public void checkDeploymentCategory(InputStream workflowDefinition);
/**
* Undeploy an exisiting Workflow Definition
*

View File

@@ -85,6 +85,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean
public static final String REDEPLOY = "redeploy";
public static final String CATEGORY_ALFRESCO_INTERNAL = "http://alfresco.org/workflows/internal";
public static final String CATEGORY_FULL_ACCESS = "http://alfresco.org/workflows/fullAccess";
// Dependencies
private TransactionService transactionService;
@@ -306,12 +307,14 @@ public class WorkflowDeployer extends AbstractLifecycleBean
if (!redeploy && workflowService.isDefinitionDeployed(engineId, workflowResource.getInputStream(), mimetype))
{
if (logger.isDebugEnabled())
logger.debug("Workflow deployer: Definition '" + location + "' already deployed");
{
logger.debug("Workflow deployer: Definition '" + location + "' already deployed. Checking deploymentcategory...");
}
workflowService.checkDeploymentCategory(engineId, workflowResource.getInputStream());
}
else
{
WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(),
mimetype, workflowResource.getFilename());
WorkflowDeployment deployment = workflowService.deployDefinition(engineId, workflowResource.getInputStream(), mimetype, workflowResource.getFilename(), true);
logDeployment(location, deployment);
}
}

View File

@@ -232,9 +232,20 @@ public class WorkflowServiceImpl implements WorkflowService
* .lang.String, java.io.InputStream, java.lang.String, java.lang.String)
*/
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name)
{
return deployDefinition(engineId, workflowDefinition, mimetype, name, false);
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.workflow.WorkflowService#deployDefinition(java
* .lang.String, java.io.InputStream, java.lang.String, java.lang.String, boolean)
*/
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name, boolean fullAccess)
{
WorkflowComponent component = getWorkflowComponent(engineId);
WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name);
WorkflowDeployment deployment = component.deployDefinition(workflowDefinition, mimetype, name, fullAccess);
if (logger.isDebugEnabled() && deployment.getProblems().length > 0)
{
@@ -277,6 +288,18 @@ public class WorkflowServiceImpl implements WorkflowService
return component.isDefinitionDeployed(workflowDefinition, mimetype);
}
/*
* (non-Javadoc)
* @see
* org.alfresco.service.cmr.workflow.WorkflowService#checkDeploymentCategory
* (java.lang.String, java.io.InputStream)
*/
public void checkDeploymentCategory(String engineId, InputStream workflowDefinition)
{
WorkflowComponent component = getWorkflowComponent(engineId);
component.checkDeploymentCategory(workflowDefinition);
}
/*
* (non-Javadoc)
* @see

View File

@@ -336,6 +336,14 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
* {@inheritDoc}
*/
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name)
{
return deployDefinition(workflowDefinition, mimetype, name, false);
}
/**
* {@inheritDoc}
*/
public WorkflowDeployment deployDefinition(InputStream workflowDefinition, String mimetype, String name, boolean fullAccess)
{
try
{
@@ -363,6 +371,10 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
{
repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL);
}
else if (fullAccess)
{
repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_FULL_ACCESS);
}
}
// No problems can be added to the WorkflowDeployment, warnings are
@@ -1005,6 +1017,46 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine
}
}
/**
* {@inheritDoc}
*/
public void checkDeploymentCategory(InputStream workflowDefinition)
{
try
{
String key = getProcessKey(workflowDefinition);
ProcessDefinition pd = activitiUtil.getProcessDefinitionByKey(key);
String deploymentId = pd.getDeploymentId();
List<ProcessDefinition> definitionList = repoService.createProcessDefinitionQuery().deploymentId(deploymentId).list();
if (definitionList != null && definitionList.size() > 0)
{
boolean internalCategory = true;
for (ProcessDefinition processDefinition : definitionList)
{
if (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(processDefinition.getCategory()) == false)
{
internalCategory = false;
break;
}
}
if (!internalCategory)
{
repoService.setDeploymentCategory(deploymentId, WorkflowDeployer.CATEGORY_FULL_ACCESS);
}
}
}
catch (Exception e)
{
if (logger.isDebugEnabled())
{
logger.debug("Category was not set: " + e.getMessage(), e);
}
}
}
private String getProcessKey(InputStream workflowDefinition) throws Exception
{
try

View File

@@ -32,8 +32,10 @@ import org.activiti.engine.delegate.VariableScope;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.el.Expression;
import org.activiti.engine.impl.persistence.entity.DeploymentEntity;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.workflow.WorkflowDeployer;
import org.alfresco.repo.workflow.activiti.ActivitiConstants;
import org.alfresco.repo.workflow.activiti.ActivitiScriptNode;
import org.alfresco.service.ServiceRegistry;
@@ -102,14 +104,18 @@ public class ActivitiScriptBase
{
// Execute the script using the appropriate processor
Object scriptResult = null;
// Checks if current workflow is secure
boolean secure = isSecure();
if (scriptProcessorName != null)
{
scriptResult = getServiceRegistry().getScriptService().executeScriptString(scriptProcessorName, theScript, model);
scriptResult = getServiceRegistry().getScriptService().executeScriptString(scriptProcessorName, theScript, model, secure);
}
else
{
// Use default script-processor
scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model);
scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model, secure);
}
return scriptResult;
@@ -142,6 +148,32 @@ public class ActivitiScriptBase
throw new IllegalStateException("No ProcessEngineCOnfiguration found in active context");
}
/**
* Checks whether the workflow must be considered secure or not - based on {@link DeploymentEntity} category.
* If it is not considered secure, the workflow will be executed in sandbox context with more restrictions
*
* @return true if workflow is considered secure, false otherwise
*/
private boolean isSecure()
{
String category = null;
try
{
if (Context.isExecutionContextActive())
{
category = Context.getExecutionContext().getDeployment().getCategory();
}
}
catch (Exception e)
{
// No action required
}
// If the workflow is considered secure, the deployment entity category matches the condition (either internal or full access)
return category != null && (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(category) || WorkflowDeployer.CATEGORY_FULL_ACCESS.equals(category));
}
/**
* Checks that the specified 'runAs' field
* specifies a valid username.

View File

@@ -70,10 +70,21 @@ public interface ScriptProcessor extends Processor
*
* @param script the script string
* @param model the context model
* @return Obejct the result of the script
* @return Object the result of the script
*/
public Object executeString(String script, Map<String, Object> model);
/**
* Execute script string
*
* @param script the script string
* @param model the context model
* @param secure the flag that indicates if string is considered secure to be executed, i.e., it will have
* access to the full execution context instead of being executed in a sandbox context
* @return Object the result of the script
*/
public Object executeString(String script, Map<String, Object> model, boolean secure);
/**
* Reset the processor - such as clearing any internal caches etc.
*/

View File

@@ -161,6 +161,21 @@ public interface ScriptService
public Object executeScriptString(String script, Map<String, Object> model)
throws ScriptException;
/**
* Process a script against the supplied data model. Uses the default script engine.
*
* @param script Script content as a String.
* @param model Object model to process script against
* @param secure A flag indicating if string script is considered secure (e.g., if it comes from the classpath)
* If true it will have access to the full execution context, if false the script will be executed in a sandbox context (more restricted)
* @return output of the script (may be null or any valid wrapped JavaScript object)
*
* @throws ScriptException
*/
@Auditable(parameters = {"script", "model", "secure"})
public Object executeScriptString(String script, Map<String, Object> model, boolean secure)
throws ScriptException;
/**
* Process a script against the supplied data model.
*
@@ -176,6 +191,22 @@ public interface ScriptService
public Object executeScriptString(String engine, String script, Map<String, Object> model)
throws ScriptException;
/**
* Process a script against the supplied data model.
*
* @param engine the script engine to use
* @param script Script content as a String.
* @param model Object model to process script against
* @param secure A flag indicating if string script is considered secure
*
* @return output of the script (may be null or any valid wrapped JavaScript object)
*
* @throws ScriptException
*/
@Auditable(parameters = {"engine", "script", "model", "secure"})
public Object executeScriptString(String engine, String script, Map<String, Object> model, boolean secure)
throws ScriptException;
/**
* Registers a script processor with the script service
*

View File

@@ -78,6 +78,23 @@ public interface WorkflowService
recordable = {true, false, true, true})
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name);
/**
* Deploy a Workflow Definition to the Alfresco Repository
*
* @param engineId the bpm engine id
* @param workflowDefinition the workflow definition
* @param mimetype the mimetype of the workflow definition
* @param name a name representing the deployment
* @parm fullAccess true if workflow should be considered secure (e.g., if it is deployed in classpath) and should have full access to the execution context,
* false if it should be executed in a sandbox context (more restricted)
* @return workflow deployment descriptor
* @since 4.0
*/
@Auditable(
parameters = {"engineId", "workflowDefinition", "mimetype", "name", "fullAccess"},
recordable = {true, false, true, true, true})
public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype, String name, boolean fullAccess);
/**
* Deploy a Workflow Definition to the Alfresco Repository
*
@@ -118,6 +135,17 @@ public interface WorkflowService
recordable = {true, false, true})
public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype);
/**
* Checks if the deployment for supplied workflow definition has the proper category
*
* @param engineId the bpm engine id
* @param workflowDefinition the definition to check
*/
@Auditable(
parameters = {"engineId", "workflowDefinition"},
recordable = {true, false})
public void checkDeploymentCategory(String engineId, InputStream workflowDefinition);
/**
* Undeploy an exisiting Workflow Definition
*

View File

@@ -25,16 +25,11 @@
*/
package org.alfresco.repo.jscript;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.dictionary.DictionaryComponent;
@@ -42,7 +37,6 @@ import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.node.BaseNodeServiceTest;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -50,6 +44,7 @@ import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptProcessor;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
@@ -62,6 +57,8 @@ import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.springframework.context.ApplicationContext;
import junit.framework.TestCase;
/**
* @author Kevin Roast
@@ -435,6 +432,50 @@ public class RhinoScriptTest extends TestCase
}
// MNT-21638
public void testSecureScriptString()
{
boolean executed = executeSecureScriptString(TESTSCRIPT2, false);
assertFalse("Script shouldn't have been executed (secure = false)", executed);
executed = executeSecureScriptString(TESTSCRIPT2, null);
assertFalse("Script shouldn't have been executed (secure = null)", executed);
executed = executeSecureScriptString(TESTSCRIPT2, true);
assertTrue("Script should have been executed (secure = true)", executed);
}
private boolean executeSecureScriptString(String script, Boolean secure)
{
return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Exception
{
StoreRef store = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "rhino_" + System.currentTimeMillis());
NodeRef root = nodeService.getRootNode(store);
BaseNodeServiceTest.buildNodeGraph(nodeService, root);
try
{
Map<String, Object> model = new HashMap<String, Object>();
model.put("out", System.out);
ScriptNode rootNode = new ScriptNode(root, serviceRegistry, null);
model.put("root", rootNode);
// test executing a script directly as string
scriptService.executeScriptString("javascript", script, model, secure);
}
catch (Exception e)
{
return false;
}
return true;
}
});
}
private static final String TESTSCRIPT_CLASSPATH1 = "org/alfresco/repo/jscript/test_script1.js";
private static final String TESTSCRIPT_CLASSPATH2 = "org/alfresco/repo/jscript/test_script2.js";
private static final String TESTSCRIPT_CLASSPATH3 = "org/alfresco/repo/jscript/test_script3.js";
@@ -454,6 +495,11 @@ public class RhinoScriptTest extends TestCase
"var xpathResults = root.childrenByXPath(\"/*\");\r\n" +
"logger.log(\"children of root from xpath: \" + xpathResults.length);\r\n";
private static final String TESTSCRIPT2 = "var exec = new org.alfresco.util.exec.RuntimeExec();\r\n"
+ "exec.setCommand([\"/bin/ls\"]);\r\n"
+ "var res = exec.execute();\r\n"
+ "java.lang.System.err.println(res.getStdOut());\r\n";
private static final String BASIC_JAVA =
"var list = com.google.common.collect.Lists.newArrayList();\n" +
"root.nodeRef.getClass().forName(\"java.lang.ProcessBuilder\")";

View File

@@ -1248,6 +1248,14 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT
return definition;
}
protected WorkflowDefinition deployDefinition(String resource, String name, boolean fullAccess)
{
InputStream input = getInputStream(resource);
WorkflowDeployment deployment = workflowService.deployDefinition(getEngine(), input, XML, name, fullAccess);
WorkflowDefinition definition = deployment.getDefinition();
return definition;
}
protected abstract QName getAdhocProcessName();

View File

@@ -790,6 +790,45 @@ public class ActivitiWorkflowServiceIntegrationTest extends AbstractWorkflowServ
assertNull("Workflow should not be deployed", workflowDef);
}
/**
*
*/
@Test
public void testMNT21638_1()
{
WorkflowDefinition definition = deployDefinition("activiti/test-MNT21638-1.bpmn20.xml");
personManager.setUser(USER1);
// Start the Workflow
try
{
WorkflowPath path = workflowService.startWorkflow(definition.getId(), null);
fail("Workflow should not have been executed");
}
catch (Exception e)
{
// Do nothing
}
}
/**
*
*/
@Test
public void testMNT21638_2()
{
WorkflowDefinition definition = deployDefinition("activiti/test-MNT21638-2.bpmn20.xml", "MNT21638", true);
personManager.setUser(USER1);
// Start the Workflow
WorkflowPath path = workflowService.startWorkflow(definition.getId(), null);
String instanceId = path.getInstance().getId();
assertNotNull(instanceId);
}
private NodeRef findWorkflowParent()
{
RepositoryLocation workflowLocation = (RepositoryLocation)

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns:activiti="http://activiti.org/bpmn" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="test-mnt21638-1" name="test-mnt21638-1">
<startEvent id="start" activiti:formKey="wf:submitAdhocTask"/>
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask2" />
<userTask id="someTask2" name="Activiti is awesome!" activiti:formKey="wf:adhocTask">
<extensionElements>
<activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
<activiti:field name="script">
<activiti:string>
var exec = new org.alfresco.util.exec.RuntimeExec();
exec.setCommand(["/bin/ls"]);
var res = exec.execute();
java.lang.System.err.println(res.getStdOut());
</activiti:string>
</activiti:field>
</activiti:taskListener>
</extensionElements>
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>admin</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
<sequenceFlow id="flow2" sourceRef="someTask2" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns:activiti="http://activiti.org/bpmn" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="test-mnt21638-2" name="test-mnt21638-2">
<startEvent id="start" activiti:formKey="wf:submitAdhocTask"/>
<sequenceFlow id="flow1" sourceRef="start" targetRef="someTask2" />
<userTask id="someTask2" name="Activiti is awesome!" activiti:formKey="wf:adhocTask">
<extensionElements>
<activiti:taskListener event="create" class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener">
<activiti:field name="script">
<activiti:string>
var exec = new org.alfresco.util.exec.RuntimeExec();
exec.setCommand(["/bin/ls"]);
var res = exec.execute();
java.lang.System.err.println(res.getStdOut());
</activiti:string>
</activiti:field>
</activiti:taskListener>
</extensionElements>
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>admin</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
<sequenceFlow id="flow2" sourceRef="someTask2" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>