Slide 1

Slide 1 text

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. Groovy Domain-Specific Languages Paul King Groovy Core Developer ASERT @paulk_asert Guillaume Laforge Groovy Project Manager SpringSource / VMware @glaforge

Slide 2

Slide 2 text

Paul King •Groovy Core Committer • Leads ASERT – software, training, consultancy company based in Brisbane, Australia • PhD in Computer Science from The University of Queensland • Co-author of Groovy in Action • Follow me: – Twitter: @paulk_asert 2

Slide 3

Slide 3 text

Guillaume Laforge • Groovy Project Manager at Pivotal • Initiator of the Grails framework • Creator of the Gaelyk • Co-author of Groovy in Action • Follow me: • My blog: http://glaforge.appspot.com • Twitter: @glaforge • Google+: http://gplus.to/glaforge 3

Slide 4

Slide 4 text

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. Introduction Definitions Examples Goals Pros & cons

Slide 5

Slide 5 text

Domain-Specific Languages • In contrast to General Purpose Languages • Also known as: fluent / humane interfaces, language oriented programming, little or mini languages, macros, business natural languages... 5 { } A Domain-Specific Language is a programming language or executable specification language that offers, through appropriate notations and abstractions, expressive power focused on, and usually restricted to, a particular problem domain.

Slide 6

Slide 6 text

Technical examples GtkWindow HelloWindow 5 destroy gtk_main_quit Hello GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE True True False GtkButton Hello World True Hello World # Poll this site first each cycle. poll pop.provider.net proto pop3 user "jsmith" with pass "secret1" is "smith" here user jones with pass "secret2" is "jjones" here with options keep # Poll this site second, unless Lord Voldemort zaps us first. poll billywig.hogwarts.com with proto imap: user harry_potter with pass "floo" is harry_potter here # Poll this site third in the cycle. # Password will be fetched from ~/.netrc poll mailhost.net with proto imap: user esr is esr here "x.z?z{1,3}y" SELECT * FROM TABLE WHERE NAME LIKE '%SMI' ORDER BY NAME Glade Regex XSLT Fetchmail SQL

Slide 7

Slide 7 text

Antimalaria drug resistance simulation HR skills representation Insurance policy risk calculation engine Market data feeds analysis Loan acceptance rules engine Nuclear safety simulations

Slide 8

Slide 8 text

Goals of DSLs • Use a more expressive language than a general-purpose one • Share a common metaphor of understanding between developers and subject matter experts • Have domain experts help with the design of the business logic of an application • Avoid cluttering business code with too much boilerplate technical code thanks to a clean separation • Let business rules have their own lifecycle 8

Slide 9

Slide 9 text

Pros and cons Pros – Domain experts can help, validate, modify, and often develop DSL programs – Somewhat self-documenting – Enhance quality, productivity, reliability, maintainability, portability, reusability – Safety; as long as the language constructs are safe, any DSL sentence can be considered safe Cons – Learning cost vs. limited applicability – Cost of designing, implementing & maintaining DSLs as well as tools/ IDEs – Attaining proper scope – Trade-offs between domain specificity and general purpose language constructs – Efficiency cost – Proliferation of similar non-standard DSLs 9

Slide 10

Slide 10 text

Groovy provides... • A flexible and malleable syntax – scripts, native syntax constructs (list, map, ranges), closures, less punctuation... • Compile-time and runtime meta-programming – metaclasses, AST transformations – also operator overloading • The ability to easily integrate into Java / Spring apps – also security and safety 10

Slide 11

Slide 11 text

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. Let’s get started!

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Your mission: build a DSL for a Mars robot

Slide 14

Slide 14 text

We need a robot! 14 package  mars class  Robot  {}

Slide 15

Slide 15 text

It should move... 15 package  mars   class  Robot  {        void  move()  {} }

Slide 16

Slide 16 text

..in a direction! 16 package  mars   class  Robot  {        void  move(String  dir)  {} }

Slide 17

Slide 17 text

More explicit direction 17 package  mars   class  Robot  {        void  move(Direction  dir)  {} } package  mars   enum  Direction  {        left,  right,  forward,  backward }

Slide 18

Slide 18 text

Now how can we control it? 18 import  static  mars.Direction.*; import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } }

Slide 19

Slide 19 text

Now how can we control it? 18 import  static  mars.Direction.*; import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } } Syntactical noise!

Slide 20

Slide 20 text

Now how can we control it? 18 import  static  mars.Direction.*; import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } } Syntactical noise!                                                            —                                  — ——————————————————————        ————————————————————————————————————————                —————                                        —                                    —        ——        — —

Slide 21

Slide 21 text

Optional semicolons & parentheses / Scripts vs classes 19 import  static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left

Slide 22

Slide 22 text

Optional semicolons & parentheses / Scripts vs classes 19 import  static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left Optional typing

Slide 23

Slide 23 text

Optional semicolons & parentheses / Scripts vs classes 19 import  static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left But I don’t want to compile a script for every command! Optional typing

