cancel
Showing results for 
Search instead for 
Did you mean: 

shell service tasks not showing in runtime tables

aoliver
Champ in-the-making
Champ in-the-making
Hello,
I am an activiti noob and am having issues with a Shell Task implementation.  Since Shell Tasks are not available in the Activiti Designer yet, i created a Service Task (Java Delegate) that mimics the Shell Task Behavior.  These Shell Tasks are running BI processes and take several minutes.  I'm using the Activiti REST API to invoke the workflows.  Activiti-Explorer and Activiti-REST are both configured to connect to an Oracle database.

3 Questions:
1) While the Shell tasks are running, i see no information in the ACT_RU_EXECUTION table.  I would expect to see my process instance row there while these tasks are running.
2) How can i get the  REST call to return once the workflow is invoked instead of returning after the workflow has completed?
3) In Activiti Explorer, when i run the attached workflow, no other activity is allowed in the activiti-explorer app.  I am even unable to log into another session.  Why is Activiti Explorer locking up like this?


Here is a snippet of the service task xml:
    <serviceTask id="NetGStageServiceTask" name="NetGStageService Task" activiti:class="com.scrippsnetworks.ibi.revenueWorkflows.serviceTasks.ShellServiceTask">
      <extensionElements>
        <activiti:field name="command">
          <activiti:expression>${ShellScriptRoot}/ADSD/adsd_staging${ShellScriptSuffix}</activiti:expression>
        </activiti:field>
        <activiti:field name="wait">
          <activiti:string>true</activiti:string>
        </activiti:field>
        <activiti:field name="outputVariable">
          <activiti:string>netGStageStatus</activiti:string>
        </activiti:field>
        <activiti:field name="errorCodeVariable">
          <activiti:string>netGStageReturnCode</activiti:string>
        </activiti:field>
        <activiti:field name="arg1">
          <activiti:expression>${DartRunDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>


Here is the java delegate code:
public class ShellServiceTask implements JavaDelegate {

   final static Logger logger = Logger.getLogger(ShellServiceTask.class.getName());

   protected Expression command;
   protected Expression wait;
   protected Expression arg1;
   protected Expression arg2;
   protected Expression arg3;
   protected Expression arg4;
   protected Expression arg5;
   protected Expression outputVariable;
   protected Expression errorCodeVariable;
   protected Expression redirectError;
   protected Expression cleanEnv;
   protected Expression directory;

   String commandStr;
   String arg1Str;
   String arg2Str;
   String arg3Str;
   String arg4Str;
   String arg5Str;
   String waitStr;
   String resultVariableStr;
   String errorCodeVariableStr;
   Boolean waitFlag;
   Boolean redirectErrorFlag;
   Boolean cleanEnvBoolan;
   String directoryStr;

   @Override
   public void execute(DelegateExecution execution) throws Exception {
      logger.info("In " + command.getValue(execution) + " ShellTask script with variables = " + execution.getVariables());
      executeScript(execution);
      // create a new value for execution
      
      logger.info("Leaving " + command.getValue(execution) + " ShellTask script with variables = " + execution.getVariables());

   }

   private void readFields(DelegateExecution execution) {
       commandStr = getStringFromField(command, execution);
       arg1Str = getStringFromField(arg1, execution);
       arg2Str = getStringFromField(arg2, execution);
       arg3Str = getStringFromField(arg3, execution);
       arg4Str = getStringFromField(arg4, execution);
       arg5Str = getStringFromField(arg5, execution);
       waitStr = getStringFromField(wait, execution);
       resultVariableStr = getStringFromField(outputVariable, execution);
       errorCodeVariableStr = getStringFromField(errorCodeVariable, execution);

       String redirectErrorStr = getStringFromField(redirectError, execution);
       String cleanEnvStr = getStringFromField(cleanEnv, execution);

       waitFlag = waitStr == null || waitStr.equals("true");
       redirectErrorFlag = redirectErrorStr != null && redirectErrorStr.equals("true");
       cleanEnvBoolan = cleanEnvStr != null && cleanEnvStr.equals("true");
       directoryStr = getStringFromField(directory, execution);
   }

   public String toString() {
      return "command="+command+", arg1="+arg1+", arg2="+arg2+", arg3="+arg3+", arg4="+arg4+", arg5="+arg5+
            ", wait="+wait+", outputVariable="+outputVariable+", errorCodeVariable="+errorCodeVariable;
   }
   
   public void executeScript(DelegateExecution execution) {
      logger.info("starting execute");
       
      readFields(execution);
       
      List<String> argList = new ArrayList<String>();
         argList.add(commandStr);
       
         if (arg1Str != null)
            argList.add(arg1Str);
         if (arg2Str != null)
            argList.add(arg2Str);
         if (arg3Str != null)
            argList.add(arg3Str);
         if (arg4Str != null)
            argList.add(arg4Str);
         if (arg5Str != null)
            argList.add(arg5Str);
       
         ProcessBuilder processBuilder = new ProcessBuilder(argList);
       
         try {
            processBuilder.redirectErrorStream(redirectErrorFlag);
            if (cleanEnvBoolan) {
               Map<String, String> env = processBuilder.environment();
               env.clear();
            }
          
            if (directoryStr != null && directoryStr.length() > 0)
               processBuilder.directory(new File(directoryStr));
          
               Process process = processBuilder.start();
          
               if (waitFlag) {
                  int errorCode = process.waitFor();
             
                  if (resultVariableStr != null) {
                     String result = convertStreamToStr(process.getInputStream());
                     execution.setVariable(resultVariableStr, result);
                  }
             
                  if (errorCodeVariableStr != null) {
                     execution.setVariable(errorCodeVariableStr, Integer.toString(errorCode));
                  }

               }
         } catch (Exception e) {
            logger.info("Could not execute shell command with error = " + e);
            throw new ActivitiException("Could not execute shell command" , e);
         }

   }
      
   public static String convertStreamToStr(InputStream is) throws IOException {
      if (is != null) {
         Writer writer = new StringWriter();
         char[] buffer = new char[1024];
         try {
            Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            int n;
            while ((n = reader.read(buffer)) != -1) {
               writer.write(buffer, 0, n);
            }
         } finally {
            is.close();
         }
         return writer.toString();
      } else {
         return "";
      }
   }

   protected String getStringFromField(Expression expression, DelegateExecution execution) {
      if (expression != null) {
         Object value = expression.getValue(execution);
         if (value != null) {
            return value.toString();
         }
      }
      return null;
   }
}


What can i change to solve these 3 issues?

thanks
15 REPLIES 15

frederikherema1
Star Contributor
Star Contributor
By default, a process flows in a single transaction until a wait-state of process-end (usertaks, receive-task, …) has been encountered. So the thread will only persist stuff in the DB when wait-state has been reached. When process has NO wait-states, there will never be Runtime-entities in the query only history (if turned on).

Take a look at http://activiti.org/userguide/index.html#asyncContinuations. This allows persisting process state right before activity is executed and returns control of the running thead. After that, your shellTask is executed async by the job executor.

aoliver
Champ in-the-making
Champ in-the-making
Thanks for the information.  That answers 2 of my questions perfectly.  However, what about the behavior i'm seeing in activiti-explorer?  Does that sound normal? 

I would expect to be able to have new sessions in activiti-explorer even after i've kicked off a long-running workflow via activiti-explorer "start process".  Once i've started the workflow in activiti-explorer that session hangs and attempting to login for new sessions hang. 

What am i doing to cause this?

thanks again for your help.

frederikherema1
Star Contributor
Star Contributor
When kicking off a process that doesn't have the async behavior, the calling thread will wait for the engine-call to complete (= process to complete/reach wait-state). Since VAADIN locks a single application (= one user logged in session) to a single thread to prevent concurrency issues. So when trying to refresh, the initial operations should first be finished.

Adding the async=true should overcome this issue IMHO

aoliver
Champ in-the-making
Champ in-the-making
async="true"  appeared to work, but now a different problem appears.  After the long-running service task has been running for 5 minutes and 1 second, it appears that activiti is firing off another instance of the same task.  I saw it in my logs and also see it in ACT_HI_ACTINST.  Activiti does this several times and eventually, the time increase to 10 minutes and then 20 minutes before it hits an integrity constraint.



Why am i seeing this behavior?  For asynchronous tasks, does Activiti automatically retry the process after 5 minutes if there has been no response?  Can i change this behavior?

The task in question is: NetGStageServiceTask
The shell script is basically a dummy windows batch file that stays busy for 6 minutes.
For this test, i am connecting to the demo h2 database.

Here is my bpmn file:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlnsSmiley Surprisedmgdc="http://www.omg.org/spec/DD/20100524/DC" xmlnsSmiley Surprisedmgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="DartProcess" name="DartProcess">
    <documentation>Process definition for Dart BI process.</documentation>
    <startEvent id="DartWorkflowStart" name="DartWorkflowStart">
      <extensionElements>
        <activiti:formProperty id="ShellScriptRoot" name="Shell Script Root Directory" type="string" value="${ShellScriptRoot}" variable="ShellScriptRoot" required="true" readable="true" writable="true"></activiti:formProperty>
        <activiti:formProperty id="ShellScriptSuffix" name="Shell Script Suffix" type="string" value="${ShellScriptSuffix}" variable="ShellScriptSuffix" required="true" readable="true" writable="true"></activiti:formProperty>
        <activiti:formProperty id="DartRunDate" name="DartRunDate (mm\/dd\/yyyy)" type="string" value="${DartRunDate}" variable="DartRunDate" required="true" readable="true" writable="true"></activiti:formProperty>
      </extensionElements>
    </startEvent>
    <serviceTask id="NetGStageServiceTask" name="NetGStageService Task" activiti:async="true" activiti:class="com.scrippsnetworks.ibi.revenueWorkflows.serviceTasks.ShellServiceTask">
      <extensionElements>
        <activiti:field name="command">
          <activiti:expression>${ShellScriptRoot}/ADSD/adsd_staging${ShellScriptSuffix}</activiti:expression>
        </activiti:field>
        <activiti:field name="wait">
          <activiti:string>true</activiti:string>
        </activiti:field>
        <activiti:field name="outputVariable">
          <activiti:string>netGStageStatus</activiti:string>
        </activiti:field>
        <activiti:field name="errorCodeVariable">
          <activiti:string>netGStageReturnCode</activiti:string>
        </activiti:field>
        <activiti:field name="arg1">
          <activiti:expression>${DartRunDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <serviceTask id="InfaProcessServiceTask" name="InfaProcessService Task" activiti:async="true" activiti:class="com.scrippsnetworks.ibi.revenueWorkflows.serviceTasks.ShellServiceTask">
      <extensionElements>
        <activiti:field name="command">
          <activiti:expression>${ShellScriptRoot}/ADSD/adsd_infa${ShellScriptSuffix}</activiti:expression>
        </activiti:field>
        <activiti:field name="wait">
          <activiti:string>true</activiti:string>
        </activiti:field>
        <activiti:field name="outputVariable">
          <activiti:string>infaProcessStatus</activiti:string>
        </activiti:field>
        <activiti:field name="errorCodeVariable">
          <activiti:string>infaProcessReturnCode</activiti:string>
        </activiti:field>
        <activiti:field name="arg1">
          <activiti:expression>${DartRunDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
    <exclusiveGateway id="exclusivegateway3" name="Exclusive Gateway"></exclusiveGateway>
    <endEvent id="DartWorkflowEnd" name="DartWorkflowEnd"></endEvent>
    <userTask id="FixInfaProcessUserTask" name="FixInfaProcessUserTask" activiti:candidateGroups="InfaGroup, WFGroup">
      <documentation>DART Informatica Processes failed with status:

${infaProcessStatus}</documentation>
      <extensionElements>
        <activiti:formProperty id="infaProcessContinueWorkflow" name="Continue Workflow?" type="enum" variable="infaProcessContinueWorkflow" default="true" required="true" readable="true" writable="true">
          <activiti:value id="true" name="Yes"></activiti:value>
          <activiti:value id="false" name="No"></activiti:value>
        </activiti:formProperty>
      </extensionElements>
    </userTask>
    <serviceTask id="FixInfaProcessEmailNotification" name="FixInfaProcessEmailNotification" activiti:type="mail">
      <extensionElements>
        <activiti:field name="to" expression="aoliver@scrippsnetworks.com,jjohnston@scrippsnetworks.com,mlugonjic@scrippsnetworks.com,cwoodall@scrippsnetworks.com"></activiti:field>
        <activiti:field name="from" expression="ibiWorkflowEngine@scrippsnetworks.com"></activiti:field>
        <activiti:field name="subject" expression="DART Infa Process Failure - TEST"></activiti:field>
        <activiti:field name="html">
          <activiti:expression><![CDATA[This is a test of the DART Infa Process Task workflow.  This is only a test.  In the event of a real DART Infa Process failure, you will receive an email similar to this one.
<p />
Status is: <br />
${infaProcessStatus}
<p />
Sincerely,<br />
Your friendly, neighborhood IBI workflow engine]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <serviceTask id="FixBOReportPrepEmailNotification" name="FixBOReportPrepEmailNotification" activiti:type="mail">
      <extensionElements>
        <activiti:field name="to" expression="aoliver@scrippsnetworks.com,jjohnston@scrippsnetworks.com,mlugonjic@scrippsnetworks.com,cwoodall@scrippsnetworks.com"></activiti:field>
        <activiti:field name="from" expression="ibiWorkflowEngine@scrippsnetworks.com"></activiti:field>
        <activiti:field name="subject" expression="DART BO Report Prep Failure - TEST"></activiti:field>
        <activiti:field name="html">
          <activiti:expression><![CDATA[This is a test of the DART BO Report Prep Task workflow.  This is only a test.  In the event of a real DART BO Report Prep failure, you will receive an email similar to this one.
<p />
Status is:<br />
${boReportPrepStatus}
<p />
Sincerely,<br />
Your friendly, neighborhood IBI workflow engine]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <userTask id="FixBOReportPrepUserTask" name="FixBOReportPrepUserTask" activiti:candidateGroups="InfaGroup, WFGroup">
      <documentation>DART BO Report Prep failed with status:

${boReportPrepStatus}</documentation>
      <extensionElements>
        <activiti:formProperty id="boReportPrepContinueWorkflow" name="Continue Workflow?" type="enum" variable="boReportPrepContinueWorkflow" default="true" required="true" readable="true" writable="true">
          <activiti:value id="true" name="Yes"></activiti:value>
          <activiti:value id="false" name="No"></activiti:value>
        </activiti:formProperty>
      </extensionElements>
    </userTask>
    <serviceTask id="NetGStageFailureEmailNotification" name="Email Notification of NetG Stage Failure" activiti:type="mail">
      <extensionElements>
        <activiti:field name="to" expression="aoliver@scrippsnetworks.com,jjohnston@scrippsnetworks.com,mlugonjic@scrippsnetworks.com,cwoodall@scrippsnetworks.com"></activiti:field>
        <activiti:field name="from" expression="ibiWorkflowEngine@scrippsnetworks.com"></activiti:field>
        <activiti:field name="subject" expression="NetG Stage Service Failure - TEST"></activiti:field>
        <activiti:field name="html">
          <activiti:expression><![CDATA[This is a test of the NetGStage Service Task workflow.  This is only a test.  In the event of a real NetGStage Service Task failure, you will receive an email similar to this one.
<p />
Status is:<br />
${netGStageStatus}
<p />
Sincerely,<br />
Your friendly, neighborhood IBI workflow engine]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <endEvent id="NetGStageFailureEnd" name="NetGStageFailureEnd"></endEvent>
    <exclusiveGateway id="exclusivegateway4" name="Exclusive Gateway"></exclusiveGateway>
    <endEvent id="InfaProcessFailureEnd" name="InfaProcessFailureEnd"></endEvent>
    <exclusiveGateway id="exclusivegateway5" name="Exclusive Gateway"></exclusiveGateway>
    <endEvent id="BOReportPrepFailureEnd" name="BOReportPrepFailureEnd"></endEvent>
    <serviceTask id="ImpressionVerification" name="ImpressionVerification" activiti:async="true" activiti:class="com.scrippsnetworks.ibi.revenueWorkflows.serviceTasks.ShellServiceTask">
      <extensionElements>
        <activiti:field name="command">
          <activiti:expression>${ShellScriptRoot}/ADSD/adsd_validate${ShellScriptSuffix}</activiti:expression>
        </activiti:field>
        <activiti:field name="wait">
          <activiti:string>true</activiti:string>
        </activiti:field>
        <activiti:field name="outputVariable">
          <activiti:string>impressionVerificationStatus</activiti:string>
        </activiti:field>
        <activiti:field name="errorCodeVariable">
          <activiti:string>impressionVerificationReturnCode</activiti:string>
        </activiti:field>
        <activiti:field name="arg1">
          <activiti:expression>${DartRunDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <exclusiveGateway id="exclusivegateway6" name="Exclusive Gateway"></exclusiveGateway>
    <serviceTask id="ImpressionVerificationEmailNotification" name="ImpressionVerificationEmailNotification" activiti:type="mail">
      <extensionElements>
        <activiti:field name="to" expression="aoliver@scrippsnetworks.com,jjohnston@scrippsnetworks.com,mlugonjic@scrippsnetworks.com,cwoodall@scrippsnetworks.com"></activiti:field>
        <activiti:field name="from" expression="ibiWorkflowEngine@scrippsnetworks.com"></activiti:field>
        <activiti:field name="subject" expression="DART Impression Verification Warning - TEST"></activiti:field>
        <activiti:field name="html">
          <activiti:expression><![CDATA[This is a test of the DART Impression Verification Task workflow.  This is only a test.  In the event of a real DART Impression Verification failure, you will receive an email similar to this one.
<p />
Status is:<br />
${impressionVerificationStatus}
<p>
Sincerely,<br />
Your friendly, neighborhood IBI workflow engine]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <userTask id="ImpressionVerificationUserTask" name="ImpressionVerificationUserTask" activiti:candidateGroups="InfaGroup, WFGroup">
      <documentation>DART Impression Verification failed with status:

${impressionVerificationStatus}</documentation>
      <extensionElements>
        <activiti:formProperty id="impressionVerificationContinueWorkflow" name="Continue Workflow?" type="enum" variable="impressionVerificationContinueWorkflow" default="true" required="true" readable="true" writable="true">
          <activiti:value id="true" name="Yes"></activiti:value>
          <activiti:value id="false" name="No"></activiti:value>
        </activiti:formProperty>
      </extensionElements>
    </userTask>
    <exclusiveGateway id="exclusivegateway7" name="Exclusive Gateway"></exclusiveGateway>
    <endEvent id="ImpressionVerificationFailureEnd" name="ImpressionVerificationFailureEnd"></endEvent>
    <serviceTask id="BOReportPrepServiceTask" name="BOReportPrepServiceTask" activiti:async="true" activiti:class="com.scrippsnetworks.ibi.revenueWorkflows.serviceTasks.ShellServiceTask">
      <extensionElements>
        <activiti:field name="command">
          <activiti:expression>${ShellScriptRoot}/ADSD/adsd_reportprep${ShellScriptSuffix}</activiti:expression>
        </activiti:field>
        <activiti:field name="wait">
          <activiti:string>true</activiti:string>
        </activiti:field>
        <activiti:field name="outputVariable">
          <activiti:string>boReportPrepStatus</activiti:string>
        </activiti:field>
        <activiti:field name="errorCodeVariable">
          <activiti:string>boReportPrepReturnCode</activiti:string>
        </activiti:field>
        <activiti:field name="arg1">
          <activiti:expression>${DartRunDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <serviceTask id="DartSetup" name="DartSetup" activiti:async="true" activiti:class="com.scrippsnetworks.ibi.revenueWorkflows.serviceTasks.ShellServiceTask">
      <extensionElements>
        <activiti:field name="command">
          <activiti:expression>${ShellScriptRoot}/ADSD/adsd_setup${ShellScriptSuffix}</activiti:expression>
        </activiti:field>
        <activiti:field name="wait">
          <activiti:string>true</activiti:string>
        </activiti:field>
        <activiti:field name="outputVariable">
          <activiti:string>dartSetupStatus</activiti:string>
        </activiti:field>
        <activiti:field name="errorCodeVariable">
          <activiti:string>dartSetupReturnCode</activiti:string>
        </activiti:field>
        <activiti:field name="arg1">
          <activiti:expression>${DartRunDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <serviceTask id="DartSetupEmailNotification" name="DartSetupEmailNotification" activiti:type="mail">
      <extensionElements>
        <activiti:field name="to" expression="aoliver@scrippsnetworks.com,jjohnston@scrippsnetworks.com,mlugonjic@scrippsnetworks.com,cwoodall@scrippsnetworks.com"></activiti:field>
        <activiti:field name="from" expression="ibiWorkflowEngine@scrippsnetworks.com"></activiti:field>
        <activiti:field name="subject" expression="DART Setup Service Failure - TEST"></activiti:field>
        <activiti:field name="html">
          <activiti:expression><![CDATA[This is a test of the DART Setup Service Task workflow.  This is only a test.  In the event of a real DART Setup Service Task failure, you will receive an email similar to this one.
<p />
Status is:<br />
${dartSetupStatus}
<p />
Sincerely,<br />
Your friendly, neighborhood IBI workflow engine]]></activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <exclusiveGateway id="exclusivegateway8" name="Exclusive Gateway"></exclusiveGateway>
    <endEvent id="DartSetupFailureEnd" name="DartSetupFailureEnd"></endEvent>
    <sequenceFlow id="flow2" name="" sourceRef="NetGStageServiceTask" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow3" name="" sourceRef="exclusivegateway1" targetRef="InfaProcessServiceTask">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${netGStageReturnCode=="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow5" name="" sourceRef="InfaProcessServiceTask" targetRef="exclusivegateway2"></sequenceFlow>
    <sequenceFlow id="flow9" name="" sourceRef="exclusivegateway3" targetRef="DartWorkflowEnd">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${boReportPrepReturnCode=="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow17" name="" sourceRef="exclusivegateway2" targetRef="FixInfaProcessEmailNotification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${infaProcessReturnCode!="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow18" name="" sourceRef="FixInfaProcessEmailNotification" targetRef="FixInfaProcessUserTask"></sequenceFlow>
    <sequenceFlow id="flow20" name="" sourceRef="exclusivegateway3" targetRef="FixBOReportPrepEmailNotification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${boReportPrepReturnCode!="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow21" name="" sourceRef="FixBOReportPrepEmailNotification" targetRef="FixBOReportPrepUserTask"></sequenceFlow>
    <sequenceFlow id="flow24" name="" sourceRef="exclusivegateway1" targetRef="NetGStageFailureEmailNotification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${netGStageReturnCode!="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow25" name="" sourceRef="NetGStageFailureEmailNotification" targetRef="NetGStageFailureEnd"></sequenceFlow>
    <sequenceFlow id="flow26" name="" sourceRef="FixInfaProcessUserTask" targetRef="exclusivegateway4"></sequenceFlow>
    <sequenceFlow id="flow27" name="" sourceRef="exclusivegateway4" targetRef="InfaProcessServiceTask">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${infaProcessContinueWorkflow=="true"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow28" name="" sourceRef="exclusivegateway4" targetRef="InfaProcessFailureEnd">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${infaProcessContinueWorkflow=="false"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow29" name="" sourceRef="FixBOReportPrepUserTask" targetRef="exclusivegateway5"></sequenceFlow>
    <sequenceFlow id="flow31" name="" sourceRef="exclusivegateway5" targetRef="BOReportPrepFailureEnd">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${boReportPrepContinueWorkflow=="false"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow32" name="" sourceRef="exclusivegateway2" targetRef="ImpressionVerification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${infaProcessReturnCode=="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow33" name="" sourceRef="ImpressionVerification" targetRef="exclusivegateway6"></sequenceFlow>
    <sequenceFlow id="flow35" name="" sourceRef="exclusivegateway6" targetRef="ImpressionVerificationEmailNotification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${impressionVerificationReturnCode!="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow36" name="" sourceRef="ImpressionVerificationEmailNotification" targetRef="ImpressionVerificationUserTask"></sequenceFlow>
    <sequenceFlow id="flow37" name="" sourceRef="ImpressionVerificationUserTask" targetRef="exclusivegateway7"></sequenceFlow>
    <sequenceFlow id="flow39" name="" sourceRef="exclusivegateway7" targetRef="ImpressionVerificationFailureEnd">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${impressionVerificationContinueWorkflow=="false"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow40" name="" sourceRef="exclusivegateway6" targetRef="BOReportPrepServiceTask">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${impressionVerificationReturnCode=="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow41" name="" sourceRef="BOReportPrepServiceTask" targetRef="exclusivegateway3"></sequenceFlow>
    <sequenceFlow id="flow42" name="" sourceRef="exclusivegateway5" targetRef="BOReportPrepServiceTask">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${boReportPrepContinueWorkflow=="true"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow43" name="" sourceRef="exclusivegateway7" targetRef="BOReportPrepServiceTask">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${impressionVerificationContinueWorkflow=="true"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow44" name="" sourceRef="DartWorkflowStart" targetRef="DartSetup"></sequenceFlow>
    <sequenceFlow id="flow46" name="" sourceRef="DartSetup" targetRef="exclusivegateway8"></sequenceFlow>
    <sequenceFlow id="flow47" name="" sourceRef="exclusivegateway8" targetRef="NetGStageServiceTask">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${dartSetupReturnCode=="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow48" name="" sourceRef="exclusivegateway8" targetRef="DartSetupEmailNotification">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${dartSetupReturnCode!="0"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow49" name="" sourceRef="DartSetupEmailNotification" targetRef="DartSetupFailureEnd"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_DartProcess">
    <bpmndi:BPMNPlane bpmnElement="DartProcess" id="BPMNPlane_DartProcess">
      <bpmndi:BPMNShape bpmnElement="DartWorkflowStart" id="BPMNShape_DartWorkflowStart">
        <omgdc:Bounds height="35" width="35" x="100" y="90"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="NetGStageServiceTask" id="BPMNShape_NetGStageServiceTask">
        <omgdc:Bounds height="55" width="105" x="440" y="80"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40" width="40" x="600" y="87"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="InfaProcessServiceTask" id="BPMNShape_InfaProcessServiceTask">
        <omgdc:Bounds height="55" width="105" x="700" y="80"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
        <omgdc:Bounds height="40" width="40" x="860" y="87"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway3" id="BPMNShape_exclusivegateway3">
        <omgdc:Bounds height="40" width="40" x="1438" y="87"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="DartWorkflowEnd" id="BPMNShape_DartWorkflowEnd">
        <omgdc:Bounds height="35" width="35" x="1523" y="90"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="FixInfaProcessUserTask" id="BPMNShape_FixInfaProcessUserTask">
        <omgdc:Bounds height="55" width="105" x="828" y="280"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="FixInfaProcessEmailNotification" id="BPMNShape_FixInfaProcessEmailNotification">
        <omgdc:Bounds height="55" width="105" x="828" y="181"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="FixBOReportPrepEmailNotification" id="BPMNShape_FixBOReportPrepEmailNotification">
        <omgdc:Bounds height="57" width="105" x="1406" y="183"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="FixBOReportPrepUserTask" id="BPMNShape_FixBOReportPrepUserTask">
        <omgdc:Bounds height="55" width="105" x="1406" y="280"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="NetGStageFailureEmailNotification" id="BPMNShape_NetGStageFailureEmailNotification">
        <omgdc:Bounds height="55" width="105" x="568" y="181"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="NetGStageFailureEnd" id="BPMNShape_NetGStageFailureEnd">
        <omgdc:Bounds height="35" width="35" x="603" y="280"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway4" id="BPMNShape_exclusivegateway4">
        <omgdc:Bounds height="40" width="40" x="732" y="287"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="InfaProcessFailureEnd" id="BPMNShape_InfaProcessFailureEnd">
        <omgdc:Bounds height="35" width="35" x="735" y="361"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway5" id="BPMNShape_exclusivegateway5">
        <omgdc:Bounds height="40" width="40" x="1310" y="287"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="BOReportPrepFailureEnd" id="BPMNShape_BOReportPrepFailureEnd">
        <omgdc:Bounds height="35" width="35" x="1313" y="361"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="ImpressionVerification" id="BPMNShape_ImpressionVerification">
        <omgdc:Bounds height="55" width="105" x="960" y="80"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway6" id="BPMNShape_exclusivegateway6">
        <omgdc:Bounds height="40" width="40" x="1120" y="87"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="ImpressionVerificationEmailNotification" id="BPMNShape_ImpressionVerificationEmailNotification">
        <omgdc:Bounds height="55" width="105" x="1088" y="181"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="ImpressionVerificationUserTask" id="BPMNShape_ImpressionVerificationUserTask">
        <omgdc:Bounds height="55" width="105" x="1088" y="280"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway7" id="BPMNShape_exclusivegateway7">
        <omgdc:Bounds height="40" width="40" x="1230" y="287"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="ImpressionVerificationFailureEnd" id="BPMNShape_ImpressionVerificationFailureEnd">
        <omgdc:Bounds height="35" width="35" x="1233" y="361"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="BOReportPrepServiceTask" id="BPMNShape_BOReportPrepServiceTask">
        <omgdc:Bounds height="55" width="105" x="1278" y="80"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="DartSetup" id="BPMNShape_DartSetup">
        <omgdc:Bounds height="55" width="105" x="180" y="80"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="DartSetupEmailNotification" id="BPMNShape_DartSetupEmailNotification">
        <omgdc:Bounds height="55" width="105" x="308" y="184"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway8" id="BPMNShape_exclusivegateway8">
        <omgdc:Bounds height="40" width="40" x="340" y="87"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="DartSetupFailureEnd" id="BPMNShape_DartSetupFailureEnd">
        <omgdc:Bounds height="35" width="35" x="343" y="280"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="545" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="600" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="640" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="700" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="805" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="860" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
        <omgdi:waypoint x="1478" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="1523" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow17" id="BPMNEdge_flow17">
        <omgdi:waypoint x="880" y="127"></omgdi:waypoint>
        <omgdi:waypoint x="880" y="181"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow18" id="BPMNEdge_flow18">
        <omgdi:waypoint x="880" y="236"></omgdi:waypoint>
        <omgdi:waypoint x="880" y="280"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow20" id="BPMNEdge_flow20">
        <omgdi:waypoint x="1458" y="127"></omgdi:waypoint>
        <omgdi:waypoint x="1458" y="183"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow21" id="BPMNEdge_flow21">
        <omgdi:waypoint x="1458" y="240"></omgdi:waypoint>
        <omgdi:waypoint x="1458" y="280"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow24" id="BPMNEdge_flow24">
        <omgdi:waypoint x="620" y="127"></omgdi:waypoint>
        <omgdi:waypoint x="620" y="181"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow25" id="BPMNEdge_flow25">
        <omgdi:waypoint x="620" y="236"></omgdi:waypoint>
        <omgdi:waypoint x="620" y="280"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow26" id="BPMNEdge_flow26">
        <omgdi:waypoint x="828" y="307"></omgdi:waypoint>
        <omgdi:waypoint x="772" y="307"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow27" id="BPMNEdge_flow27">
        <omgdi:waypoint x="752" y="287"></omgdi:waypoint>
        <omgdi:waypoint x="752" y="135"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow28" id="BPMNEdge_flow28">
        <omgdi:waypoint x="752" y="327"></omgdi:waypoint>
        <omgdi:waypoint x="752" y="361"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow29" id="BPMNEdge_flow29">
        <omgdi:waypoint x="1406" y="307"></omgdi:waypoint>
        <omgdi:waypoint x="1350" y="307"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow31" id="BPMNEdge_flow31">
        <omgdi:waypoint x="1330" y="327"></omgdi:waypoint>
        <omgdi:waypoint x="1330" y="361"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow32" id="BPMNEdge_flow32">
        <omgdi:waypoint x="900" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="960" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow33" id="BPMNEdge_flow33">
        <omgdi:waypoint x="1065" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="1120" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow35" id="BPMNEdge_flow35">
        <omgdi:waypoint x="1140" y="127"></omgdi:waypoint>
        <omgdi:waypoint x="1140" y="181"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow36" id="BPMNEdge_flow36">
        <omgdi:waypoint x="1140" y="236"></omgdi:waypoint>
        <omgdi:waypoint x="1140" y="280"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow37" id="BPMNEdge_flow37">
        <omgdi:waypoint x="1193" y="307"></omgdi:waypoint>
        <omgdi:waypoint x="1230" y="307"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow39" id="BPMNEdge_flow39">
        <omgdi:waypoint x="1250" y="327"></omgdi:waypoint>
        <omgdi:waypoint x="1250" y="361"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow40" id="BPMNEdge_flow40">
        <omgdi:waypoint x="1160" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="1278" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow41" id="BPMNEdge_flow41">
        <omgdi:waypoint x="1383" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="1438" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow42" id="BPMNEdge_flow42">
        <omgdi:waypoint x="1330" y="287"></omgdi:waypoint>
        <omgdi:waypoint x="1330" y="135"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow43" id="BPMNEdge_flow43">
        <omgdi:waypoint x="1250" y="287"></omgdi:waypoint>
        <omgdi:waypoint x="1250" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="1278" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow44" id="BPMNEdge_flow44">
        <omgdi:waypoint x="135" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="180" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow46" id="BPMNEdge_flow46">
        <omgdi:waypoint x="285" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="340" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow47" id="BPMNEdge_flow47">
        <omgdi:waypoint x="380" y="107"></omgdi:waypoint>
        <omgdi:waypoint x="440" y="107"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow48" id="BPMNEdge_flow48">
        <omgdi:waypoint x="360" y="127"></omgdi:waypoint>
        <omgdi:waypoint x="360" y="184"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow49" id="BPMNEdge_flow49">
        <omgdi:waypoint x="360" y="239"></omgdi:waypoint>
        <omgdi:waypoint x="360" y="280"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

Here is my java delegate:


public class ShellServiceTask implements JavaDelegate {

final static Logger logger = Logger.getLogger(ShellServiceTask.class.getName());

protected Expression command;
protected Expression wait;
protected Expression arg1;
protected Expression arg2;
protected Expression arg3;
protected Expression arg4;
protected Expression arg5;
protected Expression outputVariable;
protected Expression errorCodeVariable;
protected Expression redirectError;
protected Expression cleanEnv;
protected Expression directory;

String commandStr;
String arg1Str;
String arg2Str;
String arg3Str;
String arg4Str;
String arg5Str;
String waitStr;
String resultVariableStr;
String errorCodeVariableStr;
Boolean waitFlag;
Boolean redirectErrorFlag;
Boolean cleanEnvBoolan;
String directoryStr;

@Override
public void execute(DelegateExecution execution) throws Exception {
  logger.info("In " + command.getValue(execution) + " ShellTask script with variables = " + execution.getVariables());
  executeScript(execution);
  // create a new value for execution
 
  logger.info("Leaving " + command.getValue(execution) + " ShellTask script with variables = " + execution.getVariables());

}

private void readFields(DelegateExecution execution) {
     commandStr = getStringFromField(command, execution);
     arg1Str = getStringFromField(arg1, execution);
     arg2Str = getStringFromField(arg2, execution);
     arg3Str = getStringFromField(arg3, execution);
     arg4Str = getStringFromField(arg4, execution);
     arg5Str = getStringFromField(arg5, execution);
     waitStr = getStringFromField(wait, execution);
     resultVariableStr = getStringFromField(outputVariable, execution);
     errorCodeVariableStr = getStringFromField(errorCodeVariable, execution);

     String redirectErrorStr = getStringFromField(redirectError, execution);
     String cleanEnvStr = getStringFromField(cleanEnv, execution);

     waitFlag = waitStr == null || waitStr.equals("true");
     redirectErrorFlag = redirectErrorStr != null && redirectErrorStr.equals("true");
     cleanEnvBoolan = cleanEnvStr != null && cleanEnvStr.equals("true");
     directoryStr = getStringFromField(directory, execution);
}

public String toString() {
  return "command="+command+", arg1="+arg1+", arg2="+arg2+", arg3="+arg3+", arg4="+arg4+", arg5="+arg5+
    ", wait="+wait+", outputVariable="+outputVariable+", errorCodeVariable="+errorCodeVariable;
}

public void executeScript(DelegateExecution execution) {
  logger.info("starting execute");
   
  readFields(execution);
   
  List<String> argList = new ArrayList<String>();
   argList.add(commandStr);
   
   if (arg1Str != null)
    argList.add(arg1Str);
   if (arg2Str != null)
    argList.add(arg2Str);
   if (arg3Str != null)
    argList.add(arg3Str);
   if (arg4Str != null)
    argList.add(arg4Str);
   if (arg5Str != null)
    argList.add(arg5Str);
   
   ProcessBuilder processBuilder = new ProcessBuilder(argList);
   
   try {
    processBuilder.redirectErrorStream(redirectErrorFlag);
    if (cleanEnvBoolan) {
     Map<String, String> env = processBuilder.environment();
     env.clear();
    }
    
    if (directoryStr != null && directoryStr.length() > 0)
     processBuilder.directory(new File(directoryStr));
    
     Process process = processBuilder.start();
    
     if (waitFlag) {
      int errorCode = process.waitFor();
     
      if (resultVariableStr != null) {
       String result = convertStreamToStr(process.getInputStream());
       execution.setVariable(resultVariableStr, result);
      }
     
      if (errorCodeVariableStr != null) {
       execution.setVariable(errorCodeVariableStr, Integer.toString(errorCode));
      }

     }
   } catch (Exception e) {
    logger.info("Could not execute shell command with error = " + e);
    throw new ActivitiException("Could not execute shell command" , e);
   }

}
 
public static String convertStreamToStr(InputStream is) throws IOException {
  if (is != null) {
   Writer writer = new StringWriter();
   char[] buffer = new char[1024];
   try {
    Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    int n;
    while ((n = reader.read(buffer)) != -1) {
     writer.write(buffer, 0, n);
    }
   } finally {
    is.close();
   }
   return writer.toString();
  } else {
   return "";
  }
}

protected String getStringFromField(Expression expression, DelegateExecution execution) {
  if (expression != null) {
   Object value = expression.getValue(execution);
   if (value != null) {
    return value.toString();
   }
  }
  return null;
}
}

Thank you again for the help.

aoliver
Champ in-the-making
Champ in-the-making
Just in case previous post did not make it…
async="true" resolved the one issue, but created a worse issue.  For my long-running task (over 6.5 minutes), after 5 minutes and 1 second of executing, it appears, according to log files and database, that Activiti is trying to run the task again.  It continues to do this every 5 minutes and 1 second.  So, i end up with several instances of the same task attempting to run.  Eventually, it fails because it cannot find a

What am i doing wrong?  All i did was add the async="true" property to my java service task.

Thanks again for your help.

frederikherema1
Star Contributor
Star Contributor
A lot depends on the timeout limit of your database transactions… All activiti-calls are done within a database-transation. If the operation takes too long, the transaction will be timed out and exception is thrown (not marking the job as executed and unlocking it to be acquired again by the job-executor).

On top of that, to prevent job-executor not to block all of it's threads too long, a max-lock time is set (default is 5 minutes: int lockTimeInMillis = 5 * 60 * 1000Smiley Wink. If expired, job executor presumes something went wrong and unlocks the job, effectively marking it to be executed again.
The lock-time is a setter on the JobExecutor and can be altered in configuration or even from code, so this can be raised.

But consider that all job-executions are executed in a transaction. Having a lot of long-running transactions can influence db-performance. If you don't actually need to update process-properties very often when the async-servicetask is running, I suggest working with a thread-pool outside of activiti:

  • The service-task prepares (eg. read variables from process) an creates the "job" or runnable to be executed by the external thread pool
  • Service-task queues the job (saving reference to process-instance id) and finishes
  • Right after the service-taks, there's a receive-task that waits, releasing the calling thead and not holding open the transaction.
  • Long-running operation completes and signals the process-instance and perhaps set some variables on the process based on the results before signaling.

aoliver
Champ in-the-making
Champ in-the-making
Thanks for the insigtful information.  A few follow-up questions:

First of all, about my environment:
My environment is a web app that automatically triggers workflows using the activiti-rest web application.  Also, my workflows contain user tasks so i leverage activiti-explorer web application to handle user forms.  Thus, the service task i am creating i am them using the eclipse plugin to create deployment artifacts and then putting the jar file in the activiti-rest/WEB-INF/lib directory and the activiti-explorer/WEB-INF/lib directory.  So, in my service task, how do i get a handle to everything i need to inject into the long-running thread?  It seems all the context i have is DelegateExecution.  Also, once in my long-running thread, how to signal the receive task?  Since this is a jar file in the activiti-explorer/activiti-rest apps, i don't seem to be able to access the processEngine.

1) In your
    (which is very helpful), even though i'm a recent java convert from python, i think i understand most of it.  2 Questions:
    a) When you state "queues the job (saving reference to process-instance id)", do you mean that service task starts the long-running process external thread and gives it a reference to the process-instance id?
    b)Even though i've not dealt with receive tasks, i think i can figure that out.  What i don't understand is how the long-running process will set process variables on the process instance if it is running outside the scope of the job-executor?  So far, i've done this:

    Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).singleResult();

    where processId was injected into the long-running thread.  However, i don't seem to be able to get a handle on the processEngine in the long-running thread.  Thus, how do i set variables in the process context?


    2) For the less optimal solution of changing the timeout:
    a) If the number of tasks executed by Activiti is no more than a few dozen a day and most of those tasks are long-running ETL-type tasks and all are running async=true, would the danger be minimal if the timeout is increased to, say 12 hours?  The reason i ask about this solution is that i'm up against a deadline.  Add, where/how do i make this configuration change? 
    b) Currently, i would need to make this change in the Activiti-Explorer and Activiti-Rest applications.  Is this a configuration for the ProcessEngine?  I've looked in the ProcessEngineConfigurationImpl, but could not find where the lock-time was being set for the JobExecutor.  Could you give me an example where the jobExecutor lock-time is being changed via configuration, in a spring app context and/or activiti context?

    Thanks again for you assistance.  You have been most helpful in explaining this behavior and giving implementation recommendations.

frederikherema1
Star Contributor
Star Contributor
a) yep, exactly
b) the long-running process (external of activiti) can use runtimeService.setVariables(processInstanceId, mapWithProperties)

ABout "getting handle of process engine", you might trying to use the ProcessEngines static class, which can access process engines registered in the same VM.

2 a) If you raise the number of threads the job-executor has available higher than the max-expected long-running processes executed, this is fine.
b) There is a config-file in the web apps (activiti.cfg.xml) where you can set the properties. The property I'm referring to is on the job-executor itself, which you can set on the ProcessEngineConfigurationImpl (using the configuration or using java-code). Relevant props: lockTimeInMillis and corePoolSize + maxPoolSize

Example:


<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="databaseType" value="oracle" />
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="hibernateTransactionManager" />
<property name="databaseSchemaUpdate" value="false" />
<property name="history" value="audit" />
<property name="jobExecutorActivate" value="true" />
<property name="jobExecutor" ref="jobExecutor"/>
</bean>

<bean id="jobExecutor" class="org.activiti.engine.impl.jobexecutor.JobExecutor">
<property name="corePoolSize" value="${activiti.jobExecutor.corePoolSize}"/>
<property name="lockTimeInMillis" value="${activiti.jobExecutor.lockTimeInMillis}"/>
<property name="maxJobsPerAcquisition" value="${activiti.jobExecutor.maxJobsPerAcquisition}"/>
<property name="maxPoolSize" value="${activiti.jobExecutor.maxPoolSize}"/>
<property name="queueSize" value="${activiti.jobExecutor.queueSize}"/>
<property name="waitTimeInMillis" value="${activiti.jobExecutor.waitTimeInMillis}"/>
</bean>

aoliver
Champ in-the-making
Champ in-the-making
Thanks again for the valuable information.  You have answered all my questions fully.  This forum has been a deployment-saver Smiley Happy.

I was hoping you could do a quick review of my Service Task and Runnable to verify that i'm doing things correctly.  As of now, i've taken your suggestion on using the Java Receive Task and an asynchronous thread.  I've pasted my code below (Of course, i've stolen activiti's logic for running a shell task Smiley Wink 😞


public class ShellServiceTask implements JavaDelegate {

final static Logger logger = Logger.getLogger(ShellServiceTask.class.getName());

protected Expression command;
protected Expression wait;
protected Expression arg1;
protected Expression arg2;
protected Expression arg3;
protected Expression arg4;
protected Expression arg5;
protected Expression outputVariable;
protected Expression errorCodeVariable;
protected Expression redirectError;
protected Expression cleanEnv;
protected Expression directory;

String commandStr;
String arg1Str;
String arg2Str;
String arg3Str;
String arg4Str;
String arg5Str;
String waitStr;
String resultVariableStr;
String errorCodeVariableStr;
Boolean waitFlag;
Boolean redirectErrorFlag;
Boolean cleanEnvBoolan;
String directoryStr;

@Override
public void execute(DelegateExecution execution) throws Exception {
  logger.info("In " + command.getValue(execution) + " ShellTask script with variables = " + execution.getVariables());
 
  executeScript(execution);
  // create a new value for execution
 
  logger.info("Leaving " + command.getValue(execution) + " ShellTask script with variables = " + execution.getVariables());

}

private void readFields(DelegateExecution execution) {
     commandStr = getStringFromField(command, execution);
     arg1Str = getStringFromField(arg1, execution);
     arg2Str = getStringFromField(arg2, execution);
     arg3Str = getStringFromField(arg3, execution);
     arg4Str = getStringFromField(arg4, execution);
     arg5Str = getStringFromField(arg5, execution);
     waitStr = getStringFromField(wait, execution);
     resultVariableStr = getStringFromField(outputVariable, execution);
     errorCodeVariableStr = getStringFromField(errorCodeVariable, execution);

     String redirectErrorStr = getStringFromField(redirectError, execution);
     String cleanEnvStr = getStringFromField(cleanEnv, execution);

     waitFlag = waitStr == null || waitStr.equals("true");
     redirectErrorFlag = redirectErrorStr != null && redirectErrorStr.equals("true");
     cleanEnvBoolan = cleanEnvStr != null && cleanEnvStr.equals("true");
     directoryStr = getStringFromField(directory, execution);
}

public String toString() {
  return "command="+command+", arg1="+arg1+", arg2="+arg2+", arg3="+arg3+", arg4="+arg4+", arg5="+arg5+
    ", wait="+wait+", outputVariable="+outputVariable+", errorCodeVariable="+errorCodeVariable;
}

public void executeScript(DelegateExecution execution) {
  logger.info("starting execute");
   
  readFields(execution);
   
  List<String> argList = new ArrayList<String>();
   argList.add(commandStr);
   
   if (arg1Str != null)
    argList.add(arg1Str);
   if (arg2Str != null)
    argList.add(arg2Str);
   if (arg3Str != null)
    argList.add(arg3Str);
   if (arg4Str != null)
    argList.add(arg4Str);
   if (arg5Str != null)
    argList.add(arg5Str);
  
   logger.info("Firing executor with processInstanceId = " + execution.getProcessInstanceId());
  
   Thread executor = new Thread(new ShellTaskThread(argList, waitFlag, redirectErrorFlag, cleanEnvBoolan, directoryStr,
     resultVariableStr, errorCodeVariableStr, execution.getProcessInstanceId()));
   executor.start();
   logger.info("Executor started…leaving workflow");

}
 
protected String getStringFromField(Expression expression, DelegateExecution execution) {
  if (expression != null) {
   Object value = expression.getValue(execution);
   if (value != null) {
    return value.toString();
   }
  }
  return null;
}
}

class ShellTaskThread implements Runnable {
final static Logger logger = Logger.getLogger(ShellTaskThread.class.getName());

List<String> commands;
boolean waitFlag;
String resultVariableStr;
String errorCodeVariableStr;
boolean cleanEnv;
boolean redirectErrorFlag;
String directoryStr;
String processId;

public ShellTaskThread(List<String> commands, boolean waitFlag, boolean redirectErrorFlag,
   boolean cleanEnv, String directoryStr,
   String resultVariableStr, String errorCodeVariableStr, String processId) {
  this.commands = commands;
  this.waitFlag = waitFlag;
  this.resultVariableStr = resultVariableStr;
  this.errorCodeVariableStr = errorCodeVariableStr;
  this.redirectErrorFlag = redirectErrorFlag;
  this.cleanEnv = cleanEnv;
  this.directoryStr = directoryStr;
  this.processId = processId;
}

@Override
public void run() {

  logger.info("running async thread with command: " + commands);
  logger.info("other vars: resultVariableStr: " + resultVariableStr + " errorCodeVariableStr: " +
     errorCodeVariableStr + " processId: " + processId);
 
  // set activiti context
  ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  RuntimeService runtimeService = processEngine.getRuntimeService();
  Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).singleResult();

  logger.info("execution = " + execution.getId() + ": " + execution.getProcessInstanceId());
  ProcessBuilder processBuilder = new ProcessBuilder(commands);
   
  try {
   processBuilder.redirectErrorStream(redirectErrorFlag);
   if (cleanEnv) {
    Map<String, String> env = processBuilder.environment();
    env.clear();
   }
   
   if (directoryStr != null && directoryStr.length() > 0)
    processBuilder.directory(new File(directoryStr));
   
   Process process = processBuilder.start();
  
   if (waitFlag) {
    try {
     int errorCode = process.waitFor();
     logger.info("errorCode = " + errorCode);

     if (resultVariableStr != null) {
      String result = convertStreamToStr(process.getInputStream());
      runtimeService.setVariable(execution.getId(), resultVariableStr, result);
     }
    
     if (errorCodeVariableStr != null) {
      runtimeService.setVariable(execution.getId(), errorCodeVariableStr, Integer.toString(errorCode));
     }
    } catch (InterruptedException e) {
     if (resultVariableStr != null) {
      runtimeService.setVariable(execution.getId(), resultVariableStr, e);
     }
     if (errorCodeVariableStr != null) {
      runtimeService.setVariable(execution.getId(), errorCodeVariableStr, "1");
     }
    
     throw e;
    }

   }
  
  } catch (Exception e) {
   logger.info("Could not execute shell command " + commands + " with error = " + e);
  } finally {
   logger.info("Leaving thread with process variables = " + runtimeService.getVariables(execution.getId()));
   runtimeService.signal(execution.getId());
  }
}
 
public static String convertStreamToStr(InputStream is) throws IOException {
  if (is != null) {
   Writer writer = new StringWriter();
   char[] buffer = new char[1024];
   try {
    Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    int n;
    while ((n = reader.read(buffer)) != -1) {
     writer.write(buffer, 0, n);
    }
   } finally {
    is.close();
   }
   return writer.toString();
  } else {
   return "";
  }
}

}

thanks so much for your continued help.  I am learning lots!