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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 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", "Tomcat", 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.util.*;
  56. import java.util.zip.*;
  57. import java.io.*;
  58. import org.apache.tools.ant.types.Path;
  59. /**
  60. * Used to load classes within ant with a different claspath from that used to start ant.
  61. * Note that it is possible to force a class into this loader even when that class is on the
  62. * system classpath by using the forceLoadClass method. Any subsequent classes loaded by that
  63. * class will then use this loader rather than the system class loader.
  64. *
  65. * @author Conor MacNeill
  66. */
  67. public class AntClassLoader extends ClassLoader {
  68. /**
  69. * The size of buffers to be used in this classloader.
  70. */
  71. static private final int BUFFER_SIZE = 1024;
  72. /**
  73. * The classpath that is to be used when loading classes using this class loader.
  74. */
  75. private Path classpath;
  76. /**
  77. * The project to which this class loader belongs.
  78. */
  79. private Project project;
  80. /**
  81. * Indicates whether the system class loader should be
  82. * consulted before trying to load with this class loader.
  83. */
  84. private boolean systemFirst = true;
  85. /**
  86. * These are the package roots that are to be loaded by the system class loader
  87. * regardless of whether the system class loader is being searched first or not.
  88. */
  89. private Vector systemPackages = new Vector();
  90. /**
  91. * These are the package roots that are to be loaded by this class loader
  92. * regardless of whether the system class loader is being searched first or not.
  93. */
  94. private Vector loaderPackages = new Vector();
  95. /**
  96. * Create a classloader for the given project using the classpath given.
  97. *
  98. * @param project the project to ehich this classloader is to belong.
  99. * @param classpath the classpath to use to load the classes.
  100. */
  101. public AntClassLoader(Project project, Path classpath) {
  102. this.project = project;
  103. this.classpath = classpath;
  104. }
  105. /**
  106. * Create a classloader for the given project using the classpath given.
  107. *
  108. * @param project the project to ehich this classloader is to belong.
  109. * @param classpath the classpath to use to load the classes.
  110. */
  111. public AntClassLoader(Project project, Path classpath, boolean systemFirst) {
  112. this(project, classpath);
  113. this.systemFirst = systemFirst;
  114. }
  115. /**
  116. * Add a package root to the list of packages which must be loaded on the
  117. * system loader.
  118. *
  119. * All subpackages are also included.
  120. *
  121. * @param packageRoot the root of akll packages to be included.
  122. */
  123. public void addSystemPackageRoot(String packageRoot) {
  124. systemPackages.addElement(packageRoot + ".");
  125. }
  126. /**
  127. * Add a package root to the list of packages which must be loaded using
  128. * this loader.
  129. *
  130. * All subpackages are also included.
  131. *
  132. * @param packageRoot the root of akll packages to be included.
  133. */
  134. public void addLoaderPackageRoot(String packageRoot) {
  135. loaderPackages.addElement(packageRoot + ".");
  136. }
  137. /**
  138. * Load a class through this class loader even if that class is available on the
  139. * system classpath.
  140. *
  141. * This ensures that any classes which are loaded by the returned class will use this
  142. * classloader.
  143. *
  144. * @param classname the classname to be loaded.
  145. *
  146. * @return the required Class object
  147. *
  148. * @throws ClassNotFoundException if the requested class does not exist on
  149. * this loader's classpath.
  150. */
  151. public Class forceLoadClass(String classname) throws ClassNotFoundException {
  152. project.log("force loading " + classname, Project.MSG_VERBOSE);
  153. Class theClass = findLoadedClass(classname);
  154. if (theClass == null) {
  155. theClass = findClass(classname);
  156. }
  157. return theClass;
  158. }
  159. /**
  160. * Load a class through this class loader but defer to the system class loader
  161. *
  162. * This ensures that instances of the returned class will be compatible with instances which
  163. * which have already been loaded on the system loader.
  164. *
  165. * @param classname the classname to be loaded.
  166. *
  167. * @return the required Class object
  168. *
  169. * @throws ClassNotFoundException if the requested class does not exist on
  170. * this loader's classpath.
  171. */
  172. public Class forceLoadSystemClass(String classname) throws ClassNotFoundException {
  173. project.log("force system loading " + classname, Project.MSG_VERBOSE);
  174. Class theClass = findLoadedClass(classname);
  175. if (theClass == null) {
  176. theClass = findSystemClass(classname);
  177. }
  178. return theClass;
  179. }
  180. /**
  181. * Get a stream to read the requested resource name.
  182. *
  183. * @param name the name of the resource for which a stream is required.
  184. *
  185. * @return a stream to the required resource or null if the resource cannot be
  186. * found on the loader's classpath.
  187. */
  188. public InputStream getResourceAsStream(String name) {
  189. // we need to search the components of the path to see if we can find the
  190. // class we want.
  191. InputStream stream = null;
  192. String[] pathElements = classpath.list();
  193. for (int i = 0; i < pathElements.length && stream == null; ++i) {
  194. File pathComponent = project.resolveFile((String)pathElements[i]);
  195. stream = getResourceStream(pathComponent, name);
  196. }
  197. return stream;
  198. }
  199. /**
  200. * Get an inputstream to a given resource in the given file which may
  201. * either be a directory or a zip file.
  202. *
  203. * @param file the file (directory or jar) in which to search for the resource.
  204. * @param resourceName the name of the resource for which a stream is required.
  205. *
  206. * @return a stream to the required resource or null if the resource cannot be
  207. * found in the given file object
  208. */
  209. private InputStream getResourceStream(File file, String resourceName) {
  210. try {
  211. if (!file.exists()) {
  212. return null;
  213. }
  214. if (file.isDirectory()) {
  215. File resource = new File(file, resourceName);
  216. if (resource.exists()) {
  217. return new FileInputStream(resource);
  218. }
  219. }
  220. else {
  221. ZipFile zipFile = null;
  222. try {
  223. zipFile = new ZipFile(file);
  224. ZipEntry entry = zipFile.getEntry(resourceName);
  225. if (entry != null) {
  226. // we need to read the entry out of the zip file into
  227. // a baos and then
  228. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  229. byte[] buffer = new byte[BUFFER_SIZE];
  230. int bytesRead;
  231. InputStream stream = zipFile.getInputStream(entry);
  232. while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
  233. baos.write(buffer, 0, bytesRead);
  234. }
  235. return new ByteArrayInputStream(baos.toByteArray());
  236. }
  237. }
  238. finally {
  239. if (zipFile != null) {
  240. zipFile.close();
  241. }
  242. }
  243. }
  244. }
  245. catch (Exception e) {
  246. e.printStackTrace();
  247. }
  248. return null;
  249. }
  250. /**
  251. * Load a class with this class loader.
  252. *
  253. * This method will load a class.
  254. *
  255. * This class attempts to load the class firstly using the parent class loader. For
  256. * JDK 1.1 compatability, this uses the findSystemClass method.
  257. *
  258. * @param classname the name of the class to be loaded.
  259. * @param resolve true if all classes upon which this class depends are to be loaded.
  260. *
  261. * @return the required Class object
  262. *
  263. * @throws ClassNotFoundException if the requested class does not exist on
  264. * the system classpath or this loader's classpath.
  265. */
  266. protected Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {
  267. // default to the global setting and then see
  268. // if this class belongs to a package which has been
  269. // designated to use a specific loader first (this one or the system one)
  270. boolean useSystemFirst = systemFirst;
  271. for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) {
  272. String packageName = (String)e.nextElement();
  273. if (classname.startsWith(packageName)) {
  274. useSystemFirst = true;
  275. break;
  276. }
  277. }
  278. for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) {
  279. String packageName = (String)e.nextElement();
  280. if (classname.startsWith(packageName)) {
  281. useSystemFirst = false;
  282. break;
  283. }
  284. }
  285. Class theClass = findLoadedClass(classname);
  286. if (theClass == null) {
  287. if (useSystemFirst) {
  288. try {
  289. theClass = findSystemClass(classname);
  290. project.log("Class " + classname + " loaded from system loader", Project.MSG_VERBOSE);
  291. }
  292. catch (ClassNotFoundException cnfe) {
  293. theClass = findClass(classname);
  294. project.log("Class " + classname + " loaded from ant loader", Project.MSG_VERBOSE);
  295. }
  296. }
  297. else {
  298. try {
  299. theClass = findClass(classname);
  300. project.log("Class " + classname + " loaded from ant loader", Project.MSG_VERBOSE);
  301. }
  302. catch (ClassNotFoundException cnfe) {
  303. theClass = findSystemClass(classname);
  304. project.log("Class " + classname + " loaded from system loader", Project.MSG_VERBOSE);
  305. }
  306. }
  307. }
  308. if (resolve) {
  309. resolveClass(theClass);
  310. }
  311. return theClass;
  312. }
  313. /**
  314. * Convert the class dot notation to a file system equivalent for
  315. * searching purposes.
  316. *
  317. * @param classname the class name in dot format (ie java.lang.Integer)
  318. *
  319. * @return the classname in file system format (ie java/lang/Integer.class)
  320. */
  321. private String getClassFilename(String classname) {
  322. return classname.replace('.', '/') + ".class";
  323. }
  324. /**
  325. * Read a class definition from a stream.
  326. *
  327. * @param stream the stream from which the class is to be read.
  328. * @param classname the class name of the class in the stream.
  329. *
  330. * @return the Class object read from the stream.
  331. *
  332. * @throws IOException if there is a problem reading the class from the
  333. * stream.
  334. */
  335. private Class getClassFromStream(InputStream stream, String classname)
  336. throws IOException {
  337. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  338. int bytesRead = -1;
  339. byte[] buffer = new byte[1024];
  340. while ((bytesRead = stream.read(buffer, 0, 1024)) != -1) {
  341. baos.write(buffer, 0, bytesRead);
  342. }
  343. byte[] classData = baos.toByteArray();
  344. return defineClass(classname, classData, 0, classData.length);
  345. }
  346. /**
  347. * Search for and load a class on the classpath of this class loader.
  348. *
  349. * @param name the classname to be loaded.
  350. *
  351. * @return the required Class object
  352. *
  353. * @throws ClassNotFoundException if the requested class does not exist on
  354. * this loader's classpath.
  355. */
  356. public Class findClass(String name) throws ClassNotFoundException {
  357. project.log("Finding class " + name, Project.MSG_VERBOSE);
  358. try {
  359. return findClass(name, classpath);
  360. }
  361. catch (ClassNotFoundException e) {
  362. throw e;
  363. }
  364. }
  365. /**
  366. * Find a class on the given classpath.
  367. */
  368. private Class findClass(String name, Path path) throws ClassNotFoundException {
  369. // we need to search the components of the path to see if we can find the
  370. // class we want.
  371. InputStream stream = null;
  372. String classFilename = getClassFilename(name);
  373. try {
  374. String[] pathElements = path.list();
  375. for (int i = 0; i < pathElements.length && stream == null; ++i) {
  376. File pathComponent = project.resolveFile((String)pathElements[i]);
  377. stream = getResourceStream(pathComponent, classFilename);
  378. }
  379. if (stream == null) {
  380. throw new ClassNotFoundException();
  381. }
  382. return getClassFromStream(stream, name);
  383. }
  384. catch (IOException ioe) {
  385. ioe.printStackTrace();
  386. throw new ClassNotFoundException();
  387. }
  388. finally {
  389. try {
  390. if (stream != null) {
  391. stream.close();
  392. }
  393. }
  394. catch (IOException e) {}
  395. }
  396. }
  397. }