From b404a282b25f5b01eeb1646495eb2f36f675ebe9 Mon Sep 17 00:00:00 2001
From: Stefan Bodewig Basic idea behind this utility class is to use it from inside the
- * different ant objects (and user defined objects) that need dclassLoading
+ * different ant objects (and user defined objects) that need classLoading
* for their operation.
- * Normally those would have a setClasspathRef() {for the @@classpathref}
+ * Normally those would have a setClasspathRef() {for the @classpathref}
* and/or a createClasspath() {for the nested <classpath>}
- * Typically one would have
+ * ClasspathUtils.Delegate cpDelegate;
+ *
+ * public void init(){
+ * this.cpDelegate = ClasspathUtils.getDelegate(this);
+ * super.init();
+ * }
+ *
* public void setClasspathRef(Reference r) {
- * this.classpathId = r.getRefId();
- * createClasspath().setRefid(r);
+ * this.cpDelegate.setClasspathRef(r);
* }
*
* public Path createClasspath() {
- * if (this.classpath == null) {
- * this.classpath = new Path(getProject());
- * }
- * return this.classpath.createPath();
+ * return this.cpDelegate.createClasspath();
* }
*
* public void setClassname(String fqcn) {
- * this.classname = fqcn;
+ * this.cpDelegate.setClassname(fqcn);
* }
*
*
- * when you actually need the classloading you can just:
+ *At execution time, when you actually need the classloading + * you can just:
* *
- * ClassLoader cl = ClasspathUtils.getClassLoaderForPath(this.classpath, this.classpathId);
- * Object o = ClasspathUtils.newInstance(this.classname, cl);
+ * Object o = this.cpDelegate.newInstance();
*
*
* @since Ant 1.6
@@ -110,12 +114,14 @@ public class ClasspathUtils {
* @param pathId
* @return
*/
- public static ClassLoader getClassLoaderForPath(Project p, Reference ref) {
+ public static ClassLoader getClassLoaderForPath(
+ Project p, Reference ref) {
+
return getClassLoaderForPath(p, ref, false);
}
-
+
/**
- * Convenience overloaded version of {@link #geClassLoader(Path,
+ * Convenience overloaded version of {@link #geClassLoader(Project, Path,
* String, boolean)}.
*
* Delegates to the other one after extracting the referenced @@ -128,31 +134,54 @@ public class ClasspathUtils { * classloader behaviour) * @return */ - public static ClassLoader getClassLoaderForPath(Project p, Reference ref, - boolean reverseLoader) { + public static ClassLoader getClassLoaderForPath( + Project p, Reference ref, boolean reverseLoader) { + String pathId = ref.getRefId(); Object path = p.getReference(pathId); - if (!(path instanceof Path)){ - throw new BuildException ("The specified classpathref " + pathId + - " does not reference a Path."); - } - return getClassLoaderForPath((Path)path, pathId, reverseLoader); + if (!(path instanceof Path)) { + throw new BuildException( + "The specified classpathref " + + pathId + + " does not reference a Path."); + } + String loaderId = LOADER_ID_PREFIX + pathId; + return getClassLoaderForPath(p, (Path) path, loaderId, reverseLoader); } /** * Convenience overloaded version of {@link - * #getClassLoaderForPath(Path, String, boolean)}. + * #getClassLoaderForPath(Project, Path, String, boolean)}. * *
Assumes the logical 'false' for the reverseLoader.
* * @param path - * @param pathId + * @param loaderId * @return */ - public static ClassLoader getClassLoaderForPath(Path path, String pathId){ - return getClassLoaderForPath(path, pathId, false); + public static ClassLoader getClassLoaderForPath( + Project p, Path path, String loaderId) { + + return getClassLoaderForPath(p, path, loaderId, false); } - + + /** + * Convenience overloaded version of {@link + * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}. + * + *Sets value for 'reuseLoader' to true if the magic property + * has been set.
+ * + * @param path + * @param loaderId + * @return + */ + public static ClassLoader getClassLoaderForPath( + Project p, Path path, String loaderId, boolean reverseLoader) { + return getClassLoaderForPath(p, path, loaderId, reverseLoader, + isMagicPropertySet(p)); + } + /** * Gets a classloader that loads classes from the classpath * defined in the path argument. @@ -162,35 +191,38 @@ public class ClasspathUtils { * created loader with that id, and of course store it there upon * creation. * @param path Path object to be used as classpath for this classloader - * @param pathId identification for this Path, will be used to - * identify the classLoader as well. + * @param loaderID identification for this Loader, * @param reverseLoader if set to true this new loader will take * precedence over it's parent (which is contra the regular + * @param p Ant Project where the handled components are living in. * classloader behaviour) * @return ClassLoader that uses the Path as its classpath. */ - public static ClassLoader getClassLoaderForPath(Path path, String pathId, - boolean reverseLoader) { + public static ClassLoader getClassLoaderForPath( + Project p, Path path, String loaderId, boolean reverseLoader, + boolean reuseLoader) { + ClassLoader cl = null; - Project p = path.getProject(); - String loaderId = LOADER_ID_PREFIX + pathId; - // code stolen from o.a.t.a.taskdefs.Definer, might be a todo - // to remove it there didn't look at the reverse loader stuff - // however (todo that first) // magic property - if (p.getProperty(REUSE_LOADER_REF) != null) { - //chose not to do the extra instanceof checking here, consider it - // a programming error and not a config error if this fails - // so I assume the RuntimeException is OK - cl = (ClassLoader)p.getReference(loaderId); - } - if (cl == null){ - cl = getUniqueClassLoaderForPath(path, reverseLoader); - p.addReference(loaderId, cl); - } - - return cl; + if (loaderId != null && reuseLoader) { + Object reusedLoader = p.getReference(loaderId); + if (reusedLoader != null && + !(reusedLoader instanceof ClassLoader)) { + throw new BuildException("The specified loader id " + loaderId + + " does not reference a class loader"); + } + + cl = (ClassLoader) reusedLoader; + } + if (cl == null) { + cl = getUniqueClassLoaderForPath(p, path, reverseLoader); + if (loaderId != null && reuseLoader) { + p.addReference(loaderId, cl); + } + } + + return cl; } /** @@ -203,39 +235,256 @@ public class ClasspathUtils { * @param reverseLoader * @return */ - public static ClassLoader getUniqueClassLoaderForPath(Path path, - boolean reverseLoader) { - ClassLoader cl = null; - Project p = path.getProject(); + public static ClassLoader getUniqueClassLoaderForPath( + Project p, + Path path, + boolean reverseLoader) { - AntClassLoader acl = p.createClassLoader(Path.systemClasspath); + AntClassLoader acl = p.createClassLoader(path != null + ? path : Path.systemClasspath); if (reverseLoader) { acl.setParentFirst(false); acl.addJavaLibraries(); } - return cl; + return acl; } - - public static Object newInstance(String className, - ClassLoader userDefinedLoader){ + + /** + * Creates a fresh object instance of the specified classname. + * + *This uses the userDefinedLoader to load the specified class, + * and then makes an instance using the default no-argument constructor + *
+ * + * @param className the full qualified class name to load. + * @param userDefinedLoader the classloader to use. + * @return + * @throws BuildException when loading or instantiation failed. + */ + public static Object newInstance( + String className, + ClassLoader userDefinedLoader) { try { Class clazz = userDefinedLoader.loadClass(className); - Object o = clazz.newInstance(); - return o; + Object o = clazz.newInstance(); + return o; } catch (ClassNotFoundException e) { - throw new BuildException("Class " + className + - " not found by the specific classLoader.", - e); + throw new BuildException( + "Class " + + className + + " not found by the specific classLoader.", + e); } catch (InstantiationException e) { - throw new BuildException("Could not instantiate " + className - + ". Specified class should have a no " - + "argument constructor.", e); + throw new BuildException( + "Could not instantiate " + + className + + ". Specified class should have a no " + + "argument constructor.", + e); } catch (IllegalAccessException e) { - throw new BuildException("Could not instantiate " + className - + ". Specified class should have a " - + "public constructor.", e); + throw new BuildException( + "Could not instantiate " + + className + + ". Specified class should have a " + + "public constructor.", + e); } } + /** + * Obtains a delegate that helps out with classic classpath configuration. + * + * @param component your projectComponent that needs the assistence + * @return the helper, delegate. + * @see ClasspathUtils.Delegate + */ + public static Delegate getDelegate(ProjectComponent component) { + return new Delegate(component); + } + + /** + * Checks for the magic property that enables class loader reuse + * forAnt ProjectComponents that need a to be able to dynamically load + * Classes and instantiate them often expose the following ant syntax + * sugar:
+ * + *This class functions as a delegate handling the configuration + * issues for this recuring pattern. It's usage pattern, as the name + * suggests is delegation, not inheritance.
+ * + * @since Ant 1.6 + */ + public static class Delegate { + private final ProjectComponent component; + private Path classpath; + private String classpathId; + private String className; + private String loaderId; + private boolean reverseLoader = false; + + /** + * Constructs Delegate + * @param component + */ + Delegate(ProjectComponent component) { + this.component = component; + } + + /** + * Delegate method handling the @classpath attribute + * + *This attribute can set a path to add to the classpath
+ * + * @param classpath + */ + public void setClasspath(Path classpath) { + if (this.classpath == null) { + this.classpath = classpath; + } else { + this.classpath.append(classpath); + } + } + + /** + * Delegate method handling the <classpath> tag. + * + *This nested path-like structure can set a path to add to the + * classpath
+ * + * @return + */ + public Path createClasspath() { + if (this.classpath == null) { + this.classpath = new Path(component.getProject()); + } + return this.classpath.createPath(); + } + + /** + * Delegate method handling the @classname attribute. + * + *This attribute sets the full qualified class name of the class + * to lad and instantiate
+ * + * @param fcqn + */ + public void setClassname(String fcqn) { + this.className = fcqn; + } + + /** + * Delegate method handling the @classpathref attribute. + * + *This attribute can add a referenced path-like structure to the + * classpath
+ * + * @param r + */ + public void setClasspathref(Reference r) { + this.classpathId = r.getRefId(); + createClasspath().setRefid(r); + } + + /** + * Delegate method handling the @reverseLoader attribute. + * + *This attribute can set a boolean indicating that the used + * classloader should NOT follow the classical parent-first scheme. + *
+ * + *By default this is supposed to be false
+ * + *Caution: this behaviour is contradictory to the normal way + * classloaders work. Do not let your ProjectComponent use it if + * you are not really sure
+ * + * @param reverseLoader + */ + public void setReverseLoader(boolean reverseLoader) { + this.reverseLoader = reverseLoader; + } + + /** + * Sets the loaderRef + * @param r + */ + public void setLoaderRef(Reference r) { + this.loaderId = r.getRefId(); + } + + + /** + * Finds or creates the classloader for this + * @return + */ + public ClassLoader getClassLoader() { + ClassLoader cl; + cl= ClasspathUtils.getClassLoaderForPath( + getContextProject(), + this.classpath, + getClassLoadId(), + this.reverseLoader, + loaderId != null + || isMagicPropertySet(getContextProject())); + return cl; + } + + /** + * The project of the ProjectComponent we are working for. + */ + private Project getContextProject() { + return this.component.getProject(); + } + + /** + * Computes the loaderId based on the configuration of the component. + */ + public String getClassLoadId() { + if (this.loaderId == null && this.classpathId != null) { + return ClasspathUtils.LOADER_ID_PREFIX + this.classpathId; + } else { + return this.loaderId; + } + } + + /** + * Helper method obtaining a fresh instance of the class specified + * in the @classname and using the specified classpath. + * + * @return the fresh instantiated object. + */ + public Object newInstance() { + ClassLoader cl = getClassLoader(); + return ClasspathUtils.newInstance(this.className, cl); + } + + /** + * The classpath. + */ + public Path getClasspath() { + return classpath; + } + + public boolean isReverseLoader() { + return reverseLoader; + } + + //TODO no methods yet for getClassname + //TODO no method for newInstance using a reverse-classloader + } }