1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.eclipse.jetty.util;
16
17 import java.io.File;
18 import java.io.FilenameFilter;
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.Timer;
29 import java.util.TimerTask;
30
31 import org.eclipse.jetty.util.log.Log;
32
33
34
35
36
37
38
39
40
41
42 public class Scanner
43 {
44 private static int __scannerId=0;
45 private int _scanInterval;
46 private final List _listeners = Collections.synchronizedList(new ArrayList());
47 private final Map _prevScan = new HashMap();
48 private final Map _currentScan = new HashMap();
49 private FilenameFilter _filter;
50 private List<File> _scanDirs;
51 private volatile boolean _running = false;
52 private boolean _reportExisting = true;
53 private boolean _reportDirs = 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 _scanInterval = scanInterval;
106 schedule();
107 }
108
109
110
111
112
113
114 @Deprecated
115 public void setScanDir (File dir)
116 {
117 _scanDirs = new ArrayList();
118 _scanDirs.add(dir);
119 }
120
121
122
123
124
125
126 @Deprecated
127 public File getScanDir ()
128 {
129 return (_scanDirs==null?null:(File)_scanDirs.get(0));
130 }
131
132 public void setScanDirs (List dirs)
133 {
134 _scanDirs = dirs;
135 }
136
137 public List getScanDirs ()
138 {
139 return _scanDirs;
140 }
141
142 public void setRecursive (boolean recursive)
143 {
144 _recursive=recursive;
145 }
146
147 public boolean getRecursive ()
148 {
149 return _recursive;
150 }
151
152
153
154
155
156 public void setFilenameFilter (FilenameFilter filter)
157 {
158 _filter = filter;
159 }
160
161
162
163
164
165 public FilenameFilter getFilenameFilter ()
166 {
167 return _filter;
168 }
169
170
171
172
173
174
175
176
177 public void setReportExistingFilesOnStartup (boolean reportExisting)
178 {
179 _reportExisting = reportExisting;
180 }
181
182
183 public boolean getReportExistingFilesOnStartup()
184 {
185 return _reportExisting;
186 }
187
188
189
190
191
192 public void setReportDirs(boolean dirs)
193 {
194 _reportDirs=dirs;
195 }
196
197
198 public boolean getReportDirs()
199 {
200 return _reportDirs;
201 }
202
203
204
205
206
207
208 public synchronized void addListener (Listener listener)
209 {
210 if (listener == null)
211 return;
212 _listeners.add(listener);
213 }
214
215
216
217
218
219
220
221 public synchronized void removeListener (Listener listener)
222 {
223 if (listener == null)
224 return;
225 _listeners.remove(listener);
226 }
227
228
229
230
231
232 public synchronized void start ()
233 {
234 if (_running)
235 return;
236
237 _running = true;
238
239 if (_reportExisting)
240 {
241
242 scan();
243 }
244 else
245 {
246
247 scanFiles();
248 _prevScan.putAll(_currentScan);
249 }
250 schedule();
251 }
252
253 public TimerTask newTimerTask ()
254 {
255 return new TimerTask()
256 {
257 @Override
258 public void run() { scan(); }
259 };
260 }
261
262 public Timer newTimer ()
263 {
264 return new Timer("Scanner-"+__scannerId++, true);
265 }
266
267 public void schedule ()
268 {
269 if (_running)
270 {
271 if (_timer!=null)
272 _timer.cancel();
273 if (_task!=null)
274 _task.cancel();
275 if (getScanInterval() > 0)
276 {
277 _timer = newTimer();
278 _task = newTimerTask();
279 _timer.schedule(_task, 1000L*getScanInterval(),1000L*getScanInterval());
280 }
281 }
282 }
283
284
285
286 public synchronized void stop ()
287 {
288 if (_running)
289 {
290 _running = false;
291 if (_timer!=null)
292 _timer.cancel();
293 if (_task!=null)
294 _task.cancel();
295 _task=null;
296 _timer=null;
297 }
298 }
299
300
301
302
303 public synchronized void scan ()
304 {
305 scanFiles();
306 reportDifferences(_currentScan, _prevScan);
307 _prevScan.clear();
308 _prevScan.putAll(_currentScan);
309 }
310
311
312
313
314
315 public synchronized void scanFiles ()
316 {
317 if (_scanDirs==null)
318 return;
319
320 _currentScan.clear();
321 Iterator<File> itor = _scanDirs.iterator();
322 while (itor.hasNext())
323 {
324 File dir = itor.next();
325
326 if ((dir != null) && (dir.exists()))
327 scanFile(dir, _currentScan,0);
328 }
329 }
330
331
332
333
334
335
336
337
338 public void reportDifferences (Map currentScan, Map oldScan)
339 {
340 List bulkChanges = new ArrayList();
341
342 Set oldScanKeys = new HashSet(oldScan.keySet());
343 Iterator itor = currentScan.entrySet().iterator();
344 while (itor.hasNext())
345 {
346 Map.Entry entry = (Map.Entry)itor.next();
347 if (!oldScanKeys.contains(entry.getKey()))
348 {
349 Log.debug("File added: "+entry.getKey());
350 reportAddition ((String)entry.getKey());
351 bulkChanges.add(entry.getKey());
352 }
353 else if (!oldScan.get(entry.getKey()).equals(entry.getValue()))
354 {
355 Log.debug("File changed: "+entry.getKey());
356 reportChange((String)entry.getKey());
357 oldScanKeys.remove(entry.getKey());
358 bulkChanges.add(entry.getKey());
359 }
360 else
361 oldScanKeys.remove(entry.getKey());
362 }
363
364 if (!oldScanKeys.isEmpty())
365 {
366
367 Iterator keyItor = oldScanKeys.iterator();
368 while (keyItor.hasNext())
369 {
370 String filename = (String)keyItor.next();
371 Log.debug("File removed: "+filename);
372 reportRemoval(filename);
373 bulkChanges.add(filename);
374 }
375 }
376
377 if (!bulkChanges.isEmpty())
378 reportBulkChanges(bulkChanges);
379 }
380
381
382
383
384
385
386
387
388 private void scanFile (File f, Map scanInfoMap, int depth)
389 {
390 try
391 {
392 if (!f.exists())
393 return;
394
395 if (f.isFile() || depth>0&& _reportDirs && f.isDirectory())
396 {
397 if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
398 {
399 String name = f.getCanonicalPath();
400 long lastModified = f.lastModified();
401 scanInfoMap.put(name, new Long(lastModified));
402 }
403 }
404
405 if (f.isDirectory() && (_recursive || _scanDirs.contains(f)))
406 {
407 File[] files = f.listFiles();
408 for (int i=0;i<files.length;i++)
409 scanFile(files[i], scanInfoMap,depth+1);
410 }
411 }
412 catch (IOException e)
413 {
414 Log.warn("Error scanning watched files", e);
415 }
416 }
417
418 private void warn(Object listener,String filename,Throwable th)
419 {
420 Log.warn(th);
421 Log.warn(listener+" failed on '"+filename);
422 }
423
424
425
426
427
428 private void reportAddition (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).fileAdded(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 reportRemoval (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).fileRemoved(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
478
479
480
481
482 private void reportChange (String filename)
483 {
484 Iterator itor = _listeners.iterator();
485 while (itor.hasNext())
486 {
487 Object l = itor.next();
488 try
489 {
490 if (l instanceof DiscreteListener)
491 ((DiscreteListener)l).fileChanged(filename);
492 }
493 catch (Exception e)
494 {
495 warn(l,filename,e);
496 }
497 catch (Error e)
498 {
499 warn(l,filename,e);
500 }
501 }
502 }
503
504 private void reportBulkChanges (List filenames)
505 {
506 Iterator itor = _listeners.iterator();
507 while (itor.hasNext())
508 {
509 Object l = itor.next();
510 try
511 {
512 if (l instanceof BulkListener)
513 ((BulkListener)l).filesChanged(filenames);
514 }
515 catch (Exception e)
516 {
517 warn(l,filenames.toString(),e);
518 }
519 catch (Error e)
520 {
521 warn(l,filenames.toString(),e);
522 }
523 }
524 }
525
526 }