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 public synchronized void scanFiles ()
315 {
316 if (_scanDirs==null)
317 return;
318
319 _currentScan.clear();
320 Iterator<File> itor = _scanDirs.iterator();
321 while (itor.hasNext())
322 {
323 File dir = itor.next();
324
325 if ((dir != null) && (dir.exists()))
326 scanFile(dir, _currentScan,0);
327 }
328 }
329
330
331
332
333
334
335
336
337 public void reportDifferences (Map currentScan, Map oldScan)
338 {
339 List bulkChanges = new ArrayList();
340
341 Set oldScanKeys = new HashSet(oldScan.keySet());
342 Iterator itor = currentScan.entrySet().iterator();
343 while (itor.hasNext())
344 {
345 Map.Entry entry = (Map.Entry)itor.next();
346 if (!oldScanKeys.contains(entry.getKey()))
347 {
348 Log.debug("File added: "+entry.getKey());
349 reportAddition ((String)entry.getKey());
350 bulkChanges.add(entry.getKey());
351 }
352 else if (!oldScan.get(entry.getKey()).equals(entry.getValue()))
353 {
354 Log.debug("File changed: "+entry.getKey());
355 reportChange((String)entry.getKey());
356 oldScanKeys.remove(entry.getKey());
357 bulkChanges.add(entry.getKey());
358 }
359 else
360 oldScanKeys.remove(entry.getKey());
361 }
362
363 if (!oldScanKeys.isEmpty())
364 {
365
366 Iterator keyItor = oldScanKeys.iterator();
367 while (keyItor.hasNext())
368 {
369 String filename = (String)keyItor.next();
370 Log.debug("File removed: "+filename);
371 reportRemoval(filename);
372 bulkChanges.add(filename);
373 }
374 }
375
376 if (!bulkChanges.isEmpty())
377 reportBulkChanges(bulkChanges);
378 }
379
380
381
382
383
384
385
386
387 private void scanFile (File f, Map scanInfoMap, int depth)
388 {
389 try
390 {
391 if (!f.exists())
392 return;
393
394 if (f.isFile() || depth>0&& _reportDirs && f.isDirectory())
395 {
396 if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
397 {
398 String name = f.getCanonicalPath();
399 long lastModified = f.lastModified();
400 scanInfoMap.put(name, new Long(lastModified));
401 }
402 }
403
404 if (f.isDirectory() && (_recursive || _scanDirs.contains(f)))
405 {
406 File[] files = f.listFiles();
407 for (int i=0;i<files.length;i++)
408 scanFile(files[i], scanInfoMap,depth+1);
409 }
410 }
411 catch (IOException e)
412 {
413 Log.warn("Error scanning watched files", e);
414 }
415 }
416
417 private void warn(Object listener,String filename,Throwable th)
418 {
419 Log.warn(th);
420 Log.warn(listener+" failed on '"+filename);
421 }
422
423
424
425
426
427 private void reportAddition (String filename)
428 {
429 Iterator itor = _listeners.iterator();
430 while (itor.hasNext())
431 {
432 Object l = itor.next();
433 try
434 {
435 if (l instanceof DiscreteListener)
436 ((DiscreteListener)l).fileAdded(filename);
437 }
438 catch (Exception e)
439 {
440 warn(l,filename,e);
441 }
442 catch (Error e)
443 {
444 warn(l,filename,e);
445 }
446 }
447 }
448
449
450
451
452
453
454 private void reportRemoval (String filename)
455 {
456 Iterator itor = _listeners.iterator();
457 while (itor.hasNext())
458 {
459 Object l = itor.next();
460 try
461 {
462 if (l instanceof DiscreteListener)
463 ((DiscreteListener)l).fileRemoved(filename);
464 }
465 catch (Exception e)
466 {
467 warn(l,filename,e);
468 }
469 catch (Error e)
470 {
471 warn(l,filename,e);
472 }
473 }
474 }
475
476
477
478
479
480
481 private void reportChange (String filename)
482 {
483 Iterator itor = _listeners.iterator();
484 while (itor.hasNext())
485 {
486 Object l = itor.next();
487 try
488 {
489 if (l instanceof DiscreteListener)
490 ((DiscreteListener)l).fileChanged(filename);
491 }
492 catch (Exception e)
493 {
494 warn(l,filename,e);
495 }
496 catch (Error e)
497 {
498 warn(l,filename,e);
499 }
500 }
501 }
502
503 private void reportBulkChanges (List filenames)
504 {
505 Iterator itor = _listeners.iterator();
506 while (itor.hasNext())
507 {
508 Object l = itor.next();
509 try
510 {
511 if (l instanceof BulkListener)
512 ((BulkListener)l).filesChanged(filenames);
513 }
514 catch (Exception e)
515 {
516 warn(l,filenames.toString(),e);
517 }
518 catch (Error e)
519 {
520 warn(l,filenames.toString(),e);
521 }
522 }
523 }
524
525 }