/plugins/JavaSideKick/tags/javasidekick-2-3-4/src/sidekick/java/classloader/AntClassLoader.java

# · Java · 1172 lines · 523 code · 107 blank · 542 comment · 121 complexity · 636f447bf3b8a00675c21fffc667e47d MD5 · raw file

  1. /*
  2. * Copyright 2000-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package sidekick.java.classloader;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.File;
  20. import java.io.FileInputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.lang.reflect.Constructor;
  24. import java.lang.reflect.InvocationTargetException;
  25. import java.lang.reflect.Method;
  26. import java.net.MalformedURLException;
  27. import java.net.URL;
  28. import java.util.Enumeration;
  29. import java.util.Hashtable;
  30. import java.util.Iterator;
  31. import java.util.Vector;
  32. import java.util.zip.ZipEntry;
  33. import java.util.zip.ZipFile;
  34. import sidekick.java.util.*;
  35. /**
  36. * danson: I needed a classloader, so I borrowed this one from Ant since it
  37. * already does most of what I need. I removed a bunch of Ant specific stuff
  38. * that I don't need.
  39. * ---
  40. * Used to load classes within ant with a different classpath from
  41. * that used to start ant. Note that it is possible to force a class
  42. * into this loader even when that class is on the system classpath by
  43. * using the forceLoadClass method. Any subsequent classes loaded by that
  44. * class will then use this loader rather than the system class loader.
  45. *
  46. */
  47. public class AntClassLoader extends ClassLoader {
  48. private static final FileUtils FILE_UTILS = FileUtils.newFileUtils();
  49. /**
  50. * An enumeration of all resources of a given name found within the
  51. * classpath of this class loader. This enumeration is used by the
  52. * ClassLoader.findResources method, which is in
  53. * turn used by the ClassLoader.getResources method.
  54. *
  55. * @see AntClassLoader#findResources(String)
  56. * @see java.lang.ClassLoader#getResources(String)
  57. */
  58. private class ResourceEnumeration implements Enumeration {
  59. /**
  60. * The name of the resource being searched for.
  61. */
  62. private String resourceName;
  63. /**
  64. * The index of the next classpath element to search.
  65. */
  66. private int pathElementsIndex;
  67. /**
  68. * The URL of the next resource to return in the enumeration. If this
  69. * field is <code>null</code> then the enumeration has been completed,
  70. * i.e., there are no more elements to return.
  71. */
  72. private URL nextResource;
  73. /**
  74. * Constructs a new enumeration of resources of the given name found
  75. * within this class loader's classpath.
  76. *
  77. * @param name the name of the resource to search for.
  78. */
  79. ResourceEnumeration(String name) {
  80. this.resourceName = name;
  81. this.pathElementsIndex = 0;
  82. findNextResource();
  83. }
  84. /**
  85. * Indicates whether there are more elements in the enumeration to
  86. * return.
  87. *
  88. * @return <code>true</code> if there are more elements in the
  89. * enumeration; <code>false</code> otherwise.
  90. */
  91. public boolean hasMoreElements() {
  92. return (this.nextResource != null);
  93. }
  94. /**
  95. * Returns the next resource in the enumeration.
  96. *
  97. * @return the next resource in the enumeration
  98. */
  99. public Object nextElement() {
  100. URL ret = this.nextResource;
  101. findNextResource();
  102. return ret;
  103. }
  104. /**
  105. * Locates the next resource of the correct name in the classpath and
  106. * sets <code>nextResource</code> to the URL of that resource. If no
  107. * more resources can be found, <code>nextResource</code> is set to
  108. * <code>null</code>.
  109. */
  110. private void findNextResource() {
  111. URL url = null;
  112. while ((pathElementsIndex < pathComponents.size())
  113. && (url == null)) {
  114. try {
  115. File pathComponent
  116. = (File) pathComponents.elementAt(pathElementsIndex);
  117. url = getResourceURL(pathComponent, this.resourceName);
  118. pathElementsIndex++;
  119. } catch (Exception e) {
  120. // ignore path elements which are not valid relative to the
  121. // project
  122. }
  123. }
  124. this.nextResource = url;
  125. }
  126. }
  127. /**
  128. * The size of buffers to be used in this classloader.
  129. */
  130. private static final int BUFFER_SIZE = 8192;
  131. /**
  132. * Number of array elements in a test array of strings
  133. */
  134. private static final int NUMBER_OF_STRINGS = 256;
  135. /**
  136. * The components of the classpath that the classloader searches
  137. * for classes.
  138. */
  139. private Vector pathComponents = new Vector();
  140. /**
  141. * Indicates whether the parent class loader should be
  142. * consulted before trying to load with this class loader.
  143. */
  144. private boolean parentFirst = true;
  145. /**
  146. * These are the package roots that are to be loaded by the parent class
  147. * loader regardless of whether the parent class loader is being searched
  148. * first or not.
  149. */
  150. private Vector systemPackages = new Vector();
  151. /**
  152. * These are the package roots that are to be loaded by this class loader
  153. * regardless of whether the parent class loader is being searched first
  154. * or not.
  155. */
  156. private Vector loaderPackages = new Vector();
  157. /**
  158. * Whether or not this classloader will ignore the base
  159. * classloader if it can't find a class.
  160. *
  161. * @see #setIsolated(boolean)
  162. */
  163. private boolean ignoreBase = false;
  164. /**
  165. * The parent class loader, if one is given or can be determined.
  166. */
  167. private ClassLoader parent = null;
  168. /**
  169. * A hashtable of zip files opened by the classloader (File to ZipFile).
  170. */
  171. private Hashtable zipFiles = new Hashtable();
  172. /**
  173. * The context loader saved when setting the thread's current
  174. * context loader.
  175. */
  176. private ClassLoader savedContextLoader = null;
  177. /**
  178. * Whether or not the context loader is currently saved.
  179. */
  180. private boolean isContextLoaderSaved = false;
  181. /**
  182. * Reflection method reference for getProtectionDomain;
  183. * used to avoid 1.1-compatibility problems.
  184. */
  185. private static Method getProtectionDomain = null;
  186. /**
  187. * Reflection method reference for defineClassProtectionDomain;
  188. * used to avoid 1.1-compatibility problems.
  189. */
  190. private static Method defineClassProtectionDomain = null;
  191. // Set up the reflection-based Java2 methods if possible
  192. static {
  193. try {
  194. getProtectionDomain
  195. = Class.class.getMethod("getProtectionDomain", new Class[0]);
  196. Class protectionDomain
  197. = Class.forName("java.security.ProtectionDomain");
  198. Class[] args = new Class[] {String.class, byte[].class,
  199. Integer.TYPE, Integer.TYPE, protectionDomain};
  200. defineClassProtectionDomain
  201. = ClassLoader.class.getDeclaredMethod("defineClass", args);
  202. } catch (Exception e) {
  203. // ignore failure to get access to 1.2+ methods
  204. }
  205. }
  206. /**
  207. * Create an Ant Class Loader
  208. */
  209. public AntClassLoader() {
  210. setParent(null);
  211. }
  212. /**
  213. * Creates a classloader using the classpath given.
  214. *
  215. * @param classpath The classpath to use to load the classes. This
  216. * is combined with the system classpath in a manner
  217. * determined by the value of ${build.sysclasspath}.
  218. * May be <code>null</code>, in which case no path
  219. * elements are set up to start with.
  220. */
  221. public AntClassLoader(Path classpath) {
  222. setParent(null);
  223. setClassPath(classpath);
  224. }
  225. /**
  226. * Creates a classloader using the classpath given.
  227. *
  228. * @param parent The parent classloader to which unsatisfied loading
  229. * attempts are delegated. May be <code>null</code>,
  230. * in which case the classloader which loaded this
  231. * class is used as the parent.
  232. * @param classpath the classpath to use to load the classes.
  233. * May be <code>null</code>, in which case no path
  234. * elements are set up to start with.
  235. * @param parentFirst If <code>true</code>, indicates that the parent
  236. * classloader should be consulted before trying to
  237. * load the a class through this loader.
  238. */
  239. public AntClassLoader(ClassLoader parent, Path classpath,
  240. boolean parentFirst) {
  241. this(classpath);
  242. if (parent != null) {
  243. setParent(parent);
  244. }
  245. setParentFirst(parentFirst);
  246. }
  247. /**
  248. * Creates a classloader using the classpath given.
  249. *
  250. * @param classpath The classpath to use to load the classes. May be
  251. * <code>null</code>, in which case no path
  252. * elements are set up to start with.
  253. * @param parentFirst If <code>true</code>, indicates that the parent
  254. * classloader should be consulted before trying to
  255. * load the a class through this loader.
  256. */
  257. public AntClassLoader(Path classpath,
  258. boolean parentFirst) {
  259. this(null, classpath, parentFirst);
  260. }
  261. /**
  262. * Creates an empty class loader. The classloader should be configured
  263. * with path elements to specify where the loader is to look for
  264. * classes.
  265. *
  266. * @param parent The parent classloader to which unsatisfied loading
  267. * attempts are delegated. May be <code>null</code>,
  268. * in which case the classloader which loaded this
  269. * class is used as the parent.
  270. * @param parentFirst If <code>true</code>, indicates that the parent
  271. * classloader should be consulted before trying to
  272. * load the a class through this loader.
  273. */
  274. public AntClassLoader(ClassLoader parent, boolean parentFirst) {
  275. setParent(parent);
  276. this.parentFirst = parentFirst;
  277. }
  278. /**
  279. * Set the classpath to search for classes to load. This should not be
  280. * changed once the classloader starts to server classes
  281. *
  282. * @param classpath the search classpath consisting of directories and
  283. * jar/zip files.
  284. */
  285. public void setClassPath(Path classpath) {
  286. pathComponents.removeAllElements();
  287. if (classpath != null) {
  288. Path actualClasspath = classpath.concatSystemClassPath(true);
  289. for (Iterator it = actualClasspath.iterator(); it.hasNext(); ) {
  290. try {
  291. addPathElement((String)it.next());
  292. } catch (Exception e) {
  293. // ignore path elements which are invalid
  294. }
  295. }
  296. }
  297. }
  298. /**
  299. * Set the parent for this class loader. This is the class loader to which
  300. * this class loader will delegate to load classes
  301. *
  302. * @param parent the parent class loader.
  303. */
  304. public void setParent(ClassLoader parent) {
  305. if (parent == null) {
  306. this.parent = AntClassLoader.class.getClassLoader();
  307. } else {
  308. this.parent = parent;
  309. }
  310. }
  311. /**
  312. * Control whether class lookup is delegated to the parent loader first
  313. * or after this loader. Use with extreme caution. Setting this to
  314. * false violates the class loader hierarchy and can lead to Linkage errors
  315. *
  316. * @param parentFirst if true, delegate initial class search to the parent
  317. * classloader.
  318. */
  319. public void setParentFirst(boolean parentFirst) {
  320. this.parentFirst = parentFirst;
  321. }
  322. /**
  323. * Logs a message through the project object if one has been provided.
  324. *
  325. * @param message The message to log.
  326. * Should not be <code>null</code>.
  327. */
  328. protected void log(String message) {
  329. System.out.println(message);
  330. }
  331. /**
  332. * Sets the current thread's context loader to this classloader, storing
  333. * the current loader value for later resetting.
  334. */
  335. public void setThreadContextLoader() {
  336. if (isContextLoaderSaved) {
  337. throw new RuntimeException("Context loader has not been reset");
  338. }
  339. if (LoaderUtils.isContextLoaderAvailable()) {
  340. savedContextLoader = LoaderUtils.getContextClassLoader();
  341. ClassLoader loader = this;
  342. LoaderUtils.setContextClassLoader(loader);
  343. isContextLoaderSaved = true;
  344. }
  345. }
  346. /**
  347. * Resets the current thread's context loader to its original value.
  348. */
  349. public void resetThreadContextLoader() {
  350. if (LoaderUtils.isContextLoaderAvailable()
  351. && isContextLoaderSaved) {
  352. LoaderUtils.setContextClassLoader(savedContextLoader);
  353. savedContextLoader = null;
  354. isContextLoaderSaved = false;
  355. }
  356. }
  357. /**
  358. * Adds an element to the classpath to be searched.
  359. *
  360. * @param pathElement The path element to add. Must not be
  361. * <code>null</code>.
  362. *
  363. * @exception Exception if the given path element cannot be resolved
  364. * against the project.
  365. */
  366. public void addPathElement(String pathElement) throws Exception {
  367. File pathComponent = new File(pathElement);
  368. try {
  369. addPathFile(pathComponent);
  370. } catch (IOException e) {
  371. throw new Exception(e);
  372. }
  373. }
  374. /**
  375. * Add a file to the path
  376. *
  377. * @param pathComponent the file which is to be added to the path for
  378. * this class loader
  379. *
  380. * @throws IOException if data needed from the file cannot be read.
  381. */
  382. protected void addPathFile(File pathComponent) throws IOException {
  383. pathComponents.addElement(pathComponent);
  384. }
  385. /**
  386. * Returns the classpath this classloader will consult.
  387. *
  388. * @return the classpath used for this classloader, with elements
  389. * separated by the path separator for the system.
  390. */
  391. public String getClasspath() {
  392. StringBuffer sb = new StringBuffer();
  393. boolean firstPass = true;
  394. Enumeration componentEnum = pathComponents.elements();
  395. while (componentEnum.hasMoreElements()) {
  396. if (!firstPass) {
  397. sb.append(System.getProperty("path.separator"));
  398. } else {
  399. firstPass = false;
  400. }
  401. sb.append(((File) componentEnum.nextElement()).getAbsolutePath());
  402. }
  403. return sb.toString();
  404. }
  405. /**
  406. * Sets whether this classloader should run in isolated mode. In
  407. * isolated mode, classes not found on the given classpath will
  408. * not be referred to the parent class loader but will cause a
  409. * ClassNotFoundException.
  410. *
  411. * @param isolated Whether or not this classloader should run in
  412. * isolated mode.
  413. */
  414. public synchronized void setIsolated(boolean isolated) {
  415. ignoreBase = isolated;
  416. }
  417. /**
  418. * Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
  419. * way.
  420. *
  421. * @param theClass The class to initialize.
  422. * Must not be <code>null</code>.
  423. *
  424. * @deprecated use Class.forName with initialize=true instead.
  425. */
  426. public static void initializeClass(Class theClass) {
  427. // ***HACK*** We ask the VM to create an instance
  428. // by voluntarily providing illegal arguments to force
  429. // the VM to run the class' static initializer, while
  430. // at the same time not running a valid constructor.
  431. final Constructor[] cons = theClass.getDeclaredConstructors();
  432. //At least one constructor is guaranteed to be there, but check anyway.
  433. if (cons != null) {
  434. if (cons.length > 0 && cons[0] != null) {
  435. final String[] strs = new String[NUMBER_OF_STRINGS];
  436. try {
  437. cons[0].newInstance(strs);
  438. // Expecting an exception to be thrown by this call:
  439. // IllegalArgumentException: wrong number of Arguments
  440. } catch (Throwable t) {
  441. // Ignore - we are interested only in the side
  442. // effect - that of getting the static initializers
  443. // invoked. As we do not want to call a valid
  444. // constructor to get this side effect, an
  445. // attempt is made to call a hopefully
  446. // invalid constructor - come on, nobody
  447. // would have a constructor that takes in
  448. // 256 String arguments ;-)
  449. // (In fact, they can't - according to JVM spec
  450. // section 4.10, the number of method parameters is limited
  451. // to 255 by the definition of a method descriptor.
  452. // Constructors count as methods here.)
  453. }
  454. }
  455. }
  456. }
  457. /**
  458. * Adds a package root to the list of packages which must be loaded on the
  459. * parent loader.
  460. *
  461. * All subpackages are also included.
  462. *
  463. * @param packageRoot The root of all packages to be included.
  464. * Should not be <code>null</code>.
  465. */
  466. public void addSystemPackageRoot(String packageRoot) {
  467. systemPackages.addElement(packageRoot
  468. + (packageRoot.endsWith(".") ? "" : "."));
  469. }
  470. /**
  471. * Adds a package root to the list of packages which must be loaded using
  472. * this loader.
  473. *
  474. * All subpackages are also included.
  475. *
  476. * @param packageRoot The root of all packages to be included.
  477. * Should not be <code>null</code>.
  478. */
  479. public void addLoaderPackageRoot(String packageRoot) {
  480. loaderPackages.addElement(packageRoot
  481. + (packageRoot.endsWith(".") ? "" : "."));
  482. }
  483. /**
  484. * Loads a class through this class loader even if that class is available
  485. * on the parent classpath.
  486. *
  487. * This ensures that any classes which are loaded by the returned class
  488. * will use this classloader.
  489. *
  490. * @param classname The name of the class to be loaded.
  491. * Must not be <code>null</code>.
  492. *
  493. * @return the required Class object
  494. *
  495. * @exception ClassNotFoundException if the requested class does not exist
  496. * on this loader's classpath.
  497. */
  498. public Class forceLoadClass(String classname)
  499. throws ClassNotFoundException {
  500. log("force loading " + classname);
  501. Class theClass = findLoadedClass(classname);
  502. if (theClass == null) {
  503. theClass = findClass(classname);
  504. }
  505. return theClass;
  506. }
  507. /**
  508. * Loads a class through this class loader but defer to the parent class
  509. * loader.
  510. *
  511. * This ensures that instances of the returned class will be compatible
  512. * with instances which have already been loaded on the parent
  513. * loader.
  514. *
  515. * @param classname The name of the class to be loaded.
  516. * Must not be <code>null</code>.
  517. *
  518. * @return the required Class object
  519. *
  520. * @exception ClassNotFoundException if the requested class does not exist
  521. * on this loader's classpath.
  522. */
  523. public Class forceLoadSystemClass(String classname)
  524. throws ClassNotFoundException {
  525. log("force system loading " + classname);
  526. Class theClass = findLoadedClass(classname);
  527. if (theClass == null) {
  528. theClass = findBaseClass(classname);
  529. }
  530. return theClass;
  531. }
  532. /**
  533. * Returns a stream to read the requested resource name.
  534. *
  535. * @param name The name of the resource for which a stream is required.
  536. * Must not be <code>null</code>.
  537. *
  538. * @return a stream to the required resource or <code>null</code> if the
  539. * resource cannot be found on the loader's classpath.
  540. */
  541. public InputStream getResourceAsStream(String name) {
  542. InputStream resourceStream = null;
  543. if (isParentFirst(name)) {
  544. resourceStream = loadBaseResource(name);
  545. if (resourceStream != null) {
  546. log("ResourceStream for " + name
  547. + " loaded from parent loader");
  548. } else {
  549. resourceStream = loadResource(name);
  550. if (resourceStream != null) {
  551. log("ResourceStream for " + name
  552. + " loaded from ant loader");
  553. }
  554. }
  555. } else {
  556. resourceStream = loadResource(name);
  557. if (resourceStream != null) {
  558. log("ResourceStream for " + name
  559. + " loaded from ant loader");
  560. } else {
  561. resourceStream = loadBaseResource(name);
  562. if (resourceStream != null) {
  563. log("ResourceStream for " + name
  564. + " loaded from parent loader");
  565. }
  566. }
  567. }
  568. if (resourceStream == null) {
  569. log("Couldn't load ResourceStream for " + name);
  570. }
  571. return resourceStream;
  572. }
  573. /**
  574. * Returns a stream to read the requested resource name from this loader.
  575. *
  576. * @param name The name of the resource for which a stream is required.
  577. * Must not be <code>null</code>.
  578. *
  579. * @return a stream to the required resource or <code>null</code> if
  580. * the resource cannot be found on the loader's classpath.
  581. */
  582. private InputStream loadResource(String name) {
  583. // we need to search the components of the path to see if we can
  584. // find the class we want.
  585. InputStream stream = null;
  586. Enumeration e = pathComponents.elements();
  587. while (e.hasMoreElements() && stream == null) {
  588. File pathComponent = (File) e.nextElement();
  589. stream = getResourceStream(pathComponent, name);
  590. }
  591. return stream;
  592. }
  593. /**
  594. * Finds a system resource (which should be loaded from the parent
  595. * classloader).
  596. *
  597. * @param name The name of the system resource to load.
  598. * Must not be <code>null</code>.
  599. *
  600. * @return a stream to the named resource, or <code>null</code> if
  601. * the resource cannot be found.
  602. */
  603. private InputStream loadBaseResource(String name) {
  604. if (parent == null) {
  605. return getSystemResourceAsStream(name);
  606. } else {
  607. return parent.getResourceAsStream(name);
  608. }
  609. }
  610. /**
  611. * Returns an inputstream to a given resource in the given file which may
  612. * either be a directory or a zip file.
  613. *
  614. * @param file the file (directory or jar) in which to search for the
  615. * resource. Must not be <code>null</code>.
  616. * @param resourceName The name of the resource for which a stream is
  617. * required. Must not be <code>null</code>.
  618. *
  619. * @return a stream to the required resource or <code>null</code> if
  620. * the resource cannot be found in the given file.
  621. */
  622. private InputStream getResourceStream(File file, String resourceName) {
  623. try {
  624. if (!file.exists()) {
  625. return null;
  626. }
  627. if (file.isDirectory()) {
  628. File resource = new File(file, resourceName);
  629. if (resource.exists()) {
  630. return new FileInputStream(resource);
  631. }
  632. } else {
  633. // is the zip file in the cache
  634. ZipFile zipFile = (ZipFile) zipFiles.get(file);
  635. if (zipFile == null) {
  636. zipFile = new ZipFile(file);
  637. zipFiles.put(file, zipFile);
  638. }
  639. ZipEntry entry = zipFile.getEntry(resourceName);
  640. if (entry != null) {
  641. return zipFile.getInputStream(entry);
  642. }
  643. }
  644. } catch (Exception e) {
  645. log("Ignoring Exception " + e.getClass().getName()
  646. + ": " + e.getMessage() + " reading resource " + resourceName
  647. + " from " + file);
  648. }
  649. return null;
  650. }
  651. /**
  652. * Tests whether or not the parent classloader should be checked for
  653. * a resource before this one. If the resource matches both the
  654. * "use parent classloader first" and the "use this classloader first"
  655. * lists, the latter takes priority.
  656. *
  657. * @param resourceName The name of the resource to check.
  658. * Must not be <code>null</code>.
  659. *
  660. * @return whether or not the parent classloader should be checked for a
  661. * resource before this one is.
  662. */
  663. private boolean isParentFirst(String resourceName) {
  664. // default to the global setting and then see
  665. // if this class belongs to a package which has been
  666. // designated to use a specific loader first
  667. // (this one or the parent one)
  668. // XXX - shouldn't this always return false in isolated mode?
  669. boolean useParentFirst = parentFirst;
  670. for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) {
  671. String packageName = (String) e.nextElement();
  672. if (resourceName.startsWith(packageName)) {
  673. useParentFirst = true;
  674. break;
  675. }
  676. }
  677. for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) {
  678. String packageName = (String) e.nextElement();
  679. if (resourceName.startsWith(packageName)) {
  680. useParentFirst = false;
  681. break;
  682. }
  683. }
  684. return useParentFirst;
  685. }
  686. /**
  687. * Finds the resource with the given name. A resource is
  688. * some data (images, audio, text, etc) that can be accessed by class
  689. * code in a way that is independent of the location of the code.
  690. *
  691. * @param name The name of the resource for which a stream is required.
  692. * Must not be <code>null</code>.
  693. *
  694. * @return a URL for reading the resource, or <code>null</code> if the
  695. * resource could not be found or the caller doesn't have
  696. * adequate privileges to get the resource.
  697. */
  698. public URL getResource(String name) {
  699. // we need to search the components of the path to see if
  700. // we can find the class we want.
  701. URL url = null;
  702. if (isParentFirst(name)) {
  703. url = (parent == null) ? super.getResource(name)
  704. : parent.getResource(name);
  705. }
  706. if (url != null) {
  707. log("Resource " + name + " loaded from parent loader");
  708. } else {
  709. // try and load from this loader if the parent either didn't find
  710. // it or wasn't consulted.
  711. Enumeration e = pathComponents.elements();
  712. while (e.hasMoreElements() && url == null) {
  713. File pathComponent = (File) e.nextElement();
  714. url = getResourceURL(pathComponent, name);
  715. if (url != null) {
  716. log("Resource " + name
  717. + " loaded from ant loader");
  718. }
  719. }
  720. }
  721. if (url == null && !isParentFirst(name)) {
  722. // this loader was first but it didn't find it - try the parent
  723. url = (parent == null) ? super.getResource(name)
  724. : parent.getResource(name);
  725. if (url != null) {
  726. log("Resource " + name + " loaded from parent loader");
  727. }
  728. }
  729. if (url == null) {
  730. log("Couldn't load Resource " + name);
  731. }
  732. return url;
  733. }
  734. /**
  735. * Returns an enumeration of URLs representing all the resources with the
  736. * given name by searching the class loader's classpath.
  737. *
  738. * @param name The resource name to search for.
  739. * Must not be <code>null</code>.
  740. * @return an enumeration of URLs for the resources
  741. * @exception IOException if I/O errors occurs (can't happen)
  742. */
  743. protected Enumeration/*<URL>*/ findResources(String name) throws IOException {
  744. Enumeration/*<URL>*/ mine = new ResourceEnumeration(name);
  745. Enumeration/*<URL>*/ base;
  746. if (parent != null && parent != getParent()) {
  747. // Delegate to the parent:
  748. base = parent.getResources(name);
  749. // Note: could cause overlaps in case ClassLoader.this.parent has matches.
  750. } else {
  751. // ClassLoader.this.parent is already delegated to from ClassLoader.getResources, no need:
  752. base = new CollectionUtils.EmptyEnumeration();
  753. }
  754. if (isParentFirst(name)) {
  755. // Normal case.
  756. return CollectionUtils.append(base, mine);
  757. } else {
  758. // Inverted.
  759. return CollectionUtils.append(mine, base);
  760. }
  761. }
  762. /**
  763. * Returns the URL of a given resource in the given file which may
  764. * either be a directory or a zip file.
  765. *
  766. * @param file The file (directory or jar) in which to search for
  767. * the resource. Must not be <code>null</code>.
  768. * @param resourceName The name of the resource for which a stream
  769. * is required. Must not be <code>null</code>.
  770. *
  771. * @return a stream to the required resource or <code>null</code> if the
  772. * resource cannot be found in the given file object.
  773. */
  774. protected URL getResourceURL(File file, String resourceName) {
  775. try {
  776. if (!file.exists()) {
  777. return null;
  778. }
  779. if (file.isDirectory()) {
  780. File resource = new File(file, resourceName);
  781. if (resource.exists()) {
  782. try {
  783. return FILE_UTILS.getFileURL(resource);
  784. } catch (MalformedURLException ex) {
  785. return null;
  786. }
  787. }
  788. } else {
  789. ZipFile zipFile = (ZipFile) zipFiles.get(file);
  790. if (zipFile == null) {
  791. zipFile = new ZipFile(file);
  792. zipFiles.put(file, zipFile);
  793. }
  794. ZipEntry entry = zipFile.getEntry(resourceName);
  795. if (entry != null) {
  796. try {
  797. return new URL("jar:" + FILE_UTILS.getFileURL(file)
  798. + "!/" + entry);
  799. } catch (MalformedURLException ex) {
  800. return null;
  801. }
  802. }
  803. }
  804. } catch (Exception e) {
  805. e.printStackTrace();
  806. }
  807. return null;
  808. }
  809. /**
  810. * Loads a class with this class loader.
  811. *
  812. * This class attempts to load the class in an order determined by whether
  813. * or not the class matches the system/loader package lists, with the
  814. * loader package list taking priority. If the classloader is in isolated
  815. * mode, failure to load the class in this loader will result in a
  816. * ClassNotFoundException.
  817. *
  818. * @param classname The name of the class to be loaded.
  819. * Must not be <code>null</code>.
  820. * @param resolve <code>true</code> if all classes upon which this class
  821. * depends are to be loaded.
  822. *
  823. * @return the required Class object
  824. *
  825. * @exception ClassNotFoundException if the requested class does not exist
  826. * on the system classpath (when not in isolated mode) or this loader's
  827. * classpath.
  828. */
  829. protected synchronized Class loadClass(String classname, boolean resolve)
  830. throws ClassNotFoundException {
  831. // 'sync' is needed - otherwise 2 threads can load the same class
  832. // twice, resulting in LinkageError: duplicated class definition.
  833. // findLoadedClass avoids that, but without sync it won't work.
  834. Class theClass = findLoadedClass(classname);
  835. if (theClass != null) {
  836. return theClass;
  837. }
  838. if (isParentFirst(classname)) {
  839. try {
  840. theClass = findBaseClass(classname);
  841. log("Class " + classname + " loaded from parent loader "
  842. + "(parentFirst)");
  843. } catch (ClassNotFoundException cnfe) {
  844. theClass = findClass(classname);
  845. log("Class " + classname + " loaded from ant loader "
  846. + "(parentFirst)");
  847. }
  848. } else {
  849. try {
  850. theClass = findClass(classname);
  851. log("Class " + classname + " loaded from ant loader");
  852. } catch (ClassNotFoundException cnfe) {
  853. if (ignoreBase) {
  854. throw cnfe;
  855. }
  856. theClass = findBaseClass(classname);
  857. log("Class " + classname + " loaded from parent loader");
  858. }
  859. }
  860. if (resolve) {
  861. resolveClass(theClass);
  862. }
  863. return theClass;
  864. }
  865. /**
  866. * Converts the class dot notation to a filesystem equivalent for
  867. * searching purposes.
  868. *
  869. * @param classname The class name in dot format (eg java.lang.Integer).
  870. * Must not be <code>null</code>.
  871. *
  872. * @return the classname in filesystem format (eg java/lang/Integer.class)
  873. */
  874. private String getClassFilename(String classname) {
  875. return classname.replace('.', '/') + ".class";
  876. }
  877. /**
  878. * Define a class given its bytes
  879. *
  880. * @param container the container from which the class data has been read
  881. * may be a directory or a jar/zip file.
  882. *
  883. * @param classData the bytecode data for the class
  884. * @param classname the name of the class
  885. *
  886. * @return the Class instance created from the given data
  887. *
  888. * @throws IOException if the class data cannot be read.
  889. */
  890. protected Class defineClassFromData(File container, byte[] classData,
  891. String classname) throws IOException {
  892. // Simply put:
  893. // defineClass(classname, classData, 0, classData.length,
  894. // Project.class.getProtectionDomain());
  895. // Made more elaborate to be 1.1-safe.
  896. if (defineClassProtectionDomain != null) {
  897. try {
  898. Object domain
  899. = getProtectionDomain.invoke(AntClassLoader.class, new Object[0]);
  900. Object[] args
  901. = new Object[] {classname, classData, new Integer(0),
  902. new Integer(classData.length), domain};
  903. return (Class) defineClassProtectionDomain.invoke(this, args);
  904. } catch (InvocationTargetException ite) {
  905. Throwable t = ite.getTargetException();
  906. if (t instanceof ClassFormatError) {
  907. throw (ClassFormatError) t;
  908. } else if (t instanceof NoClassDefFoundError) {
  909. throw (NoClassDefFoundError) t;
  910. } else if (t instanceof SecurityException) {
  911. throw (SecurityException) t;
  912. } else {
  913. throw new IOException(t.toString());
  914. }
  915. } catch (Exception e) {
  916. throw new IOException(e.toString());
  917. }
  918. } else {
  919. return defineClass(classname, classData, 0, classData.length);
  920. }
  921. }
  922. /**
  923. * Reads a class definition from a stream.
  924. *
  925. * @param stream The stream from which the class is to be read.
  926. * Must not be <code>null</code>.
  927. * @param classname The name of the class in the stream.
  928. * Must not be <code>null</code>.
  929. * @param container the file or directory containing the class.
  930. *
  931. * @return the Class object read from the stream.
  932. *
  933. * @exception IOException if there is a problem reading the class from the
  934. * stream.
  935. * @exception SecurityException if there is a security problem while
  936. * reading the class from the stream.
  937. */
  938. private Class getClassFromStream(InputStream stream, String classname,
  939. File container)
  940. throws IOException, SecurityException {
  941. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  942. int bytesRead = -1;
  943. byte[] buffer = new byte[BUFFER_SIZE];
  944. while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
  945. baos.write(buffer, 0, bytesRead);
  946. }
  947. byte[] classData = baos.toByteArray();
  948. return defineClassFromData(container, classData, classname);
  949. }
  950. /**
  951. * Searches for and load a class on the classpath of this class loader.
  952. *
  953. * @param name The name of the class to be loaded. Must not be
  954. * <code>null</code>.
  955. *
  956. * @return the required Class object
  957. *
  958. * @exception ClassNotFoundException if the requested class does not exist
  959. * on this loader's classpath.
  960. */
  961. public Class findClass(String name) throws ClassNotFoundException {
  962. //log("Finding class " + name);
  963. return findClassInComponents(name);
  964. }
  965. /**
  966. * Indicate if the given file is in this loader's path
  967. *
  968. * @param component the file which is to be checked
  969. *
  970. * @return true if the file is in the class path
  971. */
  972. protected boolean isInPath(File component) {
  973. for (Enumeration e = pathComponents.elements(); e.hasMoreElements();) {
  974. File pathComponent = (File) e.nextElement();
  975. if (pathComponent.equals(component)) {
  976. return true;
  977. }
  978. }
  979. return false;
  980. }
  981. /**
  982. * Finds a class on the given classpath.
  983. *
  984. * @param name The name of the class to be loaded. Must not be
  985. * <code>null</code>.
  986. *
  987. * @return the required Class object
  988. *
  989. * @exception ClassNotFoundException if the requested class does not exist
  990. * on this loader's classpath.
  991. */
  992. private Class findClassInComponents(String name)
  993. throws ClassNotFoundException {
  994. // we need to search the components of the path to see if
  995. // we can find the class we want.
  996. InputStream stream = null;
  997. String classFilename = getClassFilename(name);
  998. try {
  999. Enumeration e = pathComponents.elements();
  1000. while (e.hasMoreElements()) {
  1001. File pathComponent = (File) e.nextElement();
  1002. try {
  1003. stream = getResourceStream(pathComponent, classFilename);
  1004. if (stream != null) {
  1005. log("Loaded from " + pathComponent + " "
  1006. + classFilename);
  1007. return getClassFromStream(stream, name, pathComponent);
  1008. }
  1009. } catch (SecurityException se) {
  1010. throw se;
  1011. } catch (IOException ioe) {
  1012. // ioe.printStackTrace();
  1013. log("Exception reading component " + pathComponent
  1014. + " (reason: " + ioe.getMessage() + ")");
  1015. }
  1016. }
  1017. throw new ClassNotFoundException(name);
  1018. } finally {
  1019. try {
  1020. if (stream != null) {
  1021. stream.close();
  1022. }
  1023. } catch (IOException e) {
  1024. //ignore
  1025. }
  1026. }
  1027. }
  1028. /**
  1029. * Finds a system class (which should be loaded from the same classloader
  1030. * as the Ant core).
  1031. *
  1032. * For JDK 1.1 compatibility, this uses the findSystemClass method if
  1033. * no parent classloader has been specified.
  1034. *
  1035. * @param name The name of the class to be loaded.
  1036. * Must not be <code>null</code>.
  1037. *
  1038. * @return the required Class object
  1039. *
  1040. * @exception ClassNotFoundException if the requested class does not exist
  1041. * on this loader's classpath.
  1042. */
  1043. private Class findBaseClass(String name) throws ClassNotFoundException {
  1044. if (parent == null) {
  1045. return findSystemClass(name);
  1046. } else {
  1047. return parent.loadClass(name);
  1048. }
  1049. }
  1050. /**
  1051. * Cleans up any resources held by this classloader. Any open archive
  1052. * files are closed.
  1053. */
  1054. public synchronized void cleanup() {
  1055. for (Enumeration e = zipFiles.elements(); e.hasMoreElements();) {
  1056. ZipFile zipFile = (ZipFile) e.nextElement();
  1057. try {
  1058. zipFile.close();
  1059. } catch (IOException ioe) {
  1060. // ignore
  1061. }
  1062. }
  1063. zipFiles = new Hashtable();
  1064. }
  1065. }