|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.nio.fs; |
|
|
|
import java.nio.file.*; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.io.IOException; |
|
import java.util.*; |
|
|
|
/** |
|
* Base implementation of background poller thread used in watch service |
|
* implementations. A poller thread waits on events from the file system and |
|
* also services "requests" from clients to register for new events or cancel |
|
* existing registrations. |
|
*/ |
|
|
|
abstract class AbstractPoller implements Runnable { |
|
|
|
|
|
private final LinkedList<Request> requestList; |
|
|
|
|
|
private boolean shutdown; |
|
|
|
protected AbstractPoller() { |
|
this.requestList = new LinkedList<>(); |
|
this.shutdown = false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
public void start() { |
|
final Runnable thisRunnable = this; |
|
AccessController.doPrivileged(new PrivilegedAction<>() { |
|
@Override |
|
public Object run() { |
|
Thread thr = new Thread(null, |
|
thisRunnable, |
|
"FileSystemWatchService", |
|
0, |
|
false); |
|
thr.setDaemon(true); |
|
thr.start(); |
|
return null; |
|
} |
|
}); |
|
} |
|
|
|
|
|
|
|
*/ |
|
abstract void wakeup() throws IOException; |
|
|
|
|
|
|
|
*/ |
|
abstract Object implRegister(Path path, |
|
Set<? extends WatchEvent.Kind<?>> events, |
|
WatchEvent.Modifier... modifiers); |
|
|
|
|
|
|
|
*/ |
|
abstract void implCancelKey(WatchKey key); |
|
|
|
|
|
|
|
*/ |
|
abstract void implCloseAll(); |
|
|
|
|
|
|
|
*/ |
|
final WatchKey register(Path dir, |
|
WatchEvent.Kind<?>[] events, |
|
WatchEvent.Modifier... modifiers) |
|
throws IOException |
|
{ |
|
|
|
if (dir == null) |
|
throw new NullPointerException(); |
|
Set<WatchEvent.Kind<?>> eventSet = new HashSet<>(events.length); |
|
for (WatchEvent.Kind<?> event: events) { |
|
|
|
if (event == StandardWatchEventKinds.ENTRY_CREATE || |
|
event == StandardWatchEventKinds.ENTRY_MODIFY || |
|
event == StandardWatchEventKinds.ENTRY_DELETE) |
|
{ |
|
eventSet.add(event); |
|
continue; |
|
} |
|
|
|
|
|
if (event == StandardWatchEventKinds.OVERFLOW) |
|
continue; |
|
|
|
|
|
if (event == null) |
|
throw new NullPointerException("An element in event set is 'null'"); |
|
throw new UnsupportedOperationException(event.name()); |
|
} |
|
if (eventSet.isEmpty()) |
|
throw new IllegalArgumentException("No events to register"); |
|
return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers); |
|
} |
|
|
|
|
|
|
|
*/ |
|
final void cancel(WatchKey key) { |
|
try { |
|
invoke(RequestType.CANCEL, key); |
|
} catch (IOException x) { |
|
|
|
throw new AssertionError(x.getMessage()); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
final void close() throws IOException { |
|
invoke(RequestType.CLOSE); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static enum RequestType { |
|
REGISTER, |
|
CANCEL, |
|
CLOSE; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static class Request { |
|
private final RequestType type; |
|
private final Object[] params; |
|
|
|
private boolean completed = false; |
|
private Object result = null; |
|
|
|
Request(RequestType type, Object... params) { |
|
this.type = type; |
|
this.params = params; |
|
} |
|
|
|
RequestType type() { |
|
return type; |
|
} |
|
|
|
Object[] parameters() { |
|
return params; |
|
} |
|
|
|
void release(Object result) { |
|
synchronized (this) { |
|
this.completed = true; |
|
this.result = result; |
|
notifyAll(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
Object awaitResult() { |
|
boolean interrupted = false; |
|
synchronized (this) { |
|
while (!completed) { |
|
try { |
|
wait(); |
|
} catch (InterruptedException x) { |
|
interrupted = true; |
|
} |
|
} |
|
if (interrupted) |
|
Thread.currentThread().interrupt(); |
|
return result; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private Object invoke(RequestType type, Object... params) throws IOException { |
|
|
|
Request req = new Request(type, params); |
|
synchronized (requestList) { |
|
if (shutdown) { |
|
throw new ClosedWatchServiceException(); |
|
} |
|
requestList.add(req); |
|
|
|
|
|
wakeup(); |
|
} |
|
|
|
|
|
Object result = req.awaitResult(); |
|
|
|
if (result instanceof RuntimeException) |
|
throw (RuntimeException)result; |
|
if (result instanceof IOException ) |
|
throw (IOException)result; |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
boolean processRequests() { |
|
synchronized (requestList) { |
|
Request req; |
|
while ((req = requestList.poll()) != null) { |
|
|
|
if (shutdown) { |
|
req.release(new ClosedWatchServiceException()); |
|
continue; |
|
} |
|
|
|
switch (req.type()) { |
|
|
|
|
|
*/ |
|
case REGISTER: { |
|
Object[] params = req.parameters(); |
|
Path path = (Path)params[0]; |
|
Set<? extends WatchEvent.Kind<?>> events = |
|
(Set<? extends WatchEvent.Kind<?>>)params[1]; |
|
WatchEvent.Modifier[] modifiers = |
|
(WatchEvent.Modifier[])params[2]; |
|
req.release(implRegister(path, events, modifiers)); |
|
break; |
|
} |
|
|
|
|
|
*/ |
|
case CANCEL : { |
|
Object[] params = req.parameters(); |
|
WatchKey key = (WatchKey)params[0]; |
|
implCancelKey(key); |
|
req.release(null); |
|
break; |
|
} |
|
|
|
|
|
*/ |
|
case CLOSE: { |
|
implCloseAll(); |
|
req.release(null); |
|
shutdown = true; |
|
break; |
|
} |
|
|
|
default: |
|
req.release(new IOException("request not recognized")); |
|
} |
|
} |
|
} |
|
return shutdown; |
|
} |
|
} |