OK best for last...
This fixes one bug and one lacking capability. Both relate to use of
classloaders and show up when you try to embed Ant in another tool. The
patch permits Ant to function well inside the NetBeans IDE (for
example); without it, there are some workarounds (reimplement <taskdef>
and so on) but they are not complete and it is a mess. Hopefully other
GUIs could benefit as well, I don't know.
1. AntClassLoader. [This patch can actually stand on its own, if the one
reference to project.getDefaultClassLoader() is changed to e.g.
AntClassLoader.class.getClassLoader().] AntClassLoader currently when
trying to resolve "system" classes, uses the primordial classloader. If
Ant itself was *not* loaded with the primordial classloader, this is
unworkable: e.g. a user task may be loadable but Task itself is not
(from within that AntClassLoader) -> NoClassDefFoundError when
resolving. The patch simply makes the loading work logically, wherever
Ant itself was loaded from.
Furthermore, previously AntClassLoader under Java 2 would provide no
permissions for loaded classes. This means that in a VM with an
installed security manager, any task performing a controlled action
(pretty much any task at all: e.g. open a file, ...) would throw
AccessControlException and be useless. The patch (under Java 2) supplies
loaded classes with the same permissions as Ant itself (matching the
commandline behavior), under the assumption that tasks and build scripts
are trusted and there is no sandboxing needed or wanted. Under JDK 1.1,
it should compile (note the reflection calls) but do nothing different;
someone with a 1.1 installation, please double-check!
2. Project.defaultClassLoader and related code. I added a bean property,
defaultClassLoader, set on a per-project basis (should be inherited in
<ant> calls) and defaulting to Ant's own classloader. This classloader
should be used as the default one for any task/chunk of code needing to
load something (probably provided by a user) that has not been
explicitly handed a classpath. In commandline Ant, this should cause no
difference in behavior. When embedded in a GUI that has its own
classloaders, the GUI can ask the project to use its own standard one
instead. For example, in the NetBeans IDE there is a magic classloader
that can load stuff from the user's own project (i.e. Java
classes/resources developed inside the IDE, not the startup classpath).
NetBeans can then pass that classloader to Ant, so that e.g. <taskdef>
with no explicit classpath attribute will ask NetBeans for a
user-created class of that name. Same applies to many other tasks:
<available>, ...
I have tested this patch some under Linux 1.3; at least the standard
testsuite says it is OK. A version of ant.jar with this patch, when
installed into NetBeans via the existing integration minus the existing
hacky workarounds, seems to behave as desired.
Any philosophical/practical objections? BTW #1 is more important than
#2...
Thanks,
-Jesse
--
Jesse Glick <mailto:[EMAIL PROTECTED]>
NetBeans, Open APIs <http://www.netbeans.org/>
tel (+4202) 3300-9161 Sun Micro x49161 Praha CR
Index: src/main/org/apache/tools/ant/AntClassLoader.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/AntClassLoader.java,v
retrieving revision 1.13
diff -u -r1.13 AntClassLoader.java
--- src/main/org/apache/tools/ant/AntClassLoader.java 2001/01/19 13:27:00
1.13
+++ src/main/org/apache/tools/ant/AntClassLoader.java 2001/01/25 11:58:51
@@ -54,6 +54,7 @@
package org.apache.tools.ant;
+import java.lang.reflect.*;
import java.util.*;
import java.util.zip.*;
import java.io.*;
@@ -198,7 +199,7 @@
Class theClass = findLoadedClass(classname);
if (theClass == null) {
- theClass = findSystemClass(classname);
+ theClass = findBaseClass(classname);
}
return theClass;
@@ -325,7 +326,7 @@
if (theClass == null) {
if (useSystemFirst) {
try {
- theClass = findSystemClass(classname);
+ theClass = findBaseClass(classname);
project.log("Class " + classname + " loaded from system
loader", Project.MSG_DEBUG);
}
catch (ClassNotFoundException cnfe) {
@@ -339,7 +340,7 @@
project.log("Class " + classname + " loaded from ant
loader", Project.MSG_DEBUG);
}
catch (ClassNotFoundException cnfe) {
- theClass = findSystemClass(classname);
+ theClass = findBaseClass(classname);
project.log("Class " + classname + " loaded from system
loader", Project.MSG_DEBUG);
}
}
@@ -387,7 +388,43 @@
byte[] classData = baos.toByteArray();
- return defineClass(classname, classData, 0, classData.length);
+ // Simply put:
+ // defineClass(classname, classData, 0, classData.length,
Project.class.getProtectionDomain());
+ // Made more elaborate to be 1.1-safe.
+ if (defineClassProtectionDomain != null) {
+ try {
+ Object domain = getProtectionDomain.invoke(Project.class, new
Object[0]);
+ Object[] args = new Object[] {classname, classData, new
Integer(0), new Integer(classData.length), domain};
+ return (Class)defineClassProtectionDomain.invoke(this, args);
+ }
+ catch (InvocationTargetException ite) {
+ Throwable t = ite.getTargetException();
+ if (t instanceof ClassFormatError) {
+ throw (ClassFormatError)t;
+ }
+ else {
+ throw new IOException(t.toString());
+ }
+ }
+ catch (Exception e) {
+ throw new IOException(e.toString());
+ }
+ }
+ else {
+ return defineClass(classname, classData, 0, classData.length);
+ }
+ }
+
+ private static Method getProtectionDomain = null;
+ private static Method defineClassProtectionDomain = null;
+ static {
+ try {
+ getProtectionDomain = Class.class.getMethod("getProtectionDomain",
new Class[0]);
+ Class protectionDomain =
Class.forName("java.security.ProtectionDomain");
+ Class[] args = new Class[] {String.class, byte[].class,
Integer.TYPE, Integer.TYPE, protectionDomain};
+ defineClassProtectionDomain =
ClassLoader.class.getDeclaredMethod("defineClass", args);
+ }
+ catch (Exception e) {}
}
@@ -445,6 +482,19 @@
}
}
catch (IOException e) {}
+ }
+ }
+
+ /**
+ * Find a system class (which should be loaded from the same classloader
as the Ant core).
+ */
+ private Class findBaseClass(String name) throws ClassNotFoundException {
+ ClassLoader base = project.getDefaultClassLoader();
+ if (base == null) {
+ return findSystemClass(name);
+ }
+ else {
+ return base.loadClass(name);
}
}
}
Index: src/main/org/apache/tools/ant/IntrospectionHelper.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/IntrospectionHelper.java,v
retrieving revision 1.13
diff -u -r1.13 IntrospectionHelper.java
--- src/main/org/apache/tools/ant/IntrospectionHelper.java 2001/01/20
13:41:56 1.13
+++ src/main/org/apache/tools/ant/IntrospectionHelper.java 2001/01/25
11:58:51
@@ -431,7 +431,7 @@
public void set(Project p, Object parent, String value)
throws InvocationTargetException,
IllegalAccessException, BuildException {
try {
- m.invoke(parent, new Class[]
{Class.forName(value)});
+ m.invoke(parent, new Class[] {Class.forName(value,
true, p.getDefaultClassLoader())});
} catch (ClassNotFoundException ce) {
throw new BuildException(ce);
}
Index: src/main/org/apache/tools/ant/Project.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/Project.java,v
retrieving revision 1.52
diff -u -r1.52 Project.java
--- src/main/org/apache/tools/ant/Project.java 2001/01/18 02:50:32 1.52
+++ src/main/org/apache/tools/ant/Project.java 2001/01/25 11:58:51
@@ -107,6 +107,8 @@
private Vector listeners = new Vector();
+ private ClassLoader defaultClassLoader = Project.class.getClassLoader();
+
private static java.lang.reflect.Method setLastModified = null;
private static Object lockReflection = new Object();
@@ -160,7 +162,7 @@
String key = (String) enum.nextElement();
String value = props.getProperty(key);
try {
- Class taskClass = Class.forName(value);
+ Class taskClass = Class.forName(value, true,
defaultClassLoader);
addTaskDefinition(key, taskClass);
} catch (NoClassDefFoundError ncdfe) {
// ignore...
@@ -188,7 +190,7 @@
String key = (String) enum.nextElement();
String value = props.getProperty(key);
try {
- Class dataClass = Class.forName(value);
+ Class dataClass = Class.forName(value, true,
defaultClassLoader);
addDataTypeDefinition(key, dataClass);
} catch (NoClassDefFoundError ncdfe) {
// ignore...
@@ -1026,6 +1028,27 @@
public Hashtable getReferences() {
return references;
+ }
+
+ /**
+ * Get the default classloader to use when finding classes and resources
for this project.
+ * Tasks might call this method to get a classloader to use when the task
configuration
+ * did not specify a particular classpath.
+ * @return the default classloader (might be <code>null</code> in Java 1.1)
+ */
+ public ClassLoader getDefaultClassLoader() {
+ return defaultClassLoader;
+ }
+
+ /**
+ * Set the default classloader to use when finding classes and resources
for this project.
+ * Environments providing an alternate way of running Ant which have their
+ * own standard classloaders might call this method to affect the defaults
+ * for various tasks.
+ * @return the default classloader to use (can be <code>null</code> in
Java 1.1)
+ */
+ public void setDefaultClassLoader(ClassLoader cl) {
+ defaultClassLoader = cl;
}
protected void fireBuildStarted() {
Index: src/main/org/apache/tools/ant/taskdefs/Ant.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Ant.java,v
retrieving revision 1.21
diff -u -r1.21 Ant.java
--- src/main/org/apache/tools/ant/taskdefs/Ant.java 2001/01/03 14:18:29
1.21
+++ src/main/org/apache/tools/ant/taskdefs/Ant.java 2001/01/25 11:58:53
@@ -92,6 +92,7 @@
p1.setJavaVersionProperty();
p1.addTaskDefinition("property",
(Class)project.getTaskDefinitions().get("property"));
+ p1.setDefaultClassLoader(project.getDefaultClassLoader());
}
private void reinit() {
Index: src/main/org/apache/tools/ant/taskdefs/Available.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Available.java,v
retrieving revision 1.16
diff -u -r1.16 Available.java
--- src/main/org/apache/tools/ant/taskdefs/Available.java 2001/01/08
12:52:06 1.16
+++ src/main/org/apache/tools/ant/taskdefs/Available.java 2001/01/25
11:58:53
@@ -144,7 +144,7 @@
if (loader != null) {
return (loader.getResourceAsStream(resource) != null);
} else {
- ClassLoader cL = this.getClass().getClassLoader();
+ ClassLoader cL = project.getDefaultClassLoader();
if (cL != null) {
return (cL.getResourceAsStream(resource) != null);
} else {
@@ -159,14 +159,7 @@
if (loader != null) {
loader.loadClass(classname);
} else {
- ClassLoader l = this.getClass().getClassLoader();
- // Can return null to represent the bootstrap class loader.
- // see API docs of Class.getClassLoader.
- if (l != null) {
- l.loadClass(classname);
- } else {
- Class.forName(classname);
- }
+ Class.forName(classname, true,
project.getDefaultClassLoader());
}
return true;
} catch (ClassNotFoundException e) {
Index: src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java,v
retrieving revision 1.7
diff -u -r1.7 ExecuteJava.java
--- src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java 2001/01/03
14:18:30 1.7
+++ src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java 2001/01/25
11:58:53
@@ -114,10 +114,10 @@
System.setOut(out);
}
- final Class[] param = { Class.forName("[Ljava.lang.String;") };
+ final Class[] param = { String[].class };
Class target = null;
if (classpath == null) {
- target = Class.forName(classname);
+ target = Class.forName(classname, true,
project.getDefaultClassLoader());
} else {
AntClassLoader loader = new AntClassLoader(project, classpath);
target = loader.forceLoadClass(classname);
Index: src/main/org/apache/tools/ant/taskdefs/Property.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Property.java,v
retrieving revision 1.24
diff -u -r1.24 Property.java
--- src/main/org/apache/tools/ant/taskdefs/Property.java 2001/01/08
12:42:47 1.24
+++ src/main/org/apache/tools/ant/taskdefs/Property.java 2001/01/25
11:58:54
@@ -216,7 +216,7 @@
if (classpath != null) {
cL = new AntClassLoader(project, classpath, false);
} else {
- cL = this.getClass().getClassLoader();
+ cL = project.getDefaultClassLoader();
}
if (cL == null) {
Index: src/main/org/apache/tools/ant/taskdefs/SQLExec.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/SQLExec.java,v
retrieving revision 1.15
diff -u -r1.15 SQLExec.java
--- src/main/org/apache/tools/ant/taskdefs/SQLExec.java 2001/01/21 00:41:25
1.15
+++ src/main/org/apache/tools/ant/taskdefs/SQLExec.java 2001/01/25 11:58:54
@@ -333,7 +333,7 @@
}
else {
log("Loading " + driver + " using system loader.",
Project.MSG_VERBOSE);
- dc = Class.forName(driver);
+ dc = Class.forName(driver, true,
project.getDefaultClassLoader());
}
driverInstance = (Driver) dc.newInstance();
}catch(ClassNotFoundException e){
Index: src/main/org/apache/tools/ant/taskdefs/Taskdef.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Taskdef.java,v
retrieving revision 1.13
diff -u -r1.13 Taskdef.java
--- src/main/org/apache/tools/ant/taskdefs/Taskdef.java 2001/01/03 14:18:31
1.13
+++ src/main/org/apache/tools/ant/taskdefs/Taskdef.java 2001/01/25 11:58:54
@@ -103,15 +103,10 @@
al.addSystemPackageRoot("org.apache.tools.ant");
loader = al;
} else {
- loader = this.getClass().getClassLoader();
+ loader = project.getDefaultClassLoader();
}
- Class taskClass = null;
- if (loader != null) {
- taskClass = loader.loadClass(value);
- } else {
- taskClass = Class.forName(value);
- }
+ Class taskClass = Class.forName(value, true, loader);
project.addTaskDefinition(name, taskClass);
} catch (ClassNotFoundException cnfe) {
String msg = "taskdef class " + value +
Index: src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java,v
retrieving revision 1.13
diff -u -r1.13 XSLTProcess.java
--- src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java 2001/01/11
09:12:44 1.13
+++ src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java 2001/01/25
11:58:54
@@ -238,7 +238,7 @@
Class.forName("org.apache.tools.ant.taskdefs.optional.XalanLiaison");
liaison = (XSLTLiaison)clazz.newInstance();
} else {
- liaison = (XSLTLiaison) Class.forName(processor).newInstance();
+ liaison = (XSLTLiaison) Class.forName(processor, true,
project.getDefaultClassLoader()).newInstance();
}
}
Index:
src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java,v
retrieving revision 1.1
diff -u -r1.1 CompilerAdapterFactory.java
---
src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java
2001/01/12 14:08:51 1.1
+++
src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java
2001/01/25 11:58:55
@@ -119,7 +119,7 @@
compilerType.equalsIgnoreCase("microsoft")) {
return new Jvc();
}
- return resolveClassName( compilerType );
+ return resolveClassName( compilerType,
task.getProject().getDefaultClassLoader() );
}
/**
@@ -127,13 +127,14 @@
* Throws a fit if it can't.
*
* @param className The fully qualified classname to be created.
+ * @param loader a loader to create the adapter with
* @throws BuildException This is the fit that is thrown if className
* isn't an instance of CompilerAdapter.
*/
- private static CompilerAdapter resolveClassName( String className )
+ private static CompilerAdapter resolveClassName( String className,
ClassLoader loader )
throws BuildException {
try {
- Class c = Class.forName( className );
+ Class c = Class.forName( className, true, loader );
Object o = c.newInstance();
return (CompilerAdapter) o;
} catch ( ClassNotFoundException cnfe ) {
Index:
src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java,v
retrieving revision 1.12
diff -u -r1.12 GenericDeploymentTool.java
---
src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
2001/01/20 12:41:51 1.12
+++
src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
2001/01/25 11:58:59
@@ -652,7 +652,7 @@
// only generate a URLClassLoader if we have a classpath
if (classpath == null) {
- classpathLoader = getClass().getClassLoader();
+ classpathLoader = getTask().getProject().getDefaultClassLoader();
}
else {
classpathLoader = new AntClassLoader(getTask().getProject(),
classpath);
Index: src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java,v
retrieving revision 1.14
diff -u -r1.14 JUnitTask.java
--- src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
2000/12/18 15:40:37 1.14
+++ src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
2001/01/25 11:59:00
@@ -237,7 +237,7 @@
test.getHaltonfailure(), l);
} else {
runner = new JUnitTestRunner(test, test.getHaltonerror(),
- test.getHaltonfailure());
+ test.getHaltonfailure(),
project.getDefaultClassLoader());
}
if (summary) {
Index: src/main/org/apache/tools/ant/types/Mapper.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/types/Mapper.java,v
retrieving revision 1.3
diff -u -r1.3 Mapper.java
--- src/main/org/apache/tools/ant/types/Mapper.java 2000/11/30 14:20:19
1.3
+++ src/main/org/apache/tools/ant/types/Mapper.java 2001/01/25 11:59:01
@@ -199,7 +199,7 @@
Class c = null;
if (classpath == null) {
- c = Class.forName(classname);
+ c = Class.forName(classname, true, p.getDefaultClassLoader());
} else {
AntClassLoader al = new AntClassLoader(p, classpath);
c = al.loadClass(classname);