Restart WatchService after exceptions - java

I'm using Java's WatchService API within my Spring Boot application to monitor a directory, and perform some actions on created files. This process is executed asynchronously: it starts automatically right after the application is ready and monitors the directory in the background until the application is stopped.
This is the configuration class:
#Configuration
public class DirectoryWatcherConfig {
#Value("${path}")
private String path;
#Bean
public WatchService watchService() throws IOException {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path directoryPath = Paths.get(path);
directoryPath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
return watchService;
}
}
And this is the monitoring service:
#Service
#RequiredArgsConstructor
public class DirectoryWatcherService {
private final WatchService watchService;
#Async
#EventListener(ApplicationReadyEvent.class)
public void startWatching() throws InterruptedException {
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
// actions on created files
}
key.reset();
}
}
}
This code is working as expected, with the following exception, which I'd like to fix:
Any failure during the execution makes the monitoring to stop (obviously), and I don't know how to restart the monitoring after such events occur

You should change the loop with and add a try/Catch, in the catch restarting the service. As you commented you need to keep in even if is interrupted , so you will need the use of ExecutorService. Declare the Executor out of the method
#Autowired
private ExecutorService executor;
and inside the method something similar to my last answer but using the executor
Runnable task = () -> {
while (true) {
try {
WatchKey key = watchService.take();
if (key != null) {
for (WatchEvent<?> event : key.pollEvents()) {
// Perform actions on created files here
}
key.reset();
}
} catch (Exception e) {
// Wait for some time before starting the thread again
Thread.sleep(5000);
}
}
};
//submit the task to the executor
executor.submit(task);

Actually the solution was quite simple. Wrapping the desired actions with try/catch (catching desired exceptions) in DirectoryWatcherService like this allows the thread to continue monitoring directories:
#Service
#RequiredArgsConstructor
public class DirectoryWatcherService {
private final WatchService watchService;
#Async
#EventListener(ApplicationReadyEvent.class)
public void startWatching() throws InterruptedException {
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
try {
// actions on created files
} catch (RuntimeException ex) {
// log exception or whatever you choose, as long as execution continues
}
}
key.reset();
}
}
}

Related

Watch Service Java - Two parallel threads in background

I need to monitor changes( uploaded files ) in two directories in a web application. I created a ServletContextListener that triggers the monitoring of these two directories.
My problem is that when the first monitoring starts, the thread is blocked and the second monitoring does not start.
Is possible to keep the monitoring of two different folders running in parallel and background?
I know that the problem is due to an infinite loop , but do not know how to put this thread in the background. Any help will be appreciated . Thank you very much in advance
ContextListener
#Override
public void contextInitialized(ServletContextEvent event) {
Path pathFolder1 = Paths.get("my_folder_1_path");
MyWatcher watcher1 = new MyWatcher();
Path pathFolder2 = Paths.get("my_folder_2_path");
MyWatcher watcher2 = new MyWatcher();
watcher1.startMonitoring(pathFolder1);
watcher2.startMonitoring(pathFolder2);
}
MyWatcher
public void startMonitoring(Path directory) {
try {
FileSystem fs = directory.getFileSystem ();
WatchService watcher = fs.newWatchService();
while(true) {
directory.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE);
WatchKey watckKey = watcher.take();
List<WatchEvent<?>> events = watckKey.pollEvents();
for (WatchEvent event : events) {
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("File created: " + event.context().toString());
}
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("File removed: " + event.context().toString());
}
}
watckKey.reset();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
You should configure a thread factory on the app server, inject it through a #Resource annotation, and use threads from there. An googled example would be a blog entry about this.

Execute a continious task via ThreadPoolExecutor

