Hi everybody. I'm posting this to the lists in the hopes that the search engines will pick it up and provide more help than I got. When I looked, I saw lots of messages along the lines of "Help! Can't Define/change the classpath within the ant build file"... but no good answers.
It seems some people were using or even defining their own custom Tasks, only to find that "ClassNotFoundErrors" or "ClassCastExceptions" were popping up as a result. If their experience was anything like mine, they found that they could certainly define the classpath used to load their custom task, but that once that task was loaded, it had problems finding other classes it needed - even if they were within the classpath given in the taskdef. A perplexing and frustruating problem. The ugly solutions are obvious: 1) Using a script (or abusing your wrists) to specify a "system classpath" for all of ant that includes the classes that your task can't find. This is ugly, especially when not only is it unnecessary, but you're so close to not needing to do it. 2) Putting all of the jars/classes that your task can't find into ANT_HOME/lib. So now installation/transport of whatever you're doing is a pain, and you've crudded up the ant namespace for every user of ant on that system, potentially causing other problems. I scoured google finding nothing of any substance about what exactly caused this problem, or how it might be fixed. There were, however, a few clues that led me to examine Cactus and JUnit, which have, in various ways, dealt with issues of loading arbitrary classes in ant. Ordinary Java operations like "new", casting literals, variable declarations, and other "conventional" language features will operate via the ClassLoader that loaded the Object they're running in. Even the Reflection APIs appear to attempt to use the ClassLoader of the calling Object. This, at least, makes some sense to me. The problem comes with the way some Factory bootstrappers use reflection. In these "bad" implementations, reflection will be used to find and instantiate Factories, but with a "default" ClassLoader, rather than the ClassLoader of the calling Object. I encountered this in code from Sun, in the the JDOHelper class, which is used to bootstrap JDO PersistenceManagerFactories. That method (by default) used the ClassLoader of the current thread (Thread.currentThread().getContextClassLoader()). Judging by other messages I found hunting around the net, this kind of thing comes up in other situations as well. If you defined a classpath for your taskdef, a special AntClassLoader was created, configured with this classpath, and used to load your Task. But inside your Task, when you, or anything you call, uses these "sloppy" bootstrap routines, they can lose that ClassLoader (picking up ant's instead), and that means that the custom classpath in your taskdef isn't used. Who knows what's affected. This may be rare, and I may be unlucky, but bean stuff, factory stuff, database stuff, testing stuff, or anything else that uses reflection internally could potentially have a problem. Not many people write custom tasks that do these sorts of things, but it does come up, and when it does, it seems to have left a trail of agony. I found two ways to deal with this problem while making JDO work inside an ant Task. The sloppy way is to grab the "right" ClassLoader - the one you creataed by giving a custom classpath in your taskdef - and shove it into the current Thread. Yes, you can do that. FYI, these code examples have not been compiled - they're just to give a general idea. So anywhere in the Task (init(), execute()), but ideally right before the problem occurs: // Get the task class loader we used to load this tag. AntClassLoader taskloader = (AntClassLoader) this.getClass().getClassLoader(); // Shove it into the Thread, replacing the thread's ClassLoader: taskloader.setThreadContextLoader(); Then you do what you have to do. When your done, I highly advise doing a: // Reset the Thread's original ClassLoader. taskloader.resetThreadContextLoader(); Yes, taskloader remembers the original for later reset. This puts the right ClassLoader where JDOHelper wants to look for it by default. I tested this way cursorily and it seemed to work. I think it could be useful if you're dealing with a series of similar problems rather than just one instance. However, it is messy. You don't want to change the ClassLoader except where you need to. I don't know what else looks at the Thread's ClassLoader... It might affect ant, it might affect other tasks, who knows. However, if you're in a bind, you can try it - you might get away with it. You do get some additional control with the AntClassLoader, which can be useful for forcing a solution like this to work, via methods that allow you to control which classes, or whole package roots, are handled by the child and which are immediately delegated to the parent ("system")... Anyway, another small indication of why going this way is potentially troublesome. The "targeted" way of doing this is what I settled on, because JDOHelper was the only thing having a problem, and because it also supports specifying a ClassLoader _other_ than the bad default for use in the "getFactory" method in question. So instead of doing a setThreadContextLoader(), you just pass it taskloader, and everything is as it should be. So that's how you can package custom Tasks using JDO (and anything else that has this problem) without having to give complex install instructions or write wrapper scripts. -David Wood --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]