This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 20190
Collapse All | Expand All

(-)core/src/org/netbeans/core/LookupCache.java (+407 lines)
Added Link Here
1
/*
2
 *                 Sun Public License Notice
3
 *
4
 * The contents of this file are subject to the Sun Public License
5
 * Version 1.0 (the "License"). You may not use this file except in
6
 * compliance with the License. A copy of the License is available at
7
 * http://www.sun.com/
8
 *
9
 * The Original Code is NetBeans. The Initial Developer of the Original
10
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun
11
 * Microsystems, Inc. All Rights Reserved.
12
 */
13
14
package org.netbeans.core;
15
16
import java.io.*;
17
import java.net.MalformedURLException;
18
import java.net.URL;
19
import java.util.*;
20
21
import org.openide.ErrorManager;
22
import org.openide.filesystems.FileObject;
23
import org.openide.filesystems.Repository;
24
import org.openide.loaders.DataFolder;
25
import org.openide.loaders.FolderLookup;
26
import org.openide.util.Lookup;
27
import org.openide.util.Utilities;
28
import org.openide.util.io.NbObjectInputStream;
29
import org.openide.util.io.NbObjectOutputStream;
30
31
import org.netbeans.core.modules.Module;
32
import org.netbeans.core.modules.ModuleManager;
33
import org.netbeans.core.perftool.StartLog;
34
35
/**
36
 * Responsible for persisting the structure of folder lookup.
37
 * <p>A cache is kept in serialized form in $userdir/cache/folder-lookup.ser.
38
 * Unless the cache is invalidated due to changes in module JARs or
39
 * files in $userdir/system/**, it is restored after a regular startup.
40
 * The actual objects in lookup are not serialized - only their classes,
41
 * instanceof information, position in the Services folder, and so on. This
42
 * permits us to avoid calling the XML parser for every .settings object etc.
43
 * Other forms of lookup like META-INF/services/* are not persisted.
44
 * <p>Can be enabled or disabled with the system property netbeans.cache.lookup.
45
 * @author Jesse Glick, Jaroslav Tulach
46
 * @see "#20190"
47
 */
48
class LookupCache {
49
50
    /** whether to enable the cache for this session */
51
    private static final boolean ENABLED = Boolean.valueOf(System.getProperty("netbeans.cache.lookup", "true")).booleanValue(); // NOI18N
52
    
53
    /** private logging for this class */
54
    private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.netbeans.core.LookupCache"); // NOI18N
55
    
56
    /**
57
     * Get the Services/ folder lookup.
58
     * May either do it the slow way, or might load quickly from a cache.
59
     * @return the folder lookup for the system
60
     */
61
    public static Lookup load() {
62
        err.log("enabled=" + ENABLED);
63
        if (ENABLED && cacheHit()) {
64
            try {
65
                return loadCache();
66
            } catch (Exception e) {
67
                err.notify(ErrorManager.INFORMATIONAL, e);
68
            }
69
        }
70
        return loadDirect();
71
    }
72
    
73
    /**
74
     * Load folder lookup directly from the system file system, parsing
75
     * as necessary (the slow way).
76
     */
77
    private static Lookup loadDirect() {
78
        FileObject services = Repository.getDefault().getDefaultFileSystem().findResource("Services"); // NOI18N
79
        if (services != null) {
80
            StartLog.logProgress("Got Services folder"); // NOI18N
81
            FolderLookup f = new FolderLookup(DataFolder.findFolder(services), "SL["); // NOI18N
82
            StartLog.logProgress("created FolderLookup"); // NOI18N
83
            err.log("loadDirect from Services");
84
            return f.getLookup();
85
        } else {
86
            err.log("loadDirect, but no Services");
87
            return Lookup.EMPTY;
88
        }
89
    }
90
    
91
    /**
92
     * Determine if there is an existing lookup cache which can be used
93
     * now as is.
94
     * If there is a cache and a stamp file, and the stamp agrees with
95
     * a calculation of the files and timestamps currently available to
96
     * constitute the folder lookup, then the cache is used.
97
     */
98
    private static boolean cacheHit() {
99
        File f = cacheFile();
100
        if (f == null || !f.exists()) {
101
            err.log("no cache file");
102
            return false;
103
        }
104
        File stampFile = stampFile();
105
        if (stampFile == null || !stampFile.exists()) {
106
            err.log("no stamp file");
107
            return false;
108
        }
109
        StartLog.logStart("check for lookup cache hit"); // NOI18N
110
        List files = relevantFiles(); // List<File>
111
        if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
112
            err.log("checking against " + stampFile + " for files " + files);
113
        }
114
        boolean hit;
115
        try {
116
            Stamp stamp = new Stamp(files);
117
            long newHash = stamp.getHash();
118
            BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(stampFile), "UTF-8")); // NOI18N
119
            try {
120
                String line = r.readLine();
121
                long oldHash;
122
                try {
123
                    oldHash = Long.parseLong(line);
124
                } catch (NumberFormatException nfe) {
125
                    throw new IOException(nfe.toString());
126
                }
127
                if (oldHash == newHash) {
128
                    err.log("Cache hit! with hash " + oldHash);
129
                    hit = true;
130
                } else {
131
                    err.log("Cache miss, " + oldHash + " -> " + newHash);
132
                    hit = false;
133
                }
134
            } finally {
135
                r.close();
136
            }
