|
|
@@ -56,6 +56,7 @@ package org.apache.tools.ant.util; |
|
|
|
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 @@ import org.apache.tools.ant.types.Reference; |
|
|
|
* 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 @@ 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)}. |
|
|
|
* |
|
|
|
* <p>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)}. |
|
|
|
* |
|
|
|
* <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 {@link |
|
|
|
* #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 @@ public class ClasspathUtils { |
|
|
|
* 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 @@ 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. |
|
|
|
* |
|
|
|
* <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 |
|
|
|
} |
|
|
|
} |