Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification...
DSLs with GroovyJim Driscoll     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information...
Talk Agenda    •   Introduction    •   Executing Programs    •   The Nouns - Objects    •   The Verbs - Functions    •   T...
Introductions    • Experiences    • What is a DSL    • Why Groovy?    Copyright © 2011, Oracle and/or its affiliates. All ...
Experience    • ADFm           – Part of Oracle’s ADF Framework    • Makes heavy use of Groovy as a scripting language    ...
What’s a DSL    • In general terms...           – A purpose built language to solve a specific task                    • F...
Why Groovy?    • Groovy is very extensible           – Language features (via AST Transforms, Customizers)           – Obj...
Some Caveats    • I’m not an expert in Groovy           – So many of my examples are in Java    • There are many different...
Talk Agenda    •   Introduction    •   Executing Programs    •   The Nouns - Objects    •   The Verbs - Functions    •   T...
Executing Programs     • You’ll want to run user provided scripts at runtime     • Simplest way is with GroovyShell.evalua...
Executing Scripts     import groovy.lang.GroovyShell;     import groovy.lang.Script;     public class JavaOne01 {         ...
Under the hood     • Groovy will take your unqualified text, and place it in the       Script’s run method.     • run has ...
Talk Agenda     •   Introduction     •   Executing Programs     •   The Nouns - Objects     •   The Verbs - Functions     ...
Adding Objects via Bindings     • A Binding is a way to pass values in/out of a script            – Used as unqualified va...
Sample Binding                   public class MyBinding extends groovy.lang.Binding {                     public Object ge...
Sample Binding                   public class MyBinding extends groovy.lang.Binding {                     public Object ge...
Sample Binding                   public class MyBinding extends groovy.lang.Binding {                     public Object ge...
Using the binding     package javaone02;                                                     The script:     import groovy...
Talk Agenda     •   Introduction     •   Executing Programs     •   The Nouns - Objects     •   The Verbs - Functions     ...
The ScriptBaseClass     • Allows you to declare top level methods     • Inserted as part of the CompilerConfiguration     ...
Turtle.groovy     class Turtle {        int distance = 0;        def forward(int i) {           distance += i           pr...
Turtle.groovy     class Turtle {        int distance = 0;        def forward(int i) {           distance += i           pr...
BaseScript.groovy     abstract class BaseScript extends Script {       Turtle t = new Turtle();       def forward(int i) {...
Main Class     import groovy.lang.GroovyShell;     import groovy.lang.Script;     import org.codehaus.groovy.control.Compi...
Main Class     import groovy.lang.GroovyShell;     import groovy.lang.Script;     import org.codehaus.groovy.control.Compi...
Main Class     import groovy.lang.GroovyShell;     import groovy.lang.Script;     import org.codehaus.groovy.control.Compi...
Main Class     import groovy.lang.GroovyShell;     import groovy.lang.Script;     import org.codehaus.groovy.control.Compi...
invokeMethod     • invokeMethod is a standard method, inherited from       GroovyObject     • Allows dynamic execution of ...
Objects - Cat and Dog     class Dog {        def speak() {          println "woof"        }     }     class Cat {        d...
Binding class MyBinding extends Binding {    def a = new Dog();    def getVariable(String name) {      if (name == "animal...
Binding class MyBinding extends Binding {    def a = new Dog();    def getVariable(String name) {      if (name == "animal...
Binding class MyBinding extends Binding {    def a = new Dog();    def getVariable(String name) {      if (name == "animal...
BaseScript.groovy     abstract class BaseScript extends Script {       def invokeMethod(String name, args) {          anim...
BaseScript.groovy     abstract class BaseScript extends Script {       def invokeMethod(String name, args) {          anim...
BaseScript.groovy     abstract class BaseScript extends Script {       def invokeMethod(String name, args) {          anim...
BaseScript.groovy     abstract class BaseScript extends Script {       def invokeMethod(String name, args) {          anim...
BaseScript.groovy     abstract class BaseScript extends Script {       def invokeMethod(String name, args) {          anim...
Results     The Script:     speak()     change(‘cat’)     speak()     change(‘dog’)     animal.speak()     Produces the Ou...
get/setProperty     • Properties are accessed via “object.propertyname”     • Properties can be either fields or accessors...
Talk Agenda     •   Introduction     •   Executing Programs     •   The Nouns - Objects     •   The Verbs - Functions     ...
Compilation Customizers     •   New in 1.8     •   Replace and/or augment ASTTransformations     •   Modify code as part o...
Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfi...
Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfi...
Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfi...
Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer c...
Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer c...
Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer c...
Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer c...
Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customize...
Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customize...
Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customize...
Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customize...
Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customize...
ASTTransformations     • Used to modify generated code from the compiler     • Either global or local            – Globals...
Sample AST     Sample code:     def i = 1     println i     Copyright © 2011, Oracle and/or its affiliates. All rights res...
A Simple Use of AST - Checking Bind Variables     • An early problem we ran into     • Need to determine if binding variab...
Scanning for Binding variables, part 1     class ScriptVisitingCustomizer extends CompilationCustomizer {        def visit...
Scanning for Binding variables, part 1     class ScriptVisitingCustomizer extends CompilationCustomizer {        def visit...
Scanning for Binding variables, part 1     class ScriptVisitingCustomizer extends CompilationCustomizer {        def visit...
Scanning for Binding variables, part 2     def scriptText = """     println baz     println foo     def bar = 2     printl...
Scanning for Binding variables, part 2     def scriptText = """     println baz     println foo     def bar = 2     printl...
ASTTransformations     • Numerous standard ASTTransforms are available            – ThreadInterrupt - add Thread.currentTh...
Creating an ASTTransformation     •   Create an Annotation     •   Create an ASTTransformation class     •   (Optional, bu...
NameChange.groovy     @Retention(RetentionPolicy.SOURCE)     @Target(ElementType.TYPE)     @GroovyASTTransformationClass("...
NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTrans...
NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTrans...
NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTrans...
NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTrans...
Run with Annotation                String runMe = "@NameChange n"+                               "class Name {n"+         ...
Run with Annotation     String runMe = "class Name {n"+                    "static def name = Bobn"+                    "}...
Securing Your Code     • GroovyShell(ClassLoader,Binding,CompilerConfiguration)     • GroovyShell.parse(GroovyCodeSource) ...
Last Note: Naming Matters!     This Code:                                                             Can take this as inp...
Resources     • General Groovy Books            – Groovy Programming: An Introduction for Java Programmers                ...
Q&A     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.74
Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification...
Upcoming SlideShare
Loading in …5
×

Groovy DSLs (JavaOne Presentation)

2,950 views
2,841 views

Published on

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,950
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
92
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • Ask what they’e worked on\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Note: you can also use source.AST.code\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Groovy DSLs (JavaOne Presentation)

    1. 1. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 81
    2. 2. DSLs with GroovyJim Driscoll Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 8 2
    3. 3. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved.3
    4. 4. Introductions • Experiences • What is a DSL • Why Groovy? Copyright © 2011, Oracle and/or its affiliates. All rights reserved.4
    5. 5. Experience • ADFm – Part of Oracle’s ADF Framework • Makes heavy use of Groovy as a scripting language – (But almost all the extensions are written in Java) • Extensions include – Accessing Field (Attribute) names as read-only variables – Associating functions with tables (Views) at runtime – Creating new top-level functions at runtime – A security overlay to existing classes – Extending existing classes with new functions Copyright © 2011, Oracle and/or its affiliates. All rights reserved.5
    6. 6. What’s a DSL • In general terms... – A purpose built language to solve a specific task • For example, LOGO, SQL, YACC, HTML • Some (i.e. Fowler) advocate it be minimal – But that’s not what we’re talking about today • For our purposes today: – Creating a custom language via adding and removing language features until you have what your users need – We’ll mostly focus on adding features today – (But I’ll show you how to take stuff out too.) Copyright © 2011, Oracle and/or its affiliates. All rights reserved.6
    7. 7. Why Groovy? • Groovy is very extensible – Language features (via AST Transforms, Customizers) – Object behavior (via MOP, invokeMethod, getProperty) • Groovy runs in a JVM – And most Java source will run as Groovy without modification • Groovy is very dynamic – Optional (duck) typing – Easy inline compilation • Groovy’s Open Source – With an incredibly helpful community Copyright © 2011, Oracle and/or its affiliates. All rights reserved.7
    8. 8. Some Caveats • I’m not an expert in Groovy – So many of my examples are in Java • There are many different ways to do almost everything – There are at least 7 different ways use the String “System.exit(0)” to shut down the VM. • Groovy 1.8 adds even more functionality – I haven’t used it in production yet. – But I’ll still talk about CompilationCustomizers today • This is a BOF - I’d love any comments realtime Copyright © 2011, Oracle and/or its affiliates. All rights reserved.8
    9. 9. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved.9
    10. 10. Executing Programs • You’ll want to run user provided scripts at runtime • Simplest way is with GroovyShell.evaluate(String) – GroovyShell.evaluate(“println ‘hello world’”); – but this is expensive... • Separate parsing from execution: – Script script = GroovyShell.parse(String) • Time measured in seconds (you’re running a compiler) • Offers an intercept point for caching – Script.run() Copyright © 2011, Oracle and/or its affiliates. All rights reserved.10
    11. 11. Executing Scripts import groovy.lang.GroovyShell; import groovy.lang.Script; public class JavaOne01 { public static void main(String[] args) { GroovyShell shell = new GroovyShell(); Script script = shell.parse("println hello world"); script.run(); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.11
    12. 12. Under the hood • Groovy will take your unqualified text, and place it in the Script’s run method. • run has a return of type Object • If you don’t have a return value, the final statement executed will be turned into one – thus “x == y” is turned into “return x == y”, which is a Boolean • You can provide inline methods... – “int test() {return 1} n test()+2” is valid, returns 3 – but you can also run the test() method separately via reflection Copyright © 2011, Oracle and/or its affiliates. All rights reserved.12
    13. 13. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved.13
    14. 14. Adding Objects via Bindings • A Binding is a way to pass values in/out of a script – Used as unqualified variables (“println name”) – declared variables (def, or a type) go in their own memory space • Groovy provides a default Binding, and you can write your own • Use it to provide: – Constant values – read only variables – default values Copyright © 2011, Oracle and/or its affiliates. All rights reserved.14
    15. 15. Sample Binding public class MyBinding extends groovy.lang.Binding { public Object getVariable(String name) { if ("name".equals(name)) { return "Jim"; } else if ("date".equals(name)) { return new Date(); } return super.getVariable(name); } public void setVariable(String name, Object value) { if ("date".equals(name) || "name".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.15
    16. 16. Sample Binding public class MyBinding extends groovy.lang.Binding { public Object getVariable(String name) { if ("name".equals(name)) { return "Jim"; } else if ("date".equals(name)) { return new Date(); } return super.getVariable(name); } public void setVariable(String name, Object value) { if ("date".equals(name) || "name".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.16
    17. 17. Sample Binding public class MyBinding extends groovy.lang.Binding { public Object getVariable(String name) { if ("name".equals(name)) { return "Jim"; } else if ("date".equals(name)) { return new Date(); } return super.getVariable(name); } public void setVariable(String name, Object value) { if ("date".equals(name) || "name".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.17
    18. 18. Using the binding package javaone02; The script: import groovy.lang.GroovyShell; import groovy.lang.Script; println ‘hello ‘ + name println ‘it is now: ‘ + date public class JavaOne02 { public static void main(String[] args) { The output is: String runMe = "println hello + name n"+ hello Jim "println it is now: +date"; GroovyShell shell = new GroovyShell(); it is now: Wed Sep 28 18:35:41 PDT 2011 Script script = shell.parse(runMe); script.setBinding(new MyBinding()); script.run(); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.18
    19. 19. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved.19
    20. 20. The ScriptBaseClass • Allows you to declare top level methods • Inserted as part of the CompilerConfiguration – An optional parameter to GroovyShell • Combine this with Groovy’s optional parenthesis... – (And optional chaining, introduced in 1.8...) – And simple constructs like LOGO’s turtle manipulation language start to come within reach Copyright © 2011, Oracle and/or its affiliates. All rights reserved.20
    21. 21. Turtle.groovy class Turtle { int distance = 0; def forward(int i) { distance += i println "moved forward "+i return this } def right(int i) { println "steering wheel stuck" return this } def traveled() { println "total distance traveled " + distance return this } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.21
    22. 22. Turtle.groovy class Turtle { int distance = 0; def forward(int i) { distance += i println "moved forward "+i return this } def right(int i) { println "steering wheel stuck" return this } def traveled() { println "total distance traveled " + distance return this } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.22
    23. 23. BaseScript.groovy abstract class BaseScript extends Script { Turtle t = new Turtle(); def forward(int i) { t.forward(i) } def right(int i) { t.right(i) } def traveled() { t.traveled() } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.23
    24. 24. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved.24
    25. 25. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved.25
    26. 26. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved.26
    27. 27. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved.27
    28. 28. invokeMethod • invokeMethod is a standard method, inherited from GroovyObject • Allows dynamic execution of methods • Used with BaseScript, it allows using a context object • One of many ways to do the same thing in Groovy • Can test before you run with – object.metaClass.respondsTo(object,name,*args) Copyright © 2011, Oracle and/or its affiliates. All rights reserved.28
    29. 29. Objects - Cat and Dog class Dog { def speak() { println "woof" } } class Cat { def speak() { println "meow" } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.29
    30. 30. Binding class MyBinding extends Binding { def a = new Dog(); def getVariable(String name) { if (name == "animal") { return a } return super.getVariable(name); } void setVariable(String name, Object value) { if ("animal".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } void changeAnimal(def animal) { a = animal; } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.30
    31. 31. Binding class MyBinding extends Binding { def a = new Dog(); def getVariable(String name) { if (name == "animal") { return a } return super.getVariable(name); } void setVariable(String name, Object value) { if ("animal".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } void changeAnimal(def animal) { a = animal; } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.31
    32. 32. Binding class MyBinding extends Binding { def a = new Dog(); def getVariable(String name) { if (name == "animal") { return a } return super.getVariable(name); } void setVariable(String name, Object value) { if ("animal".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } void changeAnimal(def animal) { a = animal; } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.32
    33. 33. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.33
    34. 34. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.34
    35. 35. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.35
    36. 36. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.36
    37. 37. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.37
    38. 38. Results The Script: speak() change(‘cat’) speak() change(‘dog’) animal.speak() Produces the Output: woof meow woof Copyright © 2011, Oracle and/or its affiliates. All rights reserved.38
    39. 39. get/setProperty • Properties are accessed via “object.propertyname” • Properties can be either fields or accessors/mutators – foo.bar can either be get/setBar, or the variable bar in foo • Like invokeMethod, this behavior can be overridden • Beware the “Java Field” operator - .@, which bypasses the get/setProperty methods Copyright © 2011, Oracle and/or its affiliates. All rights reserved.39
    40. 40. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved.40
    41. 41. Compilation Customizers • New in 1.8 • Replace and/or augment ASTTransformations • Modify code as part of the compilation process • For example: – adding imports (via ImportCompilationCustomizer) – disabling language features (via SecureASTCustomizer) – automatically applying ASTTransforms (either local or global) (via ASTTranformationCustomizer) Copyright © 2011, Oracle and/or its affiliates. All rights reserved.41
    42. 42. Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; public class JavaOne05Customizer { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Output: Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793 script.run(); 0.5403023058681398 } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.42
    43. 43. Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; public class JavaOne05Customizer { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Output: Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793 script.run(); 0.5403023058681398 } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.43
    44. 44. Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; public class JavaOne05Customizer { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Output: Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793 script.run(); 0.5403023058681398 } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.44
    45. 45. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.45
    46. 46. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.46
    47. 47. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.47
    48. 48. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.48
    49. 49. Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customizer = new SecureASTCustomizer();List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();statements.add(ForStatement.class);customizer.setStatementsBlacklist(statements);config.addCompilationCustomizers(customizer);GroovyShell shell = new GroovyShell(config);try { script = shell.parse("for (i in [1,2,3]) {}");} catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output:} java.lang.SecurityExceptionscript.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.49
    50. 50. Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customizer = new SecureASTCustomizer();List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();statements.add(ForStatement.class);customizer.setStatementsBlacklist(statements);config.addCompilationCustomizers(customizer);GroovyShell shell = new GroovyShell(config);try { script = shell.parse("for (i in [1,2,3]) {}");} catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output:} java.lang.SecurityExceptionscript.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.50
    51. 51. Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customizer = new SecureASTCustomizer();List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();statements.add(ForStatement.class);customizer.setStatementsBlacklist(statements);config.addCompilationCustomizers(customizer);GroovyShell shell = new GroovyShell(config);try { script = shell.parse("for (i in [1,2,3]) {}");} catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output:} java.lang.SecurityExceptionscript.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.51
    52. 52. Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customizer = new SecureASTCustomizer();List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();statements.add(ForStatement.class);customizer.setStatementsBlacklist(statements);config.addCompilationCustomizers(customizer);GroovyShell shell = new GroovyShell(config);try { script = shell.parse("for (i in [1,2,3]) {}");} catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output:} java.lang.SecurityExceptionscript.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.52
    53. 53. Forbidding for loopsScript script;CompilerConfiguration config = new CompilerConfiguration();SecureASTCustomizer customizer = new SecureASTCustomizer();List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();statements.add(ForStatement.class);customizer.setStatementsBlacklist(statements);config.addCompilationCustomizers(customizer);GroovyShell shell = new GroovyShell(config);try { script = shell.parse("for (i in [1,2,3]) {}");} catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output:} java.lang.SecurityExceptionscript.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved.53
    54. 54. ASTTransformations • Used to modify generated code from the compiler • Either global or local – Globals are triggered by compile time configuration – Locals are triggered by Annotations • Groovy compiler generates an Abstract Syntax Tree – breaks code into tree of Statements • which in turn are combinations of Statements and/or Expressions • You can examine code with groovyConsole to see the tree via Script -> Inspect AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved.54
    55. 55. Sample AST Sample code: def i = 1 println i Copyright © 2011, Oracle and/or its affiliates. All rights reserved.55
    56. 56. A Simple Use of AST - Checking Bind Variables • An early problem we ran into • Need to determine if binding variables are valid – Most useful for read-only bindings – Check for things like misspelling (so, “usrename” vs. “username”) • Create a new CompilationCustomizer • Visitor pattern means we visit every VariableExpression • Test if it’s going against the supplied Binding Copyright © 2011, Oracle and/or its affiliates. All rights reserved.56
    57. 57. Scanning for Binding variables, part 1 class ScriptVisitingCustomizer extends CompilationCustomizer { def visitor ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)} void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { for (method in classNode.methods) { if (method.name == run) method.code.visit(visitor) } } } class MyVisitor extends ClassCodeVisitorSupport { void visitVariableExpression(VariableExpression expression) { if (expression.accessedVariable instanceof DynamicVariable) println expression.name } protected SourceUnit getSourceUnit() {return source} } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.57
    58. 58. Scanning for Binding variables, part 1 class ScriptVisitingCustomizer extends CompilationCustomizer { def visitor ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)} void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { for (method in classNode.methods) { if (method.name == run) method.code.visit(visitor) } } } class MyVisitor extends ClassCodeVisitorSupport { void visitVariableExpression(VariableExpression expression) { if (expression.accessedVariable instanceof DynamicVariable) println expression.name } protected SourceUnit getSourceUnit() {return source} } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.58
    59. 59. Scanning for Binding variables, part 1 class ScriptVisitingCustomizer extends CompilationCustomizer { def visitor ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)} void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { for (method in classNode.methods) { if (method.name == run) method.code.visit(visitor) } } } class MyVisitor extends ClassCodeVisitorSupport { void visitVariableExpression(VariableExpression expression) { if (expression.accessedVariable instanceof DynamicVariable) println expression.name } protected SourceUnit getSourceUnit() {return source} } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.59
    60. 60. Scanning for Binding variables, part 2 def scriptText = """ println baz println foo def bar = 2 println bar """ CompilerConfiguration config = new CompilerConfiguration(); def customizer = new ScriptVisitingCustomizer(visitor: new MyVisitor()); Output: config.addCompilationCustomizers(customizer); baz foo GroovyShell shell = new GroovyShell(config); Script script = shell.parse(scriptText); Copyright © 2011, Oracle and/or its affiliates. All rights reserved.60
    61. 61. Scanning for Binding variables, part 2 def scriptText = """ println baz println foo def bar = 2 println bar """ CompilerConfiguration config = new CompilerConfiguration(); def customizer = new ScriptVisitingCustomizer(visitor: new MyVisitor()); Output: config.addCompilationCustomizers(customizer); baz foo GroovyShell shell = new GroovyShell(config); Script script = shell.parse(scriptText); Copyright © 2011, Oracle and/or its affiliates. All rights reserved.61
    62. 62. ASTTransformations • Numerous standard ASTTransforms are available – ThreadInterrupt - add Thread.currentThread().isInterrupted checks on loops, closures, first Statement in a Block – TimedInterrupt - as above, with a timeout check – ConditionalInterrupt - as above, with any closure – EqualsAndHashCode - create equals() and hashCode() methods – AutoClone - create clone() method – Immutable - create constructors, accessors, make final Copyright © 2011, Oracle and/or its affiliates. All rights reserved.62
    63. 63. Creating an ASTTransformation • Create an Annotation • Create an ASTTransformation class • (Optional, but likely) Create a ClassCodeVisitor • To use: – Decorate relevant object (method, class, etc) with annotation or – Use ASTTransformationCustomizer in GroovyShell Copyright © 2011, Oracle and/or its affiliates. All rights reserved.63
    64. 64. NameChange.groovy @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) @GroovyASTTransformationClass("NameChangeASTTransformation") public @interface NameChange { } Copyright © 2011, Oracle and/or its affiliates. All rights reserved.64
    65. 65. NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); }}class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); }} Copyright © 2011, Oracle and/or its affiliates. All rights reserved.65
    66. 66. NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); }}class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); }} Copyright © 2011, Oracle and/or its affiliates. All rights reserved.66
    67. 67. NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); }}class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); }} Copyright © 2011, Oracle and/or its affiliates. All rights reserved.67
    68. 68. NameChangeASTTransformation@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); }}class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); }} Copyright © 2011, Oracle and/or its affiliates. All rights reserved.68
    69. 69. Run with Annotation String runMe = "@NameChange n"+ "class Name {n"+ " static def name = Bobn"+ "} n" + "println Name.name"; GroovyShell shell = new GroovyShell(); Script script = shell.parse(runMe); script.run(); Output: Alice Copyright © 2011, Oracle and/or its affiliates. All rights reserved.69
    70. 70. Run with Annotation String runMe = "class Name {n"+ "static def name = Bobn"+ "} n" + "println Name.name"; CompilerConfiguration config = new CompilerConfiguration(); ASTTransformationCustomizer customizer = new ASTTransformationCustomizer(NameChange.class); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Script script = shell.parse(runMe); Output: script.run(); Alice Copyright © 2011, Oracle and/or its affiliates. All rights reserved.70
    71. 71. Securing Your Code • GroovyShell(ClassLoader,Binding,CompilerConfiguration) • GroovyShell.parse(GroovyCodeSource) – GroovyCodeSource(String script, String name, String codeBase) • SecureASTCustomizer – ArithmeticShell – static analysis only! • method/property wrapping Copyright © 2011, Oracle and/or its affiliates. All rights reserved.71
    72. 72. Last Note: Naming Matters! This Code: Can take this as input: class Book { void name(String name) { Book.create { println name name "DSLs in Action" } loanedTo "Jim","Sint Si" void quantity(int i) { quantity 1 println i } } void loanedTo(String[] names) { println names } static create(closure) { def book = new Book() book.with closure return book } } *example inspired by “DSLs in Action”* Copyright © 2011, Oracle and/or its affiliates. All rights reserved.72
    73. 73. Resources • General Groovy Books – Groovy Programming: An Introduction for Java Programmers • (K&R style simple book) – Programming Groovy – Groovy in Action • (2nd Ed coming soon, with DSL chapters) • Domain Specific Languages – Groovy for Domain-Specific Languages • (great discussion of MOP, Builders) – DSLs in Action • (contains multiple language examples) Copyright © 2011, Oracle and/or its affiliates. All rights reserved.73
    74. 74. Q&A Copyright © 2011, Oracle and/or its affiliates. All rights reserved.74
    75. 75. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 875

    ×