1 /*
2  * Copyright (c) 2008, 2014, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /* @test
25  * @bug 4313887 6838333 7017446 8011537 8042470
26  * @summary Unit test for java.nio.file.WatchService
27  * @library ..
28  * @run main Basic
29  */
30 
31 import java.nio.file.*;
32 import static java.nio.file.StandardWatchEventKinds.*;
33 import java.nio.file.attribute.*;
34 import java.io.*;
35 import java.util.*;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * Unit test for WatchService that exercises all methods in various scenarios.
40  */
41 
42 public class Basic {
43 
checkKey(WatchKey key, Path dir)44     static void checkKey(WatchKey key, Path dir) {
45         if (!key.isValid())
46             throw new RuntimeException("Key is not valid");
47         if (key.watchable() != dir)
48             throw new RuntimeException("Unexpected watchable");
49     }
50 
takeExpectedKey(WatchService watcher, WatchKey expected)51     static void takeExpectedKey(WatchService watcher, WatchKey expected) {
52         System.out.println("take events...");
53         WatchKey key;
54         try {
55             key = watcher.take();
56         } catch (InterruptedException x) {
57             // not expected
58             throw new RuntimeException(x);
59         }
60         if (key != expected)
61             throw new RuntimeException("removed unexpected key");
62     }
63 
checkExpectedEvent(Iterable<WatchEvent<?>> events, WatchEvent.Kind<?> expectedKind, Object expectedContext)64     static void checkExpectedEvent(Iterable<WatchEvent<?>> events,
65                                    WatchEvent.Kind<?> expectedKind,
66                                    Object expectedContext)
67     {
68         WatchEvent<?> event = events.iterator().next();
69         System.out.format("got event: type=%s, count=%d, context=%s\n",
70             event.kind(), event.count(), event.context());
71         if (event.kind() != expectedKind)
72             throw new RuntimeException("unexpected event");
73         if (!expectedContext.equals(event.context()))
74             throw new RuntimeException("unexpected context");
75     }
76 
77     /**
78      * Simple test of each of the standard events
79      */
testEvents(Path dir)80     static void testEvents(Path dir) throws IOException {
81         System.out.println("-- Standard Events --");
82 
83         FileSystem fs = FileSystems.getDefault();
84         Path name = fs.getPath("foo");
85 
86         try (WatchService watcher = fs.newWatchService()) {
87             // --- ENTRY_CREATE ---
88 
89             // register for event
90             System.out.format("register %s for ENTRY_CREATE\n", dir);
91             WatchKey myKey = dir.register(watcher,
92                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
93             checkKey(myKey, dir);
94 
95             // create file
96             Path file = dir.resolve("foo");
97             System.out.format("create %s\n", file);
98             Files.createFile(file);
99 
100             // remove key and check that we got the ENTRY_CREATE event
101             takeExpectedKey(watcher, myKey);
102             checkExpectedEvent(myKey.pollEvents(),
103                 StandardWatchEventKinds.ENTRY_CREATE, name);
104 
105             System.out.println("reset key");
106             if (!myKey.reset())
107                 throw new RuntimeException("key has been cancalled");
108 
109             System.out.println("OKAY");
110 
111             // --- ENTRY_DELETE ---
112 
113             System.out.format("register %s for ENTRY_DELETE\n", dir);
114             WatchKey deleteKey = dir.register(watcher,
115                 new WatchEvent.Kind<?>[]{ ENTRY_DELETE });
116             if (deleteKey != myKey)
117                 throw new RuntimeException("register did not return existing key");
118             checkKey(deleteKey, dir);
119 
120             System.out.format("delete %s\n", file);
121             Files.delete(file);
122             takeExpectedKey(watcher, myKey);
123             checkExpectedEvent(myKey.pollEvents(),
124                 StandardWatchEventKinds.ENTRY_DELETE, name);
125 
126             System.out.println("reset key");
127             if (!myKey.reset())
128                 throw new RuntimeException("key has been cancalled");
129 
130             System.out.println("OKAY");
131 
132             // create the file for the next test
133             Files.createFile(file);
134 
135             // --- ENTRY_MODIFY ---
136 
137             System.out.format("register %s for ENTRY_MODIFY\n", dir);
138             WatchKey newKey = dir.register(watcher,
139                 new WatchEvent.Kind<?>[]{ ENTRY_MODIFY });
140             if (newKey != myKey)
141                 throw new RuntimeException("register did not return existing key");
142             checkKey(newKey, dir);
143 
144             System.out.format("update: %s\n", file);
145             try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.APPEND)) {
146                 out.write("I am a small file".getBytes("UTF-8"));
147             }
148 
149             // remove key and check that we got the ENTRY_MODIFY event
150             takeExpectedKey(watcher, myKey);
151             checkExpectedEvent(myKey.pollEvents(),
152                 StandardWatchEventKinds.ENTRY_MODIFY, name);
153             System.out.println("OKAY");
154 
155             // done
156             Files.delete(file);
157         }
158     }
159 
160     /**
161      * Check that a cancelled key will never be queued
162      */
testCancel(Path dir)163     static void testCancel(Path dir) throws IOException {
164         System.out.println("-- Cancel --");
165 
166         try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
167 
168             System.out.format("register %s for events\n", dir);
169             WatchKey myKey = dir.register(watcher,
170                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
171             checkKey(myKey, dir);
172 
173             System.out.println("cancel key");
174             myKey.cancel();
175 
176             // create a file in the directory
177             Path file = dir.resolve("mars");
178             System.out.format("create: %s\n", file);
179             Files.createFile(file);
180 
181             // poll for keys - there will be none
182             System.out.println("poll...");
183             try {
184                 WatchKey key = watcher.poll(3000, TimeUnit.MILLISECONDS);
185                 if (key != null)
186                     throw new RuntimeException("key should not be queued");
187             } catch (InterruptedException x) {
188                 throw new RuntimeException(x);
189             }
190 
191             // done
192             Files.delete(file);
193 
194             System.out.println("OKAY");
195         }
196     }
197 
198     /**
199      * Check that deleting a registered directory causes the key to be
200      * cancelled and queued.
201      */
testAutomaticCancel(Path dir)202     static void testAutomaticCancel(Path dir) throws IOException {
203         System.out.println("-- Automatic Cancel --");
204 
205         Path subdir = Files.createDirectory(dir.resolve("bar"));
206 
207         try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
208 
209             System.out.format("register %s for events\n", subdir);
210             WatchKey myKey = subdir.register(watcher,
211                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY });
212 
213             System.out.format("delete: %s\n", subdir);
214             Files.delete(subdir);
215             takeExpectedKey(watcher, myKey);
216 
217             System.out.println("reset key");
218             if (myKey.reset())
219                 throw new RuntimeException("Key was not cancelled");
220             if (myKey.isValid())
221                 throw new RuntimeException("Key is still valid");
222 
223             System.out.println("OKAY");
224 
225         }
226     }
227 
228     /**
229      * Asynchronous close of watcher causes blocked threads to wakeup
230      */
testWakeup(Path dir)231     static void testWakeup(Path dir) throws IOException {
232         System.out.println("-- Wakeup Tests --");
233         final WatchService watcher = FileSystems.getDefault().newWatchService();
234         Runnable r = new Runnable() {
235             public void run() {
236                 try {
237                     Thread.sleep(5000);
238                     System.out.println("close WatchService...");
239                     watcher.close();
240                 } catch (InterruptedException x) {
241                     x.printStackTrace();
242                 } catch (IOException x) {
243                     x.printStackTrace();
244                 }
245             }
246         };
247 
248         // start thread to close watch service after delay
249         new Thread(r).start();
250 
251         try {
252             System.out.println("take...");
253             watcher.take();
254             throw new RuntimeException("ClosedWatchServiceException not thrown");
255         } catch (InterruptedException x) {
256             throw new RuntimeException(x);
257         } catch (ClosedWatchServiceException  x) {
258             System.out.println("ClosedWatchServiceException thrown");
259         }
260 
261         System.out.println("OKAY");
262     }
263 
264     /**
265      * Simple test to check exceptions and other cases
266      */
267     @SuppressWarnings("unchecked")
testExceptions(Path dir)268     static void testExceptions(Path dir) throws IOException {
269         System.out.println("-- Exceptions and other simple tests --");
270 
271         WatchService watcher = FileSystems.getDefault().newWatchService();
272         try {
273 
274             // Poll tests
275 
276             WatchKey key;
277             System.out.println("poll...");
278             key = watcher.poll();
279             if (key != null)
280                 throw new RuntimeException("no keys registered");
281 
282             System.out.println("poll with timeout...");
283             try {
284                 long start = System.nanoTime();
285                 key = watcher.poll(3000, TimeUnit.MILLISECONDS);
286                 if (key != null)
287                     throw new RuntimeException("no keys registered");
288                 long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
289                 if (waited < 2900)
290                     throw new RuntimeException("poll was too short");
291             } catch (InterruptedException x) {
292                 throw new RuntimeException(x);
293             }
294 
295             // IllegalArgumentException
296             System.out.println("IllegalArgumentException tests...");
297             try {
298                 dir.register(watcher /*empty event list*/);
299                 throw new RuntimeException("IllegalArgumentException not thrown");
300             } catch (IllegalArgumentException x) {
301             }
302             try {
303                 // OVERFLOW is ignored so this is equivalent to the empty set
304                 dir.register(watcher, OVERFLOW);
305                 throw new RuntimeException("IllegalArgumentException not thrown");
306             } catch (IllegalArgumentException x) {
307             }
308             try {
309                 // OVERFLOW is ignored even if specified multiple times
310                 dir.register(watcher, OVERFLOW, OVERFLOW);
311                 throw new RuntimeException("IllegalArgumentException not thrown");
312             } catch (IllegalArgumentException x) {
313             }
314 
315             // UnsupportedOperationException
316             try {
317                 dir.register(watcher,
318                              new WatchEvent.Kind<Object>() {
319                                 @Override public String name() { return "custom"; }
320                                 @Override public Class<Object> type() { return Object.class; }
321                              });
322                 throw new RuntimeException("UnsupportedOperationException not thrown");
323             } catch (UnsupportedOperationException x) {
324             }
325             try {
326                 dir.register(watcher,
327                              new WatchEvent.Kind<?>[]{ ENTRY_CREATE },
328                              new WatchEvent.Modifier() {
329                                  @Override public String name() { return "custom"; }
330                              });
331                 throw new RuntimeException("UnsupportedOperationException not thrown");
332             } catch (UnsupportedOperationException x) {
333             }
334 
335             // NullPointerException
336             System.out.println("NullPointerException tests...");
337             try {
338                 dir.register(null, ENTRY_CREATE);
339                 throw new RuntimeException("NullPointerException not thrown");
340             } catch (NullPointerException x) {
341             }
342             try {
343                 dir.register(watcher, new WatchEvent.Kind<?>[]{ null });
344                 throw new RuntimeException("NullPointerException not thrown");
345             } catch (NullPointerException x) {
346             }
347             try {
348                 dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE },
349                     (WatchEvent.Modifier)null);
350                 throw new RuntimeException("NullPointerException not thrown");
351             } catch (NullPointerException x) {
352             }
353         } finally {
354             watcher.close();
355         }
356 
357         // -- ClosedWatchServiceException --
358 
359         System.out.println("ClosedWatchServiceException tests...");
360 
361         try {
362             watcher.poll();
363             throw new RuntimeException("ClosedWatchServiceException not thrown");
364         } catch (ClosedWatchServiceException  x) {
365         }
366 
367         // assume that poll throws exception immediately
368         long start = System.nanoTime();
369         try {
370             watcher.poll(10000, TimeUnit.MILLISECONDS);
371             throw new RuntimeException("ClosedWatchServiceException not thrown");
372         } catch (InterruptedException x) {
373             throw new RuntimeException(x);
374         } catch (ClosedWatchServiceException  x) {
375             long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
376             if (waited > 5000)
377                 throw new RuntimeException("poll was too long");
378         }
379 
380         try {
381             watcher.take();
382             throw new RuntimeException("ClosedWatchServiceException not thrown");
383         } catch (InterruptedException x) {
384             throw new RuntimeException(x);
385         } catch (ClosedWatchServiceException  x) {
386         }
387 
388         try {
389             dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
390             throw new RuntimeException("ClosedWatchServiceException not thrown");
391         } catch (ClosedWatchServiceException  x) {
392         }
393 
394         System.out.println("OKAY");
395     }
396 
397     /**
398      * Test that directory can be registered with more than one watch service
399      * and that events don't interfere with each other
400      */
testTwoWatchers(Path dir)401     static void testTwoWatchers(Path dir) throws IOException {
402         System.out.println("-- Two watchers test --");
403 
404         FileSystem fs = FileSystems.getDefault();
405         WatchService watcher1 = fs.newWatchService();
406         WatchService watcher2 = fs.newWatchService();
407         try {
408             Path name1 = fs.getPath("gus1");
409             Path name2 = fs.getPath("gus2");
410 
411             // create gus1
412             Path file1 = dir.resolve(name1);
413             System.out.format("create %s\n", file1);
414             Files.createFile(file1);
415 
416             // register with both watch services (different events)
417             System.out.println("register for different events");
418             WatchKey key1 = dir.register(watcher1,
419                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
420             WatchKey key2 = dir.register(watcher2,
421                 new WatchEvent.Kind<?>[]{ ENTRY_DELETE });
422 
423             if (key1 == key2)
424                 throw new RuntimeException("keys should be different");
425 
426             // create gus2
427             Path file2 = dir.resolve(name2);
428             System.out.format("create %s\n", file2);
429             Files.createFile(file2);
430 
431             // check that key1 got ENTRY_CREATE
432             takeExpectedKey(watcher1, key1);
433             checkExpectedEvent(key1.pollEvents(),
434                 StandardWatchEventKinds.ENTRY_CREATE, name2);
435 
436             // check that key2 got zero events
437             WatchKey key = watcher2.poll();
438             if (key != null)
439                 throw new RuntimeException("key not expected");
440 
441             // delete gus1
442             Files.delete(file1);
443 
444             // check that key2 got ENTRY_DELETE
445             takeExpectedKey(watcher2, key2);
446             checkExpectedEvent(key2.pollEvents(),
447                 StandardWatchEventKinds.ENTRY_DELETE, name1);
448 
449             // check that key1 got zero events
450             key = watcher1.poll();
451             if (key != null)
452                 throw new RuntimeException("key not expected");
453 
454             // reset for next test
455             key1.reset();
456             key2.reset();
457 
458             // change registration with watcher2 so that they are both
459             // registered for the same event
460             System.out.println("register for same event");
461             key2 = dir.register(watcher2, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
462 
463             // create file and key2 should be queued
464             System.out.format("create %s\n", file1);
465             Files.createFile(file1);
466             takeExpectedKey(watcher2, key2);
467             checkExpectedEvent(key2.pollEvents(),
468                 StandardWatchEventKinds.ENTRY_CREATE, name1);
469 
470             System.out.println("OKAY");
471 
472         } finally {
473             watcher2.close();
474             watcher1.close();
475         }
476     }
477 
478     /**
479      * Test that thread interruped status is preserved upon a call
480      * to register()
481      */
testThreadInterrupt(Path dir)482     static void testThreadInterrupt(Path dir) throws IOException {
483         System.out.println("-- Thread interrupted status test --");
484 
485         FileSystem fs = FileSystems.getDefault();
486         Thread curr = Thread.currentThread();
487         try (WatchService watcher = fs.newWatchService()) {
488             System.out.println("interrupting current thread");
489             curr.interrupt();
490             dir.register(watcher, ENTRY_CREATE);
491             if (!curr.isInterrupted())
492                 throw new RuntimeException("thread should remain interrupted");
493             System.out.println("current thread is still interrupted");
494             System.out.println("OKAY");
495         } finally {
496             curr.interrupted();
497         }
498     }
499 
main(String[] args)500     public static void main(String[] args) throws IOException {
501         Path dir = TestUtil.createTemporaryDirectory();
502         try {
503 
504             testEvents(dir);
505             testCancel(dir);
506             testAutomaticCancel(dir);
507             testWakeup(dir);
508             testExceptions(dir);
509             testTwoWatchers(dir);
510             testThreadInterrupt(dir);
511 
512         } finally {
513             TestUtil.removeAll(dir);
514         }
515     }
516 }
517