Thursday, June 16, 2011

Moving from Ant to Maven: Best Practices with Maven Plugins

I would like to share my experience of moving from "our reinventing wheel" of Ant build scripts to Maven standard.

I reworked some apps which have following characteristics:

(1) Java Version:
- The apps should be compiled with Java 6 version.

(2) Basic technologies:
- Some of the apps are using Model Driven Architecture/Model Driven Software Development things, it means we have some MDA/MDSD generators inside our Ant scripts, like AndroMDA or oAW.
- Almost all the apps use Hibernate, so we need to generate the whole Database scheme to be able to create the whole database automatically.
- Some of the apps use older version of ANTLR. So we have some of ANTLR source codes which have to be compiled.
- Some of the apps use older version of Groovy. So we have some of Groovy source codes which have to be compiled.
- A webapp uses GWT as presentation layer.

(3) Packaging:
- Some of the webapps have somekind of information about the version. For example the webapps have version.html file which should be automatically generated with some information like build timestamp, the person who build the app, the description of the app, etc.
- The apps or modules should be packaged as war or jar. Additionally they also include some additional information like installation documentation, SQL scripts, etc. which have to be package as a zip file separately.
- All of the apps or modules should also package the source codes additionally to the packaged artifacts, so we can debug the apps easily.
- The webapps need to package the class files additionally as a separate jar file, so that this can be added as a dependency into another Maven project or module.

(4) Test:
- The apps have some unit tests in JUnit.
- The apps have some integration test for SpringFramework context and database or service methods test in SpringFramework.

(5) Optimization:
- JavaScript, CSS of some webapps should be optimized using YUICompressor.
- JSP files should be precompiled with JSPC from Weblogic.

(6) Quality check:
- Quality check: the apps should be quality checked using Emma (code coverage) and CheckStyle (quality of the codes).

(7) Deployment:
- The webapps should be automatically deployed to Weblogic.
- All configurations should be automatically uploaded to the server.
- The database scheme should be automatically upgraded and downgraded.

(8) Development support:
- JRebel rebel.xml file should be automatically generated for chosen projects or modules.

