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
31import java.nio.file.*;
32import static java.nio.file.StandardWatchEventKinds.*;
33import java.nio.file.attribute.*;
34import java.io.*;
35import java.util.*;
36import java.util.concurrent.TimeUnit;
37
38/**
39 * Unit test for WatchService that exercises all methods in various scenarios.
40 */
41
42public class Basic {
43
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
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
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     */
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     */
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     */
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     */
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")
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     */
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     */
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
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