1 /*
   2  * Copyright (c) 2008, 2009, 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
  26  * @summary Unit test for java.nio.file.WatchService
  27  * @library ..
  28  * @run main/timeout=120 Basic
  29  */
  30 
  31 import java.nio.file.*;
  32 import static java.nio.file.StandardWatchEventKind.*;
  33 import java.nio.file.attribute.*;
  34 import java.io.*;
  35 import java.util.*;
  36 import java.util.concurrent.TimeUnit;
  37 
  38 /**
  39  * Unit test for WatchService that exercises all methods in various scenarios.
  40  */
  41 
  42 public 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                 StandardWatchEventKind.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                 StandardWatchEventKind.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                 StandardWatchEventKind.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.currentTimeMillis();
 285                 key = watcher.poll(3000, TimeUnit.MILLISECONDS);
 286                 if (key != null)
 287                     throw new RuntimeException("no keys registered");
 288                 long waited = System.currentTimeMillis() - 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, new WatchEvent.Kind<?>[]{ } );
 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, new WatchEvent.Kind<?>[]{ OVERFLOW });
 305                 throw new RuntimeException("IllegalArgumentException not thrown");
 306             } catch (IllegalArgumentException x) {
 307             }
 308 
 309             // UnsupportedOperationException
 310             try {
 311                 dir.register(watcher, new WatchEvent.Kind<?>[]{
 312                              new WatchEvent.Kind<Object>() {
 313                                 @Override public String name() { return "custom"; }
 314                                 @Override public Class<Object> type() { return Object.class; }
 315                              }});
 316             } catch (UnsupportedOperationException x) {
 317             }
 318             try {
 319                 dir.register(watcher,
 320                              new WatchEvent.Kind<?>[]{ ENTRY_CREATE },
 321                              new WatchEvent.Modifier() {
 322                                  @Override public String name() { return "custom"; }
 323                              });
 324                 throw new RuntimeException("UnsupportedOperationException not thrown");
 325             } catch (UnsupportedOperationException x) {
 326             }
 327 
 328             // NullPointerException
 329             System.out.println("NullPointerException tests...");
 330             try {
 331                 dir.register(null, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 332                 throw new RuntimeException("NullPointerException not thrown");
 333             } catch (NullPointerException x) {
 334             }
 335             try {
 336                 dir.register(watcher, new WatchEvent.Kind<?>[]{ null });
 337                 throw new RuntimeException("NullPointerException not thrown");
 338             } catch (NullPointerException x) {
 339             }
 340             try {
 341                 dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE },
 342                     (WatchEvent.Modifier)null);
 343                 throw new RuntimeException("NullPointerException not thrown");
 344             } catch (NullPointerException x) {
 345             }
 346         } finally {
 347             watcher.close();
 348         }
 349 
 350         // -- ClosedWatchServiceException --
 351 
 352         System.out.println("ClosedWatchServiceException tests...");
 353 
 354         try {
 355             watcher.poll();
 356             throw new RuntimeException("ClosedWatchServiceException not thrown");
 357         } catch (ClosedWatchServiceException  x) {
 358         }
 359 
 360         // assume that poll throws exception immediately
 361         long start = System.currentTimeMillis();
 362         try {
 363             watcher.poll(10000, TimeUnit.MILLISECONDS);
 364             throw new RuntimeException("ClosedWatchServiceException not thrown");
 365         } catch (InterruptedException x) {
 366             throw new RuntimeException(x);
 367         } catch (ClosedWatchServiceException  x) {
 368             long waited = System.currentTimeMillis() - start;
 369             if (waited > 5000)
 370                 throw new RuntimeException("poll was too long");
 371         }
 372 
 373         try {
 374             watcher.take();
 375             throw new RuntimeException("ClosedWatchServiceException not thrown");
 376         } catch (InterruptedException x) {
 377             throw new RuntimeException(x);
 378         } catch (ClosedWatchServiceException  x) {
 379         }
 380 
 381         try {
 382             dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 383              throw new RuntimeException("ClosedWatchServiceException not thrown");
 384         } catch (ClosedWatchServiceException  x) {
 385         }
 386 
 387         System.out.println("OKAY");
 388     }
 389 
 390     /**
 391      * Test that directory can be registered with more than one watch service
 392      * and that events don't interfere with each other
 393      */
 394     static void testTwoWatchers(Path dir) throws IOException {
 395         System.out.println("-- Two watchers test --");
 396 
 397         FileSystem fs = FileSystems.getDefault();
 398         WatchService watcher1 = fs.newWatchService();
 399         WatchService watcher2 = fs.newWatchService();
 400         try {
 401             Path name1 = fs.getPath("gus1");
 402             Path name2 = fs.getPath("gus2");
 403 
 404             // create gus1
 405             Path file1 = dir.resolve(name1);
 406             System.out.format("create %s\n", file1);
 407             Files.createFile(file1);
 408 
 409             // register with both watch services (different events)
 410             System.out.println("register for different events");
 411             WatchKey key1 = dir.register(watcher1,
 412                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 413             WatchKey key2 = dir.register(watcher2,
 414                 new WatchEvent.Kind<?>[]{ ENTRY_DELETE });
 415 
 416             if (key1 == key2)
 417                 throw new RuntimeException("keys should be different");
 418 
 419             // create gus2
 420             Path file2 = dir.resolve(name2);
 421             System.out.format("create %s\n", file2);
 422             Files.createFile(file2);
 423 
 424             // check that key1 got ENTRY_CREATE
 425             takeExpectedKey(watcher1, key1);
 426             checkExpectedEvent(key1.pollEvents(),
 427                 StandardWatchEventKind.ENTRY_CREATE, name2);
 428 
 429             // check that key2 got zero events
 430             WatchKey key = watcher2.poll();
 431             if (key != null)
 432                 throw new RuntimeException("key not expected");
 433 
 434             // delete gus1
 435             Files.delete(file1);
 436 
 437             // check that key2 got ENTRY_DELETE
 438             takeExpectedKey(watcher2, key2);
 439             checkExpectedEvent(key2.pollEvents(),
 440                 StandardWatchEventKind.ENTRY_DELETE, name1);
 441 
 442             // check that key1 got zero events
 443             key = watcher1.poll();
 444             if (key != null)
 445                 throw new RuntimeException("key not expected");
 446 
 447             // reset for next test
 448             key1.reset();
 449             key2.reset();
 450 
 451             // change registration with watcher2 so that they are both
 452             // registered for the same event
 453             System.out.println("register for same event");
 454             key2 = dir.register(watcher2, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 455 
 456             // create file and key2 should be queued
 457             System.out.format("create %s\n", file1);
 458             Files.createFile(file1);
 459             takeExpectedKey(watcher2, key2);
 460             checkExpectedEvent(key2.pollEvents(),
 461                 StandardWatchEventKind.ENTRY_CREATE, name1);
 462 
 463             System.out.println("OKAY");
 464 
 465         } finally {
 466             watcher2.close();
 467             watcher1.close();
 468         }
 469     }
 470 
 471     public static void main(String[] args) throws IOException {
 472         Path dir = TestUtil.createTemporaryDirectory();
 473         try {
 474 
 475             testEvents(dir);
 476             testCancel(dir);
 477             testAutomaticCancel(dir);
 478             testWakeup(dir);
 479             testExceptions(dir);
 480             testTwoWatchers(dir);
 481 
 482         } finally {
 483             TestUtil.removeAll(dir);
 484         }
 485     }
 486 }