1 /* 2 * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.nio.file.*; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import java.io.IOException; 32 import java.util.*; 33 34 /** 35 * Base implementation of background poller thread used in watch service 36 * implementations. A poller thread waits on events from the file system and 37 * also services "requests" from clients to register for new events or cancel 38 * existing registrations. 39 */ 40 41 abstract class AbstractPoller implements Runnable { 42 43 // list of requests pending to the poller thread 44 private final LinkedList<Request> requestList; 45 46 // set to true when shutdown 47 private boolean shutdown; 48 49 protected AbstractPoller() { 50 this.requestList = new LinkedList<Request>(); 51 this.shutdown = false; 52 } 53 54 /** 55 * Starts the poller thread 56 */ 57 public void start() { 58 final Runnable thisRunnable = this; 59 AccessController.doPrivileged(new PrivilegedAction<Object>() { 60 @Override 61 public Object run() { 62 Thread thr = new Thread(thisRunnable); 63 thr.setDaemon(true); 64 thr.start(); 65 return null; 66 } 67 }); 68 } 69 70 /** 71 * Wakeup poller thread so that it can service pending requests 72 */ 73 abstract void wakeup() throws IOException; 74 75 /** 76 * Executed by poller thread to register directory for changes 77 */ 78 abstract Object implRegister(Path path, 79 Set<? extends WatchEvent.Kind<?>> events, 80 WatchEvent.Modifier... modifiers); 81 82 /** 83 * Executed by poller thread to cancel key 84 */ 85 abstract void implCancelKey(WatchKey key); 86 87 /** 88 * Executed by poller thread to shutdown and cancel all keys 89 */ 90 abstract void implCloseAll(); 91 92 /** 93 * Requests, and waits on, poller thread to register given file. 94 */ 95 final WatchKey register(FileRef dir, 96 WatchEvent.Kind<?>[] events, 97 WatchEvent.Modifier... modifiers) 98 throws IOException 99 { 100 // validate arguments before request to poller 101 if (dir == null) 102 throw new NullPointerException(); 103 if (events.length == 0) 104 throw new IllegalArgumentException("No events to register"); 105 Set<WatchEvent.Kind<?>> eventSet = new HashSet<WatchEvent.Kind<?>>(events.length); 106 for (WatchEvent.Kind<?> event: events) { 107 // standard events 108 if (event == StandardWatchEventKind.ENTRY_CREATE || 109 event == StandardWatchEventKind.ENTRY_MODIFY || 110 event == StandardWatchEventKind.ENTRY_DELETE) 111 { 112 eventSet.add(event); 113 continue; 114 } 115 116 // OVERFLOW is ignored 117 if (event == StandardWatchEventKind.OVERFLOW) { 118 if (events.length == 1) 119 throw new IllegalArgumentException("No events to register"); 120 continue; 121 } 122 123 // null/unsupported 124 if (event == null) 125 throw new NullPointerException("An element in event set is 'null'"); 126 throw new UnsupportedOperationException(event.name()); 127 } 128 return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers); 129 } 130 131 /** 132 * Cancels, and waits on, poller thread to cancel given key. 133 */ 134 final void cancel(WatchKey key) { 135 try { 136 invoke(RequestType.CANCEL, key); 137 } catch (IOException x) { 138 // should not happen 139 throw new AssertionError(x.getMessage()); 140 } 141 } 142 143 /** 144 * Shutdown poller thread 145 */ 146 final void close() throws IOException { 147 invoke(RequestType.CLOSE); 148 } 149 150 /** 151 * Types of request that the poller thread must handle 152 */ 153 private static enum RequestType { 154 REGISTER, 155 CANCEL, 156 CLOSE; 157 } 158 159 /** 160 * Encapsulates a request (command) to the poller thread. 161 */ 162 private static class Request { 163 private final RequestType type; 164 private final Object[] params; 165 166 private boolean completed = false; 167 private Object result = null; 168 169 Request(RequestType type, Object... params) { 170 this.type = type; 171 this.params = params; 172 } 173 174 RequestType type() { 175 return type; 176 } 177 178 Object[] parameters() { 179 return params; 180 } 181 182 void release(Object result) { 183 synchronized (this) { 184 this.completed = true; 185 this.result = result; 186 notifyAll(); 187 } 188 } 189 190 /** 191 * Await completion of the request. The return value is the result of 192 * the request. 193 */ 194 Object awaitResult() { 195 synchronized (this) { 196 while (!completed) { 197 try { 198 wait(); 199 } catch (InterruptedException x) { 200 // ignore 201 } 202 } 203 return result; 204 } 205 } 206 } 207 208 /** 209 * Enqueues request to poller thread and waits for result 210 */ 211 private Object invoke(RequestType type, Object... params) throws IOException { 212 // submit request 213 Request req = new Request(type, params); 214 synchronized (requestList) { 215 if (shutdown) { 216 throw new ClosedWatchServiceException(); 217 } 218 requestList.add(req); 219 } 220 221 // wakeup thread 222 wakeup(); 223 224 // wait for result 225 Object result = req.awaitResult(); 226 227 if (result instanceof RuntimeException) 228 throw (RuntimeException)result; 229 if (result instanceof IOException ) 230 throw (IOException)result; 231 return result; 232 } 233 234 /** 235 * Invoked by poller thread to process all pending requests 236 * 237 * @return true if poller thread should shutdown 238 */ 239 @SuppressWarnings("unchecked") 240 boolean processRequests() { 241 synchronized (requestList) { 242 Request req; 243 while ((req = requestList.poll()) != null) { 244 // if in process of shutdown then reject request 245 if (shutdown) { 246 req.release(new ClosedWatchServiceException()); 247 } 248 249 switch (req.type()) { 250 /** 251 * Register directory 252 */ 253 case REGISTER: { 254 Object[] params = req.parameters(); 255 Path path = (Path)params[0]; 256 Set<? extends WatchEvent.Kind<?>> events = 257 (Set<? extends WatchEvent.Kind<?>>)params[1]; 258 WatchEvent.Modifier[] modifiers = 259 (WatchEvent.Modifier[])params[2]; 260 req.release(implRegister(path, events, modifiers)); 261 break; 262 } 263 /** 264 * Cancel existing key 265 */ 266 case CANCEL : { 267 Object[] params = req.parameters(); 268 WatchKey key = (WatchKey)params[0]; 269 implCancelKey(key); 270 req.release(null); 271 break; 272 } 273 /** 274 * Close watch service 275 */ 276 case CLOSE: { 277 implCloseAll(); 278 req.release(null); 279 shutdown = true; 280 break; 281 } 282 283 default: 284 req.release(new IOException("request not recognized")); 285 } 286 } 287 } 288 return shutdown; 289 } 290 }