1 19 20 package org.netbeans; 21 22 import java.beans.PropertyChangeListener ; 23 import java.beans.PropertyChangeSupport ; 24 import java.io.File ; 25 import java.io.IOException ; 26 import java.security.AllPermission ; 27 import java.security.CodeSource ; 28 import java.security.PermissionCollection ; 29 import java.security.Permissions ; 30 import java.util.ArrayList ; 31 import java.util.Collection ; 32 import java.util.Collections ; 33 import java.util.HashMap ; 34 import java.util.HashSet ; 35 import java.util.Iterator ; 36 import java.util.LinkedList ; 37 import java.util.List ; 38 import java.util.Map ; 39 import java.util.Set ; 40 import java.util.StringTokenizer ; 41 import java.util.jar.JarFile ; 42 import java.util.jar.Manifest ; 43 import java.util.logging.Level ; 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 57 public final class ModuleManager { 58 59 public static final String PROP_MODULES = "modules"; public static final String PROP_ENABLED_MODULES = "enabledModules"; public static final String PROP_CLASS_LOADER = "classLoader"; 63 static boolean PRINT_TOPOLOGICAL_EXCEPTION_STACK_TRACES = !Boolean.getBoolean ("suppress.topological.exception"); 67 private final Set <Module> modules = new HashSet <Module>(100); 69 private final Map <String ,Module> modulesByName = new HashMap <String ,Module>(100); 71 72 private final Map <Module,Set <Union2<Dependency,InvalidException>>> moduleProblemsWithoutNeeds = new HashMap <Module,Set <Union2<Dependency,InvalidException>>>(100); 75 private final Map <Module,Set <Union2<Dependency,InvalidException>>> moduleProblemsWithNeeds = new HashMap <Module,Set <Union2<Dependency,InvalidException>>>(100); 76 77 private final Map <String ,Set <Module>> providersOf = new HashMap <String ,Set <Module>>(25); 79 80 private final ModuleInstaller installer; 81 private ModuleFactory moduleFactory; 82 83 private SystemClassLoader classLoader; 84 private List <Union2<File ,JarFile >> classLoaderPatches; 85 private final Object classLoaderLock = new String ("ModuleManager.classLoaderLock"); 87 private final Events ev; 88 89 93 public ModuleManager(ModuleInstaller installer, Events ev) { 94 this.installer = installer; 95 this.ev = ev; 96 String patches = System.getProperty("netbeans.systemclassloader.patches"); 97 if (patches != null) { 98 System.err.println("System class loader patches: " + patches); classLoaderPatches = new ArrayList <Union2<File ,JarFile >>(); 103 StringTokenizer tok = new StringTokenizer (patches, File.pathSeparator); 104 while (tok.hasMoreTokens()) { 105 File f = new File (tok.nextToken()); 106 if (f.isDirectory()) { 107 classLoaderPatches.add(Union2.<File ,JarFile >createFirst(f)); 108 } else { 109 try { 110 classLoaderPatches.add(Union2.<File ,JarFile >createSecond(new JarFile (f))); 111 } catch (IOException ioe) { 112 Util.err.log(Level.WARNING, "Problematic file: " + f, ioe); 113 } 114 } 115 } 116 } else { 117 classLoaderPatches = Collections.emptyList(); 119 } 120 classLoader = new SystemClassLoader(classLoaderPatches, new ClassLoader [] {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 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 158 public final Mutex mutex() { 159 return MUTEX; 160 } 161 164 public final Mutex.Privileged mutexPrivileged() { 165 return MUTEX_PRIVILEGED; 166 } 167 171 173 private ChangeFirer firer = new ChangeFirer(this); 174 176 private boolean readOnly = false; 177 180 void readOnly(boolean ro) { 181 readOnly = ro; 182 } 183 189 void assertWritable() throws IllegalThreadStateException { 190 if (readOnly) { 191 throw new IllegalThreadStateException ("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."); } 193 } 194 195 private PropertyChangeSupport changeSupport; 196 197 206 public final void addPropertyChangeListener(PropertyChangeListener l) { 207 synchronized (this) { 208 if (changeSupport == null) 209 changeSupport = new PropertyChangeSupport (this); 210 } 211 changeSupport.addPropertyChangeListener(l); 212 } 213 214 215 public final void removePropertyChangeListener(PropertyChangeListener l) { 216 if (changeSupport != null) 217 changeSupport.removePropertyChangeListener(l); 218 } 219 220 final void firePropertyChange(String prop, Object old, Object 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 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 244 public Lookup getModuleLookup() { 245 return lookup; 246 } 247 final void fireModulesCreatedDeleted(Set created, Set deleted) { 249 Util.err.fine("lookup created: " + created + " deleted: " + deleted); 250 lookup.changed(); 251 } 252 253 257 public Set <Module> getModules() { 258 return new HashSet <Module>(modules); 259 } 260 261 265 public final Set <Module> getEnabledModules() { 266 Set <Module> s = new HashSet <Module>(modules); 267 Iterator <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 280 public final Module get(String codeNameBase) { 281 return modulesByName.get(codeNameBase); 282 } 283 284 298 public Set getModuleInterdependencies(Module m, boolean reverse, boolean transitive) { 299 return Util.moduleInterdependencies(m, reverse, transitive, modules, modulesByName, providersOf); 300 } 301 302 308 public ClassLoader getClassLoader() { 309 synchronized (classLoaderLock) { 312 return classLoader; 313 } 314 } 315 316 317 private void invalidateClassLoader() { 318 synchronized (classLoaderLock) { 319 classLoader.destroy(); } 321 Set <ClassLoader > foundParents = new HashSet <ClassLoader >(modules.size() * 4 / 3 + 2); 324 List <ClassLoader > parents = new ArrayList <ClassLoader >(modules.size() + 1); 325 ClassLoader 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 [] parentCLs = parents.toArray(new ClassLoader [parents.size()]); 340 SystemClassLoader nue; 341 try { 342 nue = new SystemClassLoader(classLoaderPatches, parentCLs, modules); 343 } catch (IllegalArgumentException iae) { 344 Util.err.log(Level.WARNING, null, iae); 345 nue = new SystemClassLoader(classLoaderPatches, new ClassLoader [] {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 l, boolean force) { 354 ThreadGroup g = Thread.currentThread().getThreadGroup(); 356 while (g.getParent() != null) g = g.getParent(); 357 while (true) { 360 int s = g.activeCount() + 1; 361 Thread [] ts = new Thread [s]; 362 int x = g.enumerate(ts, true); 363 if (x < s) { 364 for (int i = 0; i < x; i++) { 366 if (force || (ts[i].getContextClassLoader() instanceof SystemClassLoader)) { 373 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 389 private final class SystemClassLoader extends JarClassLoader { 390 391 private final PermissionCollection allPermissions; 392 private final StringBuffer debugme; 393 private boolean empty = true; 394 395 public SystemClassLoader(List <Union2<File ,JarFile >> files, ClassLoader [] parents, Set <Module> modules) throws IllegalArgumentException { 396 super(files, parents, false); 397 allPermissions = new Permissions (); 398 allPermissions.add(new AllPermission ()); 399 allPermissions.setReadOnly(); 400 debugme = new StringBuffer (100 + 50 * modules.size()); 401 debugme.append("SystemClassLoader["); for (Union2<File ,JarFile > file : files) { 403 if (empty) { 404 empty = false; 405 } else { 406 debugme.append(','); } 408 debugme.append(file.hasFirst() ? file.first().getAbsolutePath() : file.second().getName()); 409 } 410 record(modules); 411 debugme.append(']'); } 413 414 private void record(Collection <Module> modules) { 415 for (Module m: modules) { 416 if (empty) { 417 empty = false; 418 } else { 419 debugme.append(','); } 421 debugme.append(m.getCodeNameBase()); 422 } 423 } 424 425 public void append(ClassLoader [] ls, List <Module> modules) throws IllegalArgumentException { 426 super.append(ls); 427 debugme.deleteCharAt(debugme.length() - 1); 428 record(modules); 429 debugme.append(']'); } 431 432 protected void finalize() throws Throwable { 433 super.finalize(); 434 Util.err.fine("Collected system class loader"); 435 } 436 437 public String toString() { 438 if (debugme == null) { 439 return "SystemClassLoader"; 440 } 441 return debugme.toString(); 442 } 443 444 protected boolean isSpecialResource(String pkg) { 445 if (installer.isSpecialResource(pkg)) { 446 return true; 447 } 448 return super.isSpecialResource(pkg); 449 } 450 451 454 protected PermissionCollection getPermissions(CodeSource cs) { 455 return allPermissions; 456 } 457 458 } 459 460 463 @Deprecated 464 public Module create(File jar, Object history, boolean reloadable, boolean autoload) throws IOException , DuplicateException { 465 return create(jar, history, reloadable, autoload, false); 466 } 467 468 481 public Module create(File jar, Object history, boolean reloadable, boolean autoload, boolean eager) throws IOException , 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 <Module> immediate = simulateEnable(Collections.<Module>emptySet()); 490 if (!immediate.isEmpty()) { 491 if (!immediate.contains(m)) throw new IllegalStateException ("Can immediately enable modules " + immediate + ", but not including " + m); boolean ok = true; 493 for (Module other: immediate) { 494 if (!other.isAutoload() && !other.isEager()) { 495 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 512 public Module createFixed(Manifest mani, Object history, ClassLoader loader) throws InvalidException, DuplicateException { 513 assertWritable(); 514 if (mani == null || loader == null) throw new IllegalArgumentException ("null manifest or loader"); 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 523 void refineDependencies(Module m, Set <Dependency> dependencies) { 524 installer.refineDependencies(m, dependencies); 525 } 526 528 String [] refineProvides (Module m) { 529 return installer.refineProvides (m); 530 } 531 532 public void refineClassLoader(Module m, List parents) { 533 installer.refineClassLoader(m, parents); 535 } 536 537 public boolean shouldDelegateResource(Module m, Module parent, String pkg) { 538 Module.PackageExport[] exports = (parent == null) ? null : parent.getPublicPackages(); 540 if (exports != null) { 541 boolean exported = false; 544 if (parent.isDeclaredAsFriend(m)) { for (int i = 0; i < exports.length; i++) { 546 if (exports[i].recursive ? pkg.startsWith(exports[i].pkg) : pkg.equals(exports[i].pkg)) { 547 exported = true; 549 break; 550 } 551 } 552 } 553 if (!exported) { 554 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 break; 565 } 566 } 567 if (!impldep) { 568 if (Util.err.isLoggable(Level.FINE)) { 571 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 } 581 } 583 if (pkg.startsWith("META-INF/")) { return false; 587 } 588 return installer.shouldDelegateResource(m, parent, pkg); 590 } 591 public boolean isSpecialResource(String pkg) { 592 return installer.isSpecialResource(pkg); 593 } 594 Manifest loadManifest(File jar) throws IOException { 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 clearProblemCache(); 616 firer.fire(); 617 } 618 private void possibleProviderAdded(Module m) { 619 String [] provides = m.getProvides(); 620 for (int i = 0; i < provides.length; i++) { 621 Set <Module> providing = providersOf.get(provides[i]); 622 if (providing == null) { 623 providing = new HashSet <Module>(16); 624 providersOf.put(provides[i], providing); 625 } 626 providing.add(m); 627 } 628 } 629 630 634 public void delete(Module m) throws IllegalArgumentException { 635 assertWritable(); 636 if (m.isFixed()) throw new IllegalArgumentException ("fixed module: " + m); if (m.isEnabled()) throw new IllegalArgumentException ("enabled module: " + m); 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 clearProblemCache(); 648 m.destroy(); 649 firer.fire(); 650 } 651 private void possibleProviderRemoved(Module m) { 652 for (String token : m.getProvides()) { 653 Set <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 } 663 } 664 } 665 666 679 public void reload(Module m) throws IllegalArgumentException , IOException { 680 assertWritable(); 681 Util.err.fine("reload: " + m); 683 if (m.isFixed()) throw new IllegalArgumentException ("reload fixed module: " + m); if (m.isEnabled()) throw new IllegalArgumentException ("reload enabled module: " + m); possibleProviderRemoved(m); 686 try { 687 m.reload(); 688 } catch (IOException ioe) { 689 delete(m); 691 throw ioe; 692 } 693 possibleProviderAdded(m); 694 firer.change(new ChangeFirer.Change(m, Module.PROP_MANIFEST, null, null)); 695 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 711 public final void enable(Module m) throws IllegalArgumentException , InvalidException { 712 enable(Collections.singleton(m)); 713 } 714 715 719 public final void disable(Module m) throws IllegalArgumentException { 720 disable(Collections.singleton(m)); 721 } 722 723 734 public void enable(Set <Module> modules) throws IllegalArgumentException , InvalidException { 735 assertWritable(); 736 Util.err.fine("enable: " + modules); 737 742 ev.log(Events.PERF_START, "ModuleManager.enable"); List <Module> toEnable = simulateEnable(modules); 745 ev.log(Events.PERF_TICK, "checked the required ordering and autoloads"); 747 Util.err.fine("enable: toEnable=" + toEnable); { 749 Set <Module> testing = new HashSet <Module>(toEnable); 751 if (! testing.containsAll(modules)) { 752 Set <Module> bogus = new HashSet <Module>(modules); 753 bogus.removeAll(testing); 754 throw new IllegalArgumentException ("Not all requested modules can be enabled: " + bogus); } 756 Iterator 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 ("Would also need to enable " + m); } 762 } 763 } 764 Util.err.fine("enable: verified dependencies"); 765 ev.log(Events.PERF_TICK, "verified dependencies"); 767 ev.log(Events.START_ENABLE_MODULES, toEnable); 768 { 769 LinkedList <Module> fallback = new LinkedList <Module>(); 772 boolean tryingClassLoaderUp = false; 776 Dependency failedPackageDep = null; 778 try { 779 ev.log(Events.PERF_START, "module preparation" ); 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() ); try { 785 Dependency[] dependencies = m.getDependenciesArray(); 787 Set <Module> parents = new HashSet <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 continue; 795 } 796 String name = (String )Util.parseCodeName(dep.getName())[0]; 797 Module parent = get(name); 798 if (parent == null) throw new IOException ("Parent " + name + " not found!"); parents.add(parent); 801 } 802 m.classLoaderUp(parents); 803 } catch (IOException 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() ); ev.log(Events.PERF_START, "package dependency check on " + m.getCodeName() ); 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); } 824 Util.err.fine("Successful check for: " + dep); 825 } 826 ev.log(Events.PERF_END, "package dependency check on " + m.getCodeName() ); ev.log(Events.PERF_START, "ModuleInstaller.prepare " + m.getCodeName() ); installer.prepare(m); 830 ev.log(Events.PERF_END, "ModuleInstaller.prepare " + m.getCodeName() ); } 832 ev.log(Events.PERF_END, "module preparation" ); 834 } catch (InvalidException ie) { 835 Module bad = ie.getModule(); 837 if (bad == null) throw new IllegalStateException ("Problem with no associated module: " + ie); Set <Union2<Dependency,InvalidException>> probs = moduleProblemsWithNeeds.get(bad); 839 if (probs == null) throw new IllegalStateException ("Were trying to install a module that had never been checked: " + bad); if (! probs.isEmpty()) throw new IllegalStateException ("Were trying to install a module that was known to be bad: " + bad); if (failedPackageDep != null) { 843 probs.add(Union2.<Dependency,InvalidException>createFirst(failedPackageDep)); 845 } else { 846 probs.add(Union2.<Dependency,InvalidException>createSecond(ie)); 848 } 849 clearProblemCache(); 854 firer.change(new ChangeFirer.Change(bad, Module.PROP_PROBLEMS, Collections.EMPTY_SET, Collections.singleton("something"))); Util.err.fine("enable: will roll back from: " + ie); 858 Iterator fbIt = fallback.iterator(); 859 while (fbIt.hasNext()) { 860 Module m = (Module)fbIt.next(); 861 if (m.isFixed()) { 862 continue; 864 } 865 m.setEnabled(false); 866 if (tryingClassLoaderUp) { 867 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 if (classLoader != null) { 881 Util.err.fine("enable: adding to system classloader"); 882 List <ClassLoader > nueclassloaders = new ArrayList <ClassLoader >(toEnable.size()); 883 Iterator <Module> teIt = toEnable.iterator(); 884 if (moduleFactory.removeBaseClassLoader()) { 885 ClassLoader base = ModuleManager.class.getClassLoader(); 886 nueclassloaders.add(moduleFactory.getClasspathDelegateClassLoader(this, base)); 887 while (teIt.hasNext()) { 888 ClassLoader 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 [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 Util.err.fine("enable: firing changes"); 908 firer.change(new ChangeFirer.Change(this, PROP_ENABLED_MODULES, null, null)); 909 Iterator 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 929 public void disable(Set <Module> modules) throws IllegalArgumentException { 930 assertWritable(); 931 Util.err.fine("disable: " + modules); 932 if (modules.isEmpty()) { 933 return; 934 } 935 List <Module> toDisable = simulateDisable(modules); 937 Util.err.fine("disable: toDisable=" + toDisable); 938 { 939 for (Module m: toDisable) { 941 if (!modules.contains(m) && !m.isAutoload() && !m.isEager()) { 942 throw new IllegalArgumentException ("Would also need to disable: " + m); } 944 } 945 } 946 Util.err.fine("disable: verified dependencies"); 947 ev.log(Events.START_DISABLE_MODULES, toDisable); 948 { 949 installer.unload(toDisable); 951 Iterator 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(); System.runFinalization(); 960 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 firer.change(new ChangeFirer.Change(this, PROP_ENABLED_MODULES, null, null)); 971 invalidateClassLoader(); 973 Iterator 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 1013 public List <Module> simulateEnable(Set <Module> modules) throws IllegalArgumentException { 1014 1019 Set <Module> willEnable = new HashSet <Module>(modules.size() * 2 + 1); 1021 for (Module m: modules) { 1022 if (m.isAutoload()) throw new IllegalArgumentException ("Cannot simulate enabling an autoload: " + m); if (m.isEager()) throw new IllegalArgumentException ("Cannot simulate enabling an eager module: " + m); if (m.isEnabled()) throw new IllegalArgumentException ("Already enabled: " + m); if (!m.isValid()) throw new IllegalArgumentException ("Not managed by me: " + m + " in " + m); maybeAddToEnableList(willEnable, modules, m, true); 1027 } 1028 Set <Module> stillDisabled = new HashSet <Module>(this.modules); 1030 Iterator <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)) {} 1038 Map <Module,List <Module>> deps = Util.moduleDependencies(willEnable, modulesByName, providersOf); 1039 try { 1040 List <Module> l = Utilities.topologicalSort(willEnable, deps); 1041 Collections.reverse(l); 1042 return l; 1043 } catch (TopologicalSortException ex) { 1044 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); return Collections.<Module>emptyList(); 1060 } 1061 } 1062 private void maybeAddToEnableList(Set <Module> willEnable, Set <Module> mightEnable, Module m, boolean okToFail) { 1063 if (! missingDependencies(m).isEmpty()) { 1064 if (! okToFail) throw new IllegalStateException ("Module was supposed to be OK: " + m); return; 1068 } 1069 if (willEnable.contains(m)) { 1070 return; 1072 } 1073 willEnable.add(m); 1074 for (Dependency dep : m.getDependenciesArray()) { 1077 if (dep.getType() == Dependency.TYPE_MODULE) { 1078 String codeNameBase = (String )Util.parseCodeName(dep.getName())[0]; 1079 Module other = get(codeNameBase); 1080 if (other == null) throw new IllegalStateException ("Should have found module: " + codeNameBase); 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 <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 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 continue; 1107 } 1108 for (Module other : providers) { 1110 maybeAddToEnableList(willEnable, mightEnable, other, true); 1112 if (!foundOne && willEnable.contains(other)) { 1114 foundOne = true; 1115 } 1117 } 1118 assert foundOne || dep.getType() == Dependency.TYPE_RECOMMENDS : "Should have found a nonproblematic provider of " + dep + " among " + providers + " with willEnable=" + willEnable + " mightEnable=" + mightEnable; 1120 } 1121 } 1123 } 1124 private boolean searchForPossibleEager(Set <Module> willEnable, Set <Module> stillDisabled, Set <Module> mightEnable) { 1125 boolean found = false; 1130 Iterator <Module> it = stillDisabled.iterator(); 1131 FIND_EAGER: 1132 while (it.hasNext()) { 1133 Module m = it.next(); 1134 if (willEnable.contains(m)) { 1135 it.remove(); 1140 continue; 1141 } 1142 if (m.isEager()) { 1143 if (couldBeEnabledWithEagers(m, willEnable, new HashSet <Module>())) { 1144 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 <Module> willEnable, Set <Module> recursion) { 1154 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 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 codeNameBase = (String )Util.parseCodeName(dep.getName())[0]; 1170 Module other = get(codeNameBase); 1171 if (other == null) throw new IllegalStateException ("Should have found module: " + codeNameBase); if (!couldBeEnabledWithEagers(other, willEnable, recursion)) return false; 1174 } else if (dep.getType() == Dependency.TYPE_REQUIRES) { 1175 Set <Module> providers = providersOf.get(dep.getName()); 1176 if (providers == null) throw new IllegalStateException ("Should have found a provider of: " + dep.getName()); 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 } 1189 return true; 1190 } 1191 1192 1205 public List <Module> simulateDisable(Set <Module> modules) throws IllegalArgumentException { 1206 if (modules.isEmpty()) { 1207 return Collections.<Module>emptyList(); 1208 } 1209 Set <Module> willDisable = new HashSet <Module>(20); 1212 Iterator it = modules.iterator(); 1213 while (it.hasNext()) { 1214 Module m = (Module)it.next(); 1215 if (m.isAutoload()) throw new IllegalArgumentException ("Cannot disable autoload: " + m); if (m.isEager()) throw new IllegalArgumentException ("Cannot disable eager module: " + m); if (m.isFixed()) throw new IllegalArgumentException ("Cannot disable fixed module: " + m); if (! m.isEnabled()) throw new IllegalArgumentException ("Already disabled: " + m); addToDisableList(willDisable, m); 1220 } 1221 Set <Module> stillEnabled = new HashSet <Module>(getEnabledModules()); 1222 stillEnabled.removeAll(willDisable); 1223 while (searchForUnusedAutoloads(willDisable, stillEnabled)) {} 1224 Map <Module,List <Module>> deps = Util.moduleDependencies(willDisable, modulesByName, providersOf); 1225 try { 1226 return Utilities.topologicalSort(willDisable, deps); 1227 } catch (TopologicalSortException ex) { 1228 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); return new ArrayList <Module>(willDisable); 1234 } 1235 } 1236 private void addToDisableList(Set <Module> willDisable, Module m) { 1237 if (willDisable.contains(m)) { 1238 return; 1240 } 1241 willDisable.add(m); 1242 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 addToDisableList(willDisable, other); 1256 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 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 addToDisableList(willDisable, other); 1278 break; 1279 } 1280 } 1281 } 1282 } 1284 } 1285 } 1286 private boolean searchForUnusedAutoloads(Set <Module> willDisable, Set <Module> stillEnabled) { 1287 boolean found = false; 1291 Iterator <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 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 if (m.provides(dep.getName())) { 1315 continue FIND_AUTOLOADS; 1316 } 1317 } 1318 } 1320 } 1321 found = true; 1323 it.remove(); 1324 willDisable.add(m); 1325 } 1326 } 1327 return found; 1328 } 1329 1330 private static final Union2<Dependency,InvalidException> PROBING_IN_PROCESS = Union2.createSecond(new InvalidException("PROBING_IN_PROCESS")); 1332 Set <Union2<Dependency,InvalidException>> missingDependencies(Module probed) { 1335 return missingDependencies(probed, true); 1336 } 1337 Set <Union2<Dependency,InvalidException>> missingDependencies(Module probed, boolean withNeeds) { 1338 synchronized (moduleProblemsWithNeeds) { 1343 Map <Module,Set <Union2<Dependency,InvalidException>>> mP = (withNeeds ? moduleProblemsWithNeeds : moduleProblemsWithoutNeeds); 1344 Set <Union2<Dependency,InvalidException>> probs = mP.get(probed); 1345 if (probs == null) { 1346 probs = new HashSet <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 } else if (dep.getType() == Dependency.TYPE_MODULE) { 1358 Object [] depParse = Util.parseCodeName(dep.getName()); 1360 String codeNameBase = (String )depParse[0]; 1361 int relVersionMin = (depParse[1] != null) ? ((Integer )depParse[1]).intValue() : -1; 1362 int relVersionMax = (depParse[2] != null) ? ((Integer )depParse[2]).intValue() : relVersionMin; 1363 Module other = get(codeNameBase); 1364 if (other == null) { 1365 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1367 continue; 1368 } 1369 if (relVersionMin == relVersionMax) { 1370 if (relVersionMin != other.getCodeNameRelease()) { 1372 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())) { 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 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1388 continue; 1389 } 1390 } else if (relVersionMin < relVersionMax) { 1391 int otherRel = other.getCodeNameRelease(); 1393 if (otherRel < relVersionMin || otherRel > relVersionMax) { 1394 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1396 continue; 1397 } 1398 if (dep.getComparison() == Dependency.COMPARE_IMPL) { 1399 throw new IllegalStateException ("No such thing as ranged impl dep"); } 1401 if (dep.getComparison() == Dependency.COMPARE_SPEC && 1402 otherRel == relVersionMin && 1404 new SpecificationVersion(dep.getVersion()).compareTo( 1405 other.getSpecificationVersion()) > 0) { 1406 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1408 continue; 1409 } 1410 } else { 1411 throw new IllegalStateException ("Upside-down rel vers range"); } 1413 if (! other.isEnabled()) { 1414 if ((!withNeeds && !missingDependencies(other, false).isEmpty()) || 1417 (withNeeds && !isAlmostEmpty(missingDependencies(other, true)))) { 1418 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1430 continue; 1431 } 1432 } 1435 } else if (dep.getType() == Dependency.TYPE_REQUIRES || (withNeeds && dep.getType() == Dependency.TYPE_NEEDS)) { 1437 String token = dep.getName(); 1440 Set <Module> providers = providersOf.get(token); 1441 if (providers == null) { 1442 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1444 } else { 1445 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 foundOne = true; 1459 } 1460 } 1461 } 1462 if (!foundOne) { 1463 probs.add(Union2.<Dependency,InvalidException>createFirst(dep)); 1465 } 1466 } 1467 } else if (dep.getType() == Dependency.TYPE_JAVA) { 1468 if (! Util.checkJavaDependency(dep)) { 1470 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 <Union2<Dependency,InvalidException>> probs) { 1481 return probs.isEmpty() || probs.equals(Collections.singleton(PROBING_IN_PROCESS)); 1482 } 1483 1484 1494 private void clearProblemCache() { 1495 clearProblemCache(moduleProblemsWithoutNeeds); 1496 clearProblemCache(moduleProblemsWithNeeds); 1497 } 1498 private void clearProblemCache(Map <Module,Set <Union2<Dependency,InvalidException>>> mP) { 1499 Iterator <Map.Entry <Module,Set <Union2<Dependency,InvalidException>>>> it = mP.entrySet().iterator(); 1500 while (it.hasNext()) { 1501 Map.Entry <Module,Set <Union2<Dependency,InvalidException>>> entry = it.next(); 1502 Module m = entry.getKey(); 1503 if (! m.isEnabled()) { 1504 Set <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 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 continue; 1520 } 1521 clear = true; 1524 break; 1525 } 1526 if (clear || s.isEmpty()) { it.remove(); 1528 firer.change(new ChangeFirer.Change(m, Module.PROP_PROBLEMS, null, null)); 1529 } 1530 } 1531 } 1533 } 1535 } 1536 1537 1542 public boolean shutDown() { 1543 return shutDown(null); 1544 } 1545 1546 1555 public boolean shutDown(Runnable midHook) { 1556 assertWritable(); 1557 Set <Module> unorderedModules = getEnabledModules(); 1558 Map <Module,List <Module>> deps = Util.moduleDependencies(unorderedModules, modulesByName, providersOf); 1559 List <Module> modules; 1560 try { 1561 modules = Utilities.topologicalSort(unorderedModules, deps); 1562 } catch (TopologicalSortException ex) { 1563 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); return true; 1569 } 1570 if (! installer.closing(modules)) { 1571 return false; 1572 } 1573 if (midHook != null) { 1574 try { 1575 midHook.run(); 1576 } catch (RuntimeException e) { 1577 Util.err.log(Level.WARNING, null, e); 1578 } catch (LinkageError e) { 1579 Util.err.log(Level.WARNING, null, e); 1580 } 1581 } 1582 installer.close(modules); 1583 return true; 1584 } 1585} 1586 | Popular Tags |