212565afd7
6844313: New I/O: File timestamps should be represented by a FileTime rather than a long+TimeUnit Reviewed-by: sherman
494 lines
17 KiB
Java
494 lines
17 KiB
Java
/*
|
|
* Copyright 2008-2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
* have any questions.
|
|
*/
|
|
|
|
/* @test
|
|
* @bug 4313887 6838333
|
|
* @summary Unit test for java.nio.file.WatchService
|
|
* @library ..
|
|
* @run main/timeout=120 Basic
|
|
*/
|
|
|
|
import java.nio.file.*;
|
|
import static java.nio.file.StandardWatchEventKind.*;
|
|
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 createFile(Path file) throws IOException {
|
|
file.newOutputStream().close();
|
|
}
|
|
|
|
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");
|
|
|
|
WatchService watcher = fs.newWatchService();
|
|
try {
|
|
// --- 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 });
|
|
|
|
// create file
|
|
Path file = dir.resolve("foo");
|
|
System.out.format("create %s\n", file);
|
|
createFile(file);
|
|
|
|
// remove key and check that we got the ENTRY_CREATE event
|
|
takeExpectedKey(watcher, myKey);
|
|
checkExpectedEvent(myKey.pollEvents(),
|
|
StandardWatchEventKind.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");
|
|
|
|
System.out.format("delete %s\n", file);
|
|
file.delete();
|
|
takeExpectedKey(watcher, myKey);
|
|
checkExpectedEvent(myKey.pollEvents(),
|
|
StandardWatchEventKind.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
|
|
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");
|
|
|
|
System.out.format("update: %s\n", file);
|
|
OutputStream out = file.newOutputStream(StandardOpenOption.APPEND);
|
|
try {
|
|
out.write("I am a small file".getBytes("UTF-8"));
|
|
} finally {
|
|
out.close();
|
|
}
|
|
|
|
// remove key and check that we got the ENTRY_MODIFY event
|
|
takeExpectedKey(watcher, myKey);
|
|
checkExpectedEvent(myKey.pollEvents(),
|
|
StandardWatchEventKind.ENTRY_MODIFY, name);
|
|
System.out.println("OKAY");
|
|
|
|
// done
|
|
file.delete();
|
|
|
|
} finally {
|
|
watcher.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that a cancelled key will never be queued
|
|
*/
|
|
static void testCancel(Path dir) throws IOException {
|
|
System.out.println("-- Cancel --");
|
|
|
|
WatchService watcher = FileSystems.getDefault().newWatchService();
|
|
try {
|
|
|
|
System.out.format("register %s for events\n", dir);
|
|
WatchKey myKey = dir.register(watcher,
|
|
new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
|
|
|
|
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);
|
|
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
|
|
file.delete();
|
|
|
|
System.out.println("OKAY");
|
|
|
|
} finally {
|
|
watcher.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 = dir.resolve("bar").createDirectory();
|
|
|
|
WatchService watcher = FileSystems.getDefault().newWatchService();
|
|
try {
|
|
|
|
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);
|
|
subdir.delete();
|
|
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");
|
|
|
|
} finally {
|
|
watcher.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.currentTimeMillis();
|
|
key = watcher.poll(3000, TimeUnit.MILLISECONDS);
|
|
if (key != null)
|
|
throw new RuntimeException("no keys registered");
|
|
long waited = System.currentTimeMillis() - 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.currentTimeMillis();
|
|
try {
|
|
watcher.poll(10000, TimeUnit.MILLISECONDS);
|
|
throw new RuntimeException("ClosedWatchServiceException not thrown");
|
|
} catch (InterruptedException x) {
|
|
throw new RuntimeException(x);
|
|
} catch (ClosedWatchServiceException x) {
|
|
long waited = System.currentTimeMillis() - 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);
|
|
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);
|
|
createFile(file2);
|
|
|
|
// check that key1 got ENTRY_CREATE
|
|
takeExpectedKey(watcher1, key1);
|
|
checkExpectedEvent(key1.pollEvents(),
|
|
StandardWatchEventKind.ENTRY_CREATE, name2);
|
|
|
|
// check that key2 got zero events
|
|
WatchKey key = watcher2.poll();
|
|
if (key != null)
|
|
throw new RuntimeException("key not expected");
|
|
|
|
// delete gus1
|
|
file1.delete();
|
|
|
|
// check that key2 got ENTRY_DELETE
|
|
takeExpectedKey(watcher2, key2);
|
|
checkExpectedEvent(key2.pollEvents(),
|
|
StandardWatchEventKind.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);
|
|
createFile(file1);
|
|
takeExpectedKey(watcher2, key2);
|
|
checkExpectedEvent(key2.pollEvents(),
|
|
StandardWatchEventKind.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);
|
|
}
|
|
}
|
|
}
|