{
  "version": "1.8.2",
  "timeZone": "Europe/Berlin",
  "title": "Example of a cancellable subprocess with live output logging.",
  "orderPreparation": {
    "parameters": {
      "executable_path": {
        "type": "String",
        "message": "/usr/bin/python3"
      },
      "script_path": {
        "type": "String",
        "default": "\"empty\"",
        "message": "/home/user/script.py"
      },
      "arguments": {
        "type": "String",
        "default": "\"empty\""
      },
      "script_arguments": {
        "type": "String",
        "default": "\"empty\""
      }
    },
    "allowUndeclared": false
  },
  "instructions": [
    {
      "TYPE": "Execute.Named",
      "jobName": "cancellable_subprocess",
      "label": "cancellable_subprocess"
    }
  ],
  "jobs": {
    "cancellable_subprocess": {
      "agentName": "testAgent",
      "withSubagentClusterIdExpr": false,
      "executable": {
        "TYPE": "InternalExecutable",
        "internalType": "Python_Graal",
        "script": "import os, shlex\n\n##!include Python-ManagedProcessClass\nclass JS7Job(js7.Job):\n    # Splits an argument string into a list, using Windows or POSIX shell rules depending on the platform\n    @staticmethod\n    def _split_args(arg_string):\n        if os.name == \"nt\":\n            return shlex.split(arg_string, posix=False)\n        return shlex.split(arg_string, posix=True)\n          \n    def processOrder(self, js7Step):\n        # Reads the workflow arguments\n        executable_path = js7Step.getAllArgumentsAsNameValueMap().get(\"executable_path\") # e. g. '/usr/bin/python3', '/bin/sh'\n        script_path = js7Step.getAllArgumentsAsNameValueMap().get(\"script_path\") # e. g. '/home/user/script.py', '/script.sh'\n        arguments = js7Step.getAllArgumentsAsNameValueMap().get(\"arguments\") # e. g. 'my_arg_1 my_arg_2'\n        script_arguments = js7Step.getAllArgumentsAsNameValueMap().get(\"script_arguments\") # e. g. 'my_arg_1 my_arg_2'\n        \n        # Creates the base command\n        if executable_path:\n            cmd = [executable_path]\n        else:\n            raise ValueError(\"No executable_path specified.\")\n            \n        # Adds arguments that will be passed to the executable\n        if arguments and arguments != \"empty\":\n            cmd += self._split_args(arguments)\n\n        if script_path and script_path != \"empty\":\n            cmd.append(script_path)\n            # Adds arguments that will be passed to the script\n            if script_arguments and script_arguments != \"empty\":\n                cmd += self._split_args(script_arguments)\n\n        # Executes the process\n        returncode = ManagedProcess.run(js7Step, cmd)\n        js7Step.getLogger().info(f\"[ManagedProcess]returncode={returncode}\")\n        js7Step.getOutcome().setReturnCode(returncode)\n\n    # Uses the onProcessOrderCanceled() hook after the order has been canceled\n    def onProcessOrderCanceled(self, js7Step):\n        ManagedProcess.cancel(js7Step)"
      },
      "skipIfNoAdmissionForOrderDay": false,
      "killAtEndOfAdmissionPeriod": false,
      "parallelism": 1,
      "graceTimeout": 1,
      "failOnErrWritten": false,
      "warnOnErrWritten": false,
      "title": "Example of a cancellable subprocess with live output logging.",
      "isNotRestartable": false
    }
  }
}