Ant Is Not a Shell, ClassLoaders Are Not Processes
Coming off Java for a little while after six years of immersion offered me an opportunity to look at Java from a different perspective. In particular, I got to look at some of Java's deficiencies from a non-defensive perspective.
Take this post: Static: friend or foe?, for example. Cedric pointed out that the java task in Ant, if not "fork"ed, leaves the invoked classes loaded and any of their one time (static) initializations done. Thus if two non-forked java tasks are run in the same Ant invocation, the first can affect the second in surprising ways.
His advice, avoid static variables as a rule of thumb, though good in intention, and sound in principle, does not address the root of the problem:
Ant is not a shell. It a build tool with its own wrinkles.
Now take a look at a proposed remedy for the problem. It essentially want Ant to use a different classloader for each non-forked java task. This will fix the symptom in Cedric's example, but it doesn't go far enough. For example, classes loaded by other tasks (including custom tasks) would still be loaded and initialized.
The problem with ClassLoaders is this:
ClassLoaders are not processes. It a (square) reinvention of the process wheel.
For all the ClassLoader fans out there, here's a question for you: How do you do ICC (inter-classloader communication)?
Re: Ant Is Not a Shell, ClassLoaders Are Not Processes
Re: Ant Is Not a Shell, ClassLoaders Are Not Processes
Re: Ant Is Not a Shell, ClassLoaders Are Not Processes
But then the shared interface has to be loaded in a common ancester of the class loaders of the implementing class and the calling class. This goes against the perceived isolation afforded by separate class loaders.
More importantly, this cannot be reliably done from within either the implementing class, or the calling class.
Re: Ant Is Not a Shell, ClassLoaders Are Not Processes
Re: Ant Is Not a Shell, ClassLoaders Are Not Processes
"You have isolation of implementation and state. It's OK to share interfaces."
Doesn't that pollute the class loader you are loading the interface with?
"Why do you say that?"
Because every trick I can think of to do it required either putting the interface into the global classpath, or some a priori knowledge about the ancester class loaders. Neither can be done from "inside" the implementation class in a reliable way.
I guess I should not put it in such absolute terms. There may yet to be a way. But the headache I got from thinking about class loaders forced me to do it. :(