KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > LookupCache


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;
21
22 import java.io.BufferedInputStream JavaDoc;
23 import java.io.BufferedOutputStream JavaDoc;
24 import java.io.BufferedReader JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileInputStream JavaDoc;
27 import java.io.FileOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.InputStreamReader JavaDoc;
31 import java.io.ObjectInputStream JavaDoc;
32 import java.io.ObjectOutputStream JavaDoc;
33 import java.io.OutputStream JavaDoc;
34 import java.io.OutputStreamWriter JavaDoc;
35 import java.io.Writer JavaDoc;
36 import java.net.URI JavaDoc;
37 import java.net.URISyntaxException JavaDoc;
38 import java.net.URL JavaDoc;
39 import java.util.ArrayList JavaDoc;
40 import java.util.Collections JavaDoc;
41 import java.util.Date JavaDoc;
42 import java.util.Iterator JavaDoc;
43 import java.util.List JavaDoc;
44 import java.util.Locale JavaDoc;
45 import java.util.StringTokenizer JavaDoc;
46 import java.util.logging.Level JavaDoc;
47 import java.util.logging.Logger JavaDoc;
48 import org.openide.filesystems.FileObject;
49 import org.openide.filesystems.Repository;
50 import org.openide.loaders.DataFolder;
51 import org.openide.loaders.FolderLookup;
52 import org.openide.util.Lookup;
53 import org.openide.util.io.NbObjectInputStream;
54 import org.openide.util.io.NbObjectOutputStream;
55 import org.netbeans.Module;
56 import org.netbeans.ModuleManager;
57 import org.netbeans.core.startup.StartLog;
58
59 /**
60  * Responsible for persisting the structure of folder lookup.
61  * <p>A cache is kept in serialized form in $userdir/cache/folder-lookup.ser.
62  * Unless the cache is invalidated due to changes in module JARs or
63  * files in $userdir/system/**, it is restored after a regular startup.
64  * The actual objects in lookup are not serialized - only their classes,
65  * instanceof information, position in the Services folder, and so on. This
66  * permits us to avoid calling the XML parser for every .settings object etc.
67  * Other forms of lookup like META-INF/services/* are not persisted.
68  * <p>Can be enabled or disabled with the system property netbeans.cache.lookup.
69  * @author Jesse Glick, Jaroslav Tulach
70  * @see "#20190"
71  */