Yes, our Ant scripts can do the (almost) whole things above. You can imagine how complex they are? Yes, everything are in the Ant scripts and you need to turn on/off the features you need. Remember how SAP works? Yes, we call this customizing! This is where I see the power of Maven. The plugin concept is just a better concept. Instead of having everything, we only have a small "core" and we add plugins on the top to fullfil our need. In case of the Ant scripts, you have everything (although you don't need all of them) and then you use switcher to turn them on and off.

So how did I solve the requirements above with Maven? Here they are, the plugins we need! In this article I just want to show you the plugins I used:

(1) Java Version:
This is quite simple, just add maven-compiler-plugin (2.3.2).

(2) Basic technologies:
- MDA/MDSD: In this case I have two main technologies: AndroMDA and oAW cartridges. For oAW 4.3.x cartridges we have fornax-oaw-m2-plugin (3.0.1). No problem at all. For AndroMDA 3.2 we also have some Maven plugins. My problem was that we use AndroMDA 3.1. In this version there are only some Maven 1.x available. So in this case I have to use maven-antrun-plugin (1.2). Using maven-antrun-plugin I can execute the AndroMDA task to generate the codes without any problems. I need to use the older version of this plugin because of the dependency to the older Ant version of the AndroMDA 3.1 task. One thing you should not forget: if you are generating codes you need to include the generated codes into your compile classpath. Using build-helper-maven-plugin (1.5) you can achieve this. Great!
- To be able to generate the SQL DDL scripts from Hibernate you just need to add hibernate3-maven-plugin (2.2). Working like a charm!
- Because we are using older version of ANTLR we also have to use maven-antrun-plugin (1.2) to be able to execute the ANTLR compiler.
- The same thing also happens with our older version of Groovy. Just use maven-antrun-plugin (1.2).
- For some GWT projects just use gwt-maven-plugin (2.2.0). This is a great Maven plugin (and the one and only) for GWT. This plugin has a very good documentation, so integrating it is quite easy.

(3) Packaging:
- The first step is to delete some of the target directories (generated directories). For this purpose we have maven-clean-plugin (2.4.1).
- To be able to generate a version.html file I use maven-resources-plugin (2.5). With this plugin you will be able to copy some resources (like version.html), parse the file and exchange all the variables with the content you like. In our case we need to exchange some variables with Maven pom.xml information (${maven.build.timestamp}, ${pom.version}, ${pom.description}, etc.). To be able to read these information you need to use at least the latest Maven 2.2.1. In the future we want to create our own Maven plugin to do this stuff, so that we can encapsulate the function better.
- To package additional information and files we have maven-assembly-plugin (2.1). This plugin is very flexible. So I can capture all the requirements of packaging additional zip files with it.
- To be able to deploy the source codes into Maven repository just use maven-source-plugin (2.1.2).
- In some of the webapps I need to package the "classes" into a separate jar file, so that this file can be added into a Maven dependency by other modules. In this case you can split the projects into two: one project for the "classes" and one project for the "webapp". But I did not want this because it will add the complexity of my projects (two projects instead of one). So I use maven-war-plugin (2.1.1) and add a configuration: attachClasses = true. That's it. You will get a new artifact for the classes with additional classifier: classes.
- In some business modules (Jar files) I need to exclude the model files which are the foundation of MDA/MDSD development style. For this purpose just use maven-jar-plugin (2.3.1) and exclude all the model files you don't want to be packaged in your Jar file. The cool thing is that your maven-source-plugin won't be disturbed by this action. So you will have all those model files still included in your source Jar file.

(4) Test:
- For unit test just use maven-surefire-plugin (2.8.1).
- For integration test just use maven-failsafe-plugin (2.8.1). It is important to separate unit and integration test because the integration test can take sometime. Using both plugins will make your project build more flexible, trust me!

(5) Optimization:
- For CSS, JavaScript optimization with YUICompressor just use yuicompressor-maven-plugin (1.1). It works without any problems. One thing you should aware of: if you are build a webapp you also need to configure maven-war-plugin so that it can copy the result of the optimization into the war file. This is also one thing you maybe want to use Maven profile concept, since you don't want to have optimized files within your development lifecycle.
- Precompiling JSP files with JSPC from Weblogic: this is still an open point. I can use maven-antrun-plugin, but I think that there should be a better way.

(6) Quality check:
- For Emma just use emma-maven-plugin (1.0-alpha-2).
- For CheckStyle just use maven-checkstyle-plugin (2.3). You also need to use surefire-report-maven-plugin (2.8.1) to be able to get a report on CheckStyle.

(7) Deployment:
- Automatic deployment war file with Weblogic can now be done easily with this plugin: weblogic-maven-plugin (10.3.4) from Oracle itself. This is great since this Maven support comes from Oracle. Here is the link to the Oracle documentation of this Maven plugin: Maven plugin from Oracle. To be able to define the server location without changing the pom.xml I use this plugin properties-maven-plugin (1.0-alpha-2). With this plugin you can read any properties file and you will have those properties accessible from your Maven build. Great stuff!
- If you want to be able to make automatic deployment you also need to support upload of configuration files to your server. For this purpose you can use this plugin: wagon-maven-plugin (1.0-beta-3). You can download and upload all kind of files with different kind of protocols (http, scp, ftp, etc.).
- The last important thing is to be able to automatically upgrade or downgrade your database scheme. This is an open point for me. There are some Maven plugins out there, but I had no time to analyse them. See my buzz for the list of them.

(8) Development support:
- To generate rebel.xml you can use jrebel-maven-plugin (1.0.7).

As a conclusion I just can say, Maven and its plugin ecosystem are great. No matter how complex your project you can still handle it easily with Maven plugins. My lessons learned is that DO NOT REINVENTING WHEEL! There are a lot of good Maven plugins out there! In case you cannot find one, just use maven-antrun-plugin. You can almost do everything with it. Just don't forget that you don't want that chaos back to Ant, so use it with precautions.

... and to all other developers who still doing their own build processes... try Maven, you won't regret!

Cheers,
Lofi.

5 comments:

Brian Kelly said...

Thanks for writing this up. I'm considering suggest making the move from Ant to Maven as well and this post will be quite helpful!

Karl Peterbauer said...

I'm using Maven for a couple of years now. My experience: Moving from Ant to Maven is moving from a horrible mess of custom build scripts to a horrible plugin configuration mess and a veritable dependency nightmare.

In the old Ant days, I regularly copied hundreds of carefully crafted lines of XML from one project to the next. Now I do the same with carefully crafted POMs...

Maven is plagued by lots of conceptual flaws and poor design decisions. It was a step forward a couple of years ago, but it is definitely not the holy grail of build management.

Anthavio Lenz said...

Well Karl if you don't have/use company/super pom you are destined to do it forever

Karl Peterbauer said...

@Anthavio: That's exactly the point I'm critizing.

Just a simple example how absurd Maven can be: Maven does not provide a straight way for defining even the most basic stuff such as the Java source/target code version. It's a Java build tool after all! Instead, the version must be defined as a configuration option for the Maven compiler plugin. For configuring the plugin, it must be defined in the POM, and since Maven 3.0 it's more or less mandatory to define the plugin version. As a result, Maven forces one to figure out the current version of the maven compiler plugin to define the Java source/target code version. To get rid of this ridiculous XML boilerplate we need - yo man! - a super POM!

It's not only a super POM, after a few projects you discover that you need your own repository. So you dig into Archiva, Nexus or Artifactory.

Maven advocates simply ignore the inevitable fact that Maven is a horribly complex system. Build tools should be simple.

lofi said...

@Karl: I know that Maven is not easy, but build management incl. dependency management itself is complex, so this is not the problem of Maven.

As I wrote in my blog with Maven you have a small "core" which can be extended using plugins. And you always have same phases (clean compile package, etc.) through all of your builds and this is the biggest advantage against pure Ant.

Talking about setup a Maven project: it is IMO very easy, especially if you are using M2Eclipse or Maven archetype.

Maven only for Java? I don't think this is true. Check this plugin out: http://mojo.codehaus.org/maven-native/native-maven-plugin/index.html

I don't have any experiences in Gradle but one thing I cannot agree: using another DSL would make build management easier. IMO adding new languages (DSL) will make everything much more complex and you have to find developers who know those languages, so this is not trivial...

Cheers,
Lofi.