Slide 24

Slide 24 text

Integration

Slide 25

Slide 25 text

GroovyShell to the rescue 21

Slide 26

Slide 26 text

GroovyShell to the rescue 21 def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") )

Slide 27

Slide 27 text

GroovyShell to the rescue 21 def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) integration.groovy

Slide 28

Slide 28 text

GroovyShell to the rescue 21 import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) integration.groovy

Slide 29

Slide 29 text

GroovyShell to the rescue 21 import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) integration.groovy command.groovy

Slide 30

Slide 30 text

Integration mechanisms • Different solutions available: – Groovy’s own mechanisms • Eval, GroovyScriptEngine, GroovyShell, GroovyClassLoader, CompilationUnit – Java 6: javax.script.* / JSR-223 • Groovy provides a JSR-223 implementation – Spring’s lang namespace • Groovy provides the highest level of flexibility and customization, but JSR-223 is a standard... 22

Slide 31

Slide 31 text

Integration mechanisms • Different solutions available: – Groovy’s own mechanisms • Eval, GroovyScriptEngine, GroovyShell, GroovyClassLoader, CompilationUnit – Java 6: javax.script.* / JSR-223 • Groovy provides a JSR-223 implementation – Spring’s lang namespace • Groovy provides the highest level of flexibility and customization, but JSR-223 is a standard... 22

Slide 32

Slide 32 text

What’s wrong with our DSL? 23 import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left

Slide 33

Slide 33 text

What’s wrong with our DSL? 23 import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we hide those imports?

Slide 34

Slide 34 text

What’s wrong with our DSL? 23 import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we hide those imports? Can’t we inject the robot?

Slide 35

Slide 35 text

What’s wrong with our DSL? 23 import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we hide those imports? Can’t we inject the robot? Do we really need to repeat ‘robot’?

Slide 36

Slide 36 text

I’m sorry Dave, you can’t do that!

Slide 37

Slide 37 text

I’m sorry Dave, you can’t do that!

Slide 38

Slide 38 text

What we really want is... 25  move  left  

Slide 39

Slide 39 text

Let’s inject a robot! • We can pass data in / out of scripts through the Binding – basically just a map of variable name keys and their associated values 26

Slide 40

Slide 40 text

Let’s inject a robot! • We can pass data in / out of scripts through the Binding – basically just a map of variable name keys and their associated values 26 def  binding  =  new  Binding([        robot:  new  mars.Robot() ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )

Slide 41

Slide 41 text

Let’s inject a robot! • We can pass data in / out of scripts through the Binding – basically just a map of variable name keys and their associated values 26 def  binding  =  new  Binding([        robot:  new  mars.Robot() ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) integration.groovy

Slide 42

Slide 42 text

Better? 27 import  static  mars.Direction.* robot.move  left

Slide 43

Slide 43 text

Better? 27 import  static  mars.Direction.* robot.move  left Robot import removed

Slide 44

Slide 44 text

Better? 27 import  static  mars.Direction.* robot.move  left Robot import removed Robot injected, no ‘new’ needed

Slide 45

Slide 45 text

How to inject the direction? • Using the binding... 28 import  mars.* def  binding  =  new  Binding([        robot:  new  Robot(),        left:          Direction.left,        right:        Direction.right,        backward:  Direction.backward,        forward:    Direction.forward ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )

Slide 46

Slide 46 text

How to inject the direction? • Using the binding... 28 import  mars.* def  binding  =  new  Binding([        robot:  new  Robot(),        left:          Direction.left,        right:        Direction.right,        backward:  Direction.backward,        forward:    Direction.forward ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) Fragile in case of new directions!

Slide 47

Slide 47 text

How to inject the direction? • Using the binding... 29 import  mars.*   def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        } ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) Spread map operator

Slide 48

Slide 48 text

How to inject the direction? • Using string concatenation? • Using compiler customizers 30

Slide 49

Slide 49 text

String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Slide 50

Slide 50 text

String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left") Cheat with string concatenation? Bad!

Slide 51

Slide 51 text

String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Slide 52

Slide 52 text

String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left") Line #1 becomes Line #2

Slide 53

Slide 53 text

String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Slide 54

Slide 54 text

Compilation customizers • Ability to apply some customization to the Groovy compilation process • Three available customizers – ImportCustomizer: add transparent imports – ASTTransformationCustomizer: injects an AST transform – SecureASTCustomizer: restrict the groovy language to an allowed subset • But you can implement your own 32

Slide 55

Slide 55 text

Imports customizer 33 def  configuration  =  new  CompilerConfiguration()   def  imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports)     new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                                  configuration)        .evaluate("robot.move  left")            

Slide 56

Slide 56 text

AST transformation customizer 34 def  configuration  =  new  CompilerConfiguration()   def  imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports,                          new  ASTTransformationCustomizer(Log))   new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                                  configuration)        .evaluate("robot.move  left"  +  "\n"                            "log.info  ‘Robot  moved’")                      

