The Source for Java Technology Collaboration
User: Password:



Zarar Siddiqi

Zarar Siddiqi's Blog

The pain of migrating from Ant to Maven

Posted by zarar on December 13, 2006 at 08:18 AM | Comments (20)

So you want to migrate to Maven because somebody told you it's the greatest build system around? They're probably right but what they don't tell you is that the road to Maven success is through hundreds of land mines, open JIRA's, mailing list lies and enough internal bleeding to make you wish you had stayed with good ol' Ant even though the build file had reached 4000 lines.

Don't take my word for it, just ask anybody and everybody who's ever converted a project larger than 30 source files from Ant to Maven. God forbid if you're dealing with XDoclet and generated files, if that is the case, then you'll need enough Valium to last you a mvn deploy and no you can't use -Dmaven.test.skip=true. Let's keep the attempts at humor on the side and talk about what really plagues Maven. You, you right there who’s reading this and thinking, Has this guy lost his mind? Maven is the greatest thing since the wheel and I've never run into any problems with it so it must be great. You my friend have obviously never worked with a multi-module project or mastered the maven-release-plugin which was originally designed to keep ADD patients occupied for months on end.

Let's look at the good first:

Maven does all the dirty work for you. Given a set of Java source code, it will take you no longer than two minutes to set up a Maven directory structure (using the archetype plugin), compile your sources and jar them up. Given a set of Java sources, web.xml, and other web resources, it will take you no longer than two minutes to create a fully functional war ready to be deployed. If you have any test classes, it will even run them for you without you even having to write a single line of XML.

As you can see the convention over configuration thingy does make sense. To accomplish the above tasks, you would be required to write a verbose Ant script but with Maven you have to write almost nothing. Note to Maven virgins: Maven by default assumes your source classes are in src/main/java, test classes in src/test/java, and web resources in src/main/webapp. It also assumes you have the ability to find a grain of salt on the beach when it comes to the mailing list but more on that later.

Continuing with the good, Maven's greatest feature (and it is a HUGE feature) is dependency management which it does a fine job of. No more downloading xfire-1.2.zip, unzipping it and adding it to your classpath. Almost everything is on ibiblio and you can download it from there using a reference in your pom. If something is not on ibiblio or if you’re dependent on a home grown artifact, Maven allows you to install the artifact in your local or remote repository with minimal bruises. Your application can literally be built from scratch by using a single call to mvn package.

Let's now look at the good and bad:

The first thing to do when working with Maven is to get the bleeding edge version (2.0.x) which has as many fixes as possible and then read Mergere Inc.'s book. Same goes for any plugins that you'd use. One of the better things about Maven is that it's very easy to develop plugins to fit your need; this of course might also explain why all the plugins have bugs in them.

maven-antrun-plugin

What this plugin is saying is this: We know Maven has problems and it can't do everything but we want to say that it can do everything so here's a blanket piece of code which covers all cases, granted with piss poor quality.

The maven-antrun-plugin for those of you who've never had to use it allows one to write Ant scripts inside a pom.xml, allbeit with strait-jacket type restrictions. If you plan on doing a medium sized Maven migration, be sure to familiarize with the evils of this plugin:

  1. It does not pass Maven properties to the Ant script unless you specifically redefine each property inside the Ant script, thus defeating the whole purpose.
  2. There is no way of passing Ant properties to Maven.
  3. It magically runs twice if a parent pom also has a maven-antrun-plugin defined, an entirely possible scenario.
  4. It does not allow for macrodefs to be called.
  5. It has class loading issues if you plan to use Ant optional tasks.
  6. It ideally should be used as a last ditch effort to perform tasks but has become a primary means of building with Maven.

So although the antrun plugin is powerful, it is a direct contradiction to Maven's philosophy of convention over writing-big-ass-scripts-to-do-build-tasks.

Plugins in General:

