January 25, 2012

The Maven Dependency Plugin, It’s Tree Time.

I recently inherited an external testing project that is about 6 months stale. It was written by a co-op who left a while ago. For those of your playing the home game, neither of these are good signs. It’s a …

  Show More

I recently inherited an external testing project that is about 6 months stale. It was written by a co-op who left a while ago. For those of your playing the home game, neither of these are good signs. It’s a suite of tests packaged in an executable jar that use selenium to automate a firefox (or any) browser. Its purpose is to automatically verify there have been no regressions in the target project since the last major feature set. Nifty right?

Only one issue. The project was started in, and run exclusively out of, eclipse. Specifically, all of the dependency information was created by modifying the eclipse build path settings. This means that a ton of relevant project information is stored in the .classpath file on a specific computer. To make matters worse, the .classpath, .settings, and .project files are commonly ignored globally by users of svn or git, to avoid muddying up the repository. If that one computer has a hard drive crash, or the co-op quits, dies, or goes on a road trip, we’re scuppered. The project now requires a goat sacrifice as part of the build process, it’s arcane magicka twisting just outside our mortal comprehension.

For the record: I have nothing against eclipse. I think it is a great IDE, and I use it every day. However, it is not, and never will be an enterprise level build/dependency management tool.

Enter the POM (Project Object Model). The pom.xml file is your first step to using Maven as a build and dependency management tool.

Create a basic pom.xml file at the root of your project, and make sure your directory conforms to the “convention over configuration” maven directory structure.

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1</version>
</project>

From there, you can take advantage of the fact that every pom.xml automatically inherits all of the behaviour of the standard “super pom”, and bango, without even realizing it you’re using a fully featured build tool. To add a dependency (i.e. the junit 4 api) reference its groupId, artifactId, and version as follows:

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.8.2</version>
  </dependency>
</dependencies>

Go ahead and build your project with the command:

mvn install

Notice that the dependency is downloaded, along with any other dependencies that IT in turn depends on, magic! No more DLL Hell for us (well, JAR hell, but you get the picture)! For the interested, this is called transitive dependency management: a->b->c therefore a->c.

Let’s take this one step further. To see a nicely formatted tree of your newly downloaded dependencies, add the maven dependency plugin to your pom.xml as follows:

    <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.4</version>
        </plugin>
    </plugins>
    <sourceDirectory>src/</sourceDirectory>
    </build>

Now try the command:

mvn dependency:tree

You should be looking at a somewhat simpler version of this:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Test MPG 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:2.4:tree (default-cli) @ test-mpg ---
[INFO] com.acme.myproj:test-proj:jar:1.0.0-SNAPSHOT
[INFO] +- junit:junit:jar:4.8.2:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.3:compile
[INFO] |  +- org.slf4j:slf4j-api:jar:1.6.3:compile
[INFO] |  - log4j:log4j:jar:1.2.16:compile
[INFO] +- org.seleniumhq.selenium:selenium-firefox-driver:jar:2.12.0:compile
[INFO] |  +- org.seleniumhq.selenium:selenium-remote-driver:jar:2.12.0:compile
[INFO] |  |  +- cglib:cglib-nodep:jar:2.1_3:compile
[INFO] |  |  - org.json:json:jar:20080701:compile
[INFO] |  - commons-io:commons-io:jar:2.0.1:compile
[INFO] +- org.seleniumhq.selenium:selenium-htmlunit-driver:jar:2.12.0:compile
[INFO] |  +- org.seleniumhq.selenium:selenium-api:jar:2.12.0:compile
[INFO] |  |  - com.google.guava:guava:jar:10.0.1:compile
[INFO] |  |     - com.google.code.findbugs:jsr305:jar:1.3.9:compile
[INFO] |  +- net.sourceforge.htmlunit:htmlunit:jar:2.9:compile
[INFO] |  |  +- xalan:xalan:jar:2.7.1:compile
[INFO] |  |  |  - xalan:serializer:jar:2.7.1:compile
[INFO] |  |  +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] |  |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  |  +- org.apache.httpcomponents:httpmime:jar:4.1.2:compile
[INFO] |  |  +- commons-codec:commons-codec:jar:1.4:compile
[INFO] |  |  +- net.sourceforge.htmlunit:htmlunit-core-js:jar:2.9:compile
[INFO] |  |  +- xerces:xercesImpl:jar:2.9.1:compile
[INFO] |  |  |  - xml-apis:xml-apis:jar:1.3.04:compile
[INFO] |  |  +- net.sourceforge.nekohtml:nekohtml:jar:1.9.15:compile
[INFO] |  |  +- net.sourceforge.cssparser:cssparser:jar:0.9.5:compile
[INFO] |  |  |  - org.w3c.css:sac:jar:1.3:compile
[INFO] |  |  - commons-logging:commons-logging:jar:1.1.1:compile
[INFO] |  - org.apache.httpcomponents:httpclient:jar:4.1.2:compile
[INFO] |     - org.apache.httpcomponents:httpcore:jar:4.1.2:compile
[INFO] - org.seleniumhq.selenium:selenium-java:jar:2.12.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-android-driver:jar:2.12.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-chrome-driver:jar:2.12.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-ie-driver:jar:2.12.0:compile
[INFO]    |  - net.java.dev.jna:jna:jar:3.3.0:compile
[INFO]    +- org.seleniumhq.selenium:selenium-iphone-driver:jar:2.12.0:compile
[INFO]    - org.seleniumhq.selenium:selenium-support:jar:2.12.0:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.051s
[INFO] Finished at: Wed Jan 25 00:18:37 EST 2012
[INFO] Final Memory: 6M/81M
[INFO] ------------------------------------------------------------------------

Pretty cool right? Check that pom.xml into the repository, and bask in the fact that anyone in your organization can check your project out and build it straight out of the gate. Now say three Hail Mary’s and ten Bless Me Father’s, and swear off eclipse dependency management for good. No seriously, please don’t do it, or it might be you we sacrifice next time instead of the goat 😀

Take care,
Matt

  Posted by Matt Holtom on January 25, 2012  /  Tags: , ,