Index: core/src/org/netbeans/core/LookupCache.java =================================================================== RCS file: core/src/org/netbeans/core/LookupCache.java diff -N core/src/org/netbeans/core/LookupCache.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ core/src/org/netbeans/core/LookupCache.java 24 Dec 2002 02:57:06 -0000 @@ -0,0 +1,118 @@ +/* + * 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-2002 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.core; + +import java.io.*; + +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.io.NbObjectInputStream; +import org.openide.util.io.NbObjectOutputStream; + +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. + * @author Jesse Glick, Jaroslav Tulach + * @see "#20190" + */ +class LookupCache { + + private static final boolean ENABLED = Boolean.valueOf(System.getProperty("netbeans.cache.lookup", "true")).booleanValue(); // NOI18N + + public static Lookup load() { + if (ENABLED && cacheHit()) { + try { + return loadCache(); + } catch (Exception e) { + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); + } + } + return loadDirect(); + } + + private static Lookup loadDirect() { + FileObject services = Repository.getDefault().getDefaultFileSystem().findResource("Services"); // NOI18N + if (services != null) { + StartLog.logProgress("Got Services folder"); // NOI18N + FolderLookup f = new FolderLookup(DataFolder.findFolder(services), "SL["); // NOI18N + StartLog.logProgress("created FolderLookup"); // NOI18N + return f.getLookup(); + } else { + return Lookup.EMPTY; + } + } + + private static boolean cacheHit() { + // XXX timestamp check not yet implemented + File f = cacheFile(); + return f != null && f.exists(); + } + + private static File cacheFile() { + String ud = System.getProperty("netbeans.user"); + if (ud != null) { + File cachedir = new File(ud, "cache"); // NOI18N + cachedir.mkdirs(); + return new File(cachedir, "folder-lookup.ser"); // NOI18N + } else { + return null; + } + } + + private static Lookup loadCache() throws Exception { + InputStream is = new FileInputStream(cacheFile()); + try { + ObjectInputStream ois = new NbObjectInputStream(new BufferedInputStream(is)); + return (Lookup)ois.readObject(); + } finally { + is.close(); + } + } + + public static void store(Lookup l) throws IOException { + if (!ENABLED) { + return; + } + File f = cacheFile(); + OutputStream os = new FileOutputStream(f); + try { + try { + ObjectOutputStream oos = new NbObjectOutputStream(new BufferedOutputStream(os)); + oos.writeObject(l); + oos.flush(); + } finally { + os.close(); + } + } catch (IOException ioe) { + // Delete corrupted cache. + if (f.exists()) { + f.delete(); + } + throw ioe; + } + } + +} Index: core/src/org/netbeans/core/NbTopManager.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/NbTopManager.java,v retrieving revision 1.175 diff -u -r1.175 NbTopManager.java --- core/src/org/netbeans/core/NbTopManager.java 18 Dec 2002 23:41:28 -0000 1.175 +++ core/src/org/netbeans/core/NbTopManager.java 24 Dec 2002 02:57:06 -0000 @@ -538,6 +538,11 @@ } } org.netbeans.core.projects.XMLSettingsHandler.saveOptions(); + try { + ((Lkp)Lookup.getDefault()).storeCache(); + } catch (IOException ioe) { + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe); + } org.netbeans.core.projects.SessionManager.getDefault().close(); } catch (ThreadDeath td) { throw td; @@ -804,17 +809,6 @@ private final void doInitializeLookup () { //System.err.println("doInitializeLookup"); - FileObject services = Repository.getDefault().getDefaultFileSystem() - .findResource("Services"); - Lookup nue; - if (services != null) { - StartLog.logProgress("Got Services folder"); // NOI18N - FolderLookup f = new FolderLookup(DataFolder.findFolder(services), "SL["); // NOI18N - StartLog.logProgress("created FolderLookup"); // NOI18N - nue = f.getLookup(); - } else { - nue = Lookup.EMPTY; - } // extend the lookup Lookup[] arr = new Lookup[] { @@ -823,7 +817,7 @@ getLookups()[2], // ModuleInfo lookup // XXX figure out how to put this ahead of MetaInfServicesLookup (for NonGuiMain): NbTopManager.get ().getInstanceLookup (), - nue, + LookupCache.load(), }; StartLog.logProgress ("prepared other Lookups"); // NOI18N @@ -831,6 +825,14 @@ StartLog.logProgress ("Lookups set"); // NOI18N //StartLog.logEnd ("NbTopManager$Lkp: initialization of FolderLookup"); // NOI18N + } + + public void storeCache() throws IOException { + Lookup[] ls = getLookups(); + if (ls.length == 5) { + // modulesClassPathInitialized has been called, so store folder lookup + LookupCache.store(ls[4]); + } } protected void beforeLookup(Lookup.Template templ) { Index: openide/src/org/openide/loaders/FolderLookup.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/FolderLookup.java,v retrieving revision 1.19 diff -u -r1.19 FolderLookup.java --- openide/src/org/openide/loaders/FolderLookup.java 21 Dec 2002 08:40:25 -0000 1.19 +++ openide/src/org/openide/loaders/FolderLookup.java 24 Dec 2002 02:57:07 -0000 @@ -16,6 +16,9 @@ import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; @@ -23,6 +26,7 @@ import org.openide.cookies.InstanceCookie; import org.openide.ErrorManager; +import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.util.Lookup; import org.openide.util.lookup.AbstractLookup; @@ -247,14 +251,17 @@ /** ProxyLookup delegate so we can change the lookups on fly. */ - private static final class ProxyLkp extends ProxyLookup { + private static final class ProxyLkp extends ProxyLookup implements Serializable { + + private static final long serialVersionUID = 1L; /** FolderLookup we are associated with. */ - private FolderLookup fl; + private transient FolderLookup fl; /** Content to control the abstract lookup. */ - private AbstractLookup.Content content; + private transient AbstractLookup.Content content; + private transient boolean readFromStream; /** Constructs lookup which holds all items+lookups from underlying world. * @param folder FolderLookup to associate to */ @@ -270,12 +277,45 @@ this.content = content; } + private void writeObject (ObjectOutputStream oos) throws IOException { + Lookup[] ls = getLookups(); + for (int i = 0; i < ls.length; i++) { + oos.writeObject(ls[i]); + } + oos.writeObject(null); + oos.writeObject (fl.folder); + oos.writeObject (fl.rootName); + oos.writeObject (content); + } + + private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { + List ls = new ArrayList(); // List + Lookup l; + while ((l = (Lookup)ois.readObject()) != null) { + ls.add(l); + } + Lookup[] arr = (Lookup[])ls.toArray(new Lookup[ls.size()]); + DataFolder df = (DataFolder)ois.readObject (); + String root = (String)ois.readObject (); + + fl = new FolderLookup (df, root, true); + fl.lookup = this; + + content = (AbstractLookup.Content)ois.readObject (); + + setLookups (arr); + + readFromStream = true; + } + /** Updates internal data. * @param items Items to assign to all pairs * @param lookups delegates to delegate to (first item is null) */ public void update(Collection items, List lookups) { + readFromStream = false; + // remember the instance lookup Lookup pairs = getLookups ()[0]; @@ -292,6 +332,11 @@ /** Waits before the processing of changes is finished. */ protected void beforeLookup (Template template) { + if (readFromStream) { + // ok + return; + } + // do not wait in folder recognizer, but in all other cases if (!FolderList.isFolderRecognizerThread ()) { fl.instanceFinished (); @@ -303,12 +348,17 @@ /** Item that delegates to InstanceCookie. Item which * the internal lookup data structure is made from. */ - private static final class ICItem extends AbstractLookup.Pair { - private InstanceCookie ic; + private static final class ICItem extends AbstractLookup.Pair implements Serializable { + static final long serialVersionUID = 10L; + + /** when deserialized only primary file is stored */ + private FileObject fo; + + private transient InstanceCookie ic; /** source data object */ - private DataObject obj; + private transient DataObject obj; /** reference to created object */ - private WeakReference ref; + private transient WeakReference ref; /** root folder */ private String rootName; @@ -317,6 +367,27 @@ this.ic = ic; this.obj = obj; this.rootName = rootName; + this.fo = obj.getPrimaryFile(); + } + + /** Initializes the item + */ + public void init () { + if (ic != null) return; + + ic = (InstanceCookie)obj.getCookie (InstanceCookie.class); + if (ic == null) { + // XXX handle more gracefully + throw new IllegalStateException ("No cookie: " + obj); + } + } + + + /** Initializes the cookie from data object. + */ + private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + obj = DataObject.find (fo); } @@ -324,6 +395,8 @@ * @return the class of the item */ protected boolean instanceOf (Class clazz) { + init (); + if (ic instanceof InstanceCookie.Of) { // special handling for special cookies InstanceCookie.Of of = (InstanceCookie.Of)ic; @@ -345,6 +418,8 @@ * @return the instance of the object or null if it cannot be created */ public Object getInstance() { + init (); + try { Object obj = ic.instanceCreate(); ref = new WeakReference (obj); @@ -359,6 +434,8 @@ /** Hash code is the InstanceCookie's code. */ public int hashCode () { + init (); + return System.identityHashCode (ic); } @@ -366,6 +443,8 @@ public boolean equals (Object obj) { if (obj instanceof ICItem) { ICItem i = (ICItem)obj; + i.init (); + init (); return ic == i.ic; } return false; @@ -375,11 +454,15 @@ * @return string representing the item, that can be used for * persistance purposes to locate the same item next time */ public String getId() { + init (); + return objectName(rootName, obj); } /** Display name is extracted from name of the objects node. */ public String getDisplayName () { + init (); + return obj.getNodeDelegate ().getDisplayName (); } @@ -399,6 +482,8 @@ * @return the correct class */ public Class getType() { + init (); + try { return ic.instanceClass (); } catch (IOException ex) { Index: openide/src/org/openide/loaders/XMLDataObject.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/XMLDataObject.java,v retrieving revision 1.125 diff -u -r1.125 XMLDataObject.java --- openide/src/org/openide/loaders/XMLDataObject.java 25 Sep 2002 11:43:23 -0000 1.125 +++ openide/src/org/openide/loaders/XMLDataObject.java 24 Dec 2002 02:57:07 -0000 @@ -1022,6 +1022,8 @@ /** result used for this lookup */ private Lookup.Result result; + private ThreadLocal QUERY = new ThreadLocal (); + //~~~~~~~~~~~~~~~~~~~~~ Task body and control of queue ~~~~~~~~~~~~~~~~~~~ /** Getter for public ID of the document. @@ -1037,18 +1039,41 @@ * * @param class to look for */ - public Object lookupCookie (Class clazz) { - waitFinished (); - - Lookup l = lookup != null ? lookup : Lookup.EMPTY; + public Object lookupCookie (final Class clazz) { + if (QUERY.get () == clazz) { + // somebody is querying for the same cookie in the same thread + // probably neverending-loop - ignore + return new InstanceCookie () { + public Class instanceClass () { + return clazz; + } + + public Object instanceCreate () throws IOException { + throw new IOException ("Cyclic reference, sorry: " + clazz); + } + + public String instanceName () { + return clazz.getName (); + } + }; + } - Lookup.Result r = result; - if (r != null) { - // just to initialize all listeners - r.allItems (); + Object previous = QUERY.get (); + try { + QUERY.set (clazz); + waitFinished (); + + Lookup l = lookup != null ? lookup : Lookup.EMPTY; + + Lookup.Result r = result; + if (r != null) { + // just to initialize all listeners + r.allItems (); + } + return l.lookup (clazz); + } finally { + QUERY.set (previous); } - - return l.lookup (clazz); } /* Index: openide/src/org/openide/util/lookup/AbstractLookup.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/AbstractLookup.java,v retrieving revision 1.24 diff -u -r1.24 AbstractLookup.java --- openide/src/org/openide/util/lookup/AbstractLookup.java 21 Dec 2002 08:40:35 -0000 1.24 +++ openide/src/org/openide/util/lookup/AbstractLookup.java 24 Dec 2002 02:57:07 -0000 @@ -13,7 +13,7 @@ package org.openide.util.lookup; - +import java.io.Serializable; import java.lang.ref.*; import java.util.*; @@ -29,7 +29,9 @@ * @author Jaroslav Tulach * @since 1.9 */ -public class AbstractLookup extends Lookup { +public class AbstractLookup extends Lookup implements Serializable { + static final long serialVersionUID = 5L; + /** lock for initialization of the map */ private Content treeLock; /** the tree that registers all items */ @@ -40,7 +42,7 @@ /** set (Class, List (Reference (Result)) of all listeners that are waiting in * changes in class Class */ - private Map reg; + private transient Map reg; /** count of items in to lookup */ private int count; @@ -447,7 +449,9 @@ /** Extension to the default lookup item that offers additional information * for the data structures use in AbstractLookup */ - public static abstract class Pair extends Lookup.Item { + public static abstract class Pair extends Lookup.Item implements Serializable { + private static final long serialVersionUID = 1L; + /** possition of this item in the lookup, manipulated in addPair, removePair, setPairs methods */ int index = -1; @@ -641,12 +645,14 @@ * * @since 1.25 */ - public static class Content extends Object { + public static class Content extends Object implements Serializable { + + private static final long serialVersionUID = 1L; // one of them is always null (except attach stage) /** abstract lookup we are connected to */ private AbstractLookup al = null; - private ArrayList earlyPairs = new ArrayList(3); + private transient ArrayList earlyPairs = new ArrayList(3); /** A lookup attaches to this object. */ Index: openide/src/org/openide/util/lookup/InheritanceTree.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/InheritanceTree.java,v retrieving revision 1.15 diff -u -r1.15 InheritanceTree.java --- openide/src/org/openide/util/lookup/InheritanceTree.java 3 Dec 2002 14:12:15 -0000 1.15 +++ openide/src/org/openide/util/lookup/InheritanceTree.java 24 Dec 2002 02:57:07 -0000 @@ -14,22 +14,12 @@ package org.openide.util.lookup; - +import java.io.*; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.List; - -import org.openide.util.enum.AlterEnumeration; -import org.openide.util.enum.EmptyEnumeration; -import org.openide.util.enum.QueueEnumeration; -import org.openide.util.enum.SequenceEnumeration; -import org.openide.util.enum.SingletonEnumeration; +import java.util.*; + +import org.openide.util.Lookup; +import org.openide.util.enum.*; /** A tree to represent classes with inheritance. Description of the @@ -96,11 +86,15 @@ * * @author Jaroslav Tulach */ -final class InheritanceTree extends Object implements java.util.Comparator { +final class InheritanceTree extends Object implements Comparator, Serializable { + private static final long serialVersionUID = 1L; + /** the root item (represents Object) */ - private Node object; - /** map of queried interfaces (Class, Set) */ - private Map interfaces; + private transient Node object; + /** Map of queried interfaces. + *

Type: Map<Class, (Collection<AbstractLookup.Pair> | AbstractLookup.Pair)> + */ + private transient Map interfaces; /** Constructor */ @@ -108,6 +102,33 @@ object = new Node (java.lang.Object.class); } + private void writeObject (ObjectOutputStream oos) throws IOException { + oos.writeObject(object); + Iterator it = interfaces.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + Class c = (Class)e.getKey(); + oos.writeObject(c.getName()); + Object o = e.getValue(); + if (!(o instanceof Collection) && !(o instanceof AbstractLookup.Pair)) throw new ClassCastException(String.valueOf(o)); + oos.writeObject(o); + } + oos.writeObject(null); + } + + private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { + object = (Node)ois.readObject(); + interfaces = new WeakHashMap(); + String clazz; + ClassLoader l = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class); + while ((clazz = (String)ois.readObject()) != null) { + Object o = ois.readObject(); + if (!(o instanceof Collection) && !(o instanceof AbstractLookup.Pair)) throw new ClassCastException(String.valueOf(o)); + Class c = Class.forName(clazz, false, l); + interfaces.put(c, o); + } + } + /** Adds an item into the tree. * @param item to add @@ -745,12 +766,13 @@ /** Node in the tree. */ - static final class Node extends WeakReference { + static final class Node extends WeakReference implements Serializable { + static final long serialVersionUID = 3L; /** children nodes */ - public ArrayList children; + public ArrayList children; // List /** list of items assigned to this node (suspect to be subclasses) */ - public ArrayList items; + public ArrayList items; // List /** Constructor. */ @@ -807,5 +829,44 @@ items.add (item); return true; } + + private Object writeReplace () { + return new R (this); + } + } // End of class Node. + + private static final class R implements Serializable { + static final long serialVersionUID = 1L; + + private static ClassLoader l; + + private String clazzName; + private transient Class clazz; + private ArrayList children; + private ArrayList items; + + public R (Node n) { + this.clazzName = n.getType ().getName(); + this.children = n.children; + this.items = n.items; + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (l == null) { + l = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class); + } + clazz = Class.forName(clazzName, false, l); + } + + private Object readResolve () throws ObjectStreamException { + Node n = new Node (clazz); + n.children = children; + n.items = items; + + return n; + } + + } // end of R } Index: openide/src/org/openide/util/lookup/ProxyLookup.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/ProxyLookup.java,v retrieving revision 1.11 diff -u -r1.11 ProxyLookup.java --- openide/src/org/openide/util/lookup/ProxyLookup.java 3 Dec 2002 14:12:15 -0000 1.11 +++ openide/src/org/openide/util/lookup/ProxyLookup.java 24 Dec 2002 02:57:07 -0000 @@ -40,6 +40,15 @@ this.lookups = lookups; } + /** + * Create a lookup initially proxying to no others. + * Permits serializable subclasses. + * @since XXX + */ + protected ProxyLookup() { + this(new Lookup[0]); + } + public String toString() { return "ProxyLookup" + Arrays.asList(lookups); // NOI18N }