I use gradle
and application
plugin to run the application that watches the changes in the directory.
My main class looks like this
public static void main(String[] args) throws IOException {
WatcherThread thread = new WatcherThread(EXTENSION_FOLDER);
thread.start();
try(BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
String input = null;
ConsoleInputController controller = new ConsoleInputController(br);
while (!QUIT_COMMAND.equals(StringUtils.trim(input))) {
System.out.println(CONSOLE_TEMPLATE);
System.out.println("input (to exit write [quit]):> ");
input = br.readLine();
controller.handleInput(input);
}
} catch (IOException exc) {
LOGGER.error("Failed to process input.", exc);
}
thread.stopThread();
}
WatcherThread
is a thread class that uses WatcherService
(some wrapper over WatchService
of java)
public class WatcherThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(WatcherThread.class);
private boolean watch = true;
private WatcherService watcherService;
public WatcherThread(String searchingPath) throws IOException {
watcherService = new WatcherService(Paths.get(searchingPath));
}
@Override
public void run() {
LOGGER.info("Artifact watching thread started.");
while(watch) {
if (!watcherService.watch()) {
break;
}
}
LOGGER.info("Artifact watching thread stopped.");
}
public void stopThread() {
watch = false;
}
}
WatcherService
looks like this
public class WatcherService {
private static final Logger LOGGER = LoggerFactory.getLogger(WatcherThread.class);
private final WatchService watcher;
private final Map<WatchKey, Path> keys;
private boolean trace;
WatcherService(Path dir) throws IOException {
watcher = FileSystems.getDefault().newWatchService();
keys = new HashMap<>();
register(dir);
trace = true;
}
private void register(Path dir) throws IOException {
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (trace) {
Path prev = keys.get(key);
if (null == prev) {
LOGGER.info("Register path: [{}].", dir);
} else {
if (!dir.equals(prev)) {
LOGGER.info("Updated path: [{}] -> [{}].", prev, dir);
}
}
}
keys.put(key, dir);
}
boolean watch() {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException exc) {
return false;
}
Path dir = keys.get(key);
if (null == dir) {
LOGGER.warn("WatchKey is not recognized!");
return false;
}
// forEach?
for (WatchEvent event: key.pollEvents()) {
LOGGER.info("Polling events");
WatchEvent.Kind kind = event.kind();
if (OVERFLOW == kind) {
continue;
}
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path name = ev.context();
Path child = dir.resolve(name);
LOGGER.info("Event occurred [{}] in [{}].", event.kind().name(), child);
WatchEventResolver.resolveEvent(ev, child);
}
boolean valid = key.reset();
if (!valid) {
keys.remove(key);
if (keys.isEmpty()) {
return false;
}
}
return true;
}
}
When I do not start
my WatcherThread
- console input works fine. I can quit
for example without problems. But when I run thread and want to quit, it is waiting for couple of seconds and then only ends.
As far as I understand it is something with WatchService
that cannot stop watching the directory.
How to stop it on quit
application immediately?
It looks like you need an extra method in your WatcherService
class, that calls to watcher.close()
. Then, in your WatcherThread
class you can call to that method inside stopThread()
.
In the Javadoc for WatchService
you can see that take()
keeps waiting. You can force it to finish by closing it.