1 19 20 package org.netbeans; 21 22 27 import java.io.File ; 28 import java.io.IOException ; 29 import java.io.InputStream ; 30 import java.security.AllPermission ; 31 import java.security.CodeSource ; 32 import java.security.PermissionCollection ; 33 import java.security.Permissions ; 34 import java.util.ArrayList ; 35 import java.util.Collections ; 36 import java.util.HashMap ; 37 import java.util.HashSet ; 38 import java.util.Iterator ; 39 import java.util.List ; 40 import java.util.Locale ; 41 import java.util.Map ; 42 import java.util.MissingResourceException ; 43 import java.util.Properties ; 44 import java.util.ResourceBundle ; 45 import java.util.Set ; 46 import java.util.StringTokenizer ; 47 import java.util.jar.Attributes ; 48 import java.util.jar.JarFile ; 49 import java.util.jar.Manifest ; 50 import java.util.logging.Level ; 51 import java.util.zip.ZipEntry ; 52 import org.netbeans.Module.PackageExport; 53 import org.openide.modules.Dependency; 54 import org.openide.util.Exceptions; 55 import org.openide.util.NbBundle; 56 import org.openide.util.Union2; 57 58 66 final class StandardModule extends Module { 67 68 69 private final File jar; 70 71 private File physicalJar = null; 72 73 78 private static final Map <File ,Set <File >> extensionOwners = new HashMap <File ,Set <File >>(); 79 83 private static final Set <File > moduleJARs = new HashSet <File >(); 84 85 88 private Set <File > localeVariants = null; 89 92 private Set <File > plainExtensions = null; 93 97 private Set <File > localeExtensions = null; 98 101 private Set <File > patches = null; 102 103 104 private Properties localizedProps; 105 106 107 public StandardModule(ModuleManager mgr, Events ev, File jar, Object history, boolean reloadable, boolean autoload, boolean eager) throws IOException { 108 super(mgr, ev, history, reloadable, autoload, eager); 109 this.jar = jar; 110 loadManifest(); 111 parseManifest(); 112 findExtensionsAndVariants(manifest); 113 Set <File > bogoOwners = extensionOwners.get(jar); 116 if (bogoOwners != null) { 117 Util.err.warning("module " + jar + " was incorrectly placed in the Class-Path of other JARs " + bogoOwners + "; please use OpenIDE-Module-Module-Dependencies instead"); 118 } 119 moduleJARs.add(jar); 120 } 121 122 137 public Object getLocalizedAttribute(String attr) { 138 String locb = getManifest().getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); boolean usingLoader = false; 140 if (locb != null) { 141 if (classloader != null) { 142 if (locb.endsWith(".properties")) { usingLoader = true; 144 String basename = locb.substring(0, locb.length() - 11).replace('/', '.'); 145 try { 146 ResourceBundle bundle = NbBundle.getBundle(basename, Locale.getDefault(), classloader); 147 try { 148 return bundle.getString(attr); 149 } catch (MissingResourceException mre) { 150 } 152 } catch (MissingResourceException mre) { 153 Util.err.log(Level.WARNING, null, mre); 154 } 155 } else { 156 Util.err.warning("cannot efficiently load non-*.properties OpenIDE-Module-Localizing-Bundle: " + locb); 157 } 158 } 159 if (!usingLoader) { 160 if (localizedProps == null) { 161 Util.err.fine("Trying to get localized attr " + attr + " from disabled module " + getCodeNameBase()); 162 try { 163 if (jar != null && jar.isFile ()) { 165 JarFile jarFile = new JarFile (jar, false); 166 try { 167 loadLocalizedProps(jarFile, manifest); 168 } finally { 169 jarFile.close(); 170 } 171 } else { 172 throw new IllegalStateException (); 173 } 174 } catch (IOException ioe) { 175 Util.err.log(Level.WARNING, jar.getAbsolutePath(), ioe); 176 if (localizedProps == null) { 177 localizedProps = new Properties (); 178 } 179 } 180 } 181 String val = localizedProps.getProperty(attr); 182 if (val != null) { 183 return val; 184 } 185 } 186 } 187 int idx = attr.lastIndexOf('/'); if (idx == -1) { 190 return NbBundle.getLocalizedValue(getManifest().getMainAttributes(), new Attributes.Name (attr)); 192 } else { 193 String section = attr.substring(0, idx); 195 String realAttr = attr.substring(idx + 1); 196 Attributes attrs = getManifest().getAttributes(section); 197 if (attrs != null) { 198 return NbBundle.getLocalizedValue(attrs, new Attributes.Name (realAttr)); 199 } else { 200 return null; 201 } 202 } 203 } 204 205 public boolean owns(Class clazz) { 206 ClassLoader cl = clazz.getClassLoader(); 207 if (cl instanceof Util.ModuleProvider) { 208 return ((Util.ModuleProvider) cl).getModule() == this; 209 } 210 return false; 211 212 } 213 214 public boolean isFixed() { 215 return false; 216 } 217 218 223 public File getJarFile() { 224 return jar; 225 } 226 227 231 private void ensurePhysicalJar() throws IOException { 232 if (reloadable && physicalJar == null) { 233 physicalJar = Util.makeTempJar(jar); 234 } 235 } 236 private void destroyPhysicalJar() { 237 if (physicalJar != null) { 238 if (physicalJar.isFile()) { 239 if (! physicalJar.delete()) { 240 Util.err.warning("temporary JAR " + physicalJar + " not currently deletable."); 241 } else { 242 Util.err.fine("deleted: " + physicalJar); 243 } 244 } 245 physicalJar = null; 246 } else { 247 Util.err.fine("no physicalJar to delete for " + this); 248 } 249 } 250 251 252 private void loadManifest() throws IOException { 253 Util.err.fine("loading manifest of " + jar); 254 File jarBeingOpened = null; try { 256 if (reloadable) { 257 jarBeingOpened = physicalJar; ensurePhysicalJar(); 260 jarBeingOpened = physicalJar; JarFile jarFile = new JarFile (physicalJar, false); 262 try { 263 Manifest m = jarFile.getManifest(); 264 if (m == null) throw new IOException ("No manifest found in " + physicalJar); manifest = m; 266 } finally { 267 jarFile.close(); 268 } 269 } else { 270 jarBeingOpened = jar; 271 manifest = getManager().loadManifest(jar); 272 } 273 } catch (IOException e) { 274 if (jarBeingOpened != null) { 275 Exceptions.attachMessage(e, 276 "While loading manifest from: " + 277 jarBeingOpened); } 279 throw e; 280 } 281 } 282 283 286 private void findExtensionsAndVariants(Manifest m) { 287 assert jar != null : "Cannot load extensions from classpath module " + getCodeNameBase(); 288 localeVariants = null; 289 List <File > l = Util.findLocaleVariantsOf(jar); 290 if (!l.isEmpty()) { 291 localeVariants = new HashSet <File >(l); 292 } 293 plainExtensions = null; 294 localeExtensions = null; 295 String classPath = m.getMainAttributes().getValue(Attributes.Name.CLASS_PATH); 296 if (classPath != null) { 297 StringTokenizer tok = new StringTokenizer (classPath); 298 while (tok.hasMoreTokens()) { 299 String ext = tok.nextToken(); 300 if (new File (ext).isAbsolute() || ext.indexOf("../") != -1) { Util.err.warning("Class-Path value " + ext + " from " + jar + " is illegal according to the Java Extension Mechanism: must be relative and not move up directories"); 302 } 303 File extfile = new File (jar.getParentFile(), ext.replace('/', File.separatorChar)); 304 if (! extfile.exists()) { 305 Util.err.warning("Class-Path value " + ext + " from " + jar + " cannot be found at " + extfile); 307 continue; 308 } 309 Set <File > owners = extensionOwners.get(extfile); 311 if (owners == null) { 312 owners = new HashSet <File >(2); 313 owners.add(jar); 314 extensionOwners.put(extfile, owners); 315 } else if (! owners.contains(jar)) { 316 owners.add(jar); 317 events.log(Events.EXTENSION_MULTIPLY_LOADED, extfile, owners); 318 } if (moduleJARs.contains(extfile)) { 321 Util.err.warning("Class-Path value " + ext + " from " + jar + " illegally refers to another module; use OpenIDE-Module-Module-Dependencies instead"); 322 } 323 if (plainExtensions == null) plainExtensions = new HashSet <File >(); 324 plainExtensions.add(extfile); 325 l = Util.findLocaleVariantsOf(extfile); 326 if (!l.isEmpty()) { 327 if (localeExtensions == null) { 328 localeExtensions = new HashSet <File >(); 329 } 330 localeExtensions.addAll(l); 331 } 332 } 333 } 334 File patchdir = new File (new File (jar.getParentFile(), "patches"), getCodeNameBase().replace('.', '-')); scanForPatches(patchdir); 338 String patchesClassPath = System.getProperty("netbeans.patches." + getCodeNameBase()); if (patchesClassPath != null) { 343 StringTokenizer tokenizer = new StringTokenizer (patchesClassPath, File.pathSeparator); 344 while (tokenizer.hasMoreTokens()) { 345 String element = tokenizer.nextToken(); 346 File fileElement = new File (element); 347 if (fileElement.exists()) { 348 if (patches == null) { 349 patches = new HashSet <File >(15); 350 } 351 patches.add(fileElement); 352 } 353 } 354 } 355 Util.err.fine("localeVariants of " + jar + ": " + localeVariants); 356 Util.err.fine("plainExtensions of " + jar + ": " + plainExtensions); 357 Util.err.fine("localeExtensions of " + jar + ": " + localeExtensions); 358 Util.err.fine("patches of " + jar + ": " + patches); 359 if (patches != null) { 360 for (File patch : patches) { 361 events.log(Events.PATCH, patch); 362 } 363 } 364 } 365 366 367 private void scanForPatches(File patchdir) { 368 if (!patchdir.isDirectory()) { 369 return; 370 } 371 File [] jars = patchdir.listFiles(Util.jarFilter()); 372 if (jars != null) { 373 for (File jar : jars) { 374 if (patches == null) { 375 patches = new HashSet <File >(5); 376 } 377 patches.add(jar); 378 } 379 } else { 380 Util.err.warning("Could not search for patches in " + patchdir); 381 } 382 } 383 384 393 private void loadLocalizedProps(JarFile jarFile, Manifest m) throws IOException { 394 String locbundle = m.getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); if (locbundle != null) { 396 { 399 ZipEntry bundleFile = jarFile.getEntry(locbundle); 400 if (bundleFile != null) { 402 localizedProps = new Properties (); 403 InputStream is = jarFile.getInputStream(bundleFile); 404 try { 405 localizedProps.load(is); 406 } finally { 407 is.close(); 408 } 409 } 410 } 411 { 412 int idx = locbundle.lastIndexOf('.'); String name, ext; 417 if (idx == -1) { 418 name = locbundle; 419 ext = ""; } else { 421 name = locbundle.substring(0, idx); 422 ext = locbundle.substring(idx); 423 } 424 List <Util.FileWithSuffix> pairs = Util.findLocaleVariantsWithSuffixesOf(jar); 425 Collections.reverse(pairs); 426 for (Util.FileWithSuffix pair : pairs) { 427 File localeJar = pair.file; 428 String suffix = pair.suffix; 429 String rsrc = name + suffix + ext; 430 JarFile localeJarFile = new JarFile (localeJar, false); 431 try { 432 ZipEntry bundleFile = localeJarFile.getEntry(rsrc); 433 if (bundleFile != null) { 435 if (localizedProps == null) { 436 localizedProps = new Properties (); 437 } InputStream is = localeJarFile.getInputStream(bundleFile); 439 try { 440 localizedProps.load(is); 441 } finally { 442 is.close(); 443 } 444 } 445 } finally { 446 localeJarFile.close(); 447 } 448 } 449 } 450 if (localizedProps == null) { 451 throw new IOException ("Could not find localizing bundle: " + locbundle); } 454 459 } 460 } 461 462 473 public List <File > getAllJars() { 474 List <File > l = new ArrayList <File >(); 475 if (patches != null) l.addAll(patches); 476 if (physicalJar != null) { 477 l.add(physicalJar); 478 } else if (jar != null) { 479 l.add(jar); 480 } 481 if (plainExtensions != null) l.addAll (plainExtensions); 482 if (localeVariants != null) l.addAll (localeVariants); 483 if (localeExtensions != null) l.addAll (localeExtensions); 484 return l; 485 } 486 487 494 public void setReloadable(boolean r) { 495 getManager().assertWritable(); 496 if (reloadable != r) { 497 reloadable = r; 498 getManager().fireReloadable(this); 499 } 500 } 501 502 506 private transient boolean released; 507 508 private transient int releaseCount = 0; 509 510 514 public void reload() throws IOException { 515 destroyPhysicalJar(); 517 String codeNameBase1 = getCodeNameBase(); 518 localizedProps = null; 519 loadManifest(); 520 parseManifest(); 521 findExtensionsAndVariants(manifest); 522 String codeNameBase2 = getCodeNameBase(); 523 if (! codeNameBase1.equals(codeNameBase2)) { 524 throw new InvalidException("Code name base changed during reload: " + codeNameBase1 + " -> " + codeNameBase2); } 526 } 527 528 532 protected void classLoaderUp(Set <Module> parents) throws IOException { 533 Util.err.fine("classLoaderUp on " + this + " with parents " + parents); 534 List <ClassLoader > loaders = new ArrayList <ClassLoader >(parents.size() + 1); 536 loaders.add(Module.class.getClassLoader()); 538 Iterator it = parents.iterator(); 539 for (Module parent: parents) { 540 PackageExport[] exports = parent.getPublicPackages(); 541 if (exports != null && exports.length == 0) { 542 boolean implDep = false; 544 for (Dependency dep : getDependenciesArray()) { 545 if (dep.getType() == Dependency.TYPE_MODULE && 546 dep.getComparison() == Dependency.COMPARE_IMPL && 547 dep.getName().equals(parent.getCodeName())) { 548 implDep = true; 549 break; 550 } 551 } 552 if (!implDep) { 553 continue; 558 } 559 } 560 ClassLoader l = parent.getClassLoader(); 561 if (parent.isFixed() && loaders.contains(l)) { 562 Util.err.fine("#24996: skipping duplicate classloader from " + parent); 563 continue; 564 } 565 loaders.add(l); 566 } 567 List <Union2<File ,JarFile >> classp = new ArrayList <Union2<File ,JarFile >>(3); 568 if (patches != null) { 569 for (File f : patches) { 570 if (f.isDirectory()) { 571 classp.add(Union2.<File ,JarFile >createFirst(f)); 572 } else { 573 classp.add(Union2.<File ,JarFile >createSecond(new JarFile (f, false))); 574 } 575 } 576 } 577 if (reloadable) { 578 ensurePhysicalJar(); 579 classp.add(Union2.<File ,JarFile >createSecond(new JarFile (physicalJar, false))); 583 } else { 584 classp.add(Union2.<File ,JarFile >createSecond(new JarFile (jar, false))); 585 } 586 if (localeVariants != null) { 588 for (File var : localeVariants) { 589 classp.add(Union2.<File ,JarFile >createSecond(new JarFile (var, false))); 590 } 591 } 592 if (localeExtensions != null) { 593 for (File ext : localeExtensions) { 594 classp.add(ext.isDirectory() ? 595 Union2.<File ,JarFile >createFirst(ext) : 596 Union2.<File ,JarFile >createSecond(new JarFile (ext, false))); 597 } 598 } 599 if (plainExtensions != null) { 600 for (File ext : plainExtensions) { 601 classp.add(ext.isDirectory() ? 602 Union2.<File ,JarFile >createFirst(ext) : 603 Union2.<File ,JarFile >createSecond(new JarFile (ext, false))); 604 } 605 } 606 607 getManager().refineClassLoader(this, loaders); 609 610 try { 611 classloader = new OneModuleClassLoader(classp, loaders.toArray(new ClassLoader [loaders.size()])); 612 } catch (IllegalArgumentException iae) { 613 throw (IOException ) new IOException (iae.toString()).initCause(iae); 615 } 616 } 617 618 619 protected void classLoaderDown() { 620 if (classloader instanceof ProxyClassLoader) { 621 ((ProxyClassLoader)classloader).destroy(); 622 } 623 classloader = null; 624 Util.err.fine("classLoaderDown on " + this + ": releaseCount=" + releaseCount + " released=" + released); 625 released = false; 626 } 627 628 protected void cleanup() { 629 if (isEnabled()) throw new IllegalStateException ("cleanup on enabled module: " + this); if (classloader != null) throw new IllegalStateException ("cleanup on module with classloader: " + this); if (! released) { 632 Util.err.fine("Warning: not all resources associated with module " + jar + " were successfully released."); 633 released = true; 634 } else { 635 Util.err.fine("All resources associated with module " + jar + " were successfully released."); 636 } 637 destroyPhysicalJar(); 639 } 640 641 642 public void destroy() { 643 moduleJARs.remove(jar); 644 } 645 646 647 public String toString() { 648 String s = "StandardModule:" + getCodeNameBase() + " jarFile: " + jar.getAbsolutePath(); if (!isValid()) s += "[invalid]"; return s; 651 } 652 653 654 private static PermissionCollection modulePermissions; 655 656 private static synchronized PermissionCollection getAllPermission() { 657 if (modulePermissions == null) { 658 modulePermissions = new Permissions (); 659 modulePermissions.add(new AllPermission ()); 660 modulePermissions.setReadOnly(); 661 } 662 return modulePermissions; 663 } 664 665 668 private class OneModuleClassLoader extends JarClassLoader implements Util.ModuleProvider { 669 private int rc; 670 677 public OneModuleClassLoader(List <Union2<File ,JarFile >> classp, ClassLoader [] parents) throws IllegalArgumentException { 678 super(classp, parents, false); 679 rc = releaseCount++; 680 } 681 682 public Module getModule() { 683 return StandardModule.this; 684 } 685 686 690 protected PermissionCollection getPermissions(CodeSource cs) { 691 return getAllPermission(); 692 } 693 694 695 protected String findLibrary(String libname) { 696 String mapped = System.mapLibraryName(libname); 697 File lib = new File (new File (jar.getParentFile(), "lib"), mapped); if (lib.isFile()) { 699 return lib.getAbsolutePath(); 700 } else { 701 return null; 702 } 703 } 704 705 protected boolean isSpecialResource(String pkg) { 706 if (mgr.isSpecialResource(pkg)) { 707 return true; 708 } 709 return super.isSpecialResource(pkg); 710 } 711 712 713 protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { 714 if (!super.shouldDelegateResource(pkg, parent)) { 715 return false; 716 } 717 Module other; 718 if (parent instanceof Util.ModuleProvider) { 719 other = ((Util.ModuleProvider)parent).getModule(); 720 } else { 721 other = null; 722 } 723 return getManager().shouldDelegateResource(StandardModule.this, other, pkg); 724 } 725 726 public String toString() { 727 return super.toString() + "[" + getCodeNameBase() + "]"; } 729 730 protected void finalize() throws Throwable { 731 super.finalize(); 732 Util.err.fine("Finalize for " + this + ": rc=" + rc + " releaseCount=" + releaseCount + " released=" + released); if (rc == releaseCount) { 734 released = true; 736 } else { 737 Util.err.fine("Now resources for " + getCodeNameBase() + " have been released."); } 739 } 740 } 741 742 } 743 | Popular Tags |