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
Table of Contents
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 thegrails.project.dependency.resolver
setting in grails-app/conf/BuildConfig.groovy
. The default setting is shown below:grails.project.dependency.resolver = "maven" // or ivy
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 }
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 } }
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
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'
group:name:version
.
If you are using Aether as the dependency resolution library, the Maven pattern of:<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>
runtime group: 'com.mysql', name: 'mysql-connector-java', version: '5.1.16'
group
- The group / organization (or groupId in Maven terminology)name
- The dependency name (or artifactId in Maven terminology)version
- The version of the dependencyextension
(Aether only) - The file extension of the dependencyclassifier
- The dependency classifierbranch
(Ivy only) - The branch of the dependencytransitive
(Ivy only) - Whether the dependency has transitive dependencies
runtime 'com.mysql:mysql-connector-java:5.1.16', 'net.sf.ehcache:ehcache:1.6.1'// Orruntime( [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 theexcludes
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' ])
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 thedependencyConfiguration
method to specify the configuration to use.provided("my.org:web-service:1.0") { dependencyConfiguration "api" }
"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 atuser.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"
grails.project.dependency.resolution = {
…
cacheDir "target/ivy-cache"
…
}
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 defaultgrailsHome()
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() }
repositories {
mavenRepo "http://repository.codehaus.org"
}
repositories { mavenRepo name: "Codehaus", root: "http://repository.codehaus.org" }
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
}
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 theoffline
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 cacheYou 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 = ivySettingsresolver libResolver
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 }
grails -classpath /path/to/jsch compile|run-app|etc.
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 acredentials
block:credentials { realm = ".." host = "localhost" username = "myuser" password = "mypass" }
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 thelog
method:// log level of the Aether or Ivy resolver, either 'error', 'warn',
// 'info', 'debug' or 'verbose'
log "warn"
grails.project.dependency.resolution = { … log "warn" checksums false … }
4.7.4 Inherited Dependencies
By default every Grails application inherits several framework dependencies. This is done through the line:inherits "global"
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 thedefaultDependenciesProvided
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 beforeinherits
, 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 ofgroup
, 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 anupdatePolicy
for the repository where the snapshot was resolved from, for example:repositories {
mavenCentral {
updatePolicy "interval:1"
}
}
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 snapshotsalways
- Always check for new snapshotsdaily
- 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" }
- maven local repository is searched, dependency not found
- maven network repository is searched, dependency is downloaded to the cache and used
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)
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)
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
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
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
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 ofgroup
,name
andversion
). 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
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
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 theexport
property of the dependency:compile('org.spockframework:spock-core:0.5-groovy-1.8') {
export = false
}
compile group: 'org.spockframework', name: 'spock-core',
version: '0.5-groovy-1.8', export: false
You can useexported = false
instead ofexport = 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" }
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'spom.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
..
}
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 targetsmvn 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
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
Themaven-install
command will install the Grails project or plugin artifact into your local Maven cache:grails maven-install
maven-deploy
Themaven-deploy
command will deploy a Grails project or plugin into a remote Maven repository:grails maven-deploy
<distributionManagement>
configuration within a pom.xml
or that you specify the id
of the remote repository to deploy to:grails maven-deploy --repository=myRepo
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") }
<remoteRepository id="myRepo" url="scp://localhost/www/repository"> <authentication username="..." privateKey="${user.home}/.ssh/id_dsa"/> </remoteRepository>
remoteRepository(id: "myRepo", url: "scp://localhost/www/repository") { authentication username: "...", privateKey: "${userHome}/.ssh/id_dsa" }
grails maven-deploy --repository=myRepo --protocol=webdav
- 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 thepom.xml
file. To change the version you can run the set-version
command:grails set-version 0.2
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 thegroupId
and version
are taken from the following properties in the GrailsPlugin.groovy
descriptor:String groupId = 'myOrg' String version = '0.1'
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 { … } … }
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"
}
}
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
}
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
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
--refresh-dependencies
switch to the command you use to build your projects.