View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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.Iterator;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  /**
31   * Implementation of a FilesystemAlterationObserver
32   * 
33   * @author tcurdt
34   */
35  public class FilesystemAlterationObserverImpl implements FilesystemAlterationObserver {
36  
37      private final Log log = LogFactory.getLog(FilesystemAlterationObserverImpl.class);
38      
39      private interface MonitorFile {
40  
41          long lastModified();
42          MonitorFile[] listFiles();
43          boolean isDirectory();
44          boolean exists();
45          String getName();
46  
47      }
48      
49      private final static class MonitorFileImpl implements MonitorFile {
50  
51          private final File file;
52  
53          public MonitorFileImpl( final File pFile ) {
54              file = pFile;
55          }
56  
57          public boolean exists() {
58              return file.exists();
59          }
60  
61          public MonitorFile[] listFiles() {
62              final File[] childs = file.listFiles();
63  
64              final MonitorFile[] providers = new MonitorFile[childs.length];
65              for (int i = 0; i < providers.length; i++) {
66                  providers[i] = new MonitorFileImpl(childs[i]);
67              }
68              return providers;
69          }
70  
71          public String getName() {
72              return file.getName();
73          }
74  
75          public boolean isDirectory() {
76              return file.isDirectory();
77          }
78  
79          public long lastModified() {
80              return file.lastModified();
81          }
82  
83          public String toString() {
84              return file.toString();
85          }
86  
87      }
88  
89      private final class Entry {
90  
91          private final static int TYPE_UNKNOWN = 0;
92          private final static int TYPE_FILE = 1;
93          private final static int TYPE_DIRECTORY = 2;
94  
95          private final MonitorFile file;
96          private long lastModified = -1;
97          private int lastType = TYPE_UNKNOWN;
98          private Map childs = new HashMap();
99  
100         public Entry(final MonitorFile pFile) {
101             file = pFile;
102         }
103 
104         public String getName() {
105             return file.getName();
106         }
107         
108         
109         public String toString() {
110             return file.toString();
111         }
112 
113 
114         private void compareChilds() {
115             if (!file.isDirectory()) {
116                 return;
117             }
118 
119             final MonitorFile[] files = file.listFiles();
120             final Set deleted = new HashSet(childs.values());
121             for (int i = 0; i < files.length; i++) {
122                 final MonitorFile f = files[i];
123                 final String name = f.getName();
124                 final Entry entry = (Entry)childs.get(name);
125                 if (entry != null) {
126                     // already recognized as child
127                     deleted.remove(entry);
128 
129                     if(entry.needsToBeDeleted()) {
130                         // we have to delete this one
131                         childs.remove(name);
132                     }
133                 } else {
134                     // a new child
135                     final Entry newChild = new Entry(f);
136                     childs.put(name, newChild);
137                     newChild.needsToBeDeleted();
138                 }
139             }
140 
141             // the ones not found on disk anymore
142 
143             for (Iterator it = deleted.iterator(); it.hasNext();) {
144                 final Entry entry = (Entry) it.next();
145                 entry.deleteChildsAndNotify();
146                 childs.remove(entry.getName());
147             }
148         }
149 
150 
151         private void deleteChildsAndNotify() {
152             for (Iterator it = childs.values().iterator(); it.hasNext();) {
153                 final Entry entry = (Entry) it.next();
154 
155                 entry.deleteChildsAndNotify();
156             }
157             childs.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                 // deleted or has never existed yet
170 
171 //                log.debug(file + " does not exist or has been deleted");
172 
173                 deleteChildsAndNotify();
174 
175                 // mark to be deleted by parent
176                 return true;
177             } else {
178                 // exists
179                 final long currentModified = file.lastModified(); 
180 
181                 if (currentModified != lastModified) {
182                     // last modified has changed
183                     lastModified = currentModified;
184 
185 //                    log.debug(file + " has new last modified");
186 
187                     // types only changes when also the last modified changes
188                     final int newType = (file.isDirectory()?TYPE_DIRECTORY:TYPE_FILE); 
189 
190                     if (lastType != newType) {
191                         // the type has changed
192 
193 //                        log.debug(file + " has a new type");
194 
195                         deleteChildsAndNotify();
196 
197                         lastType = newType;
198 
199                         // and then an add as the new type
200 
201                         if (newType == TYPE_DIRECTORY) {
202                             notifyOnDirectoryCreate(this);
203                             compareChilds();
204                         } else {
205                             notifyOnFileCreate(this);
206                         }
207 
208                         return false;
209                     }
210 
211                     if (newType == TYPE_DIRECTORY) {
212                         notifyOnDirectoryChange(this);
213                         compareChilds();
214                     } else {
215                         notifyOnFileChange(this);
216                     }
217 
218                     return false;
219 
220                 } else {
221 
222                     // so exists and has not changed
223 
224 //                    log.debug(file + " does exist and has not changed");
225 
226                     compareChilds();
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 Set listenersSet = new HashSet();
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 (int i = 0; i < listeners.length; i++) {
259             final FilesystemAlterationListener listener = listeners[i];
260             listener.onStart(this);
261         }
262     }
263     private void notifyOnStop() {
264         log.debug("onStop " + rootEntry);
265         for (int i = 0; i < listeners.length; i++) {
266             final FilesystemAlterationListener listener = listeners[i];
267             listener.onStop(this);
268         }
269     }
270 
271     private void notifyOnFileCreate( final Entry pEntry ) {
272         log.debug("onFileCreate " + pEntry);
273         for (int i = 0; i < listeners.length; i++) {
274             final FilesystemAlterationListener listener = listeners[i];
275             listener.onFileCreate(((MonitorFileImpl)pEntry.getFile()).file );
276         }
277     }
278     private void notifyOnFileChange( final Entry pEntry ) {
279         log.debug("onFileChange " + pEntry);
280         for (int i = 0; i < listeners.length; i++) {
281             final FilesystemAlterationListener listener = listeners[i];
282             listener.onFileChange(((MonitorFileImpl)pEntry.getFile()).file );
283         }
284     }
285     private void notifyOnFileDelete( final Entry pEntry ) {
286         log.debug("onFileDelete " + pEntry);
287         for (int i = 0; i < listeners.length; i++) {
288             final FilesystemAlterationListener listener = listeners[i];
289             listener.onFileDelete(((MonitorFileImpl)pEntry.getFile()).file );
290         }
291     }
292 
293     private void notifyOnDirectoryCreate( final Entry pEntry ) {
294         log.debug("onDirectoryCreate " + pEntry);
295         for (int i = 0; i < listeners.length; i++) {
296             final FilesystemAlterationListener listener = listeners[i];
297             listener.onDirectoryCreate(((MonitorFileImpl)pEntry.getFile()).file );
298         }
299     }
300     private void notifyOnDirectoryChange( final Entry pEntry ) {
301         log.debug("onDirectoryChange " + pEntry);
302         for (int i = 0; i < listeners.length; i++) {
303             final FilesystemAlterationListener listener = listeners[i];
304             listener.onDirectoryChange(((MonitorFileImpl)pEntry.getFile()).file );
305         }
306     }
307     private void notifyOnDirectoryDelete( final Entry pEntry ) {
308         log.debug("onDirectoryDelete " + pEntry);
309         for (int i = 0; i < listeners.length; i++) {
310             final FilesystemAlterationListener listener = listeners[i];
311             listener.onDirectoryDelete(((MonitorFileImpl)pEntry.getFile()).file );
312         }
313     }
314 
315 
316     private void checkEntries() {
317         if(rootEntry.needsToBeDeleted()) {
318             // root not existing
319             rootEntry.lastType = Entry.TYPE_UNKNOWN;
320         }
321     }
322 
323     
324     public synchronized void checkAndNotify() {
325         if (listeners.length == 0) {
326             return;
327         }
328 
329         notifyOnStart();
330         
331         checkEntries();
332         
333         notifyOnStop();
334     }
335 
336     
337     public File getRootDirectory() {
338         return rootDirectory;
339     }
340 
341     public synchronized void addListener( final FilesystemAlterationListener pListener ) {
342         if (listenersSet.add(pListener)) {
343             createArrayFromSet();
344         }
345     }
346 
347     public synchronized void removeListener( final FilesystemAlterationListener pListener ) {
348         if (listenersSet.remove(pListener)) {
349             createArrayFromSet();
350         }
351     }
352 
353     private void createArrayFromSet() {
354         final FilesystemAlterationListener[] newListeners = new FilesystemAlterationListener[listenersSet.size()];
355         listenersSet.toArray(newListeners);
356         listeners = newListeners;
357     }
358 
359     public FilesystemAlterationListener[] getListeners() {
360         return listeners;
361     }
362 
363 
364     public static void main( String[] args ) {
365         final FilesystemAlterationObserverImpl observer = new FilesystemAlterationObserverImpl(new File(args[0]));
366         while(true) {
367             observer.checkEntries();
368             try {
369                 Thread.sleep(1000);
370             } catch (InterruptedException e) {
371             }
372         }
373     }
374 }