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