1 /* 2 * Copyright (c) 2008, 2010, 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 import java.nio.file.*; 33 import static java.nio.file.StandardWatchEventKind.*; 34 import static java.nio.file.LinkOption.*; 35 import java.nio.file.attribute.*; 36 import java.io.*; 37 import java.util.*; 38 39 /** 40 * Example to watch a directory (or tree) for changes to files. 41 */ 42 43 public class WatchDir { 44 45 private final WatchService watcher; 46 private final Map<WatchKey,Path> keys; 47 private final boolean recursive; 48 private boolean trace = false; 49 50 @SuppressWarnings("unchecked") 51 static <T> WatchEvent<T> cast(WatchEvent<?> event) { 52 return (WatchEvent<T>)event; 53 } 54 55 /** 56 * Register the given directory with the WatchService 57 */ 58 private void register(Path dir) throws IOException { 59 WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 60 if (trace) { 61 FileRef prev = keys.get(key); 62 if (prev == null) { 63 System.out.format("register: %s\n", dir); 64 } else { 65 if (!dir.equals(prev)) { 66 System.out.format("update: %s -> %s\n", prev, dir); 67 } 68 } 69 } 70 keys.put(key, dir); 71 } 72 73 /** 74 * Register the given directory, and all its sub-directories, with the 75 * WatchService. 76 */ 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 */ 93 WatchDir(Path dir, boolean recursive) throws IOException { 94 this.watcher = FileSystems.getDefault().newWatchService(); 95 this.keys = new HashMap<WatchKey,Path>(); 96 this.recursive = recursive; 97 98 if (recursive) { 99 System.out.format("Scanning %s ...\n", dir); 100 registerAll(dir); 101 System.out.println("Done."); 102 } else { 103 register(dir); 104 } 105 106 // enable trace after initial registration 107 this.trace = true; 108 } 109 110 /** 111 * Process all events for keys queued to the watcher 112 */ 113 void processEvents() { 114 for (;;) { 115 116 // wait for key to be signalled 117 WatchKey key; 118 try { 119 key = watcher.take(); 120 } catch (InterruptedException x) { 121 return; 122 } 123 124 Path dir = keys.get(key); 125 if (dir == null) { 126 System.err.println("WatchKey not recognized!!"); 127 continue; 128 } 129 130 for (WatchEvent<?> event: key.pollEvents()) { 131 WatchEvent.Kind kind = event.kind(); 132 133 // TBD - provide example of how OVERFLOW event is handled 134 if (kind == OVERFLOW) { 135 continue; 136 } 137 138 // Context for directory entry event is the file name of entry 139 WatchEvent<Path> ev = cast(event); 140 Path name = ev.context(); 141 Path child = dir.resolve(name); 142 143 // print out event 144 System.out.format("%s: %s\n", event.kind().name(), child); 145 146 // if directory is created, and watching recursively, then 147 // register it and its sub-directories 148 if (recursive && (kind == ENTRY_CREATE)) { 149 try { 150 if (Attributes.readBasicFileAttributes(child, NOFOLLOW_LINKS).isDirectory()) { 151 registerAll(child); 152 } 153 } catch (IOException x) { 154 // ignore to keep sample readbale 155 } 156 } 157 } 158 159 // reset key and remove from set if directory no longer accessible 160 boolean valid = key.reset(); 161 if (!valid) { 162 keys.remove(key); 163 164 // all directories are inaccessible 165 if (keys.isEmpty()) { 166 break; 167 } 168 } 169 } 170 } 171 172 static void usage() { 173 System.err.println("usage: java WatchDir [-r] dir"); 174 System.exit(-1); 175 } 176 177 public static void main(String[] args) throws IOException { 178 // parse arguments 179 if (args.length == 0 || args.length > 2) 180 usage(); 181 boolean recursive = false; 182 int dirArg = 0; 183 if (args[0].equals("-r")) { 184 if (args.length < 2) 185 usage(); 186 recursive = true; 187 dirArg++; 188 } 189 190 // register directory and process its events 191 Path dir = Paths.get(args[dirArg]); 192 new WatchDir(dir, recursive).processEvents(); 193 } 194 }