Search code examples
javascheduled-taskstaskexecutorservicewatchservice

Can't stop a ExecutorService


I try to end a ExecutorService with executorService.shutdown(); and if that didn't worked, with executorService.shutdown();. The Problem is, that the executorService can't be stopped and the program is still running.

Here is my class which shall start a WatchService to watch a directory for changes:

public class FileWatcher {
    /**
     * This Class starts a WatchService to get changes on a specific folder.
     */

    Path dir;

    private final ConfigManager configManager;

    private final FileHandler fileHandler;

    private ExecutorService executorService;

    public FileWatcher(ConfigManager configManager, FileHandler fileHandler) {
        this.configManager = configManager;
        this.fileHandler = fileHandler;
    }

    public void start() {
        dir = configManager.getConfig().getJdfPath();

        //executorService = Executors.newFixedThreadPool(1);
        executorService = Executors.newSingleThreadExecutor();
        Runnable runWatcher;
        runWatcher = new Runnable() {
            @Override
            public void run() {
                try {
                    startWatcher();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        executorService.execute(runWatcher);
    }

    private void startWatcher() throws IOException, InterruptedException {
        /**
         * Create a new WatchService which detects created and modified files. To
         * let it detect deleted files add ENTRY_DELETE to dir.register().
         */

        WatchService watcher = FileSystems.getDefault().newWatchService();
        WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY);

        while (!Thread.currentThread().isInterrupted()) {
            key = waitForEvents(watcher, key);

        }
    }

    private WatchKey waitForEvents(WatchService watcher, WatchKey key) {

        /**
         * The WatchService tells the FileHandler the Filename of the changed or
         * new file in the given folder.
         */

        try {
            key = watcher.take();
        } catch (InterruptedException e) {
            executorService.shutdown();

        } catch (ClosedWatchServiceException e) {
            executorService.shutdown();

        }

        for (WatchEvent<?> event : key.pollEvents()) {

            fileHandler.setPath((Path) event.context());
        }
        key.reset();
        return key;
    }

    public void stop() {
        stopWatcher();
    }

    private void stopWatcher() {
        executorService.shutdown(); // Disable new tasks from being submitted
        try {
            // Wait a while for existing tasks to terminate
            if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
                executorService.shutdownNow(); // Cancel currently executing tasks
                // Wait a while for tasks to respond to being cancelled
                if (!executorService.awaitTermination(10, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (InterruptedException ie) {
            // (Re-)Cancel if current thread also interrupted
            executorService.shutdownNow();
            // Preserve interrupt status
            Thread.currentThread().interrupt();
        }
    }
} 

I got the method stopWatcher() from the oracle java api]1. I wrote a simple class to test the FileWatcher class.

public class TestFileWatcher {
    static Path path;
    static ConfigManager configmanager;

    public static void main(String[] args) {

        path = Paths.get("D:/testfolder");
        FileHandler filehandler = new FileHandler() {

            @Override
            public void setPath(Path path) {
                System.out.println("file: " + path);

            }

            @Override
            public Path getPath() {
                // TODO Auto-generated method stub
                return null;
            }
        };

        final Config config;
        config = new Config() {

            @Override
            public Path getJdfPath() {
                // TODO Auto-generated method stub
                return path;
            }
        };

        configmanager = new ConfigManager() {

            @Override
            public void injectConfig(Config config) {
                // TODO Auto-generated method stub

            }

            @Override
            public Config getConfig() {
                // TODO Auto-generated method stub
                return config;
            }
        };



        configmanager.injectConfig(config);
        filehandler.setPath(path);
        final FileWatcher filewatcher = new FileWatcher(configmanager, filehandler);
        filewatcher.start();
        Timer timer = new Timer();


        boolean stopped;

        TimerTask closeWatcherTask = new TimerTask(){
            @Override
            public void run() {

                System.out.println("filewatcher stopped.");
                filewatcher.stop();

            }
        };
        long duration = 2000;
        timer.schedule(closeWatcherTask, duration);
        return;

    }

}

So, when I start the Test Application to start the FileWatcher and end it 2 seconds after that, the program tells me: Pool did not terminate. What can I change, to terminate it correctly?

By the way, the ConfigManager and FileHandler are interfaces, where I get the Path which I have to whatch for changes.


Solution

  • shutDown() will only tell the executor to not accept any new tasks & cancels all tasks that are not runnuing yet. Tasks that are running are allowed to finish.

    You are looping indefinitely in

        while (!Thread.currentThread().isInterrupted()) {
            key = waitForEvents(watcher, key);
        }
    

    because an InterruptedException is not setting the interrupted flag.

    If you change

        try {
            key = watcher.take();
        } catch (InterruptedException e) {
            executorService.shutdown();
        } catch (ClosedWatchServiceException e) {
            executorService.shutdown();
        }
    

    To

        try {
            key = watcher.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (ClosedWatchServiceException e) {
            Thread.currentThread().interrupt();
        }
    

    and add executorService.shutdown(); after the while loop (or keep it where it is) you should have no problem.