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 Timer _timer;
54 private TimerTask _task;
55 private boolean _recursive=true;
56
57
58
59
60
61
62
63 public interface Listener
64 {
65 }
66
67
68 public interface DiscreteListener extends Listener
69 {
70 public void fileChanged (String filename) throws Exception;
71 public void fileAdded (String filename) throws Exception;
72 public void fileRemoved (String filename) throws Exception;
73 }
74
75
76 public interface BulkListener extends Listener
77 {
78 public void filesChanged (List filenames) throws Exception;
79 }
80
81
82
83
84
85 public Scanner ()
86 {
87 }
88
89
90
91
92
93 public int getScanInterval()
94 {
95 return _scanInterval;
96 }
97
98
99
100
101
102 public synchronized void setScanInterval(int scanInterval)
103 {
104 this._scanInterval = scanInterval;
105 schedule();
106 }
107
108
109
110
111
112
113 @Deprecated
114 public void setScanDir (File dir)
115 {
116 _scanDirs = new ArrayList();
117 _scanDirs.add(dir);
118 }
119
120
121
122
123
124
125 @Deprecated
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 @Override
234 public void run() { scan(); }
235 };
236 }
237
238 public Timer newTimer ()
239 {
240 return new Timer("Scanner-"+__scannerId++, true);
241 }
242
243 public void schedule ()
244 {
245 if (_running)
246 {
247 if (_timer!=null)
248 _timer.cancel();
249 if (_task!=null)
250 _task.cancel();
251 if (getScanInterval() > 0)
252 {
253 _timer = newTimer();
254 _task = newTimerTask();
255 _timer.schedule(_task, 1000L*getScanInterval(),1000L*getScanInterval());
256 }
257 }
258 }
259
260
261
262 public synchronized void stop ()
263 {
264 if (_running)
265 {
266 _running = false;
267 if (_timer!=null)
268 _timer.cancel();
269 if (_task!=null)
270 _task.cancel();
271 _task=null;
272 _timer=null;
273 }
274 }
275
276
277
278
279 public void scan ()
280 {
281 scanFiles();
282 reportDifferences(_currentScan, _prevScan);
283 _prevScan.clear();
284 _prevScan.putAll(_currentScan);
285 }
286
287
288
289
290
291 public void scanFiles ()
292 {
293 if (_scanDirs==null)
294 return;
295
296 _currentScan.clear();
297 Iterator<File> itor = _scanDirs.iterator();
298 while (itor.hasNext())
299 {
300 File dir = itor.next();
301
302 if ((dir != null) && (dir.exists()))
303 scanFile(dir, _currentScan);
304 }
305 }
306
307
308
309
310
311
312
313
314 public void reportDifferences (Map currentScan, Map oldScan)
315 {
316 List bulkChanges = new ArrayList();
317
318 Set oldScanKeys = new HashSet(oldScan.keySet());
319 Iterator itor = currentScan.entrySet().iterator();
320 while (itor.hasNext())
321 {
322 Map.Entry entry = (Map.Entry)itor.next();
323 if (!oldScanKeys.contains(entry.getKey()))
324 {
325 Log.debug("File added: "+entry.getKey());
326 reportAddition ((String)entry.getKey());
327 bulkChanges.add(entry.getKey());
328 }
329 else if (!oldScan.get(entry.getKey()).equals(entry.getValue()))
330 {
331 Log.debug("File changed: "+entry.getKey());
332 reportChange((String)entry.getKey());
333 oldScanKeys.remove(entry.getKey());
334 bulkChanges.add(entry.getKey());
335 }
336 else
337 oldScanKeys.remove(entry.getKey());
338 }
339
340 if (!oldScanKeys.isEmpty())
341 {
342
343 Iterator keyItor = oldScanKeys.iterator();
344 while (keyItor.hasNext())
345 {
346 String filename = (String)keyItor.next();
347 Log.debug("File removed: "+filename);
348 reportRemoval(filename);
349 bulkChanges.add(filename);
350 }
351 }
352
353 if (!bulkChanges.isEmpty())
354 reportBulkChanges(bulkChanges);
355 }
356
357
358
359
360
361
362
363
364 private void scanFile (File f, Map scanInfoMap)
365 {
366 try
367 {
368 if (!f.exists())
369 return;
370
371 if (f.isFile())
372 {
373 if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
374 {
375 String name = f.getCanonicalPath();
376 long lastModified = f.lastModified();
377 scanInfoMap.put(name, new Long(lastModified));
378 }
379 }
380 else if (f.isDirectory() && (_recursive || _scanDirs.contains(f)))
381 {
382 File[] files = f.listFiles();
383 for (int i=0;i<files.length;i++)
384 scanFile(files[i], scanInfoMap);
385 }
386 }
387 catch (IOException e)
388 {
389 Log.warn("Error scanning watched files", e);
390 }
391 }
392
393 private void warn(Object listener,String filename,Throwable th)
394 {
395 Log.warn(th);
396 Log.warn(listener+" failed on '"+filename);
397 }
398
399
400
401
402
403 private void reportAddition (String filename)
404 {
405 Iterator itor = _listeners.iterator();
406 while (itor.hasNext())
407 {
408 Object l = itor.next();
409 try
410 {
411 if (l instanceof DiscreteListener)
412 ((DiscreteListener)l).fileAdded(filename);
413 }
414 catch (Exception e)
415 {
416 warn(l,filename,e);
417 }
418 catch (Error e)
419 {
420 warn(l,filename,e);
421 }
422 }
423 }
424
425
426
427
428
429
430 private void reportRemoval (String filename)
431 {
432 Iterator itor = _listeners.iterator();
433 while (itor.hasNext())
434 {
435 Object l = itor.next();
436 try
437 {
438 if (l instanceof DiscreteListener)
439 ((DiscreteListener)l).fileRemoved(filename);
440 }
441 catch (Exception e)
442 {
443 warn(l,filename,e);
444 }
445 catch (Error e)
446 {
447 warn(l,filename,e);
448 }
449 }
450 }
451
452
453
454
455
456
457 private void reportChange (String filename)
458 {
459 Iterator itor = _listeners.iterator();
460 while (itor.hasNext())
461 {
462 Object l = itor.next();
463 try
464 {
465 if (l instanceof DiscreteListener)
466 ((DiscreteListener)l).fileChanged(filename);
467 }
468 catch (Exception e)
469 {
470 warn(l,filename,e);
471 }
472 catch (Error e)
473 {
474 warn(l,filename,e);
475 }
476 }
477 }
478
479 private void reportBulkChanges (List filenames)
480 {
481 Iterator itor = _listeners.iterator();
482 while (itor.hasNext())
483 {
484 Object l = itor.next();
485 try
486 {
487 if (l instanceof BulkListener)
488 ((BulkListener)l).filesChanged(filenames);
489 }
490 catch (Exception e)
491 {
492 warn(l,filenames.toString(),e);
493 }
494 catch (Error e)
495 {
496 warn(l,filenames.toString(),e);
497 }
498 }
499 }
500
501 }