KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > startup > layers > ModuleLayeredFileSystem


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.core.startup.layers;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileInputStream JavaDoc;
25 import java.io.FileOutputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.OutputStreamWriter JavaDoc;
29 import java.io.Writer JavaDoc;
30 import java.lang.reflect.Constructor JavaDoc;
31 import java.net.URI JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Collection JavaDoc;
36 import java.util.Collections JavaDoc;
37 import java.util.Comparator JavaDoc;
38 import java.util.Date JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.logging.Level JavaDoc;
42 import java.util.logging.Logger JavaDoc;
43 import org.netbeans.core.startup.StartLog;
44 import org.openide.filesystems.FileSystem;
45 import org.openide.filesystems.FileSystem.AtomicAction;
46 import org.openide.filesystems.MultiFileSystem;
47 import org.openide.filesystems.Repository;
48 import org.openide.util.NbBundle;
49 import org.openide.util.Utilities;
50
51 /** Layered file system serving itself as either the user or installation layer.
52  * Holds one layer of a writable system directory, and some number
53  * of module layers.
54  * @author Jesse Glick, Jaroslav Tulach
55  */

56 public class ModuleLayeredFileSystem extends MultiFileSystem {
57     /** serial version UID */
58     private static final long serialVersionUID = 782910986724201983L;
59     
60     private static final String JavaDoc LAYER_STAMP = "layer-stamp.txt";
61     
62     static final Logger JavaDoc err = Logger.getLogger("org.netbeans.core.projects"); // NOI18N
63

64     /** current list of URLs - r/o; or null if not yet set */
65     private List JavaDoc<URL JavaDoc> urls;
66     /** cache manager */
67     private LayerCacheManager manager;
68     /** writable layer */
69     private final FileSystem writableLayer;
70     /** cache layer */
71     private FileSystem cacheLayer;
72     /** other layers */
73     private final FileSystem[] otherLayers;
74
75     /** Create layered filesystem based on a supplied writable layer.
76      * @param writableLayer the writable layer to use, typically a LocalFileSystem
77      * @param otherLayers some other layers to use, e.g. LocalFileSystem[]
78      * @param cacheDir a directory in which to store a cache, or null for no caching
79      */

80     ModuleLayeredFileSystem (FileSystem writableLayer, FileSystem[] otherLayers, File JavaDoc cacheDir) throws IOException JavaDoc {
81         this(writableLayer, otherLayers, manager(cacheDir));
82     }
83     
84     private ModuleLayeredFileSystem(FileSystem writableLayer, FileSystem[] otherLayers, LayerCacheManager mgr) throws IOException JavaDoc {
85         this(writableLayer, otherLayers, mgr, loadCache(mgr));
86     }
87     
88     private ModuleLayeredFileSystem(FileSystem writableLayer, FileSystem[] otherLayers, LayerCacheManager mgr, FileSystem cacheLayer) throws IOException JavaDoc {
89         super(appendLayers(writableLayer, otherLayers, cacheLayer));
90         this.manager = mgr;
91         this.writableLayer = writableLayer;
92         this.otherLayers = otherLayers;
93         this.cacheLayer = cacheLayer;
94         
95         // Wish to permit e.g. a user-installed module to mask files from a
96
// root-installed module, so propagate masks up this high.
97
// SystemFileSystem leaves this off, so that the final file system
98
// will not show them if there are some left over.
99
setPropagateMasks (true);
100         
101         urls = null;
102     }
103     
104     private static LayerCacheManager manager(File JavaDoc cacheDir) throws IOException JavaDoc {
105         if (cacheDir != null) {
106             if (!cacheDir.isDirectory()) {
107                 if (!cacheDir.mkdirs()) {
108                     throw new IOException JavaDoc("Could not make dir: " + cacheDir); // NOI18N
109
}
110             }
111             String JavaDoc defaultManager = "org.netbeans.core.startup.layers.BinaryCacheManager"; // NOI18N
112
String JavaDoc managerName = System.getProperty("netbeans.cache.layers", defaultManager); // NOI18N
113
if (managerName.equals("-")) { // NOI18N
114
err.fine("Cache manager disabled");
115                 return LayerCacheManager.emptyManager();
116             }
117             try {
118                 Class JavaDoc<?> c = Class.forName(managerName);
119                 Constructor JavaDoc ctor = c.getConstructor(File JavaDoc.class);
120                 LayerCacheManager mgr = (LayerCacheManager)ctor.newInstance(cacheDir);
121                 err.fine("Using cache manager of type " + managerName + " in " + cacheDir);
122                 return mgr;
123             } catch (Exception JavaDoc e) {
124                 throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
125             }
126         } else {
127             err.fine("No cache manager");
128             return LayerCacheManager.emptyManager();
129         }
130     }
131     
132     private static FileSystem loadCache(LayerCacheManager mgr) throws IOException JavaDoc {
133         if (mgr.cacheExists()) {
134             // XXX use Events to log!
135
setStatusText(
136                 NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_start_load_cache"));
137             String JavaDoc msg = "Loading layers from " + mgr.getCacheDirectory(); // NOI18N
138
StartLog.logStart(msg);
139             FileSystem fs;
140             try {
141                 fs = mgr.createLoadedFileSystem();
142             } catch (IOException JavaDoc ioe) {
143                 err.log(Level.WARNING, null, ioe);
144                 mgr.cleanupCache();
145                 cleanStamp(mgr.getCacheDirectory());
146                 fs = mgr.createEmptyFileSystem();
147             }
148             setStatusText(
149                 NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_end_load_cache"));
150             StartLog.logEnd(msg);
151             return fs;
152         } else {
153             return mgr.createEmptyFileSystem();
154         }
155     }
156     
157     private static FileSystem[] appendLayers(FileSystem fs1, FileSystem[] fs2s, FileSystem fs3) {
158         List JavaDoc<FileSystem> l = new ArrayList JavaDoc<FileSystem>(fs2s.length + 2);
159         l.add(fs1);
160         l.addAll(Arrays.asList(fs2s));
161         l.add(fs3);
162         return l.toArray(new FileSystem[l.size()]);
163     }
164
165     private static void cleanStamp(File JavaDoc cacheDir) throws IOException JavaDoc {
166         File JavaDoc stampFile = new File JavaDoc(cacheDir, LAYER_STAMP);
167         if (stampFile.exists() && ! stampFile.delete()) {
168             throw new IOException JavaDoc("Could not delete: " + stampFile); // NOI18N
169
}
170     }
171     
172     /** Get all layers.
173      * @return all filesystems making layers
174      */

175     public/*but just for debugging*/ final FileSystem[] getLayers () {
176         return getDelegates ();
177     }
178
179     /** Get the writable layer.
180      * @return the writable layer
181      */

182     final FileSystem getWritableLayer () {
183         return writableLayer;
184     }
185     
186     /** Get the installation layer.
187      * You can take advantage of the specialized return type
188      * if working within the core.
189      */

190     public static ModuleLayeredFileSystem getInstallationModuleLayer () {
191         FileSystem fs = Repository.getDefault ().getDefaultFileSystem();
192         SystemFileSystem sfs = (SystemFileSystem)fs;
193         ModuleLayeredFileSystem home = sfs.getInstallationLayer ();
194         if (home != null)
195             return home;
196         else
197             return sfs.getUserLayer ();
198     }
199     
200     /** Get the user layer.
201      * You can take advantage of the specialized return type
202      * if working within the core.
203      */

204     public static ModuleLayeredFileSystem getUserModuleLayer () {
205         SystemFileSystem sfs = (SystemFileSystem)
206             Repository.getDefault().getDefaultFileSystem();
207         return sfs.getUserLayer ();
208     }
209
210     /** Change the list of module layers URLs.
211      * @param urls the urls describing module layers to use. List<URL>
212      */

213     public void setURLs (final List JavaDoc<URL JavaDoc> urls) throws Exception JavaDoc {
214         if (urls.contains(null)) throw new NullPointerException JavaDoc("urls=" + urls); // NOI18N
215
if (err.isLoggable(Level.FINE)) {
216             err.fine("setURLs: " + urls);
217         }
218         if (this.urls != null && urls.equals(this.urls)) {
219             err.fine("no-op");
220             return;
221         }
222         
223         StartLog.logStart("setURLs"); // NOI18N
224

225         final File JavaDoc stampFile;
226         final Stamp stamp;
227         final File JavaDoc cacheDir = manager.getCacheDirectory();
228         if (cacheDir != null) {
229             stampFile = new File JavaDoc(cacheDir, LAYER_STAMP);
230             stamp = new Stamp(manager.getClass().getName(), urls);
231         } else {
232             stampFile = null;
233             stamp = null;
234         }
235         if (cacheDir != null && stampFile.isFile()) {
236             err.fine("Stamp of new URLs: " + stamp.getHash());
237             BufferedReader JavaDoc r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(new FileInputStream JavaDoc(stampFile), "UTF-8")); // NOI18N
238
try {
239                 String JavaDoc line = r.readLine();
240                 long hash;
241                 try {
242                     hash = Long.parseLong(line);
243                 } catch (NumberFormatException JavaDoc nfe) {
244                     throw new IOException JavaDoc(nfe.toString());
245                 }
246                 err.fine("Stamp in the cache: " + hash);
247                 if (hash == stamp.getHash()) {
248                     err.fine("Cache hit!");
249                     this.urls = urls;
250                     StartLog.logEnd("setURLs"); // NOI18N
251
return;
252                 }
253             } finally {
254                 r.close();
255             }
256         }
257
258         // #17656: don't hold synch lock while firing changes, it could be dangerous...
259
runAtomicAction(new AtomicAction() {
260             public void run() throws IOException JavaDoc {
261                 synchronized (ModuleLayeredFileSystem.this) {
262                     if (cacheDir != null) {
263                         setStatusText(
264                             NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_start_rewrite_cache"));
265                         err.fine("Rewriting cache in " + cacheDir);
266                     } // else if null -> we are using emptyManager, so do not print confusing messages
267
try {
268                             if (manager.supportsLoad()) {
269                                 manager.store(cacheLayer, urls);
270                             } else {
271                                 cacheLayer = manager.store(urls);
272                                 setDelegates(appendLayers(writableLayer, otherLayers, cacheLayer));
273                             }
274                         } catch (IOException JavaDoc ioe) {
275                             err.log(Level.WARNING, null, ioe);
276                             err.fine("Abandoning cache manager");
277                             manager.cleanupCache();
278                             cleanStamp(cacheDir);
279                             manager = LayerCacheManager.emptyManager();
280                             // Try again with no-op manager.
281
try {
282                                 if (manager.supportsLoad()) {
283                                     cacheLayer = manager.createEmptyFileSystem();
284                                     manager.store(cacheLayer, urls);
285                                 } else {
286                                     cacheLayer = manager.store(urls);
287                                 }
288                                 setDelegates(appendLayers(writableLayer, otherLayers, cacheLayer));
289                             } catch (IOException JavaDoc ioe2) {
290                                 // More serious - should not happen.
291
err.log(Level.WARNING, null, ioe2);
292                             }
293                             return;
294                         }
295                     if (stampFile != null) {
296                         // Write out new stamp too.
297
Writer JavaDoc wr = new OutputStreamWriter JavaDoc(new FileOutputStream JavaDoc(stampFile), "UTF-8"); // NOI18N
298
try {
299                             // Would be nice to write out as zero-padded hex.
300
// Unfortunately while Long.toHexString works fine,
301
// Long.parseLong cannot be asked to parse unsigned longs,
302
// so fails when the high bit is set.
303
wr.write(Long.toString(stamp.getHash()));
304                             wr.write("\nLine above is identifying hash key, do not edit!\nBelow is metadata about layer cache, for debugging purposes.\n"); // NOI18N
305
wr.write(stamp.toString());
306                         } finally {
307                             wr.close();
308                         }
309                     }
310                     if (cacheDir != null) {
311                         setStatusText(
312                             NbBundle.getMessage(ModuleLayeredFileSystem.class, "MSG_end_rewrite_cache"));
313                         err.fine("Finished rewriting cache in " + cacheDir);
314                     }
315                 }
316             }
317         });
318         
319         this.urls = urls;
320         firePropertyChange ("layers", null, null); // NOI18N
321

322         StartLog.logEnd("setURLs"); // NOI18N
323
}
324     
325     /** Adds few URLs.
326      */

327     public void addURLs(Collection JavaDoc<URL JavaDoc> urls) throws Exception JavaDoc {
328         if (urls.contains(null)) throw new NullPointerException JavaDoc("urls=" + urls); // NOI18N
329
// Add to the front: #23609.
330
ArrayList JavaDoc<URL JavaDoc> arr = new ArrayList JavaDoc<URL JavaDoc>(urls);
331         if (this.urls != null) arr.addAll(this.urls);
332         setURLs(arr);
333     }
334     
335     /** Removes few URLs.
336      */

337     public void removeURLs(Collection JavaDoc<URL JavaDoc> urls) throws Exception JavaDoc {
338         if (urls.contains(null)) throw new NullPointerException JavaDoc("urls=" + urls); // NOI18N
339
ArrayList JavaDoc<URL JavaDoc> arr = new ArrayList JavaDoc<URL JavaDoc>();
340         if (this.urls != null) arr.addAll(this.urls);
341         arr.removeAll(urls);
342         setURLs(arr);
343     }
344     
345     /** Represents a hash of a bunch of jar: URLs and the associated JAR timestamps.
346      */

347     private static final class Stamp implements Comparator JavaDoc<URL JavaDoc> {
348         private final String JavaDoc managerName;
349         private final List JavaDoc<URL JavaDoc> urls;
350         private final long[] times;
351         private final long hash;
352         public Stamp(String JavaDoc name, List JavaDoc<URL JavaDoc> urls) throws IOException JavaDoc {
353             managerName = name;
354             this.urls = new ArrayList JavaDoc<URL JavaDoc>(urls);
355             Collections.sort(this.urls, this);
356             times = new long[this.urls.size()];
357             long x = 17L ^ managerName.hashCode();
358             Iterator JavaDoc it = this.urls.iterator();
359             int i = 0;
360             while (it.hasNext()) {
361                 URL JavaDoc u = (URL JavaDoc)it.next();
362                 String JavaDoc s = u.toExternalForm();
363                 x += 3199876987199633L;
364                 x ^= s.hashCode();
365                 URL JavaDoc u2;
366                 if (s.startsWith("jar:")) { // NOI18N
367
int bangSlash = s.lastIndexOf("!/"); // NOI18N
368
if (bangSlash != -1) {
369                         // underlying URL inside jar:, generally file:
370
u2 = new URL JavaDoc(s.substring(4, bangSlash));
371                     } else {
372                         err.warning("Weird JAR URL: " + u);
373                         u2 = u;
374                     }
375                 } else {
376                     // something else... plain file: URL?
377
u2 = u;
378                 }
379                 File JavaDoc extracted = new File JavaDoc(URI.create(u2.toExternalForm()));
380                 if (extracted != null) {
381                     // the JAR file containing the layer entry:
382
x ^= (times[i++] = extracted.lastModified());
383                 } else {
384                     // not a file: or jar:file: URL?
385
times[i++] = 0L;
386                 }
387             }
388             hash = x;
389         }
390         public long getHash() {
391             return hash;
392         }
393         public String JavaDoc toString() {
394             StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
395             buf.append(managerName);
396             buf.append('\n');
397             Iterator JavaDoc<URL JavaDoc> it = urls.iterator();
398             int i = 0;
399             while (it.hasNext()) {
400                 long t = times[i++];
401                 if (t == 0L) {
402                     buf.append("<file not found>"); // NOI18N
403
} else {
404                     buf.append(new Date JavaDoc(t));
405                 }
406                 buf.append('\t').append(it.next()).append('\n');
407             }
408             return buf.toString();
409         }
410         public int compare(URL JavaDoc o1, URL JavaDoc o2) {
411             return o1.toString().compareTo(o2.toString());
412         }
413     }
414     private static void setStatusText (String JavaDoc msg) {
415         org.netbeans.core.startup.Main.setStatusText(msg);
416     }
417
418 }
419
Popular Tags