72 class LookupCache {
73
74     /** whether to enable the cache for this session */
75     private static final boolean ENABLED = Boolean.valueOf(System.getProperty("netbeans.cache.lookup", "true")).booleanValue(); // NOI18N
76

77     /** private logging for this class */
78     private static final Logger JavaDoc err = Logger.getLogger("org.netbeans.core.LookupCache"); // NOI18N
79

80     /**
81      * Get the Services/ folder lookup.
82      * May either do it the slow way, or might load quickly from a cache.
83      * @return the folder lookup for the system
84      */

85     public static Lookup load() {
86         err.fine("enabled=" + ENABLED);
87         if (ENABLED && cacheHit()) {
88             try {
89                 return loadCache();
90             } catch (Exception JavaDoc e) {
91                 Logger.getLogger(LookupCache.class.getName()).log(Level.INFO, null, e);
92             }
93         }
94         return loadDirect();
95     }
96     
97     /**
98      * Load folder lookup directly from the system file system, parsing
99      * as necessary (the slow way).
100      */

101     private static Lookup loadDirect() {
102         FileObject services = Repository.getDefault().getDefaultFileSystem().findResource("Services"); // NOI18N
103
if (services != null) {
104             StartLog.logProgress("Got Services folder"); // NOI18N
105
DataFolder servicesF;
106             try {
107                 servicesF = DataFolder.findFolder(services);
108             } catch (RuntimeException JavaDoc e) {
109                 Logger.getLogger(LookupCache.class.getName()).log(Level.INFO, null, e);
110                 return Lookup.EMPTY;
111             }
112             FolderLookup f = new FolderLookup(servicesF, "SL["); // NOI18N
113
StartLog.logProgress("created FolderLookup"); // NOI18N
114
err.fine("loadDirect from Services");
115             return f.getLookup();
116         } else {
117             err.fine("loadDirect, but no Services");
118             return Lookup.EMPTY;
119         }
120     }
121     
122     /**
123      * Determine if there is an existing lookup cache which can be used
124      * now as is.
125      * If there is a cache and a stamp file, and the stamp agrees with
126      * a calculation of the files and timestamps currently available to
127      * constitute the folder lookup, then the cache is used.
128      */

129     private static boolean cacheHit() {
130         File JavaDoc f = cacheFile();
131         if (f == null || !f.exists()) {
132             err.fine("no cache file");
133             return false;
134         }
135         File JavaDoc stampFile = stampFile();
136         if (stampFile == null || !stampFile.exists()) {
137             err.fine("no stamp file");
138             return false;
139         }
140         StartLog.logStart("check for lookup cache hit"); // NOI18N
141
List JavaDoc<File JavaDoc> files = relevantFiles();
142         if (err.isLoggable(Level.FINE)) {
143             err.fine("checking against " + stampFile + " for files " + files);
144         }
145         boolean hit;
146         try {
147             Stamp stamp = new Stamp(files);
148             long newHash = stamp.getHash();
149             BufferedReader JavaDoc r = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(new FileInputStream JavaDoc(stampFile), "UTF-8")); // NOI18N
150
try {
151                 String JavaDoc line = r.readLine();
152                 long oldHash;
153                 try {
154                     oldHash = Long.parseLong(line);
155                 } catch (NumberFormatException JavaDoc nfe) {
156                     throw new IOException JavaDoc(nfe.toString());
157                 }
158                 if (oldHash == newHash) {
159                     err.fine("Cache hit! with hash " + oldHash);
160                     hit = true;
161                 } else {
162                     err.fine("Cache miss, " + oldHash + " -> " + newHash);
163                     hit = false;
164                 }
165             } finally {
166                 r.close();
167             }
168         } catch (IOException JavaDoc ioe) {
169             Logger.getLogger(LookupCache.class.getName()).log(Level.INFO, null, ioe);
170             hit = false;
171         }
172         StartLog.logEnd("check for lookup cache hit"); // NOI18N
173
return hit;
174     }
175     
176     /**
177      * The file containing the serialized lookup cache.
178      */

179     private static File JavaDoc cacheFile() {
180         String JavaDoc ud = System.getProperty("netbeans.user");
181         if ((ud != null) && (! "memory".equals(ud))) {
182             File JavaDoc cachedir = new File JavaDoc(new File JavaDoc (ud, "var"), "cache"); // NOI18N
183
cachedir.mkdirs();
184             return new File JavaDoc(cachedir, "folder-lookup.ser"); // NOI18N
185
} else {
186             return null;
187         }
188     }
189     
190     /**
191      * The file containing a stamp which indicates which modules were
192      * enabled, what versions of them, customized services, etc.
193      */

194     private static File JavaDoc stampFile() {
195         String JavaDoc ud = System.getProperty("netbeans.user");
196         if ((ud != null) && (! "memory".equals(ud))) {
197             File JavaDoc cachedir = new File JavaDoc(new File JavaDoc (ud, "var"), "cache"); // NOI18N
198
cachedir.mkdirs();
199             return new File JavaDoc(cachedir, "lookup-stamp.txt"); // NOI18N
200
} else {
201             return null;
202         }
203     }
204     
205     /**
206      * List of all files which might be relevant to the contents of folder lookup.
207      * This means: all JAR files which are modules (skip their extensions and
208      * variants which can be assumed not to contain layer files); and all files
209      * contained in the system/Services/ subdirs (if any) of the home dir,
210      * user dir, and extra installation directories (#27151).
211      * For test modules, use the original JAR, not the physical JAR,
212      * to prevent cache misses on every restart.
213      * For fixed modules with layers (e.g. core.jar), add in the matching JAR,
214      * if that can be ascertained.
215      * No particular order of returned files is assumed.
216      */

