(Quick Reference)

4.7 Dependency Resolution - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith, Lari Hotari

Version: 2.3.8

4.7 Dependency Resolution

Grails features a dependency resolution DSL that lets you control how plugins and JAR dependencies are resolved.

You can choose to use Aether (since Grails 2.3) or Apache Ivy as the dependency resolution engine. Aether is the dependency resolution library used by the Maven build tool, so if you are looking for Maven-like behavior then Aether is the better choise. Ivy allows more flexibility if you wish to resolve jars from flat file systems or none HTTP repositories. Aether is the default dependency resolution engine for Grails applications since Grails 2.3.

To configure which dependency resolution engine to use you can specify the grails.project.dependency.resolver setting in grails-app/conf/BuildConfig.groovy. The default setting is shown below:

grails.project.dependency.resolver = "maven" // or ivy

You can then specify a grails.project.dependency.resolution property inside the grails-app/conf/BuildConfig.groovy file that configures how dependencies are resolved:

grails.project.dependency.resolution = {
   // config here
}

The default configuration looks like the following:

grails.servlet.version = "3.0" // Change depending on target container compliance (2.5 or 3.0)
grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
grails.project.work.dir = "target/work"
grails.project.target.level = 1.6
grails.project.source.level = 1.6
//grails.project.war.file = "target/${appName}-${appVersion}.war"

