93df102a51ea52a2f036d61ccb58a7e1577434a0
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / FileWatcher.java
1 /**
2  * 
3  */
4 package uk.ac.vamsas.client.simpleclient;
5
6 import java.io.File;
7
8
9 /**
10  * Watches a particular file for its creation, deletion, or
11  * modification. The watcher is thread safe and different 
12  * instances watching the state of a particular file carry
13  * their own state record for the file.
14  */
15 public class FileWatcher {
16
17   private File subject = null;
18
19   private long lastStat[];
20   boolean waslocked=false;
21   boolean exists = false;
22   /**
23    * transient lock on subject - can be passed back to calling class
24    * to preserve new state of file for immediate reading.
25    */
26   private Lock subjectLock = null;
27   /**
28    * clear local locks on subject.
29    *
30    */
31   private void clearLock() {
32     if (subjectLock!=null)
33       subjectLock.release();
34     subjectLock=null;
35   }
36   /**
37    *  
38    * @return true if subject exists and is locked by another process.
39    */
40   private boolean checkLock() {
41     if (subject!=null && subject.exists()) {
42       if (subjectLock!=null) {
43         subjectLock.release();
44       }
45       subjectLock = LockFactory.tryLock(subject);
46       if (subjectLock.isLocked()) {
47         return false;
48       }
49       clearLock();
50       return true;
51     }
52     return false;
53   }
54   
55   private long[] getStat(File subject) {
56     return new long[] { subject.lastModified(), subject.length() };
57   }
58   private boolean compStat(long[] stat, long[] newstat) {
59     if (stat[0]!=newstat[0] || stat[1]!=newstat[1])
60       return false;
61     return true;
62   }
63   /**
64    * Detect changes in file state and release of any
65    * lock in place during change.
66    * @return true if file state has changed. Leaves lock in subjectLock (ready to be passed to caller)
67    */
68   private boolean check() {
69     if (subject != null) {
70       if (!subject.exists()) {
71         if (exists) {
72           if (!waslocked) {
73             // !checkLock()) {
74           
75             exists = false;
76             // waslocked=false;
77             return true;
78           }
79         }
80         // locked - state change registered after lock is released
81         return false;
82       } else {
83         long[] newStat = getStat(subject); // subject.lastModified();
84         if (!checkLock()) {
85           // file is free to access, return state change
86           if (!exists || !compStat(lastStat, newStat)) {
87             waslocked=false;
88             exists=true;
89             lastStat=newStat;
90             return true;
91           }
92           // no change
93           return false;
94         } else {
95           waslocked=true;
96           return false;
97         }
98       }
99     }
100     return false;
101   }
102   /**
103    * updates internal record of file state when caller has intentionally
104    * modified subject. (ignores locked-state of subject)
105    */
106   public void setState() {
107     if (subject!=null) {
108       if (exists = subject.exists()) {
109         lastStat = getStat(subject);
110         waslocked = false;
111       }
112     }
113   }
114   
115   /**
116    * Make a watcher for a particular file. If the file doesn't exist, the
117    * watcher will watch for its creation (and indicate a change of state) 
118    * For locked files, the removal of a lock constitutes a change of 
119    * state if the file was modified.
120    * 
121    * @param subject
122    */
123   
124   public FileWatcher(File subject) {
125     this.subject = subject;
126     setState();
127   }
128   /**
129    * Test for change in file state. Only indicates a change 
130    * after any lock on a file has been released.
131    * @return true if file has been modified.
132    */
133   public boolean hasChanged() {
134     boolean res = check();
135     clearLock();
136     return res;
137   }
138   /**
139    * passes lock back to caller if hasChanged returned true.
140    * @return
141    */
142   public Lock getChangedState() {
143     boolean res = check();
144     if (res)
145       return subjectLock;
146     else {
147       clearLock();
148       return null;
149     }
150   }
151   /**
152    * safely? getting current state of the watched file
153    * @return
154    */
155   public long[] getCurrentState() {
156     return getStat(subject);
157   }
158   /**
159    * safely compare an externally recorded state with the current state
160    * for significant modifications.
161    * @param a
162    * @param b
163    * @return
164    */
165   public boolean diffState(long[] a, long[] b) {
166     return compStat(a,b);
167   }
168   /**
169    * @return the subject
170    */
171   public File getSubject() {
172     return subject;
173   }
174 }