content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * 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.activiti.engine.test.api.variables;
  
  import static java.util.Collections.singletonMap;
  import static org.assertj.core.api.Assertions.assertThat;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.activiti.engine.delegate.DelegateExecution;
  import org.activiti.engine.delegate.DelegateTask;
  import org.activiti.engine.delegate.ExecutionListener;
  import org.activiti.engine.delegate.Expression;
  import org.activiti.engine.delegate.JavaDelegate;
  import org.activiti.engine.delegate.TaskListener;
  import org.activiti.engine.impl.test.PluggableActivitiTestCase;
  import org.activiti.engine.runtime.Execution;
  import org.activiti.engine.runtime.ProcessInstance;
  import org.activiti.engine.task.Task;
  import org.activiti.engine.test.Deployment;
  
  /**
  
   */
  public class TransientVariablesTest extends PluggableActivitiTestCase {
  
      @Deployment
      public void testSetTransientVariableInServiceTask() {
          // Process has two service task: first sets transient vars,
          // second then processes transient var and puts data in real vars.
          // (mimicing a service + processing call)
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("transientVarsTest");
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          String message = (String) taskService.getVariable(task.getId(), "message");
          assertThat(message).isEqualTo("Hello World!");
  
          // Variable should not be there after user task
          assertThat(runtimeService.getVariable(processInstance.getId(), "response")).isNull();
      }
  
      // Test case for ACT-4304
      @Deployment
      public void testTransientVariableDeletion() {
          String processInstanceId = runtimeService.startProcessInstanceByKey("transientVarsTest").getId();
          long count = historyService
              .createHistoricVariableInstanceQuery()
              .processInstanceId(processInstanceId)
              .variableName("response")
              .count();
          assertThat(count).isEqualTo(0);
          count = historyService
              .createHistoricVariableInstanceQuery()
              .processInstanceId(processInstanceId)
              .variableName("status")
              .count();
          assertThat(count).isEqualTo(0);
      }
  
      @Deployment
      public void testUseTransientVariableInExclusiveGateway() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("transientVarsTest");
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getTaskDefinitionKey()).isEqualTo("responseOk");
  
          // Variable should not be there after user task
          assertThat(runtimeService.getVariable(processInstance.getId(), "response")).isNull();
      }
  
      @Deployment
      public void testTaskCompleteWithTransientVariables() {
          Map<String, Object> persistentVars = new HashMap<String, Object>();
          persistentVars.put("persistentVar1", "Hello World");
          persistentVars.put("persistentVar2", 987654321);
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("transientVarsTest", persistentVars);
  
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("My Task");
  
          persistentVars.clear();
          Map<String, Object> transientVars = new HashMap<String, Object>();
          transientVars.put("unusedTransientVar", "Hello there");
          transientVars.put("transientVar", "OK");
          taskService.complete(task.getId(), persistentVars, transientVars);
  
          // Combined var has been set by execution listener
          String combinedVar = (String) runtimeService.getVariable(processInstance.getId(), "combinedVar");
          assertThat(combinedVar).isEqualTo("Hello WorldABC123");
  
          assertThat(runtimeService.getVariable(processInstance.getId(), "persistentVar1")).isNotNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "persistentVar2")).isNotNull();
  
          assertThat(runtimeService.getVariable(processInstance.getId(), "unusedTransientVar")).isNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "transientVar")).isNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "secondTransientVar")).isNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "thirdTransientVar")).isNull();
      }
  
      @Deployment
      public void testTaskResolveWithTransientVariables() {
          Map<String, Object> persistentVars = new HashMap<String, Object>();
          persistentVars.put("persistentVar1", "Hello World");
          persistentVars.put("persistentVar2", 987654321);
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("transientVarsTest", persistentVars);
  
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("My Task");
  
          persistentVars.clear();
          Map<String, Object> transientVars = new HashMap<String, Object>();
          transientVars.put("unusedTransientVar", "Hello there");
          transientVars.put("transientVar", "OK");
          taskService.complete(task.getId(), persistentVars, transientVars);
  
          // Combined var has been set by execution listener
          String combinedVar = (String) runtimeService.getVariable(processInstance.getId(), "combinedVar");
          assertThat(combinedVar).isEqualTo("Hello WorldABC123");
  
          assertThat(runtimeService.getVariable(processInstance.getId(), "persistentVar1")).isNotNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "persistentVar2")).isNotNull();
  
          assertThat(runtimeService.getVariable(processInstance.getId(), "unusedTransientVar")).isNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "transientVar")).isNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "secondTransientVar")).isNull();
          assertThat(runtimeService.getVariable(processInstance.getId(), "thirdTransientVar")).isNull();
      }
  
      @Deployment
      public void testTaskListenerWithTransientVariables() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("transientVarsTest");
  
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("Task after");
  
          String mergedResult = (String) taskService.getVariable(task.getId(), "mergedResult");
          assertThat(mergedResult).isEqualTo("transientVar01transientVar02transientVar03");
      }
  
      @Deployment
      public void testTransientVariableShadowsPersistentVariable() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "transientVarsTest",
              singletonMap("theVar", "Hello World")
          );
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          String varValue = (String) taskService.getVariable(task.getId(), "resultVar");
          assertThat(varValue).isEqualTo("I am shadowed");
      }
  
      @Deployment
      public void testTriggerWithTransientVars() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("transientVarsTest");
  
          Execution executionInWait1 = runtimeService.createExecutionQuery().activityId("wait1").singleResult();
          runtimeService.trigger(executionInWait1.getId(), singletonMap("persistentVar", "persistentValue01"));
  
          Execution executionInWait2 = runtimeService.createExecutionQuery().activityId("wait2").singleResult();
          runtimeService.trigger(
              executionInWait2.getId(),
              singletonMap("anotherPersistentVar", "persistentValue02"),
              singletonMap("transientVar", "transientValue")
          );
  
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          String result = (String) taskService.getVariable(task.getId(), "result");
          assertThat(result).isEqualTo("persistentValue02persistentValue01transientValue");
  
          assertThat(runtimeService.getVariable(processInstance.getId(), "transientVar")).isNull();
      }
  
      @Deployment
      public void testStartProcessInstanceByKey() {
          ProcessInstance processInstance = runtimeService
              .createProcessInstanceBuilder()
              .processDefinitionKey("transientVarsTest")
              .transientVariable("variable", "gotoA")
              .start();
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("A");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
  
          processInstance = runtimeService
              .createProcessInstanceBuilder()
              .processDefinitionKey("transientVarsTest")
              .transientVariable("variable", "gotoB")
              .start();
          task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("B");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
  
          processInstance = runtimeService
              .createProcessInstanceBuilder()
              .processDefinitionKey("transientVarsTest")
              .transientVariable("variable", "somethingElse")
              .start();
          task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("Default");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
      }
  
      @Deployment
      public void testStartProcessInstanceById() {
          String processDefinitionId = repositoryService.createProcessDefinitionQuery().singleResult().getId();
  
          ProcessInstance processInstance = runtimeService
              .createProcessInstanceBuilder()
              .processDefinitionId(processDefinitionId)
              .transientVariable("variable", "gotoA")
              .start();
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("A");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
  
          processInstance = runtimeService
              .createProcessInstanceBuilder()
              .processDefinitionId(processDefinitionId)
              .transientVariable("variable", "gotoB")
              .start();
          task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("B");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
  
          processInstance = runtimeService
              .createProcessInstanceBuilder()
              .processDefinitionId(processDefinitionId)
              .transientVariable("variable", "somethingElse")
              .start();
          task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("Default");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
      }
  
      @Deployment
      public void testStartProcessInstanceByMessage() {
          ProcessInstance processInstance = runtimeService
              .createProcessInstanceBuilder()
              .messageName("myMessage")
              .transientVariable("variable", "gotoA")
              .start();
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("A");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
  
          processInstance = runtimeService
              .createProcessInstanceBuilder()
              .messageName("myMessage")
              .transientVariable("variable", "gotoB")
              .start();
          task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("B");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
  
          processInstance = runtimeService
              .createProcessInstanceBuilder()
              .messageName("myMessage")
              .transientVariable("variable", "somethingElse")
              .start();
          task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task.getName()).isEqualTo("Default");
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(0);
      }
  
      /* Service task class for previous tests */
  
      /**
       * Mimics a service task that fetches data from a server and stored the whole thing
       * in a transient variable.
       */
      public static class FetchDataServiceTask implements JavaDelegate {
  
          public void execute(DelegateExecution execution) {
              execution.setTransientVariable("response", "author=kermit;version=3;message=Hello World");
              execution.setTransientVariable("status", Integer.valueOf(200));
          }
      }
  
      /**
       * Processes the transient variable and puts the relevant bits in real variables
       */
      public static class ServiceTask02 implements JavaDelegate {
  
          public void execute(DelegateExecution execution) {
              String response = (String) execution.getTransientVariable("response");
              for (String s : response.split(";")) {
                  String[] data = s.split("=");
                  if (data[0].equals("message")) {
                      execution.setVariable("message", data[1] + "!");
                  }
              }
          }
      }
  
      public static class CombineVariablesExecutionListener implements ExecutionListener {
  
          public void notify(DelegateExecution execution) {
              String persistentVar1 = (String) execution.getVariable("persistentVar1");
  
              Object unusedTransientVar = execution.getVariable("unusedTransientVar");
              if (unusedTransientVar != null) {
                  throw new RuntimeException("Unused transient var should have been deleted");
              }
  
              String secondTransientVar = (String) execution.getVariable("secondTransientVar");
              Number thirdTransientVar = (Number) execution.getTransientVariable("thirdTransientVar");
  
              String combinedVar = persistentVar1 + secondTransientVar + thirdTransientVar.intValue();
              execution.setVariable("combinedVar", combinedVar);
          }
      }
  
      public static class GetDataDelegate implements JavaDelegate {
  
          private Expression variableName;
  
          public void execute(DelegateExecution execution) {
              String var = (String) variableName.getValue(execution);
              execution.setTransientVariable(var, "author=kermit;version=3;message=" + var);
          }
      }
  
      public static class ProcessDataDelegate implements JavaDelegate {
  
          private Expression dataVariableName;
          private Expression resultVariableName;
  
          public void execute(DelegateExecution execution) {
              String varName = (String) dataVariableName.getValue(execution);
              String resultVar = (String) resultVariableName.getValue(execution);
  
              // Sets the name of the variable as 'resultVar'
              for (String s : ((String) execution.getVariable(varName)).split(";")) {
                  String[] data = s.split("=");
                  if (data[0].equals("message")) {
                      execution.setTransientVariable(resultVar, data[1]);
                  }
              }
          }
      }
  
      public static class MergeTransientVariablesTaskListener implements TaskListener {
  
          public void notify(DelegateTask delegateTask) {
              Map<String, Object> transientVariables = delegateTask.getTransientVariables();
              List<String> variableNames = new ArrayList(transientVariables.keySet());
              Collections.sort(variableNames);
  
              StringBuilder strb = new StringBuilder();
              for (String variableName : variableNames) {
                  if (variableName.startsWith("transientResult")) {
                      String result = (String) transientVariables.get(variableName);
                      strb.append(result);
                  }
              }
  
              delegateTask.setVariable("mergedResult", strb.toString());
          }
      }
  
      public static class MergeVariableValues implements JavaDelegate {
  
          public void execute(DelegateExecution execution) {
              Map<String, Object> vars = execution.getVariables();
              List<String> varNames = new ArrayList<String>(vars.keySet());
              Collections.sort(varNames);
  
              StringBuilder strb = new StringBuilder();
              for (String varName : varNames) {
                  strb.append(vars.get(varName));
              }
  
              execution.setVariable("result", strb.toString());
          }
      }
  }
  