Your SlideShare is downloading. ×
Groovy ds ls_spring_one2gx_2012
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

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

Groovy ds ls_spring_one2gx_2012

147

Published on

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

  • Be the first to like this

No Downloads
Views
Total Views
147
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
9
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Groovy Domain-Specific Languages Andrew Eisenberg Paul King Guillaume Laforge Groovy Eclipse Project Lead SpringSource / VMware Groovy Core Developer ASERT Groovy Project Manager SpringSource / VMware @werdnagreb @paulk_asert @glaforge © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 2. Andrew Eisenberg • Groovy-Eclipse project lead • Senior Member of Technical Staff,VMware Tools Team – Grails-IDE, AJDT, STS, GGTS, Scripted, Orion project • PhD in Computer Science from University of British Columbia • Follow me: – Twitter: @werdnagreb – Blog: http://contraptionsforprogramming.blogspot.ca/ – Google+: http://gplus.to/aeisenberg 2
  • 3. Paul King • Groovy Core Committer • Leads ASERT – software, training, consultancy company based in Brisbane, Australia • Co-author of Groovy in Action • Follow me: – Twitter: @paulk_asert 3
  • 4. Guillaume Laforge • Groovy Project Manager at VMware • 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 4
  • 5. Introduction Definitions Examples Goals Pros & cons © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 6. Domain-Specific Languages { 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. • In contrast to General Purprose Languages • Also known as: fluent / humane interfaces, language oriented programming, little or mini languages, macros, business natural languages... 6 }
  • 7. Technical examples Glade <?xml version="1.0"?> <GTK-Interface> <widget> <class>GtkWindow</class> <name>HelloWindow</name> <border_width>5</border_width> <Signal> <name>destroy</name> <handler>gtk_main_quit</handler> </Signal> <title>Hello</title> <type>GTK_WINDOW_TOPLEVEL</type> <position>GTK_WIN_POS_NONE</position> <allow_shrink>True</allow_shrink> <allow_grow>True</allow_grow> <auto_shrink>False</auto_shrink> <widget> <class>GtkButton</class> <name>Hello World</name> <can_focus>True</can_focus> <label>Hello World</label> </widget> </widget> </GTK-Interface> XSLT <?xml version="1.0"?> <xsl:stylesheetversion="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:element name="{name()}"> <xsl:for-each select="@*"> <xsl:element name="{name()}"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> <xsl:apply-templates select="*|text()"/> </xsl:element> </xsl:template> </xsl:stylesheet> SQL SELECT * FROM TABLE WHERE NAME LIKE '%SMI' ORDER BY NAME Regex "x.z?z{1,3}y" Fetchmail # 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
  • 8. Antimalaria drug resistance simulation Insurance policy risk calculation engine HR skills representation Nuclear safety simulations Market data feeds analysis Loan acceptance rules engine
  • 9. 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 9
  • 10. 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 10 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
  • 11. 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 11
  • 12. Let’s get started! © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 13. Your mission: build a DSL for a Mars robot
  • 14. We need a robot! package  mars class  Robot  {} 15
  • 15. It should move... package  mars   class  Robot  {        void  move()  {} } 16
  • 16. ..in a direction! package  mars   class  Robot  {        void  move(String  dir)  {} } 17
  • 17. More explicit direction package  mars   class  Robot  {        void  move(Direction  dir)  {} } package  mars   enum  Direction  {        left,  right,  forward,  backward } 18
  • 18. Now how can we control it? import  static  mars.Direction.*; import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } } 19
  • 19. Now how can we control it? 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! 19
  • 20. Now how can we control it?                                                            — 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! 19
  • 21. Optional semicolons & parentheses / Scripts vs classes import  static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left 20
  • 22. Optional semicolons & parentheses / Scripts vs classes import  static  mars.Direction.* import  mars.Robot Optional typing                def      robot  =  new  Robot()                robot.move  left 20
  • 23. Optional semicolons & parentheses / Scripts vs classes import  static  mars.Direction.* import  mars.Robot Optional typing                def      robot  =  new  Robot()                robot.move  left But I don’t want to compile a script for every command! 20
  • 24. Integration
  • 25. GroovyShell to the rescue 22
  • 26. GroovyShell to the rescue def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) 22
  • 27. GroovyShell to the rescue integration.groovy 22 def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") )
  • 28. GroovyShell to the rescue integration.groovy def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left 22
  • 29. GroovyShell to the rescue integration.groovy def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left 22 command.groovy
  • 30. Integration mechanisms • Different solutions available: – Groovy’s own mechanisms • GroovyScriptEngine, GroovyShell, GroovyClassLoader, Eval – 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... 23
  • 31. Integration mechanisms • Different solutions available: – Groovy’s own mechanisms • GroovyScriptEngine, GroovyShell, GroovyClassLoader, Eval – 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... 23
  • 32. What’s wrong with our DSL? import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left 24
  • 33. What’s wrong with our DSL? Can’t we hide those imports? import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left 24
  • 34. What’s wrong with our DSL? Can’t we hide those imports? import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we inject the robot? 24
  • 35. What’s wrong with our DSL? Can’t we hide those imports? import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left Do we really need to repeat ‘robot’? 24 Can’t we inject the robot?
  • 36. I’m sorry Dave, you can’t do that!
  • 37. I’m sorry Dave, you can’t do that!
  • 38. What we really want is...  move  left   26
  • 39. 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 27
  • 40. 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 def  binding  =  new  Binding([        robot:  new  mars.Robot() ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) 27
  • 41. 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 integration.groovy def  binding  =  new  Binding([        robot:  new  mars.Robot() ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) 27
  • 42. Better? import  static  mars.Direction.* robot.move  left 28
  • 43. Better? Robot import removed import  static  mars.Direction.* robot.move  left 28
  • 44. Better? Robot import removed import  static  mars.Direction.* robot.move  left Robot injected, no ‘new’ needed 28
  • 45. How to inject the direction? • Using the binding... 29 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") )
  • 46. How to inject the direction? • Using the binding... 29 import  mars.* Fragile in case of new directions! 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") )
  • 47. How to inject the direction? • Using the binding... Spread map operator 30 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") )
  • 48. How to inject the direction? • Using string concatenation? • Using compiler customizers 31
  • 49. String concatenation? Bad idea! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left") 32
  • 50. String concatenation? Bad idea! Cheat with string concatenation? Bad! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left") 32
  • 51. String concatenation? Bad idea! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left") 32
  • 52. String concatenation? Bad idea! Line #1 becomes Line #2 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left") 32
  • 53. String concatenation? Bad idea! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left") 32
  • 54. Compilation customizers • Ability to apply some customization to the Groovy compilation process • Three available customizers Groovy 1.8 – ImportCustomizer: add transparent imports – ASTTransformationCustomizer: injects an AST transform – SecureASTCustomizer: restrict the groovy language to an allowed subset • But you can implement your own 33
  • 55. Imports customizer 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")             34
  • 56. AST transformation customizer 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’")                       35
  • 57. AST transformation customizer 35 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
  • 58. Secure the onboard trajectory calculator
  • 59. Secure AST customizer • Let’s setup our environment – an import customizer to import java.lang.Math.* – prepare a secure AST customizer def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer() 37
  • 60. Secure AST customizer Idea: secure the rocket’s onboard trajectory calculation system by allowing only math expressions to be evaluated by the calculator • Let’s setup our environment – an import customizer to import java.lang.Math.* – prepare a secure AST customizer def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer() 37
  • 61. Secure AST customizer ... 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'] ... 38
  • 62. Secure AST customizer Disallow closures and methods ... 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'] ... 38
  • 63. Secure AST customizer Disallow closures and methods ... secure.with  { //  disallow  closure  creation closuresAllowed  =  false   //  disallow  method  definitions Black / white list methodDefinitionAllowed  =  false   imports   //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ... 38
  • 64. Secure AST customizer ... //  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  ] } ... 39
  • 65. Secure AST customizer You can build a subset of the Groovy syntax! ... //  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  ] } ... 39
  • 66. Secure AST customizer You can build a subset of the Groovy syntax! ... //  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  ] } ... 39 Black / white list usage of classes
  • 67. Secure AST customizer • Ready to evaluate our flight equations! def  config  =  new  CompilerConfiguration() config.addCompilationCustomizers(imports,  secure) def  shell  =  new  GroovyShell(config)   shell.evaluate  'cos  PI/3' • But the following would have failed: shell.evaluate  'System.exit(0)' 40
  • 68. Back to our robot... robot.move  left 41
  • 69. Back to our robot... Still need to get rid of the robot prefix! robot.move  left 41
  • 70. Can we remove it?
  • 71. Can we remove it? Yes !
  • 72. 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 • Inject a ‘move’ closure in the binding with a method pointer 43 • Use a base script class with a ‘move’ method delegating to the robot
  • 73. Inject a closure in the binding def  robot  =  new  mars.Robot() binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        move:  robot.&move ]) 44
  • 74. Inject a closure in the binding def  robot  =  new  mars.Robot() binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        }, Method pointer        move:  robot.&move (a closure) on ]) robot’s move instance method 44
  • 75. Define a base script class abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } 45
  • 76. Define a base script class 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 45
  • 77. Define a base script class 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 45 Access the robot through the script’s binding
  • 78. Configure the base script class def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass 46
  • 79. Configure the base script class def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass Scripts evaluated with this configuration will inherit from that class 46
  • 80. Ready for lift off!    move  left
  • 81. Beep, beep... yes but how do you define the speed? ...beep...
  • 82. Oh no!
  • 83. What we could do now is... move  left,  at:  3.km/h 50
  • 84. What we could do now is... Mix of named and normal parameters move  left,  at:  3.km/h 50
  • 85. What we could do now is... Mix of named and normal parameters move  left,  at:  3.km/h How to support this speed notation? 50
  • 86. 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 51
  • 87. Distance unit enum and distance 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  }   } 52
  • 88. Distance unit enum and distance 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  }   } 52 @TupleConstructor   class  Distance  {        double  amount          DistanceUnit  unit        String  toString()  {                  "$amount  $unit"          }   }
  • 89. Time unit enum and duration 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  }   } 53
  • 90. Time unit enum and duration 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  }   } 53 @TupleConstructor   class  Duration  {        double  amount          TimeUnit  unit        String  toString()  {                  "$amount  $unit"          }   }
  • 91. Now at (light!) speed speed = 54 distance speed @TupleConstructor   class  Speed  {        Distance  distance        Duration  dur        String  toString()  {                  "$distance/$dur"          }   }
  • 92. 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: 2.km 2.getKm() 55
  • 93. 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 Groovy 2! • Let’s have a look at the ExpandoMetaClass 56
  • 94. Using ExpandoMetaClass Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } 57
  • 95. Using ExpandoMetaClass Add that to integration.groovy Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } 57
  • 96. Using ExpandoMetaClass Add that to integration.groovy Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } ‘delegate’ is the current number 57
  • 97. Using ExpandoMetaClass Add that to integration.groovy Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } ‘delegate’ is the current number 57 Usage in your DSLs 40.cm   3.5.m 4.km
  • 98. 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 2.km/h 58
  • 99. 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 The div() method on Distance 2.km/h 58
  • 100. 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 The div() method on Distance 2.km/h An ‘h’ duration instance in the binding 58
  • 101. Inject the ‘h’ hour constant in the binding def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        h:  new  Duration(1,  TimeUnit.hour) ]) 59
  • 102. Inject the ‘h’ hour constant in the binding def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  { An ‘h’ duration added                                [(it.name()):  it] to the binding                        },        h:  new  Duration(1,  TimeUnit.hour) ]) 59
  • 103. Operator overloading 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() 60 • 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
  • 104. Operator overloading • Update the Distance class with a div() method following the naming convetion for operators class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } 61
  • 105. Operator overloading • Update the Distance class with a div() method following the naming convetion for operators class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } Optional return 61
  • 106. Equivalence of notation • Those two notations are actually equivalent: 2.km/h 2.getKm().div(h) 62
  • 107. Equivalence of notation • Those two notations are actually equivalent: 2.km/h This one might be slightly more verbose! 2.getKm().div(h) 62
  • 108. Named parameters usage move  left,  at:  3.km/h 63
  • 109. Named parameters usage move  left,  at:  3.km/h Normal parameter 63
  • 110. Named parameters usage move  left,  at:  3.km/h Normal parameter 63 Named parameter
  • 111. Named parameters usage move  left,  at:  3.km/h Normal parameter Will call: def  move(Map  m,  Direction  q) 63 Named parameter
  • 112. Named parameters usage move  left,  at:  3.km/h Normal parameter Will call: def  move(Map  m,  Direction  q) All named parameters go into the map argument 63 Named parameter
  • 113. Named parameters usage move  left,  at:  3.km/h Named parameter Normal parameter Will call: def  move(Map  m,  Direction  q) All named parameters go into the map argument 63 Positional parameters come afterwards
  • 114. Named parameters usage move  left,  at:  3.km/h 64
  • 115. Named parameters usage move  left,  at:  3.km/h Can we get rid of the comma? 64
  • 116. Named parameters usage move  left,  at:  3.km/h Can we get rid of the comma? 64 What about the colon too?
  • 117. Command chains Groovy 1.8 • 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) 65
  • 118. Command chains  move  left    at  3.km/h   66
  • 119. Command chains Alternation of method names  move  left    at  3.km/h   66
  • 120. Command chains Alternation of method names  move  left    at  3.km/h   and parameters (even named ones) 66
  • 121. Command chains  move  left    at  3.km/h   66
  • 122. Command chains Equivalent to:          (        ).    (            )  move  left    at  3.km/h   66
  • 123. Ma! ook L ns, pare No ots! od n
  • 124. 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        }        ... } 68
  • 125. Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } 69
  • 126. Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } Nested maps and closures 69
  • 127. Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } Usage in your DSLs Nested maps and closures move  left  at  3.km/h 69
  • 128. Command chains 70
  • 129. Command chains //  methods  with  multiple  arguments  (commas) 70
  • 130. Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor 70
  • 131. Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation 70
  • 132. Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good 70
  • 133. 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 70
  • 134. 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  {} 70
  • 135. 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 70
  • 136. 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 70
  • 137. 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 70
  • 138. 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 70
  • 139. 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 70
  • 140. 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 70
  • 141. 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 70
  • 142. 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 70
  • 143. 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            (        ). 70
  • 144. Final result 71
  • 145. Final result move  forward  at  3.km/h 71
  • 146. move forward at 3.km/h
  • 147. move forward at 3.km/h Yes! We did it!
  • 148. What about security and safety?
  • 149. Security and Safety JVM Security Managers SecureASTCustomizer Sandboxing Controlling script execution © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 150. Play it safe in a sandbox
  • 151. 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 76
  • 152. 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 77
  • 153. SecureASTCustomizer 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) 78
  • 154. 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 79
  • 155. @ThreadInterrupt @ThreadInterrupt import  groovy.transform.ThreadInterrupt     while  (true)  {        //  Any  extraterestrial  around? } 80
  • 156. @ThreadInterrupt { @ThreadInterrupt import  groovy.transform.ThreadInterrupt       while  (true)  {        if  (Thread.currentThread().isInterrupted())                throw  new  InterruptedException()        //  Any  extraterestrial  around? } } 80
  • 157. @TimedInterrupt @TimedInterrupt(10) import  groovy.transform.TimedInterrupt     while  (true)  {        move  left        //  circle  forever } • InterruptedException thrown when checks indicate code ran longer than desired 81
  • 158. @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 Groovy 1.8 @ConditionalInterrupt({  battery.level  <  0.1  }) import  groovy.transform.ConditionalInterrupt 100.times  {                move  forward  at  10.km/h } 82
  • 159. @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 Groovy 1.8 @ConditionalInterrupt({  battery.level  <  0.1  }) import  groovy.transform.ConditionalInterrupt 100.times  {                move  forward  at  10.km/h } 82 Can we avoid typing the conditional interrupt?
  • 160. @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 100.times  {                move  forward  at  10.km/h } 83 Groovy 1.8 Yes! Using compilation customizers
  • 161. 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 84
  • 162. What about tooling?
  • 163. Tooling Why tooling? DSL descriptor Pointcuts and contributions Packaging DSLDs © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 164. Why tooling? • I know what this language means –why do I want anything more? 87
  • 165. 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 87
  • 166. Let’s use an IDE • I hear Groovy-Eclipse is pretty good… 88
  • 167. Let’s use an IDE • I hear Groovy-Eclipse is pretty good… 88
  • 168. Let’s use an IDE • I hear Groovy-Eclipse is pretty good… Uh oh! 88
  • 169. Let’s use an IDE • I hear Groovy-Eclipse is pretty good… Uh oh! Can we do better? 88
  • 170. Of course! • Eclipse is extensible – with a plugin architecture Eclipse platform WorkBench JFace SWT Workspace Platform runtime 89 Help Team New plugin New tool
  • 171. I want my DSL supported in Eclipse 90
  • 172. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – 90 create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it
  • 173. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – 90 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
  • 174. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – 90 create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it Uh oh! • 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
  • 175. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – • Problems create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it – 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 Can we do better? 90 Uh oh!
  • 176. Of course! • Groovy is extensible! – Meta-Object Protocol – Metaprogramming – DSLs... 91
  • 177. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL 92
  • 178. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – – – – 92 Powerful Uses Groovy syntax, semantics, and APIs No knowledge of Eclipse required Can ship with Groovy libraries
  • 179. 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 93
  • 180. 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<…>' 93
  • 181. 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: Pointcut – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 93
  • 182. 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… Pointcut Contribution block property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 93
  • 183. DEMO LET’S SEE THAT IN ACTION 94
  • 184. Anatomy of a DSLD script • Pointcuts – – – – Where to do it What is the current expression? Current type? Enclosing class? • Contribution blocks – – – – 95 What to do « Add » method « Add » property Delegate to another type
  • 185. Anatomy of a DSLD script • Pointcuts – – – – Where to do it What is the current expression? Current type? Enclosing class? • Contribution blocks – – – – 95 What to do « Add » method « Add » property Delegate to another type Not at runtime... only while editing
  • 186. Talking about « x » class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } 96
  • 187. Talking about « x » Current type class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } 96
  • 188. Talking about « x » Current type Enclosing class 96 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }
  • 189. Talking about « x » Current type Enclosing class Enclosing method 96 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }
  • 190. Pointcuts currentType()        //  matches  on  current  declaring  type enclosingClass()  //  matches  on  the  enclosing  class currentType("com.bar.Foo") currentType(subType("com.bar.Foo") currentType(method("run")) annotatedBy("org.junit.runner.RunWith") //  combining  them,  and  using  the  logical  and enclosingClass(        annotatedBy("org.junit.runner.RunWith") )  &  currentType(method("run")) 97
  • 191. What goes in a contribution block? 98
  • 192. What goes in a contribution block? • property: “adds” a property – – – – 98 name: “myName” (REQUIRED) type: “java.lang.String” declaringType: ”com.foo.Frumble” doc: “Some JavaDoc”
  • 193. 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 98
  • 194. 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) 98
  • 195. 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 contribute(...)  {    property  name:  "myName"    method  name:  "getMyName"    delegatesTo  type:              "some.other.Type" } – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 98
  • 196. Wait... isn’t this Aspect-Oriented Programming? 99
  • 197. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP 99
  • 198. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime 99
  • 199. 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.* 99
  • 200. 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) 99
  • 201. DEMO LET’S GET THE EDITOR REALLY WORKING 100
  • 202. DEMO HOW DO WE SHIP IT? 101
  • 203. To summarize DSLD pointcuts contribution blocks • Getting it out there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 102
  • 204. What have we learnt? © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  • 205. 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 104
  • 206. Groovy Power!™ • A flexible and malleable syntax Groovy is a great fit for DSLs! – 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 104
  • 207. 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 105
  • 208. Thank you! 106
  • 209. Questions & Answers Got questions, really? 107
  • 210. Questions & Answers Got questions, really? 107 I might have answers!
  • 211. Image credits • • • • • • • • • • • • • • • • • • • • • • • • 108 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 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

×