View Javadoc

1   /*
2    * $Id: FileMonitor.java 8077 2007-08-27 20:15:25Z aperepel $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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 files;
36      private List 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();
47          listeners = new ArrayList();
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 (Iterator i = listeners.iterator(); i.hasNext();)
101         {
102             WeakReference reference = (WeakReference) i.next();
103             FileListener listener = (FileListener) reference.get();
104             if (listener == fileListener)
105             {
106                 return;
107             }
108         }
109 
110         // Use WeakReference to avoid memory leak if this becomes the
111         // sole reference to the object.
112         listeners.add(new WeakReference(fileListener));
113     }
114 
115     /**
116      * Remove listener from this file monitor.
117      * 
118      * @param fileListener Listener to remove.
119      */
120     public void removeListener(FileMonitor fileListener)
121     {
122         for (Iterator i = listeners.iterator(); i.hasNext();)
123         {
124             WeakReference reference = (WeakReference) i.next();
125             FileMonitor listener = (FileMonitor) reference.get();
126             if (listener == fileListener)
127             {
128                 i.remove();
129                 break;
130             }
131         }
132     }
133 
134     /**
135      * This is the timer thread which is executed every n milliseconds according to
136      * the setting of the file monitor.
137      */
138     public class FileMonitorNotifier extends TimerTask
139     {
140         private ExceptionListener exceptionListener;
141 
142         public FileMonitorNotifier()
143         {
144             super();
145         }
146 
147         public FileMonitorNotifier(ExceptionListener exceptionListener)
148         {
149             this.exceptionListener = exceptionListener;
150         }
151 
152         public void run()
153         {
154             // Loop over the registered files and see which have changed.
155             // Use a copy of the list in case listener wants to alter the
156             // list within its fileChanged method.
157             Collection fileKeys = new ArrayList(files.keySet());
158 
159             for (Iterator i = fileKeys.iterator(); i.hasNext();)
160             {
161                 File file = (File) i.next();
162                 long lastModifiedTime = ((Long) files.get(file)).longValue();
163                 long newModifiedTime = file.exists() ? file.lastModified() : -1;
164 
165                 // Chek if file has changed
166                 if (newModifiedTime != lastModifiedTime)
167                 {
168                     // Register new modified time
169                     files.put(file, new Long(newModifiedTime));
170 
171                     // Notify listeners
172                     for (Iterator j = listeners.iterator(); j.hasNext();)
173                     {
174                         WeakReference reference = (WeakReference) j.next();
175                         FileListener listener = (FileListener) reference.get();
176 
177                         // Remove from list if the back-end object has been GC'd
178                         if (listener == null)
179                         {
180                             j.remove();
181                         }
182                         else
183                         {
184                             try
185                             {
186                                 listener.fileChanged(file);
187                             }
188                             catch (IOException e)
189                             {
190                                 // TODO MULE-863: What should we do if null?
191                                 if (exceptionListener != null)
192                                 {
193                                     exceptionListener.exceptionThrown(e);
194                                 }
195                             }
196                         }
197                     }
198                 }
199             }
200         }
201     }
202 }