class TaskManager {
    private LinkedHashMap<String, Task> allTasks;
    private LinkedBlockingDeque<Task> queue;
    private boolean shouldStop;
    private boolean shouldPause;
    private Object lock;
    private ExecutorService executor;

    public TaskManager() {
      allTasks = new LinkedHashMap<>();
      queue = new LinkedBlockingDeque<>();
      shouldStop = false;
      shouldPause = false;
      executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "Task Executor"));
      lock = new Object();
      executor.execute(() -> {
        while (!shouldStop) {
          Task task = null;
          try {
            synchronized(lock) {
              while (shouldPause) {
                lock.wait();
              }
            }
            task = queue.take();
            if ("shouldStop".equals(task.id) || "shouldPause".equals(task.id)) {
              // NOOP
            } else {
              synchronized(task) {
                if (task.isWaiting()) {
                  task.updateStatus(Task.Status.RUNNING);
                }
              }
              if (task.isRunning()) {
                  task.runScript();
              }
            }
          } catch (InterruptedException ex) {
            // NOOP
          } catch (Exception ex) {
            SikulixServer.dolog(-1, "ScriptExecutor: Exception: %s", ex);
            ex.printStackTrace();
            if (task != null) {
              task.updateStatus(Task.Status.FAILED);
            }
          } finally {
            if (task != null) {
              synchronized(task) {
                task.notify();
              }
            }
          }
        }
      });
    }

    public Map<String, Task> getTasks(Optional<String> groupName, Optional<String> scriptName) {
      LinkedHashMap<String, Task> result = allTasks.entrySet().stream()
          .filter(e -> {
            if (groupName.isPresent()) {
              if (scriptName.isPresent()) {
                return groupName.get().equals(e.getValue().groupName) && scriptName.get().equals(e.getValue().scriptName);
              } else {
                return groupName.get().equals(e.getValue().groupName);
              }
            } else {
              if (scriptName.isPresent()) {
                return DEFAULT_GROUP.equals(e.getValue().groupName) && scriptName.get().equals(e.getValue().scriptName);
              } else {
                return true;
              }
            }
          })
          .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (oldValue, newValue) -> newValue, LinkedHashMap::new));
      return Collections.unmodifiableMap(result);
    }

    public Task requestSync(final String id, final String groupName, final String scriptName, final String[] scriptArgs) throws Exception {
      return request(id, groupName, scriptName, scriptArgs, false);
    }

    public Task requestAsync(final String id, final String groupName, final String scriptName, final String[] scriptArgs) throws Exception {
      return request(id, groupName, scriptName, scriptArgs, true);
    }

    private Task request(final String id, final String groupName, final String scriptName, 
        final String[] scriptArgs, boolean isAsync) throws Exception {<FILL_FUNCTION_BODY>}

    public boolean cancel(final String id) {
      Task task = allTasks.get(id);
      if (task != null) {
        synchronized (task) {
          if (task.isWaiting()) {
            task.updateStatus(Task.Status.CANCELED);
            task.notify();
            return true;
          } else {
            SikulixServer.dolog(-1, "could not cancel the task: %s", id);
            return false;
          }
        }
      } else {
        SikulixServer.dolog(-1, "the task is not found: %s", id);
        return false;
      }
    }

    public void stop() {
      shouldStop = true;
      queue.addFirst(new Task("shouldStop", null, null, null, true));
      executor.shutdown();
      while(!executor.isTerminated()) {
        try {
          executor.awaitTermination(60, TimeUnit.SECONDS);
        } catch (InterruptedException ex) {
          ex.printStackTrace();
        }
      }
    }

    public boolean pause() {
      synchronized(lock) {
        if (shouldPause) {
          return false;
        } else {
          shouldPause = true;
          queue.addFirst(new Task("shouldPause", null, null, null, true));
          return true;
        }
      }
    }

    public boolean resume() {
      synchronized(lock) {
        if (shouldPause) {
          shouldPause = false;
          lock.notify();
          return true;
        } else {
          return false;
        }
      }
    }

    public boolean isPaused() {
      return shouldPause;
    }
  }

      Task request = new Task(id, groupName, scriptName, scriptArgs, isAsync);
      synchronized(allTasks) {
        allTasks.put(request.id, request);
        queue.put(request);
      }
      if (!isAsync) {
        synchronized(request) {
          while(request.isWaiting() || request.isRunning()) {
            request.wait();
          }
        }
      }
      return request.clone();
    
<no_super_class>