ExecutorService - Unable to stop Thread from ServletContextListener when the context is destroyed ExecutorService - Unable to stop Thread from ServletContextListener when the context is destroyed multithreading multithreading

ExecutorService - Unable to stop Thread from ServletContextListener when the context is destroyed


You should call process.destroy() on instance of Process returned by processBuilder.start(). Actually what you do when calling BlinkLedTask.terminate() is just setting some flag. You should at this point call process.destroy().

Below I present an example how you can rewrite this.It does not involve your class ScriptExecutor (of course you can move your logic there and return instance of process to BlinkLedTask when calling blinkLed()).

The main difference here is that I'm keeping the reference to instance of Process in field blinkLedProcess and when terminate() is called I'm directly calling process.destroy() to destroy the process.

You wrote that "When the server get shutdown the stop() is working as expected. I am using Jetty." Yes indeed. This is because by calling processBuilder.start(); you create subprocess of your main jetty process. When you kill jetty all its subproceses are also killed. If you don't kill jetty you need to manually kill the subprocess by calling destroy() method.

It should be something like:

public enum BlinkLedTask {(...)    private Process resetLedProcess;    private Process blinkLedProcess;(...)   private void blinkLed() throws Exception {      String[] args = new String[] { frequency[0], frequency[1], frequency[2] };      List<String> commands = new ArrayList<>();      //commands.add(BASH);      commands.add(script.getAbsoultePath());      if (Objects.nonNull(args)) {        commands.addAll(Arrays.asList(args));      }      StringBuilder builder = new StringBuilder("Executing script: ");      builder.append(script.getAbsoultePath());      if (Objects.nonNull(args) && (args.length > 0)) {        builder.append(" with parameters: ");        builder.append(StringUtils.join(args, " "));      }      ProcessBuilder processBuilder = new ProcessBuilder(commands.toArray(new String[commands.size()]));      blinkLedProcess = processBuilder.start();      StringBuilder outputBuilder = new StringBuilder();      InputStream inputStream = blinkLedProcess.getInputStream();      InputStreamReader inputStreamReader = new InputStreamReader(inputStream);      BufferedReader bufferedReader = new BufferedReader(inputStreamReader);      String line = StringUtils.EMPTY;      while ((line = bufferedReader.readLine()) != null) {        outputBuilder.append(line);        outputBuilder.append("\n");      }      blinkLedProcess.waitFor();      int exitValue = blinkLedProcess.exitValue();      System.out.println(        "Process for: " + Script.BLINK_LED.getAbsoultePath() + " is executed. Exit value: " + exitValue);    }(...)   private void terminate() {      System.out.println("Stopping - " + Thread.currentThread().getName());      running = false;      if (resetLedProcess != null) {        resetLedProcess.destroy();        System.out.println("Destroyed reset process");      }      if (blinkLedProcess != null) {        blinkLedProcess.destroy();        System.out.println("Destroyed blink process");      }    }(...)}


First of all, you should use awaitTermination to wait after calling shutdownNow.

shutdownNow will interrupt your thread. Are you sure ScriptExecutordoes not surpress interrupts?

This might actually be the cause of this.

Also doing this with SchedulerService seems redundant since you are only using one thread.

You could start a new thread which you set to a daemon thread (See What is Daemon thread in Java?) and it will close automatically when your program exits.


What exactly are you trying to achieve? If its a single thread that runs during the lifetime of your web app then I would just write your own context listener with a thread like this...

public class MyContextListener implements ServletContextListener {  Thread myThread;  Task myTask;  @Override  public void contextInitialized(ServletContextEvent sce) {    myTask = new Task();    myThread = new Thread(myTask);    myThread.start();  }  @Override  public void contextDestroyed(ServletContextEvent sce) {    myTask.terminate();    myThread.interrupt();    myThread.join();  }}

I'm wondering exactly what it is you want to achieve. Do you only want a single thread running a series of scripts? Or are you hoping, at some point in the future, to have a multi-threaded application?

Any reason for the servlet context? Could you just run this is a straight forward java application?