You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

AntClassLoader.java 34 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant;
  55. import java.lang.reflect.*;
  56. import java.util.*;
  57. import java.util.zip.*;
  58. import java.io.*;
  59. import java.net.*;
  60. import org.apache.tools.ant.types.Path;
  61. /**
  62. * Used to load classes within ant with a different claspath from that used to start ant.
  63. * Note that it is possible to force a class into this loader even when that class is on the
  64. * system classpath by using the forceLoadClass method. Any subsequent classes loaded by that
  65. * class will then use this loader rather than the system class loader.
  66. *
  67. * @author <a href="mailto:conor@cortexebusiness.com.au">Conor MacNeill</a>
  68. * @author <a href="mailto:Jesse.Glick@netbeans.com">Jesse Glick</a>
  69. */
  70. public class AntClassLoader extends ClassLoader implements BuildListener {
  71. /**
  72. * An enumeration of all resources of a given name found within the
  73. * classpath of this class loader. This enumeration is used by the
  74. * {@link #findResources(String) findResources} method, which is in
  75. * turn used by the
  76. * {@link ClassLoader#getResources ClassLoader.getResources} method.
  77. *
  78. * @see AntClassLoader#findResources(String)
  79. * @see java.lang.ClassLoader#getResources(String)
  80. * @author <a href="mailto:hermand@alumni.grinnell.edu">David A. Herman</a>
  81. */
  82. private class ResourceEnumeration implements Enumeration {
  83. /**
  84. * The name of the resource being searched for.
  85. */
  86. private String resourceName;
  87. /**
  88. * The index of the next classpath element to search.
  89. */
  90. private int pathElementsIndex;
  91. /**
  92. * The URL of the next resource to return in the enumeration. If this
  93. * field is <code>null</code> then the enumeration has been completed,
  94. * i.e., there are no more elements to return.
  95. */
  96. private URL nextResource;
  97. /**
  98. * Construct a new enumeration of resources of the given name found
  99. * within this class loader's classpath.
  100. *
  101. * @param name the name of the resource to search for.
  102. */
  103. ResourceEnumeration(String name) {
  104. this.resourceName = name;
  105. this.pathElementsIndex = 0;
  106. findNextResource();
  107. }
  108. /**
  109. * Indicates whether there are more elements in the enumeration to
  110. * return.
  111. *
  112. * @return <code>true</code> if there are more elements in the
  113. * enumeration; <code>false</code> otherwise.
  114. */
  115. public boolean hasMoreElements() {
  116. return (this.nextResource != null);
  117. }
  118. /**
  119. * Returns the next resource in the enumeration.
  120. *
  121. * @return the next resource in the enumeration.
  122. */
  123. public Object nextElement() {
  124. URL ret = this.nextResource;
  125. findNextResource();
  126. return ret;
  127. }
  128. /**
  129. * Locates the next resource of the correct name in the classpath and
  130. * sets <code>nextResource</code> to the URL of that resource. If no
  131. * more resources can be found, <code>nextResource</code> is set to
  132. * <code>null</code>.
  133. */
  134. private void findNextResource() {
  135. URL url = null;
  136. while ((pathElementsIndex < pathComponents.size()) &&
  137. (url == null)) {
  138. try {
  139. File pathComponent
  140. = (File)pathComponents.elementAt(pathElementsIndex);
  141. url = getResourceURL(pathComponent, this.resourceName);
  142. pathElementsIndex++;
  143. }
  144. catch (BuildException e) {
  145. // ignore path elements which are not valid relative to the project
  146. }
  147. }
  148. this.nextResource = url;
  149. }
  150. }
  151. /**
  152. * The size of buffers to be used in this classloader.
  153. */
  154. static private final int BUFFER_SIZE = 8192;
  155. /**
  156. * The components of the classpath that the classloader searches for classes
  157. */
  158. Vector pathComponents = new Vector();
  159. /**
  160. * The project to which this class loader belongs.
  161. */
  162. private Project project;
  163. /**
  164. * Indicates whether the parent class loader should be
  165. * consulted before trying to load with this class loader.
  166. */
  167. private boolean parentFirst = true;
  168. /**
  169. * These are the package roots that are to be loaded by the parent class loader
  170. * regardless of whether the parent class loader is being searched first or not.
  171. */
  172. private Vector systemPackages = new Vector();
  173. /**
  174. * These are the package roots that are to be loaded by this class loader
  175. * regardless of whether the parent class loader is being searched first or not.
  176. */
  177. private Vector loaderPackages = new Vector();
  178. /**
  179. * This flag indicates that the classloader will ignore the base
  180. * classloader if it can't find a class.
  181. */
  182. private boolean ignoreBase = false;
  183. /**
  184. * The parent class loader, if one is given or can be determined
  185. */
  186. private ClassLoader parent = null;
  187. /**
  188. * A hashtable of zip files opened by the classloader
  189. */
  190. private Hashtable zipFiles = new Hashtable();
  191. /**
  192. * The context loader saved when setting the thread's current context loader.
  193. */
  194. private ClassLoader savedContextLoader = null;
  195. private boolean isContextLoaderSaved = false;
  196. private static Method getProtectionDomain = null;
  197. private static Method defineClassProtectionDomain = null;
  198. private static Method getContextClassLoader = null;
  199. private static Method setContextClassLoader = null;
  200. static {
  201. try {
  202. getProtectionDomain = Class.class.getMethod("getProtectionDomain", new Class[0]);
  203. Class protectionDomain = Class.forName("java.security.ProtectionDomain");
  204. Class[] args = new Class[] {String.class, byte[].class, Integer.TYPE, Integer.TYPE, protectionDomain};
  205. defineClassProtectionDomain = ClassLoader.class.getDeclaredMethod("defineClass", args);
  206. getContextClassLoader = Thread.class.getMethod("getContextClassLoader", new Class[0]);
  207. args = new Class[] {ClassLoader.class};
  208. setContextClassLoader = Thread.class.getMethod("setContextClassLoader", args);
  209. }
  210. catch (Exception e) {}
  211. }
  212. /**
  213. * Create a classloader for the given project using the classpath given.
  214. *
  215. * @param project the project to ehich this classloader is to belong.
  216. * @param classpath the classpath to use to load the classes. This
  217. * is combined with the system classpath in a manner
  218. * determined by the value of ${build.sysclasspath}
  219. */
  220. public AntClassLoader(Project project, Path classpath) {
  221. parent = AntClassLoader.class.getClassLoader();
  222. this.project = project;
  223. project.addBuildListener(this);
  224. if (classpath != null) {
  225. Path actualClasspath = classpath.concatSystemClasspath("ignore");
  226. String[] pathElements = actualClasspath.list();
  227. for (int i = 0; i < pathElements.length; ++i) {
  228. try {
  229. addPathElement((String)pathElements[i]);
  230. }
  231. catch (BuildException e) {
  232. // ignore path elements which are invalid relative to the project
  233. }
  234. }
  235. }
  236. }
  237. /**
  238. * Create a classloader for the given project using the classpath given.
  239. *
  240. * @param parent the parent classloader to which unsatisfied loading attempts
  241. * are delgated
  242. * @param project the project to which this classloader is to belong.
  243. * @param classpath the classpath to use to load the classes.
  244. * @param parentFirst if true indicates that the parent classloader should be consulted
  245. * before trying to load the a class through this loader.
  246. */
  247. public AntClassLoader(ClassLoader parent, Project project, Path classpath,
  248. boolean parentFirst) {
  249. this(project, classpath);
  250. if (parent != null) {
  251. this.parent = parent;
  252. }
  253. this.parentFirst = parentFirst;
  254. addSystemPackageRoot("java");
  255. addSystemPackageRoot("javax");
  256. }
  257. /**
  258. * Create an empty class loader. The classloader should be configured with path elements
  259. * to specify where the loader is to look for classes.
  260. *
  261. * @param parent the parent classloader to which unsatisfied loading attempts
  262. * are delgated
  263. * @param parentFirst if true indicates that the parent classloader should be consulted
  264. * before trying to load the a class through this loader.
  265. */
  266. public AntClassLoader(ClassLoader parent, boolean parentFirst) {
  267. if (parent != null) {
  268. this.parent = parent;
  269. }
  270. else {
  271. parent = AntClassLoader.class.getClassLoader();
  272. }
  273. project = null;
  274. this.parentFirst = parentFirst;
  275. }
  276. /**
  277. * Log a message through the project object if one has been provided.
  278. *
  279. * @param message the message to log
  280. * @param priority the logging priority of the message
  281. */
  282. protected void log(String message, int priority) {
  283. if (project != null) {
  284. project.log(message, priority);
  285. }
  286. // else {
  287. // System.out.println(message);
  288. // }
  289. }
  290. /**
  291. * Set the current thread's context loader to this classloader, storing the current
  292. * loader value for later resetting
  293. */
  294. public void setThreadContextLoader() {
  295. if (isContextLoaderSaved) {
  296. throw new BuildException("Context loader has not been reset");
  297. }
  298. if (getContextClassLoader != null && setContextClassLoader != null) {
  299. try {
  300. savedContextLoader
  301. = (ClassLoader)getContextClassLoader.invoke(Thread.currentThread(), new Object[0]);
  302. Object[] args = new Object[] {this};
  303. setContextClassLoader.invoke(Thread.currentThread(), args);
  304. isContextLoaderSaved = true;
  305. }
  306. catch (InvocationTargetException ite) {
  307. Throwable t = ite.getTargetException();
  308. throw new BuildException(t.toString());
  309. }
  310. catch (Exception e) {
  311. throw new BuildException(e.toString());
  312. }
  313. }
  314. }
  315. /**
  316. * Reset the current thread's context loader to its original value
  317. */
  318. public void resetThreadContextLoader() {
  319. if (isContextLoaderSaved &&
  320. getContextClassLoader != null && setContextClassLoader != null) {
  321. try {
  322. Object[] args = new Object[] {savedContextLoader};
  323. setContextClassLoader.invoke(Thread.currentThread(), args);
  324. savedContextLoader = null;
  325. isContextLoaderSaved = false;
  326. }
  327. catch (InvocationTargetException ite) {
  328. Throwable t = ite.getTargetException();
  329. throw new BuildException(t.toString());
  330. }
  331. catch (Exception e) {
  332. throw new BuildException(e.toString());
  333. }
  334. }
  335. }
  336. /**
  337. * Add an element to the classpath to be searched
  338. *
  339. */
  340. public void addPathElement(String pathElement) throws BuildException {
  341. File pathComponent
  342. = project != null ? project.resolveFile(pathElement)
  343. : new File(pathElement);
  344. pathComponents.addElement(pathComponent);
  345. }
  346. /**
  347. * Set this classloader to run in isolated mode. In isolated mode, classes not
  348. * found on the given classpath will not be referred to the base class loader
  349. * but will cause a classNotFoundException.
  350. */
  351. public void setIsolated(boolean isolated) {
  352. ignoreBase = isolated;
  353. }
  354. /**
  355. * Force initialization of a class in a JDK 1.1 compatible, albeit hacky
  356. * way
  357. */
  358. static public void initializeClass(Class theClass) {
  359. // ***HACK*** We try to create an instance to force the VM to run the
  360. // class' static initializer. We don't care if the instance can't
  361. // be created - we are just interested in the side effect.
  362. try {
  363. theClass.newInstance();
  364. }
  365. catch (Throwable t) {
  366. //ignore - our work is done
  367. }
  368. }
  369. /**
  370. * Add a package root to the list of packages which must be loaded on the
  371. * parent loader.
  372. *
  373. * All subpackages are also included.
  374. *
  375. * @param packageRoot the root of all packages to be included.
  376. */
  377. public void addSystemPackageRoot(String packageRoot) {
  378. systemPackages.addElement(packageRoot + ".");
  379. }
  380. /**
  381. * Add a package root to the list of packages which must be loaded using
  382. * this loader.
  383. *
  384. * All subpackages are also included.
  385. *
  386. * @param packageRoot the root of akll packages to be included.
  387. */
  388. public void addLoaderPackageRoot(String packageRoot) {
  389. loaderPackages.addElement(packageRoot + ".");
  390. }
  391. /**
  392. * Load a class through this class loader even if that class is available on the
  393. * parent classpath.
  394. *
  395. * This ensures that any classes which are loaded by the returned class will use this
  396. * classloader.
  397. *
  398. * @param classname the classname to be loaded.
  399. *
  400. * @return the required Class object
  401. *
  402. * @throws ClassNotFoundException if the requested class does not exist on
  403. * this loader's classpath.
  404. */
  405. public Class forceLoadClass(String classname) throws ClassNotFoundException {
  406. log("force loading " + classname, Project.MSG_DEBUG);
  407. Class theClass = findLoadedClass(classname);
  408. if (theClass == null) {
  409. theClass = findClass(classname);
  410. }
  411. return theClass;
  412. }
  413. /**
  414. * Load a class through this class loader but defer to the parent class loader
  415. *
  416. * This ensures that instances of the returned class will be compatible with instances which
  417. * which have already been loaded on the parent loader.
  418. *
  419. * @param classname the classname to be loaded.
  420. *
  421. * @return the required Class object
  422. *
  423. * @throws ClassNotFoundException if the requested class does not exist on
  424. * this loader's classpath.
  425. */
  426. public Class forceLoadSystemClass(String classname) throws ClassNotFoundException {
  427. log("force system loading " + classname, Project.MSG_DEBUG);
  428. Class theClass = findLoadedClass(classname);
  429. if (theClass == null) {
  430. theClass = findBaseClass(classname);
  431. }
  432. return theClass;
  433. }
  434. /**
  435. * Get a stream to read the requested resource name.
  436. *
  437. * @param name the name of the resource for which a stream is required.
  438. *
  439. * @return a stream to the required resource or null if the resource cannot be
  440. * found on the loader's classpath.
  441. */
  442. public InputStream getResourceAsStream(String name) {
  443. InputStream resourceStream = null;
  444. if (isParentFirst(name)) {
  445. resourceStream = loadBaseResource(name);
  446. if (resourceStream != null) {
  447. log("ResourceStream for " + name
  448. + " loaded from parent loader", Project.MSG_DEBUG);
  449. } else {
  450. resourceStream = loadResource(name);
  451. if (resourceStream != null) {
  452. log("ResourceStream for " + name
  453. + " loaded from ant loader", Project.MSG_DEBUG);
  454. }
  455. }
  456. }
  457. else {
  458. resourceStream = loadResource(name);
  459. if (resourceStream != null) {
  460. log("ResourceStream for " + name
  461. + " loaded from ant loader", Project.MSG_DEBUG);
  462. } else {
  463. resourceStream = loadBaseResource(name);
  464. if (resourceStream != null) {
  465. log("ResourceStream for " + name
  466. + " loaded from parent loader", Project.MSG_DEBUG);
  467. }
  468. }
  469. }
  470. if (resourceStream == null) {
  471. log("Couldn't load ResourceStream for " + name,
  472. Project.MSG_WARN);
  473. }
  474. return resourceStream;
  475. }
  476. /**
  477. * Get a stream to read the requested resource name from this loader.
  478. *
  479. * @param name the name of the resource for which a stream is required.
  480. *
  481. * @return a stream to the required resource or null if the resource cannot be
  482. * found on the loader's classpath.
  483. */
  484. private InputStream loadResource(String name) {
  485. // we need to search the components of the path to see if we can find the
  486. // class we want.
  487. InputStream stream = null;
  488. for (Enumeration e = pathComponents.elements(); e.hasMoreElements() && stream == null; ) {
  489. File pathComponent = (File)e.nextElement();
  490. stream = getResourceStream(pathComponent, name);
  491. }
  492. return stream;
  493. }
  494. /**
  495. * Find a system resource (which should be loaded from the parent classloader).
  496. */
  497. private InputStream loadBaseResource(String name) {
  498. if (parent == null) {
  499. return getSystemResourceAsStream(name);
  500. }
  501. else {
  502. return parent.getResourceAsStream(name);
  503. }
  504. }
  505. /**
  506. * Get an inputstream to a given resource in the given file which may
  507. * either be a directory or a zip file.
  508. *
  509. * @param file the file (directory or jar) in which to search for the resource.
  510. * @param resourceName the name of the resource for which a stream is required.
  511. *
  512. * @return a stream to the required resource or null if the resource cannot be
  513. * found in the given file object
  514. */
  515. private InputStream getResourceStream(File file, String resourceName) {
  516. try {
  517. if (!file.exists()) {
  518. return null;
  519. }
  520. if (file.isDirectory()) {
  521. File resource = new File(file, resourceName);
  522. if (resource.exists()) {
  523. return new FileInputStream(resource);
  524. }
  525. }
  526. else {
  527. // is the zip file in the cache
  528. ZipFile zipFile = (ZipFile)zipFiles.get(file);
  529. if (zipFile == null) {
  530. zipFile = new ZipFile(file);
  531. zipFiles.put(file, zipFile);
  532. }
  533. ZipEntry entry = zipFile.getEntry(resourceName);
  534. if (entry != null) {
  535. return zipFile.getInputStream(entry);
  536. }
  537. }
  538. }
  539. catch (Exception e) {
  540. log("Ignoring Exception " + e.getClass().getName() + ": " + e.getMessage() +
  541. " reading resource " + resourceName + " from " + file, Project.MSG_VERBOSE);
  542. }
  543. return null;
  544. }
  545. private boolean isParentFirst(String resourceName) {
  546. // default to the global setting and then see
  547. // if this class belongs to a package which has been
  548. // designated to use a specific loader first (this one or the parent one)
  549. boolean useParentFirst = parentFirst;
  550. for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) {
  551. String packageName = (String)e.nextElement();
  552. if (resourceName.startsWith(packageName)) {
  553. useParentFirst = true;
  554. break;
  555. }
  556. }
  557. for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) {
  558. String packageName = (String)e.nextElement();
  559. if (resourceName.startsWith(packageName)) {
  560. useParentFirst = false;
  561. break;
  562. }
  563. }
  564. return useParentFirst;
  565. }
  566. /**
  567. * Finds the resource with the given name. A resource is
  568. * some data (images, audio, text, etc)
  569. * that can be accessed by class
  570. * code in a way that is independent of the location of the code.
  571. *
  572. * @param name the name of the resource for which a stream is required.
  573. *
  574. * @return a URL for reading the resource, or null if the resource
  575. * could not be found or the caller
  576. * doesn't have adequate privileges to get the resource.
  577. */
  578. public URL getResource(String name) {
  579. // we need to search the components of the path to see if we can find the
  580. // class we want.
  581. URL url = null;
  582. if (isParentFirst(name)) {
  583. url = (parent == null) ? super.getResource(name) : parent.getResource(name);
  584. }
  585. if (url != null) {
  586. log("Resource " + name + " loaded from parent loader",
  587. Project.MSG_DEBUG);
  588. } else {
  589. // try and load from this loader if the parent either didn't find
  590. // it or wasn't consulted.
  591. for (Enumeration e = pathComponents.elements(); e.hasMoreElements() && url == null; ) {
  592. File pathComponent = (File)e.nextElement();
  593. url = getResourceURL(pathComponent, name);
  594. if (url != null) {
  595. log("Resource " + name
  596. + " loaded from ant loader",
  597. Project.MSG_DEBUG);
  598. }
  599. }
  600. }
  601. if (url == null && !isParentFirst(name)) {
  602. // this loader was first but it didn't find it - try the parent
  603. url = (parent == null) ? super.getResource(name) : parent.getResource(name);
  604. if (url != null) {
  605. log("Resource " + name + " loaded from parent loader",
  606. Project.MSG_DEBUG);
  607. }
  608. }
  609. if (url == null) {
  610. log("Couldn't load Resource " + name, Project.MSG_WARN);
  611. }
  612. return url;
  613. }
  614. /**
  615. * Returns an enumeration of URLs representing all the resources with the
  616. * given name by searching the class loader's classpath.
  617. *
  618. * @param name the resource name.
  619. * @return an enumeration of URLs for the resources.
  620. * @throws IOException if I/O errors occurs (can't happen)
  621. */
  622. protected Enumeration findResources(String name) throws IOException {
  623. return new ResourceEnumeration(name);
  624. }
  625. /**
  626. * Get an inputstream to a given resource in the given file which may
  627. * either be a directory or a zip file.
  628. *
  629. * @param file the file (directory or jar) in which to search for
  630. * the resource.
  631. * @param resourceName the name of the resource for which a stream
  632. * is required.
  633. *
  634. * @return a stream to the required resource or null if the
  635. * resource cannot be found in the given file object
  636. */
  637. private URL getResourceURL(File file, String resourceName) {
  638. try {
  639. if (!file.exists()) {
  640. return null;
  641. }
  642. if (file.isDirectory()) {
  643. File resource = new File(file, resourceName);
  644. if (resource.exists()) {
  645. try {
  646. return new URL("file:"+resource.toString());
  647. } catch (MalformedURLException ex) {
  648. return null;
  649. }
  650. }
  651. }
  652. else {
  653. ZipFile zipFile = null;
  654. try {
  655. zipFile = new ZipFile(file);
  656. ZipEntry entry = zipFile.getEntry(resourceName);
  657. if (entry != null) {
  658. try {
  659. return new URL("jar:file:"+file.toString()+"!/"+entry);
  660. } catch (MalformedURLException ex) {
  661. return null;
  662. }
  663. }
  664. }
  665. finally {
  666. if (zipFile != null) {
  667. zipFile.close();
  668. }
  669. }
  670. }
  671. }
  672. catch (Exception e) {
  673. e.printStackTrace();
  674. }
  675. return null;
  676. }
  677. /**
  678. * Load a class with this class loader.
  679. *
  680. * This method will load a class.
  681. *
  682. * This class attempts to load the class firstly using the parent class loader. For
  683. * JDK 1.1 compatability, this uses the findSystemClass method.
  684. *
  685. * @param classname the name of the class to be loaded.
  686. * @param resolve true if all classes upon which this class depends are to be loaded.
  687. *
  688. * @return the required Class object
  689. *
  690. * @throws ClassNotFoundException if the requested class does not exist on
  691. * the system classpath or this loader's classpath.
  692. */
  693. protected Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {
  694. Class theClass = findLoadedClass(classname);
  695. if (theClass != null) {
  696. return theClass;
  697. }
  698. if (isParentFirst(classname)) {
  699. try {
  700. theClass = findBaseClass(classname);
  701. log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG);
  702. }
  703. catch (ClassNotFoundException cnfe) {
  704. theClass = findClass(classname);
  705. log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG);
  706. }
  707. }
  708. else {
  709. try {
  710. theClass = findClass(classname);
  711. log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG);
  712. }
  713. catch (ClassNotFoundException cnfe) {
  714. if (ignoreBase) {
  715. throw cnfe;
  716. }
  717. theClass = findBaseClass(classname);
  718. log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG);
  719. }
  720. }
  721. if (resolve) {
  722. resolveClass(theClass);
  723. }
  724. return theClass;
  725. }
  726. /**
  727. * Convert the class dot notation to a filesystem equivalent for
  728. * searching purposes.
  729. *
  730. * @param classname the class name in dot format (ie java.lang.Integer)
  731. *
  732. * @return the classname in filesystem format (ie java/lang/Integer.class)
  733. */
  734. private String getClassFilename(String classname) {
  735. return classname.replace('.', '/') + ".class";
  736. }
  737. /**
  738. * Read a class definition from a stream.
  739. *
  740. * @param stream the stream from which the class is to be read.
  741. * @param classname the class name of the class in the stream.
  742. *
  743. * @return the Class object read from the stream.
  744. *
  745. * @throws IOException if there is a problem reading the class from the
  746. * stream.
  747. */
  748. private Class getClassFromStream(InputStream stream, String classname)
  749. throws IOException {
  750. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  751. int bytesRead = -1;
  752. byte[] buffer = new byte[BUFFER_SIZE];
  753. while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
  754. baos.write(buffer, 0, bytesRead);
  755. }
  756. byte[] classData = baos.toByteArray();
  757. // Simply put:
  758. // defineClass(classname, classData, 0, classData.length, Project.class.getProtectionDomain());
  759. // Made more elaborate to be 1.1-safe.
  760. if (defineClassProtectionDomain != null) {
  761. try {
  762. Object domain = getProtectionDomain.invoke(Project.class, new Object[0]);
  763. Object[] args = new Object[] {classname, classData, new Integer(0), new Integer(classData.length), domain};
  764. return (Class)defineClassProtectionDomain.invoke(this, args);
  765. }
  766. catch (InvocationTargetException ite) {
  767. Throwable t = ite.getTargetException();
  768. if (t instanceof ClassFormatError) {
  769. throw (ClassFormatError)t;
  770. }
  771. else {
  772. throw new IOException(t.toString());
  773. }
  774. }
  775. catch (Exception e) {
  776. throw new IOException(e.toString());
  777. }
  778. }
  779. else {
  780. return defineClass(classname, classData, 0, classData.length);
  781. }
  782. }
  783. /**
  784. * Search for and load a class on the classpath of this class loader.
  785. *
  786. * @param name the classname to be loaded.
  787. *
  788. * @return the required Class object
  789. *
  790. * @throws ClassNotFoundException if the requested class does not exist on
  791. * this loader's classpath.
  792. */
  793. public Class findClass(String name) throws ClassNotFoundException {
  794. log("Finding class " + name, Project.MSG_DEBUG);
  795. return findClassInComponents(name);
  796. }
  797. /**
  798. * Find a class on the given classpath.
  799. */
  800. private Class findClassInComponents(String name) throws ClassNotFoundException {
  801. // we need to search the components of the path to see if we can find the
  802. // class we want.
  803. InputStream stream = null;
  804. String classFilename = getClassFilename(name);
  805. try {
  806. for (Enumeration e = pathComponents.elements(); e.hasMoreElements(); ) {
  807. File pathComponent = (File)e.nextElement();
  808. try {
  809. stream = getResourceStream(pathComponent, classFilename);
  810. if (stream != null) {
  811. return getClassFromStream(stream, name);
  812. }
  813. }
  814. catch (IOException ioe) {
  815. log("Exception reading component " + pathComponent , Project.MSG_VERBOSE);
  816. }
  817. }
  818. throw new ClassNotFoundException(name);
  819. }
  820. finally {
  821. try {
  822. if (stream != null) {
  823. stream.close();
  824. }
  825. }
  826. catch (IOException e) {}
  827. }
  828. }
  829. /**
  830. * Find a system class (which should be loaded from the same classloader as the Ant core).
  831. */
  832. private Class findBaseClass(String name) throws ClassNotFoundException {
  833. if (parent == null) {
  834. return findSystemClass(name);
  835. }
  836. else {
  837. return parent.loadClass(name);
  838. }
  839. }
  840. public void buildStarted(BuildEvent event) {
  841. }
  842. public void buildFinished(BuildEvent event) {
  843. pathComponents = null;
  844. project = null;
  845. for (Enumeration e = zipFiles.elements(); e.hasMoreElements(); ) {
  846. ZipFile zipFile = (ZipFile)e.nextElement();
  847. try {
  848. zipFile.close();
  849. }
  850. catch (IOException ioe) {
  851. // ignore
  852. }
  853. }
  854. }
  855. public void targetStarted(BuildEvent event) {
  856. }
  857. public void targetFinished(BuildEvent event) {
  858. }
  859. public void taskStarted(BuildEvent event) {
  860. }
  861. public void taskFinished(BuildEvent event) {
  862. }
  863. public void messageLogged(BuildEvent event) {
  864. }
  865. }