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:
018: package org.apache.commons.jci.monitor;
019:
020: import java.io.File;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Set;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: /**
031: * Implementation of a FilesystemAlterationObserver
032: *
033: * @author tcurdt
034: */
035: public class FilesystemAlterationObserverImpl implements
036: FilesystemAlterationObserver {
037:
038: private final Log log = LogFactory
039: .getLog(FilesystemAlterationObserverImpl.class);
040:
041: private interface MonitorFile {
042:
043: long lastModified();
044:
045: MonitorFile[] listFiles();
046:
047: boolean isDirectory();
048:
049: boolean exists();
050:
051: String getName();
052:
053: }
054:
055: private final static class MonitorFileImpl implements MonitorFile {
056:
057: private final File file;
058:
059: public MonitorFileImpl(final File pFile) {
060: file = pFile;
061: }
062:
063: public boolean exists() {
064: return file.exists();
065: }
066:
067: public MonitorFile[] listFiles() {
068: final File[] childs = file.listFiles();
069:
070: final MonitorFile[] providers = new MonitorFile[childs.length];
071: for (int i = 0; i < providers.length; i++) {
072: providers[i] = new MonitorFileImpl(childs[i]);
073: }
074: return providers;
075: }
076:
077: public String getName() {
078: return file.getName();
079: }
080:
081: public boolean isDirectory() {
082: return file.isDirectory();
083: }
084:
085: public long lastModified() {
086: return file.lastModified();
087: }
088:
089: public String toString() {
090: return file.toString();
091: }
092:
093: }
094:
095: private final class Entry {
096:
097: private final static int TYPE_UNKNOWN = 0;
098: private final static int TYPE_FILE = 1;
099: private final static int TYPE_DIRECTORY = 2;
100:
101: private final MonitorFile file;
102: private long lastModified = -1;
103: private int lastType = TYPE_UNKNOWN;
104: private Map childs = new HashMap();
105:
106: public Entry(final MonitorFile pFile) {
107: file = pFile;
108: }
109:
110: public String getName() {
111: return file.getName();
112: }
113:
114: public String toString() {
115: return file.toString();
116: }
117:
118: private void compareChilds() {
119: if (!file.isDirectory()) {
120: return;
121: }
122:
123: final MonitorFile[] files = file.listFiles();
124: final Set deleted = new HashSet(childs.values());
125: for (int i = 0; i < files.length; i++) {
126: final MonitorFile f = files[i];
127: final String name = f.getName();
128: final Entry entry = (Entry) childs.get(name);
129: if (entry != null) {
130: // already recognized as child
131: deleted.remove(entry);
132:
133: if (entry.needsToBeDeleted()) {
134: // we have to delete this one
135: childs.remove(name);
136: }
137: } else {
138: // a new child
139: final Entry newChild = new Entry(f);
140: childs.put(name, newChild);
141: newChild.needsToBeDeleted();
142: }
143: }
144:
145: // the ones not found on disk anymore
146:
147: for (Iterator it = deleted.iterator(); it.hasNext();) {
148: final Entry entry = (Entry) it.next();
149: entry.deleteChildsAndNotify();
150: childs.remove(entry.getName());
151: }
152: }
153:
154: private void deleteChildsAndNotify() {
155: for (Iterator it = childs.values().iterator(); it.hasNext();) {
156: final Entry entry = (Entry) it.next();
157:
158: entry.deleteChildsAndNotify();
159: }
160: childs.clear();
161:
162: if (lastType == TYPE_DIRECTORY) {
163: notifyOnDirectoryDelete(this );
164: } else if (lastType == TYPE_FILE) {
165: notifyOnFileDelete(this );
166: }
167: }
168:
169: public boolean needsToBeDeleted() {
170:
171: if (!file.exists()) {
172: // deleted or has never existed yet
173:
174: // log.debug(file + " does not exist or has been deleted");
175:
176: deleteChildsAndNotify();
177:
178: // mark to be deleted by parent
179: return true;
180: } else {
181: // exists
182: final long currentModified = file.lastModified();
183:
184: if (currentModified != lastModified) {
185: // last modified has changed
186: lastModified = currentModified;
187:
188: // log.debug(file + " has new last modified");
189:
190: // types only changes when also the last modified changes
191: final int newType = (file.isDirectory() ? TYPE_DIRECTORY
192: : TYPE_FILE);
193:
194: if (lastType != newType) {
195: // the type has changed
196:
197: // log.debug(file + " has a new type");
198:
199: deleteChildsAndNotify();
200:
201: lastType = newType;
202:
203: // and then an add as the new type
204:
205: if (newType == TYPE_DIRECTORY) {
206: notifyOnDirectoryCreate(this );
207: compareChilds();
208: } else {
209: notifyOnFileCreate(this );
210: }
211:
212: return false;
213: }
214:
215: if (newType == TYPE_DIRECTORY) {
216: notifyOnDirectoryChange(this );
217: compareChilds();
218: } else {
219: notifyOnFileChange(this );
220: }
221:
222: return false;
223:
224: } else {
225:
226: // so exists and has not changed
227:
228: // log.debug(file + " does exist and has not changed");
229:
230: compareChilds();
231:
232: return false;
233: }
234: }
235: }
236:
237: public MonitorFile getFile() {
238: return file;
239: }
240:
241: public void markNotChanged() {
242: lastModified = file.lastModified();
243: }
244:
245: }
246:
247: private final File rootDirectory;
248: private final Entry rootEntry;
249:
250: private FilesystemAlterationListener[] listeners = new FilesystemAlterationListener[0];
251: private Set listenersSet = new HashSet();
252:
253: public FilesystemAlterationObserverImpl(final File pRootDirectory) {
254: rootDirectory = pRootDirectory;
255: rootEntry = new Entry(new MonitorFileImpl(pRootDirectory));
256: }
257:
258: private void notifyOnStart() {
259: log.debug("onStart " + rootEntry);
260: for (int i = 0; i < listeners.length; i++) {
261: final FilesystemAlterationListener listener = listeners[i];
262: listener.onStart(this );
263: }
264: }
265:
266: private void notifyOnStop() {
267: log.debug("onStop " + rootEntry);
268: for (int i = 0; i < listeners.length; i++) {
269: final FilesystemAlterationListener listener = listeners[i];
270: listener.onStop(this );
271: }
272: }
273:
274: private void notifyOnFileCreate(final Entry pEntry) {
275: log.debug("onFileCreate " + pEntry);
276: for (int i = 0; i < listeners.length; i++) {
277: final FilesystemAlterationListener listener = listeners[i];
278: listener
279: .onFileCreate(((MonitorFileImpl) pEntry.getFile()).file);
280: }
281: }
282:
283: private void notifyOnFileChange(final Entry pEntry) {
284: log.debug("onFileChange " + pEntry);
285: for (int i = 0; i < listeners.length; i++) {
286: final FilesystemAlterationListener listener = listeners[i];
287: listener
288: .onFileChange(((MonitorFileImpl) pEntry.getFile()).file);
289: }
290: }
291:
292: private void notifyOnFileDelete(final Entry pEntry) {
293: log.debug("onFileDelete " + pEntry);
294: for (int i = 0; i < listeners.length; i++) {
295: final FilesystemAlterationListener listener = listeners[i];
296: listener
297: .onFileDelete(((MonitorFileImpl) pEntry.getFile()).file);
298: }
299: }
300:
301: private void notifyOnDirectoryCreate(final Entry pEntry) {
302: log.debug("onDirectoryCreate " + pEntry);
303: for (int i = 0; i < listeners.length; i++) {
304: final FilesystemAlterationListener listener = listeners[i];
305: listener.onDirectoryCreate(((MonitorFileImpl) pEntry
306: .getFile()).file);
307: }
308: }
309:
310: private void notifyOnDirectoryChange(final Entry pEntry) {
311: log.debug("onDirectoryChange " + pEntry);
312: for (int i = 0; i < listeners.length; i++) {
313: final FilesystemAlterationListener listener = listeners[i];
314: listener.onDirectoryChange(((MonitorFileImpl) pEntry
315: .getFile()).file);
316: }
317: }
318:
319: private void notifyOnDirectoryDelete(final Entry pEntry) {
320: log.debug("onDirectoryDelete " + pEntry);
321: for (int i = 0; i < listeners.length; i++) {
322: final FilesystemAlterationListener listener = listeners[i];
323: listener.onDirectoryDelete(((MonitorFileImpl) pEntry
324: .getFile()).file);
325: }
326: }
327:
328: private void checkEntries() {
329: if (rootEntry.needsToBeDeleted()) {
330: // root not existing
331: rootEntry.lastType = Entry.TYPE_UNKNOWN;
332: }
333: }
334:
335: public synchronized void checkAndNotify() {
336: if (listeners.length == 0) {
337: return;
338: }
339:
340: notifyOnStart();
341:
342: checkEntries();
343:
344: notifyOnStop();
345: }
346:
347: public File getRootDirectory() {
348: return rootDirectory;
349: }
350:
351: public synchronized void addListener(
352: final FilesystemAlterationListener pListener) {
353: if (listenersSet.add(pListener)) {
354: createArrayFromSet();
355: }
356: }
357:
358: public synchronized void removeListener(
359: final FilesystemAlterationListener pListener) {
360: if (listenersSet.remove(pListener)) {
361: createArrayFromSet();
362: }
363: }
364:
365: private void createArrayFromSet() {
366: final FilesystemAlterationListener[] newListeners = new FilesystemAlterationListener[listenersSet
367: .size()];
368: listenersSet.toArray(newListeners);
369: listeners = newListeners;
370: }
371:
372: public FilesystemAlterationListener[] getListeners() {
373: return listeners;
374: }
375:
376: public static void main(String[] args) {
377: final FilesystemAlterationObserverImpl observer = new FilesystemAlterationObserverImpl(
378: new File(args[0]));
379: while (true) {
380: observer.checkEntries();
381: try {
382: Thread.sleep(1000);
383: } catch (InterruptedException e) {
384: }
385: }
386: }
387: }
|