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.io.monitor;
018    
019    import java.util.List;
020    import java.util.concurrent.CopyOnWriteArrayList;
021    
022    /**
023     * A runnable that spawns a monitoring thread triggering any
024     * registered {@link FileAlterationObserver} at a specified interval.
025     * 
026     * @see FileAlterationObserver
027     * @version $Id: FileAlterationMonitor.java 1022799 2010-10-15 00:56:13Z niallp $
028     * @since Commons IO 2.0
029     */
030    public final class FileAlterationMonitor implements Runnable {
031    
032        private final long interval;
033        private final List<FileAlterationObserver> observers = new CopyOnWriteArrayList<FileAlterationObserver>();
034        private Thread thread = null;
035        private volatile boolean running = false;
036    
037        /**
038         * Construct a monitor with a default interval of 10 seconds.
039         */
040        public FileAlterationMonitor() {
041            this(10000);
042        }
043    
044        /**
045         * Construct a monitor with the specified interval.
046         *
047         * @param interval The amount of time in miliseconds to wait between
048         * checks of the file system
049         */
050        public FileAlterationMonitor(long interval) {
051            this.interval = interval;
052        }
053    
054        /**
055         * Return the interval.
056         *
057         * @return the interval
058         */
059        public long getInterval() {
060            return interval;
061        }
062    
063        /**
064         * Construct a monitor with the specified interval and set of observers.
065         *
066         * @param interval The amount of time in miliseconds to wait between
067         * checks of the file system
068         * @param observers The set of observers to add to the monitor.
069         */
070        public FileAlterationMonitor(long interval, FileAlterationObserver... observers) {
071            this(interval);
072            if (observers != null) {
073                for (FileAlterationObserver observer : observers) {
074                    addObserver(observer);
075                }
076            }
077        }
078    
079        /**
080         * Add a file system observer to this monitor.
081         *
082         * @param observer The file system observer to add
083         */
084        public void addObserver(final FileAlterationObserver observer) {
085            if (observer != null) {
086                observers.add(observer);
087            }
088        }
089    
090        /**
091         * Remove a file system observer from this monitor.
092         *
093         * @param observer The file system observer to remove
094         */
095        public void removeObserver(final FileAlterationObserver observer) {
096            if (observer != null) {
097                while (observers.remove(observer)) {
098                }
099            }
100        }
101    
102        /**
103         * Returns the set of {@link FileAlterationObserver} registered with
104         * this monitor. 
105         *
106         * @return The set of {@link FileAlterationObserver}
107         */
108        public Iterable<FileAlterationObserver> getObservers() {
109            return observers;
110        }
111    
112        /**
113         * Start monitoring.
114         *
115         * @throws Exception if an error occurs initializing the observer
116         */
117        public synchronized void start() throws Exception {
118            if (running) {
119                throw new IllegalStateException("Monitor is already running");
120            }
121            for (FileAlterationObserver observer : observers) {
122                observer.initialize();
123            }
124            running = true;
125            thread = new Thread(this);
126            thread.start();
127        }
128    
129        /**
130         * Stop monitoring.
131         *
132         * @throws Exception if an error occurs initializing the observer
133         */
134        public synchronized void stop() throws Exception {
135            if (running == false) {
136                throw new IllegalStateException("Monitor is not running");
137            }
138            running = false;
139            try {
140                thread.join(interval);
141            } catch (InterruptedException e) {
142                Thread.currentThread().interrupt();
143            }
144            for (FileAlterationObserver observer : observers) {
145                observer.destroy();
146            }
147        }
148    
149        /**
150         * Run.
151         */
152        public void run() {
153            while (running) {
154                for (FileAlterationObserver observer : observers) {
155                    observer.checkAndNotify();
156                }
157                if (!running) {
158                    break;
159                }
160                try {
161                    Thread.sleep(interval);
162                } catch (final InterruptedException ignored) {
163                }
164            }
165        }
166    }