1 10 11 package org.mmbase.util; 12 13 import java.io.File ; 14 import java.util.*; 15 import org.mmbase.util.logging.*; 16 import org.mmbase.util.xml.UtilReader; 17 import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet; 18 19 68 public abstract class FileWatcher { 69 private static Logger log = Logging.getLoggerInstance(FileWatcher.class); 70 71 private static FileWatcherRunner fileWatchers = new FileWatcherRunner(); 72 static { 73 fileWatchers.start(); 74 } 75 76 80 static final public long DEFAULT_DELAY = 60000; 81 82 85 static public long THREAD_DELAY = 10000; 86 87 88 89 private static Map props; 90 91 92 95 static private Runnable watcher = new Runnable () { 96 public void run() { 97 try { 98 String delay = (String ) props.get("delay"); 99 if (delay != null) { 100 THREAD_DELAY = Integer.parseInt(delay); 101 log.service("Set thread delay time to " + THREAD_DELAY); 102 } 103 } catch (Exception e) { 104 log.error(e); 105 } 106 } 107 }; 108 109 110 static { 111 props = new UtilReader("resourcewatcher.xml", watcher).getProperties(); 112 watcher.run(); 113 } 114 115 118 public static void shutdown() { 119 fileWatchers.run = false; 120 fileWatchers.interrupt(); 121 log.service("Shut down file watcher thread"); 122 } 123 124 128 private long delay = DEFAULT_DELAY; 129 130 private Set files = new LinkedHashSet(); 131 private Set fileSet = new FileSet(); private Set removeFiles = new HashSet(); 133 private boolean stop = false; 134 private boolean continueAfterChange = false; 135 private long lastCheck = 0; 136 137 protected FileWatcher() { 138 this(true); 139 } 140 141 protected FileWatcher(boolean c) { 142 continueAfterChange = c; 144 } 145 146 public void start() { 147 fileWatchers.add(this); 148 } 149 153 abstract public void onChange(File file); 154 155 158 public void setDelay(long delay) { 159 this.delay = delay; 160 if (delay < THREAD_DELAY) { 161 log.service("Delay of " + this + " (" + delay + " ms) is smaller than the delay of the watching thread. Will not watch more often then once per " + THREAD_DELAY + " ms."); 162 } 163 } 164 165 170 public void add(File file) { 171 FileEntry fe = new FileEntry(file); 172 synchronized (this) { 173 files.add(fe); 174 if (removeFiles.remove(fe)) { 175 log.service("Canceling removal from filewatcher " + fe); 176 } 177 } 178 } 179 180 185 public boolean contains(File file) { 186 return files.contains(new FileEntry(file)); 187 } 188 189 192 public void remove(File file) { 193 synchronized (this) { 194 removeFiles.add(file); 195 } 196 } 197 198 204 public Set getFiles() { 205 return fileSet; 206 } 207 208 212 public void clear() { 213 fileSet.clear(); 214 } 215 216 219 public void exit() { 220 synchronized (this) { 221 stop = true; 222 } 223 } 224 225 228 public String toString() { 229 return files.toString(); 230 } 231 232 239 private boolean changed() { 240 synchronized (this) { 241 Iterator i = files.iterator(); 242 while (i.hasNext()) { 243 FileEntry fe = (FileEntry)i.next(); 244 if (fe.changed()) { 245 log.debug("the file :" + fe.getFile().getAbsolutePath() + " has changed."); 246 try { 247 onChange(fe.getFile()); 248 } catch (Throwable e) { 249 log.warn("onChange of " + fe.getFile().getName() + " lead to exception:"); 250 log.warn(Logging.stackTrace(e)); 251 } 252 if (continueAfterChange) { 253 fe.updated(); } else { return true; } 257 } 258 } 259 } 260 return false; 261 } 262 263 private void removeFiles() { 264 synchronized (this) { 265 Iterator ri = removeFiles.iterator(); 267 while (ri.hasNext()) { 268 File f = (File )ri.next(); 269 FileEntry found = null; 270 271 Iterator i = files.iterator(); 273 while (i.hasNext()) { 274 FileEntry fe = (FileEntry)i.next(); 275 if (fe.getFile().equals(f)) { 276 if (log.isDebugEnabled()) { 277 log.debug("removing file[" + fe.getFile().getName() + "]"); 278 } 279 found = fe; 280 break; 281 } 282 } 283 if (found != null) { 284 files.remove(found); 285 log.service("Removed " + found + " from watchlist"); 286 } 287 } 288 removeFiles.clear(); 289 } 290 } 291 292 295 private boolean mustStop() { 296 synchronized (this) { 297 return stop; 298 } 299 } 300 301 public boolean equals(Object o) { 302 if (o == this) return true; 303 if (o == null) return false; 304 if (getClass().equals(o.getClass())) { 305 FileWatcher f = (FileWatcher)o; 306 return this.files.equals(f.files); 307 } 308 return false; 309 } 310 311 312 315 public int hashCode() { 316 return files == null ? 0 : files.hashCode(); 317 } 318 319 322 public static void main(String [] args) { 323 324 for (int i = 0; i < 100; i++) { 326 FileWatcher w = new TestFileWatcher(); 327 for (int j = 0; j < 4; j++) { 329 try { 330 w.add(File.createTempFile("filewatchertestfile", ".txt")); 331 } catch (Exception e) { 332 System.out.println(e); 333 } 334 } 335 w.setDelay(1 * 1000); w.start(); 338 } 340 341 System.out.println("Starting"); 342 long start = System.currentTimeMillis(); 344 long k; 345 for (k = 0; k < 400000000;) { 346 k++; 347 } 348 System.out.println("\ntook " + (System.currentTimeMillis() - start) + " ms"); 349 } 350 351 355 private static class FileWatcherRunner extends Thread { 356 357 358 boolean run = true; 359 362 private Set watchers = new CopyOnWriteArraySet(); 363 private Set watchersToAdd = new HashSet(); 364 365 FileWatcherRunner() { 366 super("MMBase FileWatcher thread"); 367 log.service("Starting the file-watcher thread"); 368 setPriority(MIN_PRIORITY); 369 setDaemon(true); 370 } 371 372 void add(FileWatcher f) { 373 watchers.add(f); 374 } 375 376 380 public void run() { 381 List removed = new ArrayList(); 383 while (run) { 384 try { 385 long now = System.currentTimeMillis(); 386 Iterator i = watchers.iterator(); 387 while (i.hasNext()) { 388 FileWatcher f = (FileWatcher)i.next(); 389 if (now - f.lastCheck > f.delay) { 390 if (log.isDebugEnabled()) { 391 log.trace("Filewatcher will sleep for : " + f.delay / 1000 + " s. " + "Currently watching: " + f.getClass().getName() + " " + f.toString()); 392 } 393 f.removeFiles(); 395 if (f.changed() || f.mustStop()) { 397 if (log.isDebugEnabled()) { 398 log.debug("Removing filewatcher " + f + " " + f.mustStop()); 399 } 400 removed.add(f); 401 } 402 f.lastCheck = now; 403 } 404 } 405 watchers.removeAll(removed); 406 removed.clear(); 407 if (log.isTraceEnabled()) { 408 log.trace("Sleeping " + THREAD_DELAY + " ms"); 409 } 410 Thread.sleep(THREAD_DELAY); 411 } catch (InterruptedException e) { 412 Thread ct = Thread.currentThread(); 413 log.debug((ct != null ? ct.getName() : "MMBase")+ " was interrupted."); 414 break; } catch (Throwable ex) { 416 log.error("Exception: " + ex.getClass().getName() + ": " + ex.getMessage() + Logging.stackTrace(ex)); 418 } 419 } 421 } 422 } 423 424 427 private static class TestFileWatcher extends FileWatcher { 428 int i = 0; 429 public void onChange(java.io.File f) { 430 i++; 432 } 433 protected void finalize() { 434 System.out.println(this.toString() + ":" + i); 435 } 436 } 437 438 442 private class FileEntry { 443 private long lastModified = -1; 445 private boolean exists = false; 446 private File file; 447 448 public FileEntry(File file) { 449 if (file == null) { 450 String msg = "file was null"; 451 throw new RuntimeException (msg); 453 } 454 exists = file.exists(); 455 if (!exists) { 456 log.debug("file :" + file.getAbsolutePath() + " did not exist (yet)"); 459 lastModified = -1; 460 } else { 461 lastModified = file.lastModified(); 462 } 463 this.file = file; 464 } 465 466 471 public boolean changed() { 472 if (file.exists()) { 473 if (!exists) { 474 log.info("File " + file.getAbsolutePath() + " added"); 475 return true; 476 } else { 477 boolean result = lastModified < file.lastModified(); 478 if (result) { 479 log.info("File " + file.getAbsolutePath() + " changed"); 480 } 481 return result; 482 } 483 } else { 484 if (exists) { 485 log.info("File " + file.getAbsolutePath() + " removed"); 486 } 487 return exists; 488 } 489 } 490 491 494 public void updated() { 495 exists = file.exists(); 496 if (exists) { 497 lastModified = file.lastModified(); 498 } else { 499 lastModified = -1; 500 } 501 } 502 503 public File getFile() { 504 return file; 505 } 506 507 public String toString() { 508 return file.toString() + ":" + lastModified; 509 } 510 511 public boolean equals(Object o) { 512 if (o instanceof FileEntry) { 513 FileEntry fe = (FileEntry)o; 514 return file.equals(fe.file); 515 } else if (o instanceof File ) { 516 return file.equals(o); 517 } 518 return false; 519 } 520 521 public int hashCode() { 522 return file.hashCode(); 523 } 524 525 } 526 527 531 private class FileSet extends AbstractSet { 532 public int size() { 533 return FileWatcher.this.files.size(); 534 } 535 public Iterator iterator() { 536 return new FileIterator(); 537 } 538 public boolean add(Object o) { 539 int s = size(); 540 FileWatcher.this.add((File ) o); 541 return s != size(); 542 } 543 } 544 548 private class FileIterator implements Iterator { 549 Iterator it; 550 File lastFile; 551 FileIterator() { 552 it = FileWatcher.this.files.iterator(); 553 } 554 public boolean hasNext() { 555 return it.hasNext(); 556 } 557 public Object next() { 558 FileEntry f = (FileEntry) it.next(); 559 lastFile = f.getFile(); 560 return lastFile; 561 } 562 public void remove() { 563 FileWatcher.this.remove(lastFile); 564 } 565 566 } 567 568 } 569 | Popular Tags |