Basic.java revision 1319:5208d0c90d73
1/*
2 * Copyright 2008-2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/* @test
25 * @bug 4313887 6838333
26 * @summary Unit test for java.nio.file.WatchService
27 * @library ..
28 * @run main/timeout=120 Basic
29 */
30
31import java.nio.file.*;
32import static java.nio.file.StandardWatchEventKind.*;
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 createFile(Path file) throws IOException {
45        file.newOutputStream().close();
46    }
47
48    static void takeExpectedKey(WatchService watcher, WatchKey expected) {
49        System.out.println("take events...");
50        WatchKey key;
51        try {
52            key = watcher.take();
53        } catch (InterruptedException x) {
54            // not expected
55            throw new RuntimeException(x);
56        }
57        if (key != expected)
58            throw new RuntimeException("removed unexpected key");
59    }
60
61    static void checkExpectedEvent(Iterable<WatchEvent<?>> events,
62                                   WatchEvent.Kind<?> expectedKind,
63                                   Object expectedContext)
64    {
65        WatchEvent<?> event = events.iterator().next();
66        System.out.format("got event: type=%s, count=%d, context=%s\n",
67            event.kind(), event.count(), event.context());
68        if (event.kind() != expectedKind)
69            throw new RuntimeException("unexpected event");
70        if (!expectedContext.equals(event.context()))
71            throw new RuntimeException("unexpected context");
72    }
73
74    /**
75     * Simple test of each of the standard events
76     */
77    static void testEvents(Path dir) throws IOException {
78        System.out.println("-- Standard Events --");
79
80        FileSystem fs = FileSystems.getDefault();
81        Path name = fs.getPath("foo");
82
83        WatchService watcher = fs.newWatchService();
84        try {
85            // --- ENTRY_CREATE ---
86
87            // register for event
88            System.out.format("register %s for ENTRY_CREATE\n", dir);
89            WatchKey myKey = dir.register(watcher,
90                new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
91
92            // create file
93            Path file = dir.resolve("foo");
94            System.out.format("create %s\n", file);
95            createFile(file);
96
97            // remove key and check that we got the ENTRY_CREATE event
98            takeExpectedKey(watcher, myKey);
99            checkExpectedEvent(myKey.pollEvents(),
100                StandardWatchEventKind.ENTRY_CREATE, name);
101
102            System.out.println("reset key");
103            if (!myKey.reset())
104                throw new RuntimeException("key has been cancalled");
105
106            System.out.println("OKAY");
107
108            // --- ENTRY_DELETE ---
109
110            System.out.format("register %s for ENTRY_DELETE\n", dir);
111            WatchKey deleteKey = dir.register(watcher,
112                new WatchEvent.Kind<?>[]{ ENTRY_DELETE });
113            if (deleteKey != myKey)
114                throw new RuntimeException("register did not return existing key");
115
116            System.out.format("delete %s\n", file);
117            file.delete();
118            takeExpectedKey(watcher, myKey);
119            checkExpectedEvent(myKey.pollEvents(),
120                StandardWatchEventKind.ENTRY_DELETE, name);
121
122            System.out.println("reset key");
123            if (!myKey.reset())
124                throw new RuntimeException("key has been cancalled");
125
126            System.out.println("OKAY");
127
128            // create the file for the next test
129            createFile(file);
130
131            // --- ENTRY_MODIFY ---
132
133            System.out.format("register %s for ENTRY_MODIFY\n", dir);
134            WatchKey newKey = dir.register(watcher,
135                new WatchEvent.Kind<?>[]{ ENTRY_MODIFY });
136            if (newKey != myKey)
137                throw new RuntimeException("register did not return existing key");
138
139            System.out.format("update: %s\n", file);
140            OutputStream out = file.newOutputStream(StandardOpenOption.APPEND);
141            try {
142                out.write("I am a small file".getBytes("UTF-8"));
143            } finally {
144                out.close();
145            }
146
147            // remove key and check that we got the ENTRY_MODIFY event
148            takeExpectedKey(watcher, myKey);
149            checkExpectedEvent(myKey.pollEvents(),
150                StandardWatchEventKind.ENTRY_MODIFY, name);
151            System.out.println("OKAY");
152
153            // done
154            file.delete();
155
156        } finally {
157            watcher.close();
158        }
159    }
160
161    /**
162     * Check that a cancelled key will never be queued
163     */
164    static void testCancel(Path dir) throws IOException {
165        System.out.println("-- Cancel --");
166
167        WatchService watcher = FileSystems.getDefault().newWatchService();
168        try {
169
170            System.out.format("register %s for events\n", dir);
171            WatchKey myKey = dir.register(watcher,
172                new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
173
174            System.out.println("cancel key");
175            myKey.cancel();
176
177            // create a file in the directory
178            Path file = dir.resolve("mars");
179            System.out.format("create: %s\n", file);
180            createFile(file);
181
182            // poll for keys - there will be none
183            System.out.println("poll...");
184            try {
185                WatchKey key = watcher.poll(3000, TimeUnit.MILLISECONDS);
186                if (key != null)
187                    throw new RuntimeException("key should not be queued");
188            } catch (InterruptedException x) {
189                throw new RuntimeException(x);
190            }
191
192            // done
193            file.delete();
194
195            System.out.println("OKAY");
196
197        } finally {
198            watcher.close();
199        }
200    }
201
202    /**
203     * Check that deleting a registered directory causes the key to be
204     * cancelled and queued.
205     */
206    static void testAutomaticCancel(Path dir) throws IOException {
207        System.out.println("-- Automatic Cancel --");
208
209        Path subdir = dir.resolve("bar").createDirectory();
210
211        WatchService watcher = FileSystems.getDefault().newWatchService();
212        try {
213
214            System.out.format("register %s for events\n", subdir);
215            WatchKey myKey = subdir.register(watcher,
216                new WatchEvent.Kind<?>[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY });
217
218            System.out.format("delete: %s\n", subdir);
219            subdir.delete();
220            takeExpectedKey(watcher, myKey);
221
222            System.out.println("reset key");
223            if (myKey.reset())
224                throw new RuntimeException("Key was not cancelled");
225            if (myKey.isValid())
226                throw new RuntimeException("Key is still valid");
227
228            System.out.println("OKAY");
229
230        } finally {
231            watcher.close();
232        }
233    }
234
235    /**
236     * Asynchronous close of watcher causes blocked threads to wakeup
237     */
238    static void testWakeup(Path dir) throws IOException {
239        System.out.println("-- Wakeup Tests --");
240        final WatchService watcher = FileSystems.getDefault().newWatchService();
241        Runnable r = new Runnable() {
242            public void run() {
243                try {
244                    Thread.sleep(5000);
245                    System.out.println("close WatchService...");
246                    watcher.close();
247                } catch (InterruptedException x) {
248                    x.printStackTrace();
249                } catch (IOException x) {
250                    x.printStackTrace();
251                }
252            }
253        };
254
255        // start thread to close watch service after delay
256        new Thread(r).start();
257
258        try {
259            System.out.println("take...");
260            watcher.take();
261            throw new RuntimeException("ClosedWatchServiceException not thrown");
262        } catch (InterruptedException x) {
263            throw new RuntimeException(x);
264        } catch (ClosedWatchServiceException  x) {
265            System.out.println("ClosedWatchServiceException thrown");
266        }
267
268        System.out.println("OKAY");
269    }
270
271    /**
272     * Simple test to check exceptions and other cases
273     */
274    @SuppressWarnings("unchecked")
275    static void testExceptions(Path dir) throws IOException {
276        System.out.println("-- Exceptions and other simple tests --");
277
278        WatchService watcher = FileSystems.getDefault().newWatchService();
279        try {
280
281            // Poll tests
282
283            WatchKey key;
284            System.out.println("poll...");
285            key = watcher.poll();
286            if (key != null)
287                throw new RuntimeException("no keys registered");
288
289            System.out.println("poll with timeout...");
290            try {
291                long start = System.currentTimeMillis();
292                key = watcher.poll(3000, TimeUnit.MILLISECONDS);
293                if (key != null)
294                    throw new RuntimeException("no keys registered");
295                long waited = System.currentTimeMillis() - start;
296                if (waited < 2900)
297                    throw new RuntimeException("poll was too short");
298            } catch (InterruptedException x) {
299                throw new RuntimeException(x);
300            }
301
302            // IllegalArgumentException
303            System.out.println("IllegalArgumentException tests...");
304            try {
305                dir.register(watcher, new WatchEvent.Kind<?>[]{ } );
306                throw new RuntimeException("IllegalArgumentException not thrown");
307            } catch (IllegalArgumentException x) {
308            }
309            try {
310                // OVERFLOW is ignored so this is equivalent to the empty set
311                dir.register(watcher, new WatchEvent.Kind<?>[]{ OVERFLOW });
312                throw new RuntimeException("IllegalArgumentException not thrown");
313            } catch (IllegalArgumentException x) {
314            }
315
316            // UnsupportedOperationException
317            try {
318                dir.register(watcher, new WatchEvent.Kind<?>[]{
319                             new WatchEvent.Kind<Object>() {
320                                @Override public String name() { return "custom"; }
321                                @Override public Class<Object> type() { return Object.class; }
322                             }});
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, new WatchEvent.Kind<?>[]{ 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.currentTimeMillis();
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 = System.currentTimeMillis() - 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            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            createFile(file2);
430
431            // check that key1 got ENTRY_CREATE
432            takeExpectedKey(watcher1, key1);
433            checkExpectedEvent(key1.pollEvents(),
434                StandardWatchEventKind.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            file1.delete();
443
444            // check that key2 got ENTRY_DELETE
445            takeExpectedKey(watcher2, key2);
446            checkExpectedEvent(key2.pollEvents(),
447                StandardWatchEventKind.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            createFile(file1);
466            takeExpectedKey(watcher2, key2);
467            checkExpectedEvent(key2.pollEvents(),
468                StandardWatchEventKind.ENTRY_CREATE, name1);
469
470            System.out.println("OKAY");
471
472        } finally {
473            watcher2.close();
474            watcher1.close();
475        }
476    }
477
478    public static void main(String[] args) throws IOException {
479        Path dir = TestUtil.createTemporaryDirectory();
480        try {
481
482            testEvents(dir);
483            testCancel(dir);
484            testAutomaticCancel(dir);
485            testWakeup(dir);
486            testExceptions(dir);
487            testTwoWatchers(dir);
488
489        } finally {
490            TestUtil.removeAll(dir);
491        }
492    }
493}
494