The power of Maven lies in its plugins. If there is a task that needs to be performed, say compilation, it's handled by the maven-compile-plugin; if you need to run tests, the maven-surefire-plugin; if you need to filter files, the maven-resources-plugin; you need to precompile JSP's, the jspc-maven-plugin. You get the idea.

The problem is that so many of the plugins hosted at Codehaus or Apache have bugs and "features" that will keep you up for nights wondering if you'll ever get to resume your FIFA game. Allow me to point out a couple brisk examples for the unbelieving audience:

  1. Plugins getting executed in no particular order: Not really a specific plugin issue but more of a general one. If you have multiple executions for a plugin bound to the same stage, they execute in, get this, RANDOM ORDER. Instead of caring for our sleep and executing them in the order they were defined, which would make too much sense, the Maven developers have decided to throw a curveball our way. This will make you wish there was another phase between process-classes and test-compile, guaranteed.
  2. maven-resources-plugin: Treats even built-in property values as verbatim. ${project.build.directory} will not be evaluated to "target" which it should be but to ${project.build.directory}. This can drive you insane if you're filtering for different environments.
  3. maven-surefire-plugin: Despite pleas by the general public to add a configuration parameter for having an additionalClasspathElements, the surefire plugin is committed to pissing people of by forcing them to have a custom version which they will need to patch and deploy to their local repository.
  4. maven-release-plugin: If anybody has got this working with a hierarchical project, please let me know.

The maven-idea-plugin, or sorry, the idea-maven-plugin was a pleasant surprise. Yes, the name matters. If you start your plugin name using "maven-", you better be part of the Apache group or they nuke your PC. Back to the plugin: Type in mvn idea:idea and you have a bunch of IntelliJ files which get you fairly close to compilation as long as you're doing nothing fancy. Unfortunately, if you're doing anything remotely complicated like generating source files or resources, you'll have to fiddle with the settings (classpath, source tree, XML files etc) to get it to do anything. All in all, it was a decent find.

When well written like the jspc-maven-plugin, life is good. If written like the Cargo Maven plugin, it lulls you into a false sense of security, shoots you and then gnaws your head off while you’re sleeping on your keyboard. Chances are that if you're going to decide to use a plugin beyond the most basic case, you will end up looking in its source code to figure out what the hell its doing. That's probably why every plugin has a convenient link to the source repository. What better documentation!

Generated Files:

If you're using XDoclet, you fall into two categories of people: Those that have fixed a bug in XDoclet and are using a custom version and those who haven't contracted rabies from a careless XDoclet developer error. If you fall into the latter group, you can go ahead and use the maven-xdoclet-plugin and see how far it gets you. However, if you fall into the former category, what lies ahead is the unenviable task of copying all the stuff you did in your build file over to your Maven POM and in to the antrun plugin. The other thing you can do is to force the xdoclet-plugin to use your custom version of XDoclet rather than getting it via its defined dependencies. Good luck going down that road.

Maven completely missed the boat on generated sources. Since all the source code is supposed to be in src/main/java, one is forced to do one of two things, both not very glamorous:

  1. You could dump them in src/main/java in the generate-sources phase and delete them in some subsequent phase after compile. Stinks of bad programming and will probably get you fired.
  2. Create a gen directory in target or as Maven likes to call it ${project.build.directory} and then use the build-helper-maven-plugin's add-source goal which will add another 25 lines to your pom.xml, making you nostalgic about Ant. Side note: is it just me or is ${project.build.directory} excessively long an alias for the word "target". Same for target/classes which the Maven folks happily defined as ${project.build.outputDirectory}. How nice and compact.

I hope you're a religious man if you're generating a web.xml or a struts-config.xml because the only way you'll ever get them in the WEB-INF folder is through prayer. Although the following misuse of the maven-resources-plugin would work too:

<resources>
   <resource>
      <directory>src/main/resources</directory>
   </resource>
   <resource>
      <directory>target/gen/web-resources/WEB-INF/</directory>
      <filtering>true</filtering>
      <targetPath>../wardir-cant-use-property-here-due-to-bug/WEB-INF</targetPath>
   </resource>
