1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.mortbay.util;
18
19 import java.io.File;
20 import java.io.FilenameFilter;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.Timer;
31 import java.util.TimerTask;
32
33 import org.mortbay.log.Log;
34
35
36
37
38
39
40
41
42
43
44 public class Scanner
45 {
46 private int _scanInterval;
47 private List _listeners = Collections.synchronizedList(new ArrayList());
48 private Map _prevScan = new HashMap();
49 private Map _currentScan = new HashMap();
50 private FilenameFilter _filter;
51 private List _scanDirs;
52 private volatile boolean _running = false;
53 private boolean _reportExisting = true;
54 private Timer _timer;
55 private TimerTask _task;
56 private boolean _recursive=true;
57
58
59
60
61
62
63
64 public interface Listener
65 {
66 }
67
68
69 public interface DiscreteListener extends Listener
70 {
71 public void fileChanged (String filename) throws Exception;
72 public void fileAdded (String filename) throws Exception;
73 public void fileRemoved (String filename) throws Exception;
74 }
75
76
77 public interface BulkListener extends Listener
78 {
79 public void filesChanged (List filenames) throws Exception;
80 }
81
82
83
84
85
86 public Scanner ()
87 {
88 }
89
90
91
92
93
94 public int getScanInterval()
95 {
96 return _scanInterval;
97 }
98
99
100
101
102
103 public synchronized void setScanInterval(int scanInterval)
104 {
105 this._scanInterval = scanInterval;
106 schedule();
107 }
108
109
110
111
112
113
114 public void setScanDir (File dir)
115 {
116 _scanDirs = new ArrayList();
117 _scanDirs.add(dir);
118 }
119
120
121
122
123
124
125 public File getScanDir ()
126 {
127 return (_scanDirs==null?null:(File)_scanDirs.get(0));
128 }
129
130 public void setScanDirs (List dirs)
131 {
132 _scanDirs = dirs;
133 }
134
135 public List getScanDirs ()
136 {
137 return _scanDirs;
138 }
139
140 public void setRecursive (boolean recursive)
141 {
142 _recursive=recursive;
143 }
144
145 public boolean getRecursive ()
146 {
147 return _recursive;
148 }
149
150
151
152
153
154 public void setFilenameFilter (FilenameFilter filter)
155 {
156 this._filter = filter;
157 }
158
159
160
161
162
163 public FilenameFilter getFilenameFilter ()
164 {
165 return _filter;
166 }
167
168
169
170
171
172
173
174 public void setReportExistingFilesOnStartup (boolean reportExisting)
175 {
176 this._reportExisting = reportExisting;
177 }
178
179
180
181
182
183 public synchronized void addListener (Listener listener)
184 {
185 if (listener == null)
186 return;
187 _listeners.add(listener);
188 }
189
190
191
192
193
194
195
196 public synchronized void removeListener (Listener listener)
197 {
198 if (listener == null)
199 return;
200 _listeners.remove(listener);
201 }
202
203
204
205
206
207 public synchronized void start ()
208 {
209 if (_running)
210 return;
211
212 _running = true;
213
214 if (_reportExisting)
215 {
216
217 scan();
218 }
219 else
220 {
221
222 scanFiles();
223 _prevScan.putAll(_currentScan);
224 }
225 schedule();
226 }
227
228 public TimerTask newTimerTask ()
229 {
230 return new TimerTask()
231 {
232 public void run() { scan(); }
233 };
234 }
235
236 public Timer newTimer ()
237 {
238 return new Timer(true);
239 }
240
241 public void schedule ()
242 {
243 if (_running)
244 {
245 if (_timer!=null)
246 _timer.cancel();
247 if (_task!=null)
248 _task.cancel();
249 if (getScanInterval() > 0)
250 {
251 _timer = newTimer();
252 _task = newTimerTask();
253 _timer.schedule(_task, 1000L*getScanInterval(),1000L*getScanInterval());
254 }
255 }
256 }
257
258
259
260 public synchronized void stop ()
261 {
262 if (_running)
263 {
264 _running = false;
265 if (_timer!=null)
266 _timer.cancel();
267 if (_task!=null)
268 _task.cancel();
269 _task=null;
270 _timer=null;
271 }
272 }
273
274
275
276
277 public void scan ()
278 {
279 scanFiles();
280 reportDifferences(_currentScan, _prevScan);
281 _prevScan.clear();
282 _prevScan.putAll(_currentScan);
283 }
284
285
286
287
288
289 public void scanFiles ()
290 {
291 if (_scanDirs==null)
292 return;
293
294 _currentScan.clear();
295 Iterator itor = _scanDirs.iterator();
296 while (itor.hasNext())
297 {
298 File dir = (File)itor.next();
299
300 if ((dir != null) && (dir.exists()))
301 scanFile(dir, _currentScan);
302 }
303 }
304
305
306
307
308
309
310
311
312 public void reportDifferences (Map currentScan, Map oldScan)
313 {
314 List bulkChanges = new ArrayList();
315
316 Set oldScanKeys = new HashSet(oldScan.keySet());
317 Iterator itor = currentScan.entrySet().iterator();
318 while (itor.hasNext())
319 {
320 Map.Entry entry = (Map.Entry)itor.next();
321 if (!oldScanKeys.contains(entry.getKey()))
322 {
323 Log.debug("File added: "+entry.getKey());
324 reportAddition ((String)entry.getKey());
325 bulkChanges.add(entry.getKey());
326 }
327 else if (!oldScan.get(entry.getKey()).equals(entry.getValue()))
328 {
329 Log.debug("File changed: "+entry.getKey());
330 reportChange((String)entry.getKey());
331 oldScanKeys.remove(entry.getKey());
332 bulkChanges.add(entry.getKey());
333 }
334 else
335 oldScanKeys.remove(entry.getKey());
336 }
337
338 if (!oldScanKeys.isEmpty())
339 {
340
341 Iterator keyItor = oldScanKeys.iterator();
342 while (keyItor.hasNext())
343 {
344 String filename = (String)keyItor.next();
345 Log.debug("File removed: "+filename);
346 reportRemoval(filename);
347 bulkChanges.add(filename);
348 }
349 }
350
351 if (!bulkChanges.isEmpty())
352 reportBulkChanges(bulkChanges);
353 }
354
355
356
357
358
359
360
361
362 private void scanFile (File f, Map scanInfoMap)
363 {
364 try
365 {
366 if (!f.exists())
367 return;
368
369 if (f.isFile())
370 {
371 if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
372 {
373 String name = f.getCanonicalPath();
374 long lastModified = f.lastModified();
375 scanInfoMap.put(name, new Long(lastModified));
376 }
377 }
378 else if (f.isDirectory() && (_recursive || _scanDirs.contains(f)))
379 {
380 File[] files = f.listFiles();
381 for (int i=0;i<files.length;i++)
382 scanFile(files[i], scanInfoMap);
383 }
384 }
385 catch (IOException e)
386 {
387 Log.warn("Error scanning watched files", e);
388 }
389 }
390
391 private void warn(Object listener,String filename,Throwable th)
392 {
393 Log.warn(th);
394 Log.warn(listener+" failed on '"+filename);
395 }
396
397
398
399
400
401 private void reportAddition (String filename)
402 {
403 Iterator itor = _listeners.iterator();
404 while (itor.hasNext())
405 {
406 Object l = itor.next();
407 try
408 {
409 if (l instanceof DiscreteListener)
410 ((DiscreteListener)l).fileAdded(filename);
411 }
412 catch (Exception e)
413 {
414 warn(l,filename,e);
415 }
416 catch (Error e)
417 {
418 warn(l,filename,e);
419 }
420 }
421 }
422
423
424
425
426
427
428 private void reportRemoval (String filename)
429 {
430 Iterator itor = _listeners.iterator();
431 while (itor.hasNext())
432 {
433 Object l = itor.next();
434 try
435 {
436 if (l instanceof DiscreteListener)
437 ((DiscreteListener)l).fileRemoved(filename);
438 }
439 catch (Exception e)
440 {
441 warn(l,filename,e);
442 }
443 catch (Error e)
444 {
445 warn(l,filename,e);
446 }
447 }
448 }
449
450
451
452
453
454
455 private void reportChange (String filename)
456 {
457 Iterator itor = _listeners.iterator();
458 while (itor.hasNext())
459 {
460 Object l = itor.next();
461 try
462 {
463 if (l instanceof DiscreteListener)
464 ((DiscreteListener)l).fileChanged(filename);
465 }
466 catch (Exception e)
467 {
468 warn(l,filename,e);
469 }
470 catch (Error e)
471 {
472 warn(l,filename,e);
473 }
474 }
475 }
476
477 private void reportBulkChanges (List filenames)
478 {
479 Iterator itor = _listeners.iterator();
480 while (itor.hasNext())
481 {
482 Object l = itor.next();
483 try
484 {
485 if (l instanceof BulkListener)
486 ((BulkListener)l).filesChanged(filenames);
487 }
488 catch (Exception e)
489 {
490 warn(l,filenames.toString(),e);
491 }
492 catch (Error e)
493 {
494 warn(l,filenames.toString(),e);
495 }
496 }
497 }
498
499 }