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