<resources>

See, if you want to copy over a resource other than the default one, you have to redefine the default resource (the first resource element). The second resource element's targetPath assumes the default location of target/classes which makes the value of targetPath rather confusing to the untrained eye.

Maven should seriously consider having a target/gen/src/main/java etc structure to combat generated files since this is a situation faced by most medium to large projects.

Again, there is a solution but it’s not pretty.

Profiles:

Remember the good old day of AntContrib's if/else statement? How simple? If or else. Two options, pick one of them. Well in the ultimate example of killing a bird with a cannon, in order for one to execute an if/else statement in Maven, you must write a profile which stores the plugins that execute on the true condition. If you want something else to happen in the false condition, write another profile. Sounds overkill? It is. Well, you could always use the maven-antrun-plugin but that won't get you far since you can't call Maven code from Ant code and you'll be forced to do everything with Ant once you go down that road.

Having said that, profiles do allow executions to run in different conditions. For example, if you only want your JSP's only to be precompiled when you're deploying to production, you would define the jspc-maven-plugin in a profile with an id of "precompile-jsps" and it'll only get executed if you activate it, for .e.g.: mvn -P precompile-jsps deploy. But say if you want to copy a file into either dir1 or dir2 based on ${prop1}, whip out the antrun plugin.

Class Loading

Justifiably Maven's class loading mechanism is complex, especially when it comes to multi-module hierarchical projects. They do have the decency to provide you with different classpath's which are built from the dependencies specified in the POM. Although this is very convenient, you just never know when you need to specify a weird property out of the blue.

Mailing List

Exhibit A, The Maven Users Forum. Where the users providing help are as confused as the poor bastards asking for it. For every query typed into the search box, six different "solutions" pop back all claiming to work but rarely ever doing so because you have something sliiiighhtly different somewhere. These posts usually have a link to a JIRA of some sort which may or may not be related to the actual problem. What I'm trying to ramble through is that the mailing list has tons of useful information in there but you have to be careful for what you accept as a solution as it's pretty easy to get lost.

Let's take a look at the bad:

Properties:

This bothered me to no end and I ended up writing properties-maven-plugin which still hasn’t been accepted despite many users supporting the feature. The feature in question is loading various project properties from property files. It only make sense but this essential piece of the puzzle in any application is mysteriously missing from Maven. For example, I want to load a different property file for production versus for development. Is that too much to ask? What Maven wants you to do is to define two profiles with their own set of properties and activate one of those profiles during the build (and hope that the profile properties override the project properties which they don’t). This makes the POM unnecessarily huge since defining properties in XML is a bitch:

What would be this:

myprop1=myvar1
myprop2=myvar2

becomes this:

<properties>
<myprop1>myvar1</myprop1>
<myprop2>myvar2</myprop2>
</properties>

Note you have to do this each time for every environment in your POM. Those of us coming from Ant can’t get a handle on this. There needs to be a replacement for <property file="build.properties"/> in Maven. Don’t even think of using the maven-antrun-plugin to do that.

Oh, by the way, the ${line.separator} property is broken in Maven and if you try printing it out, you'll only be disappointed.

Multi-Module compilation:

Be very careful in a multi module project. If you’re doing a mvn compile, it will look at your sources in all POM’s that have a version number ending with the word SNAPSHOT, however, if you’re doing a mvn package, Maven decides to ignore your checked out sources but instead looks in the local repository for a snapshot jar. To avoid any possibility of compiling against older source, it’s best to do a mvn install for the module that’s serving as a dependency before doing a mvn package for the main module. Remember this and you’ll never find yourself doing an mvn package for hours without figuring out why your changes aren’t showing up.

Maven’s whole deal of treating –SNAPSHOT versions differently is bound to cost anybody a few hours of headache but once you’re past the initial growing pains of somebody shoving a philosophy down your throat, you’re bound to like it.