217     private static List JavaDoc<File JavaDoc> relevantFiles() {
218         final List JavaDoc<File JavaDoc> files = new ArrayList JavaDoc<File JavaDoc>(250);
219         final ModuleManager mgr = org.netbeans.core.startup.Main.getModuleSystem().getManager();
220         mgr.mutex().readAccess(new Runnable JavaDoc() {
221
222                                    public void run() {
223                                        Iterator JavaDoc<Module> it = mgr.getEnabledModules().iterator();
224
225                                        while (it.hasNext()) {
226                                            Module m = it.next();
227                                            String JavaDoc layer = (String JavaDoc) m.getAttribute("OpenIDE-Module-Layer");
228
229                                            if (layer != null) {
230                                                if (m.getJarFile() != null) {
231                                                    files.add(m.getJarFile());
232                                                } else {
233                                                    URL JavaDoc layerURL = m.getClassLoader().getResource(layer);
234
235                                                    if (layerURL != null) {
236                                                        String JavaDoc s = layerURL.toExternalForm();
237
238                                                        if (s.startsWith("jar:")) {
239                                                            int bangSlash = s.lastIndexOf("!/");
240
241                                                            if (bangSlash != -1) {
242                                                                // underlying URL inside jar:, generally file:
243
try {
244                                                                    URI JavaDoc layerJarURL = new URI JavaDoc(s.substring(4,
245                                                                                                          bangSlash));
246
247                                                                    if ("file".equals(layerJarURL.getScheme())) {
248                                                                        files.add(new File JavaDoc(layerJarURL));
249                                                                    } else {
250                                                                        err.warning(
251                                                                                "Weird jar: URL: " +
252                                                                                layerJarURL);
253                                                                    }
254                                                                }
255                                                                catch (URISyntaxException JavaDoc e) {
256                                                                    Logger.getLogger(LookupCache.class.getName()).log(Level.WARNING,
257                                                                                      null,
258                                                                                      e);
259                                                                }
260                                                            } else {
261                                                                err.warning(
262                                                                        "Malformed jar: URL: " +
263                                                                        s);
264                                                            }
265                                                        } else {
266                                                            err.warning(
267                                                                    "Not a jar: URL: " +
268                                                                    s);
269                                                        }
270                                                    } else {
271                                                        err.warning(
272                                                                "Could not find " +
273                                                                layer + " in " +
274                                                                m);
275                                                    }
276                                                }
277                                            }
278                                        }
279                                    }
280                                });
281         relevantFilesFromInst(files, System.getProperty("netbeans.home")); // NOI18N
282
relevantFilesFromInst(files, System.getProperty("netbeans.user")); // NOI18N
283
String JavaDoc nbdirs = System.getProperty("netbeans.dirs"); // NOI18N
284
if (nbdirs != null) {
285             // #27151
286
StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(nbdirs, File.pathSeparator);
287             while (tok.hasMoreTokens()) {
288                 relevantFilesFromInst(files, tok.nextToken());
289             }
290         }
291         return files;
292     }
293     /**
294      * Find relevant files from an installation directory.
295      */

296     private static void relevantFilesFromInst(List JavaDoc<File JavaDoc> files, String JavaDoc instDir) {
297         if (instDir == null) {
298             return;
299         }
300         relevantFilesFrom(files, new File JavaDoc(new File JavaDoc(new File JavaDoc(instDir), "system"), "Services")); // NOI18N
301
}
302     /**
303      * Retrieve all files in a directory, recursively.
304      */

305     private static void relevantFilesFrom(List JavaDoc<File JavaDoc> files, File JavaDoc dir) {
306         File JavaDoc[] kids = dir.listFiles();
307         if (kids != null) {
308             for (int i = 0; i < kids.length; i++) {
309                 File JavaDoc f = kids[i];
310                 if (f.isFile()) {
311                     files.add(f);
312                 } else {
313                     relevantFilesFrom(files, f);
314                 }
315             }
316         }
317     }
318     
319     /**
320      * Load folder lookup from the disk cache.
321      */

322     private static Lookup loadCache() throws Exception JavaDoc {
323         StartLog.logStart("load lookup cache");
324         File JavaDoc f = cacheFile();
325         err.fine("loading from " + f);
326         InputStream JavaDoc is = new FileInputStream JavaDoc(f);
327         try {
328             ObjectInputStream JavaDoc ois = new NbObjectInputStream(new BufferedInputStream JavaDoc(is));
329             Lookup l = (Lookup)ois.readObject();
330             StartLog.logEnd("load lookup cache");
331             return l;
332         } finally {
333             is.close();
334         }
335     }
336     
337     /**
338      * Store the current contents of folder lookup to disk, hopefully to be used
339      * in the next session to speed startup.
340      * @param l the folder lookup
341      * @throws IOException if it could not be saved
342      */

