Skip to content

Toro Cloud Dev Center


Customizing Groovy compilation

In Groovy, classes and scripts are compiled to a certain compiler configuration:

Whether you are using groovyc to compile classes or a GroovyShell, for example, to execute scripts, under the hood, a compiler configuration is used. This configuration holds information like the source encoding or the classpath but it can also be used to perform more operations like adding imports by default, applying AST transformations transparently or disabling global AST transformations.

This compiler configuration can be changed and this still holds true even in Martini's development environment. In fact, Martini itself uses a compilation customizer by default (defined in <martini-home>/conf/MartiniGroovyCompilerConfig.groovy) to apply the following changes to your Groovy classes and scripts when they're compiled from Martini:

  • Transparently import the following packages:
    • io.toro.martini.core.service.annotation.*
    • io.toro.martini.core.security.annotation.*
    • io.toro.martini.core.api.*
    • io.toro.martini.tracker.model.*
    • groovyx.net.http.*
    • groovy.json.*
    • wslite.soap.*
    • javax.validation.*
    • javax.validation.constraints.*
    • io.swagger.annotations.*
    • org.springframework.web.bind.annotation.*
    • org.springframework.security.access.annotation.*
    • javax.xml.bind.annotation.*
    • io.toro.martini.core.util.*
    • io.toro.gloop.*
    • io.toro.gloop.engine.*
    • io.toro.gloop.object.*
    • io.toro.gloop.object.property.*
    • io.toro.gloop.exception.*
    • io.toro.gloop.annotation.*
  • Parse Groovydoc or Javadoc and implicitly add @Documented annotations. The @Documented annotation allows for the automatic population of Swagger annotations and generation of Swagger API definitions.

Take the following snippet below for example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package endpointServices.groovy

@RequestMapping('SQLOnlinerExamples')
class SQLExamples {

    /**
     * Martini allows you to easily get a <a href="http://docs.groovy-lang.org/docs/latest/html/api/groovy/sql/Sql.html">Groovy SQL</a>
     * object that's already connected to a Martini JDBC Connection.
     * This script shows how easy it is to expose a RESTful api that uses a database to add a record.
     * @param person The person to add. This example uses a strongly typed object that contains JAXB and validation annotations.
     */
    @ResponseBody
    @RequestMapping(value = 'sql/add-person', method = [RequestMethod.POST])
    APIResponse addPerson(@RequestParam(value = 'dataSource', required = true) DataSource dataSource,
                          @Valid @RequestBody Person person) {
        // More code...
    }
}

Because of Martini's default compiler configuration, it will be transformed to something roughly like the snippet below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package endpointServices.groovy;

import io.toro.martini.core.service.annotation.*
import io.toro.martini.core.security.annotation.*
import io.toro.martini.core.api.*
import io.toro.martini.tracker.model.*
import groovyx.net.http.*
import groovy.json.*
import wslite.soap.*
import javax.validation.*
import javax.validation.constraints.*
import io.swagger.annotations.*
import org.springframework.web.bind.annotation.*
import org.springframework.security.access.annotation.*
import javax.xml.bind.annotation.*
import io.toro.martini.core.util.*
import io.toro.gloop.*
import io.toro.gloop.engine.*
import io.toro.gloop.object.*
import io.toro.gloop.object.property.*
import io.toro.gloop.exception.*
import io.toro.gloop.annotation.*

@RequestMapping({'SQLOnlinerExamples'})
@Documented
@Path('SQLOnlinerExamples')
@Api('SQLOnlinerExamples')
class SQLExamples {

    @ResponseBody
    @RequestMapping(value = 'sql/add-person', method = [RequestMethod.POST])
    @Documented("Martini allows you to easily get a <a href=\"http://docs.groovy-lang.org/docs/latest/html/api/groovy/sql/Sql.html\">Groovy SQL</a>\nobject that's already connected to a Martini JDBC Connection.\nThis script shows how easy it is to expose a RESTful api that uses a database to add a record.\n@param person The person to add. This example uses a strongly typed object that contains JAXB and validation annotations.")
    @Path('sql/add-person')
    @ApiOperation(notes = "Martini allows you to easily get a <a href=\"http://docs.groovy-lang.org/docs/latest/html/api/groovy/sql/Sql.html\">Groovy SQL</a> object that's already connected to a Martini JDBC Connection. This script shows how easy it is to expose a RESTful api that uses a database to add a record.", response = APIResponse.class, value = 'sql/add-person')
    @POST
    APIResponse addPerson(@RequestParam(value = 'dataSource', required = true) @ApiParam(name = 'dataSource', value = '', required = true) SQLExamples.DataSource dataSource,
                          @Valid @RequestBody @ApiParam(name = 'person', value = 'The person to add. This example uses a strongly typed object that contains JAXB and validation annotations.', required = true) Person person) {
        // More code...
    }
}

As you may have observed, Martini has injected import statements and annotations for your convenience whilst preserving the original elements of your code. Martini will do as much as it can to help you write better, less verbose code.

Procedure

To change Martini's compilation process for Groovy files, edit the <martini-home>/conf/MartiniGroovyCompilerConfig.groovy file. This configuration file is made known to the actual Groovy compiler via the configscript flag1.

With access to the configuration file, you can do plenty of things, like adding an implicit import to all your Groovy files using code like below:

1
2
3
4
5
6
def importCustomizer = new ImportCustomizer()

// More code...

importCustomizer.addStarImports 'org.foo' // import org.example.*
importCustomizer.addImport('AliasForBar', 'org.foo.Bar') // import org.foo.Bar as AliasForBar

The official Groovy documentation on compilation customizers does a great job at enumerating and discussing behaviors configurable during compilation. There are also plenty of resources available online, like this Groovy Goodness blog post that you may refer to.

Restart required

It is recommended to restart your Martini instance after changing MartiniGroovyCompilerConfig.groovy to ensure the changes are reflected.


  1. This is entirely dependent on Martini's internal implementation As of now, there is no way to use a different filename for the Groovy compiler configuration script.