001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package com.opensymphony.webwork.util.classloader.monitor;
017:
018: import com.opensymphony.webwork.util.classloader.utils.ThreadUtils;
019: import org.apache.commons.collections.MultiHashMap;
020: import org.apache.commons.logging.Log;
021: import org.apache.commons.logging.LogFactory;
022:
023: import java.io.File;
024: import java.io.FileFilter;
025: import java.util.*;
026:
027: /**
028: * @author tcurdt
029: */
030: public final class FilesystemAlterationMonitor implements Runnable {
031:
032: private final static Log log = LogFactory
033: .getLog(FilesystemAlterationMonitor.class);
034:
035: public class Entry {
036:
037: private final File root;
038: private final File file;
039: private long lastModified;
040: private Set paths = new HashSet();
041: private Set childs = new HashSet();
042: private final boolean isDirectory;
043:
044: public Entry(final File pRoot, final File pFile) {
045: root = pRoot;
046: file = pFile;
047: lastModified = -1;
048: isDirectory = file.isDirectory();
049: }
050:
051: public boolean hasChanged() {
052: final long modified = file.lastModified();
053: return modified != lastModified;
054: }
055:
056: public boolean isDelected() {
057: return !file.exists();
058: }
059:
060: public boolean isDirectory() {
061: return isDirectory;
062: }
063:
064: public Entry[] getChilds() {
065: final Entry[] r = new Entry[childs.size()];
066: childs.toArray(r);
067: return r;
068: }
069:
070: private FileFilter getFileFilter() {
071: return new FileFilter() {
072:
073: public boolean accept(final File pathname) {
074: final String p = pathname.getAbsolutePath();
075: return !paths.contains(p);
076: }
077: };
078: }
079:
080: public Entry[] getNonChilds() {
081: final File[] newFiles = file.listFiles(getFileFilter());
082: final Entry[] r = new Entry[newFiles.length];
083: for (int i = 0; i < newFiles.length; i++) {
084: r[i] = new Entry(root, newFiles[i]);
085: }
086: return r;
087: }
088:
089: public void add(final Entry entry) {
090: childs.add(entry);
091: paths.add(entry.toString());
092: onCreate(root, entry);
093: }
094:
095: private void deleteChilds() {
096: final Entry[] childs = this .getChilds();
097: for (int i = 0; i < childs.length; i++) {
098: final Entry child = childs[i];
099: delete(child);
100: }
101: }
102:
103: public void delete(final Entry entry) {
104: childs.remove(entry);
105: paths.remove(entry.toString());
106: entry.deleteChilds();
107: onDelete(root, entry);
108: }
109:
110: public File getFile() {
111: return file;
112: }
113:
114: public void markNotChanged() {
115: lastModified = file.lastModified();
116: }
117:
118: public String toString() {
119: return file.getAbsolutePath();
120: }
121: }
122:
123: private Map listeners = new MultiHashMap();
124: private Map directories = new MultiHashMap();
125: private Map entries = new HashMap();
126:
127: private final Object mutexListeners = new Object();
128: private final Object mutexRunning = new Object();
129:
130: private long delay = 3000;
131: private boolean running = true;
132:
133: public FilesystemAlterationMonitor() {
134: }
135:
136: public void stop() {
137: synchronized (mutexRunning) {
138: running = false;
139: }
140: }
141:
142: public void setInterval(final long pDelay) {
143: delay = pDelay;
144: }
145:
146: public void addListener(
147: final FilesystemAlterationListener listener,
148: final File directory) {
149: synchronized (mutexListeners) {
150: // listerner -> dir1, dir2, dir3
151:
152: final MultiHashMap newListeners = new MultiHashMap(
153: listeners);
154: newListeners.put(listener, directory);
155: listeners = newListeners;
156:
157: // directory -> listener1, listener2, listener3
158: final MultiHashMap newDirectories = new MultiHashMap(
159: directories);
160: newDirectories.put(directory, listener);
161: directories = newDirectories;
162: }
163: }
164:
165: public void removeListener(
166: final FilesystemAlterationListener listener) {
167: synchronized (mutexListeners) {
168: // listerner -> dir1, dir2, dir3
169: final MultiHashMap newListeners = new MultiHashMap(
170: listeners);
171: Collection d = (Collection) newListeners.remove(listener);
172: listeners = newListeners;
173:
174: if (d != null) {
175: // directory -> listener1, listener2, listener3
176: final MultiHashMap newDirectories = new MultiHashMap(
177: directories);
178: for (Iterator it = d.iterator(); it.hasNext();) {
179: newDirectories.remove(it.next());
180: entries.remove(d);
181: }
182: directories = newDirectories;
183: }
184: }
185: }
186:
187: private void onStart(final File root) {
188: log.debug("start checking " + root);
189:
190: Map directories;
191: synchronized (mutexListeners) {
192: directories = this .directories;
193: }
194: final Collection l = (Collection) directories.get(root);
195: if (l != null) {
196: for (Iterator it = l.iterator(); it.hasNext();) {
197: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
198: .next();
199: listener.onStart(root);
200: }
201: }
202: }
203:
204: private void onStop(final File root) {
205: log.debug("stop checking " + root);
206:
207: Map directories;
208: synchronized (mutexListeners) {
209: directories = this .directories;
210: }
211: final Collection l = (Collection) directories.get(root);
212: if (l != null) {
213: for (Iterator it = l.iterator(); it.hasNext();) {
214: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
215: .next();
216: listener.onStop(root);
217: }
218: }
219: }
220:
221: private void onCreate(final File root, final Entry entry) {
222:
223: log.debug("created "
224: + ((entry.isDirectory()) ? "dir " : "file ") + entry);
225:
226: Map directories;
227: synchronized (mutexListeners) {
228: directories = this .directories;
229: }
230:
231: final Collection l = (Collection) directories.get(root);
232: if (l != null) {
233: if (entry.isDirectory()) {
234: for (Iterator it = l.iterator(); it.hasNext();) {
235: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
236: .next();
237: listener.onCreateDirectory(entry.getFile());
238: }
239: } else {
240: for (Iterator it = l.iterator(); it.hasNext();) {
241: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
242: .next();
243: listener.onCreateFile(entry.getFile());
244: }
245: }
246: }
247:
248: entry.markNotChanged();
249: }
250:
251: private void onChange(final File root, final Entry entry) {
252:
253: log.debug("changed "
254: + ((entry.isDirectory()) ? "dir " : "file ") + entry);
255:
256: Map directories;
257: synchronized (mutexListeners) {
258: directories = this .directories;
259: }
260:
261: final Collection l = (Collection) directories.get(root);
262: if (l != null) {
263: if (entry.isDirectory()) {
264: for (Iterator it = l.iterator(); it.hasNext();) {
265: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
266: .next();
267: listener.onChangeDirectory(entry.getFile());
268: }
269: } else {
270: for (Iterator it = l.iterator(); it.hasNext();) {
271: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
272: .next();
273: listener.onChangeFile(entry.getFile());
274: }
275: }
276: }
277:
278: entry.markNotChanged();
279: }
280:
281: private void onDelete(final File root, final Entry entry) {
282:
283: log.debug("deleted "
284: + ((entry.isDirectory()) ? "dir " : "file ") + entry);
285:
286: Map directories;
287: synchronized (mutexListeners) {
288: directories = this .directories;
289: }
290:
291: final Collection l = (Collection) directories.get(root);
292: if (l != null) {
293: if (entry.isDirectory()) {
294: for (Iterator it = l.iterator(); it.hasNext();) {
295: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
296: .next();
297: listener.onDeleteDirectory(entry.getFile());
298: }
299: } else {
300: for (Iterator it = l.iterator(); it.hasNext();) {
301: final FilesystemAlterationListener listener = (FilesystemAlterationListener) it
302: .next();
303: listener.onDeleteFile(entry.getFile());
304: }
305: }
306: }
307:
308: entry.markNotChanged();
309: }
310:
311: private void check(final File root, final Entry entry,
312: final boolean create) {
313: //log.debug("checking " + entry);
314:
315: if (entry.isDirectory()) {
316: final Entry[] currentChilds = entry.getChilds();
317: if (entry.hasChanged() || create) {
318: //log.debug(entry + " has changed");
319: if (!create) {
320: onChange(root, entry);
321: for (int i = 0; i < currentChilds.length; i++) {
322: final Entry child = currentChilds[i];
323: if (child.isDelected()) {
324: entry.delete(child);
325: currentChilds[i] = null;
326: }
327: }
328: }
329: final Entry[] newChilds = entry.getNonChilds();
330: for (int i = 0; i < newChilds.length; i++) {
331: final Entry child = newChilds[i];
332: entry.add(child);
333: }
334: if (!create) {
335: for (int i = 0; i < currentChilds.length; i++) {
336: final Entry child = currentChilds[i];
337: if (child != null) {
338: check(root, child, false);
339: }
340: }
341: }
342: for (int i = 0; i < newChilds.length; i++) {
343: final Entry child = newChilds[i];
344: check(root, child, true);
345: }
346: } else {
347: //log.debug(entry + " has not changed");
348: for (int i = 0; i < currentChilds.length; i++) {
349: final Entry child = currentChilds[i];
350: check(root, child, false);
351: }
352: }
353: } else {
354: if (entry.isDelected()) {
355: onDelete(root, entry);
356: } else if (entry.hasChanged()) {
357: onChange(root, entry);
358: }
359: }
360: }
361:
362: public void run() {
363: log.info("fam running");
364:
365: while (true) {
366:
367: synchronized (mutexRunning) {
368: if (!running) {
369: break;
370: }
371: }
372:
373: doRun();
374: ThreadUtils.sleep(delay);
375: }
376:
377: log.info("fam exiting");
378: }
379:
380: public void doRun() {
381: Map directories;
382:
383: synchronized (mutexListeners) {
384: directories = this .directories;
385: }
386:
387: for (Iterator it = directories.keySet().iterator(); it
388: .hasNext();) {
389: final File directory = (File) it.next();
390: if (directory.exists()) {
391: onStart(directory);
392:
393: Entry root;
394: synchronized (mutexListeners) {
395: root = (Entry) entries.get(directory);
396: if (root == null) {
397: root = new Entry(directory, directory);
398: entries.put(directory, root);
399: }
400: }
401:
402: check(directory, root, false);
403: onStop(directory);
404: }
405: }
406: }
407: }
|