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 1304052 2012-03-22 20:55:29Z ggregory $
029 * @since 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 stop(interval);
151 }
152
153 /**
154 * Stop monitoring.
155 *
156 * @param stopInterval the amount of time in milliseconds to wait for the thread to finish.
157 * A value of zero will wait until the thread is finished (see {@link Thread#join(long)}).
158 * @throws Exception if an error occurs initializing the observer
159 * @since 2.1
160 */
161 public synchronized void stop(long stopInterval) throws Exception {
162 if (running == false) {
163 throw new IllegalStateException("Monitor is not running");
164 }
165 running = false;
166 try {
167 thread.join(stopInterval);
168 } catch (InterruptedException e) {
169 Thread.currentThread().interrupt();
170 }
171 for (FileAlterationObserver observer : observers) {
172 observer.destroy();
173 }
174 }
175
176 /**
177 * Run.
178 */
179 public void run() {
180 while (running) {
181 for (FileAlterationObserver observer : observers) {
182 observer.checkAndNotify();
183 }
184 if (!running) {
185 break;
186 }
187 try {
188 Thread.sleep(interval);
189 } catch (final InterruptedException ignored) {
190 }
191 }
192 }
193 }