@Override
    public synchronized void handleEvent(Event event) {
      JobRunner runner = (JobRunner) event.getRunner();

      if (event.getType() == Type.JOB_STATUS_CHANGED) {
        updateFlow();
      } else if (event.getType() == Type.JOB_FINISHED) {
        ExecutableNode node = runner.getNode();
        long seconds = (node.getEndTime() - node.getStartTime()) / 1000;
        synchronized (mainSyncObj) {
          logger.info(
              "Job "
                  + node.getNestedId()
                  + " finished with status "
                  + node.getStatus()
                  + " in "
                  + seconds
                  + " seconds");

          // Cancellation is handled in the main thread, but if the flow is
          // paused, the main thread is paused too.
          // This unpauses the flow for cancellation.
          if (flowPaused
              && node.getStatus() == Status.FAILED
              && failureAction == FailureAction.CANCEL_ALL) {
            flowPaused = false;
          }

          finishedNodes.add(node);
          node.getParentFlow().setUpdateTime(System.currentTimeMillis());
          interrupt();
          fireEventListeners(event);
        }
      }
    }
  public void run() {
    try {
      if (this.executorService == null) {
        this.executorService = Executors.newFixedThreadPool(numJobThreads);
      }
      setupFlowExecution();
      flow.setStartTime(System.currentTimeMillis());

      updateFlowReference();

      logger.info("Updating initial flow directory.");
      updateFlow();
      logger.info("Fetching job and shared properties.");
      loadAllProperties();

      this.fireEventListeners(Event.create(this, Type.FLOW_STARTED));
      runFlow();
    } catch (Throwable t) {
      if (logger != null) {
        logger.error("An error has occurred during the running of the flow. Quiting.", t);
      }
      flow.setStatus(Status.FAILED);
    } finally {
      if (watcher != null) {
        logger.info("Watcher is attached. Stopping watcher.");
        watcher.stopWatcher();
        logger.info("Watcher cancelled status is " + watcher.isWatchCancelled());
      }

      flow.setEndTime(System.currentTimeMillis());
      logger.info("Setting end time for flow " + execId + " to " + System.currentTimeMillis());
      closeLogger();

      updateFlow();
      this.fireEventListeners(Event.create(this, Type.FLOW_FINISHED));
    }
  }
 private void finishExecutableNode(ExecutableNode node) {
   finishedNodes.add(node);
   fireEventListeners(Event.create(this, Type.JOB_FINISHED, node));
 }