Slide 57

Slide 57 text

AST transformation customizer 34 def  configuration  =  new  CompilerConfiguration()   def  imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports,                          new  ASTTransformationCustomizer(Log))   new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                                  configuration)        .evaluate("robot.move  left"  +  "\n"                            "log.info  ‘Robot  moved’")                       @Log injects a logger in scripts and classes

Slide 58

Slide 58 text

Secure the onboard trajectory calculator

Slide 59

Slide 59 text

Secure AST customizer • Let’s set up our environment – an import customizer to import java.lang.Math.* – prepare a secure AST customizer 36 def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer()

Slide 60

Slide 60 text

Secure AST customizer • Let’s set up our environment – an import customizer to import java.lang.Math.* – prepare a secure AST customizer 36 def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer() Idea: secure the rocket’s onboard trajectory calculation system by allowing only math expressions to be evaluated by the calculator

Slide 61

Slide 61 text

Secure AST customizer 37 ... secure.with  { //  disallow  closure  creation closuresAllowed  =  false   //  disallow  method  definitions methodDefinitionAllowed  =  false     //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ...

Slide 62

Slide 62 text

Secure AST customizer 37 ... secure.with  { //  disallow  closure  creation closuresAllowed  =  false   //  disallow  method  definitions methodDefinitionAllowed  =  false     //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ... Disallow closures and methods

Slide 63

Slide 63 text

Secure AST customizer 37 ... secure.with  { //  disallow  closure  creation closuresAllowed  =  false   //  disallow  method  definitions methodDefinitionAllowed  =  false     //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ... Disallow closures and methods Black / white list imports

Slide 64

Slide 64 text

Secure AST customizer 38 ... //  language  tokens  allowed tokensWhitelist  =  [ PLUS,  MINUS,  MULTIPLY,  DIVIDE,  MOD,  POWER,  PLUS_PLUS,  MINUS_MINUS,   COMPARE_EQUAL,  COMPARE_NOT_EQUAL,  COMPARE_LESS_THAN,  COMPARE_LESS_THAN_EQUAL,   COMPARE_GREATER_THAN,  COMPARE_GREATER_THAN_EQUAL ]   //  types  allowed  to  be  used  (including  primitive  types) constantTypesClassesWhiteList  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ...

Slide 65

Slide 65 text

Secure AST customizer 38 ... //  language  tokens  allowed tokensWhitelist  =  [ PLUS,  MINUS,  MULTIPLY,  DIVIDE,  MOD,  POWER,  PLUS_PLUS,  MINUS_MINUS,   COMPARE_EQUAL,  COMPARE_NOT_EQUAL,  COMPARE_LESS_THAN,  COMPARE_LESS_THAN_EQUAL,   COMPARE_GREATER_THAN,  COMPARE_GREATER_THAN_EQUAL ]   //  types  allowed  to  be  used  (including  primitive  types) constantTypesClassesWhiteList  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ... You can build a subset of the Groovy syntax!

Slide 66

Slide 66 text

Secure AST customizer 38 ... //  language  tokens  allowed tokensWhitelist  =  [ PLUS,  MINUS,  MULTIPLY,  DIVIDE,  MOD,  POWER,  PLUS_PLUS,  MINUS_MINUS,   COMPARE_EQUAL,  COMPARE_NOT_EQUAL,  COMPARE_LESS_THAN,  COMPARE_LESS_THAN_EQUAL,   COMPARE_GREATER_THAN,  COMPARE_GREATER_THAN_EQUAL ]   //  types  allowed  to  be  used  (including  primitive  types) constantTypesClassesWhiteList  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ... You can build a subset of the Groovy syntax! Black / white list usage of classes

Slide 67

Slide 67 text

Secure AST customizer • Ready to evaluate our flight equations! • But the following would have failed: 39 shell.evaluate  'System.exit(0)' def  config  =  new  CompilerConfiguration() config.addCompilationCustomizers(imports,  secure) def  shell  =  new  GroovyShell(config)   shell.evaluate  'cos  PI/3'

Slide 68

Slide 68 text

Back to our robot... 40 robot.move  left

Slide 69

Slide 69 text

Back to our robot... 40 robot.move  left Still need to get rid of the robot prefix!

Slide 70

Slide 70 text

Can we remove it?

Slide 71

Slide 71 text

Can we remove it? Yes !

Slide 72

Slide 72 text

How to get rid of the ‘robot’? • Instead of calling the move() method on the robot instance, we should be able to call the move() method directly from within the script • Two approaches 42 • Inject a ‘move’ closure in the binding with a method pointer • Use a base script class with a ‘move’ method delegating to the robot

Slide 73

Slide 73 text

Inject a closure in the binding 43 def  robot  =  new  mars.Robot() binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        move:  robot.&move ])

Slide 74

Slide 74 text

Inject a closure in the binding 43 def  robot  =  new  mars.Robot() binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        move:  robot.&move ]) Method pointer (a closure) on robot’s move instance method

Slide 75

