In my most recent development effort at work I decided to take a look at the new kid on the block for building java projects: Maven 2. After 6 months or so of use I’m now preparing to use ant instead. Details after the link.
Maven 2 (and Maven before it) is supposed to herald the new age of build systems with the mantra “convention over customization” and it’s a good one. In the typical ant/maven/sconstruct/whatever build file you specify what steps are required to build the project, what order they have to be executed in, and exactly how to execute each step. In order to build a product ,you specify a target/task/whatever name and the build tool figures out and executes all the dependencies for that target.
What the Maven build tools do is fundamentally different in outlook. You lay out your source files according to a standard directory structure which identifies that type and purpose of each input implicitly and then, in the perfect universe, Maven automatically knows what has to be done to build each type of file and what the dependencies between files are. Not only that, but maven provides a comprehensive list of build targets (generate sources, compile, test, package, install, etc) that can be run against your project.
As a result, rather than creating a verbose ant or make file detailing all the various steps involved in your build all you have to specify are the commmand-line configuration details for the various compilers and test-suite tools like which version of the java API to use or what this particular library/application should be called. The typical maven file looks something like:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.4thmouse</groupId> <artifactId>example</artifactId> <packaging>jar</packaging> <version>CURRENT</version> <name>The example library</name> <url>http://4thmouse.com</url> <build> <plugins> <!-- support java 1.5 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> </project>
And the directory structure is strait forward:
| ---->java (java source)
| ---->resources (files to include in the jar: jpegs, xml, etc)
---->java (test source files)
---->resources (files to include in the test classpath)
Because maven knows where to find the main java sources, test java sources, and resources it can automatically build all the java files in the main directory, build all the files in the test directory, run all the tests in the test directory, and then build a jar containing only the classes and resources in the main directory.
In addition, the Maven 2 build system handles the inherently thankless problem of jar management. Jars are great as far as they go but without any sort of system registry or innate mechanism for managing jar versions, dependency management can quickly become a huge pain for any moderately large project.
Maven 2 manages all of this elegantly using something called a maven repository. These repositories not only can be used to store your own libraries but also allows you to automatically download the latest version of your favorite libraries (junit, jmock, jakarta commons, etc) and maven plugins as part of the build process. This means that the usually tedious process of finding and downloading all your dependencies, ant tasks, etc, etc, and so on is completely eliminated.
For example to have junit and jmock automatically downloaded and linked for tests all I have to do to my pom file is this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.4thmouse</groupId> <artifactId>example</artifactId> <packaging>jar</packaging> <version>CURRENT</version> <name>The example library</name> <url>http://4thmouse.com</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jmock</groupId> <artifactId>jmock-junit4</artifactId> <version>2.4.0</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- support java 1.5 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> </project>
So Why so Bad?
You may have picked up from the previous section that I love the concept behind Maven and I do. Unfortunately the implementation of Maven is such a bad fit that I’m not only going to stop using it I’m going to rewrite all my pom files as ant files.
The first, and in some ways the worst, problem is the Maven website itself. Allow me to point you to the most critical section, the cookbook. Oh wait. I can’t because it doesn’t exist. Well OK let me point you to the comprehensive guide. The what now?
What? The home site for the project has only slightly more information that can be found at the top of this article? I suppose I shouldn’t have been surprised. Apparently the apache project think their users are psychic aliens and therefore have no need for paltry documentation. Fortunately there is a maven 2 book produced by an outside organization owned by and run for non-psychic earthlings.
Now this is great as far as it goes. The book actually has enough information to allow you to understand what the hell Maven 2 is actually doing and where you should be looking for the real documentation: The auto-generated documentation pages for all the Maven2 plugins. Plugins are what do all the heavy lifting in Maven like compiling java, generating jaxb source code, etc. These plugins are all documented using a standard documentation tool al la javadoc. Unfortunately this is where Maven really falls apart.
The core maven plugins for compiling java code and creating jar files work well and follow the product mantra of configuration through convention. Everything else seems to be out to piss you off. Many have you chuck the source xml/code/whatever into a single directory (src/main/resources) or, worse, require you to tell it where to look for the files without a reasonable default. When you do have to provide configuration information it is documented in the most frustratingly hard to navigate format imaginable and when you find the option you are looking for you often get something like:
documentation: tells the plugin to run in multiple threads.
Which is often what is does not, in fact, do unless you set some other collection of options to specific values using your aforementioned paranormal coding abilities. (surefire plugin I am looking in your direction).
All of the property values for all the plugins have some common formatting (i.e. there is one way to specify a set of files) but this isn’t actually documented somewhere centrally. Most of the plugins assume you know the conventions and therefore don’t bother explaining them and of course none link to any central property format documentation. Combine this with an annoying tendency to simply ignore incorrectly specified options and you have a recipe for frustration.
The default behavior
Finally, you have the default behavior of your standard pom file. Remember that good default behavior is supposed to be the core feature of maven in the first place. By default every time you run a build Maven goes the online repository and gets the most recent version of any plugin you happen to be using. Often this plugin is XXX-pre-beta-2 and occassionally it breaks your build just for fun. If this wasn’t bad enough occasionally the repository, or your internet connection, or both go down. When this happens maven refuses to build your project at all. That’s right. Internet connection down? Can’t build your code. Even worse, when it can’t get to a repository it permanently blacklists it. Now even when the repository comes back Maven will ignore it.
Maven developers are quick to point out they have a solution to this problem.You simply reconfigure every plugin in your build to change this behavior. The average build involves at least 10+ plugins and dependencies. These dependencies are largely implicit so until something goes wrong the user is not aware of them. As a result, the Maven team is essentially saying you should use your psychic abilities to figure out what automatic dependencies are created by your build, figure out how to reconfigure them, and then update your pom file to be a 1000 line monstrosity 6 times longer than the easily readable ant file you are trying to replace.
So basically I love the concept behind Maven so much I was willing to accept the steep learning curve, lack of documentation, poorly written plugins, and occasional day spent trying to figure out how to build my software even when I’m not connected to the internet. Unfortunately I can’t do it any more. First, the maven development team has responded to criticism of their product by essentially labeling problems as features and asking the complainant to stop being stupid and RFD which of course does not really exist. These aren’t temporary issues so if you want to use maven you are just going to have to deal. Second, I have to work with other developers who will presumably have to maintain the code I’ve written. As it stands now I’d have to write a book on Maven which they would then have to read just to maintain the build system.
That being said, there have been a number of branches to the maven project recently by people as fed up as I am with the apache project and other build systems are taking note of the “convention over configuration” philosophy maven is supposed to be designed around. I look forward with great anticipation to a new generation of build tools that will take a good idea and turn it into a usable reality. For now I’m back to ant.