343     public static void store(Lookup l) throws IOException JavaDoc {
344         if (!ENABLED) {
345             return;
346         }
347         File JavaDoc f = cacheFile();
348         if (f == null) {
349             return;
350         }
351         File JavaDoc stampFile = stampFile();
352         if (stampFile == null) {
353             return;
354         }
355         StartLog.logStart("store lookup cache");
356         err.fine("storing to " + f + " with stamp in " + stampFile);
357         OutputStream JavaDoc os = new FileOutputStream JavaDoc(f);
358         try {
359             try {
360                 ObjectOutputStream JavaDoc oos = new NbObjectOutputStream(new BufferedOutputStream JavaDoc(os));
361                 oos.writeObject(l);
362                 oos.flush();
363             } finally {
364                 os.close();
365             }
366             Stamp stamp = new Stamp(relevantFiles());
367             Writer JavaDoc wr = new OutputStreamWriter JavaDoc(new FileOutputStream JavaDoc(stampFile), "UTF-8"); // NOI18N
368
try {
369                 // Would be nice to write out as zero-padded hex.
370
// Unfortunately while Long.toHexString works fine,
371
// Long.parseLong cannot be asked to parse unsigned longs,
372
// so fails when the high bit is set.
373
wr.write(Long.toString(stamp.getHash()));
374                 wr.write("\nLine above is identifying hash key, do not edit!\nBelow is metadata about folder lookup cache, for debugging purposes.\n"); // NOI18N
375
wr.write(stamp.toString());
376             } finally {
377                 wr.close();
378             }
379             StartLog.logEnd("store lookup cache");
380         } catch (IOException JavaDoc ioe) {
381             // Delete corrupted cache.
382
if (f.exists()) {
383                 f.delete();
384             }
385             if (stampFile.exists()) {
386                 stampFile.delete();
387             }
388             throw ioe;
389         }
390     }
391     
392     /**
393      * Represents a hash of a bunch of JAR or other files and their timestamps.
394      * Compare ModuleLayeredFileSystem's similar nested class.
395      * .settings files do not get their timestamps checked because generally
396      * changes to them do not reflect changes in the structure of lookup, only
397      * in the contents of one lookup instance. Otherwise autoupdate's settings
398      * alone would trigger a cache miss every time. Generally, all files other
399      * than JARs and .nbattrs (which can affect folder order) should not affect
400      * lookup structure by their contents, except in the pathological case which
401      * we do not consider that they supply zero instances or a recursive lookup
402      * (which even then would only lead to problems if such a file were changed
403      * on disk between IDE sessions, which can be expected to be very rare).
404      */

405     private static final class Stamp {
406         private final List JavaDoc<File JavaDoc> files;
407         private final long[] times;
408         private final long hash;
409         /** Create a stamp from a list of files. */
410         public Stamp(List JavaDoc<File JavaDoc> files) throws IOException JavaDoc {
411             this.files = new ArrayList JavaDoc<File JavaDoc>(files);
412             Collections.sort(this.files);
413             times = new long[this.files.size()];
414             long x = 17L;
415             Iterator JavaDoc<File JavaDoc> it = this.files.iterator();
416             int i = 0;
417             while (it.hasNext()) {
418                 File JavaDoc f = it.next();
419                 x ^= f.hashCode();
420                 x += 98679245L;
421                 long m;
422                 String JavaDoc name = f.getName().toLowerCase(Locale.US);
423                 if (name.endsWith(".jar") || ".nbattrs".equals(name)) { // NOI18N
424
m = f.lastModified();
425                 } else {
426                     m = 0L;
427                 }
428                 x ^= (times[i++] = m);
429             }
430             hash = x;
431         }
432         /** Hash of the stamp for comparison purposes. */
433         public long getHash() {
434             return hash;
435         }
436         /** Debugging information listing which files were used. */
437         public String JavaDoc toString() {
438             StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
439             Iterator JavaDoc<File JavaDoc> it = files.iterator();
440             int i = 0;
441             while (it.hasNext()) {
442                 long t = times[i++];
443                 if (t != 0L) {
444                     buf.append(new Date JavaDoc(t));
445                 } else {
446                     buf.append("<ignoring file contents>"); // NOI18N
447
}
448                 buf.append('\t');
449                 buf.append(it.next());
450                 buf.append('\n');
451             }
452             return buf.toString();
453         }
454     }
455     
456 }
457
Popular Tags