From Rails to Grails: Dependency Management

In addition to being easy to rhyme, Groovy on Grails also shares a lot of the Ruby on Rails development philosophy, so my first question when I needed to upgrade a library in the Grails project I was working on was: What's the Grails version of Bundler?

Rails to Grails 101:

Bundler (now a standard part of any Rails installation) is a dependency management tool. You have a single file, called a Gemfile, where you list any of the gems (third party libraries) you're using. You can give specific version constraints if necessary. You can also tell Bundler where to look for gems that don't exist on your system. Most importantly, there's some great documentation and plenty of greattutorials floating around.

It turns out that the equivalent to Bundler's Gemfile is BuildConfig.groovy, and the equivalent to Rails' centralized gem repositories (such as rubygems.org) are the Maven repositories. Maven also seems to dictate the style in which you format dependencies, and it is not enabled by default for a Grails application. All together, these tools seem to cover the functionality that I was used to in Bundler.

Isn'€™t this all covered in the documentation?

Yes, but, perhaps due to differences in terminology and fewer tutorials floating around out there (or because I've been away from the Java development ecosystem for a while) it still wasn't clear to me how to properly add a single library into Grails' dependency management system.

To be fair, the official documentation on dependency management will tell you how to do this and more, especially if you'€™re up to date on all things Java…but it's the "and more" that I think is problematic for a Grails novice. I didn't want to learn everything about Grails' dependency management (at least not yet). I just wanted to understand it enough to add a file to it.

After taking the plunge and digging through Grails'€™ dependency management documentation, some guessing and testing, and a fair amount of Google sleuthing, I think I'€™ve got the tutorial I was initially hoping to find. Hopefully, it makes it a little easier to add dependency management to your first Grails application.

Why use dependency management?

Before going further, I should mention that every Grails project has a /lib folder and any .jar files placed in this folder will be added to the classpath of the application. So why use Grails' dependency management capability? Here are a few reasons (which apply to software development in general, though I'll use Grails specific terminology where appropriate):

  1. Dependencies are made explicit and can be commented on in a plaintext file (BuildConfig.groovy). This makes them easier to track in a repository. BuildConfig.groovy can be diff'd like any other source code file, but you can only track the additions or deletions of .jar files.
  2. If a library depends on other libraries, you don't have to track down all of these in order to update it; simply change a single version number in BuildConfig.groovy and the dependencies on other libraries will be resolved by Grails. If you were doing this without dependency management, you might be adding and removing several files yourself.
  3. You can do fancy advanced things like specifying 'latest.release' instead of '4.1.3' so that you don't even have to think about updates. The latest and greatest version of your jar file gets automatically updated when it's ready. You will want to be careful with this in production environments, as you don't want to accidentally introduce destabilizing changes into your application.

Setting up dependency management in Grails:

Let's start by taking a look at BuildConfig.groovy:

grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
//grails.project.war.file = "target/${appName}-${appVersion}.war"
grails.project.dependency.resolution = {
   // inherit Grails' default dependencies
   inherits("global") {
       // uncomment to disable ehcache
       // excludes 'ehcache'
   }
   log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
   repositories {
       grailsPlugins()
       grailsHome()
       grailsCentral()

       // uncomment the below to enable remote dependency resolution
       // from public Maven repositories
       //mavenLocal()
       //mavenCentral()
       //mavenRepo "http://snapshots.repository.codehaus.org"
       //mavenRepo "http://repository.codehaus.org"
       //mavenRepo "http://download.java.net/maven/2/"
       //mavenRepo "http://repository.jboss.com/maven2/"
   }
   dependencies {
       // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.

       // runtime 'mysql:mysql-connector-java:5.1.5'
   }
}

In the following steps, I'm going to use the concrete example of adding the httpcore-4.1.3.jar as a dependency to a Grails application that currently does not have dependency management set up for individual .jar libraries (remote plugin resolution is enabled by default for plugins). Replace references to that with whichever library you're trying to add:

Step 1: Uncomment lines 19-20 ("mavenLocal()" and "mavenCentral()").

Step 2: Go to http://search.maven.org and search for your library (in our case, "httpcore")

Step 3: There are quite a few entries, three of which have the ArtifactId we're interested in. One of them has been recently updated (August 14, 2011). The other two are a little over a year and almost five-years-old. Notice also that the August 14 entry has 17 versions associated with it, while the others don't even come close.

This is an area where it would be nice to have a little more information about the entries, but we'll have to work with what we're given. Judging from the recency of updates and the number of versions, we'll want to use the one with GroupId: org.apache.httpcomponents. The others definitely do not look like they're being actively maintained, especially when compared to this one.

Once you have it narrowed down to a single entry, you'll want to note the GroupId, ArtifactId, and one of the associated version numbers.

Step 4: Back in BuildConfig.groovy, under the "dependencies" section, make an entry in the format:

<dependency type> '<group id>:<artifact id>:<version number>'

Here's what it would look like as a "compile" dependency, which is probably the dependency type you'll want to use most of the time:

dependencies { 

 compile 'org.apache.httpcomponents:httpcore:4.1.3'

}

Step 5: If you're using IntelliJ, it will say your Grails configuration is outdated once you save the file and ask if you want to update the project structure. Et voila! The library will be downloaded and available for use in your project and dev environment.