SlideShare ist ein Scribd-Unternehmen logo
1 von 75
Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification from Slide 8
1
DSLs with Groovy
Jim Driscoll
     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification from Slide 8
 2
Talk Agenda

    •   Introduction
    •   Executing Programs
    •   The Nouns - Objects
    •   The Verbs - Functions
    •   The AST




    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
3
Introductions

    • Experiences
    • What is a DSL
    • Why Groovy?




    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
4
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
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
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
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
Talk Agenda

    •   Introduction
    •   Executing Programs
    •   The Nouns - Objects
    •   The Verbs - Functions
    •   The AST




    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
9
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
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
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
Talk Agenda

     •   Introduction
     •   Executing Programs
     •   The Nouns - Objects
     •   The Verbs - Functions
     •   The AST




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
13
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
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
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
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
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
Talk Agenda

     •   Introduction
     •   Executing Programs
     •   The Nouns - Objects
     •   The Verbs - Functions
     •   The AST




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
19
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Talk Agenda

     •   Introduction
     •   Executing Programs
     •   The Nouns - Objects
     •   The Verbs - Functions
     •   The AST




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
40
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
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
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
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
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
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
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
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
Forbidding for loops
Script 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.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
49
Forbidding for loops
Script 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.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
50
Forbidding for loops
Script 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.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
51
Forbidding for loops
Script 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.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
52
Forbidding for loops
Script 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.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
53
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
Sample AST
     Sample code:
     def i = 1
     println i




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
55
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
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
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
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
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
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
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
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
NameChange.groovy



     @Retention(RetentionPolicy.SOURCE)
     @Target(ElementType.TYPE)
     @GroovyASTTransformationClass("NameChangeASTTransformation")
     public @interface NameChange {
     }




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
64
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
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
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
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
Run with Annotation


                String runMe = "@NameChange n"+
                               "class Name {n"+
                               " static def name = 'Bob'n"+
                               "} 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
Run with Annotation

     String runMe = "class Name {n"+
                    "static def name = 'Bob'n"+
                    "} 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
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
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
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
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 from Slide 8
75

Weitere ähnliche Inhalte

Was ist angesagt?

Excuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in KotlinExcuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in Kotlin
leonsabr
 
4java Basic Syntax
4java Basic Syntax4java Basic Syntax
4java Basic Syntax
Adil Jafri
 

Was ist angesagt? (20)

Java 7 New Features
Java 7 New FeaturesJava 7 New Features
Java 7 New Features
 
Clojure, Plain and Simple
Clojure, Plain and SimpleClojure, Plain and Simple
Clojure, Plain and Simple
 
Java 7 & 8 New Features
Java 7 & 8 New FeaturesJava 7 & 8 New Features
Java 7 & 8 New Features
 
JDK1.6
JDK1.6JDK1.6
JDK1.6
 
55 New Features in Java 7
55 New Features in Java 755 New Features in Java 7
55 New Features in Java 7
 
Clojure for Java developers
Clojure for Java developersClojure for Java developers
Clojure for Java developers
 
Java 7 - What's New?
Java 7 - What's New?Java 7 - What's New?
Java 7 - What's New?
 
JDK1.7 features
JDK1.7 featuresJDK1.7 features
JDK1.7 features
 
The Ring programming language version 1.5.2 book - Part 176 of 181
The Ring programming language version 1.5.2 book - Part 176 of 181The Ring programming language version 1.5.2 book - Part 176 of 181
The Ring programming language version 1.5.2 book - Part 176 of 181
 
The Ring programming language version 1.5.4 book - Part 180 of 185
The Ring programming language version 1.5.4 book - Part 180 of 185The Ring programming language version 1.5.4 book - Part 180 of 185
The Ring programming language version 1.5.4 book - Part 180 of 185
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainers
 
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
 
Excuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in KotlinExcuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in Kotlin
 
Priming Java for Speed at Market Open
Priming Java for Speed at Market OpenPriming Java for Speed at Market Open
Priming Java for Speed at Market Open
 
2 P Seminar
2 P Seminar2 P Seminar
2 P Seminar
 
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring framework
 
Clojure: a LISP for the JVM
Clojure: a LISP for the JVMClojure: a LISP for the JVM
Clojure: a LISP for the JVM
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with Clojure
 
4java Basic Syntax
4java Basic Syntax4java Basic Syntax
4java Basic Syntax
 

Ähnlich wie Groovy DSLs (JavaOne Presentation)

Boosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyBoosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with Groovy
James Williams
 
GTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyGTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with Groovy
Andres Almiray
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Paul King
 
Scala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on HerokuScala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on Heroku
Havoc Pennington
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01
Tino Isnich
 

Ähnlich wie Groovy DSLs (JavaOne Presentation) (20)

An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java Developers
 
Boosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyBoosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with Groovy
 
Javaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingJavaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 Groovytesting
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
What's New in Groovy 1.6?
What's New in Groovy 1.6?What's New in Groovy 1.6?
What's New in Groovy 1.6?
 
55j7
55j755j7
55j7
 
Infinum android talks_10_getting groovy on android
Infinum android talks_10_getting groovy on androidInfinum android talks_10_getting groovy on android
Infinum android talks_10_getting groovy on android
 
GTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyGTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with Groovy
 
Groovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web ApplicationsGroovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web Applications
 
Groovy - Grails as a modern scripting language for Web applications
Groovy - Grails as a modern scripting language for Web applicationsGroovy - Grails as a modern scripting language for Web applications
Groovy - Grails as a modern scripting language for Web applications
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106
 
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in Groovy
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
Back to the future with Java 7 (Geekout June/2011)
Back to the future with Java 7 (Geekout June/2011)Back to the future with Java 7 (Geekout June/2011)
Back to the future with Java 7 (Geekout June/2011)
 
Scala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on HerokuScala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on Heroku
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle Groovy
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01
 
Groovy & Grails
Groovy & GrailsGroovy & Grails
Groovy & Grails
 

Kürzlich hochgeladen

CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
giselly40
 

Kürzlich hochgeladen (20)

Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 

Groovy DSLs (JavaOne Presentation)

  • 1. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 8 1
  • 2. DSLs with Groovy Jim 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 loops Script 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.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 49
  • 50. Forbidding for loops Script 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.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 50
  • 51. Forbidding for loops Script 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.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 51
  • 52. Forbidding for loops Script 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.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 52
  • 53. Forbidding for loops Script 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.SecurityException script.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 = 'Bob'n"+ "} 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 = 'Bob'n"+ "} 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 8 75

Hinweis der Redaktion

  1. \n
  2. \n
  3. \n
  4. \n
  5. Ask what they&amp;#x2019;e worked on\n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. Note: you can also use source.AST.code\n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n