1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
package soundchan.BotListener;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import static java.lang.Thread.sleep;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
public class MediaWatcher implements Runnable {
private MediaWatcherListener listener;
private String mediaFilename;
private Path mediaDir;
private WatchService watchService;
private WatchKey watchKey;
private ArrayList<WatchKey> subDirKeys;
private boolean isDirectory;
private int sleepTime = 5000;
@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>) event;
}
/**
* Creates a MediaWatcher, which monitors changes to files either within a directory or for a specific file.
* Defaults to scanning every 5 seconds.
* @param listener Object that will get a callback when there is a watch event
* @param filepath Path to either directory or specific file
* @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
*/
public MediaWatcher(MediaWatcherListener listener, String filepath, boolean watchSubDirs) {
this.listener = listener;
startWatchService(filepath, watchSubDirs);
}
/**
* Creates a MediaWatcher, which monitors changes to files either within a directory or for a specific file.
* @param listener Object that will get a callback when there is a watch event
* @param filepath Path to either directory or specific file
* @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
* @param sleepTime How long to put the scanner thread to sleep between rescans (time in milliseconds)
*/
public MediaWatcher(MediaWatcherListener listener, String filepath, boolean watchSubDirs, int sleepTime) {
this.listener = listener;
this.sleepTime = sleepTime;
startWatchService(filepath, watchSubDirs);
}
/**
* Sets up watch service for the file/directory
* @param filepath Path to file or directory to be scanned
* @param watchSubDirs Also watch any subdirectories in the given directory (doesn't do anything if watching a file)
*/
private void startWatchService(String filepath, boolean watchSubDirs) {
File mediaFile = new File(filepath);
this.mediaFilename = mediaFile.getName();
if(mediaFile.isFile()) {
try {
this.mediaDir = mediaFile.getCanonicalFile().getParentFile().toPath();
} catch (IOException e) {
System.out.println("Error getting parent path of " + mediaFilename);
}
isDirectory = false;
} else if(mediaFile.isDirectory()) {
this.mediaDir = mediaFile.toPath();
isDirectory = true;
subDirKeys = new ArrayList<>();
}
try {
this.watchService = FileSystems.getDefault().newWatchService();
this.watchKey = mediaDir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if(isDirectory && watchSubDirs) {
Files.walkFileTree(mediaDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
try {
WatchKey temp = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
subDirKeys.add(temp);
return FileVisitResult.CONTINUE;
} catch (IOException e) {
System.out.println("Error setting up watch service with sub dirs in " + filepath);
return FileVisitResult.SKIP_SUBTREE;
}
}
});
}
} catch (IOException e) {
System.out.println("Error setting up watcher for " + filepath);
}
}
/**
* Called by an executor, checks for changes to file(s)
*/
public void run() {
try {
while(true) {
WatchKey key = watchService.take(); // Wait for an event to happen
// Check this event happened in a place we are monitoring, otherwise ignore it
if(!isDirectory && this.watchKey != key) {
System.out.println("Error with WatchKey");
continue;
} else if(isDirectory) {
if(this.watchKey != key) { // Our event doesn't happen in the root of the sounds directory
boolean noKeyMatch = true;
for(WatchKey subKey : subDirKeys) { // Check if it happened in on of the sub directories
if(subKey == key) {
noKeyMatch = false;
break;
}
}
if(noKeyMatch) {
System.out.println("Error with WatchKey");
continue;
}
}
}
for(WatchEvent<?> event : key.pollEvents()) {
WatchEvent<Path> pathEvent = cast(event);
if(isDirectory) {
listener.onWatchEvent(event);
} else {
if(pathEvent.context().endsWith(mediaFilename)) {
listener.onWatchEvent(event);
}
}
}
if(!key.reset()) {
break;
}
sleep(sleepTime);
}
} catch(InterruptedException e) {
return;
}
}
}
|