Source Code Cross Referenced for DefaultFileMonitor.java in  » Library » Apache-commons-vfs-20070724-src » org » apache » commons » vfs » impl » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Library » Apache commons vfs 20070724 src » org.apache.commons.vfs.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Licensed to the Apache Software Foundation (ASF) under one or more
003:         * contributor license agreements.  See the NOTICE file distributed with
004:         * this work for additional information regarding copyright ownership.
005:         * The ASF licenses this file to You under the Apache License, Version 2.0
006:         * (the "License"); you may not use this file except in compliance with
007:         * the License.  You may obtain a copy of the License at
008:         *
009:         *      http://www.apache.org/licenses/LICENSE-2.0
010:         *
011:         * Unless required by applicable law or agreed to in writing, software
012:         * distributed under the License is distributed on an "AS IS" BASIS,
013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         * See the License for the specific language governing permissions and
015:         * limitations under the License.
016:         */
017:        package org.apache.commons.vfs.impl;
018:
019:        import org.apache.commons.logging.Log;
020:        import org.apache.commons.logging.LogFactory;
021:        import org.apache.commons.vfs.FileListener;
022:        import org.apache.commons.vfs.FileMonitor;
023:        import org.apache.commons.vfs.FileName;
024:        import org.apache.commons.vfs.FileObject;
025:        import org.apache.commons.vfs.FileSystemException;
026:        import org.apache.commons.vfs.provider.AbstractFileSystem;
027:
028:        import java.util.HashMap;
029:        import java.util.Map;
030:        import java.util.Stack;
031:
032:        /**
033:         * A polling {@link FileMonitor} implementation.<br />
034:         * <br />
035:         * The DefaultFileMonitor is a Thread based polling file system monitor with a 1
036:         * second delay.<br />
037:         * <br />
038:         * <b>Design:</b>
039:         * <p/>
040:         * There is a Map of monitors known as FileMonitorAgents. With the thread running,
041:         * each FileMonitorAgent object is asked to "check" on the file it is
042:         * responsible for.
043:         * To do this check, the cache is cleared.
044:         * </p>
045:         * <ul>
046:         * <li>If the file existed before the refresh and it no longer exists, a delete
047:         * event is fired.</li>
048:         * <li>If the file existed before the refresh and it still exists, check the
049:         * last modified timestamp to see if that has changed.</li>
050:         * <li>If it has, fire a change event.</li>
051:         * </ul>
052:         * <p/>
053:         * With each file delete, the FileMonitorAgent of the parent is asked to
054:         * re-build its
055:         * list of children, so that they can be accurately checked when there are new
056:         * children.<br/>
057:         * New files are detected during each "check" as each file does a check for new
058:         * children.
059:         * If new children are found, create events are fired recursively if recursive
060:         * descent is
061:         * enabled.
062:         * </p>
063:         * <p/>
064:         * For performance reasons, added a delay that increases as the number of files
065:         * monitored
066:         * increases. The default is a delay of 1 second for every 1000 files processed.
067:         * </p>
068:         * <p/>
069:         * <br /><b>Example usage:</b><pre>
070:         * FileSystemManager fsManager = VFS.getManager();
071:         * FileObject listendir = fsManager.resolveFile("/home/username/monitored/");
072:         * <p/>
073:         * DefaultFileMonitor fm = new DefaultFileMonitor(new CustomFileListener());
074:         * fm.setRecursive(true);
075:         * fm.addFile(listendir);
076:         * fm.start();
077:         * </pre>
078:         * <i>(where CustomFileListener is a class that implements the FileListener
079:         * interface.)</i>
080:         *
081:         * @author <a href="mailto:xknight@users.sourceforge.net">Christopher Ottley</a>
082:         * @version $Revision: 537938 $ $Date: 2007-05-14 11:26:40 -0700 (Mon, 14 May 2007) $
083:         */
084:        public class DefaultFileMonitor implements  Runnable, FileMonitor {
085:            private final static Log log = LogFactory
086:                    .getLog(DefaultFileMonitor.class);
087:
088:            /**
089:             * Map from FileName to FileObject being monitored.
090:             */
091:            private final Map monitorMap = new HashMap();
092:
093:            /**
094:             * The low priority thread used for checking the files being monitored.
095:             */
096:            private Thread monitorThread;
097:
098:            /**
099:             * File objects to be removed from the monitor map.
100:             */
101:            private Stack deleteStack = new Stack();
102:
103:            /**
104:             * File objects to be added to the monitor map.
105:             */
106:            private Stack addStack = new Stack();
107:
108:            /**
109:             * A flag used to determine if the monitor thread should be running.
110:             */
111:            private boolean shouldRun = true;
112:
113:            /**
114:             * A flag used to determine if adding files to be monitored should be recursive.
115:             */
116:            private boolean recursive = false;
117:
118:            /**
119:             * Set the delay between checks
120:             */
121:            private long delay = 1000;
122:
123:            /**
124:             * Set the number of files to check until a delay will be inserted
125:             */
126:            private int checksPerRun = 1000;
127:
128:            /**
129:             * A listener object that if set, is notified on file creation and deletion.
130:             */
131:            private final FileListener listener;
132:
133:            public DefaultFileMonitor(final FileListener listener) {
134:                this .listener = listener;
135:            }
136:
137:            /**
138:             * Access method to get the recursive setting when adding files for monitoring.
139:             */
140:            public boolean isRecursive() {
141:                return this .recursive;
142:            }
143:
144:            /**
145:             * Access method to set the recursive setting when adding files for monitoring.
146:             */
147:            public void setRecursive(final boolean newRecursive) {
148:                this .recursive = newRecursive;
149:            }
150:
151:            /**
152:             * Access method to get the current FileListener object notified when there
153:             * are changes with the files added.
154:             */
155:            FileListener getFileListener() {
156:                return this .listener;
157:            }
158:
159:            /**
160:             * Adds a file to be monitored.
161:             */
162:            public void addFile(final FileObject file) {
163:                _addFile(file);
164:                try {
165:                    // add all direct children too
166:                    if (file.getType().hasChildren()) {
167:                        // Traverse the children
168:                        final FileObject[] children = file.getChildren();
169:                        for (int i = 0; i < children.length; i++) {
170:                            _addFile(children[i]);
171:                        }
172:                    }
173:                } catch (FileSystemException fse) {
174:                    log.error(fse.getLocalizedMessage(), fse);
175:                }
176:            }
177:
178:            /**
179:             * Adds a file to be monitored.
180:             */
181:            private void _addFile(final FileObject file) {
182:                synchronized (this .monitorMap) {
183:                    if (this .monitorMap.get(file.getName()) == null) {
184:                        this .monitorMap.put(file.getName(),
185:                                new FileMonitorAgent(this , file));
186:
187:                        try {
188:                            if (this .listener != null) {
189:                                file.getFileSystem().addListener(file,
190:                                        this .listener);
191:                            }
192:
193:                            if (file.getType().hasChildren() && this .recursive) {
194:                                // Traverse the children
195:                                final FileObject[] children = file
196:                                        .getChildren();
197:                                for (int i = 0; i < children.length; i++) {
198:                                    this .addFile(children[i]); // Add depth first
199:                                }
200:                            }
201:
202:                        } catch (FileSystemException fse) {
203:                            log.error(fse.getLocalizedMessage(), fse);
204:                        }
205:
206:                    }
207:                }
208:            }
209:
210:            /**
211:             * Removes a file from being monitored.
212:             */
213:            public void removeFile(final FileObject file) {
214:                synchronized (this .monitorMap) {
215:                    FileName fn = file.getName();
216:                    if (this .monitorMap.get(fn) != null) {
217:                        FileObject parent;
218:                        try {
219:                            parent = file.getParent();
220:                        } catch (FileSystemException fse) {
221:                            parent = null;
222:                        }
223:
224:                        this .monitorMap.remove(fn);
225:
226:                        if (parent != null) { // Not the root
227:                            FileMonitorAgent parentAgent = (FileMonitorAgent) this .monitorMap
228:                                    .get(parent.getName());
229:                            if (parentAgent != null) {
230:                                parentAgent.resetChildrenList();
231:                            }
232:                        }
233:                    }
234:                }
235:            }
236:
237:            /**
238:             * Queues a file for removal from being monitored.
239:             */
240:            protected void queueRemoveFile(final FileObject file) {
241:                this .deleteStack.push(file);
242:            }
243:
244:            /**
245:             * Get the delay between runs
246:             */
247:            public long getDelay() {
248:                return delay;
249:            }
250:
251:            /**
252:             * Set the delay between runs
253:             */
254:            public void setDelay(long delay) {
255:                if (delay > 0) {
256:                    this .delay = delay;
257:                } else {
258:                    this .delay = 1000;
259:                }
260:            }
261:
262:            /**
263:             * get the number of files to check per run
264:             */
265:            public int getChecksPerRun() {
266:                return checksPerRun;
267:            }
268:
269:            /**
270:             * set the number of files to check per run.
271:             * a additional delay will be added if there are more files to check
272:             *
273:             * @param checksPerRun a value less than 1 will disable this feature
274:             */
275:            public void setChecksPerRun(int checksPerRun) {
276:                this .checksPerRun = checksPerRun;
277:            }
278:
279:            /**
280:             * Queues a file for addition to be monitored.
281:             */
282:            protected void queueAddFile(final FileObject file) {
283:                this .addStack.push(file);
284:            }
285:
286:            /**
287:             * Starts monitoring the files that have been added.
288:             */
289:            public void start() {
290:                if (this .monitorThread == null) {
291:                    this .monitorThread = new Thread(this );
292:                    this .monitorThread.setDaemon(true);
293:                    this .monitorThread.setPriority(Thread.MIN_PRIORITY);
294:                }
295:                this .monitorThread.start();
296:            }
297:
298:            /**
299:             * Stops monitoring the files that have been added.
300:             */
301:            public void stop() {
302:                this .shouldRun = false;
303:            }
304:
305:            /**
306:             * Asks the agent for each file being monitored to check its file for changes.
307:             */
308:            public void run() {
309:                mainloop: while (!monitorThread.isInterrupted()
310:                        && this .shouldRun) {
311:                    while (!this .deleteStack.empty()) {
312:                        this .removeFile((FileObject) this .deleteStack.pop());
313:                    }
314:
315:                    // For each entry in the map
316:                    Object fileNames[];
317:                    synchronized (this .monitorMap) {
318:                        fileNames = this .monitorMap.keySet().toArray();
319:                    }
320:                    for (int iterFileNames = 0; iterFileNames < fileNames.length; iterFileNames++) {
321:                        FileName fileName = (FileName) fileNames[iterFileNames];
322:                        FileMonitorAgent agent;
323:                        synchronized (this .monitorMap) {
324:                            agent = (FileMonitorAgent) this .monitorMap
325:                                    .get(fileName);
326:                        }
327:                        if (agent != null) {
328:                            agent.check();
329:                        }
330:
331:                        if (getChecksPerRun() > 0) {
332:                            if ((iterFileNames % getChecksPerRun()) == 0) {
333:                                try {
334:                                    Thread.sleep(getDelay());
335:                                } catch (InterruptedException e) {
336:
337:                                }
338:                            }
339:                        }
340:
341:                        if (monitorThread.isInterrupted() || !this .shouldRun) {
342:                            continue mainloop;
343:                        }
344:                    }
345:
346:                    while (!this .addStack.empty()) {
347:                        this .addFile((FileObject) this .addStack.pop());
348:                    }
349:
350:                    try {
351:                        Thread.sleep(getDelay());
352:                    } catch (InterruptedException e) {
353:                        continue;
354:                    }
355:                }
356:
357:                this .shouldRun = true;
358:            }
359:
360:            /**
361:             * File monitor agent.
362:             */
363:            private static class FileMonitorAgent {
364:                private final FileObject file;
365:                private final DefaultFileMonitor fm;
366:
367:                private boolean exists;
368:                private long timestamp;
369:                private Map children = null;
370:
371:                private FileMonitorAgent(DefaultFileMonitor fm, FileObject file) {
372:                    this .fm = fm;
373:                    this .file = file;
374:
375:                    this .refresh();
376:                    this .resetChildrenList();
377:
378:                    try {
379:                        this .exists = this .file.exists();
380:                    } catch (FileSystemException fse) {
381:                        this .exists = false;
382:                    }
383:
384:                    try {
385:                        this .timestamp = this .file.getContent()
386:                                .getLastModifiedTime();
387:                    } catch (FileSystemException fse) {
388:                        this .timestamp = -1;
389:                    }
390:
391:                }
392:
393:                private void resetChildrenList() {
394:                    try {
395:                        if (this .file.getType().hasChildren()) {
396:                            this .children = new HashMap();
397:                            FileObject[] childrenList = this .file.getChildren();
398:                            for (int i = 0; i < childrenList.length; i++) {
399:                                this .children.put(childrenList[i].getName(),
400:                                        new Object()); // null?
401:                            }
402:                        }
403:                    } catch (FileSystemException fse) {
404:                        this .children = null;
405:                    }
406:                }
407:
408:                /**
409:                 * Clear the cache and re-request the file object
410:                 */
411:                private void refresh() {
412:                    try {
413:                        this .file.refresh();
414:                    } catch (FileSystemException fse) {
415:                        log.error(fse.getLocalizedMessage(), fse);
416:                    }
417:                }
418:
419:                /**
420:                 * Recursively fires create events for all children if recursive descent is
421:                 * enabled. Otherwise the create event is only fired for the initial
422:                 * FileObject.
423:                 */
424:                private void fireAllCreate(FileObject child) {
425:                    // Add listener so that it can be triggered
426:                    if (this .fm.getFileListener() != null) {
427:                        child.getFileSystem().addListener(child,
428:                                this .fm.getFileListener());
429:                    }
430:
431:                    ((AbstractFileSystem) child.getFileSystem())
432:                            .fireFileCreated(child);
433:
434:                    // Remove it because a listener is added in the queueAddFile
435:                    if (this .fm.getFileListener() != null) {
436:                        child.getFileSystem().removeListener(child,
437:                                this .fm.getFileListener());
438:                    }
439:
440:                    this .fm.queueAddFile(child); // Add
441:
442:                    try {
443:
444:                        if (this .fm.isRecursive()) {
445:                            if (child.getType().hasChildren()) {
446:                                FileObject[] newChildren = child.getChildren();
447:                                for (int i = 0; i < newChildren.length; i++) {
448:                                    fireAllCreate(newChildren[i]);
449:                                }
450:                            }
451:                        }
452:
453:                    } catch (FileSystemException fse) {
454:                        log.error(fse.getLocalizedMessage(), fse);
455:                    }
456:                }
457:
458:                /**
459:                 * Only checks for new children. If children are removed, they'll
460:                 * eventually be checked.
461:                 */
462:                private void checkForNewChildren() {
463:                    try {
464:                        if (this .file.getType().hasChildren()) {
465:                            FileObject[] newChildren = this .file.getChildren();
466:                            if (this .children != null) {
467:                                // See which new children are not listed in the current children map.
468:                                Map newChildrenMap = new HashMap();
469:                                Stack missingChildren = new Stack();
470:
471:                                for (int i = 0; i < newChildren.length; i++) {
472:                                    newChildrenMap.put(
473:                                            newChildren[i].getName(),
474:                                            new Object()); // null ?
475:                                    // If the child's not there
476:                                    if (!this .children
477:                                            .containsKey(newChildren[i]
478:                                                    .getName())) {
479:                                        missingChildren.push(newChildren[i]);
480:                                    }
481:                                }
482:
483:                                this .children = newChildrenMap;
484:
485:                                // If there were missing children
486:                                if (!missingChildren.empty()) {
487:
488:                                    while (!missingChildren.empty()) {
489:                                        FileObject child = (FileObject) missingChildren
490:                                                .pop();
491:                                        this .fireAllCreate(child);
492:                                    }
493:                                }
494:
495:                            } else {
496:                                // First set of children - Break out the cigars
497:                                if (newChildren.length > 0) {
498:                                    this .children = new HashMap();
499:                                }
500:                                for (int i = 0; i < newChildren.length; i++) {
501:                                    this .children.put(newChildren[i].getName(),
502:                                            new Object()); // null?
503:                                    this .fireAllCreate(newChildren[i]);
504:                                }
505:                            }
506:                        }
507:                    } catch (FileSystemException fse) {
508:                        log.error(fse.getLocalizedMessage(), fse);
509:                    }
510:                }
511:
512:                private void check() {
513:                    this .refresh();
514:
515:                    try {
516:                        // If the file existed and now doesn't
517:                        if (this .exists && !this .file.exists()) {
518:                            this .exists = this .file.exists();
519:                            this .timestamp = -1;
520:
521:                            // Fire delete event
522:
523:                            ((AbstractFileSystem) this .file.getFileSystem())
524:                                    .fireFileDeleted(this .file);
525:
526:                            // Remove listener in case file is re-created. Don't want to fire twice.
527:                            if (this .fm.getFileListener() != null) {
528:                                this .file.getFileSystem().removeListener(
529:                                        this .file, this .fm.getFileListener());
530:                            }
531:
532:                            // Remove from map
533:                            this .fm.queueRemoveFile(this .file);
534:                        } else if (this .exists && this .file.exists()) {
535:
536:                            // Check the timestamp to see if it has been modified
537:                            if (this .timestamp != this .file.getContent()
538:                                    .getLastModifiedTime()) {
539:                                this .timestamp = this .file.getContent()
540:                                        .getLastModifiedTime();
541:                                // Fire change event
542:
543:                                // Don't fire if it's a folder because new file children
544:                                // and deleted files in a folder have their own event triggered.
545:                                if (!this .file.getType().hasChildren()) {
546:                                    ((AbstractFileSystem) this .file
547:                                            .getFileSystem())
548:                                            .fireFileChanged(this .file);
549:                                }
550:                            }
551:
552:                        }
553:
554:                        this .checkForNewChildren();
555:
556:                    } catch (FileSystemException fse) {
557:                        log.error(fse.getLocalizedMessage(), fse);
558:                    }
559:                }
560:
561:            }
562:
563:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.