137
        } catch (IOException ioe) {
138
            err.notify(ErrorManager.INFORMATIONAL, ioe);
139
            hit = false;
140
        }
141
        StartLog.logEnd("check for lookup cache hit"); // NOI18N
142
        return hit;
143
    }
144
    
145
    /**
146
     * The file containing the serialized lookup cache.
147
     */
148
    private static File cacheFile() {
149
        String ud = System.getProperty("netbeans.user");
150
        if (ud != null) {
151
            File cachedir = new File(ud, "cache"); // NOI18N
152
            cachedir.mkdirs();
153
            return new File(cachedir, "folder-lookup.ser"); // NOI18N
154
        } else {
155
            return null;
156
        }
157
    }
158
    
159
    /**
160
     * The file containing a stamp which indicates which modules were
161
     * enabled, what versions of them, customized services, etc.
162
     */
163
    private static File stampFile() {
164
        String ud = System.getProperty("netbeans.user");
165
        if (ud != null) {
166
            File cachedir = new File(ud, "cache"); // NOI18N
167
            cachedir.mkdirs();
168
            return new File(cachedir, "lookup-stamp.txt"); // NOI18N
169
        } else {
170
            return null;
171
        }
172
    }
173
    
174
    /**
175
     * List of all files which might be relevant to the contents of folder lookup.
176
     * This means: all JAR files which are modules (skip their extensions and
177
     * variants which can be assumed not to contain layer files); and all files
178
     * contained in the system/Services/ subdirs (if any) of the home dir,
179
     * user dir, and extra installation directories (#27151).
180
     * For test modules, use the original JAR, not the physical JAR,
181
     * to prevent cache misses on every restart.
182
     * For fixed modules with layers (e.g. core.jar), add in the matching JAR,
183
     * if that can be ascertained.
184
     * No particular order of returned files is assumed.
185
     */
186
    private static List relevantFiles() {
187
        final List files = new ArrayList(250); // List<File>
188
        final ModuleManager mgr = NbTopManager.get().getModuleSystem().getManager();
189
        mgr.mutex().readAccess(new Runnable() {
190
            public void run() {
191
                Iterator it = mgr.getEnabledModules().iterator();
192
                while (it.hasNext()) {
193
                    Module m = (Module)it.next();
194
                    String layer = (String)m.getAttribute("OpenIDE-Module-Layer"); // NOI18N
195
                    if (layer != null) {
196
                        if (!m.isFixed()) {
197
                            files.add(m.getJarFile());
198
                        } else {
199
                            URL layerURL = m.getClassLoader().getResource(layer);
200
                            if (layerURL != null) {
201
                                String s = layerURL.toExternalForm();
202
                                if (s.startsWith("jar:")) { // NOI18N
203
                                    int bangSlash = s.lastIndexOf("!/"); // NOI18N
204
                                    if (bangSlash != -1) {
205
                                        // underlying URL inside jar:, generally file:
206
                                        try {
207
                                            URL layerJarURL = new URL(s.substring(4, bangSlash));
208
                                            File layerJar = Utilities.toFile(layerJarURL);
209
                                            if (layerJar != null) {
210
                                                files.add(layerJar);
211
                                            } else {
212
                                                err.log(ErrorManager.WARNING, "Weird jar: URL: " + layerJarURL);
213
                                            }
214
                                        } catch (MalformedURLException mfue) {
215
                                            err.notify(ErrorManager.INFORMATIONAL, mfue);
216
                                        }
217
                                    } else {
218
                                        err.log(ErrorManager.WARNING, "Malformed jar: URL: " + s);
219
                                    }
220
                                } else {
221
                                    err.log(ErrorManager.WARNING, "Not a jar: URL: " + s);
222
                                }
223
                            } else {
224
                                err.log(ErrorManager.WARNING, "Could not find " + layer + " in " + m);
225
                            }
226
                        }
227
                    }
228
                    // else no layer, ignore
229
                }
230
            }
231
        });
232
        relevantFilesFromInst(files, System.getProperty("netbeans.home")); // NOI18N
233
        relevantFilesFromInst(files, System.getProperty("netbeans.user")); // NOI18N
234
        String nbdirs = System.getProperty("netbeans.dirs"); // NOI18N
235
        if (nbdirs != null) {
236
            // #27151
237
            StringTokenizer tok = new StringTokenizer(nbdirs, File.pathSeparator);
238
            while (tok.hasMoreTokens()) {
239
                relevantFilesFromInst(files, tok.nextToken());
240
            }
241
        }
242
        return files;
243
    }
244
    /**
245
     * Find relevant files from an installation directory.
246
     */
247
    private static void relevantFilesFromInst(List files, String instDir) {
248
        if (instDir == null) {
249
            return;
250
        }
251
        relevantFilesFrom(files, new File(new File(new File(instDir), "system"), "Services")); // NOI18N
252
    }
253
    /**
254
     * Retrieve all files in a directory, recursively.
255
     */
256
    private static void relevantFilesFrom(List files, File dir) {
257
        File[] kids = dir.listFiles();
258
        if (kids != null) {
259
            for (int i = 0; i < kids.length; i++) {
260
                File f = kids[i];
261
                if (f.isFile()) {
262
                    files.add(f);
263
                } else {
264
                    relevantFilesFrom(files, f);
265
                }
266
            }
267
        }
268
    }
269
    
270
    /**
271
     * Load folder lookup from the disk cache.
272
     */
273
    private static Lookup loadCache() throws Exception {
274
        StartLog.logStart("load lookup cache");
275
        File f = cacheFile();
276
        err.log("loading from " + f);
277
        InputStream is = new FileInputStream(f);
278
        try {
279
            ObjectInputStream ois = new NbObjectInputStream(new BufferedInputStream(is));
280
            Lookup l = (Lookup)ois.readObject();
281
            StartLog.logEnd("load lookup cache");
282
            return l;
283
        } finally {
284
            is.close();
285
        }
286
    }
287
    
288
    /**
289
     * Store the current contents of folder lookup to disk, hopefully to be used
290
     * in the next session to speed startup.
291
     * @param l the folder lookup
292
     * @throws IOException if it could not be saved
293
     */
294
    public static void store(Lookup l) throws IOException {
295
        if (!ENABLED) {
296
            return;
297
        }
298
        File f = cacheFile();
299
        if (f == null) {
300
            return;
301
        }
302
        File stampFile = stampFile();
303
        if (stampFile == null) {
304
            return;
305
        }
306
        StartLog.logStart("store lookup cache");
307
        err.log("storing to " + f + " with stamp in " + stampFile);
308
        OutputStream os = new FileOutputStream(f);
309
        try {
310
            try {
311
                ObjectOutputStream oos = new NbObjectOutputStream(new BufferedOutputStream(os));
312
                oos.writeObject(l);
313
                oos.flush();
314
            } finally {
315
                os.close();
316
            }
317
            Stamp stamp = new Stamp(relevantFiles());
318
            Writer wr = new OutputStreamWriter(new FileOutputStream(stampFile), "UTF-8"); // NOI18N
319
            try {
320
                // Would be nice to write out as zero-padded hex.
321
                // Unfortunately while Long.toHexString works fine,
322
                // Long.parseLong cannot be asked to parse unsigned longs,
323
                // so fails when the high bit is set.
324
                wr.write(Long.toString(stamp.getHash()));
325
                wr.write("\nLine above is identifying hash key, do not edit!\nBelow is metadata about folder lookup cache, for debugging purposes.\n"); // NOI18N
326
                wr.write(stamp.toString());
327
            } finally {
328
                wr.close();
329
            }
330
            StartLog.logEnd("store lookup cache");
331
        } catch (IOException ioe) {
332
            // Delete corrupted cache.
333
            if (f.exists()) {
334
                f.delete();
335
            }
336
            if (stampFile.exists()) {
337
                stampFile.delete();
338
            }
339
            throw ioe;
340
        }
341
    }
342
    
343
    /**
344
     * Represents a hash of a bunch of JAR or other files and their timestamps.
345
     * Compare ModuleLayeredFileSystem's similar nested class.
346
     * .settings files do not get their timestamps checked because generally
347
     * changes to them do not reflect changes in the structure of lookup, only
348
     * in the contents of one lookup instance. Otherwise autoupdate's settings
349
     * alone would trigger a cache miss every time. Generally, all files other
350
     * than JARs and .nbattrs (which can affect folder order) should not affect
351
     * lookup structure by their contents, except in the pathological case which
352
     * we do not consider that they supply zero instances or a recursive lookup
353
     * (which even then would only lead to problems if such a file were changed
354
     * on disk between IDE sessions, which can be expected to be very rare).
355
     */
356
    private static final class Stamp {
357
        private final List files; // List<File>
358
        private final long[] times;
359
        private final long hash;
360
        /** Create a stamp from a list of files. */
361
        public Stamp(List files) throws IOException {
362
            this.files = new ArrayList(files);
363
            Collections.sort(this.files);
364
            times = new long[this.files.size()];
365
            long x = 17L;
366
            Iterator it = this.files.iterator();
367
            int i = 0;
368
            while (it.hasNext()) {
369
                File f = (File)it.next();
370
                x ^= f.hashCode();
371
                x += 98679245L;
372
                long m;
373
                String name = f.getName().toLowerCase(Locale.US);
374
                if (name.endsWith(".jar") || name.equals(".nbattrs")) { // NOI18N
375
                    m = f.lastModified();
376
                } else {
377
                    m = 0L;
378
                }
379
                x ^= (times[i++] = m);
380
            }
381
            hash = x;
382
        }
383
        /** Hash of the stamp for comparison purposes. */
384
        public long getHash() {
385
            return hash;
386
        }
387
        /** Debugging information listing which files were used. */
388
        public String toString() {
389
            StringBuffer buf = new StringBuffer();
390
            Iterator it = files.iterator();
391
            int i = 0;
392
            while (it.hasNext()) {
393
                long t = times[i++];
394
                if (t != 0L) {
395
                    buf.append(new Date(t));
396
                } else {
397
                    buf.append("<ignoring file contents>"); // NOI18N
398
                }
399
                buf.append('\t');
400
                buf.append(it.next());
401
                buf.append('\n');
402
            }
403
            return buf.toString();
404
        }
405
    }