Slide 75 text

Define a base script class 44 abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } }

Slide 76

Slide 76 text

Define a base script class 44 abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } The move() method is now at the script level

Slide 77

Slide 77 text

Define a base script class 44 abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } The move() method is now at the script level Access the robot through the script’s binding

Slide 78

Slide 78 text

Configure the base script class 45 def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass

Slide 79

Slide 79 text

Configure the base script class 45 def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass Scripts evaluated with this configuration will inherit from that class

Slide 80

Slide 80 text

Ready for lift off!    move  left

Slide 81

Slide 81 text

Beep, beep... yes but how do you define the speed? ...beep...

Slide 82

Slide 82 text

Oh no!

Slide 83

Slide 83 text

What we could do now is... 49 move  left,  at:  3.km/h

Slide 84

Slide 84 text

What we could do now is... 49 move  left,  at:  3.km/h Mix of named and normal parameters

Slide 85

Slide 85 text

What we could do now is... 49 move  left,  at:  3.km/h How to support this speed notation? Mix of named and normal parameters

Slide 86

Slide 86 text

Supporting the speed notation • We need to: – define units of distance, time and speed • DistanceUnit and Distance • TimeUnit and Duration • Speed – have a nice notation for them by adding properties to numbers – be able to define speed thanks to operator overloading 50

Slide 87

Slide 87 text

Distance unit enum and distance 51 enum  DistanceUnit  {        centimeter  ('cm',        0.01),        meter            (  'm',        1      ),          kilometer    ('km',  1000      )                  String  abbreviation        double  multiplier                DistanceUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   }

Slide 88

Slide 88 text

Distance unit enum and distance 51 enum  DistanceUnit  {        centimeter  ('cm',        0.01),        meter            (  'm',        1      ),          kilometer    ('km',  1000      )                  String  abbreviation        double  multiplier                DistanceUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   } @TupleConstructor   class  Distance  {        double  amount          DistanceUnit  unit        String  toString()  {                  "$amount  $unit"          }   }

Slide 89

Slide 89 text

Time unit enum and duration 52 enum  TimeUnit  {        hour            (    'h',  3600),        minute        ('min',      60),          second        (    's',        1)                  String  abbreviation        double  multiplier                TimeUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   }

Slide 90

Slide 90 text

Time unit enum and duration 52 enum  TimeUnit  {        hour            (    'h',  3600),        minute        ('min',      60),          second        (    's',        1)                  String  abbreviation        double  multiplier                TimeUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   } @TupleConstructor   class  Duration  {        double  amount          TimeUnit  unit        String  toString()  {                  "$amount  $unit"          }   }

Slide 91

Slide 91 text

Now at (light!) speed 53 @TupleConstructor   class  Speed  {        Distance  distance        Duration  dur        String  toString()  {                  "$distance/$dur"          }   } speed = distance duration

Slide 92

Slide 92 text

First, we need the distance notation • We add a dynamic property to numbers by adding a getter to them and use the property notation shortcut: 54 2.km 2.getKm() 㱻

Slide 93

Slide 93 text

Techniques to add properties to numbers • To add dynamic methods or properties, there are several approaches at your disposal: – ExpandoMetaClass – custom MetaClass – Categories – Extension modules • Let’s have a look at the ExpandoMetaClass 55

Slide 94

Slide 94 text

Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   }

Slide 95

Slide 95 text

Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } Add that to integration.groovy

Slide 96

Slide 96 text

Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } Add that to integration.groovy ‘delegate’ is the current number

Slide 97

Slide 97 text

Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } 40.cm   3.5.m 4.km Add that to integration.groovy ‘delegate’ is the current number Usage in your DSLs

Slide 98

Slide 98 text

Distance okay, but speed? • For distance, we just added a property access after the number, but we now need to divide (‘div’) by the time 57 2.km/h

Slide 99

Slide 99 text

Distance okay, but speed? • For distance, we just added a property access after the number, but we now need to divide (‘div’) by the time 57 2.km/h The div() method on Distance

Slide 100

Slide 100 text

Distance okay, but speed? • For distance, we just added a property access after the number, but we now need to divide (‘div’) by the time 57 2.km/h The div() method on Distance An ‘h’ duration instance in the binding

Slide 101

Slide 101 text

Inject the ‘h’ hour constant in the binding 58 def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        h:  new  Duration(1,  TimeUnit.hour) ])

Slide 102

Slide 102 text

Inject the ‘h’ hour constant in the binding 58 def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        h:  new  Duration(1,  TimeUnit.hour) ]) An ‘h’ duration added to the binding

Slide 103

Slide 103 text

