/* * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* @test * @bug 4313887 6838333 7017446 * @summary Unit test for java.nio.file.WatchService * @library .. * @run main Basic */ import java.nio.file.*; import static java.nio.file.StandardWatchEventKinds.*; import java.nio.file.attribute.*; import java.io.*; import java.util.*; import java.util.concurrent.TimeUnit; /** * Unit test for WatchService that exercises all methods in various scenarios. */ public class Basic { static void checkKey(WatchKey key, Path dir) { if (!key.isValid()) throw new RuntimeException("Key is not valid"); if (key.watchable() != dir) throw new RuntimeException("Unexpected watchable"); } static void takeExpectedKey(WatchService watcher, WatchKey expected) { System.out.println("take events..."); WatchKey key; try { key = watcher.take(); } catch (InterruptedException x) { // not expected throw new RuntimeException(x); } if (key != expected) throw new RuntimeException("removed unexpected key"); } static void checkExpectedEvent(Iterable<WatchEvent<?>> events, WatchEvent.Kind<?> expectedKind, Object expectedContext) { WatchEvent<?> event = events.iterator().next(); System.out.format("got event: type=%s, count=%d, context=%s\n", event.kind(), event.count(), event.context()); if (event.kind() != expectedKind) throw new RuntimeException("unexpected event"); if (!expectedContext.equals(event.context())) throw new RuntimeException("unexpected context"); } /** * Simple test of each of the standard events */ static void testEvents(Path dir) throws IOException { System.out.println("-- Standard Events --"); FileSystem fs = FileSystems.getDefault(); Path name = fs.getPath("foo"); try (WatchService watcher = fs.newWatchService()) { // --- ENTRY_CREATE --- // register for event System.out.format("register %s for ENTRY_CREATE\n", dir); WatchKey myKey = dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); checkKey(myKey, dir); // create file Path file = dir.resolve("foo"); System.out.format("create %s\n", file); Files.createFile(file); // remove key and check that we got the ENTRY_CREATE event takeExpectedKey(watcher, myKey); checkExpectedEvent(myKey.pollEvents(), StandardWatchEventKinds.ENTRY_CREATE, name); System.out.println("reset key"); if (!myKey.reset()) throw new RuntimeException("key has been cancalled"); System.out.println("OKAY"); // --- ENTRY_DELETE --- System.out.format("register %s for ENTRY_DELETE\n", dir); WatchKey deleteKey = dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_DELETE }); if (deleteKey != myKey) throw new RuntimeException("register did not return existing key"); checkKey(deleteKey, dir); System.out.format("delete %s\n", file); Files.delete(file); takeExpectedKey(watcher, myKey); checkExpectedEvent(myKey.pollEvents(), StandardWatchEventKinds.ENTRY_DELETE, name); System.out.println("reset key"); if (!myKey.reset()) throw new RuntimeException("key has been cancalled"); System.out.println("OKAY"); // create the file for the next test Files.createFile(file); // --- ENTRY_MODIFY --- System.out.format("register %s for ENTRY_MODIFY\n", dir); WatchKey newKey = dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_MODIFY }); if (newKey != myKey) throw new RuntimeException("register did not return existing key"); checkKey(newKey, dir); System.out.format("update: %s\n", file); try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.APPEND)) { out.write("I am a small file".getBytes("UTF-8")); } // remove key and check that we got the ENTRY_MODIFY event takeExpectedKey(watcher, myKey); checkExpectedEvent(myKey.pollEvents(), StandardWatchEventKinds.ENTRY_MODIFY, name); System.out.println("OKAY"); // done Files.delete(file); } } /** * Check that a cancelled key will never be queued */ static void testCancel(Path dir) throws IOException { System.out.println("-- Cancel --"); try (WatchService watcher = FileSystems.getDefault().newWatchService()) { System.out.format("register %s for events\n", dir); WatchKey myKey = dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); checkKey(myKey, dir); System.out.println("cancel key"); myKey.cancel(); // create a file in the directory Path file = dir.resolve("mars"); System.out.format("create: %s\n", file); Files.createFile(file); // poll for keys - there will be none System.out.println("poll..."); try { WatchKey key = watcher.poll(3000, TimeUnit.MILLISECONDS); if (key != null) throw new RuntimeException("key should not be queued"); } catch (InterruptedException x) { throw new RuntimeException(x); } // done Files.delete(file); System.out.println("OKAY"); } } /** * Check that deleting a registered directory causes the key to be * cancelled and queued. */ static void testAutomaticCancel(Path dir) throws IOException { System.out.println("-- Automatic Cancel --"); Path subdir = Files.createDirectory(dir.resolve("bar")); try (WatchService watcher = FileSystems.getDefault().newWatchService()) { System.out.format("register %s for events\n", subdir); WatchKey myKey = subdir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY }); System.out.format("delete: %s\n", subdir); Files.delete(subdir); takeExpectedKey(watcher, myKey); System.out.println("reset key"); if (myKey.reset()) throw new RuntimeException("Key was not cancelled"); if (myKey.isValid()) throw new RuntimeException("Key is still valid"); System.out.println("OKAY"); } } /** * Asynchronous close of watcher causes blocked threads to wakeup */ static void testWakeup(Path dir) throws IOException { System.out.println("-- Wakeup Tests --"); final WatchService watcher = FileSystems.getDefault().newWatchService(); Runnable r = new Runnable() { public void run() { try { Thread.sleep(5000); System.out.println("close WatchService..."); watcher.close(); } catch (InterruptedException x) { x.printStackTrace(); } catch (IOException x) { x.printStackTrace(); } } }; // start thread to close watch service after delay new Thread(r).start(); try { System.out.println("take..."); watcher.take(); throw new RuntimeException("ClosedWatchServiceException not thrown"); } catch (InterruptedException x) { throw new RuntimeException(x); } catch (ClosedWatchServiceException x) { System.out.println("ClosedWatchServiceException thrown"); } System.out.println("OKAY"); } /** * Simple test to check exceptions and other cases */ @SuppressWarnings("unchecked") static void testExceptions(Path dir) throws IOException { System.out.println("-- Exceptions and other simple tests --"); WatchService watcher = FileSystems.getDefault().newWatchService(); try { // Poll tests WatchKey key; System.out.println("poll..."); key = watcher.poll(); if (key != null) throw new RuntimeException("no keys registered"); System.out.println("poll with timeout..."); try { long start = System.nanoTime(); key = watcher.poll(3000, TimeUnit.MILLISECONDS); if (key != null) throw new RuntimeException("no keys registered"); long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); if (waited < 2900) throw new RuntimeException("poll was too short"); } catch (InterruptedException x) { throw new RuntimeException(x); } // IllegalArgumentException System.out.println("IllegalArgumentException tests..."); try { dir.register(watcher, new WatchEvent.Kind<?>[]{ } ); throw new RuntimeException("IllegalArgumentException not thrown"); } catch (IllegalArgumentException x) { } try { // OVERFLOW is ignored so this is equivalent to the empty set dir.register(watcher, new WatchEvent.Kind<?>[]{ OVERFLOW }); throw new RuntimeException("IllegalArgumentException not thrown"); } catch (IllegalArgumentException x) { } // UnsupportedOperationException try { dir.register(watcher, new WatchEvent.Kind<?>[]{ new WatchEvent.Kind<Object>() { @Override public String name() { return "custom"; } @Override public Class<Object> type() { return Object.class; } }}); } catch (UnsupportedOperationException x) { } try { dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }, new WatchEvent.Modifier() { @Override public String name() { return "custom"; } }); throw new RuntimeException("UnsupportedOperationException not thrown"); } catch (UnsupportedOperationException x) { } // NullPointerException System.out.println("NullPointerException tests..."); try { dir.register(null, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); throw new RuntimeException("NullPointerException not thrown"); } catch (NullPointerException x) { } try { dir.register(watcher, new WatchEvent.Kind<?>[]{ null }); throw new RuntimeException("NullPointerException not thrown"); } catch (NullPointerException x) { } try { dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }, (WatchEvent.Modifier)null); throw new RuntimeException("NullPointerException not thrown"); } catch (NullPointerException x) { } } finally { watcher.close(); } // -- ClosedWatchServiceException -- System.out.println("ClosedWatchServiceException tests..."); try { watcher.poll(); throw new RuntimeException("ClosedWatchServiceException not thrown"); } catch (ClosedWatchServiceException x) { } // assume that poll throws exception immediately long start = System.nanoTime(); try { watcher.poll(10000, TimeUnit.MILLISECONDS); throw new RuntimeException("ClosedWatchServiceException not thrown"); } catch (InterruptedException x) { throw new RuntimeException(x); } catch (ClosedWatchServiceException x) { long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); if (waited > 5000) throw new RuntimeException("poll was too long"); } try { watcher.take(); throw new RuntimeException("ClosedWatchServiceException not thrown"); } catch (InterruptedException x) { throw new RuntimeException(x); } catch (ClosedWatchServiceException x) { } try { dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); throw new RuntimeException("ClosedWatchServiceException not thrown"); } catch (ClosedWatchServiceException x) { } System.out.println("OKAY"); } /** * Test that directory can be registered with more than one watch service * and that events don't interfere with each other */ static void testTwoWatchers(Path dir) throws IOException { System.out.println("-- Two watchers test --"); FileSystem fs = FileSystems.getDefault(); WatchService watcher1 = fs.newWatchService(); WatchService watcher2 = fs.newWatchService(); try { Path name1 = fs.getPath("gus1"); Path name2 = fs.getPath("gus2"); // create gus1 Path file1 = dir.resolve(name1); System.out.format("create %s\n", file1); Files.createFile(file1); // register with both watch services (different events) System.out.println("register for different events"); WatchKey key1 = dir.register(watcher1, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); WatchKey key2 = dir.register(watcher2, new WatchEvent.Kind<?>[]{ ENTRY_DELETE }); if (key1 == key2) throw new RuntimeException("keys should be different"); // create gus2 Path file2 = dir.resolve(name2); System.out.format("create %s\n", file2); Files.createFile(file2); // check that key1 got ENTRY_CREATE takeExpectedKey(watcher1, key1); checkExpectedEvent(key1.pollEvents(), StandardWatchEventKinds.ENTRY_CREATE, name2); // check that key2 got zero events WatchKey key = watcher2.poll(); if (key != null) throw new RuntimeException("key not expected"); // delete gus1 Files.delete(file1); // check that key2 got ENTRY_DELETE takeExpectedKey(watcher2, key2); checkExpectedEvent(key2.pollEvents(), StandardWatchEventKinds.ENTRY_DELETE, name1); // check that key1 got zero events key = watcher1.poll(); if (key != null) throw new RuntimeException("key not expected"); // reset for next test key1.reset(); key2.reset(); // change registration with watcher2 so that they are both // registered for the same event System.out.println("register for same event"); key2 = dir.register(watcher2, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); // create file and key2 should be queued System.out.format("create %s\n", file1); Files.createFile(file1); takeExpectedKey(watcher2, key2); checkExpectedEvent(key2.pollEvents(), StandardWatchEventKinds.ENTRY_CREATE, name1); System.out.println("OKAY"); } finally { watcher2.close(); watcher1.close(); } } public static void main(String[] args) throws IOException { Path dir = TestUtil.createTemporaryDirectory(); try { testEvents(dir); testCancel(dir); testAutomaticCancel(dir); testWakeup(dir); testExceptions(dir); testTwoWatchers(dir); } finally { TestUtil.removeAll(dir); } } }