@NotNull
  @Override
  protected OSProcessHandler startProcess() throws ExecutionException {

    if (processHandler != null && !processHandler.isProcessTerminated()) {
      log.debug("existing running process found");

      processHandler.addProcessListener(
          new ProcessListener() {
            @Override
            public void startNotified(ProcessEvent event) {}

            @Override
            public void processTerminated(ProcessEvent event) {
              log.debug("existing process terminated");
            }

            @Override
            public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {}

            @Override
            public void onTextAvailable(ProcessEvent event, Key outputType) {}
          });

      processHandler.destroyProcess();
    }

    processHandler =
        JavaCommandLineStateUtil.startProcess(createCommandLine(), true); // ansiColoringEnabled());

    return processHandler;
  }
 public void destroyProcess() {
   synchronized (myLock) {
     if (!myIsDestroyed) {
       myIsDestroyed = true;
       myHandler.destroyProcess();
     }
   }
 }
  private void shutdownProcess() {
    final OSProcessHandler processHandler = myProcessHandler;
    if (processHandler != null) {
      if (!processHandler.isProcessTerminated()) {
        boolean forceQuite = true;
        try {
          writeLine(EXIT_COMMAND);
          forceQuite = !processHandler.waitFor(500);
          if (forceQuite) {
            LOG.warn("File watcher is still alive. Doing a force quit.");
          }
        } catch (IOException ignore) {
        }
        if (forceQuite) {
          processHandler.destroyProcess();
        }
      }

      myProcessHandler = null;
    }
  }
  @Override
  @NotNull
  public ExecutionResult execute(
      @NotNull final Executor executor, @NotNull final ProgramRunner runner)
      throws ExecutionException {

    log.debug("execute: about to call startProcess");

    OSProcessHandler processHandler = startProcess();

    ConsoleView consoleView =
        TextConsoleBuilderFactory.getInstance()
            .createBuilder(this.getEnvironment().getProject())
            .getConsole();

    consoleView.print("Starting substeps test...", ConsoleViewContentType.NORMAL_OUTPUT);

    SubstepsRunConfiguration runConfig =
        (SubstepsRunConfiguration) this.getEnvironment().getRunProfile();

    boolean substepsServerLogsToConsole = true;
    if (substepsServerLogsToConsole) {
      consoleView.attachToProcess(processHandler);
    } else {

      Semaphore consoleSemaphore = new Semaphore(1, true);
      InputStreamConsumer consumer =
          new InputStreamConsumer(
              processHandler.getProcess().getInputStream(),
              log,
              false,
              consoleView,
              consoleSemaphore);
      final Thread t = new Thread(consumer);
      t.start();

      InputStreamConsumer errorConsumer =
          new InputStreamConsumer(
              processHandler.getProcess().getErrorStream(),
              log,
              true,
              consoleView,
              consoleSemaphore);
      final Thread t2 = new Thread(errorConsumer);
      t2.start();
    }
    processHandler.startNotify();
    //
    //
    //
    //        boolean exceptionThrown = false;
    //        try {
    //            this.log.info("waiting for process to start...");
    //            processStarted.await(30, TimeUnit.SECONDS);
    //
    //            this.log.info("waited..");
    //
    //            if (!processStartedOk.get()) {
    //                exceptionThrown = true;
    //                throw new ExecutionException("Unable to launch VM process");
    //            }
    //
    //            this.log.info("process started");
    //        } catch (final InterruptedException e) {
    //
    //            e.printStackTrace();
    //        }

    //        try {
    //            Thread.currentThread().sleep(5000);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }

    log.debug("startProcess called");

    SubstepsRunnerConfigurationModel model = runConfig.getModel();

    boolean actualRunnerStarted = false;

    // TODO - this is a bit messy, concerns not well separated
    SubstepsJMXClient jmxClient = null;
    try {
      jmxClient = new SubstepsJMXClient();

      if (!jmxClient.init(jmxPort)) {
        log.error("jmx init failed");
        processHandler.destroyProcess();
        return new DefaultExecutionResult();
      }

      SubstepsExecutionConfig substepsExecutionConfig = new SubstepsExecutionConfig();

      substepsExecutionConfig.setFeatureFile(model.getPathToFeature());

      String[] stepImplsArray =
          model
              .getStepImplentationClassNames(); // .toArray(new
                                                // String[model.getStepImplentationClassNames().size()]);

      substepsExecutionConfig.setDescription("Substeps Tests");

      substepsExecutionConfig.setStepImplementationClassNames(stepImplsArray);

      substepsExecutionConfig.setSubStepsFileName(model.getSubStepDefinitionDirectory());

      substepsExecutionConfig.setScenarioName(model.getScenarioName());

      log.debug(
          "SubstepsExecutionConfig details\nFeature: "
              + model.getPathToFeature()
              + "\nsubstep dir: "
              + model.getSubStepDefinitionDirectory()
              + " scenarioName: "
              + model.getScenarioName());

      for (String s : model.getStepImplentationClassNames()) {
        log.debug("step impl classname: " + s);
      }
      /*
      private String description;
      private String tags;
      private String nonFatalTags;
      private String subStepsFileName;
      private boolean strict = true;
      private boolean fastFailParseErrors = true;
      private Properties systemProperties;
      private String[] nonStrictKeywordPrecedence;
      private String[] stepImplementationClassNames;
      private String[] initialisationClass;
      private List<Class<?>> stepImplementationClasses;
      private Class<?>[] initialisationClasses;
      private String[] executionListeners;
           */

      log.debug("preparing config");

      byte[] bytes = jmxClient.prepareExecutionConfigAsBytes(substepsExecutionConfig);

      RootNode rn = getRootNodeFromBytes(bytes);

      log.debug("got root node description: " + rn.getDescription());

      final SubstepsTestProxy unboundOutputRoot = new SubstepsTestProxy(rn);

      final SubstepsConsoleProperties consoleProperties =
          new SubstepsConsoleProperties(runConfig, executor);
      final SubstepsConsoleView substepsConsoleView =
          new SubstepsConsoleView(
              consoleView, consoleProperties, this.getEnvironment(), unboundOutputRoot);

      DefaultExecutionResult execResult =
          new DefaultExecutionResult(
              substepsConsoleView,
              processHandler,
              createActions(substepsConsoleView, processHandler, executor));

      Disposer.register(this.getEnvironment().getProject(), substepsConsoleView);
      substepsConsoleView.initUI();
      substepsConsoleView.attachToProcess(processHandler);
      unboundOutputRoot.setPrinter(substepsConsoleView.getPrinter());
      Disposer.register(substepsConsoleView, unboundOutputRoot);

      SubstepsRunningModel runModel =
          new SubstepsRunningModel(unboundOutputRoot, consoleProperties);

      SubstepsListenersNotifier eventsConsumer = unboundOutputRoot.getEventsConsumer();

      substepsConsoleView.attachToModel(runModel);

      RunningTestTracker.install(runModel);

      log.debug("rootNode result from prepare config: " + rn.getResult().getResult());

      if (rn.getResult().getResult().isFailure()) {

        // bail out early
        unboundOutputRoot.setState(SubstepTestState.FAILED);
        eventsConsumer.onEvent(new StateChangedEvent(unboundOutputRoot));

        jmxClient.shutdown();
        log.debug("shut down done!");

      } else {
        log.debug("config prepared");

        List<SubstepsTestProxy> allTestNodes = unboundOutputRoot.getAllTests();
        Map<Long, SubstepsTestProxy> proxyMap = new HashMap<>();
        for (SubstepsTestProxy proxy : allTestNodes) {
          proxyMap.put(proxy.getExecutionNodeId(), proxy);
        }

        ActualRunner actualRunner =
            new ActualRunner(jmxClient, log, eventsConsumer, proxyMap, unboundOutputRoot);

        new Thread(actualRunner).start();
        actualRunnerStarted = true;
      }

      return execResult;
    } finally {
      if (!actualRunnerStarted && jmxClient != null) {

        // if we've got to the end and not actually kicked off the runner, make sure we shut down.

        jmxClient.shutdown();
      }
    }
  }