1 19 20 package org.netbeans; 21 22 import java.io.IOException ; 23 import java.net.URL ; 24 import java.security.AccessController ; 25 import java.security.PrivilegedAction ; 26 import java.util.ArrayList ; 27 import java.util.Arrays ; 28 import java.util.Collections ; 29 import java.util.Enumeration ; 30 import java.util.HashMap ; 31 import java.util.HashSet ; 32 import java.util.List ; 33 import java.util.Map ; 34 import java.util.Set ; 35 import java.util.logging.Level ; 36 import java.util.logging.Logger ; 37 import org.openide.util.Enumerations; 38 import org.openide.util.Lookup; 39 40 50 public class ProxyClassLoader extends ClassLoader implements Util.PackageAccessibleClassLoader { 51 52 private static final Logger LOGGER = Logger.getLogger(ProxyClassLoader.class.getName()); 53 54 58 private final Map <String , ClassLoader > domainsByPackage = new HashMap <String , ClassLoader >(); 59 60 private final Map <String , Package > packages = new HashMap <String , Package >(); 61 62 63 private ClassLoader [] parents; 64 65 66 private boolean dead = false; 67 68 private final boolean transitive; 69 70 76 public ProxyClassLoader( ClassLoader [] parents ) { 77 this(parents, true); 78 } 79 80 88 public ProxyClassLoader(ClassLoader [] parents, boolean transitive) { 89 if (parents.length == 0) { 90 throw new IllegalArgumentException ("ProxyClassLoader must have a parent"); } 92 93 this.transitive = transitive; 94 95 Set <ClassLoader > check = new HashSet <ClassLoader >(Arrays.asList(parents)); 96 if (check.size() < parents.length) throw new IllegalArgumentException ("duplicate parents"); if (check.contains(null)) throw new IllegalArgumentException ("null parent in " + check); 99 this.parents = coalesceParents(parents); 100 } 101 102 108 public void append(ClassLoader [] nueparents) throws IllegalArgumentException { 109 if (nueparents == null) throw new IllegalArgumentException ("null parents array"); for (int i = 0; i < nueparents.length; i++) { 111 if (nueparents[i] == null) throw new IllegalArgumentException ("null parent"); } 113 ClassLoader [] resParents = null; 114 ModuleFactory moduleFactory = Lookup.getDefault().lookup(ModuleFactory.class); 115 if (moduleFactory != null && moduleFactory.removeBaseClassLoader()) { 116 resParents = coalesceAppend(new ClassLoader [0], nueparents); 119 } else { 120 resParents = coalesceAppend(parents, nueparents); 121 } 122 synchronized (this) { 123 parents = resParents; 126 } 127 } 128 129 132 public void destroy() { 133 dead = true; 134 } 135 136 private void zombieCheck(String hint) { 137 if (dead) { 138 141 dead = false; 143 } 144 } 145 146 164 protected synchronized final Class <?> loadClass(String name, boolean resolve) 165 throws ClassNotFoundException { 166 zombieCheck(name); 167 String filename = name.replace('.', '/').concat(".class"); int idx = filename.lastIndexOf('/'); if (idx == -1) { 170 throw new ClassNotFoundException ("Will not load classes from default package (" + name + ")"); } 172 String pkg = filename.substring(0, idx + 1); Class c = smartLoadClass(name, filename, pkg); 174 if(c == null) { 175 throw new ClassNotFoundException (name + " from " + this); 176 } 177 if (resolve) resolveClass(c); 178 return c; 179 } 180 181 194 protected Class simpleFindClass(String name, String fileName, String pkg) { 195 return null; 196 } 197 198 private String stripInitialSlash(String resource) { if (resource.startsWith("/")) { 200 LOGGER.log(Level.WARNING, "Should not use initial '/' in calls to ClassLoader.getResource(s): {0}", resource); 201 return resource.substring(1); 202 } else { 203 return resource; 204 } 205 } 206 207 223 public final URL getResource(String name) { 224 zombieCheck(name); 225 226 name = stripInitialSlash(name); 227 228 final int slashIdx = name.lastIndexOf('/'); 229 if (slashIdx == -1) { 230 printDefaultPackageWarning(name); 231 } 232 final String pkg = name.substring(0, slashIdx + 1); 233 234 if (isSpecialResource(pkg)) { 235 for (int i = 0; i < parents.length; i++) { 237 if (!shouldDelegateResource(pkg, parents[i])) continue; 238 URL u; 239 if (parents[i] instanceof ProxyClassLoader) { 240 u = ((ProxyClassLoader)parents[i]).findResource(name); 241 } else { 242 u = parents[i].getResource(name); 243 } 244 if (u != null) return u; 245 } 246 return findResource(name); 247 } 248 249 ClassLoader owner = domainsByPackage.get(pkg); 250 251 if (owner != null) { if (owner instanceof ProxyClassLoader) { 254 return ((ProxyClassLoader)owner).findResource(name); } else { 256 return owner.getResource(name); } 258 } 259 260 URL retVal = null; 262 for (int i = 0; i < parents.length; i++) { 263 owner = parents[i]; 264 if (!shouldDelegateResource(pkg, owner)) continue; 265 if (owner instanceof ProxyClassLoader) { 266 retVal = ((ProxyClassLoader)owner).findResource(name); } else { 268 retVal = owner.getResource(name); } 270 if (retVal != null) { 271 String p = new String (pkg).intern(); domainsByPackage.put(p, owner); 273 if (owner instanceof ProxyClassLoader) { 274 ((ProxyClassLoader)owner).domainsByPackage.put(p, owner); 275 } 276 return retVal; 277 } 278 } 279 280 retVal = findResource(name); 282 if (retVal != null) { 283 domainsByPackage.put(new String (pkg).intern(), this); } 285 return retVal; 286 } 287 288 295 protected URL findResource(String name) { 296 return null; 297 } 298 299 309 protected final synchronized Enumeration <URL > findResources(String name) throws IOException { 310 zombieCheck(name); 311 name = stripInitialSlash(name); 312 final int slashIdx = name.lastIndexOf('/'); 313 if (slashIdx == -1) { 314 printDefaultPackageWarning(name); 315 } 316 final String pkg = name.substring(0, slashIdx + 1); 317 318 List <Enumeration <URL >> es = new ArrayList <Enumeration <URL >>(parents.length + 1); 321 for (ClassLoader parent : parents) { 322 if (!shouldDelegateResource(pkg, parent)) { 323 continue; 324 } 325 if (parent instanceof ProxyClassLoader) { 326 es.add(((ProxyClassLoader) parent).simpleFindResources(name)); 327 } else { 328 es.add(parent.getResources(name)); 329 } 330 } 331 es.add(simpleFindResources(name)); 332 return Enumerations.concat(Collections.enumeration(es)); 337 } 338 339 348 protected Enumeration <URL > simpleFindResources(String name) throws IOException { 349 return super.findResources(name); 350 } 351 352 353 360 protected Package getPackage(String name) { 361 zombieCheck(name); 362 return getPackageFast(name, name.replace('.', '/') + '/', true); 363 } 364 365 372 protected Package getPackageFast(String name, String sname, boolean recurse) { 373 synchronized (packages) { 374 Package pkg = packages.get(name); 375 if (pkg != null) { 376 return pkg; 377 } 378 if (!recurse) { 379 return null; 380 } 381 for (int i = 0; i < parents.length; i++) { 382 ClassLoader par = parents[i]; 383 if (par instanceof ProxyClassLoader && shouldDelegateResource(sname, par)) { 384 pkg = ((ProxyClassLoader)par).getPackageFast(name, sname, false); 385 if (pkg != null) { 386 break; 387 } 388 } 389 } 390 if (pkg == null && shouldDelegateResource(sname, getParent())) { 391 pkg = super.getPackage(name); 395 } 396 if (pkg != null) { 397 packages.put(name, pkg); 398 } 399 return pkg; 400 } 401 } 402 403 408 protected Package definePackage(String name, String specTitle, 409 String specVersion, String specVendor, String implTitle, 410 String implVersion, String implVendor, URL sealBase ) 411 throws IllegalArgumentException { 412 synchronized (packages) { 413 Package pkg = super.definePackage (name, specTitle, specVersion, specVendor, implTitle, 414 implVersion, implVendor, sealBase); 415 packages.put(name, pkg); 416 return pkg; 417 } 418 } 419 420 426 protected synchronized Package [] getPackages() { 427 return getPackages(new HashSet <ClassLoader >()); 428 } 429 430 437 private synchronized Package [] getPackages(Set <ClassLoader > addedParents) { 438 zombieCheck(null); 439 Map <String ,Package > all = new HashMap <String , Package >(); 440 addPackages(all, super.getPackages()); 442 for (int i = 0; i < parents.length; i++) { 443 ClassLoader par = parents[i]; 444 if (par instanceof ProxyClassLoader && addedParents.add(par)) { 445 addPackages(all, ((ProxyClassLoader)par).getPackages(addedParents)); 447 } 448 } 449 synchronized (packages) { 450 all.keySet().removeAll(packages.keySet()); 451 packages.putAll(all); 452 } 453 return packages.values().toArray(new Package [packages.size()]); 454 } 455 456 public Package getPackageAccessibly(String name) { 457 return getPackage(name); 458 } 459 460 public Package [] getPackagesAccessibly() { 461 return getPackages(); 462 } 463 464 467 private static void printDefaultPackageWarning(String name) { 468 if (!"commons-logging.properties".equals(name) && 470 !"jndi.properties".equals(name)) { LOGGER.log(Level.INFO, null, new IllegalStateException ("You are trying to access file: " + name + " from the default package. Please see http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/classpath.html#default_package")); 472 } 473 } 474 475 483 private ClassLoader [] coalesceParents(ClassLoader [] loaders) throws IllegalArgumentException { 484 int likelySize = loaders.length * 5 + 10; 485 Set <ClassLoader > resultingUnique = new HashSet <ClassLoader >(likelySize); 486 List <ClassLoader > resulting = new ArrayList <ClassLoader >(likelySize); 487 for (int i = 0; i < loaders.length; i++) { 488 addRec(resultingUnique, resulting, loaders[i]); 489 } 490 ClassLoader [] ret = resulting.toArray(new ClassLoader [resulting.size()]); 491 return ret; 492 } 493 494 496 private ClassLoader [] coalesceAppend(ClassLoader [] existing, ClassLoader [] appended) throws IllegalArgumentException { 497 int likelySize = existing.length + 3; 498 Set <ClassLoader > resultingUnique = new HashSet <ClassLoader >(likelySize); 499 List <ClassLoader > existingL = Arrays.asList(existing); 500 resultingUnique.addAll(existingL); 501 if (resultingUnique.containsAll(Arrays.asList(appended))) { 502 return existing; 504 } 505 List <ClassLoader > resulting = new ArrayList <ClassLoader >(likelySize); 506 resulting.addAll(existingL); 507 for (int i = 0; i < appended.length; i++) { 508 addRec(resultingUnique, resulting, appended[i]); 509 } 510 ClassLoader [] ret = resulting.toArray(new ClassLoader [resulting.size()]); 511 return ret; 512 } 513 514 private void addRec(Set <ClassLoader > resultingUnique, List <ClassLoader > resulting, ClassLoader loader) throws IllegalArgumentException { 515 if (loader == this) throw new IllegalArgumentException ("cycle in parents"); if (resultingUnique.contains(loader)) return; 517 if (loader instanceof ProxyClassLoader && ((ProxyClassLoader)loader).transitive) { 518 ClassLoader [] parents = ((ProxyClassLoader)loader).parents; 519 for (int i = 0; i < parents.length; i++) { 520 addRec(resultingUnique, resulting, parents[i]); 521 } 522 } 523 resultingUnique.add(loader); 524 resulting.add(loader); 525 } 526 527 537 private final Class smartLoadClass(String name, String fileName, String pkg) throws ClassNotFoundException { 538 Class c = findLoadedClass(name); 540 if(c != null) return c; 541 542 final ClassLoader owner = isSpecialResource(pkg) ? null : domainsByPackage.get(pkg); 543 if (owner == this) { 544 return simpleFindClass(name, fileName, pkg); 545 } 546 if (owner != null) { 547 if (owner instanceof ProxyClassLoader) { 549 return ((ProxyClassLoader)owner).fullFindClass(name, fileName, pkg); 550 } else { 551 return owner.loadClass(name); } 553 } 554 555 c = loadInOrder(name, fileName, pkg); 557 558 if (c != null) { 559 final ClassLoader owner2 = getClassClassLoader(c); domainsByPackage.put(new String (pkg).intern(), owner2); } 562 return c; 563 } 564 565 private static ClassLoader getClassClassLoader(final Class c) { 568 return AccessController.doPrivileged(new PrivilegedAction <ClassLoader >() { 569 public ClassLoader run() { 570 return c.getClassLoader(); 571 } 572 }); 573 } 574 575 private final Class loadInOrder( String name, String fileName, String pkg ) throws ClassNotFoundException { 576 ClassNotFoundException cached = null; 577 for (int i = 0; i < parents.length; i++) { 578 ClassLoader par = parents[i]; 579 if (!shouldDelegateResource(pkg, par)) continue; 580 if ((par instanceof ProxyClassLoader) && 581 ((ProxyClassLoader)par).shouldBeCheckedAsParentProxyClassLoader()) { 582 ProxyClassLoader pcl = (ProxyClassLoader)par; 583 Class c = pcl.fullFindClass(name, fileName, pkg); 584 if (c != null && (pcl.transitive || getClassClassLoader(c) == pcl)) return c; 588 } else { 589 boolean skip = false; 591 if (optimizeNBLoading()) { 592 if (name.startsWith("org.netbeans.") || name.startsWith("org.openide.") || name.endsWith(".Bundle") || name.endsWith("BeanInfo") || name.endsWith("Editor")) { if (par.getResource(fileName) == null) { 598 skip = true; 602 } 603 } 604 } 605 if (!skip) { 606 try { 607 return par.loadClass(name); 608 } catch( ClassNotFoundException cnfe ) { 609 cached = cnfe; 610 } 611 } 612 } 613 } 614 615 Class c = simpleFindClass(name, fileName, pkg); if (c != null) return c; 617 if (cached != null) throw cached; 618 return null; 619 } 620 621 private synchronized Class fullFindClass(String name, String fileName, String pkg) { 622 Class c = findLoadedClass(name); 623 if (c == null) { 624 c = simpleFindClass(name, fileName, pkg); 625 if (c != null) { 626 domainsByPackage.put(new String (pkg).intern(), this); } 628 } 629 return c; 630 } 631 632 private void addPackages(Map <String ,Package > all, Package [] pkgs) { 633 for (int i = 0; i < pkgs.length; i++) { 635 all.put(pkgs[i].getName(), pkgs[i]); 636 } 637 } 638 639 644 protected boolean shouldBeCheckedAsParentProxyClassLoader() { 645 return true; 646 } 647 648 651 protected boolean optimizeNBLoading() { 652 return true; 653 } 654 655 664 protected boolean isSpecialResource(String pkg) { 665 if (pkg.startsWith("META-INF/")) return true; 667 if (pkg.length() == 0) return true; 669 670 return false; 671 } 672 673 685 protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { 686 return true; 687 } 688 689 } 690 | Popular Tags |