1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.jci.monitor;
19
20 import java.io.File;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29
30
31
32
33
34 public class FilesystemAlterationObserverImpl implements FilesystemAlterationObserver {
35
36 private final Log log = LogFactory.getLog(FilesystemAlterationObserverImpl.class);
37
38 private interface MonitorFile {
39
40 long lastModified();
41 MonitorFile[] listFiles();
42 boolean isDirectory();
43 boolean exists();
44 String getName();
45
46 }
47
48 private final static class MonitorFileImpl implements MonitorFile {
49
50 private final File file;
51
52 public MonitorFileImpl( final File pFile ) {
53 file = pFile;
54 }
55
56 public boolean exists() {
57 return file.exists();
58 }
59
60 public MonitorFile[] listFiles() {
61 final File[] children = file.listFiles();
62 if (children == null) {
63 return new MonitorFile[0];
64 }
65
66 final MonitorFile[] providers = new MonitorFile[children.length];
67 for (int i = 0; i < providers.length; i++) {
68 providers[i] = new MonitorFileImpl(children[i]);
69 }
70 return providers;
71 }
72
73 public String getName() {
74 return file.getName();
75 }
76
77 public boolean isDirectory() {
78 return file.isDirectory();
79 }
80
81 public long lastModified() {
82 return file.lastModified();
83 }
84
85 @Override
86 public String toString() {
87 return file.toString();
88 }
89
90 }
91
92 private final class Entry {
93
94 private final static int TYPE_UNKNOWN = 0;
95 private final static int TYPE_FILE = 1;
96 private final static int TYPE_DIRECTORY = 2;
97
98 private final MonitorFile file;
99 private long lastModified = -1;
100 private int lastType = TYPE_UNKNOWN;
101 private final Map<String, Entry> children = new HashMap<String, Entry>();
102
103 public Entry(final MonitorFile pFile) {
104 file = pFile;
105 }
106
107 public String getName() {
108 return file.getName();
109 }
110
111
112 @Override
113 public String toString() {
114 return file.toString();
115 }
116
117
118 private void compareChildren() {
119 if (!file.isDirectory()) {
120 return;
121 }
122
123 final MonitorFile[] files = file.listFiles();
124 final Set<Entry> deleted = new HashSet<Entry>(children.values());
125 for (MonitorFile f : files) {
126 final String name = f.getName();
127 final Entry entry = children.get(name);
128 if (entry != null) {
129
130 deleted.remove(entry);
131
132 if(entry.needsToBeDeleted()) {
133
134 children.remove(name);
135 }
136 } else {
137
138 final Entry newChild = new Entry(f);
139 children.put(name, newChild);
140 newChild.needsToBeDeleted();
141 }
142 }
143
144
145
146 for (Entry entry : deleted) {
147 entry.deleteChildrenAndNotify();
148 children.remove(entry.getName());
149 }
150 }
151
152
153 private void deleteChildrenAndNotify() {
154 for (Entry entry : children.values()) {
155 entry.deleteChildrenAndNotify();
156 }
157 children.clear();
158
159 if(lastType == TYPE_DIRECTORY) {
160 notifyOnDirectoryDelete(this);
161 } else if (lastType == TYPE_FILE) {
162 notifyOnFileDelete(this);
163 }
164 }
165
166 public boolean needsToBeDeleted() {
167
168 if (!file.exists()) {
169
170
171
172
173 deleteChildrenAndNotify();
174
175
176 return true;
177 } else {
178
179 final long currentModified = file.lastModified();
180
181 if (currentModified != lastModified) {
182
183 lastModified = currentModified;
184
185
186
187
188 final int newType = (file.isDirectory()?TYPE_DIRECTORY:TYPE_FILE);
189
190 if (lastType != newType) {
191
192
193
194
195 deleteChildrenAndNotify();
196
197 lastType = newType;
198
199
200
201 if (newType == TYPE_DIRECTORY) {
202 notifyOnDirectoryCreate(this);
203 compareChildren();
204 } else {
205 notifyOnFileCreate(this);
206 }
207
208 return false;
209 }
210
211 if (newType == TYPE_DIRECTORY) {
212 notifyOnDirectoryChange(this);
213 compareChildren();
214 } else {
215 notifyOnFileChange(this);
216 }
217
218 return false;
219
220 } else {
221
222
223
224
225
226 compareChildren();
227
228 return false;
229 }
230 }
231 }
232
233 public MonitorFile getFile() {
234 return file;
235 }
236
237 public void markNotChanged() {
238 lastModified = file.lastModified();
239 }
240
241 }
242
243 private final File rootDirectory;
244 private final Entry rootEntry;
245
246 private FilesystemAlterationListener[] listeners = new FilesystemAlterationListener[0];
247 private final Set<FilesystemAlterationListener> listenersSet = new HashSet<FilesystemAlterationListener>();
248
249 public FilesystemAlterationObserverImpl( final File pRootDirectory ) {
250 rootDirectory = pRootDirectory;
251 rootEntry = new Entry(new MonitorFileImpl(pRootDirectory));
252 }
253
254
255
256 private void notifyOnStart() {
257 log.debug("onStart " + rootEntry);
258 for (FilesystemAlterationListener listener : listeners) {
259 listener.onStart(this);
260 }
261 }
262 private void notifyOnStop() {
263 log.debug("onStop " + rootEntry);
264 for (FilesystemAlterationListener listener : listeners) {
265 listener.onStop(this);
266 }
267 }
268
269 private void notifyOnFileCreate( final Entry pEntry ) {
270 log.debug("onFileCreate " + pEntry);
271 for (FilesystemAlterationListener listener : listeners) {
272 listener.onFileCreate(((MonitorFileImpl)pEntry.getFile()).file );
273 }
274 }
275 private void notifyOnFileChange( final Entry pEntry ) {
276 log.debug("onFileChange " + pEntry);
277 for (FilesystemAlterationListener listener : listeners) {
278 listener.onFileChange(((MonitorFileImpl)pEntry.getFile()).file );
279 }
280 }
281 private void notifyOnFileDelete( final Entry pEntry ) {
282 log.debug("onFileDelete " + pEntry);
283 for (FilesystemAlterationListener listener : listeners) {
284 listener.onFileDelete(((MonitorFileImpl)pEntry.getFile()).file );
285 }
286 }
287
288 private void notifyOnDirectoryCreate( final Entry pEntry ) {
289 log.debug("onDirectoryCreate " + pEntry);
290 for (FilesystemAlterationListener listener : listeners) {
291 listener.onDirectoryCreate(((MonitorFileImpl)pEntry.getFile()).file );
292 }
293 }
294 private void notifyOnDirectoryChange( final Entry pEntry ) {
295 log.debug("onDirectoryChange " + pEntry);
296 for (FilesystemAlterationListener listener : listeners) {
297 listener.onDirectoryChange(((MonitorFileImpl)pEntry.getFile()).file );
298 }
299 }
300 private void notifyOnDirectoryDelete( final Entry pEntry ) {
301 log.debug("onDirectoryDelete " + pEntry);
302 for (FilesystemAlterationListener listener : listeners) {
303 listener.onDirectoryDelete(((MonitorFileImpl)pEntry.getFile()).file );
304 }
305 }
306
307
308 private void checkEntries() {
309 if(rootEntry.needsToBeDeleted()) {
310
311 rootEntry.lastType = Entry.TYPE_UNKNOWN;
312 }
313 }
314
315
316 public void checkAndNotify() {
317 synchronized(listenersSet) {
318 if (listeners.length == 0) {
319 return;
320 }
321
322 notifyOnStart();
323
324 checkEntries();
325
326 notifyOnStop();
327 }
328 }
329
330
331 public File getRootDirectory() {
332 return rootDirectory;
333 }
334
335 public void addListener( final FilesystemAlterationListener pListener ) {
336 synchronized(listenersSet) {
337 if (listenersSet.add(pListener)) {
338 listeners = createArrayFromSet();
339 }
340 }
341 }
342
343 public void removeListener( final FilesystemAlterationListener pListener ) {
344 synchronized(listenersSet) {
345 if (listenersSet.remove(pListener)) {
346 listeners = createArrayFromSet();
347 }
348 }
349 }
350
351 private FilesystemAlterationListener[] createArrayFromSet() {
352 final FilesystemAlterationListener[] newListeners = new FilesystemAlterationListener[listenersSet.size()];
353 listenersSet.toArray(newListeners);
354 return newListeners;
355 }
356
357 public FilesystemAlterationListener[] getListeners() {
358 synchronized(listenersSet) {
359 final FilesystemAlterationListener[] res = new FilesystemAlterationListener[listeners.length];
360 System.arraycopy(listeners, 0, res, 0, res.length);
361 return res;
362 }
363 }
364 }