Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Sign Up
Dashboard
Groovy
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<h1>Tweaking the compiler configuration</h1><p>Whether you are using <em>groovyc</em> to compile classes or a <em>GroovyShell</em>, for example, to execute scripts, under the hood, a <a href="http://groovy.codehaus.org/api/org/codehaus/groovy/control/CompilerConfiguration.html">compiler configuration</a> 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.</p><h1>Compilation customizers</h1><h2>Introduction</h2><p>Before Groovy 1.8.0, doing tasks like adding imports transparently (for DSLs) was a bit complicated. It involved writing a custom GroovyClassLoader and lots of trickery. The goal of compilation customizers is to make those common tasks easy to implement. For that, the <em>CompilerConfiguration</em> class is the entry point. The general schema will always be based on the following code:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>import org.codehaus.groovy.control.CompilerConfiguration // create a configuration def config = new CompilerConfiguration() // tweak the configuration config.addCompilationCustomizers(...) // run your script def shell = new GroovyShell(config) shell.evaluate(script)</pre></td></tr></table><p>Compilation customizers must extend the <em>org.codehaus.groovy.control.customizers.CompilationCustomizer</em> class. A customizer works:</p><ul><li>on a specific compilation phase</li><li>on <em>every</em> class node being compiled</li></ul><p>You can implement your own compilation customizer but Groovy includes some of the most common operations.</p><h2>The import customizer</h2><p>Using this compilation customizer, your code will have imports added transparently. This is in particular useful for scripts implementing a DSL where you want to avoid users from having to write imports. The import customizer will let you add all the variants of imports the Groovy language allows, that is:</p><ul><li>class imports, optionally aliased</li><li>star imports</li><li>static imports, optionally aliased</li><li>static star imports</li></ul><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>import org.codehaus.groovy.control.customizers.ImportCustomizer def icz = new ImportCustomizer() // "normal" import icz.addImports('java.util.concurrent.atomic.AtomicInteger', 'java.util.concurrent.ConcurrentHashMap') // "aliases" import icz.addImport('CHM', 'java.util.concurrent.ConcurrentHashMap') // "static" import icz.addStaticImport('java.lang.Math', 'PI') // import static java.lang.Math.Pi // "aliased static" import ica.addStaticImport('pi', 'java.lang.Math', 'PI') // import static java.lang.Math.PI as pi // "star" import icz.addStarImport 'java.util.concurrent' // import java.util.concurrent.* // "static star" import icz.addStaticStar 'java.lang.Math' // import static java.lang.Math.*</pre></td></tr></table><h2>The AST transformation customizer</h2><p>The AST transformation customizer is meant to apply AST transformations transparently. Unlike global AST transformations that apply on every class beeing compiled as long as the transform is found on classpath (which has drawbacks like increasing the compilation time or side effects due to transformations applied where they should not), the customizer will allow you to selectively apply a transform only for specific scripts or classes.</p><p>As an example, let's say you want to be able to use <em>@Log</em> in a script. The problem is that <em>@Log</em> is normally applied on a class node and a script, by definition, doesn't require one. But implementation wise, scripts are classes, it's just that you cannot annotate this implicit class node with <em>@Log</em>. Using the AST customizer, you have a workaround to do it:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer import groovy.util.logging.Log def acz = new ASTTransformationCustomizer(Log) configuration.addCompilationCustomizers(acz)</pre></td></tr></table><p>That's all! Internally, the <em>@Log</em> AST transformation is applied to every class node in the compilation unit. This means that it will be applied to the script, but also to classes defined within the script.</p><p>If the AST transformation that you are using accepts parameters, you can use parameters in the constructor too:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>def acz = new ASTTransformationCustomizer(Log, value: 'LOGGER') // use name 'LOGGER' instead of the default 'log'</pre></td></tr></table><p>As the AST transformation customizers works with objects instead of AST nodes, not all values can be converted to AST transformation parameters. For example, primitive types are converted to <em>ConstantExpression</em> (that is 'LOGGER' is converted to <em>new ConstantExpression('LOGGER')</em>, but if your AST transformation takes a closure as an argument, then you have to give it a <em>ClosureExpression</em>, like in the following example:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>final expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) {-> true }.expression[0] customizer = new ASTTransformationCustomizer(ConditionalInterrupt, value:expression, thrown:SecurityException) configuration.addCompilationCustomizers(customizer) def shell = new GroovyShell(configuration) shouldFail(SecurityException) { shell.evaluate(""" // equivalent to adding @ConditionalInterrupt(value={true}, thrown: SecurityException) class MyClass { void doIt() { } } new MyClass().doIt() """) }</pre></td></tr></table><h2>The SecureASTCustomizer</h2><p>This customizer will allow the developer of a DSL to restrict the grammar of the language, to prevent users from using some constructs, for example. It is only "secure" in that sense only and it is very important to understand that it does <strong>not</strong> replace a security manager. The only reason for it to exist is to limit the expressiveness of the language. This customizer only works at the AST (abstract syntax tree) level, not at runtime! It can be strange at first glance, but it makes much more sense if you think of Groovy as a platform to build DSLs. You may not want a user to have a complete language at hand. In the example below, we will demonstrate it using an example of language that only allows arithmetic operations, but this customizer allows you to:</p><ul><li>allow/disallow creation of closures</li><li>allow/disallow imports</li><li>allow/disallow package definition</li><li>allow/disallow definition of methods</li><li>restrict the receivers of method calls</li><li>restrict the kind of AST expressions a user can use</li><li>restrict the tokens (grammar-wise) a user can use</li><li>restrict the types of the constants that can be used in code</li></ul><p>For all those features, the secure AST customizer works using either a whitelist (list of elements that are allowed) <strong>or</strong> a blacklist (list of elements that are disallowed). For each type of feature (imports, tokens, ...) you have the choice to use either a whitelist or a blacklist, but you can mix whitelists and blacklists for distinct features. In general, you will choose whitelists (disallow all, allow selected).</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>org.codehaus.groovy.control.customizers.SecureASTCustomizer def scz = new SecureASTCustomizer() scz.with { closuresAllowed = false // user will not be able to write closures methodDefinitionAllowed = false // user will not be able to define methods importsWhitelist = [] // empty whitelist means imports are disallowed staticImportsWhitelist = [] // same for static imports staticStarImportsWhitelist = ['java.lang.Math'] // only java.lang.Math is allowed // the list of tokens the user can find // constants are defined in org.codehaus.groovy.syntax.Types tokensWhitelist = [ PLUS, MINUS, MULTIPLY, DIVIDE, MOD, POWER, PLUS_PLUS, MINUS_MINUS, COMPARE_EQUAL, COMPARE_NOT_EQUAL, COMPARE_LESS_THAN, COMPARE_LESS_THAN_EQUAL, COMPARE_GREATER_THAN, COMPARE_GREATER_THAN_EQUAL, ].asImmutable() // limit the types of constants that a user can define to number types only constantTypesClassesWhiteList = [ Integer, Float, Long, Double, BigDecimal, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE ].asImmutable() // method calls are only allowed if the receiver is of one of those types // be careful, it's not a runtime type! receiversClassesWhiteList = [ Math, Integer, Float, Double, Long, BigDecimal ].asImmutable() }</pre></td></tr></table><p>If what the secure AST customizer provides out of the box isn't enough for your needs, before creating your own compilation customizer, you might be interested in the expression and statement checkers that the AST customizer supports. Basically, it allows you to add custom checks on the AST tree, on expressions (expression checkers) or statements (statement checkers). For this, you must implement <em>org.codehaus.groovy.control.customizers.SecureASTCustomizer.StatementChecker</em> or <em>org.codehaus.groovy.control.customizers.SecureASTCustomizer.ExpressionChecker</em>.</p><p>Those interfaces define a single method called <em>isAuthorized</em>, returning a boolean, and taking a <em>Statement</em> (or <em>Expression</em>) as a parameter. It allows you to perform complex logic over expressions or statements to tell if a user is allowed to do it or not. As an example, let's think of a DSL for which you want to make sure that users only call methods for which the name is in lowercase:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>scz.addExpressionChecker { expr -> if (expr instanceof MethodCallExpression) { return expr.methodAsString?.toLowerCase() == expr.methodAsString } true } as ExpressionChecker</pre></td></tr></table><p>Here, we say that if the expression is a method call expression, then we can check the name and return true only if it's all lowercase. Otherwise, the expression is allowed.</p><h2>The SourceAwareCustomizer</h2><p>This customizer, available since Groovy 2.1.0 only, is a bit special in the sense that it may be used as a filter for other customizers. The filter, in that case, is the <em>org.codehaus.groovy.control.SourceUnit</em>. For this, the source aware customizer takes another customizer as a delegate, and it will apply customization of that delegate only and only if predicates on the source unit match.</p><p>SourceUnit gives you access to interesting things, in particular the file being compiled (if compiling from a file, of course), which gives you the potential to perform operation based on the file name, for example. Here is how you would create a source aware customizer:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>import org.codehaus.groovy.control.customizers.SourceAwareCustomizer import org.codehaus.groovy.control.customizers.ImportCustomizer def delegate = new ImportCustomizer() def sac = new SourceAwareCustomizer(delegate)</pre></td></tr></table><p>Then you can use predicates on the source aware customizer:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>// the customizer will only be applied to classes ending with 'Bean' sac.baseNameValidator = { baseName -> baseName.endsWith 'Bean' } // the customizer will only be applied to files which extension is '.spec' sac.extensionValidator = { ext -> ext == 'spec' } // source unit validation sac.sourceUnitValidator = { sourceUnit -> ... }</pre></td></tr></table><h2>The customization builder</h2><p>If you are using compilation customizers in Groovy code (like the examples above) and you are using Groovy 2.1+, then you can use an alternative syntax to customize the compilation. A builder <em>org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder</em> is available. Creating a customizer has never been so easy!</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>import org.codehaus.groovy.control.CompilerConfiguration import static org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder.withConfig def conf = new CompilerConfiguration() withConfig(conf) { ... }</pre></td></tr></table><p>The code sample above shows how to use the builder. A static method, <em>withConfig</em>, takes a closure corresponding to the builder code, and automatically registers compilation customizers to the configuration. You can use:</p><h3>Imports</h3><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>withConfig(configuration) { imports { // imports customizer normal 'my.package.MyClass' // a normal import alias 'AI', 'java.util.concurrent.atomic.AtomicInteger' // an aliased import star 'java.util.concurrent' // star imports staticMember 'java.lang.Math', 'PI' // static import staticMember 'pi', 'java.lang.Math', 'PI' // aliased static import } }</pre></td></tr></table><h3>For the AST customizer</h3><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>// apply @Log transparently withConfig(conf) { ast(Log) } // apply @Log, with a different logger name withConfig(conf) { ast(Log, value: 'LOGGER') }</pre></td></tr></table><h3>Secure ast</h3><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>withConfig(conf) { secureAst { closuresAllowed = false methodDefinitionAllowed = false } }</pre></td></tr></table><h3>Source based</h3><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>// apply CompileStatic AST annotation on .sgroovy files withConfig(configuration){ source(extension: 'sgroovy') { ast(CompileStatic) } } // apply CompileStatic AST annotation on .sgroovy or .sg files withConfig(configuration){ source(extensions: ['sgroovy','sg']) { ast(CompileStatic) } } // apply CompileStatic AST annotation on .sgroovy or .sg files withConfig(configuration) { source(extensionValidator: { it.name in ['sgroovy','sg']}) { ast(CompileStatic) } } // apply CompileStatic AST annotation on files whose name is 'foo' withConfig(configuration) { source(basename: 'foo') { ast(CompileStatic) } } // apply CompileStatic AST annotation on files whose name is 'foo' or 'bar' withConfig(configuration) { source(basenames: ['foo', 'bar']) { ast(CompileStatic) } } // apply CompileStatic AST annotation on files whose name is 'foo' or 'bar' withConfig(configuration) { source(basenameValidator: { it in ['foo', 'bar'] }) { ast(CompileStatic) } } // apply CompileStatic AST annotation on files that do not contain a class named 'Baz' withConfig(configuration) { source(unitValidator: { unit -> !unit.AST.classes.any { it.name == 'Baz' } }) { ast(CompileStatic) } }</pre></td></tr></table><h3>Inlining a customizer</h3><p>Inlined customizer allows you to write a compilation customizer directly, without having to create a dedicated class for it.</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>withConfig(configuration) { inline(phase:'CONVERSION') { source, context, classNode -> println "visiting $classNode" } }</pre></td></tr></table><h3>Multiple customizers</h3><p>Of course, the builder allows you to define multiple customizers at once:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>withConfig(configuration) { ast(ToString) ast(EqualsAndHashCode) }</pre></td></tr></table><h1>Configuring compilation using groovyc or the ant task</h1><p>For now, we've shown you how to customize compilation using a <em>CompilationConfiguration</em> access, but this is only possible if you embed Groovy and that you create your own instances of <em>CompilerConfiguration</em> (be it with <em>GroovyShell</em>, <em>GroovyScriptEngine</em>, ...). But if you want it to be applied on the classes you compile (with <em>groovyc</em>, <em>ant</em> or <em>gradle</em>, for example), until Groovy 2.1.0, there was no way to do that.</p><p>Since Groovy 2.1.0 (and Groovy 2.1.1 for the groovy Ant task), it is possible to use a compilation flag named <em>configscript</em> that takes a groovy configration script as a parameter. This script gives you access to the <em>CompilerConfiguration</em> instance <strong>before</strong> the files are compiled (exposed as a variable named <em>configuration</em>), so that you can tweak it. It also transparently integrates the compiler configuration builder above.</p><h2>Static compilation by default</h2><p>Since static compilation has been released, many people asked for it to be enabled by default. For various reasons, including the fact that we think you should only limit static compilation to pieces of code where you have performance problems, we never included such a feature. Other people asked for default imports too. Since we didn't want to add lots of flags for each and every magic that Groovy can do, we decided to go for a configuration script. This means that having static compilation by default is just a matter of compiling classes using this configuration file. And the content is very easy:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>withConfig(configuration) { ast(groovy.transform.CompileStatic) }</pre></td></tr></table><p>You don't need to add an import for the builder, it's automatically added. Then, compile your files using the following command line:</p><table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>groovyc -configscript src/conf/config.groovy src/main/groovy/MyClass.groovy</pre></td></tr></table><p>We strongly recommand you to separate configuration files from classes, hence the src/main and src/conf directories above.</p><p> </p>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced