1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.impl;
18
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Stack;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.apache.commons.vfs2.FileListener;
26 import org.apache.commons.vfs2.FileMonitor;
27 import org.apache.commons.vfs2.FileName;
28 import org.apache.commons.vfs2.FileObject;
29 import org.apache.commons.vfs2.FileSystemException;
30 import org.apache.commons.vfs2.provider.AbstractFileSystem;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public class DefaultFileMonitor implements Runnable, FileMonitor {
71 private static final Log LOG = LogFactory.getLog(DefaultFileMonitor.class);
72
73 private static final long DEFAULT_DELAY = 1000;
74
75 private static final int DEFAULT_MAX_FILES = 1000;
76
77
78
79
80 private final Map<FileName, FileMonitorAgent> monitorMap = new HashMap<>();
81
82
83
84
85 private Thread monitorThread;
86
87
88
89
90 private final Stack<FileObject> deleteStack = new Stack<>();
91
92
93
94
95 private final Stack<FileObject> addStack = new Stack<>();
96
97
98
99
100 private volatile boolean shouldRun = true;
101
102
103
104
105 private boolean recursive;
106
107
108
109
110 private long delay = DEFAULT_DELAY;
111
112
113
114
115 private int checksPerRun = DEFAULT_MAX_FILES;
116
117
118
119
120 private final FileListener listener;
121
122
123
124
125
126
127 public DefaultFileMonitor(final FileListener listener) {
128 this.listener = listener;
129 }
130
131
132
133
134
135
136 public boolean isRecursive() {
137 return this.recursive;
138 }
139
140
141
142
143
144
145 public void setRecursive(final boolean newRecursive) {
146 this.recursive = newRecursive;
147 }
148
149
150
151
152
153
154 FileListener getFileListener() {
155 return this.listener;
156 }
157
158
159
160
161
162
163 @Override
164 public void addFile(final FileObject file) {
165 synchronized (this.monitorMap) {
166 if (this.monitorMap.get(file.getName()) == null) {
167 this.monitorMap.put(file.getName(), new FileMonitorAgent(this, file));
168
169 try {
170 if (this.listener != null) {
171 file.getFileSystem().addListener(file, this.listener);
172 }
173
174 if (file.getType().hasChildren() && this.recursive) {
175
176 final FileObject[] children = file.getChildren();
177 for (final FileObject element : children) {
178 this.addFile(element);
179 }
180 }
181
182 } catch (final FileSystemException fse) {
183 LOG.error(fse.getLocalizedMessage(), fse);
184 }
185
186 }
187 }
188 }
189
190
191
192
193
194
195 @Override
196 public void removeFile(final FileObject file) {
197 synchronized (this.monitorMap) {
198 final FileName fn = file.getName();
199 if (this.monitorMap.get(fn) != null) {
200 FileObject parent;
201 try {
202 parent = file.getParent();
203 } catch (final FileSystemException fse) {
204 parent = null;
205 }
206
207 this.monitorMap.remove(fn);
208
209 if (parent != null) {
210 final FileMonitorAgent parentAgent = this.monitorMap.get(parent.getName());
211 if (parentAgent != null) {
212 parentAgent.resetChildrenList();
213 }
214 }
215 }
216 }
217 }
218
219
220
221
222
223
224 protected void queueRemoveFile(final FileObject file) {
225 this.deleteStack.push(file);
226 }
227
228
229
230
231
232
233 public long getDelay() {
234 return delay;
235 }
236
237
238
239
240
241
242 public void setDelay(final long delay) {
243 this.delay = delay > 0 ? delay : DEFAULT_DELAY;
244 }
245
246
247
248
249
250
251 public int getChecksPerRun() {
252 return checksPerRun;
253 }
254
255
256
257
258
259
260 public void setChecksPerRun(final int checksPerRun) {
261 this.checksPerRun = checksPerRun;
262 }
263
264
265
266
267
268
269 protected void queueAddFile(final FileObject file) {
270 this.addStack.push(file);
271 }
272
273
274
275
276 public void start() {
277 if (this.monitorThread == null) {
278 this.monitorThread = new Thread(this);
279 this.monitorThread.setDaemon(true);
280 this.monitorThread.setPriority(Thread.MIN_PRIORITY);
281 }
282 this.monitorThread.start();
283 }
284
285
286
287
288 public void stop() {
289 this.shouldRun = false;
290 if (this.monitorThread != null) {
291 this.monitorThread.interrupt();
292 try {
293 this.monitorThread.join();
294 } catch (final InterruptedException e) {
295
296 }
297 this.monitorThread = null;
298 }
299 }
300
301
302
303
304 @Override
305 public void run() {
306 mainloop: while (!monitorThread.isInterrupted() && this.shouldRun) {
307
308 final Object[] fileNames;
309 synchronized (this.monitorMap) {
310 fileNames = this.monitorMap.keySet().toArray();
311 }
312 for (int iterFileNames = 0; iterFileNames < fileNames.length; iterFileNames++) {
313 final FileName./../../../org/apache/commons/vfs2/FileName.html#FileName">FileName fileName = (FileName) fileNames[iterFileNames];
314 final FileMonitorAgent agent;
315 synchronized (this.monitorMap) {
316 agent = this.monitorMap.get(fileName);
317 }
318 if (agent != null) {
319 agent.check();
320 }
321
322 if (getChecksPerRun() > 0 && (iterFileNames + 1) % getChecksPerRun() == 0) {
323 try {
324 Thread.sleep(getDelay());
325 } catch (final InterruptedException e) {
326
327 }
328 }
329
330 if (monitorThread.isInterrupted() || !this.shouldRun) {
331 continue mainloop;
332 }
333 }
334
335 while (!this.addStack.empty()) {
336 this.addFile(this.addStack.pop());
337 }
338
339 while (!this.deleteStack.empty()) {
340 this.removeFile(this.deleteStack.pop());
341 }
342
343 try {
344 Thread.sleep(getDelay());
345 } catch (final InterruptedException e) {
346 continue;
347 }
348 }
349
350 this.shouldRun = true;
351 }
352
353
354
355
356 private static final class FileMonitorAgent {
357 private final FileObject fileObject;
358 private final DefaultFileMonitor defaultFileMonitor;
359
360 private boolean exists;
361 private long timestamp;
362 private Map<FileName, Object> children;
363
364 private FileMonitorAgent(final DefaultFileMonitor fm, final FileObject file) {
365 this.defaultFileMonitor = fm;
366 this.fileObject = file;
367
368 this.refresh();
369 this.resetChildrenList();
370
371 try {
372 this.exists = this.fileObject.exists();
373 } catch (final FileSystemException fse) {
374 this.exists = false;
375 this.timestamp = -1;
376 }
377
378 if (this.exists) {
379 try {
380 this.timestamp = this.fileObject.getContent().getLastModifiedTime();
381 } catch (final FileSystemException fse) {
382 this.timestamp = -1;
383 }
384 }
385 }
386
387 private void resetChildrenList() {
388 try {
389 if (this.fileObject.getType().hasChildren()) {
390 this.children = new HashMap<>();
391 final FileObject[] childrenList = this.fileObject.getChildren();
392 for (final FileObject element : childrenList) {
393 this.children.put(element.getName(), new Object());
394 }
395 }
396 } catch (final FileSystemException fse) {
397 this.children = null;
398 }
399 }
400
401
402
403
404 private void refresh() {
405 try {
406 this.fileObject.refresh();
407 } catch (final FileSystemException fse) {
408 LOG.error(fse.getLocalizedMessage(), fse);
409 }
410 }
411
412
413
414
415
416
417
418 private void fireAllCreate(final FileObject child) {
419
420 if (this.defaultFileMonitor.getFileListener() != null) {
421 child.getFileSystem().addListener(child, this.defaultFileMonitor.getFileListener());
422 }
423
424 ((AbstractFileSystem) child.getFileSystem()).fireFileCreated(child);
425
426
427 if (this.defaultFileMonitor.getFileListener() != null) {
428 child.getFileSystem().removeListener(child, this.defaultFileMonitor.getFileListener());
429 }
430
431 this.defaultFileMonitor.queueAddFile(child);
432
433 try {
434 if (this.defaultFileMonitor.isRecursive() && child.getType().hasChildren()) {
435 final FileObject[] newChildren = child.getChildren();
436 for (final FileObject element : newChildren) {
437 fireAllCreate(element);
438 }
439 }
440 } catch (final FileSystemException fse) {
441 LOG.error(fse.getLocalizedMessage(), fse);
442 }
443 }
444
445
446
447
448 private void checkForNewChildren() {
449 try {
450 if (this.fileObject.getType().hasChildren()) {
451 final FileObject[] newChildren = this.fileObject.getChildren();
452 if (this.children != null) {
453
454 final Map<FileName, Object> newChildrenMap = new HashMap<>();
455 final Stack<FileObject> missingChildren = new Stack<>();
456
457 for (final FileObject element : newChildren) {
458 newChildrenMap.put(element.getName(), new Object());
459
460 if (!this.children.containsKey(element.getName())) {
461 missingChildren.push(element);
462 }
463 }
464
465 this.children = newChildrenMap;
466
467
468 if (!missingChildren.empty()) {
469
470 while (!missingChildren.empty()) {
471 final FileObject child = missingChildren.pop();
472 this.fireAllCreate(child);
473 }
474 }
475
476 } else if (newChildren.length > 0) {
477
478 this.children = new HashMap<>();
479 for (final FileObject element : newChildren) {
480 this.children.put(element.getName(), new Object());
481 this.fireAllCreate(element);
482 }
483 }
484 }
485 } catch (final FileSystemException fse) {
486 LOG.error(fse.getLocalizedMessage(), fse);
487 }
488 }
489
490 private void check() {
491 this.refresh();
492
493 try {
494
495 if (this.exists && !this.fileObject.exists()) {
496 this.exists = this.fileObject.exists();
497 this.timestamp = -1;
498
499
500
501 ((AbstractFileSystem) this.fileObject.getFileSystem()).fireFileDeleted(this.fileObject);
502
503
504 if (this.defaultFileMonitor.getFileListener() != null) {
505 this.fileObject.getFileSystem().removeListener(this.fileObject, this.defaultFileMonitor.getFileListener());
506 }
507
508
509 this.defaultFileMonitor.queueRemoveFile(this.fileObject);
510 } else if (this.exists && this.fileObject.exists()) {
511
512
513 if (this.timestamp != this.fileObject.getContent().getLastModifiedTime()) {
514 this.timestamp = this.fileObject.getContent().getLastModifiedTime();
515
516
517
518
519 if (!this.fileObject.getType().hasChildren()) {
520 ((AbstractFileSystem) this.fileObject.getFileSystem()).fireFileChanged(this.fileObject);
521 }
522 }
523
524 } else if (!this.exists && this.fileObject.exists()) {
525 this.exists = this.fileObject.exists();
526 this.timestamp = this.fileObject.getContent().getLastModifiedTime();
527
528
529 if (!this.fileObject.getType().hasChildren()) {
530 ((AbstractFileSystem) this.fileObject.getFileSystem()).fireFileCreated(this.fileObject);
531 }
532 }
533
534 this.checkForNewChildren();
535
536 } catch (final FileSystemException fse) {
537 LOG.error(fse.getLocalizedMessage(), fse);
538 }
539 }
540
541 }
542 }