You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ni...@apache.org on 2008/04/06 11:56:33 UTC
svn commit: r645221 - in /commons/proper/io/trunk/src:
java/org/apache/commons/io/monitor/ test/org/apache/commons/io/monitor/
Author: niallp
Date: Sun Apr 6 02:56:32 2008
New Revision: 645221
URL: http://svn.apache.org/viewvc?rev=645221&view=rev
Log:
IO-132 - add File Listener/Monitor
Added:
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java (with props)
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java (with props)
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java (with props)
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java (with props)
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java (with props)
commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html (with props)
commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/
commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java (with props)
commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java (with props)
Added: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * {@link FilesystemEntry} represents the state of a file or directory, capturing
+ * the following {@link File} attributes at a point in time:
+ * <ul>
+ * <li>File Name (see {@link File#getName()})</li>
+ * <li>Exists - whether the file exists or not (see {@link File#exists()})</li>
+ * <li>Directory - whether the file is a directory or not (see {@link File#isDirectory()})</li>
+ * <li>Last Modified Date/Time (see {@link File#lastModified()})</li>
+ * <li>Children - contents of a directory (see {@link File#listFiles(java.io.FileFilter)})</li>
+ * </ul>
+ * <p>
+ * <h3>Custom Implementations</h3>
+ * If the state of additional {@link File} attributes is required then create a custom
+ * {@link FilesystemEntry} with properties for those attributes. Override the
+ * {@link #newChildInstance(File)} to return a new instance of the appropriate type.
+ * You may also want to override the {@link #refresh()} and/or {@link #hasChanged()}
+ * methods.
+ *
+ */
+public class FilesystemEntry implements Serializable {
+
+ private FilesystemEntry parent;
+ private FilesystemEntry[] children;
+ private File file;
+ private String name;
+ private boolean exists;
+ private boolean directory;
+ private long lastModified;
+
+ /**
+ * Construct a new monitor for a specified {@link File}.
+ *
+ * @param file The file being monitored
+ */
+ public FilesystemEntry(File file) {
+ this((FilesystemEntry)null, file);
+ }
+
+ /**
+ * Construct a new monitor for a specified {@link File}.
+ *
+ * @param parent The parent
+ * @param file The file being monitored
+ */
+ public FilesystemEntry(FilesystemEntry parent, File file) {
+ if (file == null) {
+ throw new IllegalArgumentException("File is missing");
+ }
+ this.file = file;
+ this.parent = parent;
+ this.name = file.getName();
+ }
+
+ /**
+ * Refresh the attributes from the underlying {@link File}.
+ * <p>
+ * This implementation refreshes the <code>name</code>, <code>exists</code>
+ * <code>directory</code> and <code>lastModified</code> properties.
+ */
+ public void refresh() {
+ name = file.getName();
+ exists = file.exists();
+ if (exists) {
+ directory = file.isDirectory();
+ lastModified = file.lastModified();
+ }
+ }
+
+ /**
+ * Create a new child instance.
+ * <p>
+ * Custom implementations should override this method to return
+ * a new instance of the appropriate type.
+ *
+ * @param file The child file
+ * @return a new child instance
+ */
+ public FilesystemEntry newChildInstance(File file) {
+ return new FilesystemEntry(this, file);
+ }
+
+ /**
+ * Indicate whether the file has changed or not.
+ * <p>
+ * This implementation compares the <code>lastModified<code>
+ * value of the {@link File} with the stored value.
+ *
+ * @return whether the file has changed or not
+ */
+ public boolean hasChanged() {
+ return (lastModified != file.lastModified());
+ }
+
+ /**
+ * Return the parent entry.
+ *
+ * @return the parent entry
+ */
+ public FilesystemEntry getParent() {
+ return parent;
+ }
+
+ /**
+ * Return the level
+ *
+ * @return the level
+ */
+ public int getLevel() {
+ return parent == null ? 0 : parent.getLevel() + 1;
+ }
+
+ /**
+ * Return the directory's files.
+ *
+ * @return This directory's files or an empty
+ * array if the file is not a directory or the
+ * directory is empty
+ */
+ public FilesystemEntry[] getChildren() {
+ return children != null ? children : FilesystemObserver.EMPTY_ENTRIES;
+ }
+
+ /**
+ * Set the directory's files.
+ *
+ * @param children This directory's files, may be null
+ */
+ public void setChildren(FilesystemEntry[] children) {
+ this.children = children;
+ }
+
+ /**
+ * Return the file being monitored.
+ *
+ * @return the file being monitored
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * Set the file being monitored.
+ *
+ * @param file the file being monitored
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Return the file name.
+ *
+ * @return the file name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the file name.
+ *
+ * @param name the file name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Return the last modified time from the last time it
+ * was checked.
+ *
+ * @return the last modified time
+ */
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ /**
+ * Return the last modified time from the last time it
+ * was checked.
+ *
+ * @param lastModified The last modified time
+ */
+ public void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ /**
+ * Indicate whether the file existed the last time it
+ * was checked.
+ *
+ * @return whether the file existed
+ */
+ public boolean isExists() {
+ return exists;
+ }
+
+ /**
+ * Set whether the file existed the last time it
+ * was checked.
+ *
+ * @param exists whether the file exists or not
+ */
+ public void setExists(boolean exists) {
+ this.exists = exists;
+ }
+
+ /**
+ * Indicate whether the file is a directory or not.
+ *
+ * @return whether the file is a directory or not
+ */
+ public boolean isDirectory() {
+ return directory;
+ }
+
+ /**
+ * Set whether the file is a directory or not.
+ *
+ * @param directory whether the file is a directory or not
+ */
+ public void setDirectory(boolean directory) {
+ this.directory = directory;
+ }
+}
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemEntry.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+import java.io.File;
+
+/**
+ * A listener that receives events of file system modifications.
+ * <p>
+ * Register {@link FilesystemListener}s with a {@link FilesystemObserver}.
+ *
+ * @see FilesystemObserver
+ * @version $Id$
+ * @since Commons IO 2.0
+ */
+public interface FilesystemListener {
+
+ /**
+ * File system observer started checking event.
+ *
+ * @param observer The file system observer
+ */
+ void onStart(final FilesystemObserver observer);
+
+ /**
+ * Directory created Event.
+ *
+ * @param directory The directory created
+ */
+ void onDirectoryCreate(final File directory);
+
+ /**
+ * Directory changed Event.
+ *
+ * @param directory The directory changed
+ */
+ void onDirectoryChange(final File directory);
+
+ /**
+ * Directory deleted Event.
+ *
+ * @param directory The directory deleted
+ */
+ void onDirectoryDelete(final File directory);
+
+ /**
+ * File created Event.
+ *
+ * @param file The file created
+ */
+ void onFileCreate(final File file);
+
+ /**
+ * File changed Event.
+ *
+ * @param file The file changed
+ */
+ void onFileChange(final File file);
+
+ /**
+ * File deleted Event.
+ *
+ * @param file The file deleted
+ */
+ void onFileDelete(final File file);
+
+ /**
+ * File system observer finished checking event.
+ *
+ * @param observer The file system observer
+ */
+ void onStop(final FilesystemObserver observer);
+}
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListener.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+
+import java.io.File;
+
+/**
+ * Convenience {@link FilesystemListener} implementation that does nothing.
+ *
+ * @see FilesystemObserver
+ * @version $Id$
+ * @since Commons IO 2.0
+ */
+public class FilesystemListenerAdaptor implements FilesystemListener {
+
+ /**
+ * File system observer started checking event.
+ *
+ * @param observer The file system observer
+ */
+ public void onStart(final FilesystemObserver observer) {
+ }
+
+ /**
+ * Directory created Event.
+ *
+ * @param directory The directory created
+ */
+ public void onDirectoryCreate(final File directory) {
+ }
+
+ /**
+ * Directory changed Event.
+ *
+ * @param directory The directory changed
+ */
+ public void onDirectoryChange(final File directory) {
+ }
+
+ /**
+ * Directory deleted Event.
+ *
+ * @param directory The directory deleted
+ */
+ public void onDirectoryDelete(final File directory) {
+ }
+
+ /**
+ * File created Event.
+ *
+ * @param file The file created
+ */
+ public void onFileCreate(final File file) {
+ }
+
+ /**
+ * File changed Event.
+ *
+ * @param file The file changed
+ */
+ public void onFileChange(final File file) {
+ }
+
+ /**
+ * File deleted Event.
+ *
+ * @param file The file deleted
+ */
+ public void onFileDelete(final File file) {
+ }
+
+ /**
+ * File system observer finished checking event.
+ *
+ * @param observer The file system observer
+ */
+ public void onStop(final FilesystemObserver observer) {
+ }
+
+}
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemListenerAdaptor.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A runnable that spawns a monitoring thread triggering any
+ * registered {@link FilesystemObserver} at a specified interval.
+ *
+ * @see FilesystemObserver
+ * @see FilesystemTimerTask
+ * @version $Id$
+ * @since Commons IO 2.0
+ */
+public final class FilesystemMonitor implements Runnable {
+
+ private final long interval;
+ private final List<FilesystemObserver> observers = new CopyOnWriteArrayList<FilesystemObserver>();
+ private Thread thread = null;
+ private volatile boolean running = false;
+
+ /**
+ * Construct a monitor with a default interval of 10 seconds.
+ */
+ public FilesystemMonitor() {
+ this(10000);
+ }
+
+ /**
+ * Construct a monitor with the specified interval.
+ *
+ * @param interval The amount of time in miliseconds to wait between
+ * checks of the file system
+ */
+ public FilesystemMonitor(long interval) {
+ this.interval = interval;
+ }
+
+ /**
+ * Construct a monitor with the specified interval and set of observers.
+ *
+ * @param interval The amount of time in miliseconds to wait between
+ * checks of the file system
+ * @param observers The set of observers to add to the monitor.
+ */
+ public FilesystemMonitor(long interval, FilesystemObserver... observers) {
+ this(interval);
+ if (observers != null) {
+ for (int i = 0; i < observers.length; i++) {
+ addObserver(observers[i]);
+ }
+ }
+ }
+
+ /**
+ * Add a file system observer to this monitor.
+ *
+ * @param observer The file system observer to add
+ */
+ public void addObserver(final FilesystemObserver observer) {
+ if (observer != null) {
+ observers.add(observer);
+ }
+ }
+
+ /**
+ * Remove a file system observer from this monitor.
+ *
+ * @param observer The file system observer to remove
+ */
+ public void removeObserver(final FilesystemObserver observer) {
+ if (observer != null) {
+ while (observers.remove(observer)) {
+ }
+ }
+ }
+
+ /**
+ * Returns the set of {@link FilesystemObserver} registered with
+ * this monitor.
+ *
+ * @return The set of {@link FilesystemObserver}
+ */
+ public Iterable<FilesystemObserver> getObservers() {
+ return observers;
+ }
+
+ /**
+ * Start monitoring.
+ *
+ * @throws Exception if an error occurs initializing the observer
+ */
+ public void start() throws Exception {
+ for (FilesystemObserver observer : observers) {
+ observer.initialize();
+ }
+ running = true;
+ thread = new Thread(this);
+ thread.start();
+ }
+
+ /**
+ * Stop monitoring.
+ *
+ * @throws Exception if an error occurs initializing the observer
+ */
+ public void stop() throws Exception {
+ running = false;
+ try {
+ thread.join(interval);
+ } catch (InterruptedException e) {
+ }
+ for (FilesystemObserver observer : observers) {
+ observer.destroy();
+ }
+ }
+
+ /**
+ * Run.
+ */
+ public void run() {
+ while (true) {
+ if (!running) {
+ break;
+ }
+ for (FilesystemObserver observer : observers) {
+ observer.checkAndNotify();
+ }
+ if (!running) {
+ break;
+ }
+ try {
+ Thread.sleep(interval);
+ } catch (final InterruptedException e) {
+ }
+ }
+ }
+}
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemMonitor.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.commons.io.comparator.NameFileComparator;
+
+/**
+ * FilesystemObserver represents the state of files below a root directory,
+ * checking the filesystem and notifying listeners of create, change or
+ * delete events.
+ * <p>
+ * To use this implementation:
+ * <ul>
+ * <li>Create {@link FilesystemListener} implementation(s) that process
+ * the file/directory create, change and delete events</li>
+ * <li>Register the listener(s) with a {@link FilesystemObserver} for
+ * the appropriate directory.</li>
+ * <li>Either register the observer(s) with a {@link FilesystemMonitor} or
+ * run manually.</li>
+ * </ul>
+ *
+ * <h2>Basic Usage</h2>
+ * Create a {@link FilesystemObserver} for the directory and register the listeners:
+ * <pre>
+ * File directory = new File(new File("."), "src");
+ * FilesystemObserver observer = new FilesystemObserver(directory);
+ * observer.addListener(...);
+ * observer.addListener(...);
+ * </pre>
+ * To manually observe a directory, initialize the observer and invoked the
+ * {@link #checkAndNotify()} method as required:
+ * <pre>
+ * // intialize
+ * observer.init();
+ * ...
+ * // invoke as required
+ * observer.checkAndNotify();
+ * ...
+ * observer.checkAndNotify();
+ * ...
+ * // finished
+ * observer.finish();
+ * </pre>
+ * Alternatively, register the oberver(s) with a {@link FilesystemMonitor},
+ * which creates a new thread, invoking the observer at the specified interval:
+ * <pre>
+ * long interval = ...
+ * FilesystemMonitor monitor = new FilesystemMonitor(interval);
+ * monitor.addObserver(observer);
+ * monitor.start();
+ * ...
+ * monitor.stop();
+ * </pre>
+ *
+ * <h2>File Filters</h2>
+ * This implementation can monitor portions of the file system
+ * by using {@link FileFilter}s to observe only the files and/or directories
+ * that are of interest. This makes it more efficient and reduces the
+ * noise from <i>unwanted</i> file system events.
+ * <p>
+ * <a href="http://commons.apache.org/io/">Commons IO</a> has a good range of
+ * useful, ready made
+ * <a href="apidocs/org/apache/commons/io/filefilter/package-summary.html">File Filter</a>
+ * implementations for this purpose.
+ * <p>
+ * For example, to only observe 1) visible directories and 2) files with a ".java" suffix
+ * in a root directory called "src" you could set up a {@link FilesystemObserver} in the following
+ * way:
+ * <pre>
+ * // Create a FileFilter
+ * IOFileFilter directories = FileFilterUtils.directoryFileFilter();
+ * IOFileFilter visible = HiddenFileFilter.VISIBLE;
+ * IOFileFilter dirFilter = FileFilterUtils.andFileFilter(directories, visible);
+ * IOFileFilter files = FileFilterUtils.fileFileFilter();
+ * IOFileFilter javaSuffix = FileFilterUtils.suffixFileFilter(".java");
+ * IOFileFilter fileFilter = FileFilterUtils.andFileFilter(files, javaSuffix);
+ * IOFileFilter filter = FileFilterUtils.orFileFilter(dirFilter, fileFilter);
+ *
+ * // Create the File system observer and register File Listeners
+ * FilesystemObserver observer = new FilesystemObserver(new File("src"), filter);
+ * observer.addListener(...);
+ * observer.addListener(...);
+ *
+ * //
+ * </pre>
+ *
+ * <h2>File Comparator</h2>
+ * This implementation works by comparing the file names of the current contents of
+ * a directory with the previous contents using the <i>case-sensitive</i>
+ * {@link NameFileComparator#NAME_COMPARATOR} to determine which files have been created,
+ * deleted or still exist. However a custom {@link Comparator} can be specified and
+ * one example usage would be to compare file names in a <i>case-insensitive</i>
+ * manner (@link {@link NameFileComparator#NAME_INSENSITIVE_COMPARATOR} could be used
+ * to do that).
+ *
+ * <h2>FilesystemEntry</h2>
+ * {@link FilesystemEntry} represents the state of a file or directory, capturing
+ * {@link File} attributes at a point in time. Custom implementations of
+ * {@link FilesystemEntry} can be used to capture additional properties that the
+ * basic implementation does not support. The {@link FilesystemEntry#hasChanged()}
+ * method is used to determine if a file or directory has changed since the last
+ * check. {@link FilesystemEntry#refresh()} stores the current state of the
+ * {@link File}'s properties.
+ *
+ * @see FilesystemListener
+ * @see FilesystemMonitor
+ * @version $Id$
+ * @since Commons IO 2.0
+ */
+public class FilesystemObserver implements Serializable {
+
+ private static final File[] EMPTY_FILES = new File[0];
+ static final FilesystemEntry[] EMPTY_ENTRIES = new FilesystemEntry[0];
+
+ private final List<FilesystemListener> listeners = new CopyOnWriteArrayList<FilesystemListener>();
+ private final FilesystemEntry rootEntry;
+ private final FileFilter fileFilter;
+ private final Comparator<File> comparator;
+
+ /**
+ * Construct an observer for the specified directory.
+ *
+ * @param directoryName the name of the directory to observe
+ */
+ public FilesystemObserver(String directoryName) {
+ this(new File(directoryName));
+ }
+
+ /**
+ * Construct an observer for the specified directory and file filter.
+ *
+ * @param directoryName the name of the directory to observe
+ * @param fileFilter The file filter or null if none
+ */
+ public FilesystemObserver(String directoryName, FileFilter fileFilter) {
+ this(new File(directoryName), fileFilter);
+ }
+
+ /**
+ * Construct an observer for the specified directory, file filter and
+ * file comparator.
+ *
+ * @param directoryName the name of the directory to observe
+ * @param fileFilter The file filter or null if none
+ * @param comparator The comparator to use when comparing file names, may be null
+ */
+ public FilesystemObserver(String directoryName, FileFilter fileFilter, Comparator<File> comparator) {
+ this(new File(directoryName), fileFilter, comparator);
+ }
+
+ /**
+ * Construct an observer for the specified directory.
+ *
+ * @param directory the directory to observe
+ */
+ public FilesystemObserver(File directory) {
+ this(directory, (FileFilter)null);
+ }
+
+ /**
+ * Construct an observer for the specified directory and file filter.
+ *
+ * @param directory the directory to observe
+ * @param fileFilter The file filter or null if none
+ */
+ public FilesystemObserver(File directory, FileFilter fileFilter) {
+ this(directory, fileFilter, (Comparator<File>)null);
+ }
+
+ /**
+ * Construct an observer for the specified directory, file filter and
+ * file comparator.
+ *
+ * @param directory the directory to observe
+ * @param fileFilter The file filter or null if none
+ * @param comparator The comparator to use when comparing file names, may be null
+ */
+ public FilesystemObserver(File directory, FileFilter fileFilter, Comparator<File> comparator) {
+ this(new FilesystemEntry(directory), fileFilter, comparator);
+ }
+
+ /**
+ * Construct an observer for the specified directory, file filter and
+ * file comparator.
+ *
+ * @param rootEntry the root directory to observe
+ * @param fileFilter The file filter or null if none
+ * @param comparator The comparator to use when comparing file names, may be null
+ */
+ protected FilesystemObserver(FilesystemEntry rootEntry, FileFilter fileFilter, Comparator<File> comparator) {
+ if (rootEntry == null) {
+ throw new IllegalArgumentException("Root entry is missing");
+ }
+ if (rootEntry.getFile() == null) {
+ throw new IllegalArgumentException("Root directory is missing");
+ }
+ this.rootEntry = rootEntry;
+ this.fileFilter = fileFilter;
+ if (comparator == null) {
+ this.comparator = NameFileComparator.NAME_COMPARATOR;
+ } else {
+ this.comparator = comparator;
+ }
+ }
+
+ /**
+ * Return the entry for the root directory.
+ *
+ * @return the entry for the root directory
+ */
+ public FilesystemEntry getRootEntry() {
+ return rootEntry;
+ }
+
+ /**
+ * Return the directory being observed.
+ *
+ * @return the directory being observed
+ */
+ public File getDirectory() {
+ return rootEntry.getFile();
+ }
+
+ /**
+ * Return the file filter, if any.
+ *
+ * @return the file filteror <code>null</code> if none
+ */
+ public FileFilter getFileFilter() {
+ return fileFilter;
+ }
+
+ /**
+ * Return the comparator.
+ *
+ * @return the comparator
+ */
+ public Comparator<File> getComparator() {
+ return comparator;
+ }
+
+ /**
+ * Add a file system listener.
+ *
+ * @param listener The file system listener
+ */
+ public void addListener(final FilesystemListener listener) {
+ if (listener != null) {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove a file system listener.
+ *
+ * @param listener The file system listener
+ */
+ public void removeListener(final FilesystemListener listener) {
+ if (listener != null) {
+ while (listeners.remove(listener)) {
+ }
+ }
+ }
+
+ /**
+ * Returns the set of registered file system listeners.
+ *
+ * @return The file system listeners
+ */
+ public Iterable<FilesystemListener> getListeners() {
+ return listeners;
+ }
+
+ /**
+ * Initialize the observer.
+ *
+ * @throws Exception if an error occurs
+ */
+ public void initialize() throws Exception {
+ rootEntry.refresh();
+ File[] files = listFiles(rootEntry.getFile());
+ FilesystemEntry[] children = files.length > 0 ? new FilesystemEntry[files.length] : EMPTY_ENTRIES;
+ for (int i = 0; i < files.length; i++) {
+ children[i] = createFileEntry(rootEntry, files[i]);
+ }
+ rootEntry.setChildren(children);
+ }
+
+ /**
+ * Final processing.
+ *
+ * @throws Exception if an error occurs
+ */
+ public void destroy() throws Exception {
+ }
+
+ /**
+ * Check whether the file and its chlidren have been created, modified or deleted.
+ */
+ public void checkAndNotify() {
+
+ /* fire onStart() */
+ for (FilesystemListener listener : listeners) {
+ listener.onStart(this);
+ }
+
+ /* fire directory/file events */
+ File rootFile = rootEntry.getFile();
+ if (rootFile.exists()) {
+ checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile));
+ } else if (rootEntry.isExists()) {
+ checkAndNotify(rootEntry, rootEntry.getChildren(), EMPTY_FILES);
+ } else {
+ // Didn't exist and still doesn't
+ }
+
+ /* fire onStop() */
+ for (FilesystemListener listener : listeners) {
+ listener.onStop(this);
+ }
+ }
+
+ /**
+ * Compare two file lists for files which have been created, modified or deleted.
+ *
+ * @param parent The parent entry
+ * @param previous The original list of files
+ * @param files The current list of files
+ */
+ private void checkAndNotify(FilesystemEntry parent, FilesystemEntry[] previous, File[] files) {
+ int c = 0;
+ FilesystemEntry[] current = files.length > 0 ? new FilesystemEntry[files.length] : EMPTY_ENTRIES;
+ for (int p = 0; p < previous.length; p++) {
+ while (c < files.length && comparator.compare(previous[p].getFile(), files[c]) > 0) {
+ current[c] = createFileEntry(parent, files[c]);
+ doCreate(current[c]);
+ c++;
+ }
+ if (c < files.length && comparator.compare(previous[p].getFile(), files[c]) == 0) {
+ doMatch(previous[p], files[c]);
+ checkAndNotify(previous[p], previous[p].getChildren(), listFiles(files[c]));
+ current[c] = previous[p];
+ c++;
+ } else {
+ checkAndNotify(previous[p], previous[p].getChildren(), EMPTY_FILES);
+ doDelete(previous[p]);
+ }
+ }
+ for (; c < files.length; c++) {
+ current[c] = createFileEntry(parent, files[c]);
+ doCreate(current[c]);
+ }
+ parent.setChildren(current);
+ }
+
+ /**
+ * Create a new file entry for the specified file.
+ *
+ * @param parent The parent file entry
+ * @param file The file to create an entry for
+ * @return A new file entry
+ */
+ private FilesystemEntry createFileEntry(FilesystemEntry parent, File file) {
+ FilesystemEntry entry = parent.newChildInstance(file);
+ entry.refresh();
+ File[] files = listFiles(file);
+ FilesystemEntry[] children = files.length > 0 ? new FilesystemEntry[files.length] : EMPTY_ENTRIES;
+ for (int i = 0; i < files.length; i++) {
+ children[i] = createFileEntry(entry, files[i]);
+ }
+ entry.setChildren(children);
+ return entry;
+ }
+
+ /**
+ * Fire directory/file created events to the registered listeners.
+ *
+ * @param entry The file entry
+ */
+ private void doCreate(FilesystemEntry entry) {
+ for (FilesystemListener listener : listeners) {
+ if (entry.isDirectory()) {
+ listener.onDirectoryCreate(entry.getFile());
+ } else {
+ listener.onFileCreate(entry.getFile());
+ }
+ }
+ FilesystemEntry[] children = entry.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ doCreate(children[i]);
+ }
+ }
+
+ /**
+ * Fire directory/file change events to the registered listeners.
+ *
+ * @param entry The previous file system entry
+ * @param file The current file
+ */
+ private void doMatch(FilesystemEntry entry, File file) {
+ if (entry.hasChanged()) {
+ for (FilesystemListener listener : listeners) {
+ if (entry.isDirectory()) {
+ listener.onDirectoryChange(entry.getFile());
+ } else {
+ listener.onFileChange(entry.getFile());
+ }
+ }
+ entry.refresh();
+ }
+ entry.setFile(file);
+ }
+
+ /**
+ * Fire directory/file delete events to the registered listeners.
+ *
+ * @param entry The file entry
+ */
+ private void doDelete(FilesystemEntry entry) {
+ for (FilesystemListener listener : listeners) {
+ if (entry.isDirectory()) {
+ listener.onDirectoryDelete(entry.getFile());
+ } else {
+ listener.onFileDelete(entry.getFile());
+ }
+ }
+ }
+
+ /**
+ * List the contents of a directory
+ *
+ * @param file The file to list the contents of
+ * @return the directory contents or a zero length array if
+ * the empty or the file is not a directory
+ */
+ private File[] listFiles(File file) {
+ File[] children = null;
+ if (file.isDirectory()) {
+ children = (fileFilter == null) ? file.listFiles() : file.listFiles(fileFilter);
+ }
+ if (children == null) {
+ children = EMPTY_FILES;
+ }
+ if (comparator != null && children.length > 1) {
+ Arrays.sort(children, comparator);
+ }
+ return children;
+ }
+
+ /**
+ * Provide a String representation of this observer.
+ *
+ * @return a String representation of this observer
+ */
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append("[file='");
+ builder.append(getDirectory().getPath());
+ if (fileFilter != null) {
+ builder.append(", ");
+ builder.append(fileFilter.toString());
+ }
+ builder.append(", listeners=");
+ builder.append(listeners.size());
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/FilesystemObserver.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html Sun Apr 6 02:56:32 2008
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<body>
+<p>
+This package provides a component for monitoring file system events
+(i.e. directory and file create, update and delete events).
+</p>
+</body>
+</html>
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/monitor/package.html
------------------------------------------------------------------------------
svn:mime-type = text/html
Added: commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java (added)
+++ commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * {@link FilesystemListener} implementation that adds created, changed and deleted
+ * files/directories to a set of {@link Collection}s.
+ */
+public class CollectionFilesystemListener implements FilesystemListener, Serializable {
+
+ private final Collection<File> createdFiles = new ArrayList<File>();
+ private final Collection<File> changedFiles = new ArrayList<File>();
+ private final Collection<File> deletedFiles = new ArrayList<File>();
+ private final Collection<File> createdDirectories = new ArrayList<File>();
+ private final Collection<File> changedDirectories = new ArrayList<File>();
+ private final Collection<File> deletedDirectories = new ArrayList<File>();
+
+ /**
+ * File system observer started checking event.
+ *
+ * @param observer The file system observer
+ */
+ public void onStart(final FilesystemObserver observer) {
+ createdFiles.clear();
+ changedFiles.clear();
+ deletedFiles.clear();
+ createdDirectories.clear();
+ changedDirectories.clear();
+ deletedDirectories.clear();
+ }
+
+ /**
+ * Return the set of changed directories.
+ *
+ * @return Directories which have changed
+ */
+ public Collection getChangedDirectories() {
+ return changedDirectories;
+ }
+
+ /**
+ * Return the set of changed files.
+ *
+ * @return Files which have changed
+ */
+ public Collection getChangedFiles() {
+ return changedFiles;
+ }
+
+ /**
+ * Return the set of created directories.
+ *
+ * @return Directories which have been created
+ */
+ public Collection getCreatedDirectories() {
+ return createdDirectories;
+ }
+
+ /**
+ * Return the set of created files.
+ *
+ * @return Files which have been created
+ */
+ public Collection getCreatedFiles() {
+ return createdFiles;
+ }
+
+ /**
+ * Return the set of deleted directories.
+ *
+ * @return Directories which been deleted
+ */
+ public Collection getDeletedDirectories() {
+ return deletedDirectories;
+ }
+
+ /**
+ * Return the set of deleted files.
+ *
+ * @return Files which been deleted
+ */
+ public Collection getDeletedFiles() {
+ return deletedFiles;
+ }
+
+ /**
+ * Directory created Event.
+ *
+ * @param directory The directory created
+ */
+ public void onDirectoryCreate(final File directory) {
+ createdDirectories.add(directory);
+ }
+
+ /**
+ * Directory changed Event.
+ *
+ * @param directory The directory changed
+ */
+ public void onDirectoryChange(final File directory) {
+ changedDirectories.add(directory);
+ }
+
+ /**
+ * Directory deleted Event.
+ *
+ * @param directory The directory deleted
+ */
+ public void onDirectoryDelete(final File directory) {
+ deletedDirectories.add(directory);
+ }
+
+ /**
+ * File created Event.
+ *
+ * @param file The file created
+ */
+ public void onFileCreate(final File file) {
+ createdFiles.add(file);
+ }
+
+ /**
+ * File changed Event.
+ *
+ * @param file The file changed
+ */
+ public void onFileChange(final File file) {
+ changedFiles.add(file);
+ }
+
+ /**
+ * File deleted Event.
+ *
+ * @param file The file deleted
+ */
+ public void onFileDelete(final File file) {
+ deletedFiles.add(file);
+ }
+
+ /**
+ * File system observer finished checking event.
+ *
+ * @param observer The file system observer
+ */
+ public void onStop(final FilesystemObserver observer) {
+ }
+
+}
Propchange: commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/CollectionFilesystemListener.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java?rev=645221&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java (added)
+++ commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java Sun Apr 6 02:56:32 2008
@@ -0,0 +1,449 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io.monitor;
+
+import java.io.File;
+import java.io.FileFilter;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.HiddenFileFilter;
+import org.apache.commons.io.filefilter.IOFileFilter;
+
+import junit.framework.TestCase;
+
+/**
+ * {@link FilesystemObserver} Test Case.
+ */
+public class FilesystemObserverTestCase extends TestCase {
+
+ /** Filesystem observer */
+ protected FilesystemObserver observer;
+
+ /** Listener which collects file changes */
+ protected CollectionFilesystemListener listener = new CollectionFilesystemListener();
+
+ /** Directory for test files */
+ protected File testDir;
+
+ /**
+ * Construct a new test case.
+ *
+ * @param name The name of the test
+ */
+ public FilesystemObserverTestCase(String name) {
+ super(name);
+ }
+
+ protected void setUp() throws Exception {
+ testDir = new File(new File("."), "test-monitor");
+ if (testDir.exists()) {
+ FileUtils.cleanDirectory(testDir);
+ } else {
+ testDir.mkdir();
+ }
+
+ IOFileFilter files = FileFilterUtils.fileFileFilter();
+ IOFileFilter javaSuffix = FileFilterUtils.suffixFileFilter(".java");
+ IOFileFilter fileFilter = FileFilterUtils.andFileFilter(files, javaSuffix);
+
+ IOFileFilter directories = FileFilterUtils.directoryFileFilter();
+ IOFileFilter visible = HiddenFileFilter.VISIBLE;
+ IOFileFilter dirFilter = FileFilterUtils.andFileFilter(directories, visible);
+
+ IOFileFilter filter = FileFilterUtils.orFileFilter(dirFilter, fileFilter);
+
+ createObserver(testDir, filter);
+ }
+
+ /**
+ * Create a {@link FilesystemObserver}.
+ *
+ * @param file The directory to observe
+ * @param fileFilter The file filter to apply
+ */
+ protected void createObserver(File file, FileFilter fileFilter) {
+ observer = new FilesystemObserver(file, fileFilter);
+ // FilesystemListener debuglistener = new JDKLoggingFilesystemListener(getClass().getSimpleName(), Level.FINEST);
+ // observer.addListener(debuglistener);
+ observer.addListener(listener);
+ try {
+ observer.initialize();
+ } catch (Exception e) {
+ fail("Observer init() threw " + e);
+ }
+ }
+
+ protected void tearDown() throws Exception {
+ FileUtils.deleteDirectory(testDir);
+ }
+
+ /**
+ * Test checkAndNotify() method
+ */
+ public void testDirectory() {
+ try {
+ checkAndNotify();
+ checkCollectionsEmpty("A");
+ File testDirA = new File(testDir, "test-dir-A");
+ File testDirB = new File(testDir, "test-dir-B");
+ File testDirC = new File(testDir, "test-dir-C");
+ testDirA.mkdir();
+ testDirB.mkdir();
+ testDirC.mkdir();
+ File testDirAFile1 = touch(new File(testDirA, "A-file1.java"));
+ File testDirAFile2 = touch(new File(testDirA, "A-file2.txt")); // filter should ignore this
+ File testDirAFile3 = touch(new File(testDirA, "A-file3.java"));
+ File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
+ File testDirBFile1 = touch(new File(testDirB, "B-file1.java"));
+
+ checkAndNotify();
+ checkCollectionSizes("B", 3, 0, 0, 4, 0, 0);
+ assertTrue("B testDirA", listener.getCreatedDirectories().contains(testDirA));
+ assertTrue("B testDirB", listener.getCreatedDirectories().contains(testDirB));
+ assertTrue("B testDirC", listener.getCreatedDirectories().contains(testDirC));
+ assertTrue("B testDirAFile1", listener.getCreatedFiles().contains(testDirAFile1));
+ assertFalse("B testDirAFile2", listener.getCreatedFiles().contains(testDirAFile2));
+ assertTrue("B testDirAFile3", listener.getCreatedFiles().contains(testDirAFile3));
+ assertTrue("B testDirAFile4", listener.getCreatedFiles().contains(testDirAFile4));
+ assertTrue("B testDirBFile1", listener.getCreatedFiles().contains(testDirBFile1));
+
+ checkAndNotify();
+ checkCollectionsEmpty("C");
+
+ touch(testDirAFile4);
+ FileUtils.deleteDirectory(testDirB);
+ checkAndNotify();
+ checkCollectionSizes("D", 0, 0, 1, 0, 1, 1);
+ assertTrue("D testDirB", listener.getDeletedDirectories().contains(testDirB));
+ assertTrue("D testDirAFile4", listener.getChangedFiles().contains(testDirAFile4));
+ assertTrue("D testDirBFile1", listener.getDeletedFiles().contains(testDirBFile1));
+
+ FileUtils.deleteDirectory(testDir);
+ checkAndNotify();
+ checkCollectionSizes("E", 0, 0, 2, 0, 0, 3);
+ assertTrue("E testDirA", listener.getDeletedDirectories().contains(testDirA));
+ assertTrue("E testDirAFile1", listener.getDeletedFiles().contains(testDirAFile1));
+ assertFalse("E testDirAFile2", listener.getDeletedFiles().contains(testDirAFile2));
+ assertTrue("E testDirAFile3", listener.getDeletedFiles().contains(testDirAFile3));
+ assertTrue("E testDirAFile4", listener.getDeletedFiles().contains(testDirAFile4));
+
+ testDir.mkdir();
+ checkAndNotify();
+ checkCollectionsEmpty("F");
+
+ checkAndNotify();
+ checkCollectionsEmpty("G");
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Threw " + e);
+ }
+ }
+
+ /**
+ * Test checkAndNotify() creating
+ */
+ public void testFileCreate() {
+ try {
+ checkAndNotify();
+ checkCollectionsEmpty("A");
+ File testDirA = new File(testDir, "test-dir-A");
+ testDirA.mkdir();
+ touch(testDir);
+ touch(testDirA);
+ File testDirAFile1 = new File(testDirA, "A-file1.java");
+ File testDirAFile2 = touch(new File(testDirA, "A-file2.java"));
+ File testDirAFile3 = new File(testDirA, "A-file3.java");
+ File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
+ File testDirAFile5 = new File(testDirA, "A-file5.java");
+
+ checkAndNotify();
+ checkCollectionSizes("B", 1, 0, 0, 2, 0, 0);
+ assertFalse("B testDirAFile1", listener.getCreatedFiles().contains(testDirAFile1));
+ assertTrue("B testDirAFile2", listener.getCreatedFiles().contains(testDirAFile2));
+ assertFalse("B testDirAFile3", listener.getCreatedFiles().contains(testDirAFile3));
+ assertTrue("B testDirAFile4", listener.getCreatedFiles().contains(testDirAFile4));
+ assertFalse("B testDirAFile5", listener.getCreatedFiles().contains(testDirAFile5));
+
+ assertFalse("B testDirAFile1 exists", testDirAFile1.exists());
+ assertTrue("B testDirAFile2 exists", testDirAFile2.exists());
+ assertFalse("B testDirAFile3 exists", testDirAFile3.exists());
+ assertTrue("B testDirAFile4 exists", testDirAFile4.exists());
+ assertFalse("B testDirAFile5 exists", testDirAFile5.exists());
+
+ checkAndNotify();
+ checkCollectionsEmpty("C");
+
+ // Create file with name < first entry
+ touch(testDirAFile1);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("D", 0, 1, 0, 1, 0, 0);
+ assertTrue("D testDirAFile1 exists", testDirAFile1.exists());
+ assertTrue("D testDirAFile1", listener.getCreatedFiles().contains(testDirAFile1));
+
+ // Create file with name between 2 entries
+ touch(testDirAFile3);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("E", 0, 1, 0, 1, 0, 0);
+ assertTrue("E testDirAFile3 exists", testDirAFile3.exists());
+ assertTrue("E testDirAFile3", listener.getCreatedFiles().contains(testDirAFile3));
+
+ // Create file with name > last entry
+ touch(testDirAFile5);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("F", 0, 1, 0, 1, 0, 0);
+ assertTrue("F testDirAFile5 exists", testDirAFile5.exists());
+ assertTrue("F testDirAFile5", listener.getCreatedFiles().contains(testDirAFile5));
+ } catch (Exception e) {
+ fail("Threw " + e);
+ }
+ }
+
+ /**
+ * Test checkAndNotify() creating
+ */
+ public void testFileUpdate() {
+ try {
+ checkAndNotify();
+ checkCollectionsEmpty("A");
+ File testDirA = new File(testDir, "test-dir-A");
+ testDirA.mkdir();
+ touch(testDir);
+ touch(testDirA);
+ File testDirAFile1 = touch(new File(testDirA, "A-file1.java"));
+ File testDirAFile2 = touch(new File(testDirA, "A-file2.java"));
+ File testDirAFile3 = touch(new File(testDirA, "A-file3.java"));
+ File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
+ File testDirAFile5 = touch(new File(testDirA, "A-file5.java"));
+
+ checkAndNotify();
+ checkCollectionSizes("B", 1, 0, 0, 5, 0, 0);
+ assertTrue("B testDirAFile1", listener.getCreatedFiles().contains(testDirAFile1));
+ assertTrue("B testDirAFile2", listener.getCreatedFiles().contains(testDirAFile2));
+ assertTrue("B testDirAFile3", listener.getCreatedFiles().contains(testDirAFile3));
+ assertTrue("B testDirAFile4", listener.getCreatedFiles().contains(testDirAFile4));
+ assertTrue("B testDirAFile5", listener.getCreatedFiles().contains(testDirAFile5));
+
+ assertTrue("B testDirAFile1 exists", testDirAFile1.exists());
+ assertTrue("B testDirAFile2 exists", testDirAFile2.exists());
+ assertTrue("B testDirAFile3 exists", testDirAFile3.exists());
+ assertTrue("B testDirAFile4 exists", testDirAFile4.exists());
+ assertTrue("B testDirAFile5 exists", testDirAFile5.exists());
+
+ checkAndNotify();
+ checkCollectionsEmpty("C");
+
+ // Update first entry
+ touch(testDirAFile1);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("D", 0, 1, 0, 0, 1, 0);
+ assertTrue("D testDirAFile1", listener.getChangedFiles().contains(testDirAFile1));
+
+ // Update file with name between 2 entries
+ touch(testDirAFile3);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("E", 0, 1, 0, 0, 1, 0);
+ assertTrue("E testDirAFile3", listener.getChangedFiles().contains(testDirAFile3));
+
+ // Update last entry
+ touch(testDirAFile5);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("F", 0, 1, 0, 0, 1, 0);
+ assertTrue("F testDirAFile5", listener.getChangedFiles().contains(testDirAFile5));
+ } catch (Exception e) {
+ fail("Threw " + e);
+ }
+ }
+
+ /**
+ * Test checkAndNotify() deleting
+ */
+ public void testFileDelete() {
+ try {
+ checkAndNotify();
+ checkCollectionsEmpty("A");
+ File testDirA = new File(testDir, "test-dir-A");
+ testDirA.mkdir();
+ touch(testDir);
+ touch(testDirA);
+ File testDirAFile1 = touch(new File(testDirA, "A-file1.java"));
+ File testDirAFile2 = touch(new File(testDirA, "A-file2.java"));
+ File testDirAFile3 = touch(new File(testDirA, "A-file3.java"));
+ File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
+ File testDirAFile5 = touch(new File(testDirA, "A-file5.java"));
+
+ assertTrue("B testDirAFile1 exists", testDirAFile1.exists());
+ assertTrue("B testDirAFile2 exists", testDirAFile2.exists());
+ assertTrue("B testDirAFile3 exists", testDirAFile3.exists());
+ assertTrue("B testDirAFile4 exists", testDirAFile4.exists());
+ assertTrue("B testDirAFile5 exists", testDirAFile5.exists());
+
+ checkAndNotify();
+ checkCollectionSizes("B", 1, 0, 0, 5, 0, 0);
+ assertTrue("B testDirAFile1", listener.getCreatedFiles().contains(testDirAFile1));
+ assertTrue("B testDirAFile2", listener.getCreatedFiles().contains(testDirAFile2));
+ assertTrue("B testDirAFile3", listener.getCreatedFiles().contains(testDirAFile3));
+ assertTrue("B testDirAFile4", listener.getCreatedFiles().contains(testDirAFile4));
+ assertTrue("B testDirAFile5", listener.getCreatedFiles().contains(testDirAFile5));
+
+ checkAndNotify();
+ checkCollectionsEmpty("C");
+
+ // Delete first entry
+ FileUtils.deleteQuietly(testDirAFile1);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("D", 0, 1, 0, 0, 0, 1);
+ assertFalse("D testDirAFile1 exists", testDirAFile1.exists());
+ assertTrue("D testDirAFile1", listener.getDeletedFiles().contains(testDirAFile1));
+
+ // Delete file with name between 2 entries
+ FileUtils.deleteQuietly(testDirAFile3);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("E", 0, 1, 0, 0, 0, 1);
+ assertFalse("E testDirAFile3 exists", testDirAFile3.exists());
+ assertTrue("E testDirAFile3", listener.getDeletedFiles().contains(testDirAFile3));
+
+ // Delete last entry
+ FileUtils.deleteQuietly(testDirAFile5);
+ touch(testDirA);
+ checkAndNotify();
+ checkCollectionSizes("F", 0, 1, 0, 0, 0, 1);
+ assertFalse("F testDirAFile5 exists", testDirAFile5.exists());
+ assertTrue("F testDirAFile5", listener.getDeletedFiles().contains(testDirAFile5));
+
+ } catch (Exception e) {
+ fail("Threw " + e);
+ }
+ }
+
+ /**
+ * Test checkAndNotify() method
+ */
+ public void testObserveSingleFile() {
+ try {
+ File testDirA = new File(testDir, "test-dir-A");
+ File testDirAFile1 = new File(testDirA, "A-file1.java");
+ testDirA.mkdir();
+
+ FileFilter nameFilter = FileFilterUtils.nameFileFilter(testDirAFile1.getName());
+ createObserver(testDirA, nameFilter);
+ checkAndNotify();
+ checkCollectionsEmpty("A");
+ assertFalse("A testDirAFile1 exists", testDirAFile1.exists());
+
+ // Create
+ touch(testDirAFile1);
+ File testDirAFile2 = touch(new File(testDirA, "A-file2.txt")); /* filter should ignore */
+ File testDirAFile3 = touch(new File(testDirA, "A-file3.java")); /* filter should ignore */
+ assertTrue("B testDirAFile1 exists", testDirAFile1.exists());
+ assertTrue("B testDirAFile2 exists", testDirAFile2.exists());
+ assertTrue("B testDirAFile3 exists", testDirAFile3.exists());
+ checkAndNotify();
+ checkCollectionSizes("C", 0, 0, 0, 1, 0, 0);
+ assertTrue("C created", listener.getCreatedFiles().contains(testDirAFile1));
+ assertFalse("C created", listener.getCreatedFiles().contains(testDirAFile2));
+ assertFalse("C created", listener.getCreatedFiles().contains(testDirAFile3));
+
+ // Modify
+ touch(testDirAFile1);
+ touch(testDirAFile2);
+ touch(testDirAFile3);
+ checkAndNotify();
+ checkCollectionSizes("D", 0, 0, 0, 0, 1, 0);
+ assertTrue("D changed", listener.getChangedFiles().contains(testDirAFile1));
+ assertFalse("D changed", listener.getChangedFiles().contains(testDirAFile2));
+ assertFalse("D changed", listener.getChangedFiles().contains(testDirAFile3));
+
+ // Delete
+ FileUtils.deleteQuietly(testDirAFile1);
+ FileUtils.deleteQuietly(testDirAFile2);
+ FileUtils.deleteQuietly(testDirAFile3);
+ assertFalse("E testDirAFile1 exists", testDirAFile1.exists());
+ assertFalse("E testDirAFile2 exists", testDirAFile2.exists());
+ assertFalse("E testDirAFile3 exists", testDirAFile3.exists());
+ checkAndNotify();
+ checkCollectionSizes("E", 0, 0, 0, 0, 0, 1);
+ assertTrue("E deleted", listener.getDeletedFiles().contains(testDirAFile1));
+ assertFalse("E deleted", listener.getDeletedFiles().contains(testDirAFile2));
+ assertFalse("E deleted", listener.getDeletedFiles().contains(testDirAFile3));
+
+ } catch (Exception e) {
+ fail("Threw " + e);
+ }
+ }
+
+ /**
+ * Call {@link FilesystemObserver#checkAndNotify()}.
+ *
+ * @throws Exception if an error occurs
+ */
+ protected void checkAndNotify() throws Exception {
+ observer.checkAndNotify();
+ }
+
+ /**
+ * Check all the Collections are empty
+ */
+ private void checkCollectionsEmpty(String label) {
+ checkCollectionSizes("EMPTY-" + label, 0, 0, 0, 0, 0, 0);
+ }
+
+ /**
+ * Check all the Collections have the expected sizes.
+ */
+ private void checkCollectionSizes(String label, int dirCreate, int dirChange, int dirDelete, int fileCreate, int fileChange, int fileDelete) {
+ assertEquals(label + ": No. of directories created", dirCreate, listener.getCreatedDirectories().size());
+ assertEquals(label + ": No. of directories changed", dirChange, listener.getChangedDirectories().size());
+ assertEquals(label + ": No. of directories deleted", dirDelete, listener.getDeletedDirectories().size());
+ assertEquals(label + ": No. of files created", fileCreate, listener.getCreatedFiles().size());
+ assertEquals(label + ": No. of files changed", fileChange, listener.getChangedFiles().size());
+ assertEquals(label + ": No. of files deleted", fileDelete, listener.getDeletedFiles().size());
+ }
+
+ /**
+ * Either creates a file if it doesn't exist or updates the last modified date/time
+ * if it does.
+ *
+ * @param file The file to touch
+ * @return The file
+ */
+ private File touch(File file) {
+ long lastModified = file.exists() ? file.lastModified() : 0;
+ try {
+ FileUtils.touch(file);
+ while (lastModified == file.lastModified()) {
+ try {
+ Thread.sleep(5);
+ } catch(InterruptedException ie) {
+ // ignore
+ }
+ FileUtils.touch(file);
+ }
+ } catch (Exception e) {
+ fail("Touching " + file +": " + e);
+ }
+ return file;
+ }
+}
Propchange: commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/io/trunk/src/test/org/apache/commons/io/monitor/FilesystemObserverTestCase.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL