27 May 2014

Recently, I have been spending my time converting Grails applications to use Maven POM files for dependency management. For the last couple releases, Grails has included the ability to rely on a POM file for dependencies, instead of declaring the repositories and dependencies directly in your grails-app/conf/BuildConfig.groovy. This works exactly as expected — that is Grails retrieves the dependency declarations from the POM file and uses them to build/run the application when using the Grails CLI. However, there is a bug in the way Grails dynamically validates and uses the POM file. I came across this issue when attempting to use the JDK version-based profile activation that I discussed in a previous post. When a JDK-activated profile is present in the POM file used by Grails, the application will report the following error:

[ERROR] Failed to determine Java version for profile java8 @ <GAV of the POM file containing the profile>
        at org.apache.maven.model.building.DefaultModelProblemCollector.newModelBuildingException(DefaultModelProblemCollector.java:195)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:416)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:368)
        at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:359)
        at grails.util.BuildSettings.doResolve(BuildSettings.groovy:513)
        at grails.util.BuildSettings.doResolve(BuildSettings.groovy)
        at grails.util.BuildSettings$_getDefaultBuildDependencies_closure17.doCall(BuildSettings.groovy:774)
        at grails.util.BuildSettings$_getDefaultBuildDependencies_closure17.doCall(BuildSettings.groovy)
        at grails.util.BuildSettings.getDefaultBuildDependencies(BuildSettings.groovy:768)
        at grails.util.BuildSettings.getBuildDependencies(BuildSettings.groovy:673)

At first, I was confused. This same error does not happen if you use the Grails Maven Plugin to build/run your application — it only happens when using the Grails CLI. I also know that Grails ⇐ 2.3.X does not support Java 8, so maybe it was related to that? After looking at the Grails source (and the stacktrace), I found some interesting code in the JdkVersionProfileActivator:

String version = context.getSystemProperties().get( "java.version" );

if ( version == null || version.length() <= 0 )
{
    problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE )
            .setMessage( "Failed to determine Java version for profile " + profile.getId() )
            .setLocation( activation.getLocation( "jdk" ) ) );
    return false;
}

The Maven code checks the java.version system property to determine which version of the JDK is in use to see if it matches up with the requested profile. After some additional digging in the Grails source, I discovered that the AetherDependencyManager programmatically builds a Maven DefaultModelBuildingRequest, but does not pass the system properties from the JVM to the request. Therefore, when the JdkVersionProfileActivator attempts to look at the value of the java.version system property, there are no properties to inspect! I made a quick change to the AetherDependencyManager class to add the setting of the system properties:

final modelRequest = new DefaultModelBuildingRequest()
modelRequest.setPomFile(pomFile)
modelRequest.setSystemProperties(System.properties)
modelRequest.setModelResolver(new GrailsModelResolver(repositorySystem, session, repositories))

After re-testing with the modified code, the Grails happily accepted the JDK-based Maven profile without exception. I have created a pull request, which has been merged into the 2.4.x version of Grails. If you need access to this fix earlier, I would suggest creating a branch off of the tagged version of Grails that you are currently using and cherry-picking the commit that contains the change. You can then build the grails-aether JAR and drop it into the dist folder of your Grails installation to consume the change.

comments powered by Disqus