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