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