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.

ClasspathUtils.java 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.util;
  19. import org.apache.tools.ant.AntClassLoader;
  20. import org.apache.tools.ant.BuildException;
  21. import org.apache.tools.ant.Project;
  22. import org.apache.tools.ant.ProjectComponent;
  23. import org.apache.tools.ant.MagicNames;
  24. import org.apache.tools.ant.types.Path;
  25. import org.apache.tools.ant.types.Reference;
  26. // CheckStyle:HideUtilityClassConstructorCheck OFF - bc
  27. /**
  28. * Offers some helper methods on the Path structure in ant.
  29. *
  30. * <p>The basic idea behind this utility class is to use it from inside the
  31. * different Ant objects (and user defined objects) that need classLoading
  32. * for their operation.
  33. * Normally those would have a setClasspathRef() {for the @classpathref}
  34. * and/or a createClasspath() {for the nested &lt;classpath&gt;}
  35. * Typically one would have in your Ant Task or DataType</p>
  36. *
  37. * <pre><code>
  38. * ClasspathUtils.Delegate cpDelegate;
  39. *
  40. * public void init() {
  41. * this.cpDelegate = ClasspathUtils.getDelegate(this);
  42. * super.init();
  43. * }
  44. *
  45. * public void setClasspathRef(Reference r) {
  46. * this.cpDelegate.setClasspathRef(r);
  47. * }
  48. *
  49. * public Path createClasspath() {
  50. * return this.cpDelegate.createClasspath();
  51. * }
  52. *
  53. * public void setClassname(String fqcn) {
  54. * this.cpDelegate.setClassname(fqcn);
  55. * }
  56. * </code></pre>
  57. *
  58. * <p>At execution time, when you actually need the classloading
  59. * you can just:</p>
  60. *
  61. * <pre><code>
  62. * Object o = this.cpDelegate.newInstance();
  63. * </code></pre>
  64. *
  65. * @since Ant 1.6
  66. */
  67. public class ClasspathUtils {
  68. /**
  69. * Name of the magic property that controls classloader reuse in Ant 1.4.
  70. */
  71. public static final String REUSE_LOADER_REF = MagicNames.REFID_CLASSPATH_REUSE_LOADER;
  72. /**
  73. * Convenience overloaded version of {@link
  74. * #getClassLoaderForPath(Project, Reference, boolean)}.
  75. *
  76. * <p>Assumes the logical 'false' for the reverseLoader.</p>
  77. *
  78. * @param p the project
  79. * @param ref the reference
  80. * @return The class loader
  81. */
  82. public static ClassLoader getClassLoaderForPath(Project p, Reference ref) {
  83. return getClassLoaderForPath(p, ref, false);
  84. }
  85. /**
  86. * Convenience overloaded version of {@link #getClassLoaderForPath(Project, Path,
  87. * String, boolean)}.
  88. *
  89. * <p>Delegates to the other one after extracting the referenced
  90. * Path from the Project. This checks also that the passed
  91. * Reference is pointing to a Path all right.</p>
  92. * @param p current Ant project
  93. * @param ref Reference to Path structure
  94. * @param reverseLoader if set to true this new loader will take
  95. * precedence over its parent (which is contra the regular
  96. * classloader behaviour)
  97. * @return The class loader
  98. */
  99. public static ClassLoader getClassLoaderForPath(
  100. Project p, Reference ref, boolean reverseLoader) {
  101. String pathId = ref.getRefId();
  102. Object path = p.getReference(pathId);
  103. if (!(path instanceof Path)) {
  104. throw new BuildException("The specified classpathref " + pathId
  105. + " does not reference a Path.");
  106. }
  107. String loaderId = MagicNames.REFID_CLASSPATH_LOADER_PREFIX + pathId;
  108. return getClassLoaderForPath(p, (Path) path, loaderId, reverseLoader);
  109. }
  110. /**
  111. * Convenience overloaded version of {@link
  112. * #getClassLoaderForPath(Project, Path, String, boolean)}.
  113. *
  114. * <p>Assumes the logical 'false' for the reverseLoader.</p>
  115. *
  116. * @param p current Ant project
  117. * @param path the path
  118. * @param loaderId the loader id string
  119. * @return The class loader
  120. */
  121. public static ClassLoader getClassLoaderForPath(Project p, Path path, String loaderId) {
  122. return getClassLoaderForPath(p, path, loaderId, false);
  123. }
  124. /**
  125. * Convenience overloaded version of {@link
  126. * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}.
  127. *
  128. * <p>Sets value for 'reuseLoader' to true if the magic property
  129. * has been set.</p>
  130. *
  131. * @param p the project
  132. * @param path the path
  133. * @param loaderId the loader id string
  134. * @param reverseLoader if set to true this new loader will take
  135. * precedence over its parent (which is contra the regular
  136. * classloader behaviour)
  137. * @return The class loader
  138. */
  139. public static ClassLoader getClassLoaderForPath(
  140. Project p, Path path, String loaderId, boolean reverseLoader) {
  141. return getClassLoaderForPath(p, path, loaderId, reverseLoader, isMagicPropertySet(p));
  142. }
  143. /**
  144. * Gets a classloader that loads classes from the classpath
  145. * defined in the path argument.
  146. *
  147. * <p>Based on the setting of the magic property
  148. * 'ant.reuse.loader' this will try to reuse the previously
  149. * created loader with that id, and of course store it there upon
  150. * creation.</p>
  151. * @param p Ant Project where the handled components are living in.
  152. * @param path Path object to be used as classpath for this classloader
  153. * @param loaderId identification for this Loader,
  154. * @param reverseLoader if set to true this new loader will take
  155. * precedence over its parent (which is contra the regular
  156. * classloader behaviour)
  157. * @param reuseLoader if true reuse the loader if it is found
  158. * @return ClassLoader that uses the Path as its classpath.
  159. */
  160. public static ClassLoader getClassLoaderForPath(
  161. Project p, Path path, String loaderId, boolean reverseLoader, boolean reuseLoader) {
  162. ClassLoader cl = null;
  163. // magic property
  164. if (loaderId != null && reuseLoader) {
  165. Object reusedLoader = p.getReference(loaderId);
  166. if (reusedLoader != null && !(reusedLoader instanceof ClassLoader)) {
  167. throw new BuildException("The specified loader id " + loaderId
  168. + " does not reference a class loader");
  169. }
  170. cl = (ClassLoader) reusedLoader;
  171. }
  172. if (cl == null) {
  173. cl = getUniqueClassLoaderForPath(p, path, reverseLoader);
  174. if (loaderId != null && reuseLoader) {
  175. p.addReference(loaderId, cl);
  176. }
  177. }
  178. return cl;
  179. }
  180. /**
  181. * Gets a fresh, different, previously unused classloader that uses the
  182. * passed path as its classpath.
  183. *
  184. * <p>This method completely ignores the ant.reuse.loader magic
  185. * property and should be used with caution.</p>
  186. * @param p Ant Project where the handled components are living in.
  187. * @param path the classpath for this loader
  188. * @param reverseLoader if set to true this new loader will take
  189. * precedence over its parent (which is contra the regular
  190. * classloader behaviour)
  191. * @return The fresh, different, previously unused class loader.
  192. */
  193. public static ClassLoader getUniqueClassLoaderForPath(Project p, Path path,
  194. boolean reverseLoader) {
  195. AntClassLoader acl = p.createClassLoader(path);
  196. if (reverseLoader) {
  197. acl.setParentFirst(false);
  198. acl.addJavaLibraries();
  199. }
  200. return acl;
  201. }
  202. /**
  203. * Creates a fresh object instance of the specified classname.
  204. *
  205. * <p> This uses the userDefinedLoader to load the specified class,
  206. * and then makes an instance using the default no-argument constructor.
  207. * </p>
  208. *
  209. * @param className the full qualified class name to load.
  210. * @param userDefinedLoader the classloader to use.
  211. * @return The fresh object instance
  212. * @throws BuildException when loading or instantiation failed.
  213. */
  214. public static Object newInstance(String className, ClassLoader userDefinedLoader) {
  215. return newInstance(className, userDefinedLoader, Object.class);
  216. }
  217. /**
  218. * Creates a fresh object instance of the specified classname.
  219. *
  220. * <p> This uses the userDefinedLoader to load the specified class,
  221. * and then makes an instance using the default no-argument constructor.
  222. * </p>
  223. *
  224. * @param className the full qualified class name to load.
  225. * @param userDefinedLoader the classloader to use.
  226. * @param expectedType the Class that the result should be assignment
  227. * compatible with. (No ClassCastException will be thrown in case
  228. * the result of this method is casted to the expectedType)
  229. * @return The fresh object instance
  230. * @throws BuildException when loading or instantiation failed.
  231. * @since Ant 1.7
  232. */
  233. public static Object newInstance(String className, ClassLoader userDefinedLoader,
  234. Class expectedType) {
  235. try {
  236. Class clazz = Class.forName(className, true, userDefinedLoader);
  237. Object o = clazz.newInstance();
  238. if (!expectedType.isInstance(o)) {
  239. throw new BuildException("Class of unexpected Type: " + className + " expected :"
  240. + expectedType);
  241. }
  242. return o;
  243. } catch (ClassNotFoundException e) {
  244. throw new BuildException("Class not found: " + className, e);
  245. } catch (InstantiationException e) {
  246. throw new BuildException("Could not instantiate " + className
  247. + ". Specified class should have a no " + "argument constructor.", e);
  248. } catch (IllegalAccessException e) {
  249. throw new BuildException("Could not instantiate " + className
  250. + ". Specified class should have a " + "public constructor.", e);
  251. } catch (LinkageError e) {
  252. throw new BuildException("Class " + className
  253. + " could not be loaded because of an invalid dependency.", e);
  254. }
  255. }
  256. /**
  257. * Obtains a delegate that helps out with classic classpath configuration.
  258. *
  259. * @param component your projectComponent that needs the assistence
  260. * @return the helper, delegate.
  261. * @see ClasspathUtils.Delegate
  262. */
  263. public static Delegate getDelegate(ProjectComponent component) {
  264. return new Delegate(component);
  265. }
  266. /**
  267. * Checks for the magic property that enables class loader reuse
  268. * for <taskdef> and <typedef> in Ant 1.5 and earlier.
  269. */
  270. private static boolean isMagicPropertySet(Project p) {
  271. return p.getProperty(REUSE_LOADER_REF) != null;
  272. }
  273. /**
  274. * Delegate that helps out any specific ProjectComponent that needs
  275. * dynamic classloading.
  276. *
  277. * <p>Ant ProjectComponents that need a to be able to dynamically load
  278. * Classes and instantiate them often expose the following ant syntax
  279. * sugar: </p>
  280. *
  281. * <ul><li> nested &lt;classpath&gt; </li>
  282. * <li> attribute @classpathref </li>
  283. * <li> attribute @classname </li></ul>
  284. *
  285. * <p> This class functions as a delegate handling the configuration
  286. * issues for this recurring pattern. Its usage pattern, as the name
  287. * suggests, is delegation rather than inheritance. </p>
  288. *
  289. * @since Ant 1.6
  290. */
  291. public static class Delegate {
  292. private final ProjectComponent component;
  293. private Path classpath;
  294. private String classpathId;
  295. private String className;
  296. private String loaderId;
  297. private boolean reverseLoader = false;
  298. /**
  299. * Construct a Delegate
  300. * @param component the ProjectComponent this delegate is for.
  301. */
  302. Delegate(ProjectComponent component) {
  303. this.component = component;
  304. }
  305. /**
  306. * This method is a Delegate method handling the @classpath attribute.
  307. *
  308. * <p>This attribute can set a path to add to the classpath.</p>
  309. *
  310. * @param classpath the path to use for the classpath.
  311. */
  312. public void setClasspath(Path classpath) {
  313. if (this.classpath == null) {
  314. this.classpath = classpath;
  315. } else {
  316. this.classpath.append(classpath);
  317. }
  318. }
  319. /**
  320. * Delegate method handling the &lt;classpath&gt; tag.
  321. *
  322. * <p>This nested path-like structure can set a path to add to the
  323. * classpath.</p>
  324. *
  325. * @return the created path.
  326. */
  327. public Path createClasspath() {
  328. if (this.classpath == null) {
  329. this.classpath = new Path(component.getProject());
  330. }
  331. return this.classpath.createPath();
  332. }
  333. /**
  334. * Delegate method handling the @classname attribute.
  335. *
  336. * <p>This attribute sets the full qualified class name of the class
  337. * to load and instantiate.</p>
  338. *
  339. * @param fcqn the name of the class to load.
  340. */
  341. public void setClassname(String fcqn) {
  342. this.className = fcqn;
  343. }
  344. /**
  345. * Delegate method handling the @classpathref attribute.
  346. *
  347. * <p>This attribute can add a referenced path-like structure to the
  348. * classpath.</p>
  349. *
  350. * @param r the reference to the classpath.
  351. */
  352. public void setClasspathref(Reference r) {
  353. this.classpathId = r.getRefId();
  354. createClasspath().setRefid(r);
  355. }
  356. /**
  357. * Delegate method handling the @reverseLoader attribute.
  358. *
  359. * <p>This attribute can set a boolean indicating that the used
  360. * classloader should NOT follow the classical parent-first scheme.
  361. * </p>
  362. *
  363. * <p>By default this is supposed to be false.</p>
  364. *
  365. * <p>Caution: this behaviour is contradictory to the normal way
  366. * classloaders work. Do not let your ProjectComponent use it if
  367. * you are not really sure.</p>
  368. *
  369. * @param reverseLoader if true reverse the order of looking up a class.
  370. */
  371. public void setReverseLoader(boolean reverseLoader) {
  372. this.reverseLoader = reverseLoader;
  373. }
  374. /**
  375. * Sets the loaderRef.
  376. * @param r the reference to the loader.
  377. */
  378. public void setLoaderRef(Reference r) {
  379. this.loaderId = r.getRefId();
  380. }
  381. /**
  382. * Finds or creates the classloader for this object.
  383. * @return The class loader.
  384. */
  385. public ClassLoader getClassLoader() {
  386. return getClassLoaderForPath(getContextProject(), classpath, getClassLoadId(),
  387. reverseLoader, loaderId != null || isMagicPropertySet(getContextProject()));
  388. }
  389. /**
  390. * The project of the ProjectComponent we are working for.
  391. */
  392. private Project getContextProject() {
  393. return component.getProject();
  394. }
  395. /**
  396. * Computes the loaderId based on the configuration of the component.
  397. * @return a loader identifier.
  398. */
  399. public String getClassLoadId() {
  400. if (loaderId == null && classpathId != null) {
  401. return MagicNames.REFID_CLASSPATH_LOADER_PREFIX + classpathId;
  402. } else {
  403. return loaderId;
  404. }
  405. }
  406. /**
  407. * Helper method obtaining a fresh instance of the class specified
  408. * in the @classname and using the specified classpath.
  409. *
  410. * @return the fresh instantiated object.
  411. */
  412. public Object newInstance() {
  413. return ClasspathUtils.newInstance(this.className, getClassLoader());
  414. }
  415. /**
  416. * The classpath.
  417. * @return the classpath.
  418. */
  419. public Path getClasspath() {
  420. return classpath;
  421. }
  422. /**
  423. * Get the reverseLoader setting.
  424. * @return true if looking up in reverse order.
  425. */
  426. public boolean isReverseLoader() {
  427. return reverseLoader;
  428. }
  429. //TODO no methods yet for getClassname
  430. //TODO no method for newInstance using a reverse-classloader
  431. }
  432. }