Grails and Maven with no Maven 4

Posted by mjwall on January 10, 2009

According the Grails 1.1 Beta2 Release Notes, Grails 1.1 will have better Maven integration. I think that is great news allowing more integration between different java projects. It means this post may not relevant for very long though.

For all it’s complexity, I like maven. Sometimes it makes complex tasks easier, but not always. Here is my situation. There are several java projects already using maven. I am building a grails project that will be used by some of these projects. The easiest integration is to package up a war file and deploy it to our local maven repository. Nexus is great by the way. The other projects can include my grails project as a dependency. So what is the best way to do that? Right, I hear you. By hand. However, I need to automate this.

I looked at the grails-maven-plugin, but it is too much. I am not trying to mavenize my project, just deploy it to Nexus.

Luckily, there is as a cleaner answer. Creating scripts in grails is easy. Those scripts can use the maven-ant-tasks. Download the jar file and put it in your lib directory.

I’ll create 2 tasks, one handle the ‘maven install’ so I can test locally and one to handle ‘maven deploy’. We need a pom file, but instead of checking one in, let’s generate it from the project so it picks up the latest version etc. (Note, I studied the gant build file pretty closely). Here is an example of MavenInstall.groovy file from the scripts directory


 1 includeTargets
<< grailsScript ( "War" )
 2
 3 final antlibXMLns = ‘antlib:org.apache.maven.artifact.ant’
 4 final tempPomFile = "pom.xml"
 5
 6 target (preparePom : "Generate a temporary pom file") {
 7   depends(packageApp)  //so config.maven properties are loaded
 8   def writer = new StringWriter()
 9   def builder = new groovy.xml.MarkupBuilder(writer)
10   builder.project(xmlns:"http://maven.apache.org/POM/4.0.0",
11       ‘xmlns:xsi’:"http://www.w3.org/2001/XMLSchema-instance",
12       ‘xsi:schemaLocation’:"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd") {
13     ‘modelVersion’ ‘4.0.0′
14     ‘groupId’ ‘com.mjwall’
15     ‘artifactId’ grailsAppName
16     ‘packaging’ ‘war’
17     ‘version’ grailsAppVersion
18   }
19
20   File temp = new File(tempPomFile)
21   temp.write writer.toString()
22   def pom =ant."${antlibXMLns}:pom" ( file : tempPomFile , id : tempPomFile )
23   echo("Temporary pom file written to ${tempPomFile}, don’t forget to clean up")
24   return tempPomFile
25 }
26
27 target (deletePom : "Clean up the temporary pom file") {
28   new File(tempPomFile).delete()
29 }
30
31 target (getWarName : "Return the war name defined by the app configs") {
32   depends(war)
33   // bug in grails clean, doesn’t seem to delete war from a custom location defined by grails.war.destFile
34   return warName
35 }
36
37 target (default : "Install to local maven repository") {
38   depends(war)
39   def tempPom = preparePom()
40   ant."${antlibXMLns}:install" ( file : getWarName() ) { pom ( refid : tempPom ) }
41   deletePom()
42 }
43

Not too scary, but what is going on here? The default task you will see depends on the war file being built. A temporary pom is created using the value defined in application.properties. Then the maven-ant-task for install is run and the pom is deleted. Pretty simple huh? It is once you see an example anyway.

How about a maven deploy. Basically the same thing, except the pom needs to generate a distributionManagement section. I set up a couple of properties in my Config.groovy at the end called maven.remoteReleaseUrl and maven.remoteSnapshotUrl. Change the pom generate to include them. Looks like this


 6 target (preparePom :
"Generate a temporary pom file") {
 7   depends(packageApp)  //so config.maven properties are loaded
 8   def writer = new StringWriter()
 9   def builder = new groovy.xml.MarkupBuilder(writer)
10   builder.project(xmlns:"http://maven.apache.org/POM/4.0.0",
11       ‘xmlns:xsi’:"http://www.w3.org/2001/XMLSchema-instance",
12       ‘xsi:schemaLocation’:"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd") {
13     ‘modelVersion’ ‘4.0.0′
14     ‘groupId’ ‘com.mjwall’
15     ‘artifactId’ grailsAppName
16     ‘packaging’ ‘war’
17     ‘version’ grailsAppVersion
 20     ‘distributionManagement’ {
 21       ‘repository’ {
 22         ‘id’ ‘releases’
 23         ‘name’ ‘Internal Releases’
 24         ‘url’ config.maven.remoteReleaseUrl //defined in Config.groovy
 25       }
 26       ’snapshotRepository’ {
 27         ‘id’ ’snapshots’
 28         ‘name’ ‘Internal Snapshots’
 29         ‘url’ config.maven.remoteSnapshotUrl //defined in Config.groovy
 30         ‘uniqueVersion’ ‘true’
 31       }
18   }
19
20   File temp = new File(tempPomFile)
21   temp.write writer.toString()
22   def pom =ant."${antlibXMLns}:pom" ( file : tempPomFile , id : tempPomFile )
23   echo("Temporary pom file written to ${tempPomFile}, don’t forget to clean up")
24   return tempPomFile
25 }

Then instead of calling ant.”${antlibXMLns}:install”, call ant.”${antlibXMLns}:deploy”

Couple of notes. I did run into problems with my settings.xml file defined in ~/.m2, so I ended up creating one much like I did with the pom.xml. Only used when deploying, so it is not built for the install. Finally, because grails can only run one task per script, I DRYed up this whole thing with one file called MavenUtils that included all code for both install and deploy. Then, my MavenInstall file just loads the MavenUtils and calls install.

Wow, more explanation than I thought it would. And I guess the title is not entirely accurate. I am using maven, but not by calling the maven executable directly. Hopefully the new Maven/Grails integration will make this easier. Fingers crossed.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. John Stoneham Sat, 10 Jan 2009 23:09:04 EST

    There’s a lot I don’t understand about your setup and why a standard POM/plugin integration won’t do the job to create and deploy a WAR. My experience with Maven says the further you get away from tying things to its basic lifecycle the harder you make things.

    Looking forward to getting to understand this more closely next week.

  2. mjwall Sat, 10 Jan 2009 23:52:08 EST

    Yeah, I started by creating a POM but the organization of the code in a grails project doesn’t follow the src/target convention. Like I mentioned, the next stop was the Grails Maven Plugin, but I really didn’t need all that functionality. Grails builds a war just fine, all I needed was to deploy to nexus.

    See you next week

  3. Martin Mon, 07 Dec 2009 07:36:44 EST

    Do you still think this is the best way to do this? I am having trouble getting your script to use a parent POM. I think this is due to it not reading the repository locations in my settings.xml, which is a problem you noted. Could you expand on how you fixed that?

    Thanks.

  4. mjwall Mon, 07 Dec 2009 09:31:29 EST

    The pom that is generated in this example does not have a parent entry. To overcome the settings.xml issue, notice the builder for the deploy task creates a distributionManagement tag.

    I would not use for a multi-module project. The grails and maven integration has come a long way since I wrote this post. Try http://grails.org/Developer+-+Maven+Integration

    I wrote an article in the Nov issue of groovymag, see http://www.groovymag.com/main.issues.description/id=15/. The article is about mavenizing and existing grails project.

    Good Luck.

Comments