/*
* 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.
*/
/*
* Win32 implementation of WatchService based on ReadDirectoryChangesW.
*/
class WindowsWatchService
extends AbstractWatchService
{
// background thread to service I/O completion port
/**
* Creates an I/O completion port and a daemon thread to service it
*/
// create I/O completion port
long port = 0L;
try {
} catch (WindowsException x) {
throw new IOException(x.getMessage());
}
}
throws IOException
{
// delegate to poller
}
// delegate to poller
}
/**
* Windows implementation of WatchKey.
*/
// file key (used to detect existing registrations)
// handle to directory
// interest events
// subtree
private boolean watchSubtree;
// buffer for change events
// pointer to bytes returned (in buffer)
private long countAddress;
// pointer to overlapped structure (in buffer)
private long overlappedAddress;
// completion key (used to map I/O completion to WatchKey)
private int completionKey;
{
}
boolean watchSubtree,
long countAddress,
long overlappedAddress,
int completionKey)
{
this.watchSubtree = watchSubtree;
this.countAddress = countAddress;
this.overlappedAddress = overlappedAddress;
this.completionKey = completionKey;
return this;
}
long handle() {
return handle;
}
return events;
}
}
boolean watchSubtree() {
return watchSubtree;
}
return buffer;
}
long countAddress() {
return countAddress;
}
long overlappedAddress() {
return overlappedAddress;
}
return fileKey;
}
int completionKey() {
return completionKey;
}
// close directory and release buffer
void releaseResources() {
}
// Invalidate key by closing directory and releasing buffer
void invalidate() {
countAddress = 0;
overlappedAddress = 0;
}
public boolean isValid() {
return handle != INVALID_HANDLE_VALUE;
}
public void cancel() {
if (isValid()) {
// delegate to poller
}
}
}
// file key to unique identify (open) directory
private static class FileKey {
private final int volSerialNumber;
private final int fileIndexHigh;
private final int fileIndexLow;
this.volSerialNumber = volSerialNumber;
this.fileIndexHigh = fileIndexHigh;
this.fileIndexLow = fileIndexLow;
}
public int hashCode() {
}
if (obj == this)
return true;
return false;
return true;
}
}
// all change events
private static final int ALL_FILE_NOTIFY_EVENTS =
/**
* Background thread to service I/O completion port.
*/
/*
* typedef struct _OVERLAPPED {
* DWORD Internal;
* DWORD InternalHigh;
* DWORD Offset;
* DWORD OffsetHigh;
* HANDLE hEvent;
* } OVERLAPPED;
*/
/*
* typedef struct _FILE_NOTIFY_INFORMATION {
* DWORD NextEntryOffset;
* DWORD Action;
* DWORD FileNameLength;
* WCHAR FileName[1];
* } FileNameLength;
*/
// size of per-directory buffer for events (FIXME - make this configurable)
private final long port;
// maps completion key to WatchKey
// maps file key to WatchKey
// unique completion key for each directory
private int lastCompletionKey;
this.lastCompletionKey = 0;
}
try {
} catch (WindowsException x) {
throw new IOException(x.getMessage());
}
}
/**
* Register a directory for changes as follows:
*
* 1. Open directory
* 2. Read its attributes (and check it really is a directory)
* 3. Assign completion key and associated handle with completion port
* 4. Call ReadDirectoryChangesW to start (async) read of changes
* 5. Create or return existing key representing registration
*/
{
boolean watchSubtree = false;
// FILE_TREE modifier allowed
watchSubtree = true;
continue;
} else {
return new NullPointerException();
continue; // ignore
return new UnsupportedOperationException("Modifier not supported");
}
}
// open directory
long handle = -1L;
try {
} catch (WindowsException x) {
return x.asIOException(dir);
}
boolean registered = false;
try {
// read attributes and check file is a directory
try {
} catch (WindowsException x) {
return x.asIOException(dir);
}
if (!attrs.isDirectory()) {
}
// check if this directory is already registered
attrs.fileIndexLow());
// if already registered and we're not changing the subtree
// modifier then simply update the event and return the key.
return existing;
}
// unique completion key (skip 0)
int completionKey = ++lastCompletionKey;
if (completionKey == 0)
// associate handle with completion port
try {
} catch (WindowsException x) {
return new IOException(x.getMessage());
}
// allocate memory for events, including space for other structures
// needed to do overlapped I/O
// start async read of changes to directory
try {
} catch (WindowsException x) {
return new IOException(x.getMessage());
}
// not registered so create new watch key
// map file key to watch key
} else {
// directory already registered so need to:
// 1. remove mapping from old completion key to existing watch key
}
// map completion map to watch key
registered = true;
return watchKey;
} finally {
}
}
// cancel single key
key.invalidate();
}
}
// close watch service
void implCloseAll() {
// cancel all keys
}
// close I/O completion port
}
// Translate file change action into watch event
{
switch (action) {
case FILE_ACTION_MODIFIED :
return StandardWatchEventKinds.ENTRY_MODIFY;
case FILE_ACTION_ADDED :
case FILE_ACTION_RENAMED_NEW_NAME :
return StandardWatchEventKinds.ENTRY_CREATE;
case FILE_ACTION_REMOVED :
case FILE_ACTION_RENAMED_OLD_NAME :
return StandardWatchEventKinds.ENTRY_DELETE;
default :
return null; // action not recognized
}
}
// process events (list of FILE_NOTIFY_INFORMATION structures)
int nextOffset;
do {
// map action to event
// copy the name
throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2");
}
// create FileName and queue event
}
// next event
address += (long)nextOffset;
} while (nextOffset != 0);
}
/**
* Poller main loop
*/
public void run() {
for (;;) {
try {
} catch (WindowsException x) {
// this should not happen
x.printStackTrace();
return;
}
// wakeup
boolean shutdown = processRequests();
if (shutdown) {
return;
}
continue;
}
// map completionKey to get WatchKey
// We get here when a registration is changed. In that case
// the directory is closed which causes an event with the
// old completion key.
continue;
}
// ReadDirectoryChangesW failed
// buffer overflow
} else {
// other error so cancel key
}
continue;
}
// process the events
} else {
// insufficient buffer size
}
// start read for next batch of changes
try {
key.watchSubtree(),
key.countAddress(),
key.overlappedAddress());
} catch (WindowsException x) {
// no choice but to cancel key
}
}
}
}
}