Facing the problem with the ThreadPoolExecutor in Java.
How can I execute a continuous task using it? For example, I want to execute something like this:
#Async
void MyVoid(){
Globals.getInstance().increment();
System.out.println(Thread.currentThread().getName()+" iteration # "+ Globals.getInstance().Iterator);
}
I want it to run forever in 2 parallel asynchronous threads until the user sends a request to stop the ThreadPoolExecutor in the "/stop" controller.
If I use this for example:
#Controller
#RequestMapping("api/test")
public class SendController {
ThreadPoolExecutor executor = new ErrorReportingThreadPoolExecutor(5);
boolean IsRunning = true;
#RequestMapping(value = "/start_new", method = RequestMethod.POST)
public Callable<String> StartNewTask(#RequestBody LaunchSend sendobj) throws IOException, InterruptedException {
Runnable runnable = () -> { MyVoid();};
executor.setCorePoolSize(2);
executor.setMaximumPoolSize(2);
while (IsRunning) {
executor.execute(runnable);
System.out.println("Active threads: " + executor.getActiveCount());
}
return () -> "Callable result";
}
#RequestMapping(value = "/stop", method = RequestMethod.GET)
public Callable<String> StopTasks() {
executor.shutdown(); //for test
if(SecurityContextHolder.getContext().getAuthentication().getName() != null && SecurityContextHolder.getContext().getAuthentication().getName() != "anonymousUser") {
executor.shutdown();
return () -> "Callable result good";
}
else { return () -> "Callable result bad";}
}
}
public class ErrorReportingThreadPoolExecutor extends ThreadPoolExecutor {
public ErrorReportingThreadPoolExecutor(int nThreads) {
super(nThreads, nThreads,
0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
#Override
protected void afterExecute(Runnable task, Throwable thrown) {
super.afterExecute(task, thrown);
if (thrown != null) {
// an unexpected exception happened inside ThreadPoolExecutor
thrown.printStackTrace();
}
if (task instanceof Future<?>) {
// try getting result
// if an exception happened in the job, it'll be thrown here
try {
Object result = ((Future<?>)task).get();
} catch (CancellationException e) {
// the job get canceled (may happen at any state)
e.printStackTrace();
} catch (ExecutionException e) {
// some uncaught exception happened during execution
e.printStackTrace();
} catch (InterruptedException e) {
// current thread is interrupted
// ignore, just re-throw
Thread.currentThread().interrupt();
}
}
}
}
I'm getting the following errors:
As I understood, a lot of tasks got submitted into the 'executor' queue within a few seconds and then the executor handled all them. (But I need each thread to wait before the current task ends and then submit the new one to the executor, I think.)
HTTP Requests to these controllers are forever "IDLE" until the next request comes, i.e. after sending a request to /api/test/start_new the controller's code executed tasks that are running, but the request is IDLE.
How can I do this in Java?
P.S. Spring MVC is used in the project. It has its own implementation of ThreadPoolExecutor - ThreadPoolTaskExecutor, but I am facing similar problems with it.

How can I terminate Tasks that have timed out in multithreading?

I need to make a library in which I will have synchronous and asynchronous methods in it.
executeSynchronous() - waits until I have a result, returns the result.
executeAsynchronous() - returns a Future immediately which can be processed after other things are done, if needed.
Core Logic of my Library
The customer will use our library and they will call it by passing DataKey builder object. We will then construct a URL by using that DataKey object and make a HTTP client call to that URL by executing it and after we get the response back as a JSON String, we will send that JSON String back to our customer as it is by creating DataResponse object. Some customer will call executeSynchronous() and some might call executeAsynchronous() so that's why I need to provide two method separately in my library.
Interface:
public interface Client {
// for synchronous
public DataResponse executeSynchronous(DataKey key);
// for asynchronous
public Future<DataResponse> executeAsynchronous(DataKey key);
}
And then I have my DataClient which implements the above Client interface:
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
#Override
public DataResponse executeSynchronous(DataKey key) {
DataResponse dataResponse = null;
Future<DataResponse> future = null;
try {
future = executeAsynchronous(key);
dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key);
dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
// does this look right the way I am doing it?
future.cancel(true); // terminating tasks that have timed out.
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
#Override
public Future<DataResponse> executeAsynchronous(DataKey key) {
Future<DataResponse> future = null;
try {
Task task = new Task(key, restTemplate);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
Simple class which will perform the actual task:
public class Task implements Callable<DataResponse> {
private DataKey key;
private RestTemplate restTemplate;
public Task(DataKey key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
#Override
public DataResponse call() {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using key object
private String createURL() {
String url = somecode;
return url;
}
}
Problem Statement:-
When I started working on this solution, I was not terminating the tasks that have timed out. I was reporting the timeout to the client, but the task continues to run in the thread pool (potentially occupying one of my limited 10 threads for a long time). So I did some research online and I found that I can cancel my tasks those have timed out by using cancel on future as shown below -
future.cancel(true);
But I wanted to make sure, does it look right the way I am doing in my executeSynchronous method to cancel the tasks that have got timedout?
Since I am calling cancel() on theFuture which will stop it from running if tasks is still in the queue so I am not sure what I am doing is right or not? What is the right approach to do this?
If there is any better way, then can anyone provide an example for that?
If task is still in the queue then cancelling it by simply calling future.cancel() is ok but obviously you don't know if that is in the queue. Also even if you ask future to interrupt the task it may not work as your task can still be doing something which is ignoring the thread interrupted status.
So you can use the future.cancel(true) but you need to make sure that your task (thread) does regard the thread interrupted status. For example as you mentioned you make http call, so you might need to close the http client resource as soon as thread is interrupted.
Please refer to the example below.
I have tried to implement the task cancellation scenario. Normally a thread can check isInterrupted() and try to terminate itself. But this becomes more complex when you are using thread pool executors, callable and if the task is not really like while(!Thread.isInterrupted()) {// execute task}.
In this example, a task is writing a file (I did not use http call to keep the it simple). A thread pool executor starts running the task but the caller wants to cancel it just after 100 milli seconds. Now future sends the interrupt signal to the thread but the callable task can not check it immediately while writing to file. So to make this happen callable maintains a list of IO resources it is going to use and as soon as future wants to cancel the task it just calls cancel() on all IO resources which terminates the task with IOException and then thread finishes.
public class CancellableTaskTest {
public static void main(String[] args) throws Exception {
CancellableThreadPoolExecutor threadPoolExecutor = new CancellableThreadPoolExecutor(0, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
long startTime = System.currentTimeMillis();
Future<String> future = threadPoolExecutor.submit(new CancellableTask());
while (System.currentTimeMillis() - startTime < 100) {
Thread.sleep(10);
}
System.out.println("Trying to cancel task");
future.cancel(true);
}
}
class CancellableThreadPoolExecutor extends ThreadPoolExecutor {
public CancellableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
#Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new CancellableFutureTask<T>(callable);
}
}
class CancellableFutureTask<V> extends FutureTask<V> {
private WeakReference<CancellableTask> weakReference;
public CancellableFutureTask(Callable<V> callable) {
super(callable);
if (callable instanceof CancellableTask) {
this.weakReference = new WeakReference<CancellableTask>((CancellableTask) callable);
}
}
public boolean cancel(boolean mayInterruptIfRunning) {
boolean result = super.cancel(mayInterruptIfRunning);
if (weakReference != null) {
CancellableTask task = weakReference.get();
if (task != null) {
try {
task.cancel();
} catch (Exception e) {
e.printStackTrace();
result = false;
}
}
}
return result;
}
}
class CancellableTask implements Callable<String> {
private volatile boolean cancelled;
private final Object lock = new Object();
private LinkedList<Object> cancellableResources = new LinkedList<Object>();
#Override
public String call() throws Exception {
if (!cancelled) {
System.out.println("Task started");
// write file
File file = File.createTempFile("testfile", ".txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
synchronized (lock) {
cancellableResources.add(writer);
}
try {
long lineCount = 0;
while (lineCount++ < 100000000) {
writer.write("This is a test text at line: " + lineCount);
writer.newLine();
}
System.out.println("Task completed");
} catch (Exception e) {
e.printStackTrace();
} finally {
writer.close();
file.delete();
synchronized (lock) {
cancellableResources.clear();
}
}
}
return "done";
}
public void cancel() throws Exception {
cancelled = true;
Thread.sleep(1000);
boolean success = false;
synchronized (lock) {
for (Object cancellableResource : cancellableResources) {
if (cancellableResource instanceof Closeable) {
((Closeable) cancellableResource).close();
success = true;
}
}
}
System.out.println("Task " + (success ? "cancelled" : "could not be cancelled. It might have completed or not started at all"));
}
}
For your REST Http client related requirement you can modify the factory class something like this -
public class CancellableSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
private List<Object> cancellableResources;
public CancellableSimpleClientHttpRequestFactory() {
}
public CancellableSimpleClientHttpRequestFactory(List<Object> cancellableResources) {
this.cancellableResources = cancellableResources;
}
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
HttpURLConnection connection = super.openConnection(url, proxy);
if (cancellableResources != null) {
cancellableResources.add(connection);
}
return connection;
}
}
Here you need to use this factory while creating RestTemplate in your runnable task.
RestTemplate template = new RestTemplate(new CancellableSimpleClientHttpRequestFactory(this.cancellableResources));
Make sure that you pass the same list of cancellable resources that you have maintained in CancellableTask.
Now you need to modify the cancel() method in CancellableTask like this -
synchronized (lock) {
for (Object cancellableResource : cancellableResources) {
if (cancellableResource instanceof HttpURLConnection) {
((HttpURLConnection) cancellableResource).disconnect();
success = true;
}
}
}

Java WatchService watches not terminated

I'm working on an application that uses the Java watchservice (Java 8) under Linux Mint. One interesting problem I am encountering is running out of inotify watches.
I'm developing under Eclipse and the behavior is as follows:
When the app starts, it recurses a directory structure, putting a watch on each directory found. The current test case uses 13,660 paths. My maximum is set to 16384.
If I stop and restart the app several (20+ times), it seems to function normally. Eventually, however, I will get a cascade of system errors indicating the maximum number of watches has been reached. However, if I restart Eclipse, the issue goes away.
Obviously, the Watch Service isn't releasing all of it's resources, but of the 13,660 watches it acquires, only a few (I'm guessing less than a hundred) are retained. It appears they aren't released unless I shut down Eclipse's Java instance and restart it.
To address this, I've ensured the watch service's close method is called when the application shuts down and the watch service task is cancelled.
The only other thing that I'm doing differently is I'm running two separate watch services for two different purposes. I'm told that you shouldn't need to run more than one, and perhaps this is the problem, but I'd rather not run one watch service if I can help it.
That said, are there any thoughts or suggestions on how I might be able to determine the cause of this bug?
Apologies for the massive code posting. This is my implementation of the WatchService class.
A few notes:
The pathFinder runs in a separate thread and is just a file visitor - walking the directory tree and returning paths to all dirs / files found.
Register is called only when changes are posted to the pathsChanged property (from the pathFinder's onSucceeded callback).
The pathsChanged property is always updated by a setAll() call. It only posts the latest changes and is not meant to be cumulative. Beyond the watchservice, other classes listen to these properties and respond accordingly.
public final class LocalWatchService extends BaseTask {
private final static String TAG = "LocalWatchService";
//watch service task
private WatchService watcher;
//path finding task and associated executor
private LocalPathFinder finder;
//root path where the watch service begins
private final Path mRootPath;
private final ExecutorService pathFinderExecutor =
createExecutor ("pathFinder", false);
//class hash map which keys watched paths to generated watch keys
private final Map<WatchKey, Path> keys = new HashMap<WatchKey, Path>();
//reference to model property of watched paths.
private final SimpleListProperty <SyncPath> mChangedPaths =
new SimpleListProperty <SyncPath>
(FXCollections.<SyncPath> observableArrayList());
public LocalWatchService (String rootPath) {
super ();
mRootPath = Paths.get(rootPath);
//create the watch service
try {
this.watcher = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
e.printStackTrace();
}
setOnCancelled(new EventHandler() {
#Override
public void handle(Event arg0) {
pathFinderExecutor.shutdown();
}
});
mChangedPaths.addListener(new ListChangeListener <SyncPath> (){
#Override
public void onChanged(
javafx.collections.ListChangeListener.Change<? extends SyncPath>
arg0) {
for (SyncPath path: arg0.getList()) {
//call register only when a directory is found
if (path.getFile() == null) {
try {
register (path.getPath());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
});
};
public SimpleListProperty<SyncPath> changedPaths() { return mChangedPaths; }
public void initializeWatchPaths() {
ArrayList <Path> paths = new ArrayList <Path> ();
//create a DirectoryStream filter that finds only directories
//and symlinks
DirectoryStream.Filter<Path> filter =
new DirectoryStream.Filter<Path>() {
public boolean accept(Path file) throws IOException {
return (Files.isDirectory(file) ||
Files.isSymbolicLink(file));
}
};
//apply the filter to a directory stream opened on the root path
//and save everything returned.
paths.addAll(utils.getFiles(mRootPath, filter));
runPathFinder (paths);
}
private void runPathFinder (ArrayList <Path> paths) {
//need to add blocking code / mechanism in case a path finder is
//currently running (rare case)
finder = new LocalPathFinder();
finder.setPaths (paths);
//callbacks on successful completion of pathfinder
EventHandler <WorkerStateEvent> eh =
new EventHandler <WorkerStateEvent> () {
ArrayList <SyncPath> paths = new ArrayList <SyncPath>();
#Override
public void handle(WorkerStateEvent arg0) {
for (Path p: finder.getPaths()) {
paths.add(
new SyncPath(mRootPath, p, SyncType.SYNC_NONE));
}
addPaths(paths);
}
};
finder.setOnSucceeded(eh);
pathFinderExecutor.execute (finder);
}
private void addPath(Path path, SyncType syncType) {
mChangedPaths.setAll(new SyncPath(mRootPath, path, syncType));
}
private void addPaths(ArrayList<SyncPath> paths) {
mChangedPaths.setAll(paths);
}
/**
* Register the given directory with the WatchService
* #throws InterruptedException
*/
public final void register(Path dir)
throws IOException, InterruptedException {
//register the key with the watch service
WatchKey key =
dir.register (watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (!keys.isEmpty()) {
Path prev = keys.get(key);
if (prev == null) {
//This is a new key
}
else if (!dir.equals(prev)) {
//This is an update
}
}
keys.put(key, dir);
}
private void processWatchEvent (WatchKey key, Path dir) throws IOException, InterruptedException {
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
System.out.println ("Overflow encountered");
}
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path target = dir.resolve(ev.context());
if (kind == ENTRY_DELETE) {
ArrayList <Path> finderList = new ArrayList <Path> ();
if (Files.isDirectory(target)) {
//directory deletion is not implemented apart from
//file deletion
}
else
addPath (target, SyncType.SYNC_DELETE);
} else if (kind == ENTRY_CREATE) {
/*
* Added paths are passed to the pathfinder service for
* subdirectory discovery. Path and subpaths are then added
* to the AddedPaths property via an event listener on
* service's onSucceeded() event.
*
* Added files are added directly to the AddedPaths property
*/
ArrayList <Path> finderList = new ArrayList <Path> ();
if (Files.isDirectory(target)) {
finderList.add (target);
runPathFinder (finderList);
}
//add files directly to the addedPaths property
else {
//a newly created file may not be immediately readable
if (Files.isReadable(target)) {
addPath (target, SyncType.SYNC_CREATE);
}
else
System.err.println ("File " + target + " cannot be read");
}
} else if (kind == ENTRY_MODIFY) {
System.out.println ("File modified: " + target.toString());
}
boolean valid = key.reset();
if (!valid)
break;
}
}
#SuppressWarnings("unchecked")
<T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>)event;
}
#Override
protected Void call () throws IOException, InterruptedException {
boolean interrupted = false;
register (mRootPath);
initializeWatchPaths();
try {
// enter watch cycle
while (!interrupted) {
//watch for a key change. Thread blocks until a change occurs
WatchKey key = null;
interrupted = isCancelled();
//thread blocks until a key change occurs
// (whether a new path is processed by finder or a watched item changes otherwise)
try {
key = watcher.take();
} catch (InterruptedException e) {
interrupted = true;
try {
watcher.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// fall through and retry
}
Path dir = keys.get (key);
if (dir == null) {
System.out.println ("Null directory key encountered.");
continue;
}
//process key change once it occurs
processWatchEvent(key, dir);
// reset key and remove from set if directory no longer accessible
if (!key.reset()) {
keys.remove(key);
// all directories are inaccessible
if (keys.isEmpty())
break;
}
}
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
keys.clear();
watcher.close();
return null;
};
}

Java Get notification if file is modified

I'm looking for a way to get a notification when a certain file is modified. I want to call a certain method when this happens, but in some cases I also want that the method is not called.
I tried the following:
class FileListener extends Thread {
private Node n;
private long timeStamp;
public FileListener(Node n) {
this.n = n;
this.timeStamp = n.getFile().lastModified();
}
private boolean isModified() {
long newStamp = n.getFile().lastModified();
if (newStamp != timeStamp) {
timeStamp = newStamp;
return true;
} else {
return false;
}
public void run() {
while(true) {
if (isModified()) {
n.setStatus(STATUS.MODIFIED);
}
try {
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
}
}
The Node class contains a reference to the file, a STATUS (enum) and a reference to the FileListener of that file. When the file is modified, I want the status to change to STATUS.MODIFIED. But, in some cases, the file where the Node refers to changes to a new File and I don't want it to automatically change the status to Modified. In that case I tried this:
n.listener.interrupt(); //interrupts the listener
n.setListener(null); //sets listener to null
n.setFile(someNewFile); //Change the file in the node
//Introduce a new listener, which will look at the new file.
n.setListener(new FileListener(n));
n.listener.start(); // start the thread of the new listener
But what I get is an Exception thrown by 'Thread.sleep(1000)', because the sleep was interrupted and when I check the status, it is still modified to STATUS.MODIFIED.
Am I doing something wrong?
What about the watch service : http://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html?
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = ...;
try {
WatchKey key = dir.register(watcher, ENTRY_MODIFY);
} catch (IOException x) {
System.err.println(x);
}
And then:
for (;;) {
//wait for key to be signaled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
if (kind == OVERFLOW) {
continue;
}
...
}

Categories