You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@ant.apache.org by Jochen Theodorou <bl...@gmx.org> on 2005/08/30 00:38:25 UTC

custom classloader for a task

Hi all,

Te problem I have is a little complex but I hope you can help me. Groovy 
has an ant task to compile groovy classes and a task to use groovy from 
within ant see http://groovy.codehaus.org/Groovy+Ant+Task for details. 
But in some enviroments such as in maven with certain plugins we have 
conflicting jars. I mean jars of a different version than needed by 
groovy. For example antlr or asm.

Our current workaround for the compile task (groovyc) is to fork the VM. 
But this can't be the solution? I mean isn't there a possibility to load 
a task through a custom classloader? It's no problem for me to write 
such a loader, but where to wire it in? I know about the loaderref 
attribute, but as far as I understand this attribute is for reusing a 
classloader. A normal classloader can't be used since a normal 
classloader looks for a class first in the parent and if the parent 
knows the conflicting jar/class then we have the same problem as before.

I heard that when you do loaderref="root" in a maven project you get the 
ant-loader, but that will be no help if the normal classpath contains a 
conflicting jar for another task. Maybe someone can explain me if a 
classpath from a taskdef is added to the loader reffered by loaderref. 
If so the ant-loader will be polluted too.

I am looking for a really easy to use soultion, don't know if it exists. 
But maybe you can give some hints as I am really no ant/maven expert.

If there are significant changes in ant in this area since 1.6.2 
pleasetell me too. If you ahve an idea how to solve that problem in a 
completly different way, I would like to hear them.

btw: of course I know this is no maven list ;)

bye blackdrag

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


Re: custom classloader for a task

Posted by Jochen Theodorou <bl...@gmx.org>.
Dominique Devienne schrieb:
> Well, I think you summed it up pretty well. Forking is IMHO a good
> solution to avoid these conflicting jars, since you fully control the
> classpath of the forked VM. I guess you could create your own
> classload, and make the root classloader (the one which loads rt.jar)
> its parent, to bypass the user/app classpath, but then you're kind of
> stuck because you most likely need to add ant.jar into your custom CL,
> and you reload the Ant classes from a different CL than the one Ant
> itself uses, which make the class be in fact different (even though
> its the same bytecode).

of course I know that ;) But how do I create a task using a custom 
classloader? Or did you talk about the case when forking?

> Ideally, when one starts Ant, only ant.jar would be on the classpath,
> and all other jars would be loaded off a custom class loader that
> 'extends' the app CL. That would allow to use different tasks with
> conflicting dependencies by making them live in different CLs.

ideally, yes.

> Maybe the -noclasspath switch would be useful too? Would force you to
> define explicit <classloader>s in build.xml for all non-core tasks,
> but could help? I've never tried such a thing.

do you have an example for this?

> Of course, the fact that you're having these problems in the context
> of Maven makes it even more complex with the tremendous dependencies
> Maven pulls in usually.

oh yes

> I'm afraid I haven't been very useful :-(  --DD

better than nothing ;)

bye blackdrag

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


Re: custom classloader for a task

Posted by Dominique Devienne <dd...@gmail.com>.
Well, I think you summed it up pretty well. Forking is IMHO a good
solution to avoid these conflicting jars, since you fully control the
classpath of the forked VM. I guess you could create your own
classload, and make the root classloader (the one which loads rt.jar)
its parent, to bypass the user/app classpath, but then you're kind of
stuck because you most likely need to add ant.jar into your custom CL,
and you reload the Ant classes from a different CL than the one Ant
itself uses, which make the class be in fact different (even though
its the same bytecode).

Ideally, when one starts Ant, only ant.jar would be on the classpath,
and all other jars would be loaded off a custom class loader that
'extends' the app CL. That would allow to use different tasks with
conflicting dependencies by making them live in different CLs.

Maybe the -noclasspath switch would be useful too? Would force you to
define explicit <classloader>s in build.xml for all non-core tasks,
but could help? I've never tried such a thing.

Of course, the fact that you're having these problems in the context
of Maven makes it even more complex with the tremendous dependencies
Maven pulls in usually.

I'm afraid I haven't been very useful :-(  --DD

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


Re: custom classloader for a task

Posted by Jochen Theodorou <bl...@gmx.org>.
first, thx for all your help from everyone. To solve my problem I wrote 
a task that will store a laoder reference for the usage by my taskdef. 
That classloader does try to load all classes by himself instead 
delegating these classes to the parent as a normal classloader would do. 
To avoid duplicate definitions of ant classes, these classes are 
excluded. This works nicely here. Of course this is not the most clean 
solution, but better than nothing.

see
http://cvs.groovy.codehaus.org/viewrep/groovy/groovy/groovy-core/src/main/org/codehaus/groovy/tools/RootLoader.java
http://cvs.groovy.codehaus.org/viewrep/groovy/groovy/groovy-core/src/main/org/codehaus/groovy/ant/RootLoaderRef.java

for details

bye blackdrag


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


RE: custom classloader for a task

Posted by Rainer Noack <ra...@noacks.net>.
Hi Jochen,

unfortunately, the displayed delegation hierarchy is typically not correct.
oata.AntClassLoader allways returns the System Classloader (class
sun.misc.Launcher$AppClassLoader) as its
parent.
See http://issues.apache.org/bugzilla/show_bug.cgi?id=35436
 
If you run ant from it's shell script you will have typically the following
delegation hierarchy:
* class org.apache.tools.ant.AntClassLoader (loads a task explicitely
defined by taskdef, contains the jars/dirs defined in taskdefs classpath
argument)
* class java.net.URLClassLoader   (created by Launcher, Classloader of
oata.Project, default tasks and most of the other ant stuff; typically
contains your classpath, the jars in $ANT_HOME/lib and the jdk's tools.jar)
* class sun.misc.Launcher$AppClassLoader (known as System-, Application- or
User- Classloader, that only loads the oata.launch.* classes, normally only
contains ant-launcher.jar)
* class sun.misc.Launcher$ExtClassLoader (Extension classloader, typically
contains the jars in $JRE_HOME/lib/ext)
* null (bootstrap classloader for all the jdk stuff, typically contains the
jars in $JRE_HOME/lib resp. $JDK_HOME/lib)

To find out the delegation hierarchy in maven you have to change your code
to something like this (not clean but should work, assumed your task extends
oata.Task):

public void execute() throws BuildException {
    ClassLoader loader = this.getClass().getClassLoader();
    if (loader instanceof AntClassLoader) {
        System.out.println(loader.getClass());
        loader = getProject().getCoreloader();
        if (loader == null)
            loader = Project.class.getClassLoader();
    }
    while (loader!=null) {
        System.out.println(loader.getClass());
        loader = loader.getParent();
    }
}

In the next weeks, I will publish a new patch (and a ready-to-use trial
version) for the proposed <classloader> and <classloaderreport> task (see
http://issues.apache.org/bugzilla/show_bug.cgi?id=28228) that gives a more
common 
solution for this problem.

Once knowing the environment's delegation hierarchy you have to decide
whether it is really possible to create a consistent second delegation
hierarchy for the conflicting jars or not. (This could be done by the
proposed <classloader> task).

However, if you want to run your task in very different environments (in
terms of classloading), I think forking is the cleanest solution. (BTW,
forking of compile tasks is IMHO mostly allways the best solution).

Cheers

Rainer

> -----Original Message-----
> From: Jochen Theodorou [mailto:blackdrag@gmx.org] 
> Sent: Tuesday, August 30, 2005 10:49 AM
> To: Ant Users List
> Subject: Re: custom classloader for a task
> 
> 
> Conor MacNeill schrieb:
> 
> > 
> > Jochen Theodorou wrote:
> > 
> >>Hi all,
> >>
> >>Te problem I have is a little complex but I hope you can help me. 
> >>Groovy has an ant task to compile groovy classes and a task to use 
> >>groovy from within ant see 
> http://groovy.codehaus.org/Groovy+Ant+Task 
> >>for details. But in some enviroments such as in maven with certain 
> >>plugins we have conflicting jars. I mean jars of a 
> different version 
> >>than needed by groovy. For example antlr or asm.
> >>
> >>Our current workaround for the compile task (groovyc) is to 
> fork the 
> >>VM. But this can't be the solution? I mean isn't there a 
> possibility 
> >>to load a task through a custom classloader? It's no 
> problem for me to 
> >>write such a loader, but where to wire it in? I know about the 
> >>loaderref attribute, but as far as I understand this 
> attribute is for 
> >>reusing a classloader. A normal classloader can't be used since a 
> >>normal classloader looks for a class first in the parent and if the 
> >>parent knows the conflicting jar/class then we have the 
> same problem 
> >>as before.
> > 
> > Since you are passing a classpath to the taskdef above, Ant will 
> > create a classloader to load this task's classes. What classes this 
> > classloader can see will depend on the classloader hierarchy under 
> > Maven. I have no idea what that will be.
> 
> classloader hirarchy for the task class without loaderref:
> 
> class org.apache.tools.ant.AntClassLoader
> class sun.misc.Launcher$AppClassLoader
> class sun.misc.Launcher$ExtClassLoader
> 
> printed by a task:
> 
>      public void execute() throws BuildException {
>          ClassLoader loader = this.getClass().getClassLoader();
>          while (loader!=null) {
>              System.out.println(loader.getClass());
>              loader = loader.getParent();
>          }
>      }
> 
> so it seems maven does not change the hirarchy...
> 
> > It is possible to specify a reverseloader="true" attribute 
> on an Ant 
> > taskdef. It is highly deprecated, unsupported, bad things 
> happen, etc. 
> > It will cause the classloader to consult it's jars first, 
> before those 
> > of its parent.
> 
> well, reverseloader=true might be the thing I am searching for, and a 
> test shows it works then... But these deprecated warnings are 
> annoying. 
> No way to work around this problem?
> 
> >>I heard that when you do loaderref="root" in a maven 
> project you get 
> >>the ant-loader, but that will be no help if the normal classpath 
> >>contains a conflicting jar for another task. Maybe someone 
> can explain 
> >>me if a classpath from a taskdef is added to the loader reffered by 
> >>loaderref. If so the ant-loader will be polluted too.
> > 
> > No, this does not happen - loaderref and classpath are exclusive, I 
> > think.
> 
> my tests are showing that with a loaderref I hae a different 
> AntClassLoader, than the normal Classloader, but I have still the 
> conflicting jar. When I enable reverseloader, then it's ok. 
> So the new 
> Loader has to share some classpath parts with the normal antloader 
> somewhere, because I am sure the sun.misc.Launcher does not caontain 
> that jar.
> 
> by blackdrag
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: user-unsubscribe@ant.apache.org
> For additional commands, e-mail: user-help@ant.apache.org
> 


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


Re: custom classloader for a task

Posted by Jochen Theodorou <bl...@gmx.org>.
Conor MacNeill schrieb:

> 
> Jochen Theodorou wrote:
> 
>>Hi all,
>>
>>Te problem I have is a little complex but I hope you can help me. Groovy
>>has an ant task to compile groovy classes and a task to use groovy from
>>within ant see http://groovy.codehaus.org/Groovy+Ant+Task for details.
>>But in some enviroments such as in maven with certain plugins we have
>>conflicting jars. I mean jars of a different version than needed by
>>groovy. For example antlr or asm.
>>
>>Our current workaround for the compile task (groovyc) is to fork the VM.
>>But this can't be the solution? I mean isn't there a possibility to load
>>a task through a custom classloader? It's no problem for me to write
>>such a loader, but where to wire it in? I know about the loaderref
>>attribute, but as far as I understand this attribute is for reusing a
>>classloader. A normal classloader can't be used since a normal
>>classloader looks for a class first in the parent and if the parent
>>knows the conflicting jar/class then we have the same problem as before.
> 
> Since you are passing a classpath to the taskdef above, Ant will create
> a classloader to load this task's classes. What classes this classloader
> can see will depend on the classloader hierarchy under Maven. I have no
> idea what that will be.

classloader hirarchy for the task class without loaderref:

class org.apache.tools.ant.AntClassLoader
class sun.misc.Launcher$AppClassLoader
class sun.misc.Launcher$ExtClassLoader

printed by a task:

     public void execute() throws BuildException {
         ClassLoader loader = this.getClass().getClassLoader();
         while (loader!=null) {
             System.out.println(loader.getClass());
             loader = loader.getParent();
         }
     }

so it seems maven does not change the hirarchy...

> It is possible to specify a reverseloader="true" attribute on an Ant
> taskdef. It is highly deprecated, unsupported, bad things happen, etc.
> It will cause the classloader to consult it's jars first, before those
> of its parent.

well, reverseloader=true might be the thing I am searching for, and a 
test shows it works then... But these deprecated warnings are annoying. 
No way to work around this problem?

>>I heard that when you do loaderref="root" in a maven project you get the
>>ant-loader, but that will be no help if the normal classpath contains a
>>conflicting jar for another task. Maybe someone can explain me if a
>>classpath from a taskdef is added to the loader reffered by loaderref.
>>If so the ant-loader will be polluted too.
> 
> No, this does not happen - loaderref and classpath are exclusive, I think.

my tests are showing that with a loaderref I hae a different 
AntClassLoader, than the normal Classloader, but I have still the 
conflicting jar. When I enable reverseloader, then it's ok. So the new 
Loader has to share some classpath parts with the normal antloader 
somewhere, because I am sure the sun.misc.Launcher does not caontain 
that jar.

by blackdrag

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


Re: custom classloader for a task

Posted by Conor MacNeill <co...@apache.org>.

Jochen Theodorou wrote:
> Hi all,
> 
> Te problem I have is a little complex but I hope you can help me. Groovy
> has an ant task to compile groovy classes and a task to use groovy from
> within ant see http://groovy.codehaus.org/Groovy+Ant+Task for details.
> But in some enviroments such as in maven with certain plugins we have
> conflicting jars. I mean jars of a different version than needed by
> groovy. For example antlr or asm.
> 
> Our current workaround for the compile task (groovyc) is to fork the VM.
> But this can't be the solution? I mean isn't there a possibility to load
> a task through a custom classloader? It's no problem for me to write
> such a loader, but where to wire it in? I know about the loaderref
> attribute, but as far as I understand this attribute is for reusing a
> classloader. A normal classloader can't be used since a normal
> classloader looks for a class first in the parent and if the parent
> knows the conflicting jar/class then we have the same problem as before.

Since you are passing a classpath to the taskdef above, Ant will create
a classloader to load this task's classes. What classes this classloader
can see will depend on the classloader hierarchy under Maven. I have no
idea what that will be.

It is possible to specify a reverseloader="true" attribute on an Ant
taskdef. It is highly deprecated, unsupported, bad things happen, etc.
It will cause the classloader to consult it's jars first, before those
of its parent.


> 
> I heard that when you do loaderref="root" in a maven project you get the
> ant-loader, but that will be no help if the normal classpath contains a
> conflicting jar for another task. Maybe someone can explain me if a
> classpath from a taskdef is added to the loader reffered by loaderref.
> If so the ant-loader will be polluted too.
> 

No, this does not happen - loaderref and classpath are exclusive, I think.

> I am looking for a really easy to use soultion, don't know if it exists.
> But maybe you can give some hints as I am really no ant/maven expert.
> 
> If there are significant changes in ant in this area since 1.6.2
> pleasetell me too. If you ahve an idea how to solve that problem in a
> completly different way, I would like to hear them.
> 
> btw: of course I know this is no maven list ;)
> 

I presume you've asked the Maven guys about this. I'm not sure what
surrounding classloader structure is in place when Ant tasks run in Maven.

Good Luck.
Conor

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