Skip to end of metadata
Go to start of metadata

Abstract 

The script GroovyWrapper described on this page, creates a self-excutable jar from a groovy script.

Description

The script GroovyWrapper allows you to distribute your groovy script as a standalone self-executable jar.

The generated jar contains the compiled groovy script, and the groovy embeddable jar. Moreover the jar's main-class is set to the compiled groovy script.

Having this self-executable jar, you can start the groovy script, like java -jar MyScriptAsJar.jar

Groovy Wrapper Script

The Groovy Wrapper Script below is quite simple.

First it parses its command line arguments using groovy's CliBuilder.

  • The argument -m defines the groovy script name, w/o {{.class} suffix
  • The optional argument -d allows defines the jar fileame, by default the jar filename is build as {{
    groovy-script-name.jar}}.
  • The argument -c compile the groovy script in the GroovyWrapper. Use this argument if your groovy script is not compiled by some build process.

Next if checks the arguments, compiles the script if specified.
Then the environment variable GROOVY_HOME is looked up for calculating the file location of the groovy embeddable jar.

Finnally GroovyWrapper builds the jar using AntBuilder.

The source of the GroovyWrapper script:

/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Wrap a script and groovy jars to an executable jar
 */
def cli = new CliBuilder()
cli.h( longOpt: 'help', required: false, 'show usage information' )
cli.d( longOpt: 'destfile', argName: 'destfile', required: false, args: 1, 'jar destintation filename, defaults to {mainclass}.jar' )
cli.m( longOpt: 'mainclass', argName: 'mainclass', required: true, args: 1, 'fully qualified main class, eg. HelloWorld' )
cli.c( longOpt: 'groovyc', required: false, 'Run groovyc' )

//--------------------------------------------------------------------------
def opt = cli.parse(args)
if (!opt) { return }
if (opt.h) {
  cli.usage();
  return
}

def mainClass = opt.m
def scriptBase = mainClass.replace( '.', '/' )
def scriptFile = new File( scriptBase + '.groovy' )
if (!scriptFile.canRead()) {
   println "Cannot read script file: '${scriptFile}'"
   return
}
def destFile = scriptBase + '.jar'
if (opt.d) {
  destFile = opt.d
}

//--------------------------------------------------------------------------
def ant = new AntBuilder()

if (opt.c) {
  ant.echo( "Compiling ${scriptFile}" )
  org.codehaus.groovy.tools.FileSystemCompiler.main( [ scriptFile ] as String[] )
}

def GROOVY_HOME = new File( System.getenv('GROOVY_HOME') )
if (!GROOVY_HOME.canRead()) {
  ant.echo( "Missing environment variable GROOVY_HOME: '${GROOVY_HOME}'" )
  return
}

ant.jar( destfile: destFile, compress: true, index: true ) {
  fileset( dir: '.', includes: scriptBase + '*.class' )

  zipgroupfileset( dir: GROOVY_HOME, includes: 'embeddable/groovy-all-*.jar' )
  zipgroupfileset( dir: GROOVY_HOME, includes: 'lib/commons*.jar' )
  // add more jars here

  manifest {
    attribute( name: 'Main-Class', value: mainClass )
  }
}

ant.echo( "Run script using: \'java -jar ${destFile} ...\'" )

Example

An example for using GroovyWrapper:

Say you have groovy script HelloWorld.groovy, use GroovyWrapper for building HelloWorld.jar, as follows:

$ groovy GroovyWrapper -c -m HelloWorld

GroovyWrapper will compile the script HelloWorld.groovy to HelloWorld.class, and creates a self-executable jar HelloWorld.jar.

Now you can use the HelloWorld.jar for launching the HelloWorld script, simply by running:

$ java -jar HelloWorld.jar

  • No labels

4 Comments

  1. Just a little patch to make GroovyWrapper Java 1.4 compatible.

    Indeed, in Java 1.4, "System.getenv()" is deprecated, so, if you need to use this java version, then apply the patch below :

    // ** BEGIN : PATCH FOR JAVA 1.4 **
    // Old code :
    //   def GROOVY_HOME = new File( System.getenv('GROOVY_HOME') )

    // New code :  
    if (System.getProperty("java.version").startsWith("1.4")) {
       GROOVY_HOME = new File( System.getProperty('groovy.home') )
    } else {
       GROOVY_HOME = new File( System.getenv('GROOVY_HOME') )
    }
    // ** END : PATCH FOR JAVA 1.4 **


  2. The logic around checking GROOVY_HOME is not quite right. If the GROOVY_HOME environment variable is not set the script explodes with a NullPointerException.

    Should probably check its existence before trying to use it in new File( ... ). Also means that if the variable is set but unreadable then the error message is a little misleading.

  3. Good script, just what I was looking for. I did som enhancements that allows for bundling all grape dependencis in jar. Jar can then be executed with java -Dgroovy.grape.enable=false -jar <destfile>.jar. The following was added:

    def grapes = [] as HashSet
    ant.echo("Resolving grape dependencies")
    def scriptCode = scriptFile.text
    matcher = (scriptCode =~ /(@Grab\('(.*):(.*):(.*)'\))/)
    matcher.each() { dep ->
    def uri = groovy.grape.Grape.resolve([groupId: dep[2], artifactId: dep[3], version: dep[4]])
    if (uri?.size()>0) {
    uri.each() { f ->
    ant.echo("Found dependency: ${f.toString()}")
    grapes << f
    }
    }
    }

    ant.jar( destfile: destFile, compress: true, index: true ) {
    fileset( dir: '.', includes: scriptBase + '*.class' )
     zipgroupfileset( dir: GROOVY_HOME, includes: 'embeddable/groovy-all-*.jar' )
    zipgroupfileset( dir: GROOVY_HOME, includes: 'lib/commons*.jar' )
     grapes.each() { grape ->
    def grapeFile = new File(grape)
    if (grapeFile.exists()) {
    zipgroupfileset(dir: grapeFile.parent, includes: grapeFile.name)
    }
    }
  4. Andreas, thanks for providing that.  At some point (Groovy 2.0?), the syntax for groovy.grape.Grape.resolve changed, and now takes an "args" map as its first parameter.  As a result, you need to change the syntax to:

    def uri = groovy.grape.Grape.resolve([autoDownload:true], [groupId: dep[2], artifactId: dep[3], version: dep[4]])