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 | */
|
---|
17 | package org.apache.tools.ant.util;
|
---|
18 |
|
---|
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.types.Path;
|
---|
24 | import 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 <classpath>}
|
---|
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 | */
|
---|
66 | public 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 <classpath> </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 <classpath> 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 | }
|
---|