KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > util > ClasspathUtils


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
20 import org.apache.tools.ant.AntClassLoader;
21 import org.apache.tools.ant.BuildException;
22 import org.apache.tools.ant.Project;
23 import org.apache.tools.ant.ProjectComponent;
24 import org.apache.tools.ant.MagicNames;
25 import org.apache.tools.ant.types.Path;
26 import org.apache.tools.ant.types.Reference;
27
28 // CheckStyle:HideUtilityClassConstructorCheck OFF - bc
29

30 /**
31  * Offers some helper methods on the Path structure in ant.
32  *
33  * <p>The basic idea behind this utility class is to use it from inside the
34  * different Ant objects (and user defined objects) that need classLoading
35  * for their operation.
36  * Normally those would have a setClasspathRef() {for the @classpathref}
37  * and/or a createClasspath() {for the nested &lt;classpath&gt;}
38  * Typically one would have in your Ant Task or DataType</p>
39  *
40  * <pre><code>
41  * ClasspathUtils.Delegate cpDelegate;
42  *
43  * public void init() {
44  * this.cpDelegate = ClasspathUtils.getDelegate(this);
45  * super.init();
46  * }
47  *
48  * public void setClasspathRef(Reference r) {
49  * this.cpDelegate.setClasspathRef(r);
50  * }
51  *
52  * public Path createClasspath() {
53  * return this.cpDelegate.createClasspath();
54  * }
55  *
56  * public void setClassname(String fqcn) {
57  * this.cpDelegate.setClassname(fqcn);
58  * }
59  * </code></pre>
60  *
61  * <p>At execution time, when you actually need the classloading
62  * you can just:</p>
63  *
64  * <pre><code>
65  * Object o = this.cpDelegate.newInstance();
66  * </code></pre>
67  *
68  * @since Ant 1.6
69  */

70 public class ClasspathUtils {
71
72     /**
73      * Name of the magic property that controls classloader reuse in Ant 1.4.
74      */

75     public static final String JavaDoc REUSE_LOADER_REF = MagicNames.REFID_CLASSPATH_REUSE_LOADER;
76
77     /**
78      * Convenience overloaded version of {@link
79      * #getClassLoaderForPath(Project, Reference, boolean)}.
80      *
81      * <p>Assumes the logical 'false' for the reverseLoader.</p>
82      *
83      * @param p the project
84      * @param ref the reference
85      * @return The class loader
86      */

87     public static ClassLoader JavaDoc getClassLoaderForPath(
88         Project p, Reference ref) {
89
90         return getClassLoaderForPath(p, ref, false);
91     }
92
93     /**
94      * Convenience overloaded version of {@link #getClassLoaderForPath(Project, Path,
95      * String, boolean)}.
96      *
97      * <p>Delegates to the other one after extracting the referenced
98      * Path from the Project. This checks also that the passed
99      * Reference is pointing to a Path all right.</p>
100      * @param p current Ant project
101      * @param ref Reference to Path structure
102      * @param reverseLoader if set to true this new loader will take
103      * precedence over its parent (which is contra the regular
104      * classloader behaviour)
105      * @return The class loader
106      */

107     public static ClassLoader JavaDoc getClassLoaderForPath(
108         Project p, Reference ref, boolean reverseLoader) {
109
110         String JavaDoc pathId = ref.getRefId();
111         Object JavaDoc path = p.getReference(pathId);
112         if (!(path instanceof Path)) {
113             throw new BuildException(
114                 "The specified classpathref "
115                     + pathId
116                     + " does not reference a Path.");
117         }
118         String JavaDoc loaderId = MagicNames.REFID_CLASSPATH_LOADER_PREFIX + pathId;
119         return getClassLoaderForPath(p, (Path) path, loaderId, reverseLoader);
120     }
121
122     /**
123      * Convenience overloaded version of {@link
124      * #getClassLoaderForPath(Project, Path, String, boolean)}.
125      *
126      * <p>Assumes the logical 'false' for the reverseLoader.</p>
127      *
128      * @param p current Ant project
129      * @param path the path
130      * @param loaderId the loader id string
131      * @return The class loader
132      */

133     public static ClassLoader JavaDoc getClassLoaderForPath(
134         Project p, Path path, String JavaDoc loaderId) {
135
136         return getClassLoaderForPath(p, path, loaderId, false);
137     }
138
139     /**
140      * Convenience overloaded version of {@link
141      * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}.
142      *
143      * <p>Sets value for 'reuseLoader' to true if the magic property
144      * has been set.</p>
145      *
146      * @param p the project
147      * @param path the path
148      * @param loaderId the loader id string
149      * @param reverseLoader if set to true this new loader will take
150      * precedence over its parent (which is contra the regular
151      * classloader behaviour)
152      * @return The class loader
153      */

154     public static ClassLoader JavaDoc getClassLoaderForPath(
155         Project p, Path path, String JavaDoc loaderId, boolean reverseLoader) {
156         return getClassLoaderForPath(p, path, loaderId, reverseLoader,
157                                      isMagicPropertySet(p));
158     }
159
160     /**
161      * Gets a classloader that loads classes from the classpath
162      * defined in the path argument.
163      *
164      * <p>Based on the setting of the magic property
165      * 'ant.reuse.loader' this will try to reuse the previously
166      * created loader with that id, and of course store it there upon
167      * creation.</p>
168      * @param p Ant Project where the handled components are living in.
169      * @param path Path object to be used as classpath for this classloader
170      * @param loaderId identification for this Loader,
171      * @param reverseLoader if set to true this new loader will take
172      * precedence over its parent (which is contra the regular
173      * classloader behaviour)
174      * @param reuseLoader if true reuse the loader if it is found
175      * @return ClassLoader that uses the Path as its classpath.
176      */

177     public static ClassLoader JavaDoc getClassLoaderForPath(
178         Project p, Path path, String JavaDoc loaderId, boolean reverseLoader,
179         boolean reuseLoader) {
180
181         ClassLoader JavaDoc cl = null;
182
183         // magic property
184
if (loaderId != null && reuseLoader) {
185             Object JavaDoc reusedLoader = p.getReference(loaderId);
186             if (reusedLoader != null
187                 && !(reusedLoader instanceof ClassLoader JavaDoc)) {
188                 throw new BuildException("The specified loader id " + loaderId
189                     + " does not reference a class loader");
190             }
191             cl = (ClassLoader JavaDoc) reusedLoader;
192         }
193         if (cl == null) {
194             cl = getUniqueClassLoaderForPath(p, path, reverseLoader);
195             if (loaderId != null && reuseLoader) {
196                 p.addReference(loaderId, cl);
197             }
198         }
199         return cl;
200     }
201
202     /**
203      * Gets a fresh, different, previously unused classloader that uses the
204      * passed path as its classpath.
205      *
206      * <p>This method completely ignores the ant.reuse.loader magic
207      * property and should be used with caution.</p>
208      * @param p Ant Project where the handled components are living in.
209      * @param path the classpath for this loader
210      * @param reverseLoader if set to true this new loader will take
211      * precedence over its parent (which is contra the regular
212      * classloader behaviour)
213      * @return The fresh, different, previously unused class loader.
214      */

215     public static ClassLoader JavaDoc getUniqueClassLoaderForPath(
216         Project p,
217         Path path,
218         boolean reverseLoader) {
219         AntClassLoader acl = p.createClassLoader(path);
220         if (reverseLoader) {
221             acl.setParentFirst(false);
222             acl.addJavaLibraries();
223         }
224         return acl;
225     }
226
227     /**
228      * Creates a fresh object instance of the specified classname.
229      *
230      * <p> This uses the userDefinedLoader to load the specified class,
231      * and then makes an instance using the default no-argument constructor.
232      * </p>
233      *
234      * @param className the full qualified class name to load.
235      * @param userDefinedLoader the classloader to use.
236      * @return The fresh object instance
237      * @throws BuildException when loading or instantiation failed.
238      */

239     public static Object JavaDoc newInstance(
240             String JavaDoc className,
241             ClassLoader JavaDoc userDefinedLoader) {
242         return newInstance(className, userDefinedLoader, Object JavaDoc.class);
243     }
244
245     /**
246      * Creates a fresh object instance of the specified classname.
247      *
248      * <p> This uses the userDefinedLoader to load the specified class,
249      * and then makes an instance using the default no-argument constructor.
250      * </p>
251      *
252      * @param className the full qualified class name to load.
253      * @param userDefinedLoader the classloader to use.
254      * @param expectedType the Class that the result should be assignment
255      * compatible with. (No ClassCastException will be thrown in case
256      * the result of this method is casted to the expectedType)
257      * @return The fresh object instance
258      * @throws BuildException when loading or instantiation failed.
259      * @since Ant 1.7
260      */

261     public static Object JavaDoc newInstance(
262         String JavaDoc className,
263         ClassLoader JavaDoc userDefinedLoader,
264         Class JavaDoc expectedType) {
265         try {
266             Class JavaDoc clazz = Class.forName(className, true, userDefinedLoader);
267             Object JavaDoc o = clazz.newInstance();
268             if (!expectedType.isInstance(o)) {
269                 throw new BuildException(
270                     "Class of unexpected Type: "
271                         + className
272                         + " expected :"
273                         + expectedType);
274             }
275             return o;
276         } catch (ClassNotFoundException JavaDoc e) {
277             throw new BuildException(
278                 "Class not found: "
279                     + className,
280                 e);
281         } catch (InstantiationException JavaDoc e) {
282             throw new BuildException(
283                 "Could not instantiate "
284                     + className
285                     + ". Specified class should have a no "
286                     + "argument constructor.",
287                 e);
288         } catch (IllegalAccessException JavaDoc e) {
289             throw new BuildException(
290                 "Could not instantiate "
291                     + className
292                     + ". Specified class should have a "
293                     + "public constructor.",
294                 e);
295         } catch (LinkageError JavaDoc e) {
296             throw new BuildException(
297                 "Class "
298                     + className
299                     + " could not be loaded because of an invalid dependency.",
300                 e);
301         }
302     }
303
304     /**
305      * Obtains a delegate that helps out with classic classpath configuration.
306      *
307      * @param component your projectComponent that needs the assistence
308      * @return the helper, delegate.
309      * @see ClasspathUtils.Delegate
310      */

311     public static Delegate getDelegate(ProjectComponent component) {
312         return new Delegate(component);
313     }
314
315     /**
316      * Checks for the magic property that enables class loader reuse
317      * for <taskdef> and <typedef> in Ant 1.5 and earlier.
318      */

319     private static boolean isMagicPropertySet(Project p) {
320         return p.getProperty(REUSE_LOADER_REF) != null;
321     }
322
323     /**
324      * Delegate that helps out any specific ProjectComponent that needs
325      * dynamic classloading.
326      *
327      * <p>Ant ProjectComponents that need a to be able to dynamically load
328      * Classes and instantiate them often expose the following ant syntax
329      * sugar: </p>
330      *
331      * <ul><li> nested &lt;classpath&gt; </li>
332      * <li> attribute @classpathref </li>
333      * <li> attribute @classname </li></ul>
334      *
335      * <p> This class functions as a delegate handling the configuration
336      * issues for this recurring pattern. Its usage pattern, as the name
337      * suggests, is delegation rather than inheritance. </p>
338      *
339      * @since Ant 1.6
340      */

341     public static class Delegate {
342         private final ProjectComponent component;
343         private Path classpath;
344         private String JavaDoc classpathId;
345         private String JavaDoc className;
346         private String JavaDoc loaderId;
347         private boolean reverseLoader = false;
348
349         /**
350          * Construct a Delegate
351          * @param component the ProjectComponent this delegate is for.
352          */

353         Delegate(ProjectComponent component) {
354             this.component = component;
355         }
356
357         /**
358          * This method is a Delegate method handling the @classpath attribute.
359          *
360          * <p>This attribute can set a path to add to the classpath.</p>
361          *
362          * @param classpath the path to use for the classpath.
363          */

364         public void setClasspath(Path classpath) {
365             if (this.classpath == null) {
366                 this.classpath = classpath;
367             } else {
368                 this.classpath.append(classpath);
369             }
370         }
371
372         /**
373          * Delegate method handling the &lt;classpath&gt; tag.
374          *
375          * <p>This nested path-like structure can set a path to add to the
376          * classpath.</p>
377          *
378          * @return the created path.
379          */

380         public Path createClasspath() {
381             if (this.classpath == null) {
382                 this.classpath = new Path(component.getProject());
383             }
384             return this.classpath.createPath();
385         }
386
387         /**
388          * Delegate method handling the @classname attribute.
389          *
390          * <p>This attribute sets the full qualified class name of the class
391          * to load and instantiate.</p>
392          *
393          * @param fcqn the name of the class to load.
394          */

395         public void setClassname(String JavaDoc fcqn) {
396             this.className = fcqn;
397         }
398
399         /**
400          * Delegate method handling the @classpathref attribute.
401          *
402          * <p>This attribute can add a referenced path-like structure to the
403          * classpath.</p>
404          *
405          * @param r the reference to the classpath.
406          */

407         public void setClasspathref(Reference r) {
408             this.classpathId = r.getRefId();
409             createClasspath().setRefid(r);
410         }
411
412         /**
413          * Delegate method handling the @reverseLoader attribute.
414          *
415          * <p>This attribute can set a boolean indicating that the used
416          * classloader should NOT follow the classical parent-first scheme.
417          * </p>
418          *
419          * <p>By default this is supposed to be false.</p>
420          *
421          * <p>Caution: this behaviour is contradictory to the normal way
422          * classloaders work. Do not let your ProjectComponent use it if
423          * you are not really sure.</p>
424          *
425          * @param reverseLoader if true reverse the order of looking up a class.
426          */

427         public void setReverseLoader(boolean reverseLoader) {
428             this.reverseLoader = reverseLoader;
429         }
430
431         /**
432          * Sets the loaderRef.
433          * @param r the reference to the loader.
434          */

435         public void setLoaderRef(Reference r) {
436             this.loaderId = r.getRefId();
437         }
438
439
440         /**
441          * Finds or creates the classloader for this object.
442          * @return The class loader.
443          */

444         public ClassLoader JavaDoc getClassLoader() {
445             return getClassLoaderForPath(
446                     getContextProject(),
447                     this.classpath,
448                     getClassLoadId(),
449                     this.reverseLoader,
450                     loaderId != null || isMagicPropertySet(getContextProject()));
451         }
452
453         /**
454          * The project of the ProjectComponent we are working for.
455          */

456         private Project getContextProject() {
457             return this.component.getProject();
458         }
459
460         /**
461          * Computes the loaderId based on the configuration of the component.
462          * @return a loader identifier.
463          */

464         public String JavaDoc getClassLoadId() {
465             return this.loaderId == null && this.classpathId != null
466                 ? MagicNames.REFID_CLASSPATH_LOADER_PREFIX + this.classpathId
467                 : this.loaderId;
468         }
469
470         /**
471          * Helper method obtaining a fresh instance of the class specified
472          * in the @classname and using the specified classpath.
473          *
474          * @return the fresh instantiated object.
475          */

476         public Object JavaDoc newInstance() {
477             return ClasspathUtils.newInstance(this.className, getClassLoader());
478         }
479
480         /**
481          * The classpath.
482          * @return the classpath.
483          */

484         public Path getClasspath() {
485             return classpath;
486         }
487
488         /**
489          * Get the reverseLoader setting.
490          * @return true if looking up in reverse order.
491          */

492         public boolean isReverseLoader() {
493             return reverseLoader;
494         }
495
496         //TODO no methods yet for getClassname
497
//TODO no method for newInstance using a reverse-classloader
498
}
499 }
500
Popular Tags