You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by Jesse Glick <Je...@netbeans.com> on 2001/01/25 13:25:17 UTC

[PATCH] classloader jumbo patch

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   <ma...@netbeans.com>
NetBeans, Open APIs  <http://www.netbeans.org/>
tel (+4202) 3300-9161 Sun Micro x49161 Praha CR

Re: [PATCH] classloader jumbo patch

Posted by Conor MacNeill <co...@cortexebusiness.com.au>.
Jesse,

I've seen it. I wanted to get the build process sorted out for Ant 1.3
before I went back into pick up the latest patches. It is on the list

Conor

----- Original Message -----
From: "Jesse Glick" <Je...@netbeans.com>
To: <an...@jakarta.apache.org>
Sent: Thursday, February 01, 2001 12:20 AM
Subject: Re: [PATCH] classloader jumbo patch


> No reply to this one yet, so gently asking again whether this will make
> it into 1.3, or whether I will need to ship a patched ant.jar with
> NetBeans...doable but not desirable.
>
> #1 is the important part, since without this nothing works inside the
> IDE. So I am reattaching it, this time as a standalone patch without #2.
> Original description below for reference.
>
> For #2, it may suffice for NetBeans to automatically define some
> additional properties, like:
>
> -Dnetbeans.filesystems.path=/my/sources/part1:/my/sources/part2
>
> and so on (taken from GUI project configuration). Then
>
> <taskdef name="foo" classname="MyCustomTask"/>
>
> will still not work, but this more explicit version will:
>
> <taskdef name="foo" classname="MyCustomTask">
>   <classpath>
>     <pathelement path="${netbeans.filesystems.path}"/>
>   </classpath>
> </taskdef>
>
> I tried this and it seems OK, provided AntClassLoader is patched.
>
> Thanks,
> -Jesse
>
> Jesse Glick wrote:
> > 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!
>
> --
> Jesse Glick   <ma...@netbeans.com>
> 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.ja
va,v
> retrieving revision 1.13
> diff -c -t -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/31 12:34:27
> ***************
> *** 54,59 ****
> --- 54,60 ----
>
>   package org.apache.tools.ant;
>
> + import java.lang.reflect.*;
>   import java.util.*;
>   import java.util.zip.*;
>   import java.io.*;
> ***************
> *** 198,204 ****
>           Class theClass = findLoadedClass(classname);
>
>           if (theClass == null) {
> !             theClass = findSystemClass(classname);
>           }
>
>           return theClass;
> --- 199,205 ----
>           Class theClass = findLoadedClass(classname);
>
>           if (theClass == null) {
> !             theClass = findBaseClass(classname);
>           }
>
>           return theClass;
> ***************
> *** 325,331 ****
>           if (theClass == null) {
>               if (useSystemFirst) {
>                   try {
> !                     theClass = findSystemClass(classname);
>                       project.log("Class " + classname + " loaded from
system loader", Project.MSG_DEBUG);
>                   }
>                   catch (ClassNotFoundException cnfe) {
> --- 326,332 ----
>           if (theClass == null) {
>               if (useSystemFirst) {
>                   try {
> !                     theClass = findBaseClass(classname);
>                       project.log("Class " + classname + " loaded from
system loader", Project.MSG_DEBUG);
>                   }
>                   catch (ClassNotFoundException cnfe) {
> ***************
> *** 339,345 ****
>                       project.log("Class " + classname + " loaded from
ant loader", Project.MSG_DEBUG);
>                   }
>                   catch (ClassNotFoundException cnfe) {
> !                     theClass = findSystemClass(classname);
>                       project.log("Class " + classname + " loaded from
system loader", Project.MSG_DEBUG);
>                   }
>               }
> --- 340,346 ----
>                       project.log("Class " + classname + " loaded from
ant loader", Project.MSG_DEBUG);
>                   }
>                   catch (ClassNotFoundException cnfe) {
> !                     theClass = findBaseClass(classname);
>                       project.log("Class " + classname + " loaded from
system loader", Project.MSG_DEBUG);
>                   }
>               }
> ***************
> *** 387,393 ****
>
>           byte[] classData = baos.toByteArray();
>
> !         return defineClass(classname, classData, 0, classData.length);
>       }
>
>
> --- 388,430 ----
>
>           byte[] classData = baos.toByteArray();
>
> !         // 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,450 ****
> --- 482,500 ----
>                   }
>               }
>               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 = AntClassLoader.class.getClassLoader();
> +         if (base == null) {
> +             return findSystemClass(name);
> +         }
> +         else {
> +             return base.loadClass(name);
>           }
>       }
>   }
>
>


---------------------------------------------------------------------------
-----


> ---------------------------------------------------------------------
> To unsubscribe, e-mail: ant-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: ant-dev-help@jakarta.apache.org


Re: [PATCH] classloader jumbo patch

Posted by Stefan Bodewig <bo...@apache.org>.
Jesse Glick <Je...@netbeans.com> wrote:

> No reply to this one yet,

Yes, sorry. Too many things that need to be looked at ATM.

I've seen your patches - but focussed on the smaller ones first. As
Conor has done the initial AntClassLoader stuff, I'd be happy if he
would take care of this.

Stefan

Re: [PATCH] classloader jumbo patch

Posted by Jesse Glick <Je...@netbeans.com>.
No reply to this one yet, so gently asking again whether this will make
it into 1.3, or whether I will need to ship a patched ant.jar with
NetBeans...doable but not desirable.

#1 is the important part, since without this nothing works inside the
IDE. So I am reattaching it, this time as a standalone patch without #2.
Original description below for reference.

For #2, it may suffice for NetBeans to automatically define some
additional properties, like:

	-Dnetbeans.filesystems.path=/my/sources/part1:/my/sources/part2

and so on (taken from GUI project configuration). Then

	<taskdef name="foo" classname="MyCustomTask"/>

will still not work, but this more explicit version will:

	<taskdef name="foo" classname="MyCustomTask">
	  <classpath>
	    <pathelement path="${netbeans.filesystems.path}"/>
	  </classpath>
	</taskdef>

I tried this and it seems OK, provided AntClassLoader is patched.

Thanks,
-Jesse

Jesse Glick wrote:
> 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!

-- 
Jesse Glick   <ma...@netbeans.com>
NetBeans, Open APIs  <http://www.netbeans.org/>
tel (+4202) 3300-9161 Sun Micro x49161 Praha CR

Re: [PATCH] classloader jumbo patch

Posted by Jesse Glick <Je...@netbeans.com>.
Conor MacNeill wrote:
> I have applied your first item but not the second. Applying the second
> wiuld have broken JDK 1.1 compatibility due to the extensive use of
> 
> Class.forName(value, true, defaultClassLoader);

Fair enough and maybe just as well; people who explicitly want to use
the container's default classpath can use some container-provided path
properties. E.g. the NetBeans integration will support <pathelement
path="${netbeans.filesystems.path}"/>. I think this is sufficient.

Thanks,
-Jesse

-- 
Jesse Glick   <ma...@netbeans.com>
NetBeans, Open APIs  <http://www.netbeans.org/>
tel (+4202) 3300-9161 Sun Micro x49161 Praha CR

Re: [PATCH] classloader jumbo patch

Posted by Conor MacNeill <co...@cortexebusiness.com.au>.
Jesse,

I have applied your first item but not the second. Applying the second
wiuld have broken JDK 1.1 compatibility due to the extensive use of

Class.forName(value, true, defaultClassLoader);

Conor