Operator overloading • Currency amounts – 15.euros + 10.dollars • Distance handling – 10.km - 10.m • Workflow, concurrency – taskA | taskB & taskC • Credit an account – account << 10.dollars account += 10.dollars account.credit 10.dollars 59 a  +  b      //  a.plus(b) a  -­‐  b      //  a.minus(b) a  *  b      //  a.multiply(b) a  /  b      //  a.div(b) a  %  b      //  a.modulo(b) a  **  b    //  a.power(b) a  |  b      //  a.or(b) a  &  b      //  a.and(b) a  ^  b      //  a.xor(b) a[b]        //  a.getAt(b) a  <<  b    //  a.leftShift(b) a  >>  b    //  a.rightShift(b) a  >>>  b  //  a.rightShiftUnsigned(b) +a            //  a.unaryPlus() -­‐a            //  a.unaryMinus() ~a            //  a.bitwiseNegate()

Slide 104

Slide 104 text

Operator overloading • Update the Distance class with a div() method following the naming convention for operators 60 class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... }

Slide 105

Slide 105 text

Operator overloading • Update the Distance class with a div() method following the naming convention for operators 60 class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } Optional return

Slide 106

Slide 106 text

Equivalence of notation • Those two notations are actually equivalent: 61 2.km/h 2.getKm().div(h) 㱻

Slide 107

Slide 107 text

Equivalence of notation • Those two notations are actually equivalent: 61 2.km/h 2.getKm().div(h) 㱻 This one might be slightly more verbose!

Slide 108

Slide 108 text

Named parameters usage move  left,  at:  3.km/h 62

Slide 109

Slide 109 text

Named parameters usage move  left,  at:  3.km/h 62 Normal parameter

Slide 110

Slide 110 text

Named parameters usage move  left,  at:  3.km/h 62 Named parameter Normal parameter

Slide 111

Slide 111 text

Named parameters usage move  left,  at:  3.km/h Will call: def  move(Map  m,  Direction  q) 62 Named parameter Normal parameter

Slide 112

Slide 112 text

Named parameters usage move  left,  at:  3.km/h Will call: def  move(Map  m,  Direction  q) 62 Named parameter Normal parameter All named parameters go into the map argument

Slide 113

Slide 113 text

Named parameters usage move  left,  at:  3.km/h Will call: def  move(Map  m,  Direction  q) 62 Named parameter Normal parameter All named parameters go into the map argument Positional parameters come afterwards

Slide 114

Slide 114 text

Named parameters usage move  left,  at:  3.km/h 63

Slide 115

Slide 115 text

Named parameters usage move  left,  at:  3.km/h 63 Can we get rid of the comma?

Slide 116

Slide 116 text

Named parameters usage move  left,  at:  3.km/h 63 Can we get rid of the comma? What about the colon too?

Slide 117

Slide 117 text

Command chains • A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println • Less dots, less parens allow you to – write more readable business rules – in almost plain English sentences • (or any language, of course) 64

Slide 118

Slide 118 text

Command chains  move  left    at  3.km/h   65

Slide 119

Slide 119 text

Command chains  move  left    at  3.km/h   Alternation of method names 65

Slide 120

Slide 120 text

Command chains  move  left    at  3.km/h   Alternation of method names and parameters (even named ones) 65

Slide 121

Slide 121 text

Command chains  move  left    at  3.km/h   65

Slide 122

Slide 122 text

Command chains  move  left    at  3.km/h            (        ).    (            ) Equivalent to: 65

Slide 123

Slide 123 text

Look Ma! No parens, no dots!

Slide 124

Slide 124 text

Command chains //  Java  fluent  API  approach class  Robot  {        ...        def  move(Direction  dir)  {                this.dir  =  dir                return  this        }        def  at(Speed  speed)  {                this.speed  =  speed                return  this        }        ... } 67

Slide 125

Slide 125 text

Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } 68

Slide 126

Slide 126 text

Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } 68 Nested maps and closures

Slide 127

Slide 127 text

Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } 68 move  left  at  3.km/h Usage in your DSLs Nested maps and closures

Slide 128

Slide 128 text

Command chains 69

Slide 129

Slide 129 text

Command chains //  methods  with  multiple  arguments  (commas) 69

Slide 130

Slide 130 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor 69

Slide 131

Slide 131 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation 69

Slide 132

Slide 132 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good 69

Slide 133

Slide 133 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures 69

Slide 134

Slide 134 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} 69

Slide 135

Slide 135 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens 69

Slide 136

Slide 136 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names 69

Slide 137

Slide 137 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms 69

Slide 138

Slide 138 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms deploy  left    arm 69

Slide 139

Slide 139 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms deploy  left    arm        (            ).        (                      ).      (            ) 69

Slide 140

Slide 140 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        ) 69

Slide 141

Slide 141 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        )          (    ).        (    ).        (    ) 69

Slide 142

Slide 142 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        )          (    ).        (    ).        (    )            (      ).                .        (          ) 69

Slide 143

Slide 143 text

Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names //  possible  with  an  odd  number  of  terms deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        )          (    ).        (    ).        (    )            (      ).                .        (          )            (        ). 69

Slide 144

Slide 144 text

Final result 70

Slide 145

Slide 145 text

Final result 70 move  forward  at  3.km/h

Slide 146

Slide 146 text

