Atom Atom feed

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

What? Class loaders and processes are two completely different concepts. If you want objects loaded from different class loaders to communicate, they should both use a common interface, the definition of which is loaded from a delegated class loader (e.g. the system class loader).

Re: Ant Is Not a Shell, ClassLoaders Are Not Processes

How do you communicate through an interface?

Re: Ant Is Not a Shell, ClassLoaders Are Not Processes

One class implements the shared interface, the other makes calls to an instance of the shared interface. In the application, the implementing class is loaded in a class loader, instantiated and the reference cast to the shared interface. The calling class is also loaded, instantiated, and is passed the reference to the implementing object. It can then make calls to the object loaded from the other class loader.

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

"This goes against the perceived isolation afforded by separate class loaders." You have isolation of implementation and state. It's OK to share interfaces. "More importantly, this cannot be reliably done from within either the implementing class, or the calling class." Why do you say that?

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. :(


Add a comment Send a TrackBack