    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    }