/*
* Copyright (c) 2008, 2011, 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);
}
}
}