Groovy Domain-Specific LanguagesAndrew Eisenberg                                                             Paul King    ...
Andrew Eisenberg    • Groovy-Eclipse project lead    • Senior Member of Technical Staff,VMware Tools Team      – Grails-ID...
Paul King    • Groovy Core Committer    • Leads ASERT      – software, training, consultancy company        based in Brisb...
Guillaume Laforge    • Groovy Project Manager at VMware      • Initiator of the Grails framework      • Creator of the Gae...
Introduction                                                                          Definitions                         ...
Domain-Specific Languages                     {                                                          }                ...
Technical examples                                                    XSLT<?xml version="1.0"?>        Glade              ...
Antimalaria drug             Insurance policy risk        HR skills representationresistance simulation        calculation...
Goals of DSLs    • Use a more expressive language than a general-purpose one    • Share a common metaphor of understanding...
Pros and cons     Pros                                 Cons      – Domain experts can help,          – Learning cost vs. l...
Groovy provides...     • A flexible and malleable syntax       – scripts, native syntax constructs (list, map, ranges),    ...
Let’s get started!© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
Your mission:   build a DSL for                  a Mars robot
We need a robot!       package	  mars       class	  Robot	  {}15
It should move...        package	  mars	          class	  Robot	  {        	  	  	  	  void	  move()	  {}        }16
..in a direction!        package	  mars	          class	  Robot	  {        	  	  	  	  void	  move(String	  dir)	  {}     ...
More explicit direction        package	  mars	          class	  Robot	  {        	  	  	  	  void	  move(Direction	  dir)	...
Now how can we control it?       import	  static	  mars.Direction.*;       import	  mars.Robot;       public	  class	  Com...
Now how can we control it?       import	  static	  mars.Direction.*;       import	  mars.Robot;       public	  class	  Com...
Now how can we control it?       	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 ...
Optional semicolons & parentheses / Scripts vs classes       import	  static	  mars.Direction.*       import	  mars.Robot ...
Optional semicolons & parentheses / Scripts vs classes       import	  static	  mars.Direction.*       import	  mars.Robot ...
Optional semicolons & parentheses / Scripts vs classes       import	  static	  mars.Direction.*       import	  mars.Robot ...
Integration
GroovyShell to the rescue22
GroovyShell to the rescue                         def	  shell	  =	  new	  GroovyShell()                         shell.eval...
GroovyShell to the rescue                              def	  shell	  =	  new	  GroovyShell()                              ...
GroovyShell to the rescue                                   def	  shell	  =	  new	  GroovyShell()                         ...
GroovyShell to the rescue                                   def	  shell	  =	  new	  GroovyShell()                         ...
Integration mechanisms     • Different solutions available:       – Groovy’s own mechanisms         • GroovyScriptEngine, ...
Integration mechanisms     • Different solutions available:       – Groovy’s own mechanisms         • GroovyScriptEngine, ...
What’s wrong with our DSL?              import	  static	  mars.Direction.*              import	  mars.Robot              d...
What’s wrong with our DSL?              Can’t we hide             those imports?               import	  static	  mars.Dire...
What’s wrong with our DSL?              Can’t we hide             those imports?               import	  static	  mars.Dire...
What’s wrong with our DSL?               Can’t we hide              those imports?                import	  static	  mars.D...
I’m sorry Dave,you can’t do that!
I’m sorry Dave,you can’t do that!
What we really want is...                        	  move	  left	  26
Let’s inject a robot!     • We can pass data in / out of scripts through the Binding       – basically just a map of varia...
Let’s inject a robot!     • We can pass data in / out of scripts through the Binding       – basically just a map of varia...
Let’s inject a robot!     • We can pass data in / out of scripts through the Binding       – basically just a map of varia...
Better?               import	  static	  mars.Direction.*               robot.move	  left28
Better?     Robot import       removed                    import	  static	  mars.Direction.*                    robot.move...
Better?     Robot import       removed                    import	  static	  mars.Direction.*                    robot.move...
How to inject the direction?     • Using the    import	  mars.*       binding...                    def	  binding	  =	  ne...
How to inject the direction?     • Using the    import	  mars.*                                Fragile in case of         ...
How to inject the direction?     • Using the        import	  mars.*	         binding...                        def	  bindi...
How to inject the direction?     • Using string concatenation?     • Using compiler customizers31
String concatenation? Bad idea!       new	  GroovyShell(new	  Binding([robot:	  new	  mars.Robot()]))       	  	  	  	  .e...
String concatenation? Bad idea!                                                       Cheat with string                   ...
String concatenation? Bad idea!       new	  GroovyShell(new	  Binding([robot:	  new	  mars.Robot()]))       	  	  	  	  .e...
String concatenation? Bad idea!                 Line #1                 becomes                 Line #2       new	  Groovy...
String concatenation? Bad idea!       new	  GroovyShell(new	  Binding([robot:	  new	  mars.Robot()]))       	  	  	  	  .e...
Compilation customizers     • Ability to apply some customization       to the Groovy compilation process     • Three avai...
Imports customizer        def	  configuration	  =	  new	  CompilerConfiguration()        	          def	  imports	  =	  ne...
AST transformation customizer        def	  configuration	  =	  new	  CompilerConfiguration()        	          def	  impor...
AST transformation customizer         def	  configuration	  =	  new	  CompilerConfiguration()         	           def	  im...
Secure the onboardtrajectory calculator
Secure AST customizer     • Let’s set up our environment       – an import customizer to import java.lang.Math.*       – p...
Secure AST customizer                                              Idea: secure the rocket’s onboard trajectory           ...
Secure AST customizer       ...       secure.with	  {            //	  disallow	  closure	  creation            closuresAll...
Secure AST customizer                                                        Disallow closures       ...                  ...
Secure AST customizer                                                        Disallow closures       ...                  ...
Secure AST customizer      ...             //	  language	  tokens	  allowed             tokensWhitelist	  =	  [           ...
Secure AST customizer                                                    You can build a subset of                        ...
Secure AST customizer                                                    You can build a subset of                        ...
Secure AST customizer     • Ready to evaluate our flight equations!              def	  config	  =	  new	  CompilerConfigura...
Back to our robot...                   robot.move	  left41
Back to our robot...                      Still need to get rid of                         the robot prefix!              ...
Can we remove it?
Yes !Can we remove it?
How to get rid of the ‘robot’?     • Instead of calling the move() method on the robot instance,       we should be able t...
Inject a closure in the binding             def	  robot	  =	  new	  mars.Robot()             binding	  =	  new	  Binding([...
Inject a closure in the binding             def	  robot	  =	  new	  mars.Robot()             binding	  =	  new	  Binding([...
Define a base script class         abstract	  class	  RobotBaseScriptClass	  extends	  Script	  {         	  	  	  	  void...
Define a base script class         abstract	  class	  RobotBaseScriptClass	  extends	  Script	  {         	  	  	  	  void...
Define a base script class         abstract	  class	  RobotBaseScriptClass	  extends	  Script	  {         	  	  	  	  void...
Configure the base script class       def	  conf	  =	  new	  CompilerConfiguration()       conf.scriptBaseClass	  =	  Robo...
Configure the base script class       def	  conf	  =	  new	  CompilerConfiguration()       conf.scriptBaseClass	  =	  Robo...
Ready for lift off!                      	  	  move	  left
Beep, beep...yes but how do you define the speed?     ...beep...
Oh no!
What we could do now is...                move	  left,	  at:	  3.km/h50
What we could do now is...                         Mix of named and                        normal parameters              ...
What we could do now is...                         Mix of named and                        normal parameters              ...
Supporting the speed notation     • We need to:      – define units of distance, time and speed        • DistanceUnit and D...
Distance unit enum and distance      enum	  DistanceUnit	  {      	  	  	  	  centimeter	  (cm,	  	  	  	  0.01),      	  ...
Distance unit enum and distance      enum	  DistanceUnit	  {                                                              ...
Time unit enum and duration      enum	  TimeUnit	  {      	  	  	  	  hour	  	  	  	  	  	  (	  	  h,	  3600),      	  	  ...
Time unit enum and duration      enum	  TimeUnit	  {                                                       @TupleConstruct...
Now at (light!) speed                             @TupleConstructor	                               class	  Speed	  {      ...
First, we need the distance notation     • We add a dynamic property to numbers by adding a getter to       them and use t...
Techniques to add properties to numbers     • To add dynamic methods or properties,       there are several approaches at ...
Using ExpandoMetaClass          Number.metaClass.getCm	  =	  {	  -­‐>	            	  	  	  	  new	  Distance(delegate,	  U...
Using ExpandoMetaClass         Add that to     integration.groovy            Number.metaClass.getCm	  =	  {	  -­‐>	       ...
Using ExpandoMetaClass         Add that to     integration.groovy            Number.metaClass.getCm	  =	  {	  -­‐>	       ...
Using ExpandoMetaClass         Add that to     integration.groovy                                                         ...
Distance okay, but speed?     • For distance, we just added a property access after the number,       but we now need to d...
Distance okay, but speed?     • For distance, we just added a property access after the number,       but we now need to d...
Distance okay, but speed?     • For distance, we just added a property access after the number,       but we now need to d...
Inject the ‘h’ hour constant in the binding               def	  binding	  =	  new	  Binding([               	  	  	  	  ro...
Inject the ‘h’ hour constant in the binding                  def	  binding	  =	  new	  Binding([                  	  	  	 ...
Operator overloading       a	  +	  b	  	  	  //	  a.plus(b)       a	  -­‐	  b	  	  	  //	  a.minus(b)           • Currency...
Operator overloading     • Update the Distance class with a div() method       following the naming convention for operato...
Operator overloading     • Update the Distance class with a div() method       following the naming convention for operato...
Equivalence of notation     • Those two notations are actually equivalent:                                2.km/h          ...
Equivalence of notation     • Those two notations are actually equivalent:                                2.km/h          ...
Named parameters usage              move	  left,	  at:	  3.km/h63
Named parameters usage              move	  left,	  at:	  3.km/h                  Normal                 parameter63
Named parameters usage              move	  left,	  at:	  3.km/h                  Normal             Named                 ...
Named parameters usage                 move	  left,	  at:	  3.km/h                      Normal                           N...
Named parameters usage                 move	  left,	  at:	  3.km/h                      Normal                           N...
Named parameters usage                 move	  left,	  at:	  3.km/h                      Normal                           N...
Named parameters usage              move	  left,	  at:	  3.km/h64
Named parameters usage               move	  left,	  at:	  3.km/h             Can we get rid of               the comma?64
Named parameters usage               move	  left,	  at:	  3.km/h             Can we get rid of    What about the          ...
Command chains               Groovy 1.8     • A grammar improvement allowing you       to drop dots & parens when chaining...
Command chains              	  move	  left	  	  at	  3.km/h	  66
Command chains              Alternation of              method names              	  move	  left	  	  at	  3.km/h	  66
Command chains              Alternation of              method names              	  move	  left	  	  at	  3.km/h	        ...
Command chains              	  move	  left	  	  at	  3.km/h	  66
Command chains              Equivalent to:              	  move	  left	  	  at	  3.km/h	                	  	  	  	  	  (	 ...
Look  Ma!No  parens, n odots!
Command chains            //	  Java	  fluent	  API	  approach           class	  Robot	  {           	  	  	  	  ...       ...
Command chains     def	  move(Direction	  dir)	  {     	  	  	  	  [at:	  {	  Speed	  speed	  -­‐>     	  	  	  	  	  	  	...
Command chains     def	  move(Direction	  dir)	  {     	  	  	  	  [at:	  {	  Speed	  speed	  -­‐>                        ...
Command chains     def	  move(Direction	  dir)	  {     	  	  	  	  [at:	  {	  Speed	  speed	  -­‐>                        ...
Command chains                 70
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)                                                  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains     //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	  ...
Command chains      //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	 ...
Command chains      //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	 ...
Command chains      //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	 ...
Command chains      //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	 ...
Command chains      //	  methods	  with	  multiple	  arguments	  (commas)     take	  coffee	  	  with	  sugar,	  milk	  	 ...
Final result71
Final result                    move	  forward	  at	  3.km/h71
move forward at 3.km/h
move forward at 3.km/h     Yes! We did it!
What aboutsecurity and  safety?
Security and Safety                                                     JVM Security Managers                             ...
Play it safe in a sandbox
Playing it safe...     • You have to think carefully about       what DSL users are allowed to do with your DSL     • Forb...
Security Managers     • Groovy is just a language leaving on the JVM,       so you have access to the usual Security Manag...
SecureASTCustomizer             def	  secure	  =	  new	  SecureASTCustomizer()             secure.with	  {                ...
Controlling code execution     • Your application may run user’s code       – what if the code runs in infinite loops or fo...
@ThreadInterrupt    @ThreadInterrupt    import	  groovy.transform.ThreadInterrupt	      	      while	  (true)	  {    	  	 ...
@ThreadInterrupt      @ThreadInterrupt      import	  groovy.transform.ThreadInterrupt	        	         	        while	  (...
@TimedInterrupt                  @TimedInterrupt(10)                  import	  groovy.transform.TimedInterrupt	           ...
@ConditionalInterrupt     • Specify your own conditions to be inserted       at the start of method and closure bodies    ...
@ConditionalInterrupt     • Specify your own conditions to be inserted       at the start of method and closure bodies    ...
@ConditionalInterrupt     • Specify your own conditions to be inserted       at the start of method and closure bodies    ...
Using compilation customizers     • In our previous examples, the usage of the interrupts       were explicit, and users h...
What about tooling?
Tooling                                                           Why tooling?                                            ...
Why tooling?     • I know what this language means      –why do I want anything more?87
Why tooling?     • I know what this language means      –why do I want anything more?     • But, tooling can make things e...
Let’s use an IDE     • I hear Groovy-Eclipse is pretty good…88
Let’s use an IDE     • I hear Groovy-Eclipse is pretty good…88
Let’s use an IDE     • I hear Groovy-Eclipse is pretty good…                                Uh oh!88
Let’s use an IDE     • I hear Groovy-Eclipse is pretty good…                                Uh oh!                        ...
Of course!     • Eclipse is extensible       – with a plugin         architecture          Eclipse platform               ...
I want my DSL supported in Eclipse90
I want my DSL supported in Eclipse     • Let’s create a plugin       –   create a plugin project       –   extend an exten...
I want my DSL supported in Eclipse     • Let’s create a plugin               • Problems       –   create a plugin project ...
I want my DSL supported in Eclipse                                                                   Uh oh!     • Let’s cr...
I want my DSL supported in Eclipse                                                                        Uh oh!     • Let...
Of course!     • Groovy is extensible!       – Meta-Object Protocol       – Metaprogramming       – DSLs...91
DSL Descriptors     • Teach the IDE about DSLs through a Groovy DSL92
DSL Descriptors     • Teach the IDE about DSLs through a Groovy DSL     • Benefits      –   Powerful      –   Uses Groovy s...
DSL Descriptors     • Teach the IDE about DSLs through a Groovy DSL     • Benefits      –   Powerful                       ...
DSL Descriptors     • Teach the IDE about DSLs through a Groovy DSL     • Benefits      –   Powerful                       ...
Let’s start simple     move     deploy     h     left     right     forward     backward93
Let’s start simple     move       • In English:     deploy     h            – When the type is this, add the following pro...
Let’s start simple     move       • In English:     deploy     h            – When the type is this, add the following pro...
Let’s start simple     move       • In English:     deploy     h            – When the type is this, add the following pro...
Let’s start simple     move       • In English:     deploy     h            – When the type is this, add the following pro...
DEMO     LET’S SEE THAT94
Anatomy of a DSLD script     • Pointcuts      –   Where to do it      –   What is the current expression?      –   Current...
Anatomy of a DSLD script     • Pointcuts                                            Where      –   Where to do it      –  ...
Anatomy of a DSLD script     • Pointcuts                                            Where      –   Where to do it      –  ...
Anatomy of a DSLD script     • Pointcuts                                                 Where      –   Where to do it    ...
Talking about « x »                           class	  Other	  {	  }                           class	  Foo	  {             ...
Talking about « x »           Current type                           class	  Other	  {	  }                           class...
Talking about « x »            Current type                            class	  Other	  {	  }                            cl...
Talking about « x »            Current type                            class	  Other	  {	  }                            cl...
Pointcuts97
Pointcuts        currentType()	  	  	  	  //	  matches	  on	  current	  declaring	  type97
Pointcuts        currentType()	  	  	  	  //	  matches	  on	  current	  declaring	  type        isScript()	  	  	  	  	  	...
Pointcuts        currentType()	  	  	  	  //	  matches	  on	  current	  declaring	  type        isScript()	  	  	  	  	  	...
Pointcuts        currentType()	  	  	  	  //	  matches	  on	  current	  declaring	  type        isScript()	  	  	  	  	  	...
Pointcuts        currentType()	  	  	  	  //	  matches	  on	  current	  declaring	  type        isScript()	  	  	  	  	 €
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Groovy Domain Specific Languages - SpringOne2GX 2012
Prochain SlideShare
Chargement dans... 5
×

Groovy Domain Specific Languages - SpringOne2GX 2012

4,432

Published on

Paul King, Andrew Eisenberg and Guillaume Laforge present about implementation of Domain-Specific Languages in Groovy, while at the SpringOne2GX 2012 conference in Washington DC.

Published in: Technologies
0 commentaires
17 mentions J'aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Total des vues
4,432
Sur Slideshare
0
À partir des ajouts
0
Nombre d'ajouts
8
Actions
Partages
0
Téléchargements
138
Commentaires
0
J'aime
17
Ajouts 0
No embeds

No notes for slide

Groovy Domain Specific Languages - SpringOne2GX 2012

  1. 1. Groovy Domain-Specific LanguagesAndrew Eisenberg Paul King Guillaume LaforgeGroovy Eclipse Project Lead Groovy Core Developer Groovy Project Manager SpringSource / VMware ASERT SpringSource / VMware @werdnagreb @paulk_asert @glaforge © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  2. 2. Andrew Eisenberg • Groovy-Eclipse project lead • Senior Member of Technical Staff,VMware Tools Team – Grails-IDE, GGTS, AJDT, STS, 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/aeisenberg2
  3. 3. 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_asert3
  4. 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/glaforge4
  5. 5. Introduction Definitions Examples Goals Pros & cons© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  6. 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 Purpose Languages • Also known as: fluent / humane interfaces, language oriented programming, little or mini languages, macros, business natural languages...6
  7. 7. Technical examples XSLT<?xml version="1.0"?> Glade Regex <?xml version="1.0"?><GTK-Interface> <xsl:stylesheetversion="1.0"<widget> <class>GtkWindow</class> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <name>HelloWindow</name> <xsl:output method="xml"/> <border_width>5</border_width> <xsl:template match="*"> <Signal> <xsl:element name="{name()}"> <name>destroy</name> <xsl:for-each select="@*"> <handler>gtk_main_quit</handler> <xsl:element name="{name()}"> </Signal> <title>Hello</title> <xsl:value-of select="."/> </xsl:element> "x.z?z{1,3}y" <type>GTK_WINDOW_TOPLEVEL</type> </xsl:for-each> <position>GTK_WIN_POS_NONE</position> <xsl:apply-templates select="*|text()"/> <allow_shrink>True</allow_shrink> </xsl:element> <allow_grow>True</allow_grow> </xsl:template> <auto_shrink>False</auto_shrink> </xsl:stylesheet> <widget> <class>GtkButton</class> Fetchmail <name>Hello World</name> <can_focus>True</can_focus> <label>Hello World</label> # Poll this site first each cycle. SQL poll pop.provider.net proto pop3 </widget> user "jsmith" with pass "secret1" is "smith" here</widget> user jones with pass "secret2" is "jjones" here with options keep</GTK-Interface> # 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 SELECT * FROM TABLE # Poll this site third in the cycle. WHERE NAME LIKE %SMI # Password will be fetched from ~/.netrc poll mailhost.net with proto imap: ORDER BY NAME user esr is esr here
  8. 8. Antimalaria drug Insurance policy risk HR skills representationresistance simulation calculation engine Nuclear safety simulationsMarket data feeds analysis Loan acceptance rules engine
  9. 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 lifecycle9
  10. 10. Pros and cons Pros Cons – Domain experts can help, – Learning cost vs. limited applicability validate, modify, and often – Cost of designing, implementing & develop DSL programs maintaining DSLs as well as tools/ – Somewhat self-documenting IDEs – Enhance quality, productivity, – Attaining proper scope reliability, maintainability, – Trade-offs between domain portability, reusability specificity and general purpose – Safety; as long as the language language constructs constructs are safe, any DSL – Efficiency cost sentence can be considered safe – Proliferation of similar non-standard DSLs10
  11. 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 safety11
  12. 12. Let’s get started!© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  13. 13. Your mission: build a DSL for a Mars robot
  14. 14. We need a robot! package  mars class  Robot  {}15
  15. 15. It should move... package  mars   class  Robot  {        void  move()  {} }16
  16. 16. ..in a direction! package  mars   class  Robot  {        void  move(String  dir)  {} }17
  17. 17. More explicit direction package  mars   class  Robot  {        void  move(Direction  dir)  {} } package  mars   enum  Direction  {        left,  right,  forward,  backward }18
  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. 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. 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. 21. Optional semicolons & parentheses / Scripts vs classes import  static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left20
  22. 22. Optional semicolons & parentheses / Scripts vs classes import  static  mars.Direction.* import  mars.Robot Optional typing                def      robot  =  new  Robot()                robot.move  left20
  23. 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. 24. Integration
  25. 25. GroovyShell to the rescue22
  26. 26. GroovyShell to the rescue def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") )22
  27. 27. GroovyShell to the rescue def  shell  =  new  GroovyShell() shell.evaluate( integration.groovy        new  File("command.groovy") )22
  28. 28. GroovyShell to the rescue def  shell  =  new  GroovyShell() shell.evaluate( integration.groovy        new  File("command.groovy") ) import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left22
  29. 29. GroovyShell to the rescue def  shell  =  new  GroovyShell() shell.evaluate( integration.groovy        new  File("command.groovy") ) import  static  mars.Direction.* import  mars.Robot command.groovy def  robot  =  new  Robot() robot.move  left22
  30. 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. 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. 32. What’s wrong with our DSL? import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left24
  33. 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  left24
  34. 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. 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 Can’t we inject Do we really need to the robot? repeat ‘robot’?24
  36. 36. I’m sorry Dave,you can’t do that!
  37. 37. I’m sorry Dave,you can’t do that!
  38. 38. What we really want is...  move  left  26
  39. 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 values27
  40. 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. 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. 42. Better? import  static  mars.Direction.* robot.move  left28
  43. 43. Better? Robot import removed import  static  mars.Direction.* robot.move  left28
  44. 44. Better? Robot import removed import  static  mars.Direction.* robot.move  left Robot injected, no ‘new’ needed28
  45. 45. How to inject the direction? • Using the import  mars.* binding... 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") )29
  46. 46. How to inject the direction? • Using the import  mars.* Fragile in case of new directions! binding... 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") )29
  47. 47. How to inject the direction? • Using the import  mars.*   binding... def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  { Spread map                                [(it.name()):  it] operator                        } ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )30
  48. 48. How to inject the direction? • Using string concatenation? • Using compiler customizers31
  49. 49. String concatenation? Bad idea! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left")32
  50. 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. 51. String concatenation? Bad idea! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left")32
  52. 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. 53. String concatenation? Bad idea! new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*n"  +                            "robot.move  left")32
  54. 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 own33
  55. 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. 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. 57. 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’")                       @Log injects a logger in scripts and classes35
  58. 58. Secure the onboardtrajectory calculator
  59. 59. Secure AST customizer • Let’s set up 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. 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 set up 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. 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. 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. 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. 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. 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. 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  =  [ Black / white list Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE usage of classes ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ...39
  67. 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. 68. Back to our robot... robot.move  left41
  69. 69. Back to our robot... Still need to get rid of the robot prefix! robot.move  left41
  70. 70. Can we remove it?
  71. 71. Yes !Can we remove it?
  72. 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 • Use a base script class the binding with a method with a ‘move’ method pointer delegating to the robot43
  73. 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. 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 method44
  75. 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. 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 level45
  77. 77. Define a base script class abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } Access the robot The move() method is through the script’s now at the script level binding45
  78. 78. Configure the base script class def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass46
  79. 79. Configure the base script class def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass Scripts evaluated with this configuration will inherit from that class46
  80. 80. Ready for lift off!    move  left
  81. 81. Beep, beep...yes but how do you define the speed? ...beep...
  82. 82. Oh no!
  83. 83. What we could do now is... move  left,  at:  3.km/h50
  84. 84. What we could do now is... Mix of named and normal parameters move  left,  at:  3.km/h50
  85. 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. 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 overloading51
  87. 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. 88. Distance unit enum and distance enum  DistanceUnit  { @TupleConstructor          centimeter  (cm,        0.01), class  Distance  {        meter            (  m,        1      ),          double  amount          kilometer    (km,  1000      )          DistanceUnit  unit                String  abbreviation        String  toString()  {          double  multiplier                "$amount  $unit"                  }          DistanceUnit(String  abbr,  double  mult)  { }                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   }52
  89. 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. 90. Time unit enum and duration enum  TimeUnit  { @TupleConstructor          hour            (    h,  3600), class  Duration  {        minute        (min,      60),          double  amount          second        (    s,        1)          TimeUnit  unit                String  abbreviation        String  toString()  {          double  multiplier                "$amount  $unit"                  }          TimeUnit(String  abbr,  double  mult)  { }                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   }53
  91. 91. Now at (light!) speed @TupleConstructor   class  Speed  { distance        Distance  distance speed =        Duration  dur duration        String  toString()  {                  "$distance/$dur"          }   }54
  92. 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. 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 ExpandoMetaClass56
  94. 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. 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. 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 number57
  97. 97. Using ExpandoMetaClass Add that to integration.groovy Usage in your DSLs Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>   40.cm          new  Distance(delegate,  Unit.meter)   3.5.m } Number.metaClass.getKm  =  {  -­‐>   4.km        new  Distance(delegate,  Unit.kilometer)   } ‘delegate’ is the current number57
  98. 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/h58
  99. 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/h58
  100. 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 binding58
  101. 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. 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. 103. Operator overloading a  +  b      //  a.plus(b) a  -­‐  b      //  a.minus(b) • Currency amounts a  *  b      //  a.multiply(b) – 15.euros + 10.dollars a  /  b      //  a.div(b) a  %  b      //  a.modulo(b) • Distance handling a  **  b    //  a.power(b) a  |  b      //  a.or(b) – 10.km - 10.m a  &  b      //  a.and(b) a  ^  b      //  a.xor(b) • Workflow, concurrency a[b]        //  a.getAt(b) – taskA | taskB & taskC a  <<  b    //  a.leftShift(b) a  >>  b    //  a.rightShift(b) • Credit an account a  >>>  b  //  a.rightShiftUnsigned(b) – account << 10.dollars +a            //  a.unaryPlus() account += 10.dollars -­‐a            //  a.unaryMinus() account.credit 10.dollars ~a            //  a.bitwiseNegate()60
  104. 104. Operator overloading • Update the Distance class with a div() method following the naming convention for operators class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... }61
  105. 105. Operator overloading • Update the Distance class with a div() method following the naming convention for operators class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } Optional return61
  106. 106. Equivalence of notation • Those two notations are actually equivalent: 2.km/h 2.getKm().div(h)62
  107. 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. 108. Named parameters usage move  left,  at:  3.km/h63
  109. 109. Named parameters usage move  left,  at:  3.km/h Normal parameter63
  110. 110. Named parameters usage move  left,  at:  3.km/h Normal Named parameter parameter63
  111. 111. Named parameters usage move  left,  at:  3.km/h Normal Named parameter parameter Will call: def  move(Map  m,  Direction  q)63
  112. 112. Named parameters usage move  left,  at:  3.km/h Normal Named parameter parameter Will call: def  move(Map  m,  Direction  q) All named parameters go into the map argument63
  113. 113. Named parameters usage move  left,  at:  3.km/h Normal Named parameter parameter Will call: def  move(Map  m,  Direction  q) All named parameters go Positional parameters into the map argument come afterwards63
  114. 114. Named parameters usage move  left,  at:  3.km/h64
  115. 115. Named parameters usage move  left,  at:  3.km/h Can we get rid of the comma?64
  116. 116. Named parameters usage move  left,  at:  3.km/h Can we get rid of What about the the comma? colon too?64
  117. 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. 118. Command chains  move  left    at  3.km/h  66
  119. 119. Command chains Alternation of method names  move  left    at  3.km/h  66
  120. 120. Command chains Alternation of method names  move  left    at  3.km/h   and parameters (even named ones)66
  121. 121. Command chains  move  left    at  3.km/h  66
  122. 122. Command chains Equivalent to:  move  left    at  3.km/h            (        ).    (            )66
  123. 123. Look Ma!No parens, n odots!
  124. 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. 125. Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐>                ...        }] } 69
  126. 126. Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐> Nested maps                ... and closures        }] } 69
  127. 127. Command chains def  move(Direction  dir)  {        [at:  {  Speed  speed  -­‐> Nested maps                ... and closures        }] } Usage in your DSLs move  left  at  3.km/h 69
  128. 128. Command chains 70
  129. 129. Command chains //  methods  with  multiple  arguments  (commas) 70
  130. 130. Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor 70
  131. 131. Command chains //  methods  with  multiple  arguments  (commas) take  coffee    with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation 70
  132. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 144. Final result71
  145. 145. Final result move  forward  at  3.km/h71
  146. 146. move forward at 3.km/h
  147. 147. move forward at 3.km/h Yes! We did it!
  148. 148. What aboutsecurity and safety?
  149. 149. Security and Safety JVM Security Managers SecureASTCustomizer Sandboxing Controlling script execution© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  150. 150. Play it safe in a sandbox
  151. 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 transformations76
  152. 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 files77
  153. 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. 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 code79
  155. 155. @ThreadInterrupt @ThreadInterrupt import  groovy.transform.ThreadInterrupt     while  (true)  {        //  Any  extraterestrial  around? } 80
  156. 156. @ThreadInterrupt @ThreadInterrupt import  groovy.transform.ThreadInterrupt       while  (true)  { {        if  (Thread.currentThread().isInterrupted())                throw  new  InterruptedException() }        //  Any  extraterestrial  around? } 80
  157. 157. @TimedInterrupt @TimedInterrupt(10) import  groovy.transform.TimedInterrupt     while  (true)  {        move  left        //  circle  forever } • InterruptedException thrown when checks indicate code ran longer than desired81
  158. 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. 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 Can we avoid typing the 100.times  {         conditional        move  forward  at  10.km/h interrupt? }82
  160. 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 Groovy 1.8 Yes! Using compilation 100.times  {         customizers        move  forward  at  10.km/h }83
  161. 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 Customizer84
  162. 162. What about tooling?
  163. 163. Tooling Why tooling? DSL descriptors Pointcuts and contributions Packaging DSLDs© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  164. 164. Why tooling? • I know what this language means –why do I want anything more?87
  165. 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 documentation87
  166. 166. Let’s use an IDE • I hear Groovy-Eclipse is pretty good…88
  167. 167. Let’s use an IDE • I hear Groovy-Eclipse is pretty good…88
  168. 168. Let’s use an IDE • I hear Groovy-Eclipse is pretty good… Uh oh!88
  169. 169. Let’s use an IDE • I hear Groovy-Eclipse is pretty good… Uh oh! Can we do better?88
  170. 170. Of course! • Eclipse is extensible – with a plugin architecture Eclipse platform New WorkBench plugin Help JFace SWT New Team tool Workspace Platform runtime89
  171. 171. I want my DSL supported in Eclipse90
  172. 172. 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 it90
  173. 173. I want my DSL supported in Eclipse • Let’s create a plugin • Problems – create a plugin project – I don’t want to learn – extend an extension point Eclipse APIs – write the code – I want an easy way for users to – build the plugin install the DSL support – host on an update site – I need a specific plugin version for my specific DSL version – convince people to install it90
  174. 174. I want my DSL supported in Eclipse Uh oh! • Let’s create a plugin • Problems – create a plugin project – I don’t want to learn – extend an extension point Eclipse APIs – write the code – I want an easy way for users to – build the plugin install the DSL support – host on an update site – I need a specific plugin version for my specific DSL version – convince people to install it90
  175. 175. I want my DSL supported in Eclipse Uh oh! • Let’s create a plugin • Problems – create a plugin project – I don’t want to learn – extend an extension point Eclipse APIs – write the code – I want an easy way for users to – build the plugin install the DSL support – host on an update site – I need a specific plugin version for my specific DSL version – convince people to install it Can we do better?90
  176. 176. Of course! • Groovy is extensible! – Meta-Object Protocol – Metaprogramming – DSLs...91
  177. 177. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL92
  178. 178. 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 libraries92
  179. 179. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – Powerful DSL Descriptors (DSLD) – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries92
  180. 180. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – Powerful DSL Descriptors (DSLD) – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries In IntelliJ. called GDSL92
  181. 181. Let’s start simple move deploy h left right forward backward93
  182. 182. Let’s start simple move • In English: deploy h – When the type is this, add the following properties/methods left • move, deploy, h, etc from binding right • Direction from import customizer forward backward93
  183. 183. Let’s start simple move • In English: deploy h – When the type is this, add the following properties/methods left • move, deploy, h, etc from binding right • Direction from import customizer forward • In DSLD: backward – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: v11.Direction … method name: move, type: java.util.Map<…>93
  184. 184. Let’s start simple move • In English: deploy h – When the type is this, add the following properties/methods left • move, deploy, h, etc from binding right • Direction from import customizer forward • In DSLD: Pointcut backward – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: v11.Direction … method name: move, type: java.util.Map<…>93
  185. 185. Let’s start simple move • In English: deploy h – When the type is this, add the following properties/methods left • move, deploy, h, etc from binding right • Direction from import customizer forward • In DSLD: Pointcut backward – When the type is this Contribution contribute( isThisType() ) {…} block – …properties/methods… property name: left, type: v11.Direction … method name: move, type: java.util.Map<…>93
  186. 186. DEMO LET’S SEE THAT94
  187. 187. 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 type95
  188. 188. Anatomy of a DSLD script • Pointcuts Where – 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 type95
  189. 189. Anatomy of a DSLD script • Pointcuts Where – Where to do it – What is the current expression? – Current type? What – Enclosing class? • Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type95
  190. 190. Anatomy of a DSLD script • Pointcuts Where – Where to do it – What is the current expression? – Current type? What – Enclosing class? • Contribution blocks – What to do – « Add » method Not at runtime... only while editing – « Add » property – Delegate to another type95
  191. 191. Talking about « x » class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }96
  192. 192. Talking about « x » Current type class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }96
  193. 193. Talking about « x » Current type class  Other  {  } class  Foo  { Enclosing class        def  method()  {                def  x  =  new  Other()                x.nuthin        } }96
  194. 194. Talking about « x » Current type class  Other  {  } class  Foo  { Enclosing class        def  method()  {                def  x  =  new  Other()                x.nuthin Enclosing method        } }96
  195. 195. Pointcuts97
  196. 196. Pointcuts currentType()        //  matches  on  current  declaring  type97
  197. 197. Pointcuts currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script97
  198. 198. Pointcuts currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot")97
  199. 199. Pointcuts currentType()        //  matches  on  current  declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot"))97
  200. 200. Pointcuts currentType()        //  matches  on  current  declaring  type isScript()         €
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×