Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Friday, July 30, 2004

Ant best practice: One Compile, One Directory, One JAR

Do yourself a favor. When you are laying out your source tree, follow this rule: One Compile, One Directory, One JAR. Just because Ant supports includes and excludes on its <javac> and <jar> tasks doesn't mean you should actually use them!

A proper source tree structure may have multiple source code roots, and will run javac multiple times -- but each execution will compile all files under a particular source directory into its own, individual classes directory. The contents of that directory (perhaps combined with some deployment descriptors or other resources) will form a JAR. If anything depends on that code, it should add the JAR (not the classes directory, and certainly not the source directory) to its compile time classpath.

Why is the compile-and-split approach dangerous? It makes the build process and build scripts more complicated -- possibly lethally so. It ensures that the build process will not generate class files the same way (and into the same directories) as the IDE. It increases the likelyhood that some classes will be duplicated (compiled multiple times to multiple directories and packaged into multiple JARs), which can easily lead to mysterious ClassCastExceptions at runtime.

A particularly important case involves deployment into an application server. Once you get into complex EAR deployments (with multiple EJB JARs and WARs), the class loader issues can quickly get out of control. By using multiple compiles, you can cleanly ensure that the libraries available at build time match the libraries available to the runtime classloader for that artifact (that EJB JAR or WAR). Without this simple bit of discipline and organization, you can lose big chunks of time to tracking down ClassNotFoundExceptions.

I'm always amazed when I see this kind of thing in otherwise sane code and build environments. I've also seen just how far wrong this approach can go ... such as one source tree, one compile, 40+ JAR files, 10,000+ lines of build scripts!!

8 comments:

Glen Stampoultzis said...

It's amazing how complicated people make things for themselves sometimes.

Luke said...

I agree wholeheartedly -- I think. I had to reread a few times to be sure you were saying what I thought you were saying. When you say "Why is this dangerous?" you should clarify that what is dangerous is the notion of adding other directories' classes or source directories to this directory's build classpath. This is not clear in the original post because the dangerous part is a parenthetical comment.

Russell said...

I disagree, Luke. What is dangerous is compiling only part of a source tree; especially if you then come back in another compile step and compile a different part...

ranjix said...

and, coming soon, from the same author, "building one big EXE file instead of EXE and DLLs", respectively, "molding a car from a single piece of metal, including wheels - case in discussion Kia Sonata".

dude, you convinced me never to try tapestry or hivemind.

btw, ddj runs the opposite story: http://www.ddj.com/dept/java/185300006?cid=RSSfeed_DDJ_Java

Howard said...

Ranjix --- you totally missed the point. I like to build proper layers of code in properly modular jars. I just organize things in such a way that the build process doesn't become a gnarly monster. But that's ok ... we won't miss you in happy Tapestry / HiveMind land!

ranjix said...

heh. I missed the point? let's quote from the article.

"Do yourself a favor. When you are laying out your source tree, follow this rule: One Compile, One Directory, One JAR."

since everybody is working on an j2ee app, basically your "point" seems to be to have all the classes (for the app, mind you) in a single jar. that IS the point of your writing, as you so generously bolded it so one is sure he'll never forget. if this is NOT actually the point you're trying to make then you might want to re-write your point, this time paying attention to what you say.

howard, the article is weird and confusing and might give to some weaklings the wrong message (read the previous comments to see confused people, luke cracks me up "I agree wholeheartedly -- I think"). For myself I know I'm not going to put one app in one jar, even if gosling declares it the "pattern of the year". but I'm afraid some dudes will.

btw. in normal j2ee containers, usually there is ONE CLASSLOADER per EAR. so as long you have only one EAR (application), then you shouldn't run into problems with the classloader.

Howard said...

Yes, Ranjix. Every single other person appears to have realized that the point: generate multple JARs, have one source tree per JAR. You are the only one who seems to have leaped to the conclusion that I would suggest building a single JAR for your entire application.

ranjix said...

you really believe your article is clear, to the point and obvious. the fact that I missed the point (in your opinion, in mine the article is just cheap guru-posing) and the other 2 commentators (luke and russell) disagree on what you actually tried to say doesn't convince you it's something wrong with your writing...
but hey, I seem to be the only one ...
:-)))))))

just a suggestion: when you try to say "multiple compiles from multiple source trees to multiple jars", don't name your article "one compile, one directory, one jar". gives the wrong impression...

I just saw this blog is from 2004, if somebody didn't really try to convince me to put all the classes in one jar (giving your article as reference) I wouldn't write anything here (waste of time)