1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 /* 18 * Copyright (C) 2008 The Android Open Source Project 19 * 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 33 package java.lang; 34 35 import dalvik.system.PathClassLoader; 36 import dalvik.system.VMStack; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.net.URL; 40 import java.nio.ByteBuffer; 41 import java.security.ProtectionDomain; 42 import java.util.Collection; 43 import java.util.Collections; 44 import java.util.Enumeration; 45 import java.util.HashMap; 46 import java.util.Map; 47 48 /** 49 * Loads classes and resources from a repository. One or more class loaders are 50 * installed at runtime. These are consulted whenever the runtime system needs a 51 * specific class that is not yet available in-memory. Typically, class loaders 52 * are grouped into a tree where child class loaders delegate all requests to 53 * parent class loaders. Only if the parent class loader cannot satisfy the 54 * request, the child class loader itself tries to handle it. 55 * <p> 56 * {@code ClassLoader} is an abstract class that implements the common 57 * infrastructure required by all class loaders. Android provides several 58 * concrete implementations of the class, with 59 * {@link dalvik.system.PathClassLoader} being the one typically used. Other 60 * applications may implement subclasses of {@code ClassLoader} to provide 61 * special ways for loading classes. 62 * </p> 63 * @see Class 64 */ 65 public abstract class ClassLoader { 66 67 /** 68 * The 'System' ClassLoader - the one that is responsible for loading 69 * classes from the classpath. It is not equal to the bootstrap class loader - 70 * that one handles the built-in classes. 71 * 72 * Because of a potential class initialization race between ClassLoader and 73 * java.lang.System, reproducible when using JDWP with "suspend=y", we defer 74 * creation of the system class loader until first use. We use a static 75 * inner class to get synchronization at init time without having to sync on 76 * every access. 77 * 78 * @see #getSystemClassLoader() 79 */ 80 static private class SystemClassLoader { 81 public static ClassLoader loader = ClassLoader.createSystemClassLoader(); 82 } 83 84 /** 85 * The parent ClassLoader. 86 */ 87 private ClassLoader parent; 88 89 /** 90 * The packages known to the class loader. 91 */ 92 private Map<String, Package> packages = new HashMap<String, Package>(); 93 94 /** 95 * Create the system class loader. Note this is NOT the bootstrap class 96 * loader (which is managed by the VM). We use a null value for the parent 97 * to indicate that the bootstrap loader is our parent. 98 */ 99 private static ClassLoader createSystemClassLoader() { 100 String classPath = System.getProperty("java.class.path", "."); 101 102 // String[] paths = classPath.split(":"); 103 // URL[] urls = new URL[paths.length]; 104 // for (int i = 0; i < paths.length; i++) { 105 // try { 106 // urls[i] = new URL("file://" + paths[i]); 107 // } 108 // catch (Exception ex) { 109 // ex.printStackTrace(); 110 // } 111 // } 112 // 113 // return new java.net.URLClassLoader(urls, null); 114 115 // TODO Make this a java.net.URLClassLoader once we have those? 116 return new PathClassLoader(classPath, BootClassLoader.getInstance()); 117 } 118 119 /** 120 * Returns the system class loader. This is the parent for new 121 * {@code ClassLoader} instances and is typically the class loader used to 122 * start the application. 123 */ 124 public static ClassLoader getSystemClassLoader() { 125 return SystemClassLoader.loader; 126 } 127 128 /** 129 * Finds the URL of the resource with the specified name. The system class 130 * loader's resource lookup algorithm is used to find the resource. 131 * 132 * @return the {@code URL} object for the requested resource or {@code null} 133 * if the resource can not be found. 134 * @param resName 135 * the name of the resource to find. 136 * @see Class#getResource 137 */ 138 public static URL getSystemResource(String resName) { 139 return SystemClassLoader.loader.getResource(resName); 140 } 141 142 /** 143 * Returns an enumeration of URLs for the resource with the specified name. 144 * The system class loader's resource lookup algorithm is used to find the 145 * resource. 146 * 147 * @return an enumeration of {@code URL} objects containing the requested 148 * resources. 149 * @param resName 150 * the name of the resource to find. 151 * @throws IOException 152 * if an I/O error occurs. 153 */ 154 public static Enumeration<URL> getSystemResources(String resName) throws IOException { 155 return SystemClassLoader.loader.getResources(resName); 156 } 157 158 /** 159 * Returns a stream for the resource with the specified name. The system 160 * class loader's resource lookup algorithm is used to find the resource. 161 * Basically, the contents of the java.class.path are searched in order, 162 * looking for a path which matches the specified resource. 163 * 164 * @return a stream for the resource or {@code null}. 165 * @param resName 166 * the name of the resource to find. 167 * @see Class#getResourceAsStream 168 */ 169 public static InputStream getSystemResourceAsStream(String resName) { 170 return SystemClassLoader.loader.getResourceAsStream(resName); 171 } 172 173 /** 174 * Constructs a new instance of this class with the system class loader as 175 * its parent. 176 */ 177 protected ClassLoader() { 178 this(getSystemClassLoader(), false); 179 } 180 181 /** 182 * Constructs a new instance of this class with the specified class loader 183 * as its parent. 184 * 185 * @param parentLoader 186 * The {@code ClassLoader} to use as the new class loader's 187 * parent. 188 */ 189 protected ClassLoader(ClassLoader parentLoader) { 190 this(parentLoader, false); 191 } 192 193 /* 194 * constructor for the BootClassLoader which needs parent to be null. 195 */ 196 ClassLoader(ClassLoader parentLoader, boolean nullAllowed) { 197 if (parentLoader == null && !nullAllowed) { 198 throw new NullPointerException("parentLoader == null && !nullAllowed"); 199 } 200 parent = parentLoader; 201 } 202 203 /** 204 * Constructs a new class from an array of bytes containing a class 205 * definition in class file format. 206 * 207 * @param classRep 208 * the memory image of a class file. 209 * @param offset 210 * the offset into {@code classRep}. 211 * @param length 212 * the length of the class file. 213 * @return the {@code Class} object created from the specified subset of 214 * data in {@code classRep}. 215 * @throws ClassFormatError 216 * if {@code classRep} does not contain a valid class. 217 * @throws IndexOutOfBoundsException 218 * if {@code offset < 0}, {@code length < 0} or if 219 * {@code offset + length} is greater than the length of 220 * {@code classRep}. 221 * @deprecated Use {@link #defineClass(String, byte[], int, int)} 222 */ 223 @Deprecated 224 protected final Class<?> defineClass(byte[] classRep, int offset, int length) 225 throws ClassFormatError { 226 227 return VMClassLoader.defineClass(this, classRep, offset, length); 228 } 229 230 /** 231 * Constructs a new class from an array of bytes containing a class 232 * definition in class file format. 233 * 234 * @param className 235 * the expected name of the new class, may be {@code null} if not 236 * known. 237 * @param classRep 238 * the memory image of a class file. 239 * @param offset 240 * the offset into {@code classRep}. 241 * @param length 242 * the length of the class file. 243 * @return the {@code Class} object created from the specified subset of 244 * data in {@code classRep}. 245 * @throws ClassFormatError 246 * if {@code classRep} does not contain a valid class. 247 * @throws IndexOutOfBoundsException 248 * if {@code offset < 0}, {@code length < 0} or if 249 * {@code offset + length} is greater than the length of 250 * {@code classRep}. 251 */ 252 protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length) 253 throws ClassFormatError { 254 255 // TODO Define a default ProtectionDomain on first use 256 return defineClass(className, classRep, offset, length, null); 257 } 258 259 /** 260 * Constructs a new class from an array of bytes containing a class 261 * definition in class file format and assigns the specified protection 262 * domain to the new class. If the provided protection domain is 263 * {@code null} then a default protection domain is assigned to the class. 264 * 265 * @param className 266 * the expected name of the new class, may be {@code null} if not 267 * known. 268 * @param classRep 269 * the memory image of a class file. 270 * @param offset 271 * the offset into {@code classRep}. 272 * @param length 273 * the length of the class file. 274 * @param protectionDomain 275 * the protection domain to assign to the loaded class, may be 276 * {@code null}. 277 * @return the {@code Class} object created from the specified subset of 278 * data in {@code classRep}. 279 * @throws ClassFormatError 280 * if {@code classRep} does not contain a valid class. 281 * @throws IndexOutOfBoundsException 282 * if {@code offset < 0}, {@code length < 0} or if 283 * {@code offset + length} is greater than the length of 284 * {@code classRep}. 285 * @throws NoClassDefFoundError 286 * if {@code className} is not equal to the name of the class 287 * contained in {@code classRep}. 288 */ 289 protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length, 290 ProtectionDomain protectionDomain) throws java.lang.ClassFormatError { 291 292 return VMClassLoader.defineClass(this, className, classRep, offset, length); 293 } 294 295 /** 296 * Defines a new class with the specified name, byte code from the byte 297 * buffer and the optional protection domain. If the provided protection 298 * domain is {@code null} then a default protection domain is assigned to 299 * the class. 300 * 301 * @param name 302 * the expected name of the new class, may be {@code null} if not 303 * known. 304 * @param b 305 * the byte buffer containing the byte code of the new class. 306 * @param protectionDomain 307 * the protection domain to assign to the loaded class, may be 308 * {@code null}. 309 * @return the {@code Class} object created from the data in {@code b}. 310 * @throws ClassFormatError 311 * if {@code b} does not contain a valid class. 312 * @throws NoClassDefFoundError 313 * if {@code className} is not equal to the name of the class 314 * contained in {@code b}. 315 */ 316 protected final Class<?> defineClass(String name, ByteBuffer b, 317 ProtectionDomain protectionDomain) throws ClassFormatError { 318 319 byte[] temp = new byte[b.remaining()]; 320 b.get(temp); 321 return defineClass(name, temp, 0, temp.length, protectionDomain); 322 } 323 324 /** 325 * Overridden by subclasses, throws a {@code ClassNotFoundException} by 326 * default. This method is called by {@code loadClass} after the parent 327 * {@code ClassLoader} has failed to find a loaded class of the same name. 328 * 329 * @param className 330 * the name of the class to look for. 331 * @return the {@code Class} object that is found. 332 * @throws ClassNotFoundException 333 * if the class cannot be found. 334 */ 335 protected Class<?> findClass(String className) throws ClassNotFoundException { 336 throw new ClassNotFoundException(className); 337 } 338 339 /** 340 * Returns the class with the specified name if it has already been loaded 341 * by the VM or {@code null} if it has not yet been loaded. 342 * 343 * @param className 344 * the name of the class to look for. 345 * @return the {@code Class} object or {@code null} if the requested class 346 * has not been loaded. 347 */ 348 protected final Class<?> findLoadedClass(String className) { 349 ClassLoader loader; 350 if (this == BootClassLoader.getInstance()) 351 loader = null; 352 else 353 loader = this; 354 return VMClassLoader.findLoadedClass(loader, className); 355 } 356 357 /** 358 * Finds the class with the specified name, loading it using the system 359 * class loader if necessary. 360 * 361 * @param className 362 * the name of the class to look for. 363 * @return the {@code Class} object with the requested {@code className}. 364 * @throws ClassNotFoundException 365 * if the class can not be found. 366 */ 367 protected final Class<?> findSystemClass(String className) throws ClassNotFoundException { 368 return Class.forName(className, false, getSystemClassLoader()); 369 } 370 371 /** 372 * Returns this class loader's parent. 373 * 374 * @return this class loader's parent or {@code null}. 375 */ 376 public final ClassLoader getParent() { 377 return parent; 378 } 379 380 /** 381 * Returns the URL of the resource with the specified name. This 382 * implementation first tries to use the parent class loader to find the 383 * resource; if this fails then {@link #findResource(String)} is called to 384 * find the requested resource. 385 * 386 * @param resName 387 * the name of the resource to find. 388 * @return the {@code URL} object for the requested resource or {@code null} 389 * if the resource can not be found 390 * @see Class#getResource 391 */ 392 public URL getResource(String resName) { 393 URL resource = parent.getResource(resName); 394 if (resource == null) { 395 resource = findResource(resName); 396 } 397 return resource; 398 } 399 400 /** 401 * Returns an enumeration of URLs for the resource with the specified name. 402 * This implementation first uses this class loader's parent to find the 403 * resource, then it calls {@link #findResources(String)} to get additional 404 * URLs. The returned enumeration contains the {@code URL} objects of both 405 * find operations. 406 * 407 * @return an enumeration of {@code URL} objects for the requested resource. 408 * @param resName 409 * the name of the resource to find. 410 * @throws IOException 411 * if an I/O error occurs. 412 */ 413 @SuppressWarnings("unchecked") 414 public Enumeration<URL> getResources(String resName) throws IOException { 415 416 Enumeration first = parent.getResources(resName); 417 Enumeration second = findResources(resName); 418 419 return new TwoEnumerationsInOne(first, second); 420 } 421 422 /** 423 * Returns a stream for the resource with the specified name. See 424 * {@link #getResource(String)} for a description of the lookup algorithm 425 * used to find the resource. 426 * 427 * @return a stream for the resource or {@code null} if the resource can not be found 428 * @param resName 429 * the name of the resource to find. 430 * @see Class#getResourceAsStream 431 */ 432 public InputStream getResourceAsStream(String resName) { 433 try { 434 URL url = getResource(resName); 435 if (url != null) { 436 return url.openStream(); 437 } 438 } catch (IOException ex) { 439 // Don't want to see the exception. 440 } 441 442 return null; 443 } 444 445 /** 446 * Loads the class with the specified name. Invoking this method is 447 * equivalent to calling {@code loadClass(className, false)}. 448 * <p> 449 * <strong>Note:</strong> In the Android reference implementation, the 450 * second parameter of {@link #loadClass(String, boolean)} is ignored 451 * anyway. 452 * </p> 453 * 454 * @return the {@code Class} object. 455 * @param className 456 * the name of the class to look for. 457 * @throws ClassNotFoundException 458 * if the class can not be found. 459 */ 460 public Class<?> loadClass(String className) throws ClassNotFoundException { 461 return loadClass(className, false); 462 } 463 464 /** 465 * Loads the class with the specified name, optionally linking it after 466 * loading. The following steps are performed: 467 * <ol> 468 * <li> Call {@link #findLoadedClass(String)} to determine if the requested 469 * class has already been loaded.</li> 470 * <li>If the class has not yet been loaded: Invoke this method on the 471 * parent class loader.</li> 472 * <li>If the class has still not been loaded: Call 473 * {@link #findClass(String)} to find the class.</li> 474 * </ol> 475 * <p> 476 * <strong>Note:</strong> In the Android reference implementation, the 477 * {@code resolve} parameter is ignored; classes are never linked. 478 * </p> 479 * 480 * @return the {@code Class} object. 481 * @param className 482 * the name of the class to look for. 483 * @param resolve 484 * Indicates if the class should be resolved after loading. This 485 * parameter is ignored on the Android reference implementation; 486 * classes are not resolved. 487 * @throws ClassNotFoundException 488 * if the class can not be found. 489 */ 490 protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { 491 Class<?> clazz = findLoadedClass(className); 492 493 if (clazz == null) { 494 try { 495 clazz = parent.loadClass(className, false); 496 } catch (ClassNotFoundException e) { 497 // Don't want to see this. 498 } 499 500 if (clazz == null) { 501 clazz = findClass(className); 502 } 503 } 504 505 return clazz; 506 } 507 508 /** 509 * Forces a class to be linked (initialized). If the class has already been 510 * linked this operation has no effect. 511 * <p> 512 * <strong>Note:</strong> In the Android reference implementation, this 513 * method has no effect. 514 * </p> 515 * 516 * @param clazz 517 * the class to link. 518 */ 519 protected final void resolveClass(Class<?> clazz) { 520 // no-op, doesn't make sense on android. 521 } 522 523 /** 524 * Finds the URL of the resource with the specified name. This 525 * implementation just returns {@code null}; it should be overridden in 526 * subclasses. 527 * 528 * @param resName 529 * the name of the resource to find. 530 * @return the {@code URL} object for the requested resource. 531 */ 532 protected URL findResource(String resName) { 533 return null; 534 } 535 536 /** 537 * Finds an enumeration of URLs for the resource with the specified name. 538 * This implementation just returns an empty {@code Enumeration}; it should 539 * be overridden in subclasses. 540 * 541 * @param resName 542 * the name of the resource to find. 543 * @return an enumeration of {@code URL} objects for the requested resource. 544 * @throws IOException 545 * if an I/O error occurs. 546 */ 547 @SuppressWarnings( { 548 "unchecked", "unused" 549 }) 550 protected Enumeration<URL> findResources(String resName) throws IOException { 551 return Collections.emptyEnumeration(); 552 } 553 554 /** 555 * Returns the absolute path of the native library with the specified name, 556 * or {@code null}. If this method returns {@code null} then the virtual 557 * machine searches the directories specified by the system property 558 * "java.library.path". 559 * <p> 560 * This implementation always returns {@code null}. 561 * </p> 562 * 563 * @param libName 564 * the name of the library to find. 565 * @return the absolute path of the library. 566 */ 567 protected String findLibrary(String libName) { 568 return null; 569 } 570 571 /** 572 * Returns the package with the specified name. Package information is 573 * searched in this class loader. 574 * 575 * @param name 576 * the name of the package to find. 577 * @return the package with the requested name; {@code null} if the package 578 * can not be found. 579 */ 580 protected Package getPackage(String name) { 581 synchronized (packages) { 582 return packages.get(name); 583 } 584 } 585 586 /** 587 * Returns all the packages known to this class loader. 588 * 589 * @return an array with all packages known to this class loader. 590 */ 591 protected Package[] getPackages() { 592 synchronized (packages) { 593 Collection<Package> col = packages.values(); 594 Package[] result = new Package[col.size()]; 595 col.toArray(result); 596 return result; 597 } 598 } 599 600 /** 601 * Defines and returns a new {@code Package} using the specified 602 * information. If {@code sealBase} is {@code null}, the package is left 603 * unsealed. Otherwise, the package is sealed using this URL. 604 * 605 * @param name 606 * the name of the package. 607 * @param specTitle 608 * the title of the specification. 609 * @param specVersion 610 * the version of the specification. 611 * @param specVendor 612 * the vendor of the specification. 613 * @param implTitle 614 * the implementation title. 615 * @param implVersion 616 * the implementation version. 617 * @param implVendor 618 * the specification vendor. 619 * @param sealBase 620 * the URL used to seal this package or {@code null} to leave the 621 * package unsealed. 622 * @return the {@code Package} object that has been created. 623 * @throws IllegalArgumentException 624 * if a package with the specified name already exists. 625 */ 626 protected Package definePackage(String name, String specTitle, String specVersion, 627 String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) 628 throws IllegalArgumentException { 629 630 synchronized (packages) { 631 if (packages.containsKey(name)) { 632 throw new IllegalArgumentException("Package " + name + " already defined"); 633 } 634 635 Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle, 636 implVersion, implVendor, sealBase); 637 638 packages.put(name, newPackage); 639 640 return newPackage; 641 } 642 } 643 644 /** 645 * Sets the signers of the specified class. This implementation does 646 * nothing. 647 * 648 * @param c 649 * the {@code Class} object for which to set the signers. 650 * @param signers 651 * the signers for {@code c}. 652 */ 653 protected final void setSigners(Class<?> c, Object[] signers) { 654 } 655 656 /** 657 * Sets the assertion status of the class with the specified name. 658 * <p> 659 * <strong>Note: </strong>This method does nothing in the Android reference 660 * implementation. 661 * </p> 662 * 663 * @param cname 664 * the name of the class for which to set the assertion status. 665 * @param enable 666 * the new assertion status. 667 */ 668 public void setClassAssertionStatus(String cname, boolean enable) { 669 } 670 671 /** 672 * Sets the assertion status of the package with the specified name. 673 * <p> 674 * <strong>Note: </strong>This method does nothing in the Android reference 675 * implementation. 676 * </p> 677 * 678 * @param pname 679 * the name of the package for which to set the assertion status. 680 * @param enable 681 * the new assertion status. 682 */ 683 public void setPackageAssertionStatus(String pname, boolean enable) { 684 } 685 686 /** 687 * Sets the default assertion status for this class loader. 688 * <p> 689 * <strong>Note: </strong>This method does nothing in the Android reference 690 * implementation. 691 * </p> 692 * 693 * @param enable 694 * the new assertion status. 695 */ 696 public void setDefaultAssertionStatus(boolean enable) { 697 } 698 699 /** 700 * Sets the default assertion status for this class loader to {@code false} 701 * and removes any package default and class assertion status settings. 702 * <p> 703 * <strong>Note:</strong> This method does nothing in the Android reference 704 * implementation. 705 * </p> 706 */ 707 public void clearAssertionStatus() { 708 } 709 } 710 711 /* 712 * Provides a helper class that combines two existing URL enumerations into one. 713 * It is required for the getResources() methods. Items are fetched from the 714 * first enumeration until it's empty, then from the second one. 715 */ 716 class TwoEnumerationsInOne implements Enumeration<URL> { 717 718 private Enumeration<URL> first; 719 720 private Enumeration<URL> second; 721 722 public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) { 723 this.first = first; 724 this.second = second; 725 } 726 727 public boolean hasMoreElements() { 728 return first.hasMoreElements() || second.hasMoreElements(); 729 } 730 731 public URL nextElement() { 732 if (first.hasMoreElements()) { 733 return first.nextElement(); 734 } else { 735 return second.nextElement(); 736 } 737 } 738 739 } 740 741 /** 742 * Provides an explicit representation of the boot class loader. It sits at the 743 * head of the class loader chain and delegates requests to the VM's internal 744 * class loading mechanism. 745 */ 746 class BootClassLoader extends ClassLoader { 747 748 private static BootClassLoader instance; 749 750 @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED") 751 public static synchronized BootClassLoader getInstance() { 752 if (instance == null) { 753 instance = new BootClassLoader(); 754 } 755 756 return instance; 757 } 758 759 public BootClassLoader() { 760 super(null, true); 761 } 762 763 @Override 764 protected Class<?> findClass(String name) throws ClassNotFoundException { 765 return VMClassLoader.loadClass(name, false); 766 } 767 768 @Override 769 protected URL findResource(String name) { 770 return VMClassLoader.getResource(name); 771 } 772 773 @SuppressWarnings("unused") 774 @Override 775 protected Enumeration<URL> findResources(String resName) throws IOException { 776 return Collections.enumeration(VMClassLoader.getResources(resName)); 777 } 778 779 /** 780 * Returns package information for the given package. Unfortunately, the 781 * Android BootClassLoader doesn't really have this information, and as a 782 * non-secure ClassLoader, it isn't even required to, according to the spec. 783 * Yet, we want to provide it, in order to make all those hopeful callers of 784 * {@code myClass.getPackage().getName()} happy. Thus we construct a Package 785 * object the first time it is being requested and fill most of the fields 786 * with dummy values. The Package object is then put into the ClassLoader's 787 * Package cache, so we see the same one next time. We don't create Package 788 * objects for null arguments or for the default package. 789 * <p> 790 * There a limited chance that we end up with multiple Package objects 791 * representing the same package: It can happen when when a package is 792 * scattered across different JAR files being loaded by different 793 * ClassLoaders. Rather unlikely, and given that this whole thing is more or 794 * less a workaround, probably not worth the effort. 795 */ 796 @Override 797 protected Package getPackage(String name) { 798 if (name != null && !name.isEmpty()) { 799 synchronized (this) { 800 Package pack = super.getPackage(name); 801 802 if (pack == null) { 803 pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0", 804 "Unknown", null); 805 } 806 807 return pack; 808 } 809 } 810 811 return null; 812 } 813 814 @Override 815 public URL getResource(String resName) { 816 return findResource(resName); 817 } 818 819 @Override 820 protected Class<?> loadClass(String className, boolean resolve) 821 throws ClassNotFoundException { 822 Class<?> clazz = findLoadedClass(className); 823 824 if (clazz == null) { 825 clazz = findClass(className); 826 } 827 828 return clazz; 829 } 830 831 @Override 832 public Enumeration<URL> getResources(String resName) throws IOException { 833 return findResources(resName); 834 } 835 } 836 837 /** 838 * TODO Open issues - Missing / empty methods - Signer stuff - Protection 839 * domains - Assertions 840 */ 841