Command Line Scripting

Command Line Scripting (Since 0.4)

Basics

Grails supports command line scripting using a Groovy ant wrapper called Gant

A Gant script is essentially a Groovy script that defines special "tasks" that can depend
on each other and invoke other tasks (just like Ant). The bonus is that your build files
are written in Groovy not XML.

A basic Gant script in Grails looks like this:

MyScript.groovy
task('default':"The task description") {
	depends(clean, compile)
	jar()
}
task(clean:"Delete stuff") { Ant.delete(dir:"build") }
task(compile:"Compile stuff") {
	Ant.mkdir(dir:"build/classes")
	Ant.javac(srcdir:"src", destdir:"build/classes")
}
task(jar:"Package stuff") {
	Ant.jar(basedir:"build/classes", destfile:"build/myproject.jar")
}

The above of course is a very simple build, as your build gets more complex the power of Groovy
comes in handy. Note the use of the implicit "Ant" instance

Using Ant from a Gant script

You can use Ant from a Gant script using the implicit Ant variable. For instance consider this example from the Compile.groovy Gant script in $GRAILS_HOME/script:

task ('default': "Performs compilation of Java sources") {
	compile()
}

task(compile : "Implementation of compilation phase") {
  println "Compiling sources.."
  Ant.sequential {
    mkdir(dir:"${basedir}/web-app/WEB-INF/classes")
       path(id:"classpath") {
          fileset(dir:"lib")
          fileset(dir:"${grailsHome}/lib")
          fileset(dir:"${grailsHome}/dist")
          fileset(dir:"${basedir}/web-app/WEB-INF/classes")
       }
       javac(srcdir:"${basedir}/src/java",
             destdir:"${basedir}/web-app/WEB-INF/classes",
             classpathref:"classpath",debug:"on",
             deprecation:"on", optimize:"off")

  }
}

Here the script uses the Ant variable in combination with the sequential method which allows you to omit the Ant prefix before each Ant call such as mkdir, javac and so on

Creating & Executing a Script in Grails

You can create a Gant script in Grails by executing the command:

grails create-script MyScript

The above will create a Gant script in $PROJECT_HOME/scripts, but this is not the only place
they can live as we will see in the next section.

To execute the above script you can type the command:

grails my-script

What happens here is that Grails uses a convention to convert the command number (in lower case, separated by
underscores) into the script name (in camel case) and then invokes the "default" task defined
within the script.

Where do Gant scripts live?

In Grails Gant scripts can live in a number of places defined below:

  • $GRAILS_HOME/scripts
  • $PROJECT_HOME/scripts
  • $USER_HOME/.grails/scripts
  • $PROJECT_HOME/plugins/*/scripts

If there are multiple scripts of the same name in these locations Grails will prompt you for
a choice of which one you want to execute. In other words, one script never overrides another.

Useful Grails provided Scripts

Grails provides a number of scripts that are built in that can be re-used. Within the
$GRAILS_HOME/scripts directory you will find these. For example the Compile.groovy
script allows you to depend on Grails' compilation step or the Package.groovy script
allows you to depend on Grails' packaging step (which involves the generation of web.xml
on the fly)

To rely on these in your code you need to "include" them in your Gant script. The general
way to do this is via establishing the GRAILS_HOME environment variable:

grailsHome = Ant.project.properties."environment.GRAILS_HOME"
includeTargets << new File ( "${grailsHome}/scripts/Package.groovy" )

task('default':"My Funky Script") {
	depends( package )
}

in the example above we include the Package.groovy script and depend on its package
task to ensure Grails is packaged correctly before executing our task.

Setting up the Grails classpath

Another useful task to depend on is within Init.groovy called classpath that does the job
of setting up the classpath using Groovy's RootLoader so that referencing within the lib, classes
or grails-app dir doesn't throw a ClassNotFoundException:

grailsHome = Ant.project.properties."environment.GRAILS_HOME"
includeTargets << new File ( "${grailsHome}/scripts/Init.groovy" )

task('default':"My Funky Script") {
	depends( classpath )

	// reference a Grails class here
}

If you don't depend on this task then an exception is thrown. Note that this is done because some
tasks need to delete classes (like Clean.groovy) and certain systems (like Windows) disallow this
if they have been loaded by the JVM

Bootstrapping Grails inside a Gant script

Sometimes it is useful to bootstrap the whole Grails environment inside a Grails script. This
is what some of the scaffolding and unit testing task do for example. To do this you can depend
on the Bootstrap.groovy script:

import org.springframework.orm.hibernate3.*

grailsHome = Ant.project.properties."environment.GRAILS_HOME"
includeTargets << new File ( "${grailsHome}/scripts/Bootstrap.groovy" )

task('default':"My Funky Script") {
	depends( configureProxy, packageApp, classpath )

        // configure context class loader to have access to domain classes 
	classLoader = new URLClassLoader([classesDir.toURI().toURL()] as URL[], rootLoader)
        Thread.currentThread().setContextClassLoader(classLoader)

        // configure and load application
        bootstrap()

	def bookClass = grailsApp.getDomainClass("Book").getClazz()

	def sf = appCtx.getBean("sessionFactory")

	def template = new HibernateTemplate(sf)

	def books = template.loadAll(bookClass)
	books.each { println it.title }
}

What the Bootstrap.groovy script does is place a "grailsApp" variable which is an instance
of [GrailsApplication] and a "appCtx" which is the Spring [ApplicationContext]

Labels

 
(None)
  1. Feb 01, 2008

    Dave Fuller says:

    Shouldn't there be a way to execute scripts through the shell environment so tha...

    Shouldn't there be a way to execute scripts through the shell environment so that it is relatively simple to, say, import/export/manipulate data in the database?

    It is far too much of a pain to create a Gant script, get the necessary includes and depends and calls, find the right beans from application context and then start doing real, actual work when everything you should need for this sort of task is already present in the shell environment.  However, at present there seems to be no such mechanism available.

     
    As an aside, this page is sadly out of date.  Perhaps if it were brought up to date, it would make more sense.  Examples include "task" vs "target" and "application" vs "appCtx".

  2. Feb 01, 2008

    Matthew Lachman says:

    As an aside, this page is sadly out of date.&nbsp; Perhaps if it were brought up...

    As an aside, this page is sadly out of date.  Perhaps if it were brought up to date, it would make more sense.  Examples include "task" vs "target" and "application" vs "appCtx".

    One of the beautiful things about the Wiki format is if anything is out of date, anyone can update it - including you!