KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > startup > NbInstaller


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

19
20 package org.netbeans.core.startup;
21
22 import java.io.BufferedInputStream JavaDoc;
23 import java.io.BufferedOutputStream JavaDoc;
24 import java.io.ByteArrayInputStream JavaDoc;
25 import java.io.ByteArrayOutputStream JavaDoc;
26 import java.io.File JavaDoc;
27 import java.io.FileInputStream JavaDoc;
28 import java.io.FileOutputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.InputStream JavaDoc;
31 import java.io.OutputStream JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Collections JavaDoc;
35 import java.util.Date JavaDoc;
36 import java.util.HashMap JavaDoc;
37 import java.util.HashSet JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.Locale JavaDoc;
40 import java.util.Map JavaDoc;
41 import java.util.Set JavaDoc;
42 import java.util.StringTokenizer JavaDoc;
43 import java.util.jar.Attributes JavaDoc;
44 import java.util.jar.JarEntry JavaDoc;
45 import java.util.jar.JarFile JavaDoc;
46 import java.util.jar.Manifest JavaDoc;
47 import java.util.logging.Level JavaDoc;
48 import org.netbeans.Events;
49 import org.netbeans.InvalidException;
50 import org.netbeans.Module;
51 import org.netbeans.ModuleInstaller;
52 import org.netbeans.ModuleManager;
53 import org.netbeans.Util;
54 import org.netbeans.core.startup.layers.ModuleLayeredFileSystem;
55 import org.openide.filesystems.FileObject;
56 import org.openide.filesystems.FileStateInvalidException;
57 import org.openide.filesystems.FileUtil;
58 import org.openide.filesystems.Repository;
59 import org.openide.modules.Dependency;
60 import org.openide.modules.ModuleInstall;
61 import org.openide.modules.SpecificationVersion;
62 import org.openide.util.Exceptions;
63 import org.openide.util.NbCollections;
64 import org.openide.util.SharedClassObject;
65 import org.openide.util.NbBundle;
66 import org.openide.util.lookup.InstanceContent;
67 import org.xml.sax.SAXException JavaDoc;
68
69 /** Concrete implementation of the module installation functionality.
70  * This class can pay attention to the details of manifest format,
71  * details of how to install particular sections and layers and so on.
72  * It may have a limited UI, i.e. notifying problems with localized
73  * messages and the like.
74  * @author Jesse Glick, Jan Pokorsky, Jaroslav Tulach, et al.
75  */

76 final class NbInstaller extends ModuleInstaller {
77     
78     /** set of manifest sections for each module */
79     private final Map JavaDoc<Module,Set JavaDoc<ManifestSection>> sections = new HashMap JavaDoc<Module,Set JavaDoc<ManifestSection>>(100);
80     /** ModuleInstall classes for each module that declares one */
81     private final Map JavaDoc<Module,Class JavaDoc<? extends ModuleInstall>> installs = new HashMap JavaDoc<Module,Class JavaDoc<? extends ModuleInstall>>(100);
82     /** layer resources for each module that declares one */
83     private final Map JavaDoc<Module,String JavaDoc> layers = new HashMap JavaDoc<Module,String JavaDoc>(100);
84     /** exact use of this is hard to explain */
85     private boolean initializedFolderLookup = false;
86     /** where to report events to */
87     private final Events ev;
88     /** associated controller of module list; needed for handling ModuleInstall ser */
89     private ModuleList moduleList;
90     /** associated manager */
91     private ModuleManager mgr;
92     /** set of permitted core or package dependencies from a module */
93     private final Map JavaDoc<Module,Set JavaDoc<String JavaDoc>> kosherPackages = new HashMap JavaDoc<Module,Set JavaDoc<String JavaDoc>>(100);
94     /** Package prefixes passed as special system property. */
95     private static String JavaDoc[] specialResourcePrefixes = null;
96         
97     /** Create an NbInstaller.
98      * You should also call {@link #registerManager} and if applicable
99      * {@link #registerList} to make the installer fully usable.
100      * @param ev events to log
101      */

102     public NbInstaller(Events ev) {
103         this.ev = ev;
104     }
105     
106     /** Access from ModuleSystem. */
107     void registerList(ModuleList list) {
108         if (moduleList != null) throw new IllegalStateException JavaDoc();
109         moduleList = list;
110     }
111     void registerManager(ModuleManager manager) {
112         if (mgr != null) throw new IllegalStateException JavaDoc();
113         mgr = manager;
114     }
115
116     // @SuppressWarnings("unchecked")
117
public void prepare(Module m) throws InvalidException {
118         ev.log(Events.PREPARE, m);
119         Set JavaDoc<ManifestSection> mysections = null;
120         Class JavaDoc<?> clazz = null;
121         {
122             // Find and load manifest sections.
123
for (Map.Entry JavaDoc<String JavaDoc,Attributes JavaDoc> entry : m.getManifest().getEntries().entrySet()) {
124                 ManifestSection section = ManifestSection.create(entry.getKey(), entry.getValue(), m);
125                 if (section != null) {
126                     if (mysections == null) {
127                         mysections = new HashSet JavaDoc<ManifestSection>(25);
128                     }
129                     mysections.add(section);
130                 }
131             }
132         }
133         String JavaDoc installClass = m.getManifest().getMainAttributes().getValue("OpenIDE-Module-Install"); // NOI18N
134
if (installClass != null) {
135             String JavaDoc installClassName;
136             try {
137                 installClassName = Util.createPackageName(installClass);
138             } catch (IllegalArgumentException JavaDoc iae) {
139                 InvalidException ie = new InvalidException(m, iae.toString());
140                 ie.initCause(iae);
141                 throw ie;
142             }
143             if (installClass.endsWith(".ser")) throw new InvalidException(m, "Serialized module installs not supported: " + installClass); // NOI18N
144
try {
145                 // We could simply load the ModuleInstall right away in all cases.
146
// However consider a ModuleInstall that has a static or instance
147
// initializer or initialize() calling NbBundle.getBundle(String). In the
148
// old module installer that would work and it was never specifically discouraged,
149
// so that needs to continue working. So do not resolve in most cases;
150
// if the module specifically has a validate method, then this is clearly
151
// documented to be called before systemClassLoader is ready, so the onus
152
// is on the module author to avoid anything dangerous there.
153
clazz = Class.forName(installClassName, false, m.getClassLoader());
154                 if (clazz.getClassLoader() != m.getClassLoader()) {
155                     ev.log(Events.WRONG_CLASS_LOADER, m, clazz, m.getClassLoader());
156                 }
157                 // Search for a validate() method; if there is none, do not resolve the class now!
158
Class JavaDoc<?> c;
159                 for (c = clazz; c != ModuleInstall.class && c != Object JavaDoc.class; c = c.getSuperclass()) {
160                     try {
161                         // #13997: do not search in superclasses.
162
c.getDeclaredMethod("validate"); // NOI18N
163
// It has one. We are permitted to resolve the class, create
164
// the installer instance, and validate it.
165
ModuleInstall install = SharedClassObject.findObject(clazz.asSubclass(ModuleInstall.class), true);
166                         // The following can throw IllegalStateException, which we would
167
// rethrow as InvalidException below:
168
install.validate();
169                     } catch (NoSuchMethodException JavaDoc nsme) {
170                         // OK, did not find it here, continue.
171
}
172                 }
173                 if (c == Object JavaDoc.class) throw new ClassCastException JavaDoc("Should extend ModuleInstall: " + clazz.getName()); // NOI18N
174
// Did not find any validate() method, so remember the class and resolve later.
175
} catch (Exception JavaDoc t) {
176                 InvalidException ie = new InvalidException(m, t.toString());
177                 ie.initCause(t);
178                 throw ie;
179             } catch (LinkageError JavaDoc t) {
180                 InvalidException ie = new InvalidException(m, t.toString());
181                 ie.initCause(t);
182                 throw ie;
183             }
184         }
185         // For layer & help set, validate only that the base-locale resource
186
// exists, not its contents or anything.
187
String JavaDoc layerResource = m.getManifest().getMainAttributes().getValue("OpenIDE-Module-Layer"); // NOI18N
188
if (layerResource != null) {
189             URL JavaDoc layer = m.getClassLoader().getResource(layerResource);
190             if (layer == null) throw new InvalidException(m, "Layer not found: " + layerResource); // NOI18N
191
}
192         String JavaDoc helpSetName = m.getManifest().getMainAttributes().getValue("OpenIDE-Module-Description"); // NOI18N
193
if (helpSetName != null) {
194             Util.err.warning("Use of OpenIDE-Module-Description in " + m.getCodeNameBase() + " is deprecated.");
195             Util.err.warning("(Please install help using an XML layer instead.)");
196         }
197         // We are OK, commit everything to our cache.
198
if (mysections != null) {
199             sections.put(m, mysections);
200         }
201         if (clazz != null) {
202             installs.put(m, clazz.asSubclass(ModuleInstall.class));
203         }
204         if (layerResource != null) {
205             layers.put(m, layerResource);
206         }
207     }
208     
209     public void dispose(Module m) {
210         Util.err.fine("dispose: " + m);
211         // Events probably not needed here.
212
Set JavaDoc<ManifestSection> s = sections.remove(m);
213         if (s != null) {
214             for (ManifestSection sect : s) {
215                 sect.dispose();
216             }
217         }
218         installs.remove(m);
219         layers.remove(m);
220         kosherPackages.remove(m);
221     }
222     
223     public void load(List JavaDoc<Module> modules) {
224         ev.log(Events.START_LOAD, modules);
225         
226         // we need to update the classloader as otherwise we might not find
227
// all the needed classes
228
if (mgr != null) { // could be null during tests
229
MainLookup.systemClassLoaderChanged(/* #61107: do not use Thread.cCL here! */mgr.getClassLoader());
230         }
231         ev.log(Events.PERF_TICK, "META-INF/services/ additions registered"); // NOI18N
232

233         for (Module m: modules) {
234             checkForDeprecations(m);
235             openideModuleEnabled(m);
236         }
237         
238         loadLayers(modules, true);
239         ev.log(Events.PERF_TICK, "layers loaded"); // NOI18N
240

241         ev.log(Events.PERF_START, "NbInstaller.load - sections"); // NOI18N
242
ev.log(Events.LOAD_SECTION);
243         CoreBridge.conditionallyLoaderPoolTransaction(true);
244         try {
245             for (Module m: modules) {
246                 try {
247                     loadSections(m, true);
248                 } catch (Exception JavaDoc t) {
249                     Util.err.log(Level.SEVERE, null, t);
250                 } catch (LinkageError JavaDoc le) {
251                     Util.err.log(Level.SEVERE, null, le);
252                 }
253                 ev.log(Events.PERF_TICK, "sections for " + m.getCodeName() + " loaded"); // NOI18N
254
}
255         } finally {
256             CoreBridge.conditionallyLoaderPoolTransaction(false);
257         }
258         ev.log(Events.PERF_END, "NbInstaller.load - sections"); // NOI18N
259

260         // Yarda says to put this here.
261
if (! initializedFolderLookup) {
262             Util.err.fine("modulesClassPathInitialized");
263             MainLookup.modulesClassPathInitialized();
264             initializedFolderLookup = true;
265         }
266         
267         // we need to initialize UI before we let modules run ModuleInstall.restore
268
Main.initUICustomizations();
269
270         ev.log(Events.PERF_START, "NbInstaller.load - ModuleInstalls"); // NOI18N
271
for (Module m: modules) {
272             try {
273                 loadCode(m, true);
274             } catch (Exception JavaDoc t) {
275                 Util.err.log(Level.SEVERE, null, t);
276             } catch (LinkageError JavaDoc le) {
277                 Util.err.log(Level.SEVERE, null, le);
278             } catch (AssertionError JavaDoc e) {
279                 Util.err.log(Level.SEVERE, null, e);
280             }
281         ev.log(Events.PERF_TICK, "ModuleInstall for " + m.getCodeName() + " called"); // NOI18N
282
}
283         ev.log(Events.PERF_END, "NbInstaller.load - ModuleInstalls"); // NOI18N
284

285         ev.log(Events.FINISH_LOAD, modules);
286         
287         maybeSaveManifestCache();
288         
289         if (Boolean.getBoolean("netbeans.preresolve.classes")) {
290             preresolveClasses(modules);
291         }
292     }
293     
294     public void unload(List JavaDoc<Module> modules) {
295         ev.log(Events.START_UNLOAD, modules);
296         for (Module m: modules) {
297             try {
298                 loadCode(m, false);
299             } catch (Exception JavaDoc t) {
300                 Util.err.log(Level.SEVERE, null, t);
301             } catch (LinkageError JavaDoc le) {
302                 Util.err.log(Level.SEVERE, null, le);
303             }
304         }
305         CoreBridge.conditionallyLoaderPoolTransaction(true);
306         try {
307             for (Module m: modules) {
308                 try {
309                     loadSections(m, false);
310                 } catch (Exception JavaDoc t) {
311                     Util.err.log(Level.SEVERE, null, t);
312                 } catch (LinkageError JavaDoc le) {
313                     Util.err.log(Level.SEVERE, null, le);
314                 }
315             }
316         } finally {
317             try {
318                 CoreBridge.conditionallyLoaderPoolTransaction(false);
319             } catch (RuntimeException JavaDoc e) {
320                 Util.err.log(Level.SEVERE, null, e);
321             }
322         }
323         loadLayers(modules, false);
324         ev.log(Events.FINISH_UNLOAD, modules);
325     }
326     
327     /** Load/unload installer code for a module. */
328     @SuppressWarnings JavaDoc("deprecation") // old ModuleInstall methods we have to call
329
private void loadCode(Module m, boolean load) throws Exception JavaDoc {
330         Class JavaDoc<? extends ModuleInstall> instClazz = installs.get(m);
331         if (instClazz != null) {
332             ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
333             if (load) {
334                 if (moduleList != null) {
335                     moduleList.installPrepare(m, inst);
336                 }
337                 // restore, install, or upgrade as appropriate
338
Object JavaDoc history = m.getHistory();
339                 if (history instanceof ModuleHistory) {
340                     ModuleHistory h = (ModuleHistory)history;
341                     if (h.isPreviouslyInstalled()) {
342                         // Check whether we have changed versions.
343
SpecificationVersion oldSpec = h.getOldSpecificationVersion();
344                         SpecificationVersion nueSpec = m.getSpecificationVersion();
345                         if (m.getCodeNameRelease() != h.getOldMajorVersion() ||
346                                 (oldSpec == null ^ nueSpec == null) ||
347                                 (oldSpec != null && nueSpec != null && oldSpec.compareTo(nueSpec) != 0)) {
348                             // Yes, different version; upgrade from the old one.
349
ev.log(Events.UPDATE, m);
350                             inst.updated(h.getOldMajorVersion(), oldSpec == null ? null : oldSpec.toString());
351                         } else {
352                             // Same version as before.
353
ev.log(Events.RESTORE, m);
354                             inst.restored();
355                         }
356                     } else {
357                         // First time.
358
ev.log(Events.INSTALL, m);
359                         inst.installed();
360                     }
361                 } else {
362                     // Probably fixed module. Just restore.
363
ev.log(Events.RESTORE, m);
364                     inst.restored();
365                 }
366                 if (moduleList != null) {
367                     moduleList.installPostpare(m, inst);
368                 }
369             } else {
370                 ev.log(Events.UNINSTALL, m);
371                 inst.uninstalled();
372                 if (m.getHistory() instanceof ModuleHistory) {
373                     ((ModuleHistory)m.getHistory()).resetHistory();
374                 }
375             }
376         }
377     }
378     
379     /** Load/unload all manifest sections for a given module. */
380     @SuppressWarnings JavaDoc("deprecation") // old ManifestSection.* we have to interpret
381
private void loadSections(Module m, boolean load) throws Exception JavaDoc {
382         Set JavaDoc<ManifestSection> s = sections.get(m);
383         if (s == null) {
384             return;
385         }
386         // Whether we yet had occasion to attach to the module actions list.
387
boolean attachedToMA = false;
388         try {
389         ev.log(Events.LOAD_SECTION);
390             for (ManifestSection sect : s) {
391                 if (sect instanceof ManifestSection.ActionSection) {
392                     if (! attachedToMA) {
393                         // First categorize the actions we will add.
394
Object JavaDoc category = m.getLocalizedAttribute("OpenIDE-Module-Display-Category"); // NOI18N
395
if (category == null) {
396                             // uncategorized modules group by themselves
397
category = m.getCodeNameBase();
398                         }
399                         CoreBridge.getDefault().attachToCategory(category);
400                         attachedToMA = true;
401                     }
402                     CoreBridge.getDefault ().loadActionSection((ManifestSection.ActionSection)sect, load);
403                 } else if (sect instanceof ManifestSection.ClipboardConvertorSection) {
404                     loadGenericSection(sect, load);
405                 } else if (sect instanceof ManifestSection.DebuggerSection) {
406                     loadGenericSection(sect, load);
407                 } else if (sect instanceof ManifestSection.LoaderSection) {
408                     CoreBridge.getDefault().loadLoaderSection((ManifestSection.LoaderSection)sect, load);
409                 } else {
410                     assert false : sect;
411                 }
412             }
413         } finally {
414             if (attachedToMA) {
415                 CoreBridge.getDefault ().attachToCategory (null);
416             }
417         }
418     }
419     
420     // Load or unload various possible manifest sections.
421

422     /** Simple section that can just be passed to lookup.
423      * The lookup sees the real object, not the section.
424      * You tell it whether to convert the result to the real
425      * instance, or just register the section itself.
426      */

427     private void loadGenericSection(ManifestSection s, boolean load) {
428         CoreBridge.getDefault().loadDefaultSection(s, convertor, load);
429     }
430     
431     private final InstanceContent.Convertor<ManifestSection,Object JavaDoc> convertor = new Convertor();
432     private final class Convertor implements InstanceContent.Convertor<ManifestSection,Object JavaDoc> { // or <ManifestSection,SharedClassObject>?
433
Convertor() {}
434         public Object JavaDoc convert(ManifestSection s) {
435             try {
436                 return s.getInstance();
437             } catch (Exception JavaDoc e) {
438                 Util.err.log(Level.WARNING, null, e);
439                 // Try to remove it from the pool so it does not continue
440
// to throw errors over and over. Hopefully it is kosher to
441
// do this while it is in the process of converting! I.e.
442
// hopefully InstanceLookup is well-synchronized.
443
loadGenericSection(s, false);
444                 return null;
445             }
446         }
447         public Class JavaDoc<?> type(ManifestSection s) {
448             return s.getSuperclass();
449         }
450         
451         /** Computes the ID of the resulted object.
452          * @param obj the registered object
453          * @return the ID for the object
454          */

455         public String JavaDoc id(ManifestSection obj) {
456             return obj.toString ();
457         }
458         
459         /** The human presentable name for the object.
460          * @param obj the registered object
461          * @return the name representing the object for the user
462          */

463         public String JavaDoc displayName(ManifestSection obj) {
464             return obj.toString ();
465         }
466         
467     }
468     
469     /** Either load or unload the layer, if any, for a set of modules.
470      * If the parameter load is true, load it, else unload it.
471      * Locale/branding variants are likewise loaded or unloaded.
472      * If a module has no declared layer, does nothing.
473      */

474     private void loadLayers(List JavaDoc<Module> modules, boolean load) {
475         ev.log(load ? Events.LOAD_LAYERS : Events.UNLOAD_LAYERS, modules);
476         // #23609: dependent modules should be able to override:
477
modules = new ArrayList JavaDoc<Module>(modules);
478         Collections.reverse(modules);
479         Map JavaDoc<ModuleLayeredFileSystem,List JavaDoc<URL JavaDoc>> urls = new HashMap JavaDoc<ModuleLayeredFileSystem,List JavaDoc<URL JavaDoc>>(5);
480         for (Module m: modules) {
481             String JavaDoc s = layers.get(m);
482             if (s != null) {
483                 Util.err.fine("loadLayer: " + s + " load=" + load);
484                 // Actually add a sequence of layers, in locale order.
485
String JavaDoc base, ext;
486                 int idx = s.lastIndexOf('.'); // NOI18N
487
if (idx == -1) {
488                     base = s;
489                     ext = ""; // NOI18N
490
} else {
491                     base = s.substring(0, idx);
492                     ext = s.substring(idx);
493                 }
494                 ClassLoader JavaDoc cl = m.getClassLoader();
495                 ModuleLayeredFileSystem host;
496                 // #19458: only put reloadables into the "session layer"
497
// (where they will not have their layers cached). All others
498
// should go into "installation layer" (so that they can mask
499
// layers according to cross-dependencies).
500
if (m.isReloadable()) {
501                     host = ModuleLayeredFileSystem.getUserModuleLayer();
502                 } else {
503                     host = ModuleLayeredFileSystem.getInstallationModuleLayer();
504                 }
505                 List JavaDoc<URL JavaDoc> theseurls = urls.get(host);
506                 if (theseurls == null) {
507                     theseurls = new ArrayList JavaDoc<URL JavaDoc>(100);
508                     urls.put(host, theseurls);
509                 }
510                 boolean foundSomething = false;
511                 for (String JavaDoc suffix : NbCollections.iterable(NbBundle.getLocalizingSuffixes())) {
512                     String JavaDoc resource = base + suffix + ext;
513                     URL JavaDoc u = cl.getResource(resource);
514                     if (u != null) {
515                         theseurls.add(u);
516                         foundSomething = true;
517                     }
518                 }
519                 if (! foundSomething) {
520                     // Should never happen (we already checked in prepare() for base layer)...
521
Util.err.fine("Module layer not found: " + s);
522                     continue;
523                 }
524             }
525         }
526         // Now actually do it.
527
for (Map.Entry JavaDoc<ModuleLayeredFileSystem,List JavaDoc<URL JavaDoc>> entry: urls.entrySet()) {
528             ModuleLayeredFileSystem host = entry.getKey();
529             List JavaDoc<URL JavaDoc> theseurls = entry.getValue();
530             Util.err.fine("Adding/removing layer URLs: host=" + host + " urls=" + theseurls);
531             try {
532                 if (load) {
533                     host.addURLs(theseurls);
534                 } else {
535                     host.removeURLs(theseurls);
536                 }
537             } catch (Exception JavaDoc e) {
538                 Util.err.log(Level.WARNING, null, e);
539             }
540         }
541     }
542     
543     /** Scan a (nondeprecated) module for direct dependencies on deprecated modules.
544      * Deprecated modules can quietly depend on other deprecated modules.
545      * And if the module is not actually enabled, it does not matter.
546      * Indirect dependencies are someone else's problem.
547      * Provide-require dependencies do not count either.
548      * @param m the module which is now being turned on
549      */

550     private void checkForDeprecations(Module m) {
551         if (!Boolean.valueOf((String JavaDoc)m.getAttribute("OpenIDE-Module-Deprecated")).booleanValue()) { // NOI18N
552
for (Dependency dep : m.getDependencies()) {
553                 if (dep.getType() == Dependency.TYPE_MODULE) {
554                     String JavaDoc cnb = (String JavaDoc) Util.parseCodeName(dep.getName())[0];
555                     Module o = mgr.get(cnb);
556                     if (o == null) throw new IllegalStateException JavaDoc("No such module: " + cnb); // NOI18N
557
if (Boolean.parseBoolean((String JavaDoc) o.getAttribute("OpenIDE-Module-Deprecated"))) { // NOI18N
558
String JavaDoc message = (String JavaDoc)o.getLocalizedAttribute("OpenIDE-Module-Deprecation-Message"); // NOI18N
559
// XXX use NbEvents? I18N?
560
// For now, assume this is a developer-oriented message that need not be localized
561
// or displayed in a pretty fashion.
562
if (message != null) {
563                             Util.err.warning("the module " + m.getCodeNameBase() + " uses " + cnb + " which is deprecated: " + message); // NOI18N
564
} else {
565                             Util.err.warning("the module " + m.getCodeNameBase() + " uses " + cnb + " which is deprecated."); // NOI18N
566
}
567                     }
568                 }
569             }
570         }
571     }
572         
573     public boolean closing(List JavaDoc<Module> modules) {
574         Util.err.fine("closing: " + modules);
575     for (Module m: modules) {
576             Class JavaDoc<? extends ModuleInstall> instClazz = installs.get(m);
577             if (instClazz != null) {
578                 try {
579                     ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
580                     if (! inst.closing()) {
581                         Util.err.fine("Module " + m + " refused to close");
582                         return false;
583                     }
584                 } catch (RuntimeException JavaDoc re) {
585                     Util.err.log(Level.SEVERE, null, re);
586                     // continue, assume it is trash
587
} catch (LinkageError JavaDoc le) {
588                     Util.err.log(Level.SEVERE, null, le);
589                 }
590             }
591         }
592         return true;
593     }
594     
595     public void close(List JavaDoc<Module> modules) {
596         Util.err.fine("close: " + modules);
597         ev.log(Events.CLOSE);
598         // [PENDING] this may need to write out changed ModuleInstall externalized
599
// forms...is that really necessary to do here, or isn't it enough to
600
// do right after loading etc.? Currently these are only written when
601
// a ModuleInstall has just been restored etc. which is probably fine.
602
for (Module m : modules) {
603             Class JavaDoc<? extends ModuleInstall> instClazz = installs.get(m);
604             if (instClazz != null) {
605                 try {
606                     ModuleInstall inst = SharedClassObject.findObject(instClazz, true);
607                     if (inst == null) throw new IllegalStateException JavaDoc("Inconsistent state: " + instClazz); // NOI18N
608
inst.close();
609                 } catch (ThreadDeath JavaDoc td) {
610                     throw td;
611                 } catch (Throwable JavaDoc t) {
612                     // Catch even the heavy stuff here, we are going away.
613
Util.err.log(Level.SEVERE, null, t);
614                     // oh well
615
}
616             }
617         }
618     }
619     
620     private AutomaticDependencies autoDepsHandler = null;
621     
622     /** Overridden to perform automatic API upgrades.
623      * That is, should do nothing on new modules, but for older ones will
624      * automatically make them depend on things they need.
625      * This is now all handled from declarative configuration files:
626      * in the system filesystem, ModuleAutoDeps/*.xml may be added
627      * according to the DTD "-//NetBeans//DTD Module Automatic Dependencies 1.0//EN".
628      */

629     public void refineDependencies(Module m, Set JavaDoc<Dependency> dependencies) {
630         /* JST-PENDING just tring to comment this out
631         // All modules implicitly depend on the APIs somehow.
632         if (!m.getCodeNameBase().equals("org.openide") &&
633                 Util.getModuleDep(dependencies, "org.openide") == null) {
634             dependencies.addAll(Dependency.create(Dependency.TYPE_MODULE, "org.openide/1 > 0")); // NOI18N
635         }
636          */

637         if (Boolean.getBoolean("org.netbeans.core.modules.NbInstaller.noAutoDeps")) {
638             // Skip them all - useful for unit tests.
639
return;
640         }
641         if (autoDepsHandler == null) {
642             FileObject depsFolder = Repository.getDefault().getDefaultFileSystem().findResource("ModuleAutoDeps");
643             if (depsFolder != null) {
644                 FileObject[] kids = depsFolder.getChildren();
645                 List JavaDoc<URL JavaDoc> urls = new ArrayList JavaDoc<URL JavaDoc>(Math.max(kids.length, 1));
646                 for (FileObject kid : kids) {
647                     if (kid.hasExt("xml")) { // NOI18N
648
try {
649                             urls.add(kid.getURL());
650                         } catch (FileStateInvalidException e) {
651                             Util.err.log(Level.WARNING, null, e);
652                         }
653                     }
654                 }
655                 try {
656                     autoDepsHandler = AutomaticDependencies.parse(urls.toArray(new URL JavaDoc[urls.size()]));
657                 } catch (IOException JavaDoc e) {
658                     Util.err.log(Level.WARNING, null, e);
659                 } catch (SAXException JavaDoc e) {
660                     Util.err.log(Level.WARNING, null, e);
661                 }
662             }
663             if (autoDepsHandler == null) {
664                 // Parsing failed, or no files.
665
autoDepsHandler = AutomaticDependencies.empty();
666             }
667             if (Util.err.isLoggable(Level.FINE)) {
668                 Util.err.fine("Auto deps: " + autoDepsHandler);
669             }
670         }
671         AutomaticDependencies.Report rep = autoDepsHandler.refineDependenciesAndReport(m.getCodeNameBase(), dependencies);
672         if (rep.isModified()) {
673             Util.err.warning("had to upgrade dependencies for module " + m.getCodeNameBase() + ": added = " + rep.getAdded() + " removed = " + rep.getRemoved() + "; details: " + rep.getMessages());
674         }
675     }
676     
677     public String JavaDoc[] refineProvides (Module m) {
678         if (m.getCodeNameBase ().equals ("org.openide.modules")) { // NOI18N
679
List JavaDoc<String JavaDoc> arr = new ArrayList JavaDoc<String JavaDoc>(4);
680             
681             boolean isOS2 = (org.openide.util.Utilities.getOperatingSystem () & org.openide.util.Utilities.OS_OS2) != 0;
682             
683             if (org.openide.util.Utilities.isUnix ()) {
684                 arr.add ("org.openide.modules.os.Unix"); // NOI18N
685
if (!org.openide.util.Utilities.isMac()) {
686                     arr.add("org.openide.modules.os.PlainUnix"); // NOI18N
687
}
688             }
689             
690             if (
691                 org.openide.util.Utilities.isWindows ()
692             ) {
693                 arr.add ("org.openide.modules.os.Windows"); // NOI18N
694
}
695             
696             if (org.openide.util.Utilities.isMac()) {
697                 arr.add ("org.openide.modules.os.MacOSX"); // NOI18N
698
}
699             if (isOS2) {
700                 arr.add ("org.openide.modules.os.OS2"); // NOI18N
701
}
702             
703             // module format is now 1
704
arr.add ("org.openide.modules.ModuleFormat1"); // NOI18N
705

706             return arr.toArray (new String JavaDoc[0]);
707         }
708         return null;
709     }
710     
711     public boolean shouldDelegateResource(Module m, Module parent, String JavaDoc pkg) {
712         //Util.err.fine("sDR: m=" + m + " parent=" + parent + " pkg=" + pkg);
713
// Cf. #19622:
714
if (parent == null) {
715             // Application classpath checks.
716
for (String JavaDoc cppkg : CLASSPATH_PACKAGES) {
717                 if (pkg.startsWith(cppkg) && !findKosher(m).contains(cppkg)) {
718                     // Undeclared use of a classpath package. Refuse it.
719
if (Util.err.isLoggable(Level.FINE)) {
720                         Util.err.fine("Refusing to load classpath package " + pkg + " for " + m.getCodeNameBase() + " without a proper dependency"); // NOI18N
721
}
722                     return false;
723                 }
724             }
725         }
726         return true;
727     }
728     
729     private static final String JavaDoc[] CLASSPATH_PACKAGES = new String JavaDoc[] {
730         // core.jar shall be inaccessible
731
"org/netbeans/core/startup/",
732         // Java language infrastructure bundled with IDE; do not want clashes with JDK 6:
733
"com/sun/tools/javac/",
734         "com/sun/tools/javadoc/",
735         "com/sun/javadoc/",
736         "com/sun/source/",
737         "javax/annotation/",
738         "javax/lang/model/",
739         "javax/tools/",
740         // do not want JAX-WS 2.0 classes from JDK 6;
741
"javax/xml/bind/", // NOI18N
742
"javax/xml/ws/", // NOI18N
743
"javax/xml/stream/", // NOI18N
744
"javax/jws/", // NOI18N
745
"javax/xml/soap/" // NOI18N
746
};
747     
748     private Set JavaDoc<String JavaDoc> findKosher(Module m) {
749         Set JavaDoc<String JavaDoc> s = kosherPackages.get(m);
750         if (s == null) {
751             s = new HashSet JavaDoc<String JavaDoc>();
752             Set JavaDoc<Dependency> deps = m.getDependencies();
753             SpecificationVersion openide = Util.getModuleDep(deps, "org.openide"); // NOI18N
754
boolean pre27853 = (openide == null || openide.compareTo(new SpecificationVersion("1.3.12")) < 0); // NOI18N
755
for (Dependency dep : deps) {
756                 // Extend this for other classpath modules:
757
if (dep.getType() == Dependency.TYPE_MODULE &&
758                         dep.getName().equals("org.netbeans.core.startup/1")) { // NOI18N
759
// Various modules (incl. o.n.core) dep on this as friends and need to access it.
760
s.add("org/netbeans/core/startup/"); // NOI18N
761
} else if (pre27853 && dep.getType() == Dependency.TYPE_MODULE) {
762                     // Module dependency. If a package was kosher for A and B depends
763
// on A, we let B use it undeclared. Cf. javacvs -> vcscore & RE.
764
// But #27853: only do this for old modules.
765
String JavaDoc name = dep.getName();
766                     int idx = name.indexOf('/');
767                     if (idx != -1) {
768                         name = name.substring(0, idx);
769                     }
770                     Module other = mgr.get(name);
771                     if (other == null) throw new IllegalStateException JavaDoc("Should have found dep " + dep + " from " + m); // NOI18N
772
s.addAll(findKosher(other));
773                 } else if (dep.getType() == Dependency.TYPE_PACKAGE) {
774                     String JavaDoc depname = dep.getName();
775                     String JavaDoc req;
776                     int idx = depname.indexOf('['); // NOI18N
777
if (idx == -1) {
778                         // depname = org.apache.xerces.parsers
779
// req = org/apache/xerces/parsers/
780
req = depname.replace('.', '/').concat("/"); // NOI18N
781
} else if (idx == 0) {
782                         // depname = [org.apache.xerces.parsers.DOMParser]
783
// req = org/apache/xerces/parsers/
784
int idx2 = depname.lastIndexOf('.');
785                         req = depname.substring(1, idx2).replace('.', '/').concat("/"); // NOI18N
786
} else {
787                         // depname = org.apache.xerces.parsers[DOMParser]
788
// req = org/apache/xerces/parsers/
789
req = depname.substring(0, idx).replace('.', '/').concat("/"); // NOI18N
790
}
791                     for (String JavaDoc cppkg : CLASSPATH_PACKAGES) {
792                         if (req.startsWith(cppkg)) {
793                             // Module requested this exact package or some subpackage or
794
// a class in one of these packages; it is kosher.
795
s.add(cppkg);
796                         }
797                     }
798                 }
799             }
800             if (s.isEmpty()) s = Collections.<String JavaDoc>emptySet();
801             kosherPackages.put(m, s);
802         }
803         return s;
804     }
805
806     /** true if optimizations of openide classloading shall be disabled.
807      */

808     private static boolean withoutOptimizations;
809     /** Whenever an openide module is enabled, it is checked to be
810      * on for whose we provide optimizations.
811      */

812     static void openideModuleEnabled(Module module) {
813         String JavaDoc m = module.getCodeNameBase();
814         if (!m.startsWith("org.openide.")) {
815             return;
816         }
817         
818         if ("org.openide.util".equals(m)) return; // NOI18N
819
if ("org.openide.actions".equals(m)) return; // NOI18N
820
if ("org.openide.awt".equals(m)) return; // NOI18N
821
if ("org.openide.modules".equals(m)) return; // NOI18N
822
if ("org.openide.nodes".equals(m)) return; // NOI18N
823
if ("org.openide.windows".equals(m)) return; // NOI18N
824
if ("org.openide.explorer".equals(m)) return; // NOI18N
825
if ("org.openide.util.enumerations".equals(m)) return; // NOI18N
826
if ("org.openide.execution".equals(m)) return; // NOI18N
827
if ("org.openide.options".equals(m)) return; // NOI18N
828
if ("org.openide.execution".equals(m)) return; // NOI18N
829
if ("org.openide.loaders".equals(m)) return; // NOI18N
830
if ("org.openide.dialogs".equals(m)) return; // NOI18N
831
if ("org.openide.filesystems".equals(m)) return; // NOI18N
832
if ("org.openide.io".equals(m)) return; // NOI18N
833
if ("org.openide.text".equals(m)) return; // NOI18N
834
if ("org.openide.src".equals(m)) return; // NOI18N
835

836         Util.err.warning("Disabling openide load optimizations due to use of " + m); // NOI18N
837

838         withoutOptimizations = true;
839     }
840     
841     /** Information about contents of some JARs on the startup classpath (both lib/ and lib/ext/).
842      * The first item in each is a prefix for JAR filenames.
843      * For a JAR matching such a prefix, the remaining items are entries
844      * from CLASSPATH_PACKAGES.
845      * <p>In this case, the JAR is only on a module's effective classpath in case
846      * {@link #findKosher} reports that at least one of the packages is requested -
847      * in which case that package and any descendants, but not other packages in the JAR,
848      * are included in the effective classpath.
849      * <p>JARs which are not listed here but are in lib/ or lib/ext/ are assumed to be
850      * in the effective classpath of every module, automatically.
851      * <p>Note that updater.jar is *not* on the startup classpath and is explicitly
852      * added to autoupdate.jar via Class-Path, so it need not concern us here -
853      * it should appear on the effective classpath of autoupdate only.
854      * @see "#22466"
855      */

856     private static final String JavaDoc[][] CLASSPATH_JARS = {
857         {"core", "org/netbeans/core/", "org/netbeans/beaninfo/"}, // NOI18N
858
// No one ought to be using boot.jar:
859
{"boot"}, // NOI18N
860
};
861     
862     /** These packages have been refactored into several modules.
863      * Disable the domain cache for them.
864      */

865     public boolean isSpecialResource(String JavaDoc pkg) {
866         // JST-PENDING here is experimental enumeration of shared packages in openide
867
// maybe this will speed up startup, but it is not accurate as if
868
// someone enables some long time deprecated openide jar list will
869
// get wrong.
870
// Probably I need to watch for list of modules that export org.openide
871
// or subclass and if longer than expected, disable this optimization
872

873         // the old good way:
874
if (pkg.startsWith("org/openide/")) {
875             
876             // util & dialogs
877
if ("org/openide/".equals (pkg)) return true; // NOI18N
878
// loaders, actions, and who know what else
879
if ("org/openide/actions/".equals (pkg)) return true; // NOI18N
880
// loaders & awt
881
if ("org/openide/awt/".equals (pkg)) return true; // NOI18N
882
// loaders, nodes, text...
883
if ("org/openide/cookies/".equals (pkg)) return true; // NOI18N
884

885             // some are provided by java/srcmodel
886
if ("org/openide/explorer/propertysheet/editors/".equals (pkg)) return true; // NOI18N
887

888             // windows & io
889
if ("org/openide/windows/".equals (pkg)) return true; // NOI18N
890

891             // text & loaders
892
if ("org/openide/text/".equals (pkg)) return true; // NOI18N
893

894             // util & nodes
895
if ("org/openide/util/actions/".equals (pkg)) return true; // NOI18N
896

897             if (withoutOptimizations) {
898                 // these should be removed as soon as we get rid of org-openide-compat
899
if ("org/openide/explorer/".equals (pkg)) return true; // NOI18N
900
if ("org/openide/util/".equals (pkg)) return true; // NOI18N
901
}
902         }
903
904         if (isSpecialResourceFromSystemProperty(pkg)) {
905             return true;
906         }
907         
908         // Some classes like DOMError are only in xerces.jar, not in JDK:
909
if (pkg.equals("org/w3c/dom/")) return true; // NOI18N
910
// #36578: JDK 1.5 has DOM3
911
if (pkg.equals("org/w3c/dom/ls/")) return true; // NOI18N
912
return super.isSpecialResource(pkg);
913     }
914     
915     /**
916      * Checks the passed in package for having a prefix one
917      * of the strings specified in the system property
918      * "org.netbeans.core.startup.specialResource".
919      */

920     private boolean isSpecialResourceFromSystemProperty(String JavaDoc pkg) {
921         for (String JavaDoc prefix : getSpecialResourcePrefixes()) {
922             if (pkg.startsWith(prefix)) {
923                 return true;
924             }
925         }
926         return false;
927     }
928     
929     /**
930      * Reads the system property
931      * "org.netbeans.core.startup.specialResource"
932      * and extracts the comma separated entries in it.
933      */

934     private static String JavaDoc[] getSpecialResourcePrefixes() {
935         if (specialResourcePrefixes == null) {
936             String JavaDoc sysProp = System.getProperty("org.netbeans.core.startup.specialResource");
937             if (sysProp != null) {
938                 specialResourcePrefixes = sysProp.split(",");
939             } else {
940                 specialResourcePrefixes = new String JavaDoc[0];
941             }
942         }
943         return specialResourcePrefixes;
944     }
945     
946     /** Get the effective "classpath" used by a module.
947      * Specific syntax: classpath entries as usual, but
948      * they may be qualified with packages, e.g.:
949      * <code>/path/to/api-module.jar[org.netbeans.api.foo.*,org.netbeans.spi.foo.**]:/path/to/wide-open.jar</code>
950      * @see ModuleSystem#getEffectiveClasspath
951      * @see "#22466"
952      */

953     String JavaDoc getEffectiveClasspath(Module m) {
954         if (!m.isEnabled()) {
955             // For disabled modules, we do not know for sure what it can load.
956
return ""; // NOI18N
957
}
958         // The classpath entries - each is a filename possibly followed by package qualifications.
959
List JavaDoc<String JavaDoc> l = new ArrayList JavaDoc<String JavaDoc>(100);
960         // Start with boot classpath.
961
createBootClassPath(l);
962         // Move on to "startup classpath", qualified by applicable package deps etc.
963
// Fixed classpath modules don't get restricted in this way.
964
Set JavaDoc<String JavaDoc> kosher = m.isFixed() ? null : findKosher(m);
965         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(System.getProperty("java.class.path", ""), File.pathSeparator);
966         while (tok.hasMoreTokens()) {
967             addStartupClasspathEntry(new File JavaDoc(tok.nextToken()), l, kosher);
968         }
969         // See org.netbeans.Main for actual computation of the dynamic classpath.
970
tok = new StringTokenizer JavaDoc(System.getProperty("netbeans.dynamic.classpath", ""), File.pathSeparator);
971         while (tok.hasMoreTokens()) {
972             addStartupClasspathEntry(new File JavaDoc(tok.nextToken()), l, kosher);
973         }
974         // Finally include this module and its dependencies recursively.
975
// Modules whose direct classpath has already been added to the list:
976
Set JavaDoc<Module> modulesConsidered = new HashSet JavaDoc<Module>(50);
977         // Code names of modules on which this module has an impl dependency
978
// (so can use any package):
979
Set JavaDoc<String JavaDoc> implDeps = new HashSet JavaDoc<String JavaDoc>(10);
980         for (Dependency dep : m.getDependencies()) {
981             // Remember, provide-require deps do not affect classpath!
982
if (dep.getType() == Dependency.TYPE_MODULE && dep.getComparison() == Dependency.COMPARE_IMPL) {
983                 // We can assume the impl dep has the correct version;
984
// otherwise this module could not have been enabled to begin with.
985
implDeps.add(dep.getName());
986             }
987         }
988         SpecificationVersion openide = Util.getModuleDep(m.getDependencies(), "org.openide"); // NOI18N
989
boolean pre27853 = (openide == null || openide.compareTo(new SpecificationVersion("1.3.12")) < 0); // NOI18N
990
// #27853: only make recursive for old modules.
991
addModuleClasspathEntries(m, m, modulesConsidered, implDeps, l, pre27853 ? Integer.MAX_VALUE : 1);
992         // Done, package it all up as a string.
993
StringBuilder JavaDoc buf = new StringBuilder JavaDoc(l.size() * 100 + 1);
994         for (String JavaDoc s: l) {
995             if (buf.length() > 0) {
996                 buf.append(File.pathSeparatorChar);
997             }
998             buf.append(s);
999         }
1000        return buf.toString();
1001    }
1002    
1003    // Copied from NbClassPath:
1004
private static void createBootClassPath(List JavaDoc<String JavaDoc> l) {
1005        // boot
1006
String JavaDoc boot = System.getProperty("sun.boot.class.path"); // NOI18N
1007
if (boot != null) {
1008            StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(boot, File.pathSeparator);
1009            while (tok.hasMoreTokens()) {
1010                l.add(tok.nextToken());
1011            }
1012        }
1013        
1014        // std extensions
1015
String JavaDoc extensions = System.getProperty("java.ext.dirs"); // NOI18N
1016
if (extensions != null) {
1017            for (StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(extensions, File.pathSeparator); st.hasMoreTokens();) {
1018                File JavaDoc dir = new File JavaDoc(st.nextToken());
1019                File JavaDoc[] entries = dir.listFiles();
1020                if (entries != null) {
1021                    for (File JavaDoc f : entries) {
1022                        String JavaDoc name = f.getName().toLowerCase(Locale.US);
1023                        if (name.endsWith(".zip") || name.endsWith(".jar")) { // NOI18N
1024
l.add(f.getAbsolutePath());
1025                        }
1026                    }
1027                }
1028            }
1029        }
1030    }
1031    
1032    /** Add a classpath entry from the lib/ or lib/ext/ dirs, if appropriate.
1033     * @param entry a classpath entry; either a directory, or a JAR file which might be controlled
1034     * @param cp the classpath (<code>List&lt;String&gt;</code>) to add to
1035     * @param kosher known packages which may be accessed (<code>Set&lt;String&gt;</code>), or null for no restrictions
1036     * @see "#22466"
1037     */

1038    private static void addStartupClasspathEntry(File JavaDoc cpEntry, List JavaDoc<String JavaDoc> cp, Set JavaDoc<String JavaDoc> kosher) {
1039        if (cpEntry.isDirectory()) {
1040            cp.add(cpEntry.getAbsolutePath());
1041            return;
1042        }
1043        // JAR or ZIP. Check whether we can access it.
1044
String JavaDoc name = cpEntry.getName();
1045        for (String JavaDoc[] cpjar : CLASSPATH_JARS) {
1046            if (kosher != null && name.startsWith(cpjar[0])) {
1047                // Restricted JAR.
1048
StringBuffer JavaDoc entry = null; // will be set if there are any packages
1049
for (int k = 1; k < cpjar.length; k++) {
1050                    String JavaDoc pkg = cpjar[k];
1051                    if (kosher.contains(pkg)) {
1052                        // Module is permitted to use this package.
1053
if (entry == null) {
1054                            entry = new StringBuffer JavaDoc(100);
1055                            entry.append(cpEntry.getAbsolutePath());
1056                            entry.append('['); // NOI18N
1057
} else {
1058                            entry.append(','); // NOI18N
1059
}
1060                        // pkg format: org/foo/bar/
1061
entry.append(pkg.replace('/', '.')); // NOI18N
1062
entry.append("**"); // NOI18N
1063
}
1064                }
1065                if (entry != null) {
1066                    // Had >= 1 packages available from this JAR.
1067
entry.append(']'); // NOI18N
1068
cp.add(entry.toString());
1069                }
1070                return;
1071            }
1072        }
1073        // Did not match any, assumed to be on classpath.
1074
cp.add(cpEntry.getAbsolutePath());
1075    }
1076
1077    /** Recursively build a classpath based on the module dependency tree.
1078     * @param m the current module whose JAR(s) might be added to the classpath
1079     * @param orig the module whose classpath we are ultimately trying to compute
1080     * @param considered modules we have already handled
1081     * @param implDeps module code names on which the orig module has an impl dep
1082     * @param cp the classpath to add to
1083     * @param depth the recursion depth to go to: 0 = only m's JAR, 1 = m + its parents, ..., MAX_INT = full recursion
1084     * @see "#22466"
1085     */

1086    private void addModuleClasspathEntries(Module m, Module orig, Set JavaDoc<Module> considered, Set JavaDoc<String JavaDoc> implDeps, List JavaDoc<String JavaDoc> cp, int depth) {
1087        // Head recursion so that baser modules are added to the front of the classpath:
1088
if (!considered.add(m)) return;
1089        for (Dependency dep : m.getDependencies()) {
1090            if (dep.getType() == Dependency.TYPE_MODULE) {
1091                String JavaDoc cnb = (String JavaDoc) Util.parseCodeName(dep.getName())[0];
1092                Module next = mgr.get(cnb);
1093                if (next == null) throw new IllegalStateException JavaDoc("No such module: " + cnb); // NOI18N
1094
if (depth > 0) {
1095                    addModuleClasspathEntries(next, orig, considered, implDeps, cp, depth - 1);
1096                }
1097            }
1098        }
1099        // Now add entries from m, if applicable.
1100
// We are friendly if we are considering the original module itself,
1101
// or if the original module has a (direct) impl dep on this module.
1102
boolean friend = (m == orig) || implDeps.contains(m.getCodeName());
1103        // Friend modules export everything to the classpath.
1104
// Otherwise, there may or may not be package restrictions.
1105
Module.PackageExport[] exports = friend ? null : m.getPublicPackages();
1106        String JavaDoc qualification = ""; // NOI18N
1107
if (exports != null) {
1108            if (exports.length == 0) {
1109                // Everything is blocked.
1110
return;
1111            }
1112            // Only certain packages are exported, so list them explicitly.
1113
StringBuffer JavaDoc b = new StringBuffer JavaDoc(100);
1114            b.append('['); // NOI18N
1115
for (int i = 0; i < exports.length; i++) {
1116                if (i > 0) {
1117                    b.append(','); // NOI18N
1118
}
1119                b.append(exports[i].pkg.replace('/', '.')); // NOI18N
1120
b.append(exports[i].recursive ? "**" : "*"); // NOI18N
1121
}
1122            b.append(']'); // NOI18N
1123
qualification = b.toString();
1124        }
1125        for (File JavaDoc jar : m.getAllJars()) {
1126            cp.add(jar.getAbsolutePath() + qualification);
1127        }
1128    }
1129    
1130    // Manifest caching: #26786.
1131

1132    /** The actual file where the manifest cache is stored,
1133     * or null if it will not be used.
1134     * Binary format:
1135     * Sequence of records, one per cached manifest; no particular order.
1136     * Record:
1137     * 1. Absolute JAR path, UTF-8.
1138     * 2. Null byte.
1139     * 3. Last modification time of JAR, System.currentTimeMillis format, big-endian (8-byte long).
1140     * 4. The manifest body.
1141     * 5. Null byte.
1142     */

1143    private File JavaDoc manifestCacheFile;
1144    
1145    /** While true, try to use the manifest cache.
1146     * So (non-reloadable) JARs scanned during startup will have their manifests cached.
1147     * After the primary set of modules has been scanned, this will be set to false.
1148     * Initially true, unless -J-Dnetbeans.cache.manifests=false is specified,
1149     * or there is no available cache directory.
1150     */

1151    private boolean usingManifestCache;
1152
1153    {
1154        usingManifestCache = Boolean.valueOf(System.getProperty("netbeans.cache.manifests", "true")).booleanValue();
1155        if (usingManifestCache) {
1156            String JavaDoc userdir = System.getProperty("netbeans.user");
1157            if (userdir != null) {
1158                manifestCacheFile = new File JavaDoc(new File JavaDoc(new File JavaDoc(new File JavaDoc (userdir), "var"), "cache"), "all-manifests.dat"); // NOI18N
1159
Util.err.fine("Using manifest cache in " + manifestCacheFile);
1160            } else {
1161                // Some special startup mode, e.g. with Plain.
1162
usingManifestCache = false;
1163                Util.err.fine("Not using any manifest cache; no user directory");
1164            }
1165        } else {
1166            Util.err.fine("Manifest cache disabled");
1167        }
1168    }
1169    
1170    /** Cache of known JAR manifests.
1171     * Initially null. If the cache is read, it may be used to quickly serve JAR manifests.
1172     * Each JAR file is mapped to a two-element array consisting of
1173     * its modification date when last read; and the manifest itself.
1174     */

1175    private Map JavaDoc<File JavaDoc,/*[Date,Manifest]*/Object JavaDoc[]> manifestCache = null;
1176    
1177    /** If true, at least one manifest has had to be read explicitly.
1178     * This might be because the cache did not initially exist;
1179     * or the JAR was not present in the cache;
1180     * or the JAR was present but did not match the cache timestamp.
1181     */

1182    private boolean manifestCacheDirty = false;
1183    
1184    // XXX consider logging using Events
1185

1186    /** Overrides superclass method to keep a cache of module manifests,
1187     * so that their JARs do not have to be opened twice during startup.
1188     */

1189    public Manifest JavaDoc loadManifest(File JavaDoc jar) throws IOException JavaDoc {
1190        if (!usingManifestCache) {
1191            return super.loadManifest(jar);
1192        }
1193        if (manifestCache == null) {
1194            manifestCache = loadManifestCache(manifestCacheFile);
1195        }
1196        Object JavaDoc[] entry = manifestCache.get(jar);
1197        if (entry != null) {
1198            if (((Date JavaDoc)entry[0]).getTime() == jar.lastModified()) {
1199                // Cache hit.
1200
Util.err.fine("Found manifest for " + jar + " in cache");
1201                return (Manifest JavaDoc)entry[1];
1202            } else {
1203                Util.err.fine("Wrong timestamp for " + jar + " in manifest cache");
1204            }
1205        } else {
1206            Util.err.fine("No entry for " + jar + " in manifest cache");
1207        }
1208        // Cache miss.
1209
Manifest JavaDoc m = super.loadManifest(jar);
1210        // (If that threw IOException, we leave it out of the cache.)
1211
manifestCache.put(jar, new Object JavaDoc[] {new Date JavaDoc(jar.lastModified()), m});
1212        manifestCacheDirty = true;
1213        return m;
1214    }
1215    
1216    /** If the manifest cache had been in use, and is now dirty, write it to disk.
1217     */

1218    private void maybeSaveManifestCache() {
1219        if (usingManifestCache && manifestCacheDirty) {
1220            try {
1221                saveManifestCache(manifestCache, manifestCacheFile);
1222            } catch (IOException JavaDoc ioe) {
1223                Util.err.log(Level.WARNING, null, ioe);
1224            }
1225            usingManifestCache = false;
1226            manifestCacheDirty = false;
1227            manifestCache = null;
1228            manifestCacheFile = null;
1229        }
1230    }
1231    
1232    /** Really save the cache.
1233     * @see #manifestCacheFile
1234     */

1235    private void saveManifestCache(Map JavaDoc<File JavaDoc,Object JavaDoc[]> manifestCache, File JavaDoc manifestCacheFile) throws IOException JavaDoc {
1236        Util.err.fine("Saving manifest cache");
1237        manifestCacheFile.getParentFile().mkdirs();
1238        OutputStream JavaDoc os = new FileOutputStream JavaDoc(manifestCacheFile);
1239        try {
1240            try {
1241                os = new BufferedOutputStream JavaDoc(os);
1242                for (Map.Entry JavaDoc<File JavaDoc,Object JavaDoc[]> entry : manifestCache.entrySet()) {
1243                    File JavaDoc jar = entry.getKey();
1244                    Object JavaDoc[] v = entry.getValue();
1245                    long time = ((Date JavaDoc)v[0]).getTime();
1246                    Manifest JavaDoc m = (Manifest JavaDoc)v[1];
1247                    os.write(jar.getAbsolutePath().getBytes("UTF-8")); // NOI18N
1248
os.write(0);
1249                    for (int i = 7; i >= 0; i--) {
1250                        os.write((int)((time >> (i * 8)) & 0xFF));
1251                    }
1252                    m.write(os);
1253                    os.write(0);
1254                }
1255            } finally {
1256                os.close();
1257            }
1258        } catch (IOException JavaDoc ioe) {
1259            // Do not leave behind a bogus half-written file.
1260
manifestCacheFile.delete();
1261            throw ioe;
1262        }
1263    }
1264    
1265    /** Load the cache if present.
1266     * If not present, or there are problems with it,
1267     * just create an empty cache.
1268     * @see #manifestCacheFile
1269     */

1270    private Map JavaDoc<File JavaDoc,Object JavaDoc[]> loadManifestCache(File JavaDoc manifestCacheFile) {
1271        if (!manifestCacheFile.canRead()) {
1272            Util.err.fine("No manifest cache found at " + manifestCacheFile);
1273            return new HashMap JavaDoc<File JavaDoc,Object JavaDoc[]>(200);
1274        }
1275        ev.log(Events.PERF_START, "NbInstaller - loadManifestCache"); // NOI18N
1276
try {
1277            InputStream JavaDoc is = new FileInputStream JavaDoc(manifestCacheFile);
1278            try {
1279                BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(is);
1280                Map JavaDoc<File JavaDoc,Object JavaDoc[]> m = new HashMap JavaDoc<File JavaDoc,Object JavaDoc[]>(200);
1281                ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc((int)manifestCacheFile.length());
1282                FileUtil.copy(bis, baos);
1283                byte[] data = baos.toByteArray();
1284                readManifestCacheEntries(data, m);
1285                return m;
1286            } finally {
1287                is.close();
1288                ev.log(Events.PERF_END, "NbInstaller - loadManifestCache"); // NOI18N
1289
}
1290        } catch (IOException JavaDoc ioe) {
1291            Util.err.log(Level.WARNING, "While reading: " + manifestCacheFile, ioe); // NOI18N
1292
return new HashMap JavaDoc<File JavaDoc,Object JavaDoc[]>(200);
1293        }
1294    }
1295    
1296    private static int findNullByte(byte[] data, int start) {
1297        int len = data.length;
1298        for (int i = start; i < len; i++) {
1299            if (data[i] == 0) {
1300                return i;
1301            }
1302        }
1303        return -1;
1304    }
1305    
1306    private static void readManifestCacheEntries(byte[] data, Map JavaDoc<File JavaDoc,Object JavaDoc[]> m) throws IOException JavaDoc {
1307        int pos = 0;
1308        while (true) {
1309            if (pos == data.length) {
1310                return;
1311            }
1312            int end = findNullByte(data, pos);
1313            if (end == -1) throw new IOException JavaDoc("Could not find next manifest JAR name from " + pos); // NOI18N
1314
File JavaDoc jar = new File JavaDoc(new String JavaDoc(data, pos, end - pos, "UTF-8")); // NOI18N
1315
long time = 0L;
1316            if (end + 8 >= data.length) throw new IOException JavaDoc("Ran out of space for timestamp for " + jar); // NOI18N
1317
for (int i = 0; i < 8; i++) {
1318                long b = data[end + i + 1];
1319                if (b < 0) b += 256;
1320                int exponent = 7 - i;
1321                long addin = b << (exponent * 8);
1322                time |= addin;
1323                //System.err.println("i=" + i + " b=0x" + Long.toHexString(b) + " exponent=" + exponent + " addin=0x" + Long.toHexString(addin) + " time=0x" + Long.toHexString(time));
1324
}
1325            pos = end + 9;
1326            end = findNullByte(data, pos);
1327            if (end == -1) throw new IOException JavaDoc("Could not find manifest body for " + jar); // NOI18N
1328
Manifest JavaDoc mani;
1329            try {
1330                mani = new Manifest JavaDoc(new ByteArrayInputStream JavaDoc(data, pos, end - pos));
1331            } catch (IOException JavaDoc ioe) {
1332                Exceptions.attachMessage(ioe, "While in entry for " + jar);
1333                throw ioe;
1334            }
1335            m.put(jar, new Object JavaDoc[] {new Date JavaDoc(time), mani});
1336            if (Util.err.isLoggable(Level.FINE)) {
1337                Util.err.fine("Manifest cache entry: jar=" + jar + " date=" + new Date JavaDoc(time) + " codename=" + mani.getMainAttributes().getValue("OpenIDE-Module"));
1338            }
1339            pos = end + 1;
1340        }
1341    }
1342    
1343    /** Check all module classes to make sure there are no unresolvable compile-time
1344     * dependencies. Turn on this mode with
1345     * <code>-J-Dnetbeans.preresolve.classes=true</code>
1346     * May be more useful to run org.netbeans.core.ValidateClassLinkageTest.
1347     * @param modules a list of modules, newly enabled, to check; fixed modules will be ignored
1348     */

1349    private void preresolveClasses(List JavaDoc<Module> modules) {
1350        Util.err.info("Pre-resolving classes for all loaded modules...be sure you have not specified -J-Xverify:none in ide.cfg");
1351        for (Module m : modules) {
1352            if (m.isFixed()) continue;
1353            if (m.getJarFile() == null) continue;
1354            File JavaDoc jar = m.getJarFile();
1355            // Note: extension JARs not checked.
1356
try {
1357                JarFile JavaDoc j = new JarFile JavaDoc(jar, true);
1358                try {
1359                    for (JarEntry JavaDoc entry : NbCollections.iterable(j.entries())) {
1360                        String JavaDoc name = entry.getName();
1361                        if (name.endsWith(".class")) { // NOI18N
1362
String JavaDoc clazz = name.substring(0, name.length() - 6).replace('/', '.'); // NOI18N
1363
Throwable JavaDoc t = null;
1364                            try {
1365                                Class.forName(clazz, false, m.getClassLoader());
1366                            } catch (ClassNotFoundException JavaDoc cnfe) { // e.g. "Will not load classes from default package" from ProxyClassLoader
1367
t = cnfe;
1368                            } catch (LinkageError JavaDoc le) {
1369                                t = le;
1370                            } catch (RuntimeException JavaDoc re) { // e.g. IllegalArgumentException from package defs
1371
t = re;
1372                            }
1373                            if (t != null) {
1374                                Util.err.log(Level.WARNING, "From " + clazz + " in " + m.getCodeNameBase() + " with effective classpath " + getEffectiveClasspath(m), t);
1375                            }
1376                        }
1377                    }
1378                } finally {
1379                    j.close();
1380                }
1381            } catch (IOException JavaDoc ioe) {
1382                Util.err.log(Level.WARNING, null, ioe);
1383            }
1384        }
1385    }
1386
1387}
1388
Popular Tags