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 33package java.lang; 34 35import dalvik.system.PathClassLoader; 36import dalvik.system.VMStack; 37import java.io.IOException; 38import java.io.InputStream; 39import java.net.URL; 40import java.nio.ByteBuffer; 41import java.security.ProtectionDomain; 42import java.util.Collection; 43import java.util.Collections; 44import java.util.Enumeration; 45import java.util.HashMap; 46import 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 */ 65public 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)} instead. 222 */ 223 @Deprecated 224 protected final Class<?> defineClass(byte[] classRep, int offset, int length) 225 throws ClassFormatError { 226 throw new UnsupportedOperationException("can't load this type of class file"); 227 } 228 229 /** 230 * Constructs a new class from an array of bytes containing a class 231 * definition in class file format. 232 * 233 * @param className 234 * the expected name of the new class, may be {@code null} if not 235 * known. 236 * @param classRep 237 * the memory image of a class file. 238 * @param offset 239 * the offset into {@code classRep}. 240 * @param length 241 * the length of the class file. 242 * @return the {@code Class} object created from the specified subset of 243 * data in {@code classRep}. 244 * @throws ClassFormatError 245 * if {@code classRep} does not contain a valid class. 246 * @throws IndexOutOfBoundsException 247 * if {@code offset < 0}, {@code length < 0} or if 248 * {@code offset + length} is greater than the length of 249 * {@code classRep}. 250 */ 251 protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length) 252 throws ClassFormatError { 253 throw new UnsupportedOperationException("can't load this type of class file"); 254 } 255 256 /** 257 * Constructs a new class from an array of bytes containing a class 258 * definition in class file format and assigns the specified protection 259 * domain to the new class. If the provided protection domain is 260 * {@code null} then a default protection domain is assigned to the class. 261 * 262 * @param className 263 * the expected name of the new class, may be {@code null} if not 264 * known. 265 * @param classRep 266 * the memory image of a class file. 267 * @param offset 268 * the offset into {@code classRep}. 269 * @param length 270 * the length of the class file. 271 * @param protectionDomain 272 * the protection domain to assign to the loaded class, may be 273 * {@code null}. 274 * @return the {@code Class} object created from the specified subset of 275 * data in {@code classRep}. 276 * @throws ClassFormatError 277 * if {@code classRep} does not contain a valid class. 278 * @throws IndexOutOfBoundsException 279 * if {@code offset < 0}, {@code length < 0} or if 280 * {@code offset + length} is greater than the length of 281 * {@code classRep}. 282 * @throws NoClassDefFoundError 283 * if {@code className} is not equal to the name of the class 284 * contained in {@code classRep}. 285 */ 286 protected final Class<?> defineClass(String className, byte[] classRep, int offset, int length, 287 ProtectionDomain protectionDomain) throws java.lang.ClassFormatError { 288 throw new UnsupportedOperationException("can't load this type of class file"); 289 } 290 291 /** 292 * Defines a new class with the specified name, byte code from the byte 293 * buffer and the optional protection domain. If the provided protection 294 * domain is {@code null} then a default protection domain is assigned to 295 * the class. 296 * 297 * @param name 298 * the expected name of the new class, may be {@code null} if not 299 * known. 300 * @param b 301 * the byte buffer containing the byte code of the new class. 302 * @param protectionDomain 303 * the protection domain to assign to the loaded class, may be 304 * {@code null}. 305 * @return the {@code Class} object created from the data in {@code b}. 306 * @throws ClassFormatError 307 * if {@code b} does not contain a valid class. 308 * @throws NoClassDefFoundError 309 * if {@code className} is not equal to the name of the class 310 * contained in {@code b}. 311 */ 312 protected final Class<?> defineClass(String name, ByteBuffer b, 313 ProtectionDomain protectionDomain) throws ClassFormatError { 314 315 byte[] temp = new byte[b.remaining()]; 316 b.get(temp); 317 return defineClass(name, temp, 0, temp.length, protectionDomain); 318 } 319 320 /** 321 * Overridden by subclasses, throws a {@code ClassNotFoundException} by 322 * default. This method is called by {@code loadClass} after the parent 323 * {@code ClassLoader} has failed to find a loaded class of the same name. 324 * 325 * @param className 326 * the name of the class to look for. 327 * @return the {@code Class} object that is found. 328 * @throws ClassNotFoundException 329 * if the class cannot be found. 330 */ 331 protected Class<?> findClass(String className) throws ClassNotFoundException { 332 throw new ClassNotFoundException(className); 333 } 334 335 /** 336 * Returns the class with the specified name if it has already been loaded 337 * by the VM or {@code null} if it has not yet been loaded. 338 * 339 * @param className 340 * the name of the class to look for. 341 * @return the {@code Class} object or {@code null} if the requested class 342 * has not been loaded. 343 */ 344 protected final Class<?> findLoadedClass(String className) { 345 ClassLoader loader; 346 if (this == BootClassLoader.getInstance()) 347 loader = null; 348 else 349 loader = this; 350 return VMClassLoader.findLoadedClass(loader, className); 351 } 352 353 /** 354 * Finds the class with the specified name, loading it using the system 355 * class loader if necessary. 356 * 357 * @param className 358 * the name of the class to look for. 359 * @return the {@code Class} object with the requested {@code className}. 360 * @throws ClassNotFoundException 361 * if the class can not be found. 362 */ 363 protected final Class<?> findSystemClass(String className) throws ClassNotFoundException { 364 return Class.forName(className, false, getSystemClassLoader()); 365 } 366 367 /** 368 * Returns this class loader's parent. 369 * 370 * @return this class loader's parent or {@code null}. 371 */ 372 public final ClassLoader getParent() { 373 return parent; 374 } 375 376 /** 377 * Returns the URL of the resource with the specified name. This 378 * implementation first tries to use the parent class loader to find the 379 * resource; if this fails then {@link #findResource(String)} is called to 380 * find the requested resource. 381 * 382 * @param resName 383 * the name of the resource to find. 384 * @return the {@code URL} object for the requested resource or {@code null} 385 * if the resource can not be found 386 * @see Class#getResource 387 */ 388 public URL getResource(String resName) { 389 URL resource = parent.getResource(resName); 390 if (resource == null) { 391 resource = findResource(resName); 392 } 393 return resource; 394 } 395 396 /** 397 * Returns an enumeration of URLs for the resource with the specified name. 398 * This implementation first uses this class loader's parent to find the 399 * resource, then it calls {@link #findResources(String)} to get additional 400 * URLs. The returned enumeration contains the {@code URL} objects of both 401 * find operations. 402 * 403 * @return an enumeration of {@code URL} objects for the requested resource. 404 * @param resName 405 * the name of the resource to find. 406 * @throws IOException 407 * if an I/O error occurs. 408 */ 409 @SuppressWarnings("unchecked") 410 public Enumeration<URL> getResources(String resName) throws IOException { 411 412 Enumeration first = parent.getResources(resName); 413 Enumeration second = findResources(resName); 414 415 return new TwoEnumerationsInOne(first, second); 416 } 417 418 /** 419 * Returns a stream for the resource with the specified name. See 420 * {@link #getResource(String)} for a description of the lookup algorithm 421 * used to find the resource. 422 * 423 * @return a stream for the resource or {@code null} if the resource can not be found 424 * @param resName 425 * the name of the resource to find. 426 * @see Class#getResourceAsStream 427 */ 428 public InputStream getResourceAsStream(String resName) { 429 try { 430 URL url = getResource(resName); 431 if (url != null) { 432 return url.openStream(); 433 } 434 } catch (IOException ex) { 435 // Don't want to see the exception. 436 } 437 438 return null; 439 } 440 441 /** 442 * Loads the class with the specified name. Invoking this method is 443 * equivalent to calling {@code loadClass(className, false)}. 444 * <p> 445 * <strong>Note:</strong> In the Android reference implementation, the 446 * second parameter of {@link #loadClass(String, boolean)} is ignored 447 * anyway. 448 * </p> 449 * 450 * @return the {@code Class} object. 451 * @param className 452 * the name of the class to look for. 453 * @throws ClassNotFoundException 454 * if the class can not be found. 455 */ 456 public Class<?> loadClass(String className) throws ClassNotFoundException { 457 return loadClass(className, false); 458 } 459 460 /** 461 * Loads the class with the specified name, optionally linking it after 462 * loading. The following steps are performed: 463 * <ol> 464 * <li> Call {@link #findLoadedClass(String)} to determine if the requested 465 * class has already been loaded.</li> 466 * <li>If the class has not yet been loaded: Invoke this method on the 467 * parent class loader.</li> 468 * <li>If the class has still not been loaded: Call 469 * {@link #findClass(String)} to find the class.</li> 470 * </ol> 471 * <p> 472 * <strong>Note:</strong> In the Android reference implementation, the 473 * {@code resolve} parameter is ignored; classes are never linked. 474 * </p> 475 * 476 * @return the {@code Class} object. 477 * @param className 478 * the name of the class to look for. 479 * @param resolve 480 * Indicates if the class should be resolved after loading. This 481 * parameter is ignored on the Android reference implementation; 482 * classes are not resolved. 483 * @throws ClassNotFoundException 484 * if the class can not be found. 485 */ 486 protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { 487 Class<?> clazz = findLoadedClass(className); 488 489 if (clazz == null) { 490 try { 491 clazz = parent.loadClass(className, false); 492 } catch (ClassNotFoundException e) { 493 // Don't want to see this. 494 } 495 496 if (clazz == null) { 497 clazz = findClass(className); 498 } 499 } 500 501 return clazz; 502 } 503 504 /** 505 * Forces a class to be linked (initialized). If the class has already been 506 * linked this operation has no effect. 507 * <p> 508 * <strong>Note:</strong> In the Android reference implementation, this 509 * method has no effect. 510 * </p> 511 * 512 * @param clazz 513 * the class to link. 514 */ 515 protected final void resolveClass(Class<?> clazz) { 516 // no-op, doesn't make sense on android. 517 } 518 519 /** 520 * Finds the URL of the resource with the specified name. This 521 * implementation just returns {@code null}; it should be overridden in 522 * subclasses. 523 * 524 * @param resName 525 * the name of the resource to find. 526 * @return the {@code URL} object for the requested resource. 527 */ 528 protected URL findResource(String resName) { 529 return null; 530 } 531 532 /** 533 * Finds an enumeration of URLs for the resource with the specified name. 534 * This implementation just returns an empty {@code Enumeration}; it should 535 * be overridden in subclasses. 536 * 537 * @param resName 538 * the name of the resource to find. 539 * @return an enumeration of {@code URL} objects for the requested resource. 540 * @throws IOException 541 * if an I/O error occurs. 542 */ 543 @SuppressWarnings( { 544 "unchecked", "unused" 545 }) 546 protected Enumeration<URL> findResources(String resName) throws IOException { 547 return Collections.emptyEnumeration(); 548 } 549 550 /** 551 * Returns the absolute path of the native library with the specified name, 552 * or {@code null}. If this method returns {@code null} then the virtual 553 * machine searches the directories specified by the system property 554 * "java.library.path". 555 * <p> 556 * This implementation always returns {@code null}. 557 * </p> 558 * 559 * @param libName 560 * the name of the library to find. 561 * @return the absolute path of the library. 562 */ 563 protected String findLibrary(String libName) { 564 return null; 565 } 566 567 /** 568 * Returns the package with the specified name. Package information is 569 * searched in this class loader. 570 * 571 * @param name 572 * the name of the package to find. 573 * @return the package with the requested name; {@code null} if the package 574 * can not be found. 575 */ 576 protected Package getPackage(String name) { 577 synchronized (packages) { 578 return packages.get(name); 579 } 580 } 581 582 /** 583 * Returns all the packages known to this class loader. 584 * 585 * @return an array with all packages known to this class loader. 586 */ 587 protected Package[] getPackages() { 588 synchronized (packages) { 589 Collection<Package> col = packages.values(); 590 Package[] result = new Package[col.size()]; 591 col.toArray(result); 592 return result; 593 } 594 } 595 596 /** 597 * Defines and returns a new {@code Package} using the specified 598 * information. If {@code sealBase} is {@code null}, the package is left 599 * unsealed. Otherwise, the package is sealed using this URL. 600 * 601 * @param name 602 * the name of the package. 603 * @param specTitle 604 * the title of the specification. 605 * @param specVersion 606 * the version of the specification. 607 * @param specVendor 608 * the vendor of the specification. 609 * @param implTitle 610 * the implementation title. 611 * @param implVersion 612 * the implementation version. 613 * @param implVendor 614 * the specification vendor. 615 * @param sealBase 616 * the URL used to seal this package or {@code null} to leave the 617 * package unsealed. 618 * @return the {@code Package} object that has been created. 619 * @throws IllegalArgumentException 620 * if a package with the specified name already exists. 621 */ 622 protected Package definePackage(String name, String specTitle, String specVersion, 623 String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) 624 throws IllegalArgumentException { 625 626 synchronized (packages) { 627 if (packages.containsKey(name)) { 628 throw new IllegalArgumentException("Package " + name + " already defined"); 629 } 630 631 Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle, 632 implVersion, implVendor, sealBase); 633 634 packages.put(name, newPackage); 635 636 return newPackage; 637 } 638 } 639 640 /** 641 * Sets the signers of the specified class. This implementation does 642 * nothing. 643 * 644 * @param c 645 * the {@code Class} object for which to set the signers. 646 * @param signers 647 * the signers for {@code c}. 648 */ 649 protected final void setSigners(Class<?> c, Object[] signers) { 650 } 651 652 /** 653 * Sets the assertion status of the class with the specified name. 654 * <p> 655 * <strong>Note: </strong>This method does nothing in the Android reference 656 * implementation. 657 * </p> 658 * 659 * @param cname 660 * the name of the class for which to set the assertion status. 661 * @param enable 662 * the new assertion status. 663 */ 664 public void setClassAssertionStatus(String cname, boolean enable) { 665 } 666 667 /** 668 * Sets the assertion status of the package with the specified name. 669 * <p> 670 * <strong>Note: </strong>This method does nothing in the Android reference 671 * implementation. 672 * </p> 673 * 674 * @param pname 675 * the name of the package for which to set the assertion status. 676 * @param enable 677 * the new assertion status. 678 */ 679 public void setPackageAssertionStatus(String pname, boolean enable) { 680 } 681 682 /** 683 * Sets the default assertion status for this class loader. 684 * <p> 685 * <strong>Note: </strong>This method does nothing in the Android reference 686 * implementation. 687 * </p> 688 * 689 * @param enable 690 * the new assertion status. 691 */ 692 public void setDefaultAssertionStatus(boolean enable) { 693 } 694 695 /** 696 * Sets the default assertion status for this class loader to {@code false} 697 * and removes any package default and class assertion status settings. 698 * <p> 699 * <strong>Note:</strong> This method does nothing in the Android reference 700 * implementation. 701 * </p> 702 */ 703 public void clearAssertionStatus() { 704 } 705} 706 707/* 708 * Provides a helper class that combines two existing URL enumerations into one. 709 * It is required for the getResources() methods. Items are fetched from the 710 * first enumeration until it's empty, then from the second one. 711 */ 712class TwoEnumerationsInOne implements Enumeration<URL> { 713 714 private Enumeration<URL> first; 715 716 private Enumeration<URL> second; 717 718 public TwoEnumerationsInOne(Enumeration<URL> first, Enumeration<URL> second) { 719 this.first = first; 720 this.second = second; 721 } 722 723 public boolean hasMoreElements() { 724 return first.hasMoreElements() || second.hasMoreElements(); 725 } 726 727 public URL nextElement() { 728 if (first.hasMoreElements()) { 729 return first.nextElement(); 730 } else { 731 return second.nextElement(); 732 } 733 } 734 735} 736 737/** 738 * Provides an explicit representation of the boot class loader. It sits at the 739 * head of the class loader chain and delegates requests to the VM's internal 740 * class loading mechanism. 741 */ 742class BootClassLoader extends ClassLoader { 743 744 private static BootClassLoader instance; 745 746 @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED") 747 public static synchronized BootClassLoader getInstance() { 748 if (instance == null) { 749 instance = new BootClassLoader(); 750 } 751 752 return instance; 753 } 754 755 public BootClassLoader() { 756 super(null, true); 757 } 758 759 @Override 760 protected Class<?> findClass(String name) throws ClassNotFoundException { 761 return VMClassLoader.loadClass(name, false); 762 } 763 764 @Override 765 protected URL findResource(String name) { 766 return VMClassLoader.getResource(name); 767 } 768 769 @SuppressWarnings("unused") 770 @Override 771 protected Enumeration<URL> findResources(String resName) throws IOException { 772 return Collections.enumeration(VMClassLoader.getResources(resName)); 773 } 774 775 /** 776 * Returns package information for the given package. Unfortunately, the 777 * Android BootClassLoader doesn't really have this information, and as a 778 * non-secure ClassLoader, it isn't even required to, according to the spec. 779 * Yet, we want to provide it, in order to make all those hopeful callers of 780 * {@code myClass.getPackage().getName()} happy. Thus we construct a Package 781 * object the first time it is being requested and fill most of the fields 782 * with dummy values. The Package object is then put into the ClassLoader's 783 * Package cache, so we see the same one next time. We don't create Package 784 * objects for null arguments or for the default package. 785 * <p> 786 * There a limited chance that we end up with multiple Package objects 787 * representing the same package: It can happen when when a package is 788 * scattered across different JAR files being loaded by different 789 * ClassLoaders. Rather unlikely, and given that this whole thing is more or 790 * less a workaround, probably not worth the effort. 791 */ 792 @Override 793 protected Package getPackage(String name) { 794 if (name != null && !name.isEmpty()) { 795 synchronized (this) { 796 Package pack = super.getPackage(name); 797 798 if (pack == null) { 799 pack = definePackage(name, "Unknown", "0.0", "Unknown", "Unknown", "0.0", 800 "Unknown", null); 801 } 802 803 return pack; 804 } 805 } 806 807 return null; 808 } 809 810 @Override 811 public URL getResource(String resName) { 812 return findResource(resName); 813 } 814 815 @Override 816 protected Class<?> loadClass(String className, boolean resolve) 817 throws ClassNotFoundException { 818 Class<?> clazz = findLoadedClass(className); 819 820 if (clazz == null) { 821 clazz = findClass(className); 822 } 823 824 return clazz; 825 } 826 827 @Override 828 public Enumeration<URL> getResources(String resName) throws IOException { 829 return findResources(resName); 830 } 831} 832 833/** 834 * TODO Open issues - Missing / empty methods - Signer stuff - Protection 835 * domains - Assertions 836 */ 837