Tuesday, 17 May 2011

Dependency management - the gotchas

Love it or hate it Maven and its ilk have reached a level of use in mainstream Java projects that it is generally simpler to set up your project dependencies using pom.xml file(s) rather than manually obtaining the appropriate jars for the libraries that you need to make use of.

To bring in a simple standalone library that is self-contained (i.e. a single jar with no dependencies) this can be a simple matter of including a few lines of XML.

If your project is doing anything slightly non-trivial then there is a reasonable chance that it will, in turn, be making use of non-trivial dependencies - which depend on other third party libraries/jars. These are termed as transitive dependencies.

If you are not paying attention to the way transitive dependencies are defined in third party POM files, you can experience what a few people I generally respect for their software development experience refer to the phenomenon of "maven downloading the Internet".

Sonatype has circulated a blog post on that subject:

http://www.sonatype.com/people/2011/04/how-not-to-download-the-internet/

I would like to highlight a couple of gotchas, surprising things that can slip into your project if you don't pay enough attention.

Take for example the popular logging system log4j. If you simply specify in your POM file that you want log4j included in your project would you expect javax.mail to be brought along with it?

javax.mail then has a dependency on the activation framework - which could be another jar.

This is a relatively basic example of a system specifying dependencies for functionality that you might not actually make use of. In this case log4j can allow you to specify and SMTPAppender to send out email messages when significant events are logged.

I first tried to use the SMTPAppender several years ago on a project that wasn't making use of a depency management system, so I ended up scratching my head wondering why the application was silently failing every time that something should have been logged using the SMTPAppender. Any guesses what the problem was? - I hadn't deployed javax.mail or the activation framework jar.

The log4j example above might be considered as an example of how dependency management can stop you from making silly mistakes, so the example that follows will show how the opposite can also be true.

On a fairly recent project I have been making use of Jersey to expose RESTful web services with the JAX-RS standard.

Partway through the project I was following some posts on the user group and realised that the latest version would allow us to have our JSON objects represented in a much more efficient manner, so I simply updated the version number for the relevant dependencies in my project pom.xml file and set maven off to download and assemble a fresh artifact.

Everything went fine until I attempted to deploy the generated WAR file. Then, much to my surprise, I began to see a warning about the inclusion of a jar file containing the servlet API - something which Tomcat is thankfully clever enough to exclude from the classpath.

Using the m2eclipse Maven plugin for eclipse I was soon able to determine that the Jersey 1.6 server artifact included a dependency upon the servlet 3.0 API.

I received a prompt response to a post on the Jersey user group advising that the error would be corrected in the next nightly build, but I am reluctant to deploy with a nightly build.

I don't know about you, but I really don't like seeing warnings in my application logs. So after a few days of tolerating the warning and checking on the status of version 1.7 of Jersey I decided to eliminate the problem by overriding the dependency using an alternative scope in a dependencyManagement section of my project's POM file. Not ideal, but effective.

So, the main thing to take away from these cases is that you should take the time to examine what your project dependency tree looks like whenever you bring in a new dependency or change version of an existing dependency.