406
    
407
}
(-)core/src/org/netbeans/core/NbTopManager.java (-12 / +14 lines)
Lines 538-543 Link Here
538
                            }
538
                            }
539
                        }
539
                        }
540
                        org.netbeans.core.projects.XMLSettingsHandler.saveOptions();
540
                        org.netbeans.core.projects.XMLSettingsHandler.saveOptions();
541
                        try {
542
                            ((Lkp)Lookup.getDefault()).storeCache();
543
                        } catch (IOException ioe) {
544
                            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
545
                        }
541
                        org.netbeans.core.projects.SessionManager.getDefault().close();
546
                        org.netbeans.core.projects.SessionManager.getDefault().close();
542
                    } catch (ThreadDeath td) {
547
                    } catch (ThreadDeath td) {
543
                        throw td;
548
                        throw td;
Lines 804-820 Link Here
804
        
809
        
805
        private final void doInitializeLookup () {
810
        private final void doInitializeLookup () {
806
            //System.err.println("doInitializeLookup");
811
            //System.err.println("doInitializeLookup");
807
            FileObject services = Repository.getDefault().getDefaultFileSystem()
808
                    .findResource("Services");
809
            Lookup nue;
810
            if (services != null) {
811
                StartLog.logProgress("Got Services folder"); // NOI18N
812
                FolderLookup f = new FolderLookup(DataFolder.findFolder(services), "SL["); // NOI18N
813
                StartLog.logProgress("created FolderLookup"); // NOI18N
814
                nue = f.getLookup();
815
            } else {
816
                nue = Lookup.EMPTY;
817
            }
818
812
819
            // extend the lookup
813
            // extend the lookup
820
            Lookup[] arr = new Lookup[] {
814
            Lookup[] arr = new Lookup[] {
Lines 823-829 Link Here
823
                getLookups()[2], // ModuleInfo lookup
817
                getLookups()[2], // ModuleInfo lookup
824
                // XXX figure out how to put this ahead of MetaInfServicesLookup (for NonGuiMain):
818
                // XXX figure out how to put this ahead of MetaInfServicesLookup (for NonGuiMain):
825
                NbTopManager.get ().getInstanceLookup (),
819
                NbTopManager.get ().getInstanceLookup (),
826
                nue,
820
                LookupCache.load(),
827
            };
821
            };
828
            StartLog.logProgress ("prepared other Lookups"); // NOI18N
822
            StartLog.logProgress ("prepared other Lookups"); // NOI18N
829
823
Lines 831-836 Link Here
831
            StartLog.logProgress ("Lookups set"); // NOI18N
825
            StartLog.logProgress ("Lookups set"); // NOI18N
832
826
833
	    //StartLog.logEnd ("NbTopManager$Lkp: initialization of FolderLookup"); // NOI18N
827
	    //StartLog.logEnd ("NbTopManager$Lkp: initialization of FolderLookup"); // NOI18N
828
        }
829
        
830
        public void storeCache() throws IOException {
831
            Lookup[] ls = getLookups();
832
            if (ls.length == 5) {
833
                // modulesClassPathInitialized has been called, so store folder lookup
834
                LookupCache.store(ls[4]);
835
            }
834
        }
836
        }
835
        
837
        
836
        protected void beforeLookup(Lookup.Template templ) {
838
        protected void beforeLookup(Lookup.Template templ) {
(-)openide/src/org/openide/loaders/FolderLookup.java (-7 / +92 lines)
Lines 16-21 Link Here
16
16
17
17
18
import java.io.IOException;
18
import java.io.IOException;
19
import java.io.ObjectInputStream;
20
import java.io.ObjectOutputStream;
21
import java.io.Serializable;
19
import java.lang.ref.WeakReference;
22
import java.lang.ref.WeakReference;
20
import java.util.ArrayList;
23
import java.util.ArrayList;
21
import java.util.Collection;
24
import java.util.Collection;
Lines 23-28 Link Here
23
26
24
import org.openide.cookies.InstanceCookie;
27
import org.openide.cookies.InstanceCookie;
25
import org.openide.ErrorManager;
28
import org.openide.ErrorManager;
29
import org.openide.filesystems.FileObject;
26
import org.openide.loaders.DataObject;
30
import org.openide.loaders.DataObject;
27
import org.openide.util.Lookup;
31
import org.openide.util.Lookup;
28
import org.openide.util.lookup.AbstractLookup;
32
import org.openide.util.lookup.AbstractLookup;
Lines 247-260 Link Here
247
251
248
    
252
    
249
    /** <code>ProxyLookup</code> delegate so we can change the lookups on fly. */
253
    /** <code>ProxyLookup</code> delegate so we can change the lookups on fly. */
250
    private static final class ProxyLkp extends ProxyLookup {
254
    private static final class ProxyLkp extends ProxyLookup implements Serializable {
255
        
256
        private static final long serialVersionUID = 1L;
251
257
252
        /** <code>FolderLookup</code> we are associated with. */
258
        /** <code>FolderLookup</code> we are associated with. */
253
        private FolderLookup fl;
259
        private transient FolderLookup fl;
254
        
260
        
255
        /** Content to control the abstract lookup. */
261
        /** Content to control the abstract lookup. */
256
        private AbstractLookup.Content content;
262
        private transient AbstractLookup.Content content;
257
        
263
        
264
        private transient boolean readFromStream;
258
265
259
        /** Constructs lookup which holds all items+lookups from underlying world.
266
        /** Constructs lookup which holds all items+lookups from underlying world.
260
         * @param folder <code>FolderLookup</code> to associate to */
267
         * @param folder <code>FolderLookup</code> to associate to */
Lines 270-281 Link Here
270
            this.content = content;
277
            this.content = content;
271
        }
278
        }
272
        
279
        
280
        private void writeObject (ObjectOutputStream oos) throws IOException {
281
            Lookup[] ls = getLookups();
282
            for (int i = 0; i < ls.length; i++) {
283
                oos.writeObject(ls[i]);
284
            }
285
            oos.writeObject(null);
286
            oos.writeObject (fl.folder);
287
            oos.writeObject (fl.rootName);
288
            oos.writeObject (content);
289
        }
290
        
291
        private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
292
            List ls = new ArrayList(); // List<Lookup>
293
            Lookup l;
294
            while ((l = (Lookup)ois.readObject()) != null) {
295
                ls.add(l);
296
            }
297
            Lookup[] arr = (Lookup[])ls.toArray(new Lookup[ls.size()]);
298
            DataFolder df = (DataFolder)ois.readObject ();
299
            String root = (String)ois.readObject ();
300
            
301
            fl = new FolderLookup (df, root, true);
302
            fl.lookup = this;
303
            
304
            content = (AbstractLookup.Content)ois.readObject ();
305
            
306
            setLookups (arr);
307
            
308
            readFromStream = true;
309
        }
310
        
273
        
311
        
274
        /** Updates internal data. 
312
        /** Updates internal data. 
275
         * @param items Items to assign to all pairs
313
         * @param items Items to assign to all pairs
276
         * @param lookups delegates to delegate to (first item is null)
314
         * @param lookups delegates to delegate to (first item is null)
277
         */
315
         */
278
        public void update(Collection items, List lookups) {
316
        public void update(Collection items, List lookups) {
317
            readFromStream = false;
318
            
279
            // remember the instance lookup 
319
            // remember the instance lookup 
280
            Lookup pairs = getLookups ()[0];
320
            Lookup pairs = getLookups ()[0];
281
321
Lines 292-297 Link Here
292
        
332
        
293
        /** Waits before the processing of changes is finished. */
333
        /** Waits before the processing of changes is finished. */
294
        protected void beforeLookup (Template template) {
334
        protected void beforeLookup (Template template) {
335
            if (readFromStream) {
336
                // ok
337
                return;
338
            }
339
            
295
            // do not wait in folder recognizer, but in all other cases
340
            // do not wait in folder recognizer, but in all other cases
296
            if (!FolderList.isFolderRecognizerThread ()) {
341
            if (!FolderList.isFolderRecognizerThread ()) {
297
                fl.instanceFinished ();
342
                fl.instanceFinished ();
Lines 303-314 Link Here
303
    
348
    
304
    /** Item that delegates to <code>InstanceCookie</code>. Item which 
349
    /** Item that delegates to <code>InstanceCookie</code>. Item which 
305
     * the internal lookup data structure is made from. */
350
     * the internal lookup data structure is made from. */
306
    private static final class ICItem extends AbstractLookup.Pair {
351
    private static final class ICItem extends AbstractLookup.Pair implements Serializable {
307
        private InstanceCookie ic;
352
        static final long serialVersionUID = 10L;
353
354
        /** when deserialized only primary file is stored */
355
        private FileObject fo;
356
        
357
        private transient InstanceCookie ic;
308
        /** source data object */
358
        /** source data object */
309
        private DataObject obj;
359
        private transient DataObject obj;
310
        /** reference to created object */
360
        /** reference to created object */
311
        private WeakReference ref;
361
        private transient WeakReference ref;
312
        /** root folder */
362
        /** root folder */
313
        private String rootName;
363
        private String rootName;
314
364
Lines 317-322 Link Here
317
            this.ic = ic;
367
            this.ic = ic;
318
            this.obj = obj;
368
            this.obj = obj;
319
            this.rootName = rootName;
369
            this.rootName = rootName;
370
            this.fo = obj.getPrimaryFile();
371
        }
372
        
373
        /** Initializes the item
374
         */
375
        public void init () {
376
            if (ic != null) return;
377
            
378
            ic = (InstanceCookie)obj.getCookie (InstanceCookie.class);
379
            if (ic == null) {
380
                // XXX handle more gracefully
381
                throw new IllegalStateException ("No cookie: " + obj);
382
            }
383
        }
384
            
385
        
386
        /** Initializes the cookie from data object.
387
         */
388
        private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
389
            ois.defaultReadObject();
390
            obj = DataObject.find (fo);
320
        }
391
        }
321
392
322
393
Lines 324-329 Link Here
324
         * @return the class of the item
395
         * @return the class of the item
325
         */
396
         */
326
        protected boolean instanceOf (Class clazz) {
397
        protected boolean instanceOf (Class clazz) {
398
            init ();
399
            
327
            if (ic instanceof InstanceCookie.Of) {
400
            if (ic instanceof InstanceCookie.Of) {
328
                // special handling for special cookies
401
                // special handling for special cookies
329
                InstanceCookie.Of of = (InstanceCookie.Of)ic;
402
                InstanceCookie.Of of = (InstanceCookie.Of)ic;
Lines 345-350 Link Here
345
         * @return the instance of the object or null if it cannot be created
418
         * @return the instance of the object or null if it cannot be created
346
         */
419
         */
347
        public Object getInstance() {
420
        public Object getInstance() {
421
            init ();
422
            
348
            try {
423
            try {
349
                Object obj = ic.instanceCreate();
424
                Object obj = ic.instanceCreate();
350
                ref = new WeakReference (obj);
425
                ref = new WeakReference (obj);
Lines 359-364 Link Here
359
434
360
        /** Hash code is the <code>InstanceCookie</code>'s code. */
435
        /** Hash code is the <code>InstanceCookie</code>'s code. */
361
        public int hashCode () {
436
        public int hashCode () {
437
            init ();
438
            
362
            return System.identityHashCode (ic);
439
            return System.identityHashCode (ic);
363
        }
440
        }
364
441
Lines 366-371 Link Here
366
        public boolean equals (Object obj) {
443
        public boolean equals (Object obj) {
367
            if (obj instanceof ICItem) {
444
            if (obj instanceof ICItem) {
368
                ICItem i = (ICItem)obj;
445
                ICItem i = (ICItem)obj;
446
                i.init ();
447
                init ();
369
                return ic == i.ic;
448
                return ic == i.ic;
370
            }
449
            }
371
            return false;
450
            return false;
Lines 375-385 Link Here
375
         * @return string representing the item, that can be used for
454
         * @return string representing the item, that can be used for
376
         *   persistance purposes to locate the same item next time */
455
         *   persistance purposes to locate the same item next time */
377
        public String getId() {
456
        public String getId() {
457
            init ();
458
            
378
            return objectName(rootName, obj);
459
            return objectName(rootName, obj);
379
        }
460
        }
380
461
381
        /** Display name is extracted from name of the objects node. */
462
        /** Display name is extracted from name of the objects node. */
382
        public String getDisplayName () {
463
        public String getDisplayName () {
464
            init ();
465
            
383
            return obj.getNodeDelegate ().getDisplayName ();
466
            return obj.getNodeDelegate ().getDisplayName ();
384
        }
467
        }
385
468
Lines 399-404 Link Here
399
         * @return the correct class
482
         * @return the correct class
400
         */
483
         */
401
        public Class getType() {
484
        public Class getType() {
485
            init ();
486
            
402
            try {
487
            try {
403
                return ic.instanceClass ();
488
                return ic.instanceClass ();
404
            } catch (IOException ex) {
489
            } catch (IOException ex) {
(-)openide/src/org/openide/loaders/XMLDataObject.java (-10 / +35 lines)
Lines 1022-1027 Link Here
1022
        /** result used for this lookup */
1022
        /** result used for this lookup */
1023
        private Lookup.Result result;
1023
        private Lookup.Result result;
1024
        
1024
        
1025
        private ThreadLocal QUERY = new ThreadLocal ();
1026
        
1025
        //~~~~~~~~~~~~~~~~~~~~~ Task body and control of queue ~~~~~~~~~~~~~~~~~~~
1027
        //~~~~~~~~~~~~~~~~~~~~~ Task body and control of queue ~~~~~~~~~~~~~~~~~~~
1026
        
1028
        
1027
        /** Getter for public ID of the document.
1029
        /** Getter for public ID of the document.
Lines 1037-1054 Link Here
1037
         *
1039
         *
1038
         * @param class to look for
1040
         * @param class to look for
1039
         */
1041
         */
1040
        public Object lookupCookie (Class clazz) {
1042
        public Object lookupCookie (final Class clazz) {
1041
            waitFinished ();
1043
            if (QUERY.get () == clazz) {
1042
            
1044
                // somebody is querying for the same cookie in the same thread
1043
            Lookup l = lookup != null ? lookup : Lookup.EMPTY;
1045
                // probably neverending-loop - ignore
1046
                return new InstanceCookie () {
1047
                    public Class instanceClass () {
1048
                        return clazz;
1049
                    }
1050
                    
1051
                    public Object instanceCreate () throws IOException {
1052
                        throw new IOException ("Cyclic reference, sorry: " + clazz);
1053
                    }
1054
                    
1055
                    public String instanceName () {
1056
                        return clazz.getName ();
1057
                    }
1058
                };
1059
            }
1044
            
1060
            
1045
            Lookup.Result r = result;
1061
            Object previous = QUERY.get ();
1046
            if (r != null) {
1062
            try {
1047
                // just to initialize all listeners
1063
                QUERY.set (clazz);
1048
                r.allItems ();
1064
                waitFinished ();
1065
1066
                Lookup l = lookup != null ? lookup : Lookup.EMPTY;
1067
1068
                Lookup.Result r = result;
1069
                if (r != null) {
1070
                    // just to initialize all listeners
1071
                    r.allItems ();
1072
                }
1073
                return l.lookup (clazz);
1074
            } finally {
1075
                QUERY.set (previous);
1049
            }
1076
            }
1050
         
1051
            return l.lookup (clazz);
1052
        }
1077
        }
1053
           
1078
           
1054
        /*
1079
        /*
(-)openide/src/org/openide/util/lookup/AbstractLookup.java (-6 / +12 lines)
Lines 13-19 Link Here
13
13
14
package org.openide.util.lookup;
14
package org.openide.util.lookup;
15
15
16
16
import java.io.Serializable;
17
import java.lang.ref.*;
17
import java.lang.ref.*;
18
import java.util.*;
18
import java.util.*;
19
19
Lines 29-35 Link Here
29
 * @author  Jaroslav Tulach
29
 * @author  Jaroslav Tulach
30
 * @since 1.9
30
 * @since 1.9
31
 */
31
 */
32
public class AbstractLookup extends Lookup {
32
public class AbstractLookup extends Lookup implements Serializable {
33
    static final long serialVersionUID = 5L;
34
    
33
    /** lock for initialization of the map */
35
    /** lock for initialization of the map */
34
    private Content treeLock;
36
    private Content treeLock;
35
    /** the tree that registers all items */
37
    /** the tree that registers all items */
Lines 40-46 Link Here
40
    /** set (Class, List (Reference (Result)) of all listeners that are waiting in 
42
    /** set (Class, List (Reference (Result)) of all listeners that are waiting in 
41
     * changes in class Class
43
     * changes in class Class
42
     */
44
     */
43
    private Map reg;
45
    private transient Map reg;
44
    /** count of items in to lookup */
46
    /** count of items in to lookup */
45
    private int count;
47
    private int count;
46
    
48
    
Lines 447-453 Link Here
447
    /** Extension to the default lookup item that offers additional information
449
    /** Extension to the default lookup item that offers additional information
448
     * for the data structures use in AbstractLookup
450
     * for the data structures use in AbstractLookup
449
     */
451
     */
450
    public static abstract class Pair extends Lookup.Item {
452
    public static abstract class Pair extends Lookup.Item implements Serializable {
453
        private static final long serialVersionUID = 1L;
454
        
451
        /** possition of this item in the lookup, manipulated in addPair, removePair, setPairs methods */
455
        /** possition of this item in the lookup, manipulated in addPair, removePair, setPairs methods */
452
        int index = -1;
456
        int index = -1;
453
457
Lines 641-652 Link Here
641
     *
645
     *
642
     * @since 1.25
646
     * @since 1.25
643
     */
647
     */
644
    public static class Content extends Object {
648
    public static class Content extends Object implements Serializable {
649
        
650
        private static final long serialVersionUID = 1L;
645
        
651
        
646
        // one of them is always null (except attach stage)
652
        // one of them is always null (except attach stage)
647
        /** abstract lookup we are connected to */
653
        /** abstract lookup we are connected to */
648
        private AbstractLookup al = null;
654
        private AbstractLookup al = null;
649
        private ArrayList earlyPairs = new ArrayList(3);
655
        private transient ArrayList earlyPairs = new ArrayList(3);
650
        
656
        
651
        /** A lookup attaches to this object.
657
        /** A lookup attaches to this object.
652
         */
658
         */
(-)openide/src/org/openide/util/lookup/InheritanceTree.java (-22 / +83 lines)
Lines 14-35 Link Here
14
14
15
package org.openide.util.lookup;
15
package org.openide.util.lookup;
16
16
17
17
import java.io.*;
18
import java.lang.ref.WeakReference;
18
import java.lang.ref.WeakReference;
19
import java.util.ArrayList;
19
import java.util.*;
20
import java.util.Collection;
20
21
import java.util.Collections;
21
import org.openide.util.Lookup;
22
import java.util.Enumeration;
22
import org.openide.util.enum.*;
23
import java.util.Iterator;
24
import java.util.Map;
25
import java.util.WeakHashMap;
26
import java.util.List;
27
28
import org.openide.util.enum.AlterEnumeration;
29
import org.openide.util.enum.EmptyEnumeration;
30
import org.openide.util.enum.QueueEnumeration;
31
import org.openide.util.enum.SequenceEnumeration;
32
import org.openide.util.enum.SingletonEnumeration;
33
23
34
24
35
/** A tree to represent classes with inheritance. Description of the
25
/** A tree to represent classes with inheritance. Description of the
Lines 96-106 Link Here
96
 *
86
 *
97
 * @author  Jaroslav Tulach
87
 * @author  Jaroslav Tulach
98
 */
88
 */
99
final class InheritanceTree extends Object implements java.util.Comparator {
89
final class InheritanceTree extends Object implements Comparator, Serializable {
90
    private static final long serialVersionUID = 1L;
91
100
    /** the root item (represents Object) */
92
    /** the root item (represents Object) */
101
    private Node object;
93
    private transient Node object;
102
    /** map of queried interfaces (Class, Set) */
94
    /** Map of queried interfaces.
103
    private Map interfaces;
95
     * <p>Type: <code>Map&lt;Class, (Collection&lt;AbstractLookup.Pair&gt; | AbstractLookup.Pair)&gt;</code>
96
     */
97
    private transient Map interfaces;
104
98
105
    /** Constructor
99
    /** Constructor
106
     */
100
     */
Lines 108-113 Link Here
108
        object = new Node (java.lang.Object.class);
102
        object = new Node (java.lang.Object.class);
109
    }
103
    }
110
104
105
    private void writeObject (ObjectOutputStream oos) throws IOException {
106
        oos.writeObject(object);
107
        Iterator it = interfaces.entrySet().iterator();
108
        while (it.hasNext()) {
109
            Map.Entry e = (Map.Entry)it.next();
110
            Class c = (Class)e.getKey();
111
            oos.writeObject(c.getName());
112
            Object o = e.getValue();
113
            if (!(o instanceof Collection) && !(o instanceof AbstractLookup.Pair)) throw new ClassCastException(String.valueOf(o));
114
            oos.writeObject(o);
115
        }
116
        oos.writeObject(null);
117
    }
118
119
    private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
120
        object = (Node)ois.readObject();
121
        interfaces = new WeakHashMap();
122
        String clazz;
123
        ClassLoader l = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class);
124
        while ((clazz = (String)ois.readObject()) != null) {
125
            Object o = ois.readObject();
126
            if (!(o instanceof Collection) && !(o instanceof AbstractLookup.Pair)) throw new ClassCastException(String.valueOf(o));
127
            Class c = Class.forName(clazz, false, l);
128
            interfaces.put(c, o);
129
        }
130
    }
131
        
111
132
112
    /** Adds an item into the tree.
133
    /** Adds an item into the tree.
113
    * @param item to add
134
    * @param item to add
Lines 745-756 Link Here
745
766
746
    /** Node in the tree.
767
    /** Node in the tree.
747
    */
768
    */
748
    static final class Node extends WeakReference {
769
    static final class Node extends WeakReference implements Serializable {
770
        static final long serialVersionUID = 3L;
749
        /** children nodes */
771
        /** children nodes */
750
        public ArrayList children;
772
        public ArrayList children; // List<Node>
751
773
752
        /** list of items assigned to this node (suspect to be subclasses) */
774
        /** list of items assigned to this node (suspect to be subclasses) */
753
        public ArrayList items;
775
        public ArrayList items; // List<AbstractLookup.Pair>
754
776
755
        /** Constructor.
777
        /** Constructor.
756
        */
778
        */
Lines 807-811 Link Here
807
            items.add (item);
829
            items.add (item);
808
            return true;
830
            return true;
809
        }
831
        }
832
        
833
        private Object writeReplace () {
834
            return new R (this);
835
        }
836
        
810
    } // End of class Node.
837
    } // End of class Node.
838
    
839
    private static final class R implements Serializable {
840
        static final long serialVersionUID = 1L;
841
        
842
        private static ClassLoader l;
843
        
844
        private String clazzName;
845
        private transient Class clazz;
846
        private ArrayList children;
847
        private ArrayList items;
848
        
849
        public R (Node n) {
850
            this.clazzName = n.getType ().getName();
851
            this.children = n.children;
852
            this.items = n.items;
853
        }
854
        
855
        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
856
            ois.defaultReadObject();
857
            if (l == null) {
858
                l = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class);
859
            }
860
            clazz = Class.forName(clazzName, false, l);
861
        }
862
        
863
        private Object readResolve () throws ObjectStreamException {
864
            Node n = new Node (clazz);
865
            n.children = children;
866
            n.items = items;
867
            
868
            return n;
869
        }
870
            
871
    } // end of R
811
}
872
}
(-)openide/src/org/openide/util/lookup/ProxyLookup.java (+9 lines)
Lines 40-45 Link Here
40
        this.lookups = lookups;
40
        this.lookups = lookups;
41
    }
41
    }
42
    
42
    
43
    /**
44
     * Create a lookup initially proxying to no others.
45
     * Permits serializable subclasses.
46
     * @since XXX
47
     */
48
    protected ProxyLookup() {
49
        this(new Lookup[0]);
50
    }
51
    
43
    public String toString() {
52
    public String toString() {
44
        return "ProxyLookup" + Arrays.asList(lookups); // NOI18N
53
        return "ProxyLookup" + Arrays.asList(lookups); // NOI18N
45
    }
54
    }

Return to bug 20190