So in closing: Maven has been a complete pain to work with but it has pleasantly surprised me at how integrated my build has become and how source code from multiple modules is combined together to produce multiple artifacts. Something Ant just couldn’t do.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • We've tried to use Maven to build genesis early in 2004, but then we got sane and wrote an Ant build file again. Even this build process is complicated, but at least it is manageable.
    Currently we use Maven just as a nice way of generating our docs and project reports.

    Posted by: mister__m on December 13, 2006 at 10:14 AM

  • I had the same issues trying to use xdoclet with maven. it was a pain in the ass. I couldnt even find good documentation! I was better off just using my existing xdoclet scripts, and I'll try to do the migration later on.

    I gave up and realized, maven will be good for generating my idea and eclipse project files, which it does a nice job of.

    Posted by: spicysquid on December 13, 2006 at 10:43 AM

  • We are using maven but still we struggle when it comes to integration of the some more exotic plugins. Building an ear took us several days. After reading the plugin doc you find yourself in a trial and error circle. Most of the time the docs are too brief to be understood by anyone except the authors of the plugin.

    Posted by: schiewe on December 14, 2006 at 05:40 AM

  • I ended up hitting a wall with ANT tasks as well, especially when it came to finding the right JavaScript library (since the ANT build script I was porting uses JavaScript). It would be

    Any luck posting your Properties plugin on Codehaus? They've got a repository as well that you might be able to upload it to.

    Posted by: jimbethancourt on December 14, 2006 at 07:18 AM

  • When our users demanded we provide Maven support, we wrote an Ant task that generates the required .jar files.

    Posted by: jessewilson on December 14, 2006 at 09:02 AM

  • Hah! This was a great post. Sorry for your pain, and thanks for the warning.

    The repository management sounds nice, but not as part of maven. In any case, I'm not sure I'd like to have dependencies on the Internet just to build our production software.

    Posted by: erickson on December 14, 2006 at 09:34 AM

  • You don't need to have dependencies on the internet, after the first run you will have all the dependencies in a local repository and you can switch to off line build to avoid net connections.

    And if everyone needs stability, don't go with 2.0, choose 1.x which is well tested and everything will work fine for you.

    I think maven is the best tool ever to build your projects !!

    Posted by: adrian_tarau on December 14, 2006 at 09:54 AM

  • +1 on the recommendation to stay with Maven 1.x.

    Posted by: ljnelson on December 14, 2006 at 10:00 AM

  • I just finished creating a proximity installation for Maven2. In a few weeks I'll likely be doing a presentation on Maven2 at our company. The only thing our developers will need to know is how to install maven, the 4 second change to settings.xml, and mvn install. With everything we have at the office I can literally deploy a Jar from scratch in 3 minutes. For people that don't use maven they will still be able to access the jar by browsing the proximity web server. I give Maven2 two thumbs up.

    Posted by: coding on December 14, 2006 at 11:18 AM

  • Hi,

    why don't you use maven 2 as dependencies manager but keep everything in Ant script? I think this is the best strategy with the current architecture/implementation of maven.

    I think, what most Ant developers are missing - it's how to manage dependencies. I feel the same when work with maven - it's not flexible.

    Alexander Shvets.

    Posted by: shvets on December 14, 2006 at 11:37 AM

  • As far as dependency management goes, does anyone here have any experience with Ant+Ivy? How does that experience compare to Maven?

    Posted by: jeeky on December 14, 2006 at 07:43 PM

  • Why not looking for other options to make your build scripts?
    Personnally, I have adopted "el4ant" which is a generator of ant build scripts that uses simple xml files for describing your projects.
    It supports modules and modules dependencies.
    It is based on "plugins" that are quite easy to develop (for supporting special features; eg I developed a few for abbot GUI testing integration, one for jetty...)
    Its main cons (there unfortunately are) are:
    lack of documentation for writing plugins (you generally finish reading el4ant source code)
    lack of plugins (if compared with maven)

    Posted by: jfpoilpret on December 16, 2006 at 06:14 PM

  • I've started tracking gosling ( https://gosling.dev.java.net/ ). The approach sounds fairly rational. in the mean time, at work, we're using beanshell scripts to drive ant.

    Posted by: dwalend on December 20, 2006 at 08:34 PM

  • I've been successfully using Maven *only* for dependency management and Ant for everything else. You may be interested in this blog post.

    Posted by: ptrthomas on December 25, 2006 at 01:01 PM

  • I agree with erickson, I don't like depending on the internet for some files - all libraries should be committed in the source repository for having 100% reproducibiliry.

    And...

    "You don't need to have dependencies on the internet, after the first run you will have all the dependencies in a local repository and you can switch to off line build to avoid net connections."

    ... that is, the whole repository management idea is completely useless. I don't see why should I embark such a complexity monster over Ant only to save a couple of manual downloads.

    Posted by: fabriziogiudici on January 08, 2007 at 04:59 PM

  • I agree with erickson, I don't like depending on the internet for some files - all libraries should be committed in the source repository for having 100% reproducibiliry. And... "You don't need to have dependencies on the internet, after the first run you will have all the dependencies in a local repository and you can switch to off line build to avoid net connections." ... that is, the whole repository management idea is completely useless. I don't see why should I embark such a complexity monster over Ant only to save a couple of manual downloads.

    Posted by: alicia67 on February 12, 2007 at 12:28 AM

  • I've used Ant + Ivy extensively, and have found it to be a rather pleasant experience. Ivy basically provides the same functionality as the Maven Ant tasks do. However, in my opinion, it's quite a bit better, for the following reasons:

    * Multiple Artifact Support (something that is extremely difficult to do in maven or maven 2).
    * Better support for multiple configurations (you can group artifacts into different configurations).
    * Better configurability of conflict and latest version strategies
    * More repository options (fs, http, sftp/ssh, vfs)

    Basically, I use the svn code for ant-contrib which has a relatively new task in it, which allows me to package up build scripts into a .jar, and basically "import" the jar file into my project. By importing, it uses ivy to download the correct jar, unpacks it in the ivy cache, and imports the "build.xml" file (you can override the name of the file to import) into your project:

    That's all i need to define my common build targets. Of course, this requires that ant-contrib and ivy be installed into your ANT_HOME/lib directory (or be added to the startup classpath for Ant).


    Posted by: mattinger on March 06, 2007 at 06:43 AM

  • I've used Ant + Ivy extensively, and have found it to be a rather pleasant experience. Ivy basically provides the same functionality as the Maven Ant tasks do. However, in my opinion, it's quite a bit better, for the following reasons: * Multiple Artifact Support (something that is extremely difficult to do in maven or maven 2). * Better support for multiple configurations (you can group artifacts into different configurations). * Better configurability of conflict and latest version strategies * More repository options (fs, http, sftp/ssh, vfs) Basically, I use the svn code for ant-contrib which has a relatively new task in it, which allows me to package up build scripts into a .jar, and basically "import" the jar file into my project. By importing, it uses ivy to download the correct jar, unpacks it in the ivy cache, and imports the "build.xml" file (you can override the name of the file to import) into your project: That's all i need to define my common build targets. Of course, this requires that ant-contrib and ivy be installed into your ANT_HOME/lib directory (or be added to the startup classpath for Ant).

    Posted by: davidddfe on April 23, 2007 at 12:44 AM

  • topic maven-antrun-plugin: It does not pass Maven properties to the Ant ...
    It is a little tricky, but quite simple to pass the project parameters to ant. Just use the <xmlproperty file="pom.xml" /> in the ant file. It reads all properties so that you have access to them like issueManagement = ${project.issueManagement.url}

    Posted by: christian_bauer on May 13, 2007 at 12:40 PM

  • We have examples of both Maven and Ant, perhaps this could be of help to some of you. The link is non-profit, no ads.

    Some Maven scripts

    Ant Scripts

    These are somewhat specific to common tasks, but are useful nonetheless. Rare time saving information.

    Posted by: screenedtwenty on May 25, 2007 at 02:30 PM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds