KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > ModuleManager


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;
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.security.AllPermission JavaDoc;
27 import java.security.CodeSource JavaDoc;
28 import java.security.PermissionCollection JavaDoc;
29 import java.security.Permissions JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Collections JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.LinkedList JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Set JavaDoc;
40 import java.util.StringTokenizer JavaDoc;
41 import java.util.jar.JarFile JavaDoc;
42 import java.util.jar.Manifest JavaDoc;
43 import java.util.logging.Level JavaDoc;
44 import org.openide.modules.Dependency;
45 import org.openide.modules.ModuleInfo;
46 import org.openide.modules.SpecificationVersion;
47 import org.openide.util.Lookup;
48 import org.openide.util.Mutex;
49 import org.openide.util.TopologicalSortException;
50 import org.openide.util.Union2;
51 import org.openide.util.Utilities;
52
53 /** Manages a collection of modules.
54  * Must use {@link #mutex} to access its important methods.
55  * @author Jesse Glick
56  */

57 public final class ModuleManager {
58
59     public static final String JavaDoc PROP_MODULES = "modules"; // NOI18N
60
public static final String JavaDoc PROP_ENABLED_MODULES = "enabledModules"; // NOI18N
61
public static final String JavaDoc PROP_CLASS_LOADER = "classLoader"; // NOI18N
62

63     // JST-PENDING: Document in arch. used in org.netbeans.core.startup tests
64
// For unit testing only:
65
static boolean PRINT_TOPOLOGICAL_EXCEPTION_STACK_TRACES = !Boolean.getBoolean ("suppress.topological.exception"); // NOI18N
66

67     // the modules being managed (not all need be installed)
68
private final Set JavaDoc<Module> modules = new HashSet JavaDoc<Module>(100);
69     // the same, indexed by code name base
70
private final Map JavaDoc<String JavaDoc,Module> modulesByName = new HashMap JavaDoc<String JavaDoc,Module>(100);
71
72     // for any module, set of known failed dependencies or problems,
73
// or null if this has not been computed yet
74
private final Map JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>> moduleProblemsWithoutNeeds = new HashMap JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>>(100);
75     private final Map JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>> moduleProblemsWithNeeds = new HashMap JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>>(100);
76
77     // modules providing a given requires token; set may never be empty
78
private final Map JavaDoc<String JavaDoc,Set JavaDoc<Module>> providersOf = new HashMap JavaDoc<String JavaDoc,Set JavaDoc<Module>>(25);
79
80     private final ModuleInstaller installer;
81     private ModuleFactory moduleFactory;
82
83     private SystemClassLoader classLoader;
84     private List JavaDoc<Union2<File JavaDoc,JarFile JavaDoc>> classLoaderPatches;
85     private final Object JavaDoc classLoaderLock = new String JavaDoc("ModuleManager.classLoaderLock"); // NOI18N
86

87     private final Events ev;
88
89     /** Create a manager, initially with no managed modules.
90      * The handler for installing modules is given.
91      * Also the sink for event messages must be given.
92      */

93     public ModuleManager(ModuleInstaller installer, Events ev) {
94         this.installer = installer;
95         this.ev = ev;
96         String JavaDoc patches = System.getProperty("netbeans.systemclassloader.patches");
97         if (patches != null) {
98             // Probably temporary helper for XTest. By setting this system property
99
// to a classpath (list of directories and JARs separated by the normal
100
// path separator) you may append to the system class loader.
101
System.err.println("System class loader patches: " + patches); // NOI18N
102
classLoaderPatches = new ArrayList JavaDoc<Union2<File JavaDoc,JarFile JavaDoc>>();
103             StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(patches, File.pathSeparator);
104             while (tok.hasMoreTokens()) {
105                 File JavaDoc f = new File JavaDoc(tok.nextToken());
106                 if (f.isDirectory()) {
107                     classLoaderPatches.add(Union2.<File JavaDoc,JarFile JavaDoc>createFirst(f));
108                 } else {
109                     try {
110                         classLoaderPatches.add(Union2.<File JavaDoc,JarFile JavaDoc>createSecond(new JarFile JavaDoc(f)));
111                     } catch (IOException JavaDoc ioe) {
112                         Util.err.log(Level.WARNING, "Problematic file: " + f, ioe);
113                     }
114                 }
115             }
116         } else {
117             // Normal case.
118
classLoaderPatches = Collections.emptyList();
119         }
120         classLoader = new SystemClassLoader(classLoaderPatches, new ClassLoader JavaDoc[] {installer.getClass ().getClassLoader()}, Collections.<Module>emptySet());
121         updateContextClassLoaders(classLoader, true);
122
123         moduleFactory = Lookup.getDefault().lookup(ModuleFactory.class);
124         if (moduleFactory == null) {
125             moduleFactory = new ModuleFactory();
126         }
127     }
128
129     /** Access for ManifestSection.
130      * @since JST-PENDING needed by ManifestSection
131      */

132     public final Events getEvents() {
133         return ev;
134     }
135
136     private final Mutex.Privileged MUTEX_PRIVILEGED = new Mutex.Privileged();
137     private final Mutex MUTEX = new Mutex(MUTEX_PRIVILEGED);
138     /** Get a locking mutex for this module installer.
139      * All calls other than adding or removing property change
140      * listeners, or getting the module lookup, called on this
141      * class must be done within the scope of this mutex
142      * (with read or write access as appropriate). Methods
143      * on ModuleInfo need not be called within it; methods
144      * specifically on Module do need to be called within it
145      * (read access is sufficient). Note that property changes
146      * are fired with read access already held for convenience.
147      * Please avoid entering the mutex from "sensitive" threads
148      * such as the event thread, the folder recognizer/lookup
149      * thread, etc., or with other locks held (such as the Children
150      * mutex), especially when entering the mutex as a writer:
151      * actions such as enabling modules in particular can call
152      * arbitrary foreign module code which may do a number of
153      * strange things (including consuming a significant amount of
154      * time and waiting for other tasks such as lookup or data
155      * object recognition). Use the request processor or the IDE's
156      * main startup thread or the execution engine to be safe.
157      */

158     public final Mutex mutex() {
159         return MUTEX;
160     }
161     /** Classes in this package can, if careful, use the privileged form.
162      * @since JST-PENDING this had to be made public as the package is now split in two
163      */

164     public final Mutex.Privileged mutexPrivileged() {
165         return MUTEX_PRIVILEGED;
166     }
167     // [PENDING] with improved API for Mutex, could throw
168
// IllegalStateException if any thread attempts to call
169
// a controlled method without holding the proper mutex lock
170

171     /** Manages changes accumulating in this manager and fires them when ready.
172      */

173     private ChangeFirer firer = new ChangeFirer(this);
174     /** True while firer is firing changes.
175      */

176     private boolean readOnly = false;
177     /** Sets the r/o flag. Access from ChangeFirer.
178      * @param ro if true, cannot make any changes until set to false again
179      */

180     void readOnly(boolean ro) {
181         readOnly = ro;
182     }
183     /** Assert that the current thread state permits writing.
184      * Currently does not check that there is a write mutex!
185      * (Pending #13352.)
186      * But does check that I am not firing changes.
187      * @throws IllegalThreadStateException if currently firing changes
188      */

189     void assertWritable() throws IllegalThreadStateException JavaDoc {
190         if (readOnly) {
191             throw new IllegalThreadStateException JavaDoc("You are attempting to make changes to " + this + " in a property change callback. This is illegal. You may only make module system changes while holding a write mutex and not inside a change callback. See #16328."); // NOI18N
192
}
193     }
194
195     private PropertyChangeSupport JavaDoc changeSupport;
196
197     /** Add a change listener.
198      * Only the declared properties will be fired, and they are
199      * not guaranteed to be fired synchronously with the change
200      * (currently they are not in fact, for safety). The change
201      * events are not guaranteed to provide an old and new value,
202      * so you will need to use the proper
203      * getter methods. When the changes are fired, you are inside
204      * the mutex with read access.
205      */

206     public final void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
207         synchronized (this) {
208             if (changeSupport == null)
209                 changeSupport = new PropertyChangeSupport JavaDoc(this);
210         }
211         changeSupport.addPropertyChangeListener(l);
212     }
213
214     /** Remove a change listener. */
215     public final void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
216         if (changeSupport != null)
217             changeSupport.removePropertyChangeListener(l);
218     }
219
220     // Access from ChangeFirer:
221
final void firePropertyChange(String JavaDoc prop, Object JavaDoc old, Object JavaDoc nue) {
222         if (Util.err.isLoggable(Level.FINE)) {
223             Util.err.fine("ModuleManager.propertyChange: " + prop + ": " + old + " -> " + nue);
224         }
225         if (changeSupport != null)
226             changeSupport.firePropertyChange(prop, old, nue);
227     }
228
229     /** For access from Module. */
230     final void fireReloadable(Module m) {
231         firer.change(new ChangeFirer.Change(m, Module.PROP_RELOADABLE, null, null));
232         firer.fire();
233     }
234
235     private final Util.ModuleLookup lookup = new Util.ModuleLookup();
236     /** Retrieve set of modules in Lookup form.
237      * The core top manager should install this into the set of
238      * available lookups. Will fire lookup events when the
239      * set of modules changes (not for enabling/disabling/etc.).
240      * No other subsystem should make any attempt to provide an instance of
241      * ModuleInfo via lookup, so an optimization could be to jump
242      * straight to this lookup when ModuleInfo/Module is requested.
243      */

244     public Lookup getModuleLookup() {
245         return lookup;
246     }
247     // Access from ChangeFirer:
248
final void fireModulesCreatedDeleted(Set JavaDoc created, Set JavaDoc deleted) {
249         Util.err.fine("lookup created: " + created + " deleted: " + deleted);
250         lookup.changed();
251     }
252
253     /** Get a set of {@link Module}s being managed.
254      * No two contained modules may at any time share the same code name base.
255      * @see #PROP_MODULES
256      */

257     public Set JavaDoc<Module> getModules() {
258         return new HashSet JavaDoc<Module>(modules);
259     }
260
261     /** Get a set of modules managed which are currently enabled.
262      * Convenience method only.
263      * @see #PROP_ENABLED_MODULES
264      */

265     public final Set JavaDoc<Module> getEnabledModules() {
266         Set JavaDoc<Module> s = new HashSet JavaDoc<Module>(modules);
267         Iterator JavaDoc<Module> it = s.iterator();
268         while (it.hasNext()) {
269             Module m = it.next();
270             if (! m.isEnabled()) {
271                 it.remove();
272             }
273         }
274         return s;
275     }
276
277     /** Convenience method to find a module by name.
278      * Returns null if there is no such managed module.
279      */

280     public final Module get(String JavaDoc codeNameBase) {
281         return modulesByName.get(codeNameBase);
282     }
283
284     /**
285      * Get a set of modules depended upon or depending on this module.
286      * <p>Note that provide-require dependencies are listed alongside direct
287      * dependencies; a module with a required token is considered to depend on
288      * <em>all</em> modules providing that token (though in fact only one is needed
289      * to enable it).
290      * <p>Illegal cyclic dependencies are omitted.
291      * @param m a module to start from; may be enabled or not, but must be owned by this manager
292      * @param reverse if true, find modules depending on this module; if false, find
293      * modules this module depends upon
294      * @param transitive if true, these dependencies are considered transitively as well
295      * @return a set (possibly empty) of modules managed by this manager, never including m
296      * @since org.netbeans.core/1 > 1.17
297      */

298     public Set JavaDoc/*<Module>*/ getModuleInterdependencies(Module m, boolean reverse, boolean transitive) {
299         return Util.moduleInterdependencies(m, reverse, transitive, modules, modulesByName, providersOf);
300     }
301
302     /** Get a classloader capable of loading from any
303      * of the enabled modules or their declared extensions.
304      * Should be used as the result of TopManager.systemClassLoader.
305      * Thread-safe.
306      * @see #PROP_CLASS_LOADER
307      */

308     public ClassLoader JavaDoc getClassLoader() {
309         // #16265: should not require mutex to get at. Many pieces of the IDE
310
// require the correct result immediately.
311
synchronized (classLoaderLock) {
312             return classLoader;
313         }
314     }
315
316     /** Mark the current class loader as invalid and make a new one. */
317     private void invalidateClassLoader() {
318         synchronized (classLoaderLock) {
319             classLoader.destroy(); // probably has no effect, but just in case...
320
}
321         // Set, not List, because if we have >1 bootstrap module (using Plain),
322
// it is likely that some of these classloaders will overlap.
323
Set JavaDoc<ClassLoader JavaDoc> foundParents = new HashSet JavaDoc<ClassLoader JavaDoc>(modules.size() * 4 / 3 + 2);
324         List JavaDoc<ClassLoader JavaDoc> parents = new ArrayList JavaDoc<ClassLoader JavaDoc>(modules.size() + 1);
325         ClassLoader JavaDoc base = ModuleManager.class.getClassLoader();
326         foundParents.add(base);
327         parents.add(base);
328         for (Module m : modules) {
329             if (! m.isEnabled()) {
330                 continue;
331             }
332             if (foundParents.add(m.getClassLoader())) {
333                 parents.add(m.getClassLoader());
334             }
335         }
336         if (moduleFactory.removeBaseClassLoader()) {
337             parents.remove(base);
338         }
339         ClassLoader JavaDoc[] parentCLs = parents.toArray(new ClassLoader JavaDoc[parents.size()]);
340         SystemClassLoader nue;
341         try {
342             nue = new SystemClassLoader(classLoaderPatches, parentCLs, modules);
343         } catch (IllegalArgumentException JavaDoc iae) {
344             Util.err.log(Level.WARNING, null, iae);
345             nue = new SystemClassLoader(classLoaderPatches, new ClassLoader JavaDoc[] {ModuleManager.class.getClassLoader()}, Collections.<Module>emptySet());
346         }
347         synchronized (classLoaderLock) {
348             classLoader = nue;
349             updateContextClassLoaders(classLoader, false);
350         }
351         firer.change(new ChangeFirer.Change(this, PROP_CLASS_LOADER, null, null));
352     }
353     private static void updateContextClassLoaders(ClassLoader JavaDoc l, boolean force) {
354         // See #20663.
355
ThreadGroup JavaDoc g = Thread.currentThread().getThreadGroup();
356         while (g.getParent() != null) g = g.getParent();
357         // Now g is the master thread group, hopefully.
358
// See #4097747 for an explanation of the convoluted logic.
359
while (true) {
360             int s = g.activeCount() + 1;
361             Thread JavaDoc[] ts = new Thread JavaDoc[s];
362             int x = g.enumerate(ts, true);
363             if (x < s) {
364                 // We got all of the threads, good.
365
for (int i = 0; i < x; i++) {
366                     // The first time when we make the manager, set all of the
367
// threads to have this context classloader. Let's hope no
368
// threads needing a special context loader have been started
369
// yet! On subsequent occasions, when the classloader
370
// changes, we update all threads for which setContextClassLoader
371
// has not been called with some other special classloader.
372
if (force || (ts[i].getContextClassLoader() instanceof SystemClassLoader)) {
373                         //Util.err.fine("Setting ctxt CL on " + ts[i].getName() + " to " + l);
374
ts[i].setContextClassLoader(l);
375                     } else {
376                         Util.err.fine("Not touching context class loader " + ts[i].getContextClassLoader() + " on thread " + ts[i].getName());
377                     }
378                 }
379                 Util.err.fine("Set context class loader on " + x + " threads");
380                 break;
381             } else {
382                 Util.err.fine("Race condition getting all threads, restarting...");
383                 continue;
384             }
385         }
386     }
387
388     /** A classloader giving access to all the module classloaders at once. */
389     private final class SystemClassLoader extends JarClassLoader {
390
391         private final PermissionCollection JavaDoc allPermissions;
392         private final StringBuffer JavaDoc debugme;
393         private boolean empty = true;
394
395         public SystemClassLoader(List JavaDoc<Union2<File JavaDoc,JarFile JavaDoc>> files, ClassLoader JavaDoc[] parents, Set JavaDoc<Module> modules) throws IllegalArgumentException JavaDoc {
396             super(files, parents, false);
397             allPermissions = new Permissions JavaDoc();
398             allPermissions.add(new AllPermission JavaDoc());
399             allPermissions.setReadOnly();
400             debugme = new StringBuffer JavaDoc(100 + 50 * modules.size());
401             debugme.append("SystemClassLoader["); // NOI18N
402
for (Union2<File JavaDoc,JarFile JavaDoc> file : files) {
403                 if (empty) {
404                     empty = false;
405                 } else {
406                     debugme.append(','); // NOI18N
407
}
408                 debugme.append(file.hasFirst() ? file.first().getAbsolutePath() : file.second().getName());
409             }
410             record(modules);
411             debugme.append(']'); // NOI18N
412
}
413
414         private void record(Collection JavaDoc<Module> modules) {
415         for (Module m: modules) {
416                 if (empty) {
417                     empty = false;
418                 } else {
419                     debugme.append(','); // NOI18N
420
}
421                 debugme.append(m.getCodeNameBase());
422             }
423         }
424
425         public void append(ClassLoader JavaDoc[] ls, List JavaDoc<Module> modules) throws IllegalArgumentException JavaDoc {
426             super.append(ls);
427             debugme.deleteCharAt(debugme.length() - 1);
428             record(modules);
429             debugme.append(']'); // NOI18N
430
}
431
432         protected void finalize() throws Throwable JavaDoc {
433             super.finalize();
434             Util.err.fine("Collected system class loader");
435         }
436
437         public String JavaDoc toString() {
438             if (debugme == null) {
439                 return "SystemClassLoader";
440             }
441             return debugme.toString();
442         }
443
444         protected boolean isSpecialResource(String JavaDoc pkg) {
445             if (installer.isSpecialResource(pkg)) {
446                 return true;
447             }
448             return super.isSpecialResource(pkg);
449         }
450
451         /** Provide all permissions for any code loaded from the files list
452          * (i.e. with netbeans.systemclassloader.patches).
453          */

454         protected PermissionCollection JavaDoc getPermissions(CodeSource JavaDoc cs) {
455             return allPermissions;
456         }
457
458     }
459
460     /** @see #create(File,Object,boolean,boolean,boolean)
461      * @deprecated since org.netbeans.core/1 1.3
462      */

463     @Deprecated JavaDoc
464     public Module create(File JavaDoc jar, Object JavaDoc history, boolean reloadable, boolean autoload) throws IOException JavaDoc, DuplicateException {
465         return create(jar, history, reloadable, autoload, false);
466     }
467
468     /** Create a module from a JAR and add it to the managed set.
469      * Will initially be disabled, unless it is eager and can
470      * be enabled immediately.
471      * May throw an IOException if the JAR file cannot be opened
472      * for some reason, or is malformed.
473      * If there is already a module of the same name managed,
474      * throws a duplicate exception. In this case you may wish
475      * to delete the original and try again.
476      * You must give it some history object which can be used
477      * to provide context for where the module came from and
478      * whether it has been here before.
479      * You cannot request that a module be both autoload and eager.
480      */

481     public Module create(File JavaDoc jar, Object JavaDoc history, boolean reloadable, boolean autoload, boolean eager) throws IOException JavaDoc, DuplicateException {
482         assertWritable();
483         ev.log(Events.START_CREATE_REGULAR_MODULE, jar);
484         Module m = moduleFactory.create(jar.getAbsoluteFile(),
485                         history, reloadable, autoload, eager, this, ev);
486         ev.log(Events.FINISH_CREATE_REGULAR_MODULE, jar);
487         subCreate(m);
488         if (m.isEager()) {
489             List JavaDoc<Module> immediate = simulateEnable(Collections.<Module>emptySet());
490             if (!immediate.isEmpty()) {
491                 if (!immediate.contains(m)) throw new IllegalStateException JavaDoc("Can immediately enable modules " + immediate + ", but not including " + m); // NOI18N
492
boolean ok = true;
493         for (Module other: immediate) {
494                     if (!other.isAutoload() && !other.isEager()) {
495                         // Nope, would require a real module to be turned on first.
496
ok = false;
497                         break;
498                     }
499                 }
500                 if (ok) {
501                     Util.err.fine("Enabling " + m + " immediately");
502                     enable(Collections.<Module>emptySet());
503                 }
504             }
505         }
506         return m;
507     }
508
509     /** Create a fixed module (e.g. from classpath).
510      * Will initially be disabled.
511      */

512     public Module createFixed(Manifest JavaDoc mani, Object JavaDoc history, ClassLoader JavaDoc loader) throws InvalidException, DuplicateException {
513         assertWritable();
514         if (mani == null || loader == null) throw new IllegalArgumentException JavaDoc("null manifest or loader"); // NOI18N
515
ev.log(Events.START_CREATE_BOOT_MODULE, history);
516         Module m = moduleFactory.createFixed(mani, history, loader, this, ev);
517         ev.log(Events.FINISH_CREATE_BOOT_MODULE, history);
518         subCreate(m);
519         return m;
520     }
521
522     /** Used by Module to communicate with the ModuleInstaller re. dependencies. */
523     void refineDependencies(Module m, Set JavaDoc<Dependency> dependencies) {
524         installer.refineDependencies(m, dependencies);
525     }
526     /** Allows the installer to add provides (used to provide name of platform we run on)
527      */

528     String JavaDoc[] refineProvides (Module m) {
529         return installer.refineProvides (m);
530     }
531     /** Used by Module to communicate with the ModuleInstaller re. classloader. */
532     public void refineClassLoader(Module m, List JavaDoc parents) {
533         // #27853:
534
installer.refineClassLoader(m, parents);
535     }
536     /** Use by OneModuleClassLoader to communicate with the ModuleInstaller re. masking. */
537     public boolean shouldDelegateResource(Module m, Module parent, String JavaDoc pkg) {
538         // Cf. #19621:
539
Module.PackageExport[] exports = (parent == null) ? null : parent.getPublicPackages();
540         if (exports != null) {
541             //Util.err.fine("exports=" + Arrays.asList(exports));
542
// Packages from parent are restricted: #19621.
543
boolean exported = false;
544             if (parent.isDeclaredAsFriend(m)) { // meaning public to all, or at least to me
545
for (int i = 0; i < exports.length; i++) {
546                     if (exports[i].recursive ? pkg.startsWith(exports[i].pkg) : pkg.equals(exports[i].pkg)) {
547                         //Util.err.fine("matches " + exports[i]);
548
exported = true;
549                         break;
550                     }
551                 }
552             }
553             if (!exported) {
554                 // This package is not public. m must have a direct impl-version
555
// dependency on parent or it has no right to use this package.
556
boolean impldep = false;
557                 Dependency[] deps = m.getDependenciesArray();
558                 for (int i = 0; i < deps.length; i++) {
559                     if (deps[i].getType() == Dependency.TYPE_MODULE &&
560                             deps[i].getComparison() == Dependency.COMPARE_IMPL &&
561                             deps[i].getName().equals(parent.getCodeName())) {
562                         impldep = true;
563                         //Util.err.fine("impldep in " + deps[i]);
564
break;
565                     }
566                 }
567                 if (!impldep) {
568                     // This module cannot use the package, sorry! It's private.
569
//Util.err.fine("forbidden");
570
if (Util.err.isLoggable(Level.FINE)) {
571                         // Note that this is usually harmless. Typical case: Introspector.getBeanInfo
572
// is called on some module-supplied class; this looks in the module's classloader
573
// for org.netbeans.beaninfo.ModuleClassBeanInfo, which of course would not be
574
// found anyway.
575
Util.err.fine("Refusing to load non-public package " + pkg + " for " + m + " from parent module " + parent + " without an impl dependency");
576                     }
577                     return false;
578                 }
579                 //Util.err.fine("impl dep");
580
}
581             //Util.err.fine("exported");
582
}
583         if (pkg.startsWith("META-INF/")) { // NOI18N
584
// Modules should not make direct reference to metainfo dirs of
585
// other modules. Don't bother logging it, however.
586
return false;
587         }
588         // The installer can perform additional checks:
589
return installer.shouldDelegateResource(m, parent, pkg);
590     }
591     public boolean isSpecialResource(String JavaDoc pkg) {
592         return installer.isSpecialResource(pkg);
593     }
594     // Again, access from Module to ModuleInstaller:
595
Manifest JavaDoc loadManifest(File JavaDoc jar) throws IOException JavaDoc {
596         return installer.loadManifest(jar);
597     }
598
599     private void subCreate(Module m) throws DuplicateException {
600         Util.err.fine("created: " + m);
601         Module old = get(m.getCodeNameBase());
602         if (old != null) {
603             throw new DuplicateException(old, m);
604         }
605         modules.add(m);
606         modulesByName.put(m.getCodeNameBase(), m);
607         possibleProviderAdded(m);
608         lookup.add(m);
609         firer.created(m);
610         firer.change(new ChangeFirer.Change(this, PROP_MODULES, null, null));
611         // It might have been that some other modules were thought to be missing
612
// dependencies only because they needed this one. And other modules still
613
// might have depended on this one, etc. So forget any cached info about
614
// problems arising from inter-module dependencies.
615
clearProblemCache();
616         firer.fire();
617     }
618     private void possibleProviderAdded(Module m) {
619         String JavaDoc[] provides = m.getProvides();
620         for (int i = 0; i < provides.length; i++) {
621             Set JavaDoc<Module> providing = providersOf.get(provides[i]);
622             if (providing == null) {
623                 providing = new HashSet JavaDoc<Module>(16);
624                 providersOf.put(provides[i], providing);
625             }
626             providing.add(m);
627         }
628     }
629
630     /** Remove a module from the managed set.
631      * Must be disabled first.
632      * Must not be a "fixed" module.
633      */

634     public void delete(Module m) throws IllegalArgumentException JavaDoc {
635         assertWritable();
636         if (m.isFixed()) throw new IllegalArgumentException JavaDoc("fixed module: " + m); // NOI18N
637
if (m.isEnabled()) throw new IllegalArgumentException JavaDoc("enabled module: " + m); // NOI18N
638
ev.log(Events.DELETE_MODULE, m);
639         modules.remove(m);
640         modulesByName.remove(m.getCodeNameBase());
641         possibleProviderRemoved(m);
642         lookup.remove(m);
643         firer.deleted(m);
644         firer.change(new ChangeFirer.Change(this, PROP_MODULES, null, null));
645         firer.change(new ChangeFirer.Change(m, Module.PROP_VALID, Boolean.TRUE, Boolean.FALSE));
646         // #14561: some other module might now be uninstallable as a result.
647
clearProblemCache();
648         m.destroy();
649         firer.fire();
650     }
651     private void possibleProviderRemoved(Module m) {
652         for (String JavaDoc token : m.getProvides()) {
653             Set JavaDoc<Module> providing = providersOf.get(token);
654             if (providing != null) {
655                 providing.remove(m);
656                 if (providing.isEmpty()) {
657                     providersOf.remove(token);
658                 }
659             } else {
660                 // Else we called reload and m.reload threw IOException, so
661
// it has already removed its provider list
662
}
663         }
664     }
665
666     /** Reload a module.
667      * This could make a fresh copy of its JAR file preparing
668      * to enable it with different contents; at least it will
669      * rescan the manifest.
670      * It must currently be disabled and not "fixed", and it will
671      * stay disabled after this call; to actually reinstall it
672      * requires a separate call.
673      * It may or may not actually be marked "reloadable", but
674      * for greatest reliability it should be.
675      * Besides actually reloading the contents, any cached information
676      * about failed dependencies or runtime problems with the module
677      * is cleared so it may be tried again.
678      */

679     public void reload(Module m) throws IllegalArgumentException JavaDoc, IOException JavaDoc {
680         assertWritable();
681         // No Events, not a user- nor performance-interesting action.
682
Util.err.fine("reload: " + m);
683         if (m.isFixed()) throw new IllegalArgumentException JavaDoc("reload fixed module: " + m); // NOI18N
684
if (m.isEnabled()) throw new IllegalArgumentException JavaDoc("reload enabled module: " + m); // NOI18N
685
possibleProviderRemoved(m);
686         try {
687             m.reload();
688         } catch (IOException JavaDoc ioe) {
689             // Module is trash, remove it from our list and pass on the exception.
690
delete(m);
691             throw ioe;
692         }
693         possibleProviderAdded(m);
694         firer.change(new ChangeFirer.Change(m, Module.PROP_MANIFEST, null, null));
695         // Some problem with this module may now have gone away. In turn, some
696
// other modules may now no longer have problems. So clear the cache
697
// of "soft" problems (interdependencies between modules).
698
// Also clear any "hard" problems associated with this module, as they
699
// may now have been fixed.
700
moduleProblemsWithoutNeeds.remove(m);
701         moduleProblemsWithNeeds.remove(m);
702         firer.change(new ChangeFirer.Change(m, Module.PROP_PROBLEMS, null, null));
703         clearProblemCache();
704         firer.fire();
705     }
706
707     /** Enable a single module.
708      * Must have satisfied its dependencies.
709      * Must not be an autoload module, when supported.
710      */

711     public final void enable(Module m) throws IllegalArgumentException JavaDoc, InvalidException {
712         enable(Collections.singleton(m));
713     }
714
715     /** Disable a single module.
716      * Must not be required by any enabled modules.
717      * Must not be an autoload module, when supported.
718      */

719     public final void disable(Module m) throws IllegalArgumentException JavaDoc {
720         disable(Collections.singleton(m));
721     }
722
723     /** Enable a set of modules together.
724      * Must have satisfied their dependencies
725      * (possibly with one another).
726      * Must not contain autoload nor eager modules.
727      * Might contain fixed modules (they can only be installed once of course).
728      * It is permissible to pass in modules which in fact at runtime cannot
729      * satisfy their package dependencies, or which {@link ModuleInstaller#prepare}
730      * rejects on the basis of missing contents. In such a case InvalidException
731      * will be thrown and nothing will be installed. The InvalidException in such
732      * a case should contain a reference to the offending module.
733      */

734     public void enable(Set JavaDoc<Module> modules) throws IllegalArgumentException JavaDoc, InvalidException {
735         assertWritable();
736         Util.err.fine("enable: " + modules);
737         /* Consider eager modules:
738         if (modules.isEmpty()) {
739             return;
740         }
741          */

742         ev.log(Events.PERF_START, "ModuleManager.enable"); // NOI18N
743
// Basic problems will be caught here, and we also get the autoloads:
744
List JavaDoc<Module> toEnable = simulateEnable(modules);
745         ev.log(Events.PERF_TICK, "checked the required ordering and autoloads"); // NOI18N
746

747         Util.err.fine("enable: toEnable=" + toEnable); // NOI18N
748
{
749             // Verify that we are cool as far as basic dependencies go.
750
Set JavaDoc<Module> testing = new HashSet JavaDoc<Module>(toEnable);
751             if (! testing.containsAll(modules)) {
752                 Set JavaDoc<Module> bogus = new HashSet JavaDoc<Module>(modules);
753                 bogus.removeAll(testing);
754                 throw new IllegalArgumentException JavaDoc("Not all requested modules can be enabled: " + bogus); // NOI18N
755
}
756             Iterator JavaDoc it = testing.iterator();
757             while (it.hasNext()) {
758                 Module m = (Module)it.next();
759                 if (!modules.contains(m) && !m.isAutoload() && !m.isEager()) {
760                     throw new IllegalArgumentException JavaDoc("Would also need to enable " + m); // NOI18N
761
}
762             }
763         }
764         Util.err.fine("enable: verified dependencies");
765         ev.log(Events.PERF_TICK, "verified dependencies"); // NOI18N
766

767         ev.log(Events.START_ENABLE_MODULES, toEnable);
768         {
769             // Actually turn on the listed modules.
770
// List of modules that need to be "rolled back".
771
LinkedList JavaDoc<Module> fallback = new LinkedList JavaDoc<Module>();
772             // Whether we were attempting to bring a classloader up.
773
// This affects whether we need to rollback that change on the
774
// problem module or not.
775
boolean tryingClassLoaderUp = false;
776             // If a failure due to package dep occurs, store it here.
777
Dependency failedPackageDep = null;
778             try {
779                 ev.log(Events.PERF_START, "module preparation" ); // NOI18N
780
for (Module m: toEnable) {
781                     fallback.addFirst(m);
782                     Util.err.fine("enable: bringing up: " + m);
783                     ev.log(Events.PERF_START, "bringing up classloader on " + m.getCodeName() ); // NOI18N
784
try {
785                         // Calculate the parents to initialize the classloader with.
786
Dependency[] dependencies = m.getDependenciesArray();
787                         Set JavaDoc<Module> parents = new HashSet JavaDoc<Module>(dependencies.length * 4 / 3 + 1);
788                         for (int i = 0; i < dependencies.length; i++) {
789                             Dependency dep = dependencies[i];
790                             if (dep.getType() != Dependency.TYPE_MODULE) {
791                                 // Token providers do *not* go into the parent classloader
792
// list. The providing module must have been turned on first.
793
// But you cannot automatically access classes from it.
794
continue;
795                             }
796                             String JavaDoc name = (String JavaDoc)Util.parseCodeName(dep.getName())[0];
797                             Module parent = get(name);
798                             // Should not happen:
799
if (parent == null) throw new IOException JavaDoc("Parent " + name + " not found!"); // NOI18N
800
parents.add(parent);
801                         }
802                         m.classLoaderUp(parents);
803                     } catch (IOException JavaDoc ioe) {
804                         tryingClassLoaderUp = true;
805                         InvalidException ie = new InvalidException(m, ioe.toString());
806                         ie.initCause(ioe);
807                         throw ie;
808                     }
809                     m.setEnabled(true);
810                     ev.log(Events.PERF_END, "bringing up classloader on " + m.getCodeName() ); // NOI18N
811
// Check package dependencies.
812
ev.log(Events.PERF_START, "package dependency check on " + m.getCodeName() ); // NOI18N
813
Util.err.fine("enable: checking package dependencies for " + m);
814                     Dependency[] dependencies = m.getDependenciesArray();
815                     for (int i = 0; i < dependencies.length; i++) {
816                         Dependency dep = dependencies[i];
817                         if (dep.getType() != Dependency.TYPE_PACKAGE) {
818                             continue;
819                         }
820                         if (! Util.checkPackageDependency(dep, m.getClassLoader())) {
821                             failedPackageDep = dep;
822                             throw new InvalidException(m, "Dependency failed on " + dep); // NOI18N
823
}
824                         Util.err.fine("Successful check for: " + dep);
825                     }
826                     ev.log(Events.PERF_END, "package dependency check on " + m.getCodeName() ); // NOI18N
827
// Prepare to load it.
828
ev.log(Events.PERF_START, "ModuleInstaller.prepare " + m.getCodeName() ); // NOI18N
829
installer.prepare(m);
830                     ev.log(Events.PERF_END, "ModuleInstaller.prepare " + m.getCodeName() ); // NOI18N
831
}
832                 ev.log(Events.PERF_END, "module preparation" ); // NOI18N
833

834             } catch (InvalidException ie) {
835                 // Remember that there was a problem with this guy.
836
Module bad = ie.getModule();
837                 if (bad == null) throw new IllegalStateException JavaDoc("Problem with no associated module: " + ie); // NOI18N
838
Set JavaDoc<Union2<Dependency,InvalidException>> probs = moduleProblemsWithNeeds.get(bad);
839                 if (probs == null) throw new IllegalStateException JavaDoc("Were trying to install a module that had never been checked: " + bad); // NOI18N
840
if (! probs.isEmpty()) throw new IllegalStateException JavaDoc("Were trying to install a module that was known to be bad: " + bad); // NOI18N
841
// Record for posterity.
842
if (failedPackageDep != null) {
843                     // Structured package dependency failed, track this.
844
probs.add(Union2.<Dependency,InvalidException>createFirst(failedPackageDep));
845                 } else {
846                     // Some other problem (exception).
847
probs.add(Union2.<Dependency,InvalidException>createSecond(ie));
848                 }
849                 // Other modules may have depended on this one and now will not be OK.
850
// So clear all "soft" problems from the cache.
851
// Remember, the problem we just added will be left alone, only
852
// inter-module dependencies will be cleared.
853
clearProblemCache();
854                 // #14560: this one definitely changed its set of problems.
855
firer.change(new ChangeFirer.Change(bad, Module.PROP_PROBLEMS, Collections.EMPTY_SET, Collections.singleton("something"))); // NOI18N
856
// Rollback changes made so far before rethrowing.
857
Util.err.fine("enable: will roll back from: " + ie);
858                 Iterator JavaDoc fbIt = fallback.iterator();
859                 while (fbIt.hasNext()) {
860                     Module m = (Module)fbIt.next();
861                     if (m.isFixed()) {
862                         // cannot disable fixed modules
863
continue;
864                     }
865                     m.setEnabled(false);
866                     if (tryingClassLoaderUp) {
867                         // OK, taken into account for first module, others are up.
868
tryingClassLoaderUp = false;
869                     } else {
870                         m.classLoaderDown();
871                         System.gc();
872                         System.runFinalization();
873                         m.cleanup();
874                     }
875                 }
876                 firer.fire();
877                 throw ie;
878             }
879             // They all were OK so far; add to system classloader and install them.
880
if (classLoader != null) {
881                 Util.err.fine("enable: adding to system classloader");
882                 List JavaDoc<ClassLoader JavaDoc> nueclassloaders = new ArrayList JavaDoc<ClassLoader JavaDoc>(toEnable.size());
883                 Iterator JavaDoc<Module> teIt = toEnable.iterator();
884                 if (moduleFactory.removeBaseClassLoader()) {
885                     ClassLoader JavaDoc base = ModuleManager.class.getClassLoader();
886                     nueclassloaders.add(moduleFactory.getClasspathDelegateClassLoader(this, base));
887                     while (teIt.hasNext()) {
888                         ClassLoader JavaDoc c1 = teIt.next().getClassLoader();
889                         if (c1 != base) {
890                             nueclassloaders.add(c1);
891                         }
892                     }
893                 } else {
894                     while (teIt.hasNext()) {
895                         nueclassloaders.add(teIt.next().getClassLoader());
896                     }
897                 }
898                 classLoader.append((nueclassloaders.toArray(new ClassLoader JavaDoc[nueclassloaders.size()])), toEnable);
899             } else {
900                 Util.err.fine("enable: no class loader yet, not appending");
901             }
902             Util.err.fine("enable: continuing to installation");
903             installer.load(toEnable);
904         }
905         {
906             // Take care of notifying various changes.
907
Util.err.fine("enable: firing changes");
908             firer.change(new ChangeFirer.Change(this, PROP_ENABLED_MODULES, null, null));
909             // The class loader does not actually change as a result of this.
910
Iterator JavaDoc it = toEnable.iterator();
911             while (it.hasNext()) {
912                 Module m = (Module)it.next();
913                 firer.change(new ChangeFirer.Change(m, ModuleInfo.PROP_ENABLED, Boolean.FALSE, Boolean.TRUE));
914                 if (! m.isFixed()) {
915                     firer.change(new ChangeFirer.Change(m, Module.PROP_CLASS_LOADER, null, null));
916                 }
917             }
918         }
919         ev.log(Events.FINISH_ENABLE_MODULES, toEnable);
920         firer.fire();
921     }
922
923     /** Disable a set of modules together.
924      * Must not be required by any enabled
925      * modules (except one another).
926      * Must not contain autoload nor eager modules.
927      * Must not contain fixed modules.
928      */

929     public void disable(Set JavaDoc<Module> modules) throws IllegalArgumentException JavaDoc {
930         assertWritable();
931         Util.err.fine("disable: " + modules);
932         if (modules.isEmpty()) {
933             return;
934         }
935         // Checks for invalid items, plus includes autoloads to turn off.
936
List JavaDoc<Module> toDisable = simulateDisable(modules);
937         Util.err.fine("disable: toDisable=" + toDisable);
938         {
939             // Verify that dependencies are OK.
940
for (Module m: toDisable) {
941                 if (!modules.contains(m) && !m.isAutoload() && !m.isEager()) {
942                     throw new IllegalArgumentException JavaDoc("Would also need to disable: " + m); // NOI18N
943
}
944             }
945         }
946         Util.err.fine("disable: verified dependencies");
947         ev.log(Events.START_DISABLE_MODULES, toDisable);
948         {
949             // Actually turn off all modules.
950
installer.unload(toDisable);
951             Iterator JavaDoc it = toDisable.iterator();
952             while (it.hasNext()) {
953                 Module m = (Module)it.next();
954                 installer.dispose(m);
955                 m.setEnabled(false);
956                 m.classLoaderDown();
957             }
958             System.gc(); // hope OneModuleClassLoader.finalize() is called...
959
System.runFinalization();
960             // but probably it won't be. See #4405807.
961
it = toDisable.iterator();
962             while (it.hasNext()) {
963                 Module m = (Module)it.next();
964                 m.cleanup();
965             }
966         }
967         Util.err.fine("disable: finished, will notify changes");
968         {
969             // Notify various changes.
970
firer.change(new ChangeFirer.Change(this, PROP_ENABLED_MODULES, null, null));
971             // Class loader will change as a result.
972
invalidateClassLoader();
973             Iterator JavaDoc it = toDisable.iterator();
974             while (it.hasNext()) {
975                 Module m = (Module)it.next();
976                 firer.change(new ChangeFirer.Change(m, ModuleInfo.PROP_ENABLED, Boolean.TRUE, Boolean.FALSE));
977                 firer.change(new ChangeFirer.Change(m, Module.PROP_CLASS_LOADER, null, null));
978             }
979         }
980         ev.log(Events.FINISH_DISABLE_MODULES, toDisable);
981         firer.fire();
982     }
983
984     /** Simulate what would happen if a set of modules were to be enabled.
985      * None of the listed modules may be autoload modules, nor eager, nor currently enabled,
986      * though they may be fixed (if they have not yet been enabled).
987      * It may happen that some of them do not satisfy their dependencies.
988      * It may also happen that some of them require other, currently disabled,
989      * modules to be enabled in order for them to be enabled.
990      * It may further happen that some currently disabled eager modules could
991      * be enabled as a result of these modules being enabled.
992      * The returned set is the set of all modules that actually could be enabled.
993      * It will include the requested modules, minus any that cannot satisfy
994      * their dependencies (even on each other), plus any managed but currently
995      * disabled modules that would need to be enabled (including autoload modules
996      * required by some listed module but not by any currently enabled module),
997      * plus any eager modules which can be enabled with the other enablements
998      * (and possibly any autoloads needed by those eager modules).
999      * Where a requested module requires some token, either it will not be included
1000     * in the result (in case the dependency cannot be satisfied), or it will, and
1001     * all modules providing that token which can be included will be included, even
1002     * if it would suffice to choose only one - unless a module providing that token
1003     * is already enabled or in the requested list,
1004     * in which case just the requested module will be listed.
1005     * Modules are returned in an order in which they could be enabled (where
1006     * base modules are always enabled before dependent modules).
1007     * Note that the returned list might include modules which in fact cannot be
1008     * enabled either because some package dependencies (which are checked only
1009     * on a live classloader) cannot be met; or {@link ModuleInstaller#prepare}
1010     * indicates that the modules are not in a valid format to install; or
1011     * creating the module classloader fails unexpectedly.
1012     */

1013    public List JavaDoc<Module> simulateEnable(Set JavaDoc<Module> modules) throws IllegalArgumentException JavaDoc {
1014        /* Not quite, eager modules may change this:
1015        if (modules.isEmpty()) {
1016            return Collections.EMPTY_LIST;
1017        }
1018         */

1019        // XXX also optimize for modules.size == 1
1020
Set JavaDoc<Module> willEnable = new HashSet JavaDoc<Module>(modules.size() * 2 + 1);
1021        for (Module m: modules) {
1022            if (m.isAutoload()) throw new IllegalArgumentException JavaDoc("Cannot simulate enabling an autoload: " + m); // NOI18N
1023
if (m.isEager()) throw new IllegalArgumentException JavaDoc("Cannot simulate enabling an eager module: " + m); // NOI18N
1024
if (m.isEnabled()) throw new IllegalArgumentException JavaDoc("Already enabled: " + m); // NOI18N
1025
if (!m.isValid()) throw new IllegalArgumentException JavaDoc("Not managed by me: " + m + " in " + m); // NOI18N
1026
maybeAddToEnableList(willEnable, modules, m, true);
1027        }
1028        // XXX clumsy but should work:
1029
Set JavaDoc<Module> stillDisabled = new HashSet JavaDoc<Module>(this.modules);
1030        Iterator JavaDoc<Module> it = stillDisabled.iterator();
1031        while (it.hasNext()) {
1032            Module m = it.next();
1033            if (m.isEnabled() || willEnable.contains(m)) {
1034                it.remove();
1035            }
1036        }
1037        while (searchForPossibleEager(willEnable, stillDisabled, modules)) {/* search again */}
1038        Map JavaDoc<Module,List JavaDoc<Module>> deps = Util.moduleDependencies(willEnable, modulesByName, providersOf);
1039        try {
1040            List JavaDoc<Module> l = Utilities.topologicalSort(willEnable, deps);
1041            Collections.reverse(l);
1042            return l;
1043        } catch (TopologicalSortException ex) {
1044            // Some kind of cycle involving prov-req deps. Should be extremely rare.
1045
// Example (from random failures of MMT.testProvReqCycles):
1046
// m1 => {m2 | m3}
1047
// m2 => {m1 | m4}
1048
// m3 => {m1}
1049
// m4 => {}
1050
// Now consider:
1051
// sE(m2) = ?
1052
// [m4, m2] is fine.
1053
// [m4, m2, m1] would be OK too, but will result in TSE.
1054
// Do not know what to do here, actually, so give up.
1055
if (PRINT_TOPOLOGICAL_EXCEPTION_STACK_TRACES) {
1056                Util.err.log(Level.WARNING, null, ex);
1057            }
1058            Util.err.warning("Cyclic module dependencies, will refuse to enable: " + deps); // NOI18N
1059
return Collections.<Module>emptyList();
1060        }
1061    }
1062    private void maybeAddToEnableList(Set JavaDoc<Module> willEnable, Set JavaDoc<Module> mightEnable, Module m, boolean okToFail) {
1063        if (! missingDependencies(m).isEmpty()) {
1064            // Should never happen:
1065
if (! okToFail) throw new IllegalStateException JavaDoc("Module was supposed to be OK: " + m); // NOI18N
1066
// Cannot satisfy its dependencies, exclude it.
1067
return;
1068        }
1069        if (willEnable.contains(m)) {
1070            // Already there, done.
1071
return;
1072        }
1073        willEnable.add(m);
1074        // Also add anything it depends on, if not already there,
1075
// or already enabled.
1076
for (Dependency dep : m.getDependenciesArray()) {
1077            if (dep.getType() == Dependency.TYPE_MODULE) {
1078                String JavaDoc codeNameBase = (String JavaDoc)Util.parseCodeName(dep.getName())[0];
1079                Module other = get(codeNameBase);
1080                // Should never happen:
1081
if (other == null) throw new IllegalStateException JavaDoc("Should have found module: " + codeNameBase); // NOI18N
1082
if (! other.isEnabled()) {
1083                    maybeAddToEnableList(willEnable, mightEnable, other, false);
1084                }
1085            } else if (
1086                dep.getType() == Dependency.TYPE_REQUIRES ||
1087                dep.getType() == Dependency.TYPE_NEEDS ||
1088                dep.getType() == Dependency.TYPE_RECOMMENDS
1089            ) {
1090                Set JavaDoc<Module> providers = providersOf.get(dep.getName());
1091                if (providers == null) {
1092                    assert dep.getType() == Dependency.TYPE_RECOMMENDS : "Should have found a provider of " + dep;
1093                    continue;
1094                }
1095                // First check if >= 1 is already enabled or will be soon. If so, great.
1096
boolean foundOne = false;
1097                for (Module other : providers) {
1098                    if (other.isEnabled() ||
1099                            (other.getProblems().isEmpty() && mightEnable.contains(other))) {
1100                        foundOne = true;
1101                        break;
1102                    }
1103                }
1104                if (foundOne) {
1105                    // OK, we are satisfied.
1106
continue;
1107                }
1108                // All disabled. So add them all to the enable list.
1109
for (Module other : providers) {
1110                    // It is OK if one of them fails.
1111
maybeAddToEnableList(willEnable, mightEnable, other, true);
1112                    // But we still check to ensure that at least one did not!
1113
if (!foundOne && willEnable.contains(other)) {
1114                        foundOne = true;
1115                        // and continue with the others, try to add them too...
1116
}
1117                }
1118                // Logic is that missingDependencies(m) should contain dep in this case.
1119
assert foundOne || dep.getType() == Dependency.TYPE_RECOMMENDS : "Should have found a nonproblematic provider of " + dep + " among " + providers + " with willEnable=" + willEnable + " mightEnable=" + mightEnable;
1120            }
1121            // else some other kind of dependency that does not concern us
1122
}
1123    }
1124    private boolean searchForPossibleEager(Set JavaDoc<Module> willEnable, Set JavaDoc<Module> stillDisabled, Set JavaDoc<Module> mightEnable) {
1125        // Check for any eagers in stillDisabled which could be enabled based
1126
// on currently enabled modules and willEnable. For any such, remove from
1127
// stillDisabled and add to willEnable (using maybeAddToEnableList, so that
1128
// autoloads needed by them are picked up too). If any were found, return true.
1129
boolean found = false;
1130        Iterator JavaDoc<Module> it = stillDisabled.iterator();
1131    FIND_EAGER:
1132        while (it.hasNext()) {
1133            Module m = it.next();
1134            if (willEnable.contains(m)) {
1135                // Presumably real module M1, eager M2 dep. on M1, eager M3 dep.
1136
// on M2; already called couldBeEnabledWithEagers(M3) and it
1137
// added M3 to willEnable (thus M2 also) but only removed M3
1138
// from willEnable, so we should skip it now.
1139
it.remove();
1140                continue;
1141            }
1142            if (m.isEager()) {
1143                if (couldBeEnabledWithEagers(m, willEnable, new HashSet JavaDoc<Module>())) {
1144                    // Go for it!
1145
found = true;
1146                    it.remove();
1147                    maybeAddToEnableList(willEnable, mightEnable, m, false);
1148                }
1149            }
1150        }
1151        return found;
1152    }
1153    private boolean couldBeEnabledWithEagers(Module m, Set JavaDoc<Module> willEnable, Set JavaDoc<Module> recursion) {
1154        // True if a search of the dependencies of this module reveals
1155
// only modules which are currently enabled; in the willEnable
1156
// list; or are autoloads or eager modules for which this predicate
1157
// is recursively true.
1158
if (m.isEnabled() || willEnable.contains(m)) return true;
1159        if (!m.isAutoload() && !m.isEager()) return false;
1160        if (!m.getProblems().isEmpty()) return false;
1161        if (!recursion.add(m)) {
1162            // A cycle, they can enable one another...
1163
return true;
1164        }
1165        Dependency[] dependencies = m.getDependenciesArray();
1166        for (int i = 0; i < dependencies.length; i++) {
1167            Dependency dep = dependencies[i];
1168            if (dep.getType() == Dependency.TYPE_MODULE) {
1169                String JavaDoc codeNameBase = (String JavaDoc)Util.parseCodeName(dep.getName())[0];
1170                Module other = get(codeNameBase);
1171                // Should never happen:
1172
if (other == null) throw new IllegalStateException JavaDoc("Should have found module: " + codeNameBase); // NOI18N
1173
if (!couldBeEnabledWithEagers(other, willEnable, recursion)) return false;
1174            } else if (dep.getType() == Dependency.TYPE_REQUIRES) {
1175                Set JavaDoc<Module> providers = providersOf.get(dep.getName());
1176                if (providers == null) throw new IllegalStateException JavaDoc("Should have found a provider of: " + dep.getName()); // NOI18N
1177
// Just need *one* to match.
1178
boolean foundOne = false;
1179                for (Module other : providers) {
1180                    if (couldBeEnabledWithEagers(other, willEnable, recursion)) {
1181                        foundOne = true;
1182                        break;
1183                    }
1184                }
1185                if (!foundOne) return false;
1186            }
1187            // else some other dep type
1188
}
1189        return true;
1190    }
1191
1192    /** Simulate what would happen if a set of modules were to be disabled.
1193     * None of the listed modules may be autoload modules, nor eager, nor currently disabled, nor fixed.
1194     * The returned set will list all modules that would actually be disabled,
1195     * meaning the listed modules, plus any currently enabled but unlisted modules
1196     * (including autoloads) that require some listed modules, plus any autoloads
1197     * which would no longer be needed as they were only required by modules
1198     * otherwise disabled.
1199     * Provide-require pairs count for purposes of disablement: if the set of
1200     * requested modules includes all remaining enabled providers of some token,
1201     * and modules requiring that token will need to be disabled as well.
1202     * Modules are returned in an order in which they could be disabled (where
1203     * dependent modules are always disabled before base modules).
1204     */

1205    public List JavaDoc<Module> simulateDisable(Set JavaDoc<Module> modules) throws IllegalArgumentException JavaDoc {
1206        if (modules.isEmpty()) {
1207            return Collections.<Module>emptyList();
1208        }
1209        // XXX also optimize for modules.size == 1
1210
// Probably not a very efficient algorithm. But it probably does not need to be.
1211
Set JavaDoc<Module> willDisable = new HashSet JavaDoc<Module>(20);
1212        Iterator JavaDoc it = modules.iterator();
1213        while (it.hasNext()) {
1214            Module m = (Module)it.next();
1215            if (m.isAutoload()) throw new IllegalArgumentException JavaDoc("Cannot disable autoload: " + m); // NOI18N
1216
if (m.isEager()) throw new IllegalArgumentException JavaDoc("Cannot disable eager module: " + m); // NOI18N
1217
if (m.isFixed()) throw new IllegalArgumentException JavaDoc("Cannot disable fixed module: " + m); // NOI18N
1218
if (! m.isEnabled()) throw new IllegalArgumentException JavaDoc("Already disabled: " + m); // NOI18N
1219
addToDisableList(willDisable, m);
1220        }
1221        Set JavaDoc<Module> stillEnabled = new HashSet JavaDoc<Module>(getEnabledModules());
1222        stillEnabled.removeAll(willDisable);
1223        while (searchForUnusedAutoloads(willDisable, stillEnabled)) {/* search again */}
1224        Map JavaDoc<Module,List JavaDoc<Module>> deps = Util.moduleDependencies(willDisable, modulesByName, providersOf);
1225        try {
1226            return Utilities.topologicalSort(willDisable, deps);
1227        } catch (TopologicalSortException ex) {
1228            // Again, don't know what to do exactly, so give up and just turn them off.
1229
if (PRINT_TOPOLOGICAL_EXCEPTION_STACK_TRACES) {
1230                Util.err.log(Level.WARNING, null, ex);
1231            }
1232            Util.err.warning("Cyclic module dependencies, will turn them off in a random order: " + deps); // NOI18N
1233
return new ArrayList JavaDoc<Module>(willDisable);
1234        }
1235    }
1236    private void addToDisableList(Set JavaDoc<Module> willDisable, Module m) {
1237        if (willDisable.contains(m)) {
1238            // E.g. if original set had A then B, B depends on A.
1239
return;
1240        }
1241        willDisable.add(m);
1242        // Find any modules depending on this one which are currently enabled.
1243
// (And not already here.)
1244
// If there are any, add them.
1245
for (Module other : modules) {
1246            if (other.isFixed() || ! other.isEnabled() || willDisable.contains(other)) {
1247                continue;
1248            }
1249            Dependency[] depenencies = other.getDependenciesArray();
1250            for (int i = 0; i < depenencies.length; i++) {
1251                Dependency dep = depenencies[i];
1252                if (dep.getType() == Dependency.TYPE_MODULE) {
1253                    if (dep.getName().equals(m.getCodeName())) {
1254                        // Need to disable this one too.
1255
addToDisableList(willDisable, other);
1256                        // No need to scan the rest of its dependencies.
1257
break;
1258                    }
1259                } else if (
1260                    dep.getType() == Dependency.TYPE_REQUIRES ||
1261                    dep.getType() == Dependency.TYPE_NEEDS
1262                ) {
1263                    if (m.provides(dep.getName())) {
1264                        // Careful. There may be some third module still enabled which
1265
// provides this same token too.
1266
boolean foundOne = false;
1267            for (Module third: getEnabledModules()) {
1268                            if (third.isEnabled() &&
1269                                    !willDisable.contains(third) &&
1270                                    third.provides(dep.getName())) {
1271                                foundOne = true;
1272                                break;
1273                            }
1274                        }
1275                        if (!foundOne) {
1276                            // Nope, we were the only/last one to provide it.
1277
addToDisableList(willDisable, other);
1278                            break;
1279                        }
1280                    }
1281                }
1282                // else some other kind of dependency, we do not care
1283
}
1284        }
1285    }
1286    private boolean searchForUnusedAutoloads(Set JavaDoc<Module> willDisable, Set JavaDoc<Module> stillEnabled) {
1287        // Check for any autoloads in stillEnabled which are not used by anything else
1288
// in stillEnabled. For each such, remove it from stillEnabled and add
1289
// to willDisable. If any were found, return true.
1290
boolean found = false;
1291        Iterator JavaDoc<Module> it = stillEnabled.iterator();
1292    FIND_AUTOLOADS:
1293        while (it.hasNext()) {
1294            Module m = it.next();
1295            if (m.isAutoload()) {
1296                for (Module other: stillEnabled) {
1297                    Dependency[] dependencies = other.getDependenciesArray();
1298                    for (int i = 0; i < dependencies.length; i++) {
1299                        Dependency dep = dependencies[i];
1300                        if (dep.getType() == Dependency.TYPE_MODULE) {
1301                            if (dep.getName().equals(m.getCodeName())) {
1302                                // Still used, skip it.
1303
continue FIND_AUTOLOADS;
1304                            }
1305                        } else if (
1306                            dep.getType() == Dependency.TYPE_REQUIRES ||
1307                            dep.getType() == Dependency.TYPE_NEEDS ||
1308                            dep.getType() == Dependency.TYPE_RECOMMENDS
1309                        ) {
1310                            // Here we play it safe and leave autoloads on if they provide
1311
// something used by some module - even if technically it would
1312
// be possible to turn off the autoload because there is another
1313
// enabled module providing the same thing. Leave it on anyway.
1314
if (m.provides(dep.getName())) {
1315                                continue FIND_AUTOLOADS;
1316                            }
1317                        }
1318                        // else some other type
1319
}
1320                }
1321                // Nobody uses it!
1322
found = true;
1323                it.remove();
1324                willDisable.add(m);
1325            }
1326        }
1327        return found;
1328    }
1329
1330    // dummy object to be placed in the problem set while recursive checking is in progress
1331
private static final Union2<Dependency,InvalidException> PROBING_IN_PROCESS = Union2.createSecond(new InvalidException("PROBING_IN_PROCESS"));
1332    // Access from Module.getProblems, q.v.
1333
// The probed module must not be currently enabled or fixed.
1334
Set JavaDoc<Union2<Dependency,InvalidException>> missingDependencies(Module probed) {
1335        return missingDependencies(probed, true);
1336    }
1337    Set JavaDoc<Union2<Dependency,InvalidException>> missingDependencies(Module probed, boolean withNeeds) {
1338        // We need to synchronize here because though this method may be called
1339
// only within a read mutex, it can write to moduleProblems. Other places
1340
// where moduleProblems are used are write-mutex only and so do not have
1341
// to worry about contention.
1342
synchronized (moduleProblemsWithNeeds) {
1343            Map JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>> mP = (withNeeds ? moduleProblemsWithNeeds : moduleProblemsWithoutNeeds);
1344            Set JavaDoc<Union2<Dependency,InvalidException>> probs = mP.get(probed);
1345            if (probs == null) {
1346                probs = new HashSet JavaDoc<Union2<Dependency,InvalidException>>(8);
1347                if (withNeeds) {
1348                    probs.addAll(missingDependencies(probed, false));
1349                }
1350                probs.add(PROBING_IN_PROCESS);
1351                mP.put(probed, probs);
1352                for (Dependency dep : probed.getDependenciesArray()) {
1353                    if (dep.getType() == Dependency.TYPE_PACKAGE) {
1354                        // Can't check it in advance. Assume it is OK; if not
1355
// a problem will be indicated during an actual installation
1356
// attempt.
1357
} else if (dep.getType() == Dependency.TYPE_MODULE) {
1358                        // Look for the corresponding module.
1359
Object JavaDoc[] depParse = Util.parseCodeName(dep.getName());
1360                        String JavaDoc codeNameBase = (String JavaDoc)depParse[0];
1361                        int relVersionMin = (depParse[1] != null) ? ((Integer JavaDoc)depParse[1]).intValue() : -1;
1362                        int relVersionMax = (depParse[2] != null) ? ((Integer JavaDoc)depParse[2]).intValue() : relVersionMin;
1363                        Module other = get(codeNameBase);
1364                        if (other == null) {
1365                            // No such module, bad.
1366
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1367                            continue;
1368                        }
1369                        if (relVersionMin == relVersionMax) {
1370                            // Non-ranged dep.
1371
if (relVersionMin != other.getCodeNameRelease()) {
1372                                // Wrong major version, bad.
1373
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1374                                continue;
1375                            }
1376                            if (dep.getComparison() == Dependency.COMPARE_IMPL &&
1377                                    ! Utilities.compareObjects(dep.getVersion(),
1378                                          other.getImplementationVersion())) { // NOI18N
1379
// Wrong impl version, bad.
1380
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1381                                continue;
1382                            }
1383                            if (dep.getComparison() == Dependency.COMPARE_SPEC &&
1384                                    new SpecificationVersion(dep.getVersion()).compareTo(
1385                                        other.getSpecificationVersion()) > 0) {
1386                                // Spec version not high enough, bad.
1387
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1388                                continue;
1389                            }
1390                        } else if (relVersionMin < relVersionMax) {
1391                            // Ranged dep.
1392
int otherRel = other.getCodeNameRelease();
1393                            if (otherRel < relVersionMin || otherRel > relVersionMax) {
1394                                // Major version outside of range, bad.
1395
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1396                                continue;
1397                            }
1398                            if (dep.getComparison() == Dependency.COMPARE_IMPL) {
1399                                throw new IllegalStateException JavaDoc("No such thing as ranged impl dep"); // NOI18N
1400
}
1401                            if (dep.getComparison() == Dependency.COMPARE_SPEC &&
1402                                    // Spec comparisons only apply to the earliest major rel.
1403
otherRel == relVersionMin &&
1404                                    new SpecificationVersion(dep.getVersion()).compareTo(
1405                                        other.getSpecificationVersion()) > 0) {
1406                                // Spec version not high enough, bad.
1407
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1408                                continue;
1409                            }
1410                        } else {
1411                            throw new IllegalStateException JavaDoc("Upside-down rel vers range"); // NOI18N
1412
}
1413                        if (! other.isEnabled()) {
1414                            // Need to make sure the other one is not missing anything either.
1415
// Nor that it depends (directly on indirectly) on this one.
1416
if ((!withNeeds && !missingDependencies(other, false).isEmpty()) ||
1417                                    (withNeeds && !isAlmostEmpty(missingDependencies(other, true)))) {
1418                                // This is a little subtle. Either the other module had real
1419
// problems, in which case our dependency on it is not legit.
1420
// Or, the other actually depends cyclically on this one. In
1421
// that case, *it* would wind up calling missingDependencies
1422
// on this module, but this module has already put a nonempty
1423
// set in the mapping (containing at least the element
1424
// PROBING_IN_PROCESS), causing the other module to fail and
1425
// return a dependency on this module, causing this module to
1426
// also fail with a dependency on that module. In the process,
1427
// both modules get marked permanently bogus (unless you reload
1428
// them both of course).
1429
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1430                                continue;
1431                            }
1432                            // If the other module is thought to be OK, assume we can depend
1433
// on it if we need it.
1434
}
1435                        // Already-installed modules are of course fine.
1436
} else if (dep.getType() == Dependency.TYPE_REQUIRES || (withNeeds && dep.getType() == Dependency.TYPE_NEEDS)) {
1437                        // Works much like a regular module dependency. However it only
1438
// fails if there are no satisfying modules with no problems.
1439
String JavaDoc token = dep.getName();
1440                        Set JavaDoc<Module> providers = providersOf.get(token);
1441                        if (providers == null) {
1442                            // Nobody provides it. This dep failed.
1443
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1444                        } else {
1445                            // We have some possible providers. Check that at least one is good.
1446
boolean foundOne = false;
1447                            for (Module other : providers) {
1448                                if (foundOne) {
1449                                    break;
1450                                }
1451                                if (other.isEnabled()) {
1452                                    foundOne = true;
1453                                } else {
1454                                    if ((!withNeeds && missingDependencies(other, false).isEmpty()) ||
1455                                            (withNeeds && isAlmostEmpty(missingDependencies(other, true)))) {
1456                                        // See comment above for regular module deps
1457
// re. use of PROBING_IN_PROCESS.
1458
foundOne = true;
1459                                    }
1460                                }
1461                            }
1462                            if (!foundOne) {
1463                                // Nobody can provide it, fail.
1464
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1465                            }
1466                        }
1467                    } else if (dep.getType() == Dependency.TYPE_JAVA) {
1468                        // Java dependency. Fixed for whole VM session, safe to check once and keep.
1469
if (! Util.checkJavaDependency(dep)) {
1470                            // Bad.
1471
probs.add(Union2.<Dependency,InvalidException>createFirst(dep));
1472                        }
1473                    }
1474                }
1475                probs.remove(PROBING_IN_PROCESS);
1476            }
1477            return probs;
1478        }
1479    }
1480    private static boolean isAlmostEmpty(Set JavaDoc<Union2<Dependency,InvalidException>> probs) {
1481        return probs.isEmpty() || probs.equals(Collections.singleton(PROBING_IN_PROCESS));
1482    }
1483
1484    /** Forget about any possible "soft" problems there might have been.
1485     * Next time anyone asks, recompute them.
1486     * Currently enabled modules are left alone (no problems).
1487     * Otherwise, any problems which are "hard" (result from failed
1488     * Java/IDE/package dependencies, runtime errors, etc.) are left alone;
1489     * "soft" problems of inter-module dependencies are cleared
1490     * so they will be recomputed next time, and corresponding
1491     * changes are fired (since the next call to getProblem might
1492     * return a different result).
1493     */

1494    private void clearProblemCache() {
1495        clearProblemCache(moduleProblemsWithoutNeeds);
1496        clearProblemCache(moduleProblemsWithNeeds);
1497    }
1498    private void clearProblemCache(Map JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>> mP) {
1499        Iterator JavaDoc<Map.Entry JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>>> it = mP.entrySet().iterator();
1500        while (it.hasNext()) {
1501            Map.Entry JavaDoc<Module,Set JavaDoc<Union2<Dependency,InvalidException>>> entry = it.next();
1502            Module m = entry.getKey();
1503            if (! m.isEnabled()) {
1504                Set JavaDoc<Union2<Dependency,InvalidException>> s = entry.getValue();
1505                if (s != null) {
1506                    boolean clear = false;
1507                    for (Union2<Dependency,InvalidException> problem : s) {
1508                        if (problem.hasSecond()) {
1509                            // Hard problem, skip this one.
1510
continue;
1511                        }
1512                        Dependency dep = problem.first();
1513                        if (dep.getType() != Dependency.TYPE_MODULE &&
1514                            dep.getType() != Dependency.TYPE_REQUIRES &&
1515                            dep.getType() != Dependency.TYPE_NEEDS &&
1516                            dep.getType() != Dependency.TYPE_RECOMMENDS
1517                        ) {
1518                            // Also a hard problem.
1519
continue;
1520                        }
1521                        // Some soft problems found, i.e. module deps. Clear them all.
1522
// #76917: Even clear any hard problems.
1523
clear = true;
1524                        break;
1525                    }
1526                    if (clear || s.isEmpty()) { // leave alone only if all hard problems
1527
it.remove();
1528                        firer.change(new ChangeFirer.Change(m, Module.PROP_PROBLEMS, null, null));
1529                    }
1530                }
1531                // if we never computed anything, make no change now
1532
}
1533            // enabled modules are definitely OK, no change there
1534
}
1535    }
1536
1537    /** Try to shut down the system.
1538     * First all modules are asked if they wish to close, in the proper order.
1539     * Assuming they say yes, then they are informed of the close.
1540     * Returns true if they all said yes.
1541     */

1542    public boolean shutDown() {
1543        return shutDown(null);
1544    }
1545
1546    /**
1547     * Try to shut down the system.
1548     * First all modules are asked if they wish to close, in the proper order.
1549     * Assuming they say yes, a hook is run, then they are informed of the close.
1550     * If they did not agree to close, the hook is not run.
1551     * @param midHook a hook to run before closing modules if they agree to close
1552     * @return true if they all said yes and the module system is now shut down
1553     * @since org.netbeans.core/1 1.11
1554     */

1555    public boolean shutDown(Runnable JavaDoc midHook) {
1556        assertWritable();
1557        Set JavaDoc<Module> unorderedModules = getEnabledModules();
1558        Map JavaDoc<Module,List JavaDoc<Module>> deps = Util.moduleDependencies(unorderedModules, modulesByName, providersOf);
1559        List JavaDoc<Module> modules;
1560        try {
1561            modules = Utilities.topologicalSort(unorderedModules, deps);
1562        } catch (TopologicalSortException ex) {
1563            // Once again, weird situation.
1564
if (PRINT_TOPOLOGICAL_EXCEPTION_STACK_TRACES) {
1565                Util.err.log(Level.WARNING, null, ex);
1566            }
1567            Util.err.warning("Cyclic module dependencies, will not shut down cleanly: " + deps); // NOI18N
1568
return true;
1569        }
1570        if (! installer.closing(modules)) {
1571            return false;
1572        }
1573        if (midHook != null) {
1574            try {
1575                midHook.run();
1576            } catch (RuntimeException JavaDoc e) {
1577                Util.err.log(Level.WARNING, null, e);
1578            } catch (LinkageError JavaDoc e) {
1579                Util.err.log(Level.WARNING, null, e);
1580            }
1581        }
1582        installer.close(modules);
1583        return true;
1584    }
1585}
1586
Popular Tags