Useful New Features in Groovy 1.7

At the Spring S2G Forum in Munich, the Groovy project lead Guillaume Laforge elaborated on Groovy 1.7’s new and noteworthy improvements. There was a huge interest in Groovy at the forum, which reinforces our strategic investment in Groovy as a capable and mature JVM-based language. Overall, I got a lot of insights and have here prepared some illustrative examples.

My project team at mgm invested heavily in Groovy and used it to great success for ad-hoc scripting stuff in general but also for serious server-side processing. A good example is Groovy’s built-in XML support: it reduces the code required to build, parse and navigate XML documents to a minimum and it is much easier to read and maintain than the equivalent Java code.

But let’s now focus on the improvements in Groovy 1.7. The 1.7 release notes provide a good overview, but some features are best understood by examples.

@Grab for Imports and Packages

By using the @Grab annotation, a script declares the libraries it depends on. @Grab utilizes the Ivy/Maven-based library referencing support of Grape (Groovy Advances Packaging Engine).

// grab a grape ...
@Grab(group='commons-net', module='commons-net', version='2.0')
import org.apache.commons.net.ftp.FTPClient
import java.math.RoundingMode.HALF_UP

host     = 'ftp.mgm-tp.com'
path     = '/public*'
user     = 'anonymous'
password = 'me@here.de'

def asGb = { bytes ->
    "${(bytes/1073741824).setScale(2, HALF_UP)} GB"
}

new FTPClient().with {
	connect host
	println replyString

	login user, password
	println replyString

	files = listFiles(path)
	files.each { println it }

	println "Total: ${asGb(files.collect{it.size}.sum())}"

	disconnect()
}

New ‘power assert’

The behaviour-driven spock testing framework committed a nice enhancement of the assert keyword that prints really useful diagnostic information about why the assertion failed:

a = 4
b = 7

assert a * b == a ** b

Exception thrown
18.03.2010 20:01:20 org.codehaus.groovy.runtime.StackTraceUtils sanitize
WARNING: Sanitizing stacktrace:
Assertion failed:

assert a * b == a ** b
       | | | |  | |  |
       4 | 7 |  4 |  7
         28  |   16384
           false

Customization of the Truth with asBoolean()

Implementing asBoolean() in an arbitrary class allows the developer to define what’s true or false:

class MyTruth {
	boolean value
	boolean asBoolean() { !value }
}

assert true  == new MyTruth(value: false) as Boolean
assert false == (boolean) new MyTruth(value: true)

Improved JDBC support: Sql.withBatch and Sql.withTransaction

The built-in JDBC support adds Sql.withTransaction and Sql.withBatch to execute JDBC statements under transaction control. For example, the task of inserting rows, which are read from a file containing a bunch of INSERT statements (one statement each line), is now expressed with a single line of code:

Sql.newInstance(
   'jdbc:postgresql://localhost:5432/sandbox',
   'sandbox', 'secret')
   .withBatch { jdbcStatement ->
       new File('inserts.sql').eachLine { sqlStatement ->
           jdbcStatement.addBatch sqlStatement
       }
   }

If you provide the JDBC driver on the classpath this statement could also be executed directly from the command line using the -e switch of the Groovy executable.

The documentation is not clear on transaction handling. The obvious idea to nest Sql.withBatch into Sql.withTransaction is not necessary: Sql.withBatch opens a transaction, sets auto-commit to false and rolls back the batch update if the execution raises an exception.

ASTs (Abstract Syntax Trees)

The ASTs are worth some words although introduced with Groovy 1.6 already. ASTs hook into the Groovy compiler before bytecode gets generated. They can be used to generate standard boiler-plate code one would have to write manually otherwise. Because this is done at compile-time, there is no runtime penalty.

This technique allows you to even change the semantics of your code, so this should be used with care. A simple use-case is the @Singleton annotation applied to a class: it generates the well-known code implementing the singleton (anti-)pattern:

@Singleton(lazy=true) class EvilSingleton()

generates

public class EvilSingleton {
    public static final EvilSingleton instance =
        new EvilSingleton();

    public static EvilSingleton getInstance() {
        return instance;
    }

    private EvilSingleton() {}
}

The Groovy Swing console has a built-in AST-browser and there is an AST builder class allowing to create ASTs programmatically.

Delegates and Mix-in Support

This new feature allow you to inject the behaviour of a different class into your class. It’s best illustrated in an example:

// Delegation
class Employee {
	def doTheWork() { 'done.' }
}

class Manager {
	@Delegate slave = new Employee()
}

def hardWorker = new Manager()
assert hardWorker.doTheWork() == 'done.'

// mix-in
class Dog { def bark() { 'Bark!' } }
class Cat {}

Cat.mixin Dog

assert 'Bark!' == new Cat().bark()

Support for inner and nested classes

This is not really exiting. But it improves the Java source compatibility. Closures are a better option, but if one prefers to copy/paste existing Java code this might be helpful.

Summary

Groovy is evolving constantly and is more than yet-another JVM language – it is the base for a growing list of projects using Groovy as a language:

  • Grails (Web application framework)
  • Griffon (Rich client application framework)
  • Gradle (Build system)
  • GPars (Concurrency in Groovy)
  • Spock (behaviour-driven testing framework)
  • Gaelyk (Google App Engine Toolkit)

A last tip: everyone not yet infected by putting more groove into the daily code – the Groovy Web Console is highly recommended.

Share

Leave a Reply

*