Sunday, February 18, 2007

Hating Maven less than Ant

I've been writing Java code since 1996 and struggling with build tools at least as long. Ant is the most comprehensive and Maven is the most ambitious. I hate both of them, mostly because they each require me to describe my project's build requirements using XML, which should never be used as a programming language, though often it's used as a configuration file format, but because all configuration files ultimately will evolve into programming languages... I digress.

For different reasons, Ant and Maven are both terrific and terrible at once. Ant allows you to describe your build however you please, but you must effectively do it from scratch for each project. Maven encourages many best practices by providing a lot of build features out-of-the-box, adopting a sort of "convention over configuration" philosophy popularized by the Ruby on Rails project, among others.


So for my money, Maven is the logical choice for a typical Java project. Unfortunately, few Java projects are typical, especially few enterprise Java projects. And for those projects, Maven can be frustrating because, like a child or a turtle, it's difficult to make it do something it doesn't want to do.

For example, let's say I'm debugging a build and I want to echo the value of a property to the console during a build. With Ant, I could add a target to build.xml:

<project>
<target name="debug">
<echo>The value is ${some.property}</echo>
</target>
</project>

With Maven 1, I can invoke Ant tasks within a goal defined in maven.xml:

<project>
<goal name="debug">
<echo>The value is ${some.property}</echo>
</goal>
</project>

Maven 1 provides easy access to Ant tasks, as you can see. Unfortunately, doing anything non-trivial with Maven 1 involves Jelly, which is pure "executable XML hell". Maven 2 did away with Jelly, but...

With Maven 2, it's bewilderingly complicated to echo a simple property to the console. You must include the "antrun" plugin in your pom.xml, configure an execution to run Ant's echo task, and bind that execution to a particular phase of your build (but see comments for other options). Ready? Here we go:

<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>debug</id>
<phase>validate</phase>
<configuration>
<tasks>
<echo>The value is ${some.property}</echo>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Note that our choice of the "validate" phase is completely arbitrary, but in order for our task to execute, it must be bound to some phase. Unlike our Ant and Maven 1 examples above, Maven 2 provides no way to invoke a single task without invoking all other tasks bound to the same phase along with all those before it. Maven 2 introduced the concept of profiles to address this perceived limitation, but I'll save that discussion for a future blog post.

Clearly, the Maven designers have violated a fundamental tenet: "the solution cannot be more abstract than the problem!" In trying to define and encourage best practices, Maven unapologetically sacrifices simplicity and flexibility, arguably two of the most important qualities of any software build tool, even for those projects exhibiting all accepted best practices.

Somewhat surprisingly, I generally still think it's worthwhile to convert your Ant projects to Maven, no matter how difficult. Maven's pain is concentrated, sort of like filling a cavity early in the project, resulting in a relatively pain-free existence once your project leaves the dentist's office. Ant is more like a minor toothache annoying you for the entire duration of your project, and then spreading to the other projects in your mouth as each begins its life with a copy of some other's build.xml file. Yes, of course it's simpler to do simple things with Ant, but adding enough of those simple things to projects over time can (and will, in my experience) lead to a pretty darn complex, brittle, and incomprehensible build.

In the long run, I think you'll end up hating Maven less than Ant, too. But if you insist on using Ant, use Ivy to manage your dependencies.

UPDATED Maven 2 example, removing unnecessary <property> element to reflect Eric's comment.

Saturday, February 17, 2007

Linux Podcasts

Since I got my new Meizu, I've been listening to a lot of podcasts, especially those dedicated to Linux. There are a surprising number of them out there, and I've listened to quite a few, but I wanted to share my favorites.
  • Linux Reality - Chess Griffin's generous, informative and practical podcast, presumably for newbies but really for anyone interested in Linux
  • LugRadio - can't understand them half the time, but usually very funny
  • JaK Attack - kind of hit and miss, but nice to hear a female voice
  • ITConversations Programming series - not active any longer, and focused on software development rather than linux, but great interviews with industry heavyweights like Paul Graham, Kent Beck, etc
  • Linux Action Show - often silly and self-conscious, but not boring and usually informative
  • Run Your Own Server - smart cats, more geared toward BSD than Linux, though

Saturday, February 03, 2007

Charter DNS still sucks

I set up dnsmasq on the Debian box I had been running bind9 on. It was infinitely easier to configure than a "real" DNS server -- just edit /etc/hosts -- and it perfectly suited the host-naming requirements for my admittedly tiny home network.

As with all things computer, of course, I still had hurdles to overcome, and they all involved difficulties with the commercial operating systems in my network, specifically Win XP and Mac OS X. All the Linux boxes were blissfully easy to setup and predictably functional and even adaptable to the weirdness required by the commercial ones.

As far as the Mac goes, it wouldn't resolve my local domain name, apparently because it ended in .local. I used that suffix because I read somewhere that it was an illegal TLD (top level domain, e.g. .com, .org, .edu, etc). Apparently, Mac's lookupd process eats those names because of the file /etc/resolver/local. I could fix the problem by adding a file for my domain to /etc/resolver, but I decided it was easier to use the suffix .home instead. I'm pretty sure it's an illegal TLD, too.

The WinXP box was sad for another reason. I had initially tried configuring dnsmasq as a DHCP daemon, which was cool, because then I could control all my static IP's in /etc/dnsmasq.conf instead of having to configure each host on the network. But alas, that broke my work VPN client on the WinXP box. I didn't spend any time trying to figure out why, though.

So now my router still acts as the DHCP server, even though the work laptop is the only thing that uses it. The other hosts are configured with static IP's that are replicated in the /etc/hosts file on the host running dnsmasq. Suboptimal, but functional.