Details
Description
At present, Grails has the ability to create a skinny WAR by making use of the "grails war --nojars" command. However, this excludes all libraries from the WEB-INF/lib folder. Based on some of the discussions in SpringSource support cases 9372 and 8627, I would like to request the addition of a new feature.
Objective:
Within our development environment, I have noticed that grails applications tend to use a large amount of PermGen space. Basically, the deployment of a single grails application appears to load the core libraries and other dependencies consuming X MB in PermGen. The deployment of a second grails application appears to result in the same libraries being loaded into PermGen consuming approximately 2 * X Mb. A third grails application appears to consume 3 * X MB, with the trend continuing as more grails applications are deployed.
This tends to make the use of grails applications packaged by means of the standard mechanism more memory intensive and unsuitable for use with things like, portlet development (e.g. in Liferay). The deployment of a large number of grails applications to a single application server becomes more of a problem too as the PermGen space needs to be resized to a large value. What I would like to do is shared all of the core libraries identified in GRAILS_HOME/src/java/org/codehaus/groovy/grails/resolve/IvyDependencyManager.groovy, getDefaultDependencies(String grailsVersion) via an application server's sharing mechanism, e.g. shared loader in tomcat.
The idea would be that the standard libraries are deployed via the shared loader and when the "grails war" command is executed to package a grails application, only the dependent libraries needed for a plugin are packaged in WEB-INF/lib.
Possible solution:
Modify the dependency DSL to be able to do something similar to the following:
grails.project.dependency.resolution = {
// Grails' default dependencies are provided by shared libraries
inherits( "provided" )
where "provided" indicates the use of something like GRAILS_HOME/src/java/org/codehaus/groovy/grails/resolve/IvyDependencyManager.groovy, getProvidedDependencies(String grailsVersion) - basically getDefaultDependencies(..) with the compile and runtime scopes changed to provided.
As an added convenience, it may be possible to provide a new grails target that will automatically package the core dependencies in a format suitable for distribution to the shared loader directory.
Solution discussion:
At present, you can make use of the exclude mechanism, but this is very labour intensive and prone to errors. Problems would also exist with upgrades where the user would have to manually ensure that dependencies are changed to reflect the current upgrade status. The process involves using
grails.project.dependency.resolution = {
// inherit Grails' default dependencies
inherits( "global" ) {
// uncomment to disable ehcache
excludes ( "groovy-all",
"commons-beanutils",
"commons-el",...
..exclude all core items
}
dependencies {
// Core dependencies now provided via tomcat shared library.
provided("org.codehaus.groovy:groovy-all:1.6.7") {
excludes 'jline'
}
provided("commons-beanutils:commons-beanutils:1.8.0", "commons-el:commons-el:1.0", "commons-validator:commons-validator:1.3.1") {
excludes "commons-logging", "xml-apis"
}
provided ("aopalliance:aopalliance:1.0",
"commons-codec:commons-codec:1.3",
"commons-collections:commons-collections:3.2.1",...
}
... Add all core items back in as dependencies.
The proposed solution would remove this level of complexity and ensure that if you upgrade to grails 1.3.3, the relevant core dependencies are maintained within the code based, preventing the problem where BuildConfig.groovy may still be referencing older dependencies, e.g. spring-component-3.0.x-1.jar instead of the new spring-component-3.0.x.jar.
Upgrading to a new grails version would then just require the deployment of the new core dependencies to the shared loader directory with the standard "grails upgrade" operation working seamlessly on the project.
I have the feature working but I am not sure about the proposed syntax. inherits('provided') doesn't seem right. What about one of these?...
grails.project.dependency.resolution = { defaultDependenciesProvided = true ... }grails.project.dependency.resolution = { defaultDependenciesProvided() ... }