View Javadoc

1   /*
2    * $Id: FileMonitor.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util.monitor;
12  
13  import java.beans.ExceptionListener;
14  import java.io.File;
15  import java.io.IOException;
16  import java.lang.ref.WeakReference;
17  import java.util.ArrayList;
18  import java.util.Collection;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Timer;
24  import java.util.TimerTask;
25  
26  /**
27   * Class for monitoring changes in disk files. Usage: 1. Implement the FileListener
28   * interface. 2. Create a FileMonitor instance. 3. Add the file(s)/directory(ies) to
29   * listen for. fileChanged() will be called when a monitored file is created, deleted
30   * or its modified time changes.
31   */
32  public class FileMonitor
33  {
34      private Timer timer;
35      private Map<File, Long> files;
36      private List<WeakReference<FileListener>> listeners;
37      private long pollingInterval;
38  
39      /**
40       * Create a file monitor instance with specified polling interval.
41       * 
42       * @param pollingInterval Polling interval in milli seconds.
43       */
44      public FileMonitor(long pollingInterval)
45      {
46          files = new HashMap<File, Long>();
47          listeners = new ArrayList<WeakReference<FileListener>>();
48          timer = new Timer(true);
49          this.pollingInterval = pollingInterval;
50      }
51  
52      /**
53       * Stop the file monitor polling.
54       */
55      public void stop()
56      {
57          timer.cancel();
58      }
59  
60      public void start()
61      {
62          timer.schedule(new FileMonitorNotifier(), 0, pollingInterval);
63      }
64  
65      /**
66       * Add file to listen for. File may be any java.io.File (including a directory)
67       * and may well be a non-existing file in the case where the creating of the file
68       * is to be trepped. <p/> More than one file can be listened for. When the
69       * specified file is created, modified or deleted, listeners are notified.
70       * 
71       * @param file File to listen for.
72       */
73      public void addFile(File file)
74      {
75          if (!files.containsKey(file))
76          {
77              long modifiedTime = file.exists() ? file.lastModified() : -1;
78              files.put(file, new Long(modifiedTime));
79          }
80      }
81  
82      /**
83       * Remove specified file for listening.
84       * 
85       * @param file File to remove.
86       */
87      public void removeFile(File file)
88      {
89          files.remove(file);
90      }
91  
92      /**
93       * Add listener to this file monitor.
94       * 
95       * @param fileListener Listener to add.
96       */
97      public void addListener(FileListener fileListener)
98      {
99          // Don't add if its already there
100         for (WeakReference<FileListener> reference : listeners)
101         {
102             FileListener listener = reference.get();
103             if (listener == fileListener)
104             {
105                 return;
106             }
107         }
108 
109         // Use WeakReference to avoid memory leak if this becomes the
110         // sole reference to the object.
111         listeners.add(new WeakReference<FileListener>(fileListener));
112     }
113 
114     /**
115      * Remove listener from this file monitor.
116      * 
117      * @param fileListener Listener to remove.
118      */
119     public void removeListener(FileListener fileListener)
120     {
121         for (Iterator<WeakReference<FileListener>> i = listeners.iterator(); i.hasNext();)
122         {
123             WeakReference<FileListener> reference = i.next();
124             FileListener listener = reference.get();
125             if (listener == fileListener)
126             {
127                 i.remove();
128                 break;
129             }
130         }
131     }
132 
133     /**
134      * This is the timer thread which is executed every n milliseconds according to
135      * the setting of the file monitor.
136      */
137     public class FileMonitorNotifier extends TimerTask
138     {
139         private ExceptionListener exceptionListener;
140 
141         public FileMonitorNotifier()
142         {
143             super();
144         }
145 
146         public FileMonitorNotifier(ExceptionListener exceptionListener)
147         {
148             this.exceptionListener = exceptionListener;
149         }
150 
151         public void run()
152         {
153             // Loop over the registered files and see which have changed.
154             // Use a copy of the list in case listener wants to alter the
155             // list within its fileChanged method.
156             Collection<File> fileKeys = new ArrayList<File>(files.keySet());
157 
158             for (File file : fileKeys)
159             {
160                 long lastModifiedTime = files.get(file).longValue();
161                 long newModifiedTime = file.exists() ? file.lastModified() : -1;
162 
163                 // Chek if file has changed
164                 if (newModifiedTime != lastModifiedTime)
165                 {
166                     // Register new modified time
167                     files.put(file, new Long(newModifiedTime));
168 
169                     // Notify listeners
170                     for (Iterator<WeakReference<FileListener>> j = listeners.iterator(); j.hasNext();)
171                     {
172                         WeakReference<FileListener> reference = j.next();
173                         FileListener listener = reference.get();
174 
175                         // Remove from list if the back-end object has been GC'd
176                         if (listener == null)
177                         {
178                             j.remove();
179                         }
180                         else
181                         {
182                             try
183                             {
184                                 listener.fileChanged(file);
185                             }
186                             catch (IOException e)
187                             {
188                                 // TODO MULE-863: What should we do if null?
189                                 if (exceptionListener != null)
190                                 {
191                                     exceptionListener.exceptionThrown(e);
192                                 }
193                             }
194                         }
195                     }
196                 }
197             }
198         }
199     }
200 }