Grokbase Groups Ant user January 2011
FAQ

[Ant-user] Ant + Spring = classpath purgatory

Mitch Gitman
Jan 29, 2011 at 7:34 am
I have a Spring-based application that I'm exposing through an Ant task,
among other interfaces.

The moment where I run into trouble is when the application tries to use
Spring's ClassLoader-based code to load a Spring application context:
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext(
"/com/foo/ApplicationContext.xml");

I see an error that the resource com/foo/ApplicationContext.xml cannot be
found on the classpath. The XML file, together with the application code,
happen to be in the same JAR as the task itself.

Now, I came across this very, very old post on the ant-dev list that gave me
the makings of a solution:
http://www.mail-archive.com/dev@ant.apache.org/msg07704.html

So at the start of my task, I would do this:
AntClassLoader taskloader = (AntClassLoader)
this.getClass().getClassLoader();
taskloader.setThreadContextLoader();

And at the close of my task, I would do this:
taskloader.resetThreadContextLoader();

This works actually.*

I'm leery though of having to resort to this fix. Does anyone know of a
"better" way to get Spring to use the right ClassLoader or just in general
to fool task-invoked application code that might play fast and loose with
classloaders? I haven't delved into the Spring source myself. What's odd is
that this problem doesn't arise in other classloading containers in which a
Spring-based application might be running. For example, a web container
would be loading a web application with a child classloader, and yet that
works fine.

Thanks.

* Rather, this almost works. Almost in that Spring tries to configure its
logging through log4j, but it can't find the class
org.apache.log4j.Category. I've verified that the log4j JAR is in my
classpath. The quick fix is to stick the log4j JAR in the Ant lib directory.
Obviously, that is not a sustainable solution. I'm still trying to get to
the bottom of this and might have to write a follow-up message.
reply

Search Discussions

