KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > util > ResourceWatcher


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10
11 package org.mmbase.util;
12
13 import java.io.File JavaDoc;
14 import java.net.URL JavaDoc;
15 import java.util.*;
16
17
18 import org.mmbase.core.event.*;
19 import org.mmbase.util.logging.*;
20 import org.mmbase.bridge.*;
21
22 /**
23  * Like {@link org.mmbase.util.FileWatcher} but for Resources. If (one of the) file(s) to which the resource resolves
24  * to is added or changed, it's onChange will be triggered, if not a 'more important' wil was
25  * existing already. If a file is removed, and was the most important one, it will be removed from the filewatcher.
26  *
27  * @author Michiel Meeuwissen
28  * @since MMBase-1.8
29  * @version $Id: ResourceWatcher.java,v 1.13 2006/04/18 13:07:55 michiel Exp $
30  * @see org.mmbase.util.FileWatcher
31  * @see org.mmbase.util.ResourceLoader
32  */

33 public abstract class ResourceWatcher implements NodeEventListener {
34     private static final Logger log = Logging.getLoggerInstance(ResourceWatcher.class);
35
36     /**
37      * All instantiated ResourceWatchers. Only used until setResourceBuilder is called. Then it
38      * is set to null, and not used any more (also used in ResourceLoader).
39      *
40      */

41     static Set resourceWatchers = new HashSet();
42
43     /**
44      * Considers all resource-watchers. Perhaps onChange must be called, because there is a node for this resource available now.
45      */

46     static void setResourceBuilder() {
47         synchronized(resourceWatchers) {
48             Iterator i = resourceWatchers.iterator();
49             while (i.hasNext()) {
50                 ResourceWatcher rw = (ResourceWatcher) i.next();
51                 if (rw.running) {
52                     EventManager.getInstance().addEventListener(rw);
53                 }
54                 Iterator j = rw.resources.iterator();
55                 while (j.hasNext()) {
56                     String JavaDoc resource = (String JavaDoc) j.next();
57                     if (rw.mapNodeNumber(resource)) {
58                         log.service("ResourceBuilder is available now. Resource " + resource + " must be reloaded.");
59                         rw.onChange(resource);
60
61                     }
62                 }
63             }
64         }
65         resourceWatchers = null; // no need to store those any more.
66
}
67
68     /**
69      * Delay setting used for the filewatchers.
70      */

71    private long delay = -1;
72
73     /**
74      * All resources watched by this ResourceWatcher. A Set of Strings. Often, a ResourceWatcher would watch only one resource.
75      */

76     protected SortedSet resources = new TreeSet();
77
78     /**
79      * When a resource is loaded from a Node, we must know which Nodes correspond to which
80      * resource. You could ask the node itself, but if the node happens to be deleted, then you
81      * can't know that any more. Used in {@link #notify(NodeEvent)}
82      */

83     protected Map nodeNumberToResourceName = new HashMap();
84
85     /**
86      * Whether this ResourceWatcher has been started (see {@link #start})
87      */

88     private boolean running = false;
89
90     /**
91      * Wrapped FileWatcher for watching the file-resources. ResourceName -> FileWatcher.
92      */

93     protected Map fileWatchers = new HashMap();
94
95     /**
96      * The resource-loader associated with this ResourceWatcher.
97      */

98     protected ResourceLoader resourceLoader;
99
100
101     /**
102      * Constructor.
103      */

104     protected ResourceWatcher(ResourceLoader rl) {
105         resourceLoader = rl;
106         if (resourceWatchers != null) {
107             synchronized(resourceWatchers) {
108                 resourceWatchers.add(this);
109             }
110         }
111     }
112     /**
113      * Constructor, defaulting to the Root ResourceLoader (see {@link ResourceLoader#getConfigurationRoot}).
114      */

115     protected ResourceWatcher() {
116         this(ResourceLoader.getConfigurationRoot());
117     }
118
119
120
121     /**
122      * @return Unmodifiable set of String of watched resources
123      */

124     public Set getResources() {
125         return Collections.unmodifiableSortedSet(resources);
126     }
127
128     /**
129      * The associated ResourceLoader
130      */

131     public ResourceLoader getResourceLoader() {
132         return resourceLoader;
133     }
134
135     /**
136      *
137      * @param resourceName The resource to be monitored.
138      */

139     public synchronized void add(String JavaDoc resourceName) {
140         if (resourceName == null || resourceName.equals("")) {
141             log.warn("Cannot watch resource '" + resourceName + "' " + Logging.stackTrace());
142             return;
143         }
144         resources.add(resourceName);
145         log.service("Started watching '" + resourceName + "' for resource loader " + resourceLoader + "(now watching " + resources + ")");
146         if (running) {
147             createFileWatcher(resourceName);
148             mapNodeNumber(resourceName);
149         }
150     }
151
152     /**
153      * If you resolved a resource already to an URL, you can still add it for watching.
154      */

155     public synchronized void add(URL JavaDoc url) {
156         if (url.getProtocol().equals(ResourceLoader.PROTOCOL)) {
157             String JavaDoc path = url.getPath();
158             add(path.substring(resourceLoader.getContext().getPath().length()));
159         } else {
160             throw new UnsupportedOperationException JavaDoc("Don't know how to watch " + url + " (Only URLs produced by ResourceLoader are supported)");
161         }
162     }
163
164     /**
165      * When a resource is added to this ResourceWatcher, this method is called to create a
166      * {@link FileWatcher}, and add all files associated with the resource to it.
167      */

168     protected synchronized void createFileWatcher(String JavaDoc resource) {
169         FileWatcher fileWatcher = new ResourceFileWatcher(resource);
170         if (delay != -1) {
171             fileWatcher.setDelay(delay);
172         }
173         fileWatcher.getFiles().addAll(resourceLoader.getFiles(resource));
174         fileWatcher.start(); // filewatchers are only created on start, so must always be started themselves.
175
fileWatchers.put(resource, fileWatcher);
176     }
177
178     /**
179      * When a resource is added to this ResourceWatcher, this method is called to check wether a
180      * ResourceBuilder node is associated with this resource. If so, this methods maps the number of
181      * the node to the resource name. This is needed in {@link #notify(NodeEvent)} in case of a
182      * node-deletion.
183      * @return Whether a Node as found to map.
184      */

185     protected synchronized boolean mapNodeNumber(String JavaDoc resource) {
186         Node node = resourceLoader.getResourceNode(resource);
187         if (node != null) {
188             nodeNumberToResourceName.put("" + node.getNumber(), resource);
189             return true;
190         } else {
191             return false;
192         }
193
194     }
195
196
197
198
199     /**
200      * If a node (of the type 'resourceBuilder') changes, checks if it is a node belonging to one of the resource of this resource-watcher.
201      * If so, {@link #onChange} is called.
202      */

203     public void notify(NodeEvent event) {
204         if (event.getBuilderName().equals("resources")) {
205             String JavaDoc number = "" + event.getNodeNumber();
206             switch(event.getType()) {
207             case NodeEvent.TYPE_DELETE: {
208                 // hard..
209
String JavaDoc name = (String JavaDoc) nodeNumberToResourceName.get(number);
210                 if (name != null && resources.contains(name)) {
211                     nodeNumberToResourceName.remove(number);
212                     log.service("Resource " + name + " changed (node removed)");
213                     onChange(name);
214                 }
215                 break;
216             }
217             default: {
218                 Node node = ResourceLoader.resourceBuilder.getCloud().getNode(number);
219                 int contextPrefix = resourceLoader.getContext().getPath().length() - 1;
220                 String JavaDoc name = node.getStringValue(ResourceLoader.RESOURCENAME_FIELD);
221                 if (name.length() > contextPrefix && resources.contains(name.substring(contextPrefix))) {
222                     log.service("Resource " + name + " changed (node added or changed)");
223                     nodeNumberToResourceName.put(number, name);
224                     onChange(name);
225                 }
226             }
227             }
228         }
229     }
230
231     public synchronized void start() {
232         // create and start all filewatchers.
233
Iterator i = resources.iterator();
234         while (i.hasNext()) {
235             String JavaDoc resource = (String JavaDoc) i.next();
236             //resourceLoader.checkShadowedNewerResources(resource);
237
mapNodeNumber(resource);
238             createFileWatcher(resource);
239         }
240         if (resourceWatchers == null) {
241             EventManager.getInstance().addEventListener(this);
242         }
243         running = true;
244     }
245
246     /**
247      * Put here the stuff that has to be executed, when a file has been changed.
248      * @param resourceName The resource that was changed.
249      */

250     abstract public void onChange(String JavaDoc resourceName);
251
252     /**
253      * Calls {@link #onChange(String)} for every added resource.
254      */

255     public final void onChange() {
256         Iterator i = resources.iterator();
257         while (i.hasNext()) {
258             onChange((String JavaDoc) i.next());
259         }
260     }
261
262
263     /**
264      * Set the delay to observe between each check of the file changes.
265      */

266     public synchronized void setDelay(long delay) {
267         this.delay = delay;
268         Iterator i = fileWatchers.values().iterator();
269         while (i.hasNext()) {
270             FileWatcher fw = (FileWatcher) i.next();
271             fw.setDelay(delay);
272         }
273     }
274
275
276     /**
277      */

278     public synchronized void remove(String JavaDoc resourceName) {
279         boolean wasRunning = running;
280         if (running) { // it's simplest like this.
281
exit();
282         }
283         resources.remove(resourceName);
284         if (wasRunning) {
285             start();
286         }
287     }
288
289     /**
290      * Removes all resources.
291      */

292     public synchronized void clear() {
293         if (running) {
294             exit();
295             resources.clear();
296             start();
297         } else {
298             resources.clear();
299         }
300     }
301
302     /**
303      * Stops watching. Stops all filewatchers, removes observers.
304      */

305     public synchronized void exit() {
306         Iterator i = fileWatchers.values().iterator();
307         while (i.hasNext()) {
308             FileWatcher fw = (FileWatcher) i.next();
309             fw.exit();
310             i.remove();
311         }
312         if (ResourceLoader.resourceBuilder != null) {
313             EventManager.getInstance().removeEventListener(this);
314         }
315         running = false;
316     }
317
318
319     /**
320      * Shows the 'contents' of the filewatcher. It shows a list of files/last modified timestamps.
321      */

322     public String JavaDoc toString() {
323         return "" + resources + " " + fileWatchers;
324     }
325
326
327     /**
328      * A FileWatcher associated with a certain resource of this ResourceWatcher.
329      */

330
331     protected class ResourceFileWatcher extends FileWatcher {
332         private String JavaDoc resource;
333         ResourceFileWatcher(String JavaDoc resource) {
334             this.resource = resource;
335         }
336         public void onChange(File JavaDoc f) {
337             URL JavaDoc shadower = resourceLoader.shadowed(f, resource);
338             if (shadower == null) {
339                 ResourceWatcher.this.onChange(resource);
340             } else {
341                 log.warn("File " + f + " changed, but it is shadowed by " + shadower);
342             }
343         }
344     }
345
346
347 }
348
Popular Tags