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 */ 017package org.apache.commons.io.monitor; 018 019import java.util.List; 020import java.util.concurrent.CopyOnWriteArrayList; 021import 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 1415850 2012-11-30 20:51:39Z ggregory $ 029 * @since 2.0 030 */ 031public 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(final 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(final long interval, final FileAlterationObserver... observers) { 064 this(interval); 065 if (observers != null) { 066 for (final 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(final 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 (final 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(final 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 (final InterruptedException e) { 169 Thread.currentThread().interrupt(); 170 } 171 for (final FileAlterationObserver observer : observers) { 172 observer.destroy(); 173 } 174 } 175 176 /** 177 * Run. 178 */ 179 public void run() { 180 while (running) { 181 for (final 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}