move forward at 3.km/h

Slide 147

Slide 147 text

move forward at 3.km/h Yes! We did it!

Slide 148

Slide 148 text

What about security and safety?

Slide 149

Slide 149 text

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. Security and Safety JVM Security Managers SecureASTCustomizer Sandboxing Controlling script execution

Slide 150

Slide 150 text

Play it safe in a sandbox

Slide 151

Slide 151 text

Playing it safe... • You have to think carefully about what DSL users are allowed to do with your DSL • Forbid things which are not allowed – leverage the JVM’s Security Managers • this might have an impact on performance – use a Secure AST compilation customizer • not so easy to think about all possible cases – avoid long running scripts with *Interrupt transformations 75

Slide 152

Slide 152 text

Security Managers • Groovy is just a language leaving on the JVM, so you have access to the usual Security Managers mechanism – Nothing Groovy specific here – Please check the documentation on Security Managers and how to design policy files 76

Slide 153

Slide 153 text

SecureASTCustomizer 77 def  secure  =  new  SecureASTCustomizer() secure.with  { //  disallow  closure  creation      closuresAllowed  =  false   //  disallow  method  definitions      methodDefinitionAllowed  =  false   //  empty  white  list  =>  forbid  certain  imports      importsWhitelist  =  [...]        staticImportsWhitelist  =  [...] //  only  allow  some  static  import      staticStarImportsWhitelist  =  [...] //  language  tokens  allowed        tokensWhitelist  =  [...] //  types  allowed  to  be  used      constantTypesClassesWhiteList  =  [...] //  classes  who  are  allowed  to  be  receivers  of  method  calls      receiversClassesWhiteList  =  [...] } def  config  =  new  CompilerConfiguration() config.addCompilationCustomizers(secure) def  shell  =  new  GroovyShell(config)

Slide 154

Slide 154 text

Controlling code execution • Your application may run user’s code – what if the code runs in infinite loops or for too long? – what if the code consumes too many resources? • 3 new transforms at your rescue – @ThreadInterrupt: adds Thread#isInterrupted checks so your executing thread stops when interrupted – @TimedInterrupt: adds checks in method and closure bodies to verify it’s run longer than expected – @ConditionalInterrupt: adds checks with your own conditional logic to break out from the user code 78

Slide 155

Slide 155 text

@ThreadInterrupt 79 @ThreadInterrupt import  groovy.transform.ThreadInterrupt     while  (true)  {        //  Any  extraterestrial  around? }

Slide 156

Slide 156 text

@ThreadInterrupt 79 @ThreadInterrupt import  groovy.transform.ThreadInterrupt     while  (true)  {        //  Any  extraterestrial  around? }          if  (Thread.currentThread().isInterrupted())                throw  new  InterruptedException() { }

Slide 157

Slide 157 text

@TimedInterrupt • InterruptedException thrown when checks indicate code ran longer than desired 80 @TimedInterrupt(10) import  groovy.transform.TimedInterrupt     while  (true)  {        move  left        //  circle  forever }

Slide 158

Slide 158 text

@ConditionalInterrupt • Specify your own conditions to be inserted at the start of method and closure bodies – check for available resources, number of times run, etc. • Leverages closure annotation parameters 81 @ConditionalInterrupt({  battery.level  <  0.1  }) import  groovy.transform.ConditionalInterrupt 100.times  {                move  forward  at  10.km/h }

Slide 159

Slide 159 text

@ConditionalInterrupt • Specify your own conditions to be inserted at the start of method and closure bodies – check for available resources, number of times run, etc. • Leverages closure annotation parameters 81 @ConditionalInterrupt({  battery.level  <  0.1  }) import  groovy.transform.ConditionalInterrupt 100.times  {                move  forward  at  10.km/h } Can we avoid typing the conditional interrupt?

Slide 160

Slide 160 text

@ConditionalInterrupt • Specify your own conditions to be inserted at the start of method and closure bodies – check for available resources, number of times run, etc. • Leverages closure annotation parameters 82 100.times  {                move  forward  at  10.km/h } Yes! Using compilation customizers

Slide 161

Slide 161 text

Using compilation customizers • In our previous examples, the usage of the interrupts were explicit, and users had to type them – if they want to deplete the battery of your robot, they won’t use interrupts, so you have to impose interrupts yourself • With compilation customizers you can inject those interrupts thanks to the AST Transformation Customizer 83

Slide 162

Slide 162 text

What about tooling?

Slide 163

Slide 163 text

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. Tooling Why tooling? DSL descriptors Pointcuts and contributions Packaging DSLDs

Slide 164

Slide 164 text

Why tooling? • I know what this language means –why do I want anything more? • But, tooling can make things even better –syntax checking –content assist –search –inline documentation 86

Slide 165

Slide 165 text

88 Let’s use an IDE 88 • I hear Groovy-Eclipse is pretty good…

Slide 166

Slide 166 text

88 Let’s use an IDE 88 • I hear Groovy-Eclipse is pretty good…

Slide 167

Slide 167 text

88 Let’s use an IDE 88 • I hear Groovy-Eclipse is pretty good… Uh oh!

Slide 168

Slide 168 text

88 Let’s use an IDE 88 • I hear Groovy-Eclipse is pretty good… Uh oh! Can we do better?

Slide 169

Slide 169 text

Of course! • Eclipse is extensible – with a plugin architecture 88 Eclipse platform Platform runtime WorkBench JFace SWT Workspace Help Team New plugin New tool

Slide 170

Slide 170 text

I want my DSL supported in Eclipse 89

Slide 171

Slide 171 text

I want my DSL supported in Eclipse • Let’s create a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it 89

Slide 172

Slide 172 text

I want my DSL supported in Eclipse • Let’s create a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version 89

Slide 173

Slide 173 text

I want my DSL supported in Eclipse • Let’s create a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version 89 Uh oh!

Slide 174

Slide 174 text

I want my DSL supported in Eclipse • Let’s create a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version 89 Uh oh! Can we do better?

Slide 175

Slide 175 text

Of course! • Groovy is extensible! – Meta-Object Protocol – Metaprogramming – DSLs... 90

Slide 176

Slide 176 text

DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL 91

Slide 177

Slide 177 text

DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – Powerful – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries 91

Slide 178

Slide 178 text

DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – Powerful – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries 91 DSL Descriptors (DSLD)

Slide 179

Slide 179 text

DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – Powerful – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries 91 DSL Descriptors (DSLD) In IntelliJ. called GDSL

Slide 180

Slide 180 text

Let’s start simple 92 move deploy h left right forward backward

Slide 181

Slide 181 text

Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer 92 move deploy h left right forward backward

Slide 182

Slide 182 text

Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 92 move deploy h left right forward backward

Slide 183

Slide 183 text

Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 92 Pointcut move deploy h left right forward backward

Slide 184

Slide 184 text

Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 92 Pointcut Contribution block move deploy h left right forward backward

Slide 185

Slide 185 text

Anatomy of a DSLD script •Pointcuts – Where to do it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93

Slide 186

Slide 186 text

Anatomy of a DSLD script •Pointcuts – Where to do it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93 Where

Slide 187

Slide 187 text

Anatomy of a DSLD script •Pointcuts – Where to do it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93 Where What

Slide 188

Slide 188 text

Anatomy of a DSLD script •Pointcuts – Where to do it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93 Not at runtime... only while editing Where What

Slide 189

Slide 189 text

Talking about « x » 94 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }

Slide 190

Slide 190 text

Talking about « x » 94 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } Current type

Slide 191

Slide 191 text

Talking about « x » 94 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } Current type Enclosing class

Slide 192

Slide 192 text

Talking about « x » 94 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } Current type Enclosing class Enclosing method

