Your SlideShare is downloading. ×
0
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Groovy DSLs (JavaOne Presentation)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Groovy DSLs (JavaOne Presentation)

2,428

Published on

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

No Downloads
Views
Total Views
2,428
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
89
Comments
0
Likes
8
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
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
  • Transcript

    • 1. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 81
    • 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. 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. Introductions • Experiences • What is a DSL • Why Groovy? Copyright © 2011, Oracle and/or its affiliates. All rights reserved.4
    • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Sample AST Sample code: def i = 1 println i Copyright © 2011, Oracle and/or its affiliates. All rights reserved.55
    • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Q&A Copyright © 2011, Oracle and/or its affiliates. All rights reserved.74
    • 75. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 875

    ×