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