applied LGPLv3 and source code formatting.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / FileWatcher.java
1 /*\r
2  * This file is part of the Vamsas Client version 0.1. \r
3  * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, \r
4  *  Andrew Waterhouse and Dominik Lindner.\r
5  * \r
6  * Earlier versions have also been incorporated into Jalview version 2.4 \r
7  * since 2008, and TOPALi version 2 since 2007.\r
8  * \r
9  * The Vamsas Client is free software: you can redistribute it and/or modify\r
10  * it under the terms of the GNU Lesser General Public License as published by\r
11  * the Free Software Foundation, either version 3 of the License, or\r
12  * (at your option) any later version.\r
13  *  \r
14  * The Vamsas Client is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU Lesser General Public License for more details.\r
18  * \r
19  * You should have received a copy of the GNU Lesser General Public License\r
20  * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.\r
21  */\r
22 package uk.ac.vamsas.client.simpleclient;\r
23 \r
24 import java.io.File;\r
25 \r
26 /**\r
27  * Watches a particular file for its creation, deletion, or modification. The\r
28  * watcher is thread safe and different instances watching the state of a\r
29  * particular file carry their own state record for the file.\r
30  */\r
31 public class FileWatcher {\r
32 \r
33   private File subject = null;\r
34 \r
35   private long lastStat[];\r
36 \r
37   boolean waslocked = false;\r
38 \r
39   boolean exists = false;\r
40 \r
41   /**\r
42    * transient lock on subject - can be passed back to calling class to preserve\r
43    * new state of file for immediate reading.\r
44    */\r
45   private Lock subjectLock = null;\r
46 \r
47   /**\r
48    * clear local locks on subject.\r
49    * \r
50    */\r
51   private void clearLock() {\r
52     if (subjectLock != null)\r
53       subjectLock.release();\r
54     subjectLock = null;\r
55   }\r
56 \r
57   /**\r
58    * \r
59    * @return true if subject exists and is locked by another process.\r
60    */\r
61   private boolean checkLock() {\r
62     if (subject != null && subject.exists()) {\r
63       if (subjectLock != null) {\r
64         subjectLock.release();\r
65       }\r
66       subjectLock = LockFactory.tryLock(subject);\r
67       if (subjectLock.isLocked()) {\r
68         return false;\r
69       }\r
70       clearLock();\r
71       return true;\r
72     }\r
73     return false;\r
74   }\r
75 \r
76   private long[] getStat(File subject) {\r
77     return new long[] { subject.lastModified(), subject.length() };\r
78   }\r
79 \r
80   private boolean compStat(long[] stat, long[] newstat) {\r
81     if (stat[0] != newstat[0] || stat[1] != newstat[1])\r
82       return false;\r
83     return true;\r
84   }\r
85 \r
86   /**\r
87    * Detect changes in file state and release of any lock in place during\r
88    * change.\r
89    * \r
90    * @return true if file state has changed. Leaves lock in subjectLock (ready\r
91    *         to be passed to caller)\r
92    */\r
93   private boolean check() {\r
94     if (subject != null) {\r
95       if (!subject.exists()) {\r
96         if (exists) {\r
97           if (!waslocked) {\r
98             // !checkLock()) {\r
99 \r
100             exists = false;\r
101             // waslocked=false;\r
102             return true;\r
103           }\r
104         }\r
105         // locked - state change registered after lock is released\r
106         return false;\r
107       } else {\r
108         long[] newStat = getStat(subject); // subject.lastModified();\r
109         if (!checkLock()) {\r
110           // file is free to access, return state change\r
111           if (!exists || !compStat(lastStat, newStat)) {\r
112             waslocked = false;\r
113             exists = true;\r
114             lastStat = newStat;\r
115             return true;\r
116           }\r
117           // no change\r
118           return false;\r
119         } else {\r
120           waslocked = true;\r
121           return false;\r
122         }\r
123       }\r
124     }\r
125     return false;\r
126   }\r
127 \r
128   /**\r
129    * updates internal record of file state when caller has intentionally\r
130    * modified subject. (ignores locked-state of subject)\r
131    */\r
132   public void setState() {\r
133     if (subject != null) {\r
134       if (exists = subject.exists()) {\r
135         lastStat = getStat(subject);\r
136         waslocked = false;\r
137       }\r
138     }\r
139   }\r
140 \r
141   /**\r
142    * Make a watcher for a particular file. If the file doesn't exist, the\r
143    * watcher will watch for its creation (and indicate a change of state) For\r
144    * locked files, the removal of a lock constitutes a change of state if the\r
145    * file was modified.\r
146    * \r
147    * @param subject\r
148    */\r
149 \r
150   public FileWatcher(File subject) {\r
151     this.subject = subject;\r
152     setState();\r
153   }\r
154 \r
155   /**\r
156    * Test for change in file state. Only indicates a change after any lock on a\r
157    * file has been released.\r
158    * \r
159    * @return true if file has been modified.\r
160    */\r
161   public boolean hasChanged() {\r
162     boolean res = check();\r
163     clearLock();\r
164     return res;\r
165   }\r
166 \r
167   /**\r
168    * passes lock back to caller if hasChanged returned true.\r
169    * \r
170    * @return\r
171    */\r
172   public Lock getChangedState() {\r
173     boolean res = check();\r
174     if (res)\r
175       return subjectLock;\r
176     else {\r
177       clearLock();\r
178       return null;\r
179     }\r
180   }\r
181 \r
182   /**\r
183    * safely? getting current state of the watched file\r
184    * \r
185    * @return\r
186    */\r
187   public long[] getCurrentState() {\r
188     return getStat(subject);\r
189   }\r
190 \r
191   /**\r
192    * safely compare an externally recorded state with the current state for\r
193    * significant modifications.\r
194    * \r
195    * @param a\r
196    * @param b\r
197    * @return\r
198    */\r
199   public boolean diffState(long[] a, long[] b) {\r
200     return compStat(a, b);\r
201   }\r
202 \r
203   /**\r
204    * @return the subject\r
205    */\r
206   public File getSubject() {\r
207     return subject;\r
208   }\r
209 }\r