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 try
360 {
361 scanFile(dir.getCanonicalFile(), _currentScan,0);
362 }
363 catch (IOException e)
364 {
365 Log.warn("Error scanning files.", e);
366 }
367 }
368 }
369
370
371
372
373
374
375
376
377 public void reportDifferences (Map<String,Long> currentScan, Map<String,Long> oldScan)
378 {
379 List<String> bulkChanges = new ArrayList<String>();
380
381 Set<String> oldScanKeys = new HashSet<String>(oldScan.keySet());
382 Iterator<Entry<String, Long>> itor = currentScan.entrySet().iterator();
383 while (itor.hasNext())
384 {
385 Map.Entry<String, Long> entry = itor.next();
386 if (!oldScanKeys.contains(entry.getKey()))
387 {
388 Log.debug("File added: "+entry.getKey());
389 reportAddition ((String)entry.getKey());
390 bulkChanges.add(entry.getKey());
391 }
392 else if (!oldScan.get(entry.getKey()).equals(entry.getValue()))
393 {
394 Log.debug("File changed: "+entry.getKey());
395 reportChange((String)entry.getKey());
396 oldScanKeys.remove(entry.getKey());
397 bulkChanges.add(entry.getKey());
398 }
399 else
400 oldScanKeys.remove(entry.getKey());
401 }
402
403 if (!oldScanKeys.isEmpty())
404 {
405
406 Iterator<String> keyItor = oldScanKeys.iterator();
407 while (keyItor.hasNext())
408 {
409 String filename = (String)keyItor.next();
410 Log.debug("File removed: "+filename);
411 reportRemoval(filename);
412 bulkChanges.add(filename);
413 }
414 }
415
416 if (!bulkChanges.isEmpty())
417 reportBulkChanges(bulkChanges);
418 }
419
420
421
422
423
424
425
426
427 private void scanFile (File f, Map<String,Long> scanInfoMap, int depth)
428 {
429 try
430 {
431 if (!f.exists())
432 return;
433
434 if (f.isFile() || depth>0&& _reportDirs && f.isDirectory())
435 {
436 if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
437 {
438 String name = f.getCanonicalPath();
439 long lastModified = f.lastModified();
440 scanInfoMap.put(name, new Long(lastModified));
441 }
442 }
443
444
445 if (f.isDirectory() && (depth<_scanDepth || _scanDepth==-1 || _scanDirs.contains(f)))
446 {
447 File[] files = f.listFiles();
448 for (int i=0;i<files.length;i++)
449 scanFile(files[i], scanInfoMap,depth+1);
450 }
451 }
452 catch (IOException e)
453 {
454 Log.warn("Error scanning watched files", e);
455 }
456 }
457
458 private void warn(Object listener,String filename,Throwable th)
459 {
460 Log.warn(th);
461 Log.warn(listener+" failed on '"+filename);
462 }
463
464
465
466
467
468 private void reportAddition (String filename)
469 {
470 Iterator<Listener> itor = _listeners.iterator();
471 while (itor.hasNext())
472 {
473 Listener l = itor.next();
474 try
475 {
476 if (l instanceof DiscreteListener)
477 ((DiscreteListener)l).fileAdded(filename);
478 }
479 catch (Exception e)
480 {
481 warn(l,filename,e);
482 }
483 catch (Error e)
484 {
485 warn(l,filename,e);
486 }
487 }
488 }
489
490
491
492
493
494
495 private void reportRemoval (String filename)
496 {
497 Iterator<Listener> itor = _listeners.iterator();
498 while (itor.hasNext())
499 {
500 Object l = itor.next();
501 try
502 {
503 if (l instanceof DiscreteListener)
504 ((DiscreteListener)l).fileRemoved(filename);
505 }
506 catch (Exception e)
507 {
508 warn(l,filename,e);
509 }
510 catch (Error e)
511 {
512 warn(l,filename,e);
513 }
514 }
515 }
516
517
518
519
520
521
522 private void reportChange (String filename)
523 {
524 Iterator<Listener> itor = _listeners.iterator();
525 while (itor.hasNext())
526 {
527 Listener l = itor.next();
528 try
529 {
530 if (l instanceof DiscreteListener)
531 ((DiscreteListener)l).fileChanged(filename);
532 }
533 catch (Exception e)
534 {
535 warn(l,filename,e);
536 }
537 catch (Error e)
538 {
539 warn(l,filename,e);
540 }
541 }
542 }
543
544 private void reportBulkChanges (List<String> filenames)
545 {
546 Iterator<Listener> itor = _listeners.iterator();
547 while (itor.hasNext())
548 {
549 Listener l = itor.next();
550 try
551 {
552 if (l instanceof BulkListener)
553 ((BulkListener)l).filesChanged(filenames);
554 }
555 catch (Exception e)
556 {
557 warn(l,filenames.toString(),e);
558 }
559 catch (Error e)
560 {
561 warn(l,filenames.toString(),e);
562 }
563 }
564 }
565
566 }