Disable @Grab with a global AST transformation

On the Groovy mailing-list, we had an interesting question about how to disable annotations like @Grab, to prevent users from downloading third-party dependencies.
There are a few possibilities for that, but my favorite was to create a global AST transformation that would generate a compilation error if the @Grab annotation is found on an import.

I created a first small prototype within a script, but I used an injected local transformation to get everything working with a simple script. So I decided afterwards to do it for real this time, using a real project on Github with a proper global AST transformation this time.

If you checkout the project from Github, with:
git clone git@github.com:glaforge/disable-grab-sample.git
You then 'cd' in the disable-grab-sample directory, and you can run the following command to launch the Spock test showing the transformation in action:
./gradlew test
So what's inside that project? First of all, we need to create our AST transformation (I'll skip the imports for brevity):
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class DisableGrabTransformation implements ASTTransformation {
    void visit(ASTNode[] nodes, SourceUnit source) {
        def imports = source.AST.imports
        if (imports) {
            imports.each { anImport ->
                anImport.annotations.each { anno ->
                    if (anno.classNode.name in ['groovy.lang.Grab', 'groovy.lang.Grapes']) {
                            new SyntaxErrorMessage(
                                new SyntaxException('@Grab and @Grapes are forbidden',
                                anImport.lineNumber, anImport.columnNumber), 
We create a class implementing the ASTTransformation class with its visit() method. In that method, we access the "module" imports, and if there are any import, we iterate over each of them, checking if there are annotations put on them. If those imports are annotated with @Grab or @Grapes (ie. if the annotation class node's fully qualified class name are the FQN of the Grab and Grapes annotations), we then use the error collector to add a new syntax error, so that a compilation error is thrown by the Groovy compiler if ever someone uses @Grab in a Groovy script or class.

We need to wire in that global transformation. As they are not triggered by annotations like local transformations, we need do declare the transformation in a specific META-INF / services / org.codehaus.groovy.transform.ASTTransformation file, that will just contain one line: the fully qualified class name of the AST transformation that needs to be applied to each script and classes that will be compiled when this transformation is on the classpath. So our services file will just contain:
Now we need to see if our transformation is applied, and works as expected. For that purpose, we'll create a Spock test:
class DisableGrabSpec extends Specification {
    def "test"() {
        def shell = new GroovyShell()
        shell.evaluate '''
            import org.apache.commons.lang3.StringUtils
            println "hi"        
        def e = thrown(MultipleCompilationErrorsException)
        e.message.contains('@Grab and @Grapes are forbidden')
We are using the GroovyShell class to evaluate (and thus compile) a script that contains a @Grab instruction. When evaluating that script, a compilation error should be thrown, and we indeed assert that it's the case, and that our compilation error contains the message we've crafted.


Let's step back a little with a couple words about our build file:
apply plugin: 'groovy'

repositories {
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.6'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
    testCompile 'org.apache.ivy:ivy:2.4.0-rc1'
Not much to see here actually! We're just applying the groovy plugin, use the jcenter repository from Bintray. We're using Groovy-all, the Spock library for our test scope, as well as the Ivy library that's needed by the grape infrastructure for fully functioning, for retrieving artifacts.

With our Gradle build file, we can call the jar task to create a JAR that will contain the META-INF/services file, and as soon as you'll have that JAR on your classpath with that AST transformation, any script or class compiled with it will get compilation errors if @Grab is used.

Groovy Weekly #33

You probably remember Groovy and Gradle being selected in RebelLabs’ report as part of the 10 “kick-ass” technologies developers love? But I’m also happy to report that Groovy won the Geek Choice Award, showing how groovy Groovy is!

GR8Conf US 2014 last week is delivering tons of great videos and materials from all the speakers that came to the conference, and I hope you’ll find time to watch a few. My personal highlight was the great Q&A talk by Scott Hickey and Jim McGill on looking back into 9 years of using Groovy at a big US insurance company. Don’t miss it!

Also, if you missed GR8Conf US 2014, remember there’s SpringOne2GX fast approaching, and the early bird price for registering to the conference ends August 9th, so be quick!

Last but not least, the Groovy Weekly schedule will slow down a little this month, as I’ll take some vacations! So I might be skipping a beat or two in the agenda, but fear not, Groovy Weekly will come back in full force in September!





Google+ posts


Code snippets


Groovy receives Geek Choice Award

RebelLabs launched an annual study and awards to select 10 "kick-ass" technologies that developers love, and both Gradle and Groovy were among the winners!

You can learn more about this "kick-ass" technology study in the PDF report they crafted and which can be downloaded there (form filing required to download the PDF).

Details are available for:

It's always nice to see such reports and awards recognizing the great work and energy the Groovy and Gradle teams have been put into developing those technologies, and making developers around the world delighted to work with them.

Groovy Weekly #32

Live from GR8Conf US 2014, in Minneapolis, MN, USA! The code samples and slides of presentations are already flowing and are listed below in the presentations section. Lots of great content and talks to learn from.





Mailing-list posts


Code snippets




Groovy 2.3.5 out with upward compatibility

The Groovy team is pleased to announce the release of Groovy 2.3.5.

Groovy 2.3.5 is a bug fix release of our Groovy 2.3 branch.

You’ll find fixes for static compilation and type-checking, JSON serialization issues, markup template engine errors, and performance improvements.

We care a lot about backward and binary compatibility, but in this release, we also thought about upward compatibility, so that code compiled with a newer version can even run on an older runtime.

So we leveraged this version to add a new artifact, named groovy-backports-compat23. This artifact shouldn’t be necessary for most of you, but if you face an error like:

Caused by: java.lang.ClassNotFoundException: org.codehaus.groovy.runtime.typehandling.ShortTypeHandling
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)

in your project, then it means that a class has been compiled with Groovy 2.3+ but that you are trying to use it with an older version of Groovy. By adding this jar on classpath, you give a chance to your program to run. This may be particularily interesting for Gradle users that want to use a plugin built on Gradle 2+ on older versions of Gradle and face this error. Adding the following line to their build files should help:

buildscript {
   // ...
   dependencies {
        classpath 'some plugin build on gradle 2'
        classpath 'org.codehaus.groovy:groovy-backports-compat23:2.3.5'

Note that for now, this jar only contains the ShortTypeHandlingClass. Future versions may include more.

You can download Groovy 2.3.5 here:


The detailed JIRA release notes can be found here:


© 2012 Guillaume Laforge | The views and opinions expressed here are mine and don't reflect the ones from my employer.