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
  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     }
  48 
  49     static void takeExpectedKey(WatchService watcher, WatchKey expected) {
  50         System.out.println("take events...");
  51         WatchKey key;
  52         try {
  53             key = watcher.take();
  54         } catch (InterruptedException x) {
  55             // not expected
  56             throw new RuntimeException(x);
  57         }
  58         if (key != expected)
  59             throw new RuntimeException("removed unexpected key");
  60     }
  61 
  62     static void checkExpectedEvent(Iterable<WatchEvent<?>> events,
  63                                    WatchEvent.Kind<?> expectedKind,
  64                                    Object expectedContext)
  65     {
  66         WatchEvent<?> event = events.iterator().next();
  67         System.out.format("got event: type=%s, count=%d, context=%s\n",
  68             event.kind(), event.count(), event.context());
  69         if (event.kind() != expectedKind)
  70             throw new RuntimeException("unexpected event");
  71         if (!expectedContext.equals(event.context()))
  72             throw new RuntimeException("unexpected context");
  73     }
  74 
  75     /**
  76      * Simple test of each of the standard events
  77      */
  78     static void testEvents(Path dir) throws IOException {
  79         System.out.println("-- Standard Events --");
  80 
  81         FileSystem fs = FileSystems.getDefault();
  82         Path name = fs.getPath("foo");
  83 
  84         try (WatchService watcher = fs.newWatchService()) {
  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             checkKey(myKey, dir);
  92 
  93             // create file
  94             Path file = dir.resolve("foo");
  95             System.out.format("create %s\n", file);
  96             Files.createFile(file);
  97 
  98             // remove key and check that we got the ENTRY_CREATE event
  99             takeExpectedKey(watcher, myKey);
 100             checkExpectedEvent(myKey.pollEvents(),
 101                 StandardWatchEventKind.ENTRY_CREATE, name);
 102 
 103             System.out.println("reset key");
 104             if (!myKey.reset())
 105                 throw new RuntimeException("key has been cancalled");
 106 
 107             System.out.println("OKAY");
 108 
 109             // --- ENTRY_DELETE ---
 110 
 111             System.out.format("register %s for ENTRY_DELETE\n", dir);
 112             WatchKey deleteKey = dir.register(watcher,
 113                 new WatchEvent.Kind<?>[]{ ENTRY_DELETE });
 114             if (deleteKey != myKey)
 115                 throw new RuntimeException("register did not return existing key");
 116             checkKey(deleteKey, dir);
 117 
 118             System.out.format("delete %s\n", file);
 119             Files.delete(file);
 120             takeExpectedKey(watcher, myKey);
 121             checkExpectedEvent(myKey.pollEvents(),
 122                 StandardWatchEventKind.ENTRY_DELETE, name);
 123 
 124             System.out.println("reset key");
 125             if (!myKey.reset())
 126                 throw new RuntimeException("key has been cancalled");
 127 
 128             System.out.println("OKAY");
 129 
 130             // create the file for the next test
 131             Files.createFile(file);
 132 
 133             // --- ENTRY_MODIFY ---
 134 
 135             System.out.format("register %s for ENTRY_MODIFY\n", dir);
 136             WatchKey newKey = dir.register(watcher,
 137                 new WatchEvent.Kind<?>[]{ ENTRY_MODIFY });
 138             if (newKey != myKey)
 139                 throw new RuntimeException("register did not return existing key");
 140             checkKey(newKey, dir);
 141 
 142             System.out.format("update: %s\n", file);
 143             try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.APPEND)) {
 144                 out.write("I am a small file".getBytes("UTF-8"));
 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             Files.delete(file);
 155         }
 156     }
 157 
 158     /**
 159      * Check that a cancelled key will never be queued
 160      */
 161     static void testCancel(Path dir) throws IOException {
 162         System.out.println("-- Cancel --");
 163 
 164         try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
 165 
 166             System.out.format("register %s for events\n", dir);
 167             WatchKey myKey = dir.register(watcher,
 168                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 169             checkKey(myKey, dir);
 170 
 171             System.out.println("cancel key");
 172             myKey.cancel();
 173 
 174             // create a file in the directory
 175             Path file = dir.resolve("mars");
 176             System.out.format("create: %s\n", file);
 177             Files.createFile(file);
 178 
 179             // poll for keys - there will be none
 180             System.out.println("poll...");
 181             try {
 182                 WatchKey key = watcher.poll(3000, TimeUnit.MILLISECONDS);
 183                 if (key != null)
 184                     throw new RuntimeException("key should not be queued");
 185             } catch (InterruptedException x) {
 186                 throw new RuntimeException(x);
 187             }
 188 
 189             // done
 190             Files.delete(file);
 191 
 192             System.out.println("OKAY");
 193         }
 194     }
 195 
 196     /**
 197      * Check that deleting a registered directory causes the key to be
 198      * cancelled and queued.
 199      */
 200     static void testAutomaticCancel(Path dir) throws IOException {
 201         System.out.println("-- Automatic Cancel --");
 202 
 203         Path subdir = Files.createDirectory(dir.resolve("bar"));
 204 
 205         try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
 206 
 207             System.out.format("register %s for events\n", subdir);
 208             WatchKey myKey = subdir.register(watcher,
 209                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY });
 210 
 211             System.out.format("delete: %s\n", subdir);
 212             Files.delete(subdir);
 213             takeExpectedKey(watcher, myKey);
 214 
 215             System.out.println("reset key");
 216             if (myKey.reset())
 217                 throw new RuntimeException("Key was not cancelled");
 218             if (myKey.isValid())
 219                 throw new RuntimeException("Key is still valid");
 220 
 221             System.out.println("OKAY");
 222 
 223         }
 224     }
 225 
 226     /**
 227      * Asynchronous close of watcher causes blocked threads to wakeup
 228      */
 229     static void testWakeup(Path dir) throws IOException {
 230         System.out.println("-- Wakeup Tests --");
 231         final WatchService watcher = FileSystems.getDefault().newWatchService();
 232         Runnable r = new Runnable() {
 233             public void run() {
 234                 try {
 235                     Thread.sleep(5000);
 236                     System.out.println("close WatchService...");
 237                     watcher.close();
 238                 } catch (InterruptedException x) {
 239                     x.printStackTrace();
 240                 } catch (IOException x) {
 241                     x.printStackTrace();
 242                 }
 243             }
 244         };
 245 
 246         // start thread to close watch service after delay
 247         new Thread(r).start();
 248 
 249         try {
 250             System.out.println("take...");
 251             watcher.take();
 252             throw new RuntimeException("ClosedWatchServiceException not thrown");
 253         } catch (InterruptedException x) {
 254             throw new RuntimeException(x);
 255         } catch (ClosedWatchServiceException  x) {
 256             System.out.println("ClosedWatchServiceException thrown");
 257         }
 258 
 259         System.out.println("OKAY");
 260     }
 261 
 262     /**
 263      * Simple test to check exceptions and other cases
 264      */
 265     @SuppressWarnings("unchecked")
 266     static void testExceptions(Path dir) throws IOException {
 267         System.out.println("-- Exceptions and other simple tests --");
 268 
 269         WatchService watcher = FileSystems.getDefault().newWatchService();
 270         try {
 271 
 272             // Poll tests
 273 
 274             WatchKey key;
 275             System.out.println("poll...");
 276             key = watcher.poll();
 277             if (key != null)
 278                 throw new RuntimeException("no keys registered");
 279 
 280             System.out.println("poll with timeout...");
 281             try {
 282                 long start = System.currentTimeMillis();
 283                 key = watcher.poll(3000, TimeUnit.MILLISECONDS);
 284                 if (key != null)
 285                     throw new RuntimeException("no keys registered");
 286                 long waited = System.currentTimeMillis() - start;
 287                 if (waited < 2900)
 288                     throw new RuntimeException("poll was too short");
 289             } catch (InterruptedException x) {
 290                 throw new RuntimeException(x);
 291             }
 292 
 293             // IllegalArgumentException
 294             System.out.println("IllegalArgumentException tests...");
 295             try {
 296                 dir.register(watcher, new WatchEvent.Kind<?>[]{ } );
 297                 throw new RuntimeException("IllegalArgumentException not thrown");
 298             } catch (IllegalArgumentException x) {
 299             }
 300             try {
 301                 // OVERFLOW is ignored so this is equivalent to the empty set
 302                 dir.register(watcher, new WatchEvent.Kind<?>[]{ OVERFLOW });
 303                 throw new RuntimeException("IllegalArgumentException not thrown");
 304             } catch (IllegalArgumentException x) {
 305             }
 306 
 307             // UnsupportedOperationException
 308             try {
 309                 dir.register(watcher, new WatchEvent.Kind<?>[]{
 310                              new WatchEvent.Kind<Object>() {
 311                                 @Override public String name() { return "custom"; }
 312                                 @Override public Class<Object> type() { return Object.class; }
 313                              }});
 314             } catch (UnsupportedOperationException x) {
 315             }
 316             try {
 317                 dir.register(watcher,
 318                              new WatchEvent.Kind<?>[]{ ENTRY_CREATE },
 319                              new WatchEvent.Modifier() {
 320                                  @Override public String name() { return "custom"; }
 321                              });
 322                 throw new RuntimeException("UnsupportedOperationException not thrown");
 323             } catch (UnsupportedOperationException x) {
 324             }
 325 
 326             // NullPointerException
 327             System.out.println("NullPointerException tests...");
 328             try {
 329                 dir.register(null, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 330                 throw new RuntimeException("NullPointerException not thrown");
 331             } catch (NullPointerException x) {
 332             }
 333             try {
 334                 dir.register(watcher, new WatchEvent.Kind<?>[]{ null });
 335                 throw new RuntimeException("NullPointerException not thrown");
 336             } catch (NullPointerException x) {
 337             }
 338             try {
 339                 dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE },
 340                     (WatchEvent.Modifier)null);
 341                 throw new RuntimeException("NullPointerException not thrown");
 342             } catch (NullPointerException x) {
 343             }
 344         } finally {
 345             watcher.close();
 346         }
 347 
 348         // -- ClosedWatchServiceException --
 349 
 350         System.out.println("ClosedWatchServiceException tests...");
 351 
 352         try {
 353             watcher.poll();
 354             throw new RuntimeException("ClosedWatchServiceException not thrown");
 355         } catch (ClosedWatchServiceException  x) {
 356         }
 357 
 358         // assume that poll throws exception immediately
 359         long start = System.currentTimeMillis();
 360         try {
 361             watcher.poll(10000, TimeUnit.MILLISECONDS);
 362             throw new RuntimeException("ClosedWatchServiceException not thrown");
 363         } catch (InterruptedException x) {
 364             throw new RuntimeException(x);
 365         } catch (ClosedWatchServiceException  x) {
 366             long waited = System.currentTimeMillis() - start;
 367             if (waited > 5000)
 368                 throw new RuntimeException("poll was too long");
 369         }
 370 
 371         try {
 372             watcher.take();
 373             throw new RuntimeException("ClosedWatchServiceException not thrown");
 374         } catch (InterruptedException x) {
 375             throw new RuntimeException(x);
 376         } catch (ClosedWatchServiceException  x) {
 377         }
 378 
 379         try {
 380             dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 381              throw new RuntimeException("ClosedWatchServiceException not thrown");
 382         } catch (ClosedWatchServiceException  x) {
 383         }
 384 
 385         System.out.println("OKAY");
 386     }
 387 
 388     /**
 389      * Test that directory can be registered with more than one watch service
 390      * and that events don't interfere with each other
 391      */
 392     static void testTwoWatchers(Path dir) throws IOException {
 393         System.out.println("-- Two watchers test --");
 394 
 395         FileSystem fs = FileSystems.getDefault();
 396         WatchService watcher1 = fs.newWatchService();
 397         WatchService watcher2 = fs.newWatchService();
 398         try {
 399             Path name1 = fs.getPath("gus1");
 400             Path name2 = fs.getPath("gus2");
 401 
 402             // create gus1
 403             Path file1 = dir.resolve(name1);
 404             System.out.format("create %s\n", file1);
 405             Files.createFile(file1);
 406 
 407             // register with both watch services (different events)
 408             System.out.println("register for different events");
 409             WatchKey key1 = dir.register(watcher1,
 410                 new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 411             WatchKey key2 = dir.register(watcher2,
 412                 new WatchEvent.Kind<?>[]{ ENTRY_DELETE });
 413 
 414             if (key1 == key2)
 415                 throw new RuntimeException("keys should be different");
 416 
 417             // create gus2
 418             Path file2 = dir.resolve(name2);
 419             System.out.format("create %s\n", file2);
 420             Files.createFile(file2);
 421 
 422             // check that key1 got ENTRY_CREATE
 423             takeExpectedKey(watcher1, key1);
 424             checkExpectedEvent(key1.pollEvents(),
 425                 StandardWatchEventKind.ENTRY_CREATE, name2);
 426 
 427             // check that key2 got zero events
 428             WatchKey key = watcher2.poll();
 429             if (key != null)
 430                 throw new RuntimeException("key not expected");
 431 
 432             // delete gus1
 433             Files.delete(file1);
 434 
 435             // check that key2 got ENTRY_DELETE
 436             takeExpectedKey(watcher2, key2);
 437             checkExpectedEvent(key2.pollEvents(),
 438                 StandardWatchEventKind.ENTRY_DELETE, name1);
 439 
 440             // check that key1 got zero events
 441             key = watcher1.poll();
 442             if (key != null)
 443                 throw new RuntimeException("key not expected");
 444 
 445             // reset for next test
 446             key1.reset();
 447             key2.reset();
 448 
 449             // change registration with watcher2 so that they are both
 450             // registered for the same event
 451             System.out.println("register for same event");
 452             key2 = dir.register(watcher2, new WatchEvent.Kind<?>[]{ ENTRY_CREATE });
 453 
 454             // create file and key2 should be queued
 455             System.out.format("create %s\n", file1);
 456             Files.createFile(file1);
 457             takeExpectedKey(watcher2, key2);
 458             checkExpectedEvent(key2.pollEvents(),
 459                 StandardWatchEventKind.ENTRY_CREATE, name1);
 460 
 461             System.out.println("OKAY");
 462 
 463         } finally {
 464             watcher2.close();
 465             watcher1.close();
 466         }
 467     }
 468 
 469     public static void main(String[] args) throws IOException {
 470         Path dir = TestUtil.createTemporaryDirectory();
 471         try {
 472 
 473             testEvents(dir);
 474             testCancel(dir);
 475             testAutomaticCancel(dir);
 476             testWakeup(dir);
 477             testExceptions(dir);
 478             testTwoWatchers(dir);
 479 
 480         } finally {
 481             TestUtil.removeAll(dir);
 482         }
 483     }
 484 }