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 * @since 2.0 029 */ 030public final class FileAlterationMonitor implements Runnable { 031 032 private final long interval; 033 private final List<FileAlterationObserver> observers = new CopyOnWriteArrayList<>(); 034 private Thread thread = null; 035 private ThreadFactory threadFactory; 036 private volatile boolean running = false; 037 038 /** 039 * Constructs a monitor with a default interval of 10 seconds. 040 */ 041 public FileAlterationMonitor() { 042 this(10000); 043 } 044 045 /** 046 * Constructs a monitor with the specified interval. 047 * 048 * @param interval The amount of time in milliseconds to wait between 049 * checks of the file system 050 */ 051 public FileAlterationMonitor(final long interval) { 052 this.interval = interval; 053 } 054 055 /** 056 * Constructs a monitor with the specified interval and set of observers. 057 * 058 * @param interval The amount of time in milliseconds to wait between 059 * checks of the file system 060 * @param observers The set of observers to add to the monitor. 061 */ 062 public FileAlterationMonitor(final long interval, final FileAlterationObserver... observers) { 063 this(interval); 064 if (observers != null) { 065 for (final FileAlterationObserver observer : observers) { 066 addObserver(observer); 067 } 068 } 069 } 070 071 /** 072 * Returns the interval. 073 * 074 * @return the interval 075 */ 076 public long getInterval() { 077 return interval; 078 } 079 080 /** 081 * Sets the thread factory. 082 * 083 * @param threadFactory the thread factory 084 */ 085 public synchronized void setThreadFactory(final ThreadFactory threadFactory) { 086 this.threadFactory = threadFactory; 087 } 088 089 /** 090 * Adds a file system observer to this monitor. 091 * 092 * @param observer The file system observer to add 093 */ 094 public void addObserver(final FileAlterationObserver observer) { 095 if (observer != null) { 096 observers.add(observer); 097 } 098 } 099 100 /** 101 * Removes a file system observer from this monitor. 102 * 103 * @param observer The file system observer to remove 104 */ 105 public void removeObserver(final FileAlterationObserver observer) { 106 if (observer != null) { 107 while (observers.remove(observer)) { 108 // empty 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 * Starts 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 * Stops 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 * Stops 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.interrupt(); 168 thread.join(stopInterval); 169 } catch (final InterruptedException e) { 170 Thread.currentThread().interrupt(); 171 } 172 for (final FileAlterationObserver observer : observers) { 173 observer.destroy(); 174 } 175 } 176 177 /** 178 * Runs this monitor. 179 */ 180 @Override 181 public void run() { 182 while (running) { 183 for (final FileAlterationObserver observer : observers) { 184 observer.checkAndNotify(); 185 } 186 if (!running) { 187 break; 188 } 189 try { 190 Thread.sleep(interval); 191 } catch (final InterruptedException ignored) { 192 // ignore 193 } 194 } 195 } 196}