/** * */ package uk.ac.vamsas.client.simpleclient; import java.io.File; /** * Watches a particular file for its creation, deletion, or * modification. The watcher is thread safe and different * instances watching the state of a particular file carry * their own state record for the file. */ public class FileWatcher { private File subject = null; private long lastStat[]; boolean waslocked=false; boolean exists = false; /** * transient lock on subject - can be passed back to calling class * to preserve new state of file for immediate reading. */ private Lock subjectLock = null; /** * clear local locks on subject. * */ private void clearLock() { if (subjectLock!=null) subjectLock.release(); subjectLock=null; } /** * * @return true if subject exists and is locked by another process. */ private boolean checkLock() { if (subject!=null && subject.exists()) { if (subjectLock!=null) { subjectLock.release(); } subjectLock = LockFactory.tryLock(subject); if (subjectLock.isLocked()) { return false; } clearLock(); return true; } return false; } private long[] getStat(File subject) { return new long[] { subject.lastModified(), subject.length() }; } private boolean compStat(long[] stat, long[] newstat) { if (stat[0]!=newstat[0] || stat[1]!=newstat[1]) return false; return true; } /** * Detect changes in file state and release of any * lock in place during change. * @return true if file state has changed. Leaves lock in subjectLock (ready to be passed to caller) */ private boolean check() { if (subject != null) { if (!subject.exists()) { if (exists) { if (!waslocked) { // !checkLock()) { exists = false; // waslocked=false; return true; } } // locked - state change registered after lock is released return false; } else { long[] newStat = getStat(subject); // subject.lastModified(); if (!checkLock()) { // file is free to access, return state change if (!exists || !compStat(lastStat, newStat)) { waslocked=false; exists=true; lastStat=newStat; return true; } // no change return false; } else { waslocked=true; return false; } } } return false; } /** * updates internal record of file state when caller has intentionally * modified subject. (ignores locked-state of subject) */ public void setState() { if (subject!=null) { if (exists = subject.exists()) { lastStat = getStat(subject); waslocked = false; } } } /** * Make a watcher for a particular file. If the file doesn't exist, the * watcher will watch for its creation (and indicate a change of state) * For locked files, the removal of a lock constitutes a change of * state if the file was modified. * * @param subject */ public FileWatcher(File subject) { this.subject = subject; setState(); } /** * Test for change in file state. Only indicates a change * after any lock on a file has been released. * @return true if file has been modified. */ public boolean hasChanged() { boolean res = check(); clearLock(); return res; } /** * passes lock back to caller if hasChanged returned true. * @return */ public Lock getChangedState() { boolean res = check(); if (res) return subjectLock; else { clearLock(); return null; } } /** * safely? getting current state of the watched file * @return */ public long[] getCurrentState() { return getStat(subject); } /** * safely compare an externally recorded state with the current state * for significant modifications. * @param a * @param b * @return */ public boolean diffState(long[] a, long[] b) { return compStat(a,b); } /** * @return the subject */ public File getSubject() { return subject; } }