grails.project.fork = [ // configure settings for compilation JVM, note that if you alter the Groovy version forked compilation is required // compile: [maxMemory: 256, minMemory: 64, debug: false, maxPerm: 256, daemon:true],

// configure settings for the test-app JVM, uses the daemon by default test: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, daemon:true], // configure settings for the run-app JVM run: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false], // configure settings for the run-war JVM war: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false], // configure settings for the Console UI JVM console: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256] ]

grails.project.dependency.resolver = "maven" // or ivy grails.project.dependency.resolution = { // inherit Grails' default dependencies inherits("global") { // specify dependency exclusions here; for example, uncomment this to disable ehcache: // excludes 'ehcache' } log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose' checksums true // Whether to verify checksums on resolve legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility

repositories { inherits true // Whether to inherit repository definitions from plugins

grailsPlugins() grailsHome() mavenLocal() grailsCentral() mavenCentral() // uncomment these (or add new ones) to enable remote dependency resolution from public Maven repositories //mavenRepo "http://repository.codehaus.org" //mavenRepo "http://download.java.net/maven/2/" }

dependencies { // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g. runtime 'mysql:mysql-connector-java:5.1.24' compile 'org.springframework.integration:spring-integration-core:2.2.5.RELEASE' }

plugins { // plugins for the build system only } }

The details of the above will be explained in the next few sections.

4.7.1 Configurations and Dependencies

Grails features five dependency resolution configurations (or 'scopes'):
  • build: Dependencies for the build system only
  • compile: Dependencies for the compile step
  • runtime: Dependencies needed at runtime but not for compilation (see above)
  • test: Dependencies needed for testing but not at runtime (see above)
  • provided: Dependencies needed at development time, but not during WAR deployment

Within the dependencies block you can specify a dependency that falls into one of these configurations by calling the equivalent method. For example if your application requires the MySQL driver to function at runtime you can specify that like this:

runtime 'com.mysql:mysql-connector-java:5.1.16'

This uses the string syntax: group:name:version.

If you are using Aether as the dependency resolution library, the Maven pattern of:

<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>

You can also use a Map-based syntax:

runtime group: 'com.mysql',
        name: 'mysql-connector-java',
        version: '5.1.16'

Possible settings to the map syntax are:

  • group - The group / organization (or groupId in Maven terminology)
  • name - The dependency name (or artifactId in Maven terminology)
  • version - The version of the dependency
  • extension (Aether only) - The file extension of the dependency
  • classifier - The dependency classifier
  • branch (Ivy only) - The branch of the dependency
  • transitive (Ivy only) - Whether the dependency has transitive dependencies

As you can see from the list above some dependency configuration settings work only in Aether and some only in Ivy.

Multiple dependencies can be specified by passing multiple arguments:

runtime 'com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1'

// Or

runtime( [group:'com.mysql', name:'mysql-connector-java', version:'5.1.16'], [group:'net.sf.ehcache', name:'ehcache', version:'1.6.1'] )

Disabling transitive dependency resolution

By default, Grails will not only get the JARs and plugins that you declare, but it will also get their transitive dependencies. This is usually what you want, but there are occasions where you want a dependency without all its baggage. In such cases, you can disable transitive dependency resolution on a case-by-case basis:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    transitive = false
}

// Or runtime group:'com.mysql', name:'mysql-connector-java', version:'5.1.16', transitive:false

Disabling transitive dependency resolution only works with the Ivy dependency manager. Aether does not support disabling of transitive resolution, instead explicit exclusions are required (see below).

Excluding specific transitive dependencies

A far more common scenario is where you want the transitive dependencies, but some of them cause issues with your own dependencies or are unnecessary. For example, many Apache projects have 'commons-logging' as a transitive dependency, but it shouldn't be included in a Grails project (we use SLF4J). That's where the excludes option comes in:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    excludes "xml-apis", "commons-logging"
}

// Or runtime(group:'com.mysql', name:'mysql-connector-java', version:'5.1.16') { excludes([ group: 'xml-apis', name: 'xml-apis'], [ group: 'org.apache.httpcomponents' ], [ name: 'commons-logging' ])

As you can see, you can either exclude dependencies by their artifact ID (also known as a module name) or any combination of group and artifact IDs (if you use the Map notation). You may also come across exclude as well, but that can only accept a single string or Map:

runtime('com.mysql:mysql-connector-java:5.1.16',
        'net.sf.ehcache:ehcache:1.6.1') {
    exclude "xml-apis"
}

Using Ivy module configurations

Using the Ivy dependency manager (Aether not supported), if you use Ivy module configurations and wish to depend on a specific configuration of a module, you can use the dependencyConfiguration method to specify the configuration to use.

provided("my.org:web-service:1.0") {
    dependencyConfiguration "api"
}

If the dependency configuration is not explicitly set, the configuration named "default" will be used (which is also the correct value for dependencies coming from Maven style repositories).

Where are the JARs?

With all these declarative dependencies, you may wonder where all the JARs end up. They have to go somewhere after all. By default Grails puts them into a directory, called the dependency cache, that resides on your local file system at user.home/.grails/ivy-cache or user.home/.m2/repository when using Aether. You can change this either via the settings.groovy file:

grails.dependency.cache.dir = "${userHome}/.my-dependency-cache"

or in the dependency DSL:

grails.project.dependency.resolution = {
    …
    cacheDir "target/ivy-cache"
    …
}

The settings.groovy option applies to all projects, so it's the preferred approach.

4.7.2 Dependency Repositories

Remote Repositories

Initially your BuildConfig.groovy does not use any remote public Maven repositories. There is a default grailsHome() repository that will locate the JAR files Grails needs from your Grails installation. To use a public repository, specify it in the repositories block:

repositories {
    mavenCentral()
}

In this case the default public Maven repository is specified.

You can also specify a specific Maven repository to use by URL:

repositories {
    mavenRepo "http://repository.codehaus.org"
}

and even give it a name:

repositories {
    mavenRepo name: "Codehaus", root: "http://repository.codehaus.org"
}

so that you can easily identify it in logs.

Controlling Repositories Inherited from Plugins

A plugin you have installed may define a reference to a remote repository just as an application can. By default your application will inherit this repository definition when you install the plugin.

If you do not wish to inherit repository definitions from plugins then you can disable repository inheritance:

repositories {
    inherit false
}

In this case your application will not inherit any repository definitions from plugins and it is down to you to provide appropriate (possibly internal) repository definitions.

Offline Mode

There are times when it is not desirable to connect to any remote repositories (whilst working on the train for example!). In this case you can use the offline flag to execute Grails commands and Grails will not connect to any remote repositories:

grails --offline run-app

Note that this command will fail if you do not have the necessary dependencies in your local Ivy cache

You can also globally configure offline mode by setting grails.offline.mode to true in ~/.grails/settings.groovy or in your project's BuildConfig.groovy file:

grails.offline.mode=true

Local Resolvers

If you do not wish to use a public Maven repository you can specify a flat file repository:

repositories {
    flatDir name:'myRepo', dirs:'/path/to/repo'
}

Aether does not support the flatDir resolver or any custom file system resolvers. The above feature works only if you are using the Ivy dependency manager.

To specify your local Maven cache (~/.m2/repository) as a repository:

repositories {
    mavenLocal()
}

Custom Resolvers

If you are using the Ivy dependency manager (Aether does not support custom resolvers), then you can explicitly specify an Ivy resolver:

/*
 * Configure our resolver.
 */
def libResolver = new org.apache.ivy.plugins.resolver.URLResolver()
['libraries', 'builds'].each {

libResolver.addArtifactPattern( "http://my.repository/${it}/" + "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]")

libResolver.addIvyPattern( "http://my.repository/${it}/" + "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]") }

libResolver.name = "my-repository" libResolver.settings = ivySettings

resolver libResolver

It's also possible to pull dependencies from a repository using SSH. Ivy comes with a dedicated resolver that you can configure and include in your project like so:

import org.apache.ivy.plugins.resolver.SshResolver
…
repositories {
    ...

def sshResolver = new SshResolver( name: "myRepo", user: "username", host: "dev.x.com", keyFile: new File("/home/username/.ssh/id_rsa"), m2compatible: true)

sshResolver.addArtifactPattern( "/home/grails/repo/[organisation]/[artifact]/" + "[revision]/[artifact]-[revision].[ext]")

sshResolver.latestStrategy = new org.apache.ivy.plugins.latest.LatestTimeStrategy()

sshResolver.changingPattern = ".*SNAPSHOT"

sshResolver.setCheckmodified(true)

resolver sshResolver }

Download the JSch JAR and add it to Grails' classpath to use the SSH resolver. You can do this by passing the path in the Grails command line:

grails -classpath /path/to/jsch compile|run-app|etc.

You can also add its path to the CLASSPATH environment variable but be aware this it affects many Java applications. An alternative on Unix is to create an alias for grails -classpath ... so that you don't have to type the extra arguments each time.

Authentication

If your repository requires authentication you can configure this using a credentials block:

credentials {
    realm = ".."
    host = "localhost"
    username = "myuser"
    password = "mypass"
}

This can be placed in your USER_HOME/.grails/settings.groovy file using the grails.project.ivy.authentication setting:

grails.project.ivy.authentication = {
    credentials {
        realm = ".."
        host = "localhost"
        username = "myuser"
        password = "mypass"
    }
}

4.7.3 Debugging Resolution

If you are having trouble getting a dependency to resolve you can enable more verbose debugging from the underlying engine using the log method:

// log level of the Aether or Ivy resolver, either 'error', 'warn',
// 'info', 'debug' or 'verbose'
log "warn"

A common issue is that the checksums for a dependency don't match the associated JAR file, and so Ivy rejects the dependency. This helps ensure that the dependencies are valid. But for a variety of reasons some dependencies simply don't have valid checksums in the repositories, even if they are valid JARs. To get round this, you can disable Ivy's dependency checks like so:

grails.project.dependency.resolution = {
    …
    log "warn"
    checksums false
    …
}

This is a global setting, so only use it if you have to.

4.7.4 Inherited Dependencies

By default every Grails application inherits several framework dependencies. This is done through the line:

inherits "global"

Inside the BuildConfig.groovy file. To exclude specific inherited dependencies you use the excludes method:

inherits("global") {
    excludes "oscache", "ehcache"
}

4.7.5 Providing Default Dependencies

Most Grails applications have runtime dependencies on several jar files that are provided by the Grails framework. These include libraries like Spring, Sitemesh, Hibernate etc. When a war file is created, all of these dependencies will be included in it. But, an application may choose to exclude these jar files from the war. This is useful when the jar files will be provided by the container, as would normally be the case if multiple Grails applications are deployed to the same container.

The dependency resolution DSL provides a mechanism to express that all of the default dependencies will be provided by the container. This is done by invoking the defaultDependenciesProvided method and passing true as an argument:

grails.project.dependency.resolution = {

defaultDependenciesProvided true // all of the default dependencies will // be "provided" by the container

inherits "global" // inherit Grails' default dependencies

repositories { grailsHome() … } dependencies { … } }

defaultDependenciesProvided must come before inherits, otherwise the Grails dependencies will be included in the war.

4.7.6 Snapshots and Other Changing Dependencies

Configuration Changing dependencies

Typically, dependencies are constant. That is, for a given combination of group, name and version the jar (or plugin) that it refers to will never change. The Grails dependency management system uses this fact to cache dependencies in order to avoid having to download them from the source repository each time. Sometimes this is not desirable. For example, many developers use the convention of a snapshot (i.e. a dependency with a version number ending in “-SNAPSHOT”) that can change from time to time while still retaining the same version number. We call this a "changing dependency".

Whenever you have a changing dependency, Grails will always check the remote repository for a new version. More specifically, when a changing dependency is encountered during dependency resolution its last modified timestamp in the local cache is compared against the last modified timestamp in the dependency repositories. If the version on the remote server is deemed to be newer than the version in the local cache, the new version will be downloaded and used.

Be sure to read the next section on "Dependency Resolution Caching" in addition to this one as it affects changing dependencies.

All dependencies (jars and plugins) with a version number ending in -SNAPSHOT are implicitly considered to be changing by Grails. You can also explicitly specify that a dependency is changing by setting the changing flag in the dependency DSL (This is only required for Ivy, Aether does not support the 'changing' flag and treats dependencies that end with -SNAPSHOT as changing):

runtime ('org.my:lib:1.2.3') {
    changing = true
}

Aether and SNAPSHOT dependencies

The semantics for handling snapshots when using Aether in Grails are the same as those when using the Maven build tool. The default snapshot check policy is to check once a day for a new version of the dependency. This means that if a new snapshot is published during the day to a remote repository you may not see that change unless you manually clear out your local snapshot.

If you wish to change the snapshot update policy you can do so by configuring an updatePolicy for the repository where the snapshot was resolved from, for example:

repositories {
    mavenCentral {
        updatePolicy "interval:1"
    }
}

The above example configures an update policy that checks once a minute for changes. Note that that an updatePolicy like the above will seriously impact performance of dependency resolution. The possibly configuration values for updatePolicy are as follows:

  • never - Never check for new snapshots
  • always - Always check for new snapshots
  • daily - Check once a day for new snapshots (the default)
  • interval:x - Check once every x minutes for new snapshots

Ivy and Changing dependencies

For those used to Maven snapshot handling, if you use Aether dependency management you can expect the same semantics as Maven. If you choose to use Ivy there is a caveat to the support for changing dependencies that you should be aware of. Ivy will stop looking for newer versions of a dependency once it finds a remote repository that has the dependency.

Consider the following setup:

grails.project.dependency.resolution = {
    repositories {
        mavenLocal()
        mavenRepo "http://my.org/repo"
    }
    dependencies {
        compile "myorg:mylib:1.0-SNAPSHOT"
    }

In this example we are using the local maven repository and a remote network maven repository. Assuming that the local OI dependency and the local Maven cache do not contain the dependency but the remote repository does, when we perform dependency resolution the following actions will occur:

  • maven local repository is searched, dependency not found
  • maven network repository is searched, dependency is downloaded to the cache and used

Note that the repositories are checked in the order they are defined in the BuildConfig.groovy file.

If we perform dependency resolution again without the dependency changing on the remote server, the following will happen:

  • maven local repository is searched, dependency not found
  • maven network repository is searched, dependency is found to be the same "age" as the version in the cache so will not be updated (i.e. downloaded)

Later on, a new version of mylib 1.0-SNAPSHOT is published changing the version on the server. The next time we perform dependency resolution, the following will happen:

  • maven local repository is searched, dependency not found
  • maven network repository is searched, dependency is found to newer than version in the cache so will be updated (i.e. downloaded to the cache)

So far everything is working well.

Now we want to test some local changes to the mylib library. To do this we build it locally and install it to the local Maven cache (how doesn't particularly matter). The next time we perform a dependency resolution, the following will occur:

  • maven local repository is searched, dependency is found to newer than version in the cache so will be updated (i.e. downloaded to the cache)
  • maven network repository is NOT searched as we've already found the dependency

This is what we wanted to occur.

Later on, a new version of mylib 1.0-SNAPSHOT is published changing the version on the server. The next time we perform dependency resolution, the following will happen:

  • maven local repository is searched, dependency is found to be the same "age" as the version in the cache so will not be updated (i.e. downloaded)
  • maven network repository is NOT searched as we've already found the dependency

This is likely to not be the desired outcome. We are now out of sync with the latest published snapshot and will continue to keep using the version from the local maven repository.

The rule to remember is this: when resolving a dependency, Ivy will stop searching as soon as it finds a repository that has the dependency at the specified version number. It will not continue searching all repositories trying to find a more recently modified instance.

To remedy this situation (i.e. build against the newer version of mylib 1.0-SNAPSHOT in the remote repository), you can either:

  • Delete the version from the local maven repository, or
  • Reorder the repositories in the BuildConfig.groovy file

Where possible, prefer deleting the version from the local maven repository. In general, when you have finished building against a locally built SNAPSHOT always try to clear it from the local maven repository.

This changing dependency behaviour is an unmodifiable characteristic of the underlying dependency management system Apache Ivy. It is currently not possible to have Ivy search all repositories to look for newer versions (in terms of modification date) of the same dependency (i.e. the same combination of group, name and version). If you want this behavior consider switching to Aether as the dependency manager.

4.7.7 Dependency Reports

As mentioned in the previous section a Grails application consists of dependencies inherited from the framework, the plugins installed and the application dependencies itself.

To obtain a report of an application's dependencies you can run the dependency-report command:

grails dependency-report

By default this will generate reports in the target/dependency-report directory. You can specify which configuration (scope) you want a report for by passing an argument containing the configuration name:

grails dependency-report runtime

As of Grails 2.3 the dependency-report command will also output to the console a graph of the dependencies of an application. Example output it shown below:

compile - Dependencies placed on the classpath for compilation (total: 73)
+--- org.codehaus.groovy:groovy-all:2.0.6
+--- org.grails:grails-plugin-codecs:2.3.0
|    --- org.grails:grails-web:2.3.0
|         --- commons-fileupload:commons-fileupload:1.2.2
|         --- xpp3:xpp3_min:1.1.4c
|         --- commons-el:commons-el:1.0
|         --- opensymphony:sitemesh:2.4
|         --- org.springframework:spring-webmvc:3.1.2.RELEASE
|    --- commons-codec:commons-codec:1.5
|    --- org.slf4j:slf4j-api:1.7.2
+--- org.grails:grails-plugin-controllers:2.3.0
|    --- commons-beanutils:commons-beanutils:1.8.3
|    --- org.grails:grails-core:2.3.0
...

4.7.8 Plugin JAR Dependencies

Specifying Plugin JAR dependencies

The way in which you specify dependencies for a plugin is identical to how you specify dependencies in an application. When a plugin is installed into an application the application automatically inherits the dependencies of the plugin.

To define a dependency that is resolved for use with the plugin but not exported to the application then you can set the export property of the dependency:

compile('org.spockframework:spock-core:0.5-groovy-1.8') {
    export = false
}

In this case the Spock dependency will be available only to the plugin and not resolved as an application dependency. Alternatively, if you're using the Map syntax:

compile group: 'org.spockframework', name: 'spock-core',
     version: '0.5-groovy-1.8', export: false

You can use exported = false instead of export = false, but we recommend the latter because it's consistent with the Map argument.

Overriding Plugin JAR Dependencies in Your Application

If a plugin is using a JAR which conflicts with another plugin, or an application dependency then you can override how a plugin resolves its dependencies inside an application using exclusions. For example:

plugins {
    compile(":hibernate:$grailsVersion") {
        excludes "javassist"
    }
}

dependencies { runtime "javassist:javassist:3.4.GA" }

In this case the application explicitly declares a dependency on the "hibernate" plugin and specifies an exclusion using the excludes method, effectively excluding the javassist library as a dependency.

4.7.9 Maven Integration

When using the Grails Maven plugin with the Maven build tool, Grails' dependency resolution mechanics are disabled as it is assumed that you will manage dependencies with Maven's pom.xml file.

However, if you would like to continue using Grails regular commands like run-app, test-app and so on then you can tell Grails' command line to load dependencies from the Maven pom.xml file instead.

To do so simply add the following line to your BuildConfig.groovy:

grails.project.dependency.resolution = {
    pom true
    ..
}

The line pom true tells Grails to parse Maven's pom.xml and load dependencies from there.

4.7.10 Deploying to a Maven Repository

If you use Maven to build your Grails project, you can use the standard Maven targets mvn install and mvn deploy. If not, you can deploy a Grails project or plugin to a Maven repository using the release plugin.

The plugin provides the ability to publish Grails projects and plugins to local and remote Maven repositories. There are two key additional targets added by the plugin:

  • maven-install - Installs a Grails project or plugin into your local Maven cache
  • maven-deploy - Deploys a Grails project or plugin to a remote Maven repository

By default this plugin will automatically generate a valid pom.xml for you unless a pom.xml is already present in the root of the project, in which case this pom.xml file will be used.

maven-install

The maven-install command will install the Grails project or plugin artifact into your local Maven cache:

grails maven-install

In the case of plugins, the plugin zip file will be installed, whilst for application the application WAR file will be installed.

maven-deploy

The maven-deploy command will deploy a Grails project or plugin into a remote Maven repository:

grails maven-deploy

It is assumed that you have specified the necessary <distributionManagement> configuration within a pom.xml or that you specify the id of the remote repository to deploy to:

grails maven-deploy --repository=myRepo

The repository argument specifies the 'id' for the repository. Configure the details of the repository specified by this 'id' within your grails-app/conf/BuildConfig.groovy file or in your $USER_HOME/.grails/settings.groovy file:

grails.project.dependency.distribution = {
    localRepository = "/path/to/my/local"
    remoteRepository(id: "myRepo", url: "http://myserver/path/to/repo")
}

The syntax for configuring remote repositories matches the syntax from the remoteRepository element in the Ant Maven tasks. For example the following XML:

<remoteRepository id="myRepo" url="scp://localhost/www/repository">
    <authentication username="..." privateKey="${user.home}/.ssh/id_dsa"/>
</remoteRepository>

Can be expressed as:

remoteRepository(id: "myRepo", url: "scp://localhost/www/repository") {
    authentication username: "...", privateKey: "${userHome}/.ssh/id_dsa"
}

By default the plugin will try to detect the protocol to use from the URL of the repository (ie "http" from "http://.." etc.), however to specify a different protocol you can do:

grails maven-deploy --repository=myRepo --protocol=webdav

The available protocols are:

  • http
  • scp
  • scpexe
  • ftp
  • webdav

Groups, Artifacts and Versions

Maven defines the notion of a 'groupId', 'artifactId' and a 'version'. This plugin pulls this information from the Grails project conventions or plugin descriptor.

Projects

For applications this plugin will use the Grails application name and version provided by Grails when generating the pom.xml file. To change the version you can run the set-version command:

grails set-version 0.2

The Maven groupId will be the same as the project name, unless you specify a different one in Config.groovy:

grails.project.groupId="com.mycompany"

Plugins

With a Grails plugin the groupId and version are taken from the following properties in the GrailsPlugin.groovy descriptor:

String groupId = 'myOrg'
String version = '0.1'

The 'artifactId' is taken from the plugin name. For example if you have a plugin called FeedsGrailsPlugin the artifactId will be "feeds". If your plugin does not specify a groupId then this defaults to "org.grails.plugins".

4.7.11 Plugin Dependencies

You can declaratively specify plugins as dependencies via the dependency DSL instead of using the install-plugin command:

grails.project.dependency.resolution = {
    …
    repositories {
        …
    }

plugins { runtime ':hibernate:1.2.1' }

dependencies { … } … }

If you don't specify a group id the default plugin group id of org.grails.plugins is used.

Latest Integration

Only the Ivy dependency manager supports the "latest.integration" version. For Aether you can achieve a similar effect with version ranges.

You can specify to use the latest version of a particular plugin by using "latest.integration" as the version number:

plugins {
    runtime ':hibernate:latest.integration'
}

Integration vs. Release

The "latest.integration" version label will also include resolving snapshot versions. To not include snapshot versions then use the "latest.release" label:

plugins {
    runtime ':hibernate:latest.release'
}

The "latest.release" label only works with Maven compatible repositories. If you have a regular SVN-based Grails repository then you should use "latest.integration".

And of course if you use a Maven repository with an alternative group id you can specify a group id:

plugins {
    runtime 'mycompany:hibernate:latest.integration'
}

Plugin Exclusions

You can control how plugins transitively resolves both plugin and JAR dependencies using exclusions. For example:

plugins {
    runtime(':weceem:0.8') {
        excludes "searchable"
    }
}

Here we have defined a dependency on the "weceem" plugin which transitively depends on the "searchable" plugin. By using the excludes method you can tell Grails not to transitively install the searchable plugin. You can combine this technique to specify an alternative version of a plugin:

plugins {
    runtime(':weceem:0.8') {
        excludes "searchable" // excludes most recent version
    }
    runtime ':searchable:0.5.4' // specifies a fixed searchable version
}

You can also completely disable transitive plugin installs, in which case no transitive dependencies will be resolved:

plugins {
    runtime(':weceem:0.8') {
        transitive = false
    }
    runtime ':searchable:0.5.4' // specifies a fixed searchable version
}

4.7.12 Caching of Dependency Resolution Results

As a performance optimisation, when using Ivy (this does not apply to Aether), Grails does not resolve dependencies for every command invocation. Even with all the necessary dependencies downloaded and cached, resolution may take a second or two. To minimise this cost, Grails caches the result of dependency resolution (i.e. the location on the local file system of all of the declared dependencies, typically inside the dependency cache) and reuses this result for subsequent commands when it can reasonably expect that nothing has changed.

Grails only performs dependency resolution under the following circumstances:

  • The project is clean (i.e. fresh checkout or after grails clean)
  • The BuildConfig.groovy file has changed since the last command was run
  • The --refresh-dependencies command line switch is provided to the command (any command)
  • The refresh-dependencies command is the command being executed

Generally, this strategy works well and you can ignore dependency resolution caching. Every time you change your dependencies (i.e. modify BuildConfig.groovy) Grails will do the right thing and resolve your new dependencies.

However, when you have changing or dynamic dependencies you will have to consider dependency resolution caching.

{info} A changing dependency is one whose version number does not change, but its contents do (like a SNAPSHOT). A dynamic dependency is one that is defined as one of many possible options (like a dependency with a version range, or symbolic version number like latest.integration). {info}

Both changing and dynamic dependencies are influenced by the environment. With caching active, any changes to the environment are effectively ignored. For example, your project may not automatically fetch the very latest version of a dependency when using latest.integration. Or if you declare a SNAPSHOT dependency, you may not automatically get the latest that's available on the server.

To ensure you have the correct version of a changing or dynamic dependency in your project, you can:

  • clean the project
  • run the refresh-dependencies command
  • run any command with the --refresh-dependencies switch; or
  • make a change to BuildConfig.groovy

If you have your CI builds configured to not perform clean builds, it may be worth adding the --refresh-dependencies switch to the command you use to build your projects.