BowlerKernel
FileChangeWatcher.java
Go to the documentation of this file.
1 package com.neuronrobotics.bowlerstudio.util;
2 
3 /*
4  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * - Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * - Neither the name of Oracle nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 import java.io.File;
35 import java.io.IOException;
36 import java.nio.file.*;
37 
38 import static java.nio.file.StandardWatchEventKinds.*;
39 import java.nio.file.attribute.*;
40 import java.util.*;
41 
42 import com.neuronrobotics.bowlerstudio.IssueReportingExceptionHandler;
43 
44 // TODO: Auto-generated Javadoc
48 public class FileChangeWatcher {
49 
51  private File fileToWatch;
52 
54  private boolean run = true;
55 
57  private final WatchService watcher;
58 
60  private final Map<WatchKey, Path> keys;
61 
63  private final boolean recursive = false;
64 
66  private ArrayList<IFileChangeListener> listeners = new ArrayList<IFileChangeListener>();
67  private static boolean runThread = true;
68 
69  private static HashMap<String, FileChangeWatcher> activeListener = new HashMap<String, FileChangeWatcher>();
70  private Thread watcherThread = null;
71 
72 
73 
74 
78  public static void clearAll() {
79  Object[] array = activeListener.keySet().toArray();
80  for (int i = 0; i < array.length; i++) {
81  Object key = array[i];
82  activeListener.get(key).close();
83  }
84  activeListener.clear();
85  }
86 
87  public static void notifyOfDelete(File fileToWatch) {
88  String path = fileToWatch.getAbsolutePath();
89  if (activeListener.get(path) != null) {
90  ArrayList<IFileChangeListener> listeners2 = new ArrayList<>();
91  listeners2.addAll( activeListener.get(path).listeners);
92  for (int i = 0; i < listeners2.size(); i++) {
93  IFileChangeListener l = listeners2.get(i);
95  }
96  }
97  }
98  public static void close(File fileToWatch) {
99  String path = fileToWatch.getAbsolutePath();
100  if (activeListener.get(path) != null) {
101  activeListener.get(path).close();
102  }
103  }
113  public static FileChangeWatcher watch(File fileToWatch) throws IOException {
114  String path = fileToWatch.getAbsolutePath();
115  if (activeListener.get(path) == null) {
117  System.err.println("Adding file to listening " + fileToWatch.getAbsolutePath());
118  }
119  return activeListener.get(path);
120  }
121 
130  private FileChangeWatcher(File fileToWatch) throws IOException {
131 
132  this.setFileToWatch(fileToWatch);
133  //System.err.println("\n\n\n\tWatching "+fileToWatch.getAbsolutePath()+"\n\n\n");
134  this.watcher = FileSystems.getDefault().newWatchService();
135  this.keys = new HashMap<WatchKey, Path>();
136  Path dir = Paths.get(fileToWatch.getParent());
137  if (recursive) {
138  System.out.format("Scanning %s ...\n", dir);
139  registerAll(dir);
140  System.out.println("Done.");
141  } else {
142  register(dir);
143  }
144  watcherThread = new Thread() {
145  public void run() {
146  setName("File Watcher Thread "+fileToWatch.getName());
147  //new Exception("Starting File Watcher Thread").printStackTrace();
148  Thread.currentThread().setUncaughtExceptionHandler(new IssueReportingExceptionHandler());
149 
150  while (run) {
151  try {
152  //System.err.println("Checking File: " + getFileToWatch().getAbsolutePath());
153  watch();
154  } catch (Exception ex) {
155  ex.printStackTrace();
156  }
157 
158  try {
159  Thread.sleep(100);
160  } catch (InterruptedException e) {
161  // TODO Auto-generated catch block
162  e.printStackTrace();
163  }
164  }
165 
166  //new Exception("File Watcher Thread Died").printStackTrace();
167  }
168  };
169  watcherThread.start();
170  }
171 
179  if (!listeners.contains(l)) {
180  listeners.add(l);
181  }
182  }
183 
191  if (listeners.contains(l)) {
192  listeners.remove(l);
193  }
194 // if(listeners.size()==0){
195 // close() ;
196 // }
197  }
198 
208  @SuppressWarnings("unchecked")
209  static <T> WatchEvent<T> cast(WatchEvent<?> event) {
210  return (WatchEvent<T>) event;
211  }
212 
221  private void register(Path dir) throws IOException {
222  WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
223 
224  Path prev = keys.get(key);
225  if (prev == null) {
226  // System.out.format("register: %s\n", dir);
227  } else {
228  if (!dir.equals(prev)) {
229  // System.out.format("update: %s -> %s\n", prev, dir);
230  }
231  }
232 
233  keys.put(key, dir);
234  }
235 
245  private void registerAll(final Path start) throws IOException {
246  // register directory and sub-directories
247  Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
248  @Override
249  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
250  register(dir);
251  return FileVisitResult.CONTINUE;
252  }
253  });
254  }
255 
259  public void watch() {
260 
261  // wait for key to be signalled
262  WatchKey key;
263  try {
264  key = watcher.take();
265  } catch (Exception x) {
266  return;
267  }
268  if(!run)
269  return;
270 
271  Path dir = keys.get(key);
272  if (dir == null) {
273  System.err.println("WatchKey not recognized!!");
274  return;
275  }
276 
277  for (WatchEvent<?> event : key.pollEvents()) {
278  WatchEvent.Kind kind = event.kind();
279 
280  // TBD - provide example of how OVERFLOW event is handled
281  if (kind == OVERFLOW) {
282  continue;
283  }
284 
285  // Context for directory entry event is the file name of entry
286  WatchEvent<Path> ev = cast(event);
287  Path name = ev.context();
288  Path child = dir.resolve(name);
289  try {
290  if (!child.toFile().getCanonicalPath().equals(fileToWatch.getCanonicalPath())) {
291  continue;
292  }
293  // print out event
294  // System.out.format("%s: %s\n", event.kind().name(), child);
295  System.err.println("File Changed: " + getFileToWatch().getAbsolutePath());
296  for (int i = 0; i < listeners.size(); i++) {
297 
298  listeners.get(i).onFileChange(child.toFile(), event);
299  Thread.sleep(50);// pad out the events to avoid file box
300  // overwrites
301  }
302  } catch (Exception e) {
303  // TODO Auto-generated catch block
304  e.printStackTrace();
305  }
306 
307  }
308 
309  // reset key and remove from set if directory no longer accessible
310  boolean valid = key.reset();
311  if (!valid) {
312  keys.remove(key);
313 
314  // all directories are inaccessible
315  if (keys.isEmpty()) {
316  return;
317  }
318  }
319 
320  }
321 
327  public File getFileToWatch() {
328  return fileToWatch;
329  }
330 
337  public void setFileToWatch(File fileToWatch) {
338  this.fileToWatch = fileToWatch;
339  }
340 
346  public boolean isRun() {
347  return run;
348  }
349 
353  public void close() {
354  //new Exception("File watcher closed " + fileToWatch.getAbsolutePath()).printStackTrace();
355  this.run = false;
356  try {
357  System.err.println("Closing watcher for "+fileToWatch.getAbsolutePath());
358  watcher.close();
359  } catch (IOException e) {
360  // TODO Auto-generated catch block
361  e.printStackTrace();
362  }
363  activeListener.remove(fileToWatch.getAbsolutePath());
364  }
365 
366 }
static FileChangeWatcher watch(File fileToWatch)
static HashMap< String, FileChangeWatcher > activeListener