Slide 193

Slide 193 text

Pointcuts 95

Slide 194

Slide 194 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type

Slide 195

Slide 195 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script

Slide 196

Slide 196 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot")

Slide 197

Slide 197 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot"))

Slide 198

Slide 198 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move"))

Slide 199

Slide 199 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic"))

Slide 200

Slide 200 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic")) //  combining  them,  and  using  the  logical  and

Slide 201

Slide 201 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic")) //  combining  them,  and  using  the  logical  and isScript(        annotatedBy("groovy.dsl.Robotic")

Slide 202

Slide 202 text

Pointcuts 95 currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic")) //  combining  them,  and  using  the  logical  and isScript(        annotatedBy("groovy.dsl.Robotic") )  &  currentType(method("move"))

Slide 203

Slide 203 text

What goes in a contribution block? 96

Slide 204

Slide 204 text

What goes in a contribution block? • property: “adds” a property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” 96

Slide 205

Slide 205 text

What goes in a contribution block? • property: “adds” a property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams 96

Slide 206

Slide 206 text

What goes in a contribution block? • property: “adds” a property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 96

Slide 207

Slide 207 text

What goes in a contribution block? • property: “adds” a property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 96 contribute(...)  {    property  name:  "myName"    method  name:  "getMyName"    delegatesTo  type:              "some.other.Type" }

Slide 208

Slide 208 text

Wait... isn’t this Aspect-Oriented Programming? 97

Slide 209

Slide 209 text

Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP 97

Slide 210

Slide 210 text

Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime 97

Slide 211

Slide 211 text

Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime • DSLD: pointcuts and contribution blocks – operates on AST in the editor org.codehaus.groovy.ast.expr.* 97

Slide 212

Slide 212 text

Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime • DSLD: pointcuts and contribution blocks – operates on AST in the editor org.codehaus.groovy.ast.expr.* • Join Point Model – Join points (e.g., instructions, expressions) – Mechanism for quantifying join points (e.g., pointcuts) – Means of affect at a join point (e.g., advice, contribution blocks) 97

Slide 213

Slide 213 text

