alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.core;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import org.openide.ErrorManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.Repository;
import org.openide.loaders.DataFolder;
import org.openide.loaders.FolderLookup;
import org.openide.util.Lookup;
import org.openide.util.Utilities;
import org.openide.util.io.NbObjectInputStream;
import org.openide.util.io.NbObjectOutputStream;

import org.netbeans.core.modules.Module;
import org.netbeans.core.modules.ModuleManager;
import org.netbeans.core.perftool.StartLog;

/**
 * Responsible for persisting the structure of folder lookup.
 * 

A cache is kept in serialized form in $userdir/cache/folder-lookup.ser. * Unless the cache is invalidated due to changes in module JARs or * files in $userdir/system/**, it is restored after a regular startup. * The actual objects in lookup are not serialized - only their classes, * instanceof information, position in the Services folder, and so on. This * permits us to avoid calling the XML parser for every .settings object etc. * Other forms of lookup like META-INF/services/* are not persisted. *

Can be enabled or disabled with the system property netbeans.cache.lookup. * @author Jesse Glick, Jaroslav Tulach * @see "#20190" */ class LookupCache { /** whether to enable the cache for this session */ private static final boolean ENABLED = Boolean.valueOf(System.getProperty("netbeans.cache.lookup", "true")).booleanValue(); // NOI18N /** private logging for this class */ private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.netbeans.core.LookupCache"); // NOI18N /** * Get the Services/ folder lookup. * May either do it the slow way, or might load quickly from a cache. * @return the folder lookup for the system */ public static Lookup load() { err.log("enabled=" + ENABLED); if (ENABLED && cacheHit()) { try { return loadCache(); } catch (Exception e) { err.notify(ErrorManager.INFORMATIONAL, e); } } return loadDirect(); } /** * Load folder lookup directly from the system file system, parsing * as necessary (the slow way). */ private static Lookup loadDirect() { FileObject services = Repository.getDefault().getDefaultFileSystem().findResource("Services"); // NOI18N if (services != null) { StartLog.logProgress("Got Services folder"); // NOI18N DataFolder servicesF; try { servicesF = DataFolder.findFolder(services); } catch (RuntimeException e) { err.notify(ErrorManager.INFORMATIONAL, e); return Lookup.EMPTY; } FolderLookup f = new FolderLookup(servicesF, "SL["); // NOI18N StartLog.logProgress("created FolderLookup"); // NOI18N err.log("loadDirect from Services"); return f.getLookup(); } else { err.log("loadDirect, but no Services"); return Lookup.EMPTY; } } /** * Determine if there is an existing lookup cache which can be used * now as is. * If there is a cache and a stamp file, and the stamp agrees with * a calculation of the files and timestamps currently available to * constitute the folder lookup, then the cache is used. */ private static boolean cacheHit() { File f = cacheFile(); if (f == null || !f.exists()) { err.log("no cache file"); return false; } File stampFile = stampFile(); if (stampFile == null || !stampFile.exists()) { err.log("no stamp file"); return false; } StartLog.logStart("check for lookup cache hit"); // NOI18N List files = relevantFiles(); // List if (err.isLoggable(ErrorManager.INFORMATIONAL)) { err.log("checking against " + stampFile + " for files " + files); } boolean hit; try { Stamp stamp = new Stamp(files); long newHash = stamp.getHash(); BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(stampFile), "UTF-8")); // NOI18N try { String line = r.readLine(); long oldHash; try { oldHash = Long.parseLong(line); } catch (NumberFormatException nfe) { throw new IOException(nfe.toString()); } if (oldHash == newHash) { err.log("Cache hit! with hash " + oldHash); hit = true; } else { err.log("Cache miss, " + oldHash + " -> " + newHash); hit = false; } } finally { r.close(); } } catch (IOException ioe) { err.notify(ErrorManager.INFORMATIONAL, ioe); hit = false; } StartLog.logEnd("check for lookup cache hit"); // NOI18N return hit; } /** * The file containing the serialized lookup cache. */ private static File cacheFile() { String ud = System.getProperty("netbeans.user"); if (ud != null) { File cachedir = new File(new File (ud, "var"), "cache"); // NOI18N cachedir.mkdirs(); return new File(cachedir, "folder-lookup.ser"); // NOI18N } else { return null; } } /** * The file containing a stamp which indicates which modules were * enabled, what versions of them, customized services, etc. */ private static File stampFile() { String ud = System.getProperty("netbeans.user"); if (ud != null) { File cachedir = new File(new File (ud, "var"), "cache"); // NOI18N cachedir.mkdirs(); return new File(cachedir, "lookup-stamp.txt"); // NOI18N } else { return null; } } /** * List of all files which might be relevant to the contents of folder lookup. * This means: all JAR files which are modules (skip their extensions and * variants which can be assumed not to contain layer files); and all files * contained in the system/Services/ subdirs (if any) of the home dir, * user dir, and extra installation directories (#27151). * For test modules, use the original JAR, not the physical JAR, * to prevent cache misses on every restart. * For fixed modules with layers (e.g. core.jar), add in the matching JAR, * if that can be ascertained. * No particular order of returned files is assumed. */ private static List relevantFiles() { final List files = new ArrayList(250); // List final ModuleManager mgr = NbTopManager.get().getModuleSystem().getManager(); mgr.mutex().readAccess(new Runnable() { public void run() { Iterator it = mgr.getEnabledModules().iterator(); while (it.hasNext()) { Module m = (Module)it.next(); String layer = (String)m.getAttribute("OpenIDE-Module-Layer"); // NOI18N if (layer != null) { if (!m.isFixed()) { files.add(m.getJarFile()); } else { URL layerURL = m.getClassLoader().getResource(layer); if (layerURL != null) { String s = layerURL.toExternalForm(); if (s.startsWith("jar:")) { // NOI18N int bangSlash = s.lastIndexOf("!/"); // NOI18N if (bangSlash != -1) { // underlying URL inside jar:, generally file: try { URL layerJarURL = new URL(s.substring(4, bangSlash)); File layerJar = Utilities.toFile(layerJarURL); if (layerJar != null) { files.add(layerJar); } else { err.log(ErrorManager.WARNING, "Weird jar: URL: " + layerJarURL); } } catch (MalformedURLException mfue) { err.notify(ErrorManager.INFORMATIONAL, mfue); } } else { err.log(ErrorManager.WARNING, "Malformed jar: URL: " + s); } } else { err.log(ErrorManager.WARNING, "Not a jar: URL: " + s); } } else { err.log(ErrorManager.WARNING, "Could not find " + layer + " in " + m); } } } // else no layer, ignore } } }); relevantFilesFromInst(files, System.getProperty("netbeans.home")); // NOI18N relevantFilesFromInst(files, System.getProperty("netbeans.user")); // NOI18N String nbdirs = System.getProperty("netbeans.dirs"); // NOI18N if (nbdirs != null) { // #27151 StringTokenizer tok = new StringTokenizer(nbdirs, File.pathSeparator); while (tok.hasMoreTokens()) { relevantFilesFromInst(files, tok.nextToken()); } } return files; } /** * Find relevant files from an installation directory. */ private static void relevantFilesFromInst(List files, String instDir) { if (instDir == null) { return; } relevantFilesFrom(files, new File(new File(new File(instDir), "system"), "Services")); // NOI18N } /** * Retrieve all files in a directory, recursively. */ private static void relevantFilesFrom(List files, File dir) { File[] kids = dir.listFiles(); if (kids != null) { for (int i = 0; i < kids.length; i++) { File f = kids[i]; if (f.isFile()) { files.add(f); } else { relevantFilesFrom(files, f); } } } } /** * Load folder lookup from the disk cache. */ private static Lookup loadCache() throws Exception { StartLog.logStart("load lookup cache"); File f = cacheFile(); err.log("loading from " + f); InputStream is = new FileInputStream(f); try { ObjectInputStream ois = new NbObjectInputStream(new BufferedInputStream(is)); Lookup l = (Lookup)ois.readObject(); StartLog.logEnd("load lookup cache"); return l; } finally { is.close(); } } /** * Store the current contents of folder lookup to disk, hopefully to be used * in the next session to speed startup. * @param l the folder lookup * @throws IOException if it could not be saved */ public static void store(Lookup l) throws IOException { if (!ENABLED) { return; } File f = cacheFile(); if (f == null) { return; } File stampFile = stampFile(); if (stampFile == null) { return; } StartLog.logStart("store lookup cache"); err.log("storing to " + f + " with stamp in " + stampFile); OutputStream os = new FileOutputStream(f); try { try { ObjectOutputStream oos = new NbObjectOutputStream(new BufferedOutputStream(os)); oos.writeObject(l); oos.flush(); } finally { os.close(); } Stamp stamp = new Stamp(relevantFiles()); Writer wr = new OutputStreamWriter(new FileOutputStream(stampFile), "UTF-8"); // NOI18N try { // Would be nice to write out as zero-padded hex. // Unfortunately while Long.toHexString works fine, // Long.parseLong cannot be asked to parse unsigned longs, // so fails when the high bit is set. wr.write(Long.toString(stamp.getHash())); wr.write("\nLine above is identifying hash key, do not edit!\nBelow is metadata about folder lookup cache, for debugging purposes.\n"); // NOI18N wr.write(stamp.toString()); } finally { wr.close(); } StartLog.logEnd("store lookup cache"); } catch (IOException ioe) { // Delete corrupted cache. if (f.exists()) { f.delete(); } if (stampFile.exists()) { stampFile.delete(); } throw ioe; } } /** * Represents a hash of a bunch of JAR or other files and their timestamps. * Compare ModuleLayeredFileSystem's similar nested class. * .settings files do not get their timestamps checked because generally * changes to them do not reflect changes in the structure of lookup, only * in the contents of one lookup instance. Otherwise autoupdate's settings * alone would trigger a cache miss every time. Generally, all files other * than JARs and .nbattrs (which can affect folder order) should not affect * lookup structure by their contents, except in the pathological case which * we do not consider that they supply zero instances or a recursive lookup * (which even then would only lead to problems if such a file were changed * on disk between IDE sessions, which can be expected to be very rare). */ private static final class Stamp { private final List files; // List private final long[] times; private final long hash; /** Create a stamp from a list of files. */ public Stamp(List files) throws IOException { this.files = new ArrayList(files); Collections.sort(this.files); times = new long[this.files.size()]; long x = 17L; Iterator it = this.files.iterator(); int i = 0; while (it.hasNext()) { File f = (File)it.next(); x ^= f.hashCode(); x += 98679245L; long m; String name = f.getName().toLowerCase(Locale.US); if (name.endsWith(".jar") || name.equals(".nbattrs")) { // NOI18N m = f.lastModified(); } else { m = 0L; } x ^= (times[i++] = m); } hash = x; } /** Hash of the stamp for comparison purposes. */ public long getHash() { return hash; } /** Debugging information listing which files were used. */ public String toString() { StringBuffer buf = new StringBuffer(); Iterator it = files.iterator(); int i = 0; while (it.hasNext()) { long t = times[i++]; if (t != 0L) { buf.append(new Date(t)); } else { buf.append(""); // NOI18N } buf.append('\t'); buf.append(it.next()); buf.append('\n'); } return buf.toString(); } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.