1 /* 2 * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41 import java.nio.file.*; 42 import static java.nio.file.StandardWatchEventKinds.*; 43 import static java.nio.file.LinkOption.*; 44 import java.nio.file.attribute.*; 45 import java.io.IOException; 46 47 /** 48 * Example to watch a directory (or tree) for changes to files. 49 */ 50 51 public class WatchDir { 52 53 private final WatchService watcher; 54 private final boolean recursive; 55 private boolean trace = false; 56 private int count; 57 58 @SuppressWarnings("unchecked") cast(WatchEvent<?> event)59 static <T> WatchEvent<T> cast(WatchEvent<?> event) { 60 return (WatchEvent<T>)event; 61 } 62 63 /** 64 * Register the given directory with the WatchService 65 */ register(Path dir)66 private void register(Path dir) throws IOException { 67 WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 68 count++; 69 if (trace) 70 System.out.format("register: %s\n", dir); 71 } 72 73 /** 74 * Register the given directory, and all its sub-directories, with the 75 * WatchService. 76 */ registerAll(final Path start)77 private void registerAll(final Path start) throws IOException { 78 // register directory and sub-directories 79 Files.walkFileTree(start, new SimpleFileVisitor<Path>() { 80 @Override 81 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) 82 throws IOException 83 { 84 register(dir); 85 return FileVisitResult.CONTINUE; 86 } 87 }); 88 } 89 90 /** 91 * Creates a WatchService and registers the given directory 92 */ WatchDir(Path dir, boolean recursive)93 WatchDir(Path dir, boolean recursive) throws IOException { 94 this.watcher = FileSystems.getDefault().newWatchService(); 95 this.recursive = recursive; 96 97 if (recursive) { 98 System.out.format("Scanning %s ...\n", dir); 99 registerAll(dir); 100 System.out.println("Done."); 101 } else { 102 register(dir); 103 } 104 105 // enable trace after initial registration 106 this.trace = true; 107 } 108 109 /** 110 * Process all events for keys queued to the watcher 111 */ processEvents()112 void processEvents() { 113 for (;;) { 114 115 // wait for key to be signalled 116 WatchKey key; 117 try { 118 key = watcher.take(); 119 } catch (InterruptedException x) { 120 return; 121 } 122 123 for (WatchEvent<?> event: key.pollEvents()) { 124 WatchEvent.Kind kind = event.kind(); 125 126 // TBD - provide example of how OVERFLOW event is handled 127 if (kind == OVERFLOW) { 128 continue; 129 } 130 131 // Context for directory entry event is the file name of entry 132 WatchEvent<Path> ev = cast(event); 133 Path name = ev.context(); 134 Path child = ((Path)key.watchable()).resolve(name); 135 136 // print out event 137 System.out.format("%s: %s\n", event.kind().name(), child); 138 139 // if directory is created, and watching recursively, then 140 // register it and its sub-directories 141 if (recursive && (kind == ENTRY_CREATE)) { 142 try { 143 if (Files.isDirectory(child, NOFOLLOW_LINKS)) { 144 registerAll(child); 145 } 146 } catch (IOException x) { 147 // ignore to keep sample readbale 148 } 149 } 150 } 151 152 // reset key 153 boolean valid = key.reset(); 154 if (!valid) { 155 // directory no longer accessible 156 count--; 157 if (count == 0) 158 break; 159 } 160 } 161 } 162 usage()163 static void usage() { 164 System.err.println("usage: java WatchDir [-r] dir"); 165 System.exit(-1); 166 } 167 main(String[] args)168 public static void main(String[] args) throws IOException { 169 // parse arguments 170 if (args.length == 0 || args.length > 2) 171 usage(); 172 boolean recursive = false; 173 int dirArg = 0; 174 if (args[0].equals("-r")) { 175 if (args.length < 2) 176 usage(); 177 recursive = true; 178 dirArg++; 179 } 180 181 // register directory and process its events 182 Path dir = Paths.get(args[dirArg]); 183 new WatchDir(dir, recursive).processEvents(); 184 } 185 } 186