bodewig 2003/04/23 08:03:06
Modified: src/etc/testcases/core/loaderref loaderref.xml
src/main/org/apache/tools/ant/taskdefs Definer.java
src/main/org/apache/tools/ant/util ClasspathUtils.java
Log:
Make Definer use the new ClasspathUtils, make ClasspathUtils even more
reuse-friendly.
PR: 19213
Submitted by: Marc Portier <mpo at apache dot org>
Revision Changes Path
1.2 +1 -0 ant/src/etc/testcases/core/loaderref/loaderref.xml
Index: loaderref.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/core/loaderref/loaderref.xml,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- loaderref.xml 6 Jan 2003 13:58:31 -0000 1.1
+++ loaderref.xml 23 Apr 2003 15:03:06 -0000 1.2
@@ -22,6 +22,7 @@
<target name="testbadref" depends="compile" >
<taskdef loaderref="loaderref-test"
+ name="test1"
classname="Test1"
classpath="${classes.dir}"/>
</target>
1.29 +23 -93 ant/src/main/org/apache/tools/ant/taskdefs/Definer.java
Index: Definer.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Definer.java,v
retrieving revision 1.28
retrieving revision 1.29
diff -u -r1.28 -r1.29
--- Definer.java 7 Mar 2003 11:23:01 -0000 1.28
+++ Definer.java 23 Apr 2003 15:03:06 -0000 1.29
@@ -66,6 +66,7 @@
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.ClasspathUtils;
/**
* Base class for Taskdef and Typedef - does all the classpath
@@ -79,21 +80,16 @@
public abstract class Definer extends Task {
private String name;
private String value;
- private Path classpath;
private File file;
private String resource;
- private boolean reverseLoader = false;
- private String loaderId = null;
- private String classpathId = null;
-
- private static final String REUSE_LOADER_REF = "ant.reuse.loader";
+ private ClasspathUtils.Delegate cpDelegate;
/**
* @deprecated stop using this attribute
* @ant.attribute ignore="true"
*/
public void setReverseLoader(boolean reverseLoader) {
- this.reverseLoader = reverseLoader;
+ this.cpDelegate.setReverseLoader(reverseLoader);
log("The reverseloader attribute is DEPRECATED. It will be removed",
Project.MSG_WARN);
}
@@ -103,7 +99,7 @@
}
public Path getClasspath() {
- return classpath;
+ return cpDelegate.getClasspath();
}
public File getFile() {
@@ -115,15 +111,15 @@
}
public boolean isReverseLoader() {
- return reverseLoader;
+ return cpDelegate.isReverseLoader();
}
public String getLoaderId() {
- return loaderId;
+ return cpDelegate.getClassLoadId();
}
public String getClasspathId() {
- return classpathId;
+ return cpDelegate.getClassLoadId();
}
/**
@@ -132,21 +128,14 @@
* @param classpath an Ant Path object containing the classpath.
*/
public void setClasspath(Path classpath) {
- if (this.classpath == null) {
- this.classpath = classpath;
- } else {
- this.classpath.append(classpath);
- }
+ this.cpDelegate.setClasspath(classpath);
}
/**
* Create the classpath to be used when searching for component being
defined
*/
public Path createClasspath() {
- if (this.classpath == null) {
- this.classpath = new Path(getProject());
- }
- return this.classpath.createPath();
+ return this.cpDelegate.createClasspath();
}
/**
@@ -154,8 +143,7 @@
* To actually share the same loader, set loaderref as well
*/
public void setClasspathRef(Reference r) {
- classpathId=r.getRefId();
- createClasspath().setRefid(r);
+ this.cpDelegate.setClasspathref(r);
}
/**
@@ -170,7 +158,7 @@
* @since Ant 1.5
*/
public void setLoaderRef(Reference r) {
- loaderId = r.getRefId();
+ this.cpDelegate.setLoaderRef(r);
}
@@ -272,81 +260,12 @@
* create a classloader for this definition
*/
private ClassLoader createLoader() {
- // magic property
- if (getProject().getProperty(REUSE_LOADER_REF) != null) {
- // Generate the 'reuse' name automatically from the reference.
- // This allows <taskdefs> that work on both ant1.4 and ant1.5.
- // ( in 1.4 it'll require the task/type to be in classpath if
they
- // are used togheter ).
- if (loaderId == null && classpathId != null) {
- loaderId = "ant.loader." + classpathId;
- }
- }
-
- // If a loader has been set ( either by loaderRef or magic property )
- if (loaderId != null) {
- Object reusedLoader = getProject().getReference(loaderId);
- if (reusedLoader != null) {
- if (!(reusedLoader instanceof ClassLoader)) {
- throw new BuildException("The specified loader id " +
- loaderId + " does not reference a class loader");
- }
-
- return (ClassLoader)reusedLoader;
- //if (reusedLoader instanceof AntClassLoader) {
- // return (AntClassLoader)reusedLoader;
- //}
- // In future the reference object may be the <loader> type
- // if (reusedLoader instanceof Loader ) {
- // return ((Loader)reusedLoader).getLoader(project);
- // }
- }
- }
-
- ClassLoader al = null;
-
- if (classpath == null) {
- // do we need to create another loader ?
- al=project.getCoreLoader();
- if (al != null ) {
- return al;
- }
- }
-
- if (classpath != null) {
- project.log( "Creating new loader for taskdef using " +
classpath +
- " reverse=" + reverseLoader, Project.MSG_DEBUG );
- AntClassLoader acl = getProject().createClassLoader(classpath);
- if (reverseLoader) {
- acl.setParentFirst(false);
- acl.addJavaLibraries();
- }
- al = acl;
- } else {
- // XXX Probably it would be better to reuse
getClass().getClassLoader()
- // I don't think we need a new ( identical ) loader for each task
- AntClassLoader acl
- = getProject().createClassLoader(Path.systemClasspath);
- if (reverseLoader) {
- acl.setParentFirst(false);
- acl.addJavaLibraries();
- }
- al = acl;
- }
+ ClassLoader al = this.cpDelegate.getClassLoader();
// need to load Task via system classloader or the new
// task we want to define will never be a Task but always
// be wrapped into a TaskAdapter.
((AntClassLoader)al).addSystemPackageRoot("org.apache.tools.ant");
-
- // If the loader is new, record it for future uses by other
- // task/typedefs
- if (loaderId != null) {
- if (getProject().getReference(loaderId) == null) {
- getProject().addReference(loaderId, al);
- }
- }
-
return al;
}
@@ -396,4 +315,15 @@
* they will get to add a new definition of their type.
*/
protected abstract void addDefinition(String name, Class c);
+
+
+ /**
+ * @see org.apache.tools.ant.Task#init()
+ * @since Ant 1.6
+ */
+ public void init() throws BuildException {
+ this.cpDelegate = ClasspathUtils.getDelegate(this);
+ super.init();
+ }
+
}
1.2 +318 -69
ant/src/main/org/apache/tools/ant/util/ClasspathUtils.java
Index: ClasspathUtils.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/util/ClasspathUtils.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ClasspathUtils.java 14 Apr 2003 07:06:55 -0000 1.1
+++ ClasspathUtils.java 23 Apr 2003 15:03:06 -0000 1.2
@@ -56,6 +56,7 @@
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
@@ -63,35 +64,38 @@
* Offers some helper methods on the Path structure in ant.
*
* <p>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</p>
+ * Typically one would have in your Ant Task or DataType</p>
*
* <pre><code>
+ * 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);
* }
* </code></pre>
*
- * <p>when you actually need the classloading you can just:</p>
+ * <p>At execution time, when you actually need the classloading
+ * you can just:</p>
*
* <pre><code>
- * ClassLoader cl = ClasspathUtils.getClassLoaderForPath(this.classpath,
this.classpathId);
- * Object o = ClasspathUtils.newInstance(this.classname, cl);
+ * Object o = this.cpDelegate.newInstance();
* </code></pre>
*
* @since Ant 1.6
@@ -110,12 +114,14 @@
* @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 [EMAIL PROTECTED]
#geClassLoader(Path,
+ * Convenience overloaded version of [EMAIL PROTECTED]
#geClassLoader(Project, Path,
* String, boolean)}.
*
* <p>Delegates to the other one after extracting the referenced
@@ -128,31 +134,54 @@
* 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 [EMAIL PROTECTED]
- * #getClassLoaderForPath(Path, String, boolean)}.
+ * #getClassLoaderForPath(Project, Path, String, boolean)}.
*
* <p>Assumes the logical 'false' for the reverseLoader.</p>
*
* @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 [EMAIL PROTECTED]
+ * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}.
+ *
+ * <p>Sets value for 'reuseLoader' to true if the magic property
+ * has been set.</p>
+ *
+ * @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 @@
* created loader with that id, and of course store it there upon
* creation.</p>
* @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 @@
* @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.
+ *
+ * <p> This uses the userDefinedLoader to load the specified class,
+ * and then makes an instance using the default no-argument constructor
+ * </p>
+ *
+ * @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
+ * for <taskdef> and <typedef> in Ant 1.5 and earlier.
+ */
+ private static boolean isMagicPropertySet(Project p) {
+ return p.getProperty(REUSE_LOADER_REF) != null;
+ }
+
+ /**
+ * Delegate that helps out any specific ProjectComponent that needs
+ * dynamic classloading.
+ *
+ * <p>Ant ProjectComponents that need a to be able to dynamically load
+ * Classes and instantiate them often expose the following ant syntax
+ * sugar: </p>
+ *
+ * <ul><li> nested <classpath> </li>
+ * <li> attribute @classpathref </li>
+ * <li> attribute @classname </li></ul>
+ *
+ * <p> 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. </p>
+ *
+ * @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
+ *
+ * <p>This attribute can set a path to add to the classpath</p>
+ *
+ * @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.
+ *
+ * <p>This nested path-like structure can set a path to add to the
+ * classpath</p>
+ *
+ * @return
+ */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(component.getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+ /**
+ * Delegate method handling the @classname attribute.
+ *
+ * <p>This attribute sets the full qualified class name of the class
+ * to lad and instantiate</p>
+ *
+ * @param fcqn
+ */
+ public void setClassname(String fcqn) {
+ this.className = fcqn;
+ }
+
+ /**
+ * Delegate method handling the @classpathref attribute.
+ *
+ * <p>This attribute can add a referenced path-like structure to the
+ * classpath</p>
+ *
+ * @param r
+ */
+ public void setClasspathref(Reference r) {
+ this.classpathId = r.getRefId();
+ createClasspath().setRefid(r);
+ }
+
+ /**
+ * Delegate method handling the @reverseLoader attribute.
+ *
+ * <p>This attribute can set a boolean indicating that the used
+ * classloader should NOT follow the classical parent-first scheme.
+ * </p>
+ *
+ * <p>By default this is supposed to be false</p>
+ *
+ * <p>Caution: this behaviour is contradictory to the normal way
+ * classloaders work. Do not let your ProjectComponent use it if
+ * you are not really sure</p>
+ *
+ * @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
+ }
}