source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/util/ClasspathUtils.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

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