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 43 kB

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