3 responses

  • Mitch Gitman at Jan 29, 2011 at 8:27 am
    Here's another old post that corroborates the one I linked to:
    http://www.mail-archive.com/user@ant.apache.org/msg22871.html

    This poster isolated the problem with the base class of
    ClassPathXmlApplicationContext,
    org.springframework.core.io.DefaultResourceLoader. And I notice that
    DefaultResourceLoader does have a setClassLoader method:
    http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/core/io/DefaultResourceLoader.html#setClassLoader%28java.lang.ClassLoader%29

    This has the makings of a possibly more elegant solution. So I get:
    AntClassLoader taskloader = (AntClassLoader)
    this.getClass().getClassLoader();

    And then I pass this this AntClassLoader to
    DefaultResourceLoader.setClassLoader. I'll give this a shot. Might solve my
    log4j problem in the process.

    I'm still curious why this classloader isn't used in the first place and
    what the default one, as obtained by LoaderUtils.getContextClassLoader(),
    represents. This goes back to my observation that this sort of problem
    doesn't arise elsewhere.

    Note. I'm using Ant 1.7.1.
    On Fri, Jan 28, 2011 at 11:33 PM, Mitch Gitman wrote:

    I have a Spring-based application that I'm exposing through an Ant task,
    among other interfaces.

    The moment where I run into trouble is when the application tries to use
    Spring's ClassLoader-based code to load a Spring application context:
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(
    "/com/foo/ApplicationContext.xml");

    I see an error that the resource com/foo/ApplicationContext.xml cannot be
    found on the classpath. The XML file, together with the application code,
    happen to be in the same JAR as the task itself.

    Now, I came across this very, very old post on the ant-dev list that gave
    me the makings of a solution:
    http://www.mail-archive.com/dev@ant.apache.org/msg07704.html

    So at the start of my task, I would do this:
    AntClassLoader taskloader = (AntClassLoader)
    this.getClass().getClassLoader();
    taskloader.setThreadContextLoader();

    And at the close of my task, I would do this:
    taskloader.resetThreadContextLoader();

    This works actually.*

    I'm leery though of having to resort to this fix. Does anyone know of a
    "better" way to get Spring to use the right ClassLoader or just in general
    to fool task-invoked application code that might play fast and loose with
    classloaders? I haven't delved into the Spring source myself. What's odd is
    that this problem doesn't arise in other classloading containers in which a
    Spring-based application might be running. For example, a web container
    would be loading a web application with a child classloader, and yet that
    works fine.

    Thanks.

    * Rather, this almost works. Almost in that Spring tries to configure its
    logging through log4j, but it can't find the class
    org.apache.log4j.Category. I've verified that the log4j JAR is in my
    classpath. The quick fix is to stick the log4j JAR in the Ant lib directory.
    Obviously, that is not a sustainable solution. I'm still trying to get to
    the bottom of this and might have to write a follow-up message.
  • Mitch Gitman at Jan 29, 2011 at 6:27 pm
    Here's the solution that finally worked for me, and that includes locating
    the log4j class. This is all in my Ant task:

    ClassPathXmlApplicationContext applicationContext = new
    ClassPathXmlApplicationContext();
    applicationContext.setClassLoader(this.getClass().getClassLoader());
    applicationContext
    .setConfigLocation("com/foo/ApplicationContext.xml");
    applicationContext.refresh();

    I'm going to clean this up to make the application layer initialize its own
    ApplicationContext internally. The caller/consumer of the application will
    just be responsible for passing a ClassLoader to the application layer. To
    my mind, having to supply a ClassLoader is the least evil--the smallest
    concession to the need for this application to be run by an Ant task as
    opposed to some other execution environment.

    Still, I'm curious why an executing task uses the ClassLoader obtained by
    LoaderUtils.getContextClassLoader() rather than the task's own
    AntClassLoader.
    On Sat, Jan 29, 2011 at 12:26 AM, Mitch Gitman wrote:

    Here's another old post that corroborates the one I linked to:
    http://www.mail-archive.com/user@ant.apache.org/msg22871.html

    This poster isolated the problem with the base class of
    ClassPathXmlApplicationContext,
    org.springframework.core.io.DefaultResourceLoader. And I notice that
    DefaultResourceLoader does have a setClassLoader method:

    http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/core/io/DefaultResourceLoader.html#setClassLoader%28java.lang.ClassLoader%29

    This has the makings of a possibly more elegant solution. So I get:

    AntClassLoader taskloader = (AntClassLoader)
    this.getClass().getClassLoader();

    And then I pass this this AntClassLoader to
    DefaultResourceLoader.setClassLoader. I'll give this a shot. Might solve my
    log4j problem in the process.

    I'm still curious why this classloader isn't used in the first place and
    what the default one, as obtained by LoaderUtils.getContextClassLoader(),
    represents. This goes back to my observation that this sort of problem
    doesn't arise elsewhere.

    Note. I'm using Ant 1.7.1.

    On Fri, Jan 28, 2011 at 11:33 PM, Mitch Gitman wrote:

    I have a Spring-based application that I'm exposing through an Ant task,
    among other interfaces.

    The moment where I run into trouble is when the application tries to use
    Spring's ClassLoader-based code to load a Spring application context:
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(
    "/com/foo/ApplicationContext.xml");

    I see an error that the resource com/foo/ApplicationContext.xml cannot be
    found on the classpath. The XML file, together with the application code,
    happen to be in the same JAR as the task itself.

    Now, I came across this very, very old post on the ant-dev list that gave
    me the makings of a solution:
    http://www.mail-archive.com/dev@ant.apache.org/msg07704.html

    So at the start of my task, I would do this:
    AntClassLoader taskloader = (AntClassLoader)
    this.getClass().getClassLoader();
    taskloader.setThreadContextLoader();

    And at the close of my task, I would do this:
    taskloader.resetThreadContextLoader();

    This works actually.*

    I'm leery though of having to resort to this fix. Does anyone know of a
    "better" way to get Spring to use the right ClassLoader or just in general
    to fool task-invoked application code that might play fast and loose with
    classloaders? I haven't delved into the Spring source myself. What's odd is
    that this problem doesn't arise in other classloading containers in which a
    Spring-based application might be running. For example, a web container
    would be loading a web application with a child classloader, and yet that
    works fine.

    Thanks.

    * Rather, this almost works. Almost in that Spring tries to configure its
    logging through log4j, but it can't find the class
    org.apache.log4j.Category. I've verified that the log4j JAR is in my
    classpath. The quick fix is to stick the log4j JAR in the Ant lib directory.
    Obviously, that is not a sustainable solution. I'm still trying to get to
    the bottom of this and might have to write a follow-up message.
  • Antoine Levy-Lambert at Feb 1, 2011 at 3:05 pm
    I think it is log4j which wants to look for config files on the thread
    context loader.

    Regards,

    Antoine
    On 1/29/2011 1:27 PM, Mitch Gitman wrote:
    Here's the solution that finally worked for me, and that includes locating
    the log4j class. This is all in my Ant task:

    ClassPathXmlApplicationContext applicationContext = new
    ClassPathXmlApplicationContext();
    applicationContext.setClassLoader(this.getClass().getClassLoader());
    applicationContext
    .setConfigLocation("com/foo/ApplicationContext.xml");
    applicationContext.refresh();

    I'm going to clean this up to make the application layer initialize its own
    ApplicationContext internally. The caller/consumer of the application will
    just be responsible for passing a ClassLoader to the application layer. To
    my mind, having to supply a ClassLoader is the least evil--the smallest
    concession to the need for this application to be run by an Ant task as
    opposed to some other execution environment.

    Still, I'm curious why an executing task uses the ClassLoader obtained by
    LoaderUtils.getContextClassLoader() rather than the task's own
    AntClassLoader.

    On Sat, Jan 29, 2011 at 12:26 AM, Mitch Gitmanwrote:
    Here's another old post that corroborates the one I linked to:
    http://www.mail-archive.com/user@ant.apache.org/msg22871.html

    This poster isolated the problem with the base class of
    ClassPathXmlApplicationContext,
    org.springframework.core.io.DefaultResourceLoader. And I notice that
    DefaultResourceLoader does have a setClassLoader method:

    http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/core/io/DefaultResourceLoader.html#setClassLoader%28java.lang.ClassLoader%29

    This has the makings of a possibly more elegant solution. So I get:

    AntClassLoader taskloader = (AntClassLoader)
    this.getClass().getClassLoader();

    And then I pass this this AntClassLoader to
    DefaultResourceLoader.setClassLoader. I'll give this a shot. Might solve my
    log4j problem in the process.

    I'm still curious why this classloader isn't used in the first place and
    what the default one, as obtained by LoaderUtils.getContextClassLoader(),
    represents. This goes back to my observation that this sort of problem
    doesn't arise elsewhere.

    Note. I'm using Ant 1.7.1.


    On Fri, Jan 28, 2011 at 11:33 PM, Mitch Gitmanwrote:
    I have a Spring-based application that I'm exposing through an Ant task,
    among other interfaces.

    The moment where I run into trouble is when the application tries to use
    Spring's ClassLoader-based code to load a Spring application context:
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext(
    "/com/foo/ApplicationContext.xml");

    I see an error that the resource com/foo/ApplicationContext.xml cannot be
    found on the classpath. The XML file, together with the application code,
    happen to be in the same JAR as the task itself.

    Now, I came across this very, very old post on the ant-dev list that gave
    me the makings of a solution:
    http://www.mail-archive.com/dev@ant.apache.org/msg07704.html

    So at the start of my task, I would do this:
    AntClassLoader taskloader = (AntClassLoader)
    this.getClass().getClassLoader();
    taskloader.setThreadContextLoader();

    And at the close of my task, I would do this:
    taskloader.resetThreadContextLoader();

    This works actually.*

    I'm leery though of having to resort to this fix. Does anyone know of a
    "better" way to get Spring to use the right ClassLoader or just in general
    to fool task-invoked application code that might play fast and loose with
    classloaders? I haven't delved into the Spring source myself. What's odd is
    that this problem doesn't arise in other classloading containers in which a
    Spring-based application might be running. For example, a web container
    would be loading a web application with a child classloader, and yet that
    works fine.

    Thanks.

    * Rather, this almost works. Almost in that Spring tries to configure its
    logging through log4j, but it can't find the class
    org.apache.log4j.Category. I've verified that the log4j JAR is in my
    classpath. The quick fix is to stick the log4j JAR in the Ant lib directory.
    Obviously, that is not a sustainable solution. I'm still trying to get to
    the bottom of this and might have to write a follow-up message.

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

Related Discussions

Discussion Navigation
viewthread | post