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