/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Simple WatchService implementation that uses periodic tasks to poll
* registered directories for changes. This implementation is for use on
* operating systems that do not have native file change notification support.
*/
class PollingWatchService
extends AbstractWatchService
{
// map of registrations
// used to execute the periodic tasks that poll for changes
// TBD: Make the number of threads configurable
t.setDaemon(true);
return t;
}});
}
/**
* Register the given file with this watch service
*/
throws IOException
{
// check events - CCE will be thrown if there are invalid elements
throw new IllegalArgumentException("No events to register");
// standard events
{
continue;
}
// OVERFLOW is ignored
throw new IllegalArgumentException("No events to register");
continue;
}
// null/unsupported
throw new NullPointerException("An element in event set is 'null'");
}
// A modifier may be used to specify the sensitivity level
throw new NullPointerException();
if (modifier instanceof SensitivityWatchEventModifier) {
continue;
}
throw new UnsupportedOperationException("Modifier not supported");
}
}
// check if watch service is closed
if (!isOpen())
throw new ClosedWatchServiceException();
// registration is done in privileged block as it requires the
// attributes of the entries in the directory.
try {
final SensitivityWatchEventModifier s = sensivity;
return AccessController.doPrivileged(
new PrivilegedExceptionAction<PollingWatchKey>() {
}
});
} catch (PrivilegedActionException pae) {
throw (IOException)cause;
throw new AssertionError(pae);
}
}
// registers directory returning a new key if not already registered or
// existing key if already registered
throws IOException
{
// check file is a directory and get its file key if possible
if (!attrs.isDirectory()) {
}
throw new AssertionError("File keys must be supported");
// grab close lock to ensure that watch service cannot be closed
synchronized (closeLock()) {
if (!isOpen())
throw new ClosedWatchServiceException();
synchronized (map) {
// new registration
} else {
// update to existing registration
}
}
return watchKey;
}
}
synchronized (map) {
}
}
return null;
}
});
}
/**
* Entry in directory cache to record file last-modified-time and tick-count
*/
private static class CacheEntry {
private long lastModified;
private int lastTickCount;
this.lastModified = lastModified;
this.lastTickCount = lastTickCount;
}
int lastTickCount() {
return lastTickCount;
}
long lastModified() {
return lastModified;
}
this.lastModified = lastModified;
this.lastTickCount = tickCount;
}
}
/**
* WatchKey implementation that encapsulates a map of the entries of the
* entries in the directory. Polling the key causes it to re-scan the
* directory and queue keys when entries are added, modified, or deleted.
*/
// current event set
// the result of the periodic task that causes this key to be polled
// indicates if the key is valid
private volatile boolean valid;
// used to detect files that have been deleted
private int tickCount;
// map of entries in directory
throws IOException
{
this.valid = true;
this.tickCount = 0;
// get the initial entries in the directory
// don't follow links
long lastModified =
}
} catch (DirectoryIteratorException e) {
throw e.getCause();
}
}
return fileKey;
}
public boolean isValid() {
return valid;
}
void invalidate() {
valid = false;
}
// enables periodic polling
synchronized (this) {
// update the events
// create the periodic task
this.poller = scheduledExecutor
}
}
// disables periodic polling
void disable() {
synchronized (this) {
}
}
public void cancel() {
valid = false;
synchronized (map) {
}
disable();
}
/**
* Polls the directory to detect for new files, modified files, or
* deleted files.
*/
synchronized void poll() {
if (!valid) {
return;
}
// update tick
tickCount++;
// open directory
try {
} catch (IOException x) {
// directory is no longer accessible so cancel key
cancel();
signal();
return;
}
// iterate over all entries in directory
try {
long lastModified = 0L;
try {
} catch (IOException x) {
// unable to get attributes of entry. If file has just
// been deleted then we'll report it as deleted on the
// next poll
continue;
}
// lookup cache
if (e == null) {
// new file found
// queue ENTRY_CREATE if event enabled
continue;
} else {
// if ENTRY_CREATE is not enabled and ENTRY_MODIFY is
// enabled then queue event to avoid missing out on
// modifications to the file immediately after it is
// created.
}
}
continue;
}
// check if file has changed
if (e.lastModified != lastModified) {
entry.getFileName());
}
}
// entry in cache so update poll time
}
} catch (DirectoryIteratorException e) {
// ignore for now; if the directory is no longer accessible
// then the key will be cancelled on the next poll
} finally {
// close directory stream
try {
} catch (IOException x) {
// ignore
}
}
// iterate over cache to detect entries that have been deleted
while (i.hasNext()) {
// remove from map and queue delete event (if enabled)
i.remove();
}
}
}
}
}
}