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