How do we ship it? 98 • jar/war file • DSLD file: – as source in dsld package • Hint: – Use script folder support in preferences – **/*.dsld to be copied to bin folder as source • Can also use maven or gradle

Slide 214

Slide 214 text

To summarize: Editing support for DSLs • Getting it out there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 99 DSLD contribution blocks pointcuts

Slide 215

Slide 215 text

To summarize: Editing support for DSLs • Getting it out there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 99 DSLD contribution blocks pointcuts Where

Slide 216

Slide 216 text

To summarize: Editing support for DSLs • Getting it out there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 99 DSLD contribution blocks pointcuts Where What

Slide 217

Slide 217 text

© 2013 SpringOne 2GX. All rights reserved. Do not distribute without permission. What have we learnt?

Slide 218

Slide 218 text

Groovy Power!™ • A flexible and malleable syntax – scripts vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs – operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences – for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox • Groovy DSLs can be tooled for improved authoring capabilities 101

Slide 219

Slide 219 text

Groovy Power!™ • A flexible and malleable syntax – scripts vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs – operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences – for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox • Groovy DSLs can be tooled for improved authoring capabilities 101 Groovy is a great fit for DSLs!

Slide 220

Slide 220 text

And there’s more! • We haven’t dived into... – How to implement your own control structures with closures – How to create Groovy « builders » – How to define extension modules – How to hijack the Groovy syntax to develop our own language extensions with AST Transformations – Source preprocessing for custom syntax – How to use the other metaprogramming techniques available – How to improve error reporting with customizers 102

Slide 221

Slide 221 text

Thank you! 103

Slide 222

Slide 222 text

Questions & Answers 104 Got questions, really?

Slide 223

Slide 223 text

Questions & Answers 104 Got questions, really? I might have answers!

Slide 224

Slide 224 text

Image credits • Wikipedia logo: http://www.geekosystem.com/wp-content/uploads/2011/01/wikipedia-logo.png • Chains: http://2.bp.blogspot.com/-GXDVqUYSCa0/TVdBsON4tdI/AAAAAAAAAW4/EgJOUmAxB28/s1600/breaking-chains5_copy9611.jpg • Space odissey: http://dearjesus.files.wordpress.com/2010/04/2001_a_space_odyssey_1.jpg • Yes we scan: http://i.huffpost.com/gen/1218045/thumbs/o-YES-WE-SCAN-facebook.jpg • HAL red: http://2.bp.blogspot.com/-yjsyPxUFicY/TcazwAltOaI/AAAAAAAAAho/GVT7wGhnrUM/s1600/2001-a-space-odyssey-HAL.jpg • USSR Space posters: http://www.flickr.com/photos/justinvg • General: http://www.defense.gov/dodcmsshare/newsphoto/2009-02/hires_090217-D-7203C-004.jpg • Rocket: http://astro.vision.free.fr/download/fonds/7/saturn5c.jpg • Star Trek / 747: http://24.media.tumblr.com/tumblr_m35foiJI6a1qzz0iho1_1280.jpg • Man in space: http://www.nasa.gov/images/content/60130main_image_feature_182_jwfull.jpg • Sputnik 2: http://launiusr.files.wordpress.com/2010/06/sputnik2.jpg • Lunakod: http://www.astr.ua.edu/keel/space/lunakhod_moscow.jpg • Sandbox: http://www.turnbacktogod.com/wp-content/uploads/2008/09/sandbox.jpg • Repair: http://www.oneangels.com/wp-content/uploads/2012/03/repair1.jpg • Mars rover: http://wallpapers.free-review.net/wallpapers/49/Mars_rover%2C_Mars_-_03.jpg • Mars rover 2: http://www.universetoday.com/wp-content/uploads/2011/06/551038main_pia14156-43_946-710.jpg • Thumb: http://www.wpclipart.com/sign_language/thumbs_up_large.png.html • Night sky: http://www.aplf-planetariums.info/galeries/ciel_profond/2004-07-01-Voie_Lactee_Scorpion-Jean-Luc_PUGLIESI.jpg • Obama yes we can: http://www.dessinemoiunboulon.net/wp-content/uploads/2009/01/obama-yes-we-can_04-nov-08.jpg • Hook: http://winningware.com/blog/wp-content/uploads/2009/12/FishHookXSmall.jpg • HP 48 GX: http://calculators.torensma.net/files/images/hewlett-packard_hp-48g.jpg • Omer: http://www.irmin.com/wallpaper/TV/Homer%20Simpson%20Oh%20No.jpg • Cadenat: http://acsgsecurite.com/upl/site/cadenat.png • Thanks: http://4.bp.blogspot.com/-hTdT5Ebk5ws/Tu_x2tE4ccI/AAAAAAAAAZc/pxtG8A0w7VE/s1600/thanks-digital-calligraphy-md.png • Buzz Aldrin: http://2.bp.blogspot.com/-rpV5Oy5N78U/TprVli-2ZdI/AAAAAAAABN8/WiHob4rp2b8/s1600/Astronaut.jpg 105