Top Banner
Groovy Domain-Specific Languages Andrew Eisenberg Groovy Eclipse Project Lead SpringSource / VMware @werdnagreb © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission. Paul King Groovy Core Developer ASERT @paulk_asert Guillaume Laforge Groovy Project Manager SpringSource / VMware @glaforge
229

Groovy Domain Specific Languages - SpringOne2GX 2012

May 17, 2015

Download

Technology

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.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Groovy Domain Specific Languages - SpringOne2GX 2012

Groovy Domain-Specific LanguagesAndrew EisenbergGroovy Eclipse Project Lead

SpringSource / VMware@werdnagreb

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.

Paul KingGroovy Core Developer

ASERT@paulk_asert

Guillaume LaforgeGroovy Project ManagerSpringSource / VMware

@glaforge

Page 2: Groovy Domain Specific Languages - SpringOne2GX 2012

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/aeisenberg

2

Page 3: Groovy Domain Specific Languages - SpringOne2GX 2012

Paul King

•Groovy Core Committer• Leads ASERT

– software, training, consultancy company based in Brisbane, Australia

• PhD in Computer Science from The University of Queensland• Co-author of Groovy in Action

• Follow me: – Twitter: @paulk_asert

3

Page 4: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 5: Groovy Domain Specific Languages - SpringOne2GX 2012

Introduction

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.

DefinitionsExamples

GoalsPros & cons

Page 6: Groovy Domain Specific Languages - SpringOne2GX 2012

Domain-Specific Languages

• In contrast to General Purpose Languages• Also known as: fluent / humane interfaces, language oriented

programming, little or mini languages, macros, business natural languages...

6

{ }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.

Page 7: Groovy Domain Specific Languages - SpringOne2GX 2012

Technical examples

<?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>

<?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>

# 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 ~/.netrcpoll mailhost.net with proto imap: user esr is esr here

"x.z?z{1,3}y"

SELECT * FROM TABLEWHERE NAME LIKE '%SMI'ORDER BY NAME

GladeRegex

XSLT

Fetchmail

SQL

Page 8: Groovy Domain Specific Languages - SpringOne2GX 2012

Antimalaria drug resistance simulation

HR skills representationInsurance policy risk calculation engine

Market data feeds analysis Loan acceptance rules engine

Nuclear safety simulations

Page 9: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 10: Groovy Domain Specific Languages - SpringOne2GX 2012

Pros and cons

Pros– Domain experts can help,

validate, modify, and often develop DSL programs

– Somewhat self-documenting– Enhance quality, productivity,

reliability, maintainability, portability, reusability

– Safety; as long as the language constructs are safe, any DSL sentence can be considered safe

Cons– Learning cost vs. limited applicability– Cost of designing, implementing &

maintaining DSLs as well as tools/IDEs

– Attaining proper scope– Trade-offs between domain

specificity and general purpose language constructs

– Efficiency cost– Proliferation of similar

non-standard DSLs10

Page 11: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 12: Groovy Domain Specific Languages - SpringOne2GX 2012

Let’s get started!

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.

Page 13: Groovy Domain Specific Languages - SpringOne2GX 2012
Page 14: Groovy Domain Specific Languages - SpringOne2GX 2012

Your mission: build a DSL for a Mars robot

Page 15: Groovy Domain Specific Languages - SpringOne2GX 2012

We need a robot!

15

package  mars

class  Robot  {}

Page 16: Groovy Domain Specific Languages - SpringOne2GX 2012

It should move...

16

package  mars  

class  Robot  {        void  move()  {}}

Page 17: Groovy Domain Specific Languages - SpringOne2GX 2012

..in a direction!

17

package  mars  

class  Robot  {        void  move(String  dir)  {}}

Page 18: Groovy Domain Specific Languages - SpringOne2GX 2012

More explicit direction

18

package  mars  

class  Robot  {        void  move(Direction  dir)  {}}

package  mars  

enum  Direction  {        left,  right,  forward,  backward}

Page 19: Groovy Domain Specific Languages - SpringOne2GX 2012

Now how can we control it?

19

import  static  mars.Direction.*;import  mars.Robot;

public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        }}

Page 20: Groovy Domain Specific Languages - SpringOne2GX 2012

Now how can we control it?

19

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!

Page 21: Groovy Domain Specific Languages - SpringOne2GX 2012

Now how can we control it?

19

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!

                                                           —                                  —

——————————————————————        ————————————————————————————————————————                —————                                        —                                    —        ——        ——

Page 22: Groovy Domain Specific Languages - SpringOne2GX 2012

Optional semicolons & parentheses / Scripts vs classes

20

import  static  mars.Direction.*import  mars.Robot

               def      robot  =  new  Robot()                robot.move  left

Page 23: Groovy Domain Specific Languages - SpringOne2GX 2012

Optional semicolons & parentheses / Scripts vs classes

20

import  static  mars.Direction.*import  mars.Robot

               def      robot  =  new  Robot()                robot.move  left

Optional typing

Page 24: Groovy Domain Specific Languages - SpringOne2GX 2012

Optional semicolons & parentheses / Scripts vs classes

20

import  static  mars.Direction.*import  mars.Robot

               def      robot  =  new  Robot()                robot.move  left

But I don’t want to compile a script for

every command!

Optional typing

Page 25: Groovy Domain Specific Languages - SpringOne2GX 2012

Integration

Page 26: Groovy Domain Specific Languages - SpringOne2GX 2012

GroovyShell to the rescue

22

Page 27: Groovy Domain Specific Languages - SpringOne2GX 2012

GroovyShell to the rescue

22

def  shell  =  new  GroovyShell()shell.evaluate(        new  File("command.groovy"))

Page 28: Groovy Domain Specific Languages - SpringOne2GX 2012

GroovyShell to the rescue

22

def  shell  =  new  GroovyShell()shell.evaluate(        new  File("command.groovy"))

integration.groovy

Page 29: Groovy Domain Specific Languages - SpringOne2GX 2012

GroovyShell to the rescue

22

import  static  mars.Direction.*import  mars.Robot

def  robot  =  new  Robot()robot.move  left

def  shell  =  new  GroovyShell()shell.evaluate(        new  File("command.groovy"))

integration.groovy

Page 30: Groovy Domain Specific Languages - SpringOne2GX 2012

GroovyShell to the rescue

22

import  static  mars.Direction.*import  mars.Robot

def  robot  =  new  Robot()robot.move  left

def  shell  =  new  GroovyShell()shell.evaluate(        new  File("command.groovy"))

integration.groovy

command.groovy

Page 31: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 32: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 33: Groovy Domain Specific Languages - SpringOne2GX 2012

What’s wrong with our DSL?

24

import  static  mars.Direction.*import  mars.Robot

def  robot  =  new  Robot()robot.move  left

Page 34: Groovy Domain Specific Languages - SpringOne2GX 2012

What’s wrong with our DSL?

24

import  static  mars.Direction.*import  mars.Robot

def  robot  =  new  Robot()robot.move  left

Can’t we hide those imports?

Page 35: Groovy Domain Specific Languages - SpringOne2GX 2012

What’s wrong with our DSL?

24

import  static  mars.Direction.*import  mars.Robot

def  robot  =  new  Robot()robot.move  left

Can’t we hide those imports?

Can’t we inject the robot?

Page 36: Groovy Domain Specific Languages - SpringOne2GX 2012

What’s wrong with our DSL?

24

import  static  mars.Direction.*import  mars.Robot

def  robot  =  new  Robot()robot.move  left

Can’t we hide those imports?

Can’t we inject the robot?Do we really need to

repeat ‘robot’?

Page 37: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 38: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 39: Groovy Domain Specific Languages - SpringOne2GX 2012

What we really want is...

26

 move  left  

Page 40: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 41: Groovy Domain Specific Languages - SpringOne2GX 2012

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

def  binding  =  new  Binding([        robot:  new  mars.Robot()])def  shell  =  new  GroovyShell(binding)shell.evaluate(        new  File("command.groovy"))

Page 42: Groovy Domain Specific Languages - SpringOne2GX 2012

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

def  binding  =  new  Binding([        robot:  new  mars.Robot()])def  shell  =  new  GroovyShell(binding)shell.evaluate(        new  File("command.groovy"))

integration.groovy

Page 43: Groovy Domain Specific Languages - SpringOne2GX 2012

Better?

28

import  static  mars.Direction.*

robot.move  left

Page 44: Groovy Domain Specific Languages - SpringOne2GX 2012

Better?

28

import  static  mars.Direction.*

robot.move  left

Robot import removed

Page 45: Groovy Domain Specific Languages - SpringOne2GX 2012

Better?

28

import  static  mars.Direction.*

robot.move  left

Robot import removed

Robot injected,no ‘new’ needed

Page 46: Groovy Domain Specific Languages - SpringOne2GX 2012

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"))

Page 47: Groovy Domain Specific Languages - SpringOne2GX 2012

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"))

Fragile in case of new directions!

Page 48: Groovy Domain Specific Languages - SpringOne2GX 2012

How to inject the direction?

• Using the binding...

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"))

Spread map operator

Page 49: Groovy Domain Specific Languages - SpringOne2GX 2012

How to inject the direction?

• Using string concatenation?

• Using compiler customizers

31

Page 50: Groovy Domain Specific Languages - SpringOne2GX 2012

String concatenation? Bad idea!

32

new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Page 51: Groovy Domain Specific Languages - SpringOne2GX 2012

String concatenation? Bad idea!

32

new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Cheat with string concatenation? Bad!

Page 52: Groovy Domain Specific Languages - SpringOne2GX 2012

String concatenation? Bad idea!

32

new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Page 53: Groovy Domain Specific Languages - SpringOne2GX 2012

String concatenation? Bad idea!

32

new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Line #1 becomes Line #2

Page 54: Groovy Domain Specific Languages - SpringOne2GX 2012

String concatenation? Bad idea!

32

new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))        .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")

Page 55: Groovy Domain Specific Languages - SpringOne2GX 2012

Compilation customizers

• Ability to apply some customization to the Groovy compilation process

• Three available customizers– ImportCustomizer: add transparent imports– ASTTransformationCustomizer: injects an AST transform– SecureASTCustomizer:

restrict the groovy language to an allowed subset

• But you can implement your own

33

Groovy 1.8

Page 56: Groovy Domain Specific Languages - SpringOne2GX 2012

Imports customizer

34

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")            

Page 57: Groovy Domain Specific Languages - SpringOne2GX 2012

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’")                      

Page 58: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 59: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure the onboard trajectory calculator

Page 60: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

• Let’s set up our environment– an import customizer to import java.lang.Math.*– prepare a secure AST customizer

37

def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math')def  secure  =  new  SecureASTCustomizer()

Page 61: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

• Let’s set up our environment– an import customizer to import java.lang.Math.*– prepare a secure AST customizer

37

def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math')def  secure  =  new  SecureASTCustomizer()

Idea: secure the rocket’s onboard trajectory calculation system by allowing only math

expressions to be evaluated by the calculator

Page 62: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

38

...secure.with  {

//  disallow  closure  creationclosuresAllowed  =  false  //  disallow  method  definitionsmethodDefinitionAllowed  =  false  

 //  empty  white  list  =>  forbid  importsimportsWhitelist  =  []  staticImportsWhitelist  =  []//  only  allow  the  java.lang.Math.*  static  importstaticStarImportsWhitelist  =  ['java.lang.Math']

...

Page 63: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

38

...secure.with  {

//  disallow  closure  creationclosuresAllowed  =  false  //  disallow  method  definitionsmethodDefinitionAllowed  =  false  

 //  empty  white  list  =>  forbid  importsimportsWhitelist  =  []  staticImportsWhitelist  =  []//  only  allow  the  java.lang.Math.*  static  importstaticStarImportsWhitelist  =  ['java.lang.Math']

...

Disallow closures and methods

Page 64: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

38

...secure.with  {

//  disallow  closure  creationclosuresAllowed  =  false  //  disallow  method  definitionsmethodDefinitionAllowed  =  false  

 //  empty  white  list  =>  forbid  importsimportsWhitelist  =  []  staticImportsWhitelist  =  []//  only  allow  the  java.lang.Math.*  static  importstaticStarImportsWhitelist  =  ['java.lang.Math']

...

Disallow closures and methods

Black / white listimports

Page 65: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

39

...//  language  tokens  allowedtokensWhitelist  =  [

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  callsreceiversClassesWhiteList  =  [  

Math,  Integer,  Float,  Double,  Long,  BigDecimal  ]}...

Page 66: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

39

...//  language  tokens  allowedtokensWhitelist  =  [

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  callsreceiversClassesWhiteList  =  [  

Math,  Integer,  Float,  Double,  Long,  BigDecimal  ]}...

You can build a subset of the Groovy syntax!

Page 67: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

39

...//  language  tokens  allowedtokensWhitelist  =  [

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  callsreceiversClassesWhiteList  =  [  

Math,  Integer,  Float,  Double,  Long,  BigDecimal  ]}...

You can build a subset of the Groovy syntax!

Black / white list usage of classes

Page 68: Groovy Domain Specific Languages - SpringOne2GX 2012

Secure AST customizer

• Ready to evaluate our flight equations!

• But the following would have failed:

40

shell.evaluate  'System.exit(0)'

def  config  =  new  CompilerConfiguration()config.addCompilationCustomizers(imports,  secure)def  shell  =  new  GroovyShell(config)  shell.evaluate  'cos  PI/3'

Page 69: Groovy Domain Specific Languages - SpringOne2GX 2012

Back to our robot...

41

robot.move  left

Page 70: Groovy Domain Specific Languages - SpringOne2GX 2012

Back to our robot...

41

robot.move  left

Still need to get rid of the robot prefix!

Page 71: Groovy Domain Specific Languages - SpringOne2GX 2012

Can we remove it?

Page 72: Groovy Domain Specific Languages - SpringOne2GX 2012

Can we remove it?Yes !

Page 73: Groovy Domain Specific Languages - SpringOne2GX 2012

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

43

• Inject a ‘move’ closure in the binding with a method pointer

• Use a base script class with a ‘move’ method delegating to the robot

Page 74: Groovy Domain Specific Languages - SpringOne2GX 2012

Inject a closure in the binding

44

def  robot  =  new  mars.Robot()binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },

       move:  robot.&move])

Page 75: Groovy Domain Specific Languages - SpringOne2GX 2012

Inject a closure in the binding

44

def  robot  =  new  mars.Robot()binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },

       move:  robot.&move])

Method pointer (a closure) on robot’s move

instance method

Page 76: Groovy Domain Specific Languages - SpringOne2GX 2012

Define a base script class

45

abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        }}

Page 77: Groovy Domain Specific Languages - SpringOne2GX 2012

Define a base script class

45

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

Page 78: Groovy Domain Specific Languages - SpringOne2GX 2012

Define a base script class

45

abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        }}

The move() method is now at the script level

Access the robot through the script’s

binding

Page 79: Groovy Domain Specific Languages - SpringOne2GX 2012

Configure the base script class

46

def  conf  =  new  CompilerConfiguration()conf.scriptBaseClass  =  RobotBaseScriptClass

Page 80: Groovy Domain Specific Languages - SpringOne2GX 2012

Configure the base script class

46

def  conf  =  new  CompilerConfiguration()conf.scriptBaseClass  =  RobotBaseScriptClass

Scripts evaluated with this configuration will inherit

from that class

Page 81: Groovy Domain Specific Languages - SpringOne2GX 2012

Ready for lift off!

   move  left

Page 82: Groovy Domain Specific Languages - SpringOne2GX 2012

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

...beep...

Page 83: Groovy Domain Specific Languages - SpringOne2GX 2012

Oh no!

Page 84: Groovy Domain Specific Languages - SpringOne2GX 2012

What we could do now is...

50

move  left,  at:  3.km/h

Page 85: Groovy Domain Specific Languages - SpringOne2GX 2012

What we could do now is...

50

move  left,  at:  3.km/h

Mix of named and normal parameters

Page 86: Groovy Domain Specific Languages - SpringOne2GX 2012

What we could do now is...

50

move  left,  at:  3.km/h

How to support this speed notation?

Mix of named and normal parameters

Page 87: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 88: Groovy Domain Specific Languages - SpringOne2GX 2012

Distance unit enum and distance

52

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  }  }

Page 89: Groovy Domain Specific Languages - SpringOne2GX 2012

Distance unit enum and distance

52

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

       String  toString()  {  abbreviation  }  }

@TupleConstructor  class  Distance  {        double  amount          DistanceUnit  unit

       String  toString()  {                  "$amount  $unit"          }  }

Page 90: Groovy Domain Specific Languages - SpringOne2GX 2012

Time unit enum and duration

53

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  }  }

Page 91: Groovy Domain Specific Languages - SpringOne2GX 2012

Time unit enum and duration

53

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

       String  toString()  {  abbreviation  }  }

@TupleConstructor  class  Duration  {        double  amount          TimeUnit  unit

       String  toString()  {                  "$amount  $unit"          }  }

Page 92: Groovy Domain Specific Languages - SpringOne2GX 2012

Now at (light!) speed

54

@TupleConstructor  class  Speed  {        Distance  distance        Duration  dur

       String  toString()  {                  "$distance/$dur"          }  }

speed =distance

duration

Page 93: Groovy Domain Specific Languages - SpringOne2GX 2012

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:

55

2.km

2.getKm()

Page 94: Groovy Domain Specific Languages - SpringOne2GX 2012

Techniques to add properties to numbers

• To add dynamic methods or properties, there are several approaches at your disposal:

– ExpandoMetaClass– custom MetaClass– Categories– Extension modules

• Let’s have a look at the ExpandoMetaClass

56

Groovy 2!

Page 95: Groovy Domain Specific Languages - SpringOne2GX 2012

Using ExpandoMetaClass

57

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

Page 96: Groovy Domain Specific Languages - SpringOne2GX 2012

Using ExpandoMetaClass

57

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

Add that to integration.groovy

Page 97: Groovy Domain Specific Languages - SpringOne2GX 2012

Using ExpandoMetaClass

57

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

Add that to integration.groovy

‘delegate’ is the current number

Page 98: Groovy Domain Specific Languages - SpringOne2GX 2012

Using ExpandoMetaClass

57

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

40.cm  3.5.m4.km

Add that to integration.groovy

‘delegate’ is the current number

Usage in your DSLs

Page 99: Groovy Domain Specific Languages - SpringOne2GX 2012

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

58

2.km/h

Page 100: Groovy Domain Specific Languages - SpringOne2GX 2012

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

58

2.km/h

The div() method on Distance

Page 101: Groovy Domain Specific Languages - SpringOne2GX 2012

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

58

2.km/h

The div() method on Distance

An ‘h’ duration instance in the binding

Page 102: Groovy Domain Specific Languages - SpringOne2GX 2012

Inject the ‘h’ hour constant in the binding

59

def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },

       h:  new  Duration(1,  TimeUnit.hour)])

Page 103: Groovy Domain Specific Languages - SpringOne2GX 2012

Inject the ‘h’ hour constant in the binding

59

def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },

       h:  new  Duration(1,  TimeUnit.hour)])

An ‘h’ duration added to the binding

Page 104: Groovy Domain Specific Languages - SpringOne2GX 2012

Operator overloading

• Currency amounts– 15.euros + 10.dollars

• Distance handling– 10.km - 10.m

• Workflow, concurrency– taskA | taskB & taskC

• Credit an account– account << 10.dollarsaccount += 10.dollarsaccount.credit 10.dollars

60

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()

Page 105: Groovy Domain Specific Languages - SpringOne2GX 2012

Operator overloading

• Update the Distance class with a div() method following the naming convention for operators

61

class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ...}

Page 106: Groovy Domain Specific Languages - SpringOne2GX 2012

Operator overloading

• Update the Distance class with a div() method following the naming convention for operators

61

class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ...} Optional return

Page 107: Groovy Domain Specific Languages - SpringOne2GX 2012

Equivalence of notation

• Those two notations are actually equivalent:

62

2.km/h

2.getKm().div(h)

Page 108: Groovy Domain Specific Languages - SpringOne2GX 2012

Equivalence of notation

• Those two notations are actually equivalent:

62

2.km/h

2.getKm().div(h)

⇔ This one might be slightly more verbose!

Page 109: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

63

Page 110: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

63

Normal parameter

Page 111: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

63

Namedparameter

Normal parameter

Page 112: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

Will call:

def  move(Map  m,  Direction  q)

63

Namedparameter

Normal parameter

Page 113: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

Will call:

def  move(Map  m,  Direction  q)

63

Namedparameter

Normal parameter

All named parameters go into the map argument

Page 114: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

Will call:

def  move(Map  m,  Direction  q)

63

Namedparameter

Normal parameter

All named parameters go into the map argument

Positional parameters come afterwards

Page 115: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

64

Page 116: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

64

Can we get rid of the comma?

Page 117: Groovy Domain Specific Languages - SpringOne2GX 2012

Named parameters usage

move  left,  at:  3.km/h

64

Can we get rid of the comma?

What about the colon too?

Page 118: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

• A grammar improvement allowing you to drop dots & parens when chaining method calls– an extended version of top-level statements like println

• Less dots, less parens allow you to – write more readable business rules– in almost plain English sentences

• (or any language, of course)

65

Groovy 1.8

Page 119: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

 move  left    at  3.km/h  

66

Page 120: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

 move  left    at  3.km/h  

Alternation of method names

66

Page 121: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

 move  left    at  3.km/h  

Alternation of method names

and parameters(even named ones)

66

Page 122: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

 move  left    at  3.km/h  

66

Page 123: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

 move  left    at  3.km/h            (        ).    (            )

Equivalent to:

66

Page 124: Groovy Domain Specific Languages - SpringOne2GX 2012

Look Ma!

No parens,

no dots!

Page 125: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

//  Java  fluent  API  approachclass  Robot  {        ...        def  move(Direction  dir)  {                this.dir  =  dir                return  this        }

       def  at(Speed  speed)  {                this.speed  =  speed                return  this        }        ...}

68

Page 126: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

69

Page 127: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

69

Nested maps and closures

Page 128: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

69

move  left  at  3.km/hUsage in your DSLs

Nested maps and closures

Page 129: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

70

Page 130: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

//  methods  with  multiple  arguments  (commas)

70

Page 131: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

70

Page 132: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuation

70

Page 133: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

70

Page 134: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structures

70

Page 135: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

70

Page 136: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parens

70

Page 137: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

70

Page 138: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  terms

70

Page 139: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  termsdeploy  left    arm

70

Page 140: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  termsdeploy  left    arm

       (            ).        (                      ).      (            )

70

Page 141: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  termsdeploy  left    arm

       (            ).        (                      ).      (            )

         (                      ).            (        )

70

Page 142: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  termsdeploy  left    arm

       (            ).        (                      ).      (            )

         (                      ).            (        )

         (    ).        (    ).        (    )

70

Page 143: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  termsdeploy  left    arm

       (            ).        (                      ).      (            )

         (                      ).            (        )

         (    ).        (    ).        (    )

           (      ).                .        (          )

70

Page 144: Groovy Domain Specific Languages - SpringOne2GX 2012

Command chains

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

//  leverage  named-­‐args  as  punctuationcheck  that:  vodka    tastes  good

//  closure  parameters  for  new  control  structuresgiven  {}    when  {}    then  {}

//  zero-­‐arg  methods  require  parensselect  all    unique()  from  names

//  possible  with  an  odd  number  of  termsdeploy  left    arm

       (            ).        (                      ).      (            )

         (                      ).            (        )

         (    ).        (    ).        (    )

           (      ).                .        (          )

           (        ).

70

Page 145: Groovy Domain Specific Languages - SpringOne2GX 2012

Final result

71

Page 146: Groovy Domain Specific Languages - SpringOne2GX 2012

Final result

71

move  forward  at  3.km/h

Page 147: Groovy Domain Specific Languages - SpringOne2GX 2012

move forward at 3.km/h

Page 148: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 149: Groovy Domain Specific Languages - SpringOne2GX 2012

What about security and

safety?

Page 150: Groovy Domain Specific Languages - SpringOne2GX 2012

Security and Safety

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.

JVM Security ManagersSecureASTCustomizer

SandboxingControlling script execution

Page 151: Groovy Domain Specific Languages - SpringOne2GX 2012

Play it safe in a sandbox

Page 152: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 153: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 154: Groovy Domain Specific Languages - SpringOne2GX 2012

SecureASTCustomizer

78

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)

Page 155: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 156: Groovy Domain Specific Languages - SpringOne2GX 2012

@ThreadInterrupt

80

@ThreadInterruptimport  groovy.transform.ThreadInterrupt    while  (true)  {

       //  Any  extraterestrial  around?}

Page 157: Groovy Domain Specific Languages - SpringOne2GX 2012

@ThreadInterrupt

80

@ThreadInterruptimport  groovy.transform.ThreadInterrupt    while  (true)  {

       //  Any  extraterestrial  around?}

 

       if  (Thread.currentThread().isInterrupted())                throw  new  InterruptedException(){ }

Page 158: Groovy Domain Specific Languages - SpringOne2GX 2012

@TimedInterrupt

• InterruptedException thrown when checks indicate code ran longer than desired

81

@TimedInterrupt(10)import  groovy.transform.TimedInterrupt    while  (true)  {        move  left        //  circle  forever}

Page 159: Groovy Domain Specific Languages - SpringOne2GX 2012

@ConditionalInterrupt

• Specify your own conditions to be inserted at the start of method and closure bodies– check for available resources, number of times run, etc.

• Leverages closure annotation parameters

82

Groovy 1.8

@ConditionalInterrupt({  battery.level  <  0.1  })import  groovy.transform.ConditionalInterrupt

100.times  {                move  forward  at  10.km/h}

Page 160: Groovy Domain Specific Languages - SpringOne2GX 2012

@ConditionalInterrupt

• Specify your own conditions to be inserted at the start of method and closure bodies– check for available resources, number of times run, etc.

• Leverages closure annotation parameters

82

Groovy 1.8

@ConditionalInterrupt({  battery.level  <  0.1  })import  groovy.transform.ConditionalInterrupt

100.times  {                move  forward  at  10.km/h}

Can we avoid typing the conditional interrupt?

Page 161: Groovy Domain Specific Languages - SpringOne2GX 2012

@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

83

Groovy 1.8

100.times  {                move  forward  at  10.km/h}

Yes! Using compilation customizers

Page 162: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 163: Groovy Domain Specific Languages - SpringOne2GX 2012

What about tooling?

Page 164: Groovy Domain Specific Languages - SpringOne2GX 2012

Tooling

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.

Why tooling?DSL descriptors

Pointcuts and contributionsPackaging DSLDs

Page 165: Groovy Domain Specific Languages - SpringOne2GX 2012

87

Why tooling?

• I know what this language means–why do I want anything more?

Page 166: Groovy Domain Specific Languages - SpringOne2GX 2012

87

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

Page 167: Groovy Domain Specific Languages - SpringOne2GX 2012

88

Let’s use an IDE

88

• I hear Groovy-Eclipse is pretty good…

Page 168: Groovy Domain Specific Languages - SpringOne2GX 2012

88

Let’s use an IDE

88

• I hear Groovy-Eclipse is pretty good…

Page 169: Groovy Domain Specific Languages - SpringOne2GX 2012

88

Let’s use an IDE

88

• I hear Groovy-Eclipse is pretty good…

Uh oh!

Page 170: Groovy Domain Specific Languages - SpringOne2GX 2012

88

Let’s use an IDE

88

• I hear Groovy-Eclipse is pretty good…

Uh oh!

Can we do better?

Page 171: Groovy Domain Specific Languages - SpringOne2GX 2012

Of course!

• Eclipse is extensible– with a plugin

architecture

89

Eclipse platform

Platform runtime

WorkBench

JFace

SWT

Workspace

Help

Team

New plugin

New tool

Page 172: Groovy Domain Specific Languages - SpringOne2GX 2012

I want my DSL supported in Eclipse

90

Page 173: Groovy Domain Specific Languages - SpringOne2GX 2012

I want my DSL supported in Eclipse

• Let’s create a plugin– create a plugin project– extend an extension point– write the code– build the plugin– host on an update site– convince people to install it

90

Page 174: Groovy Domain Specific Languages - SpringOne2GX 2012

I want my DSL supported in Eclipse

• Let’s create a plugin– create a plugin project– extend an extension point– write the code– build the plugin– host on an update site– convince people to install it

• Problems– I don’t want to learn

Eclipse APIs– I want an easy way for users to

install the DSL support– I need a specific plugin version

for my specific DSL version

90

Page 175: Groovy Domain Specific Languages - SpringOne2GX 2012

I want my DSL supported in Eclipse

• Let’s create a plugin– create a plugin project– extend an extension point– write the code– build the plugin– host on an update site– convince people to install it

• Problems– I don’t want to learn

Eclipse APIs– I want an easy way for users to

install the DSL support– I need a specific plugin version

for my specific DSL version

90

Uh oh!

Page 176: Groovy Domain Specific Languages - SpringOne2GX 2012

I want my DSL supported in Eclipse

• Let’s create a plugin– create a plugin project– extend an extension point– write the code– build the plugin– host on an update site– convince people to install it

• Problems– I don’t want to learn

Eclipse APIs– I want an easy way for users to

install the DSL support– I need a specific plugin version

for my specific DSL version

90

Uh oh!

Can we do better?

Page 177: Groovy Domain Specific Languages - SpringOne2GX 2012

Of course!

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

91

Page 178: Groovy Domain Specific Languages - SpringOne2GX 2012

DSL Descriptors

• Teach the IDE about DSLs through a Groovy DSL

92

Page 179: Groovy Domain Specific Languages - SpringOne2GX 2012

DSL Descriptors

• Teach the IDE about DSLs through a Groovy DSL

• Benefits– Powerful– Uses Groovy syntax, semantics, and APIs– No knowledge of Eclipse required– Can ship with Groovy libraries

92

Page 180: Groovy Domain Specific Languages - SpringOne2GX 2012

DSL Descriptors

• Teach the IDE about DSLs through a Groovy DSL

• Benefits– Powerful– Uses Groovy syntax, semantics, and APIs– No knowledge of Eclipse required– Can ship with Groovy libraries

92

DSL Descriptors(DSLD)

Page 181: Groovy Domain Specific Languages - SpringOne2GX 2012

DSL Descriptors

• Teach the IDE about DSLs through a Groovy DSL

• Benefits– Powerful– Uses Groovy syntax, semantics, and APIs– No knowledge of Eclipse required– Can ship with Groovy libraries

92

DSL Descriptors(DSLD)

In IntelliJ. called GDSL

Page 182: Groovy Domain Specific Languages - SpringOne2GX 2012

Let’s start simple

93

movedeployhleftrightforwardbackward

Page 183: Groovy Domain Specific Languages - SpringOne2GX 2012

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

movedeployhleftrightforwardbackward

Page 184: Groovy Domain Specific Languages - SpringOne2GX 2012

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 thiscontribute( isThisType() ) {…}

– …properties/methods…property name: left, type: 'v11.Direction' …method name: move, type: 'java.util.Map<…>'

93

movedeployhleftrightforwardbackward

Page 185: Groovy Domain Specific Languages - SpringOne2GX 2012

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 thiscontribute( isThisType() ) {…}

– …properties/methods…property name: left, type: 'v11.Direction' …method name: move, type: 'java.util.Map<…>'

93

Pointcut

movedeployhleftrightforwardbackward

Page 186: Groovy Domain Specific Languages - SpringOne2GX 2012

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 thiscontribute( isThisType() ) {…}

– …properties/methods…property name: left, type: 'v11.Direction' …method name: move, type: 'java.util.Map<…>'

93

Pointcut

Contribution block

movedeployhleftrightforwardbackward

Page 187: Groovy Domain Specific Languages - SpringOne2GX 2012

DEMOLET’S SEE THAT

94

Page 188: Groovy Domain Specific Languages - SpringOne2GX 2012

Anatomy of a DSLD script

•Pointcuts– Where to do it– What is the current expression?– Current type?– Enclosing class?

•Contribution blocks– What to do– « Add » method– « Add » property– Delegate to another type

95

Page 189: Groovy Domain Specific Languages - SpringOne2GX 2012

Anatomy of a DSLD script

•Pointcuts– Where to do it– What is the current expression?– Current type?– Enclosing class?

•Contribution blocks– What to do– « Add » method– « Add » property– Delegate to another type

95

Where

Page 190: Groovy Domain Specific Languages - SpringOne2GX 2012

Anatomy of a DSLD script

•Pointcuts– Where to do it– What is the current expression?– Current type?– Enclosing class?

•Contribution blocks– What to do– « Add » method– « Add » property– Delegate to another type

95

Where

What

Page 191: Groovy Domain Specific Languages - SpringOne2GX 2012

Anatomy of a DSLD script

•Pointcuts– Where to do it– What is the current expression?– Current type?– Enclosing class?

•Contribution blocks– What to do– « Add » method– « Add » property– Delegate to another type

95

Not at runtime... only while editing

Where

What

Page 192: Groovy Domain Specific Languages - SpringOne2GX 2012

Talking about « x »

96

class  Other  {  }

class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        }}

Page 193: Groovy Domain Specific Languages - SpringOne2GX 2012

Talking about « x »

96

class  Other  {  }

class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        }}

Current type

Page 194: Groovy Domain Specific Languages - SpringOne2GX 2012

Talking about « x »

96

class  Other  {  }

class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        }}

Current type

Enclosing class

Page 195: Groovy Domain Specific Languages - SpringOne2GX 2012

Talking about « x »

96

class  Other  {  }

class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        }}

Current type

Enclosing class

Enclosing method

Page 196: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

Page 197: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  type

Page 198: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

Page 199: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")

Page 200: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")currentType(subType("groovy.dsl.Robot"))

Page 201: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")currentType(subType("groovy.dsl.Robot"))currentType(method("move"))

Page 202: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")currentType(subType("groovy.dsl.Robot"))currentType(method("move"))currentType(annotatedBy("groovy.dsl.Robotic"))

Page 203: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")currentType(subType("groovy.dsl.Robot"))currentType(method("move"))currentType(annotatedBy("groovy.dsl.Robotic"))

//  combining  them,  and  using  the  logical  and

Page 204: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")currentType(subType("groovy.dsl.Robot"))currentType(method("move"))currentType(annotatedBy("groovy.dsl.Robotic"))

//  combining  them,  and  using  the  logical  andisScript(        annotatedBy("groovy.dsl.Robotic")

Page 205: Groovy Domain Specific Languages - SpringOne2GX 2012

Pointcuts

97

currentType()        //  matches  on  current  declaring  typeisScript()              //  matches  on  the  enclosing  script

currentType("groovy.dsl.Robot")currentType(subType("groovy.dsl.Robot"))currentType(method("move"))currentType(annotatedBy("groovy.dsl.Robotic"))

//  combining  them,  and  using  the  logical  andisScript(        annotatedBy("groovy.dsl.Robotic"))  &  currentType(method("move"))

Page 206: Groovy Domain Specific Languages - SpringOne2GX 2012

What goes in a contribution block?

98

Page 207: Groovy Domain Specific Languages - SpringOne2GX 2012

What goes in a contribution block?

• property: “adds” a property– name: “myName” (REQUIRED)– type: “java.lang.String”– declaringType: ”com.foo.Frumble”– doc: “Some JavaDoc”

98

Page 208: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 209: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 210: Groovy Domain Specific Languages - SpringOne2GX 2012

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

contribute(...)  {    property  name:  "myName"    method  name:  "getMyName"    delegatesTo  type:              "some.other.Type"}

Page 211: Groovy Domain Specific Languages - SpringOne2GX 2012

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

99

Page 212: Groovy Domain Specific Languages - SpringOne2GX 2012

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

• Pointcut– Intentionally borrowed from AOP

99

Page 213: Groovy Domain Specific Languages - SpringOne2GX 2012

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

• Pointcut– Intentionally borrowed from AOP

• AspectJ: pointcuts and advice– operates on Java instructions at runtime

99

Page 214: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 215: Groovy Domain Specific Languages - SpringOne2GX 2012

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

Page 216: Groovy Domain Specific Languages - SpringOne2GX 2012

DEMOLET’S GET THE EDITOR REALLY WORKING

100

Page 217: Groovy Domain Specific Languages - SpringOne2GX 2012

How do we ship it?

101

• jar/war file• DSLD file:

– as source in dsld package

• Hint:– Use script folder support in preferences– **/*.dsld to be copied to bin folder as source

• Can also use maven or gradle

Page 218: Groovy Domain Specific Languages - SpringOne2GX 2012

DEMOHOW DO WE SHIP IT?

102

Page 219: Groovy Domain Specific Languages - SpringOne2GX 2012

To summarize: Editing support for DSLs

• Getting it out there– include a dsld package in your JAR

– add the DSLD for your DSL to the package as source– ship it!

103

DSLDcontribution

blockspointcuts

Page 220: Groovy Domain Specific Languages - SpringOne2GX 2012

To summarize: Editing support for DSLs

• Getting it out there– include a dsld package in your JAR

– add the DSLD for your DSL to the package as source– ship it!

103

DSLDcontribution

blockspointcuts

Where

Page 221: Groovy Domain Specific Languages - SpringOne2GX 2012

To summarize: Editing support for DSLs

• Getting it out there– include a dsld package in your JAR

– add the DSLD for your DSL to the package as source– ship it!

103

DSLDcontribution

blockspointcuts

Where

What

Page 222: Groovy Domain Specific Languages - SpringOne2GX 2012

What have we learnt?

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.

Page 223: Groovy Domain Specific Languages - SpringOne2GX 2012

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

105

Page 224: Groovy Domain Specific Languages - SpringOne2GX 2012

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

105

Groovy is a great fit for

DSLs!

Page 225: Groovy Domain Specific Languages - SpringOne2GX 2012

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

106

Page 226: Groovy Domain Specific Languages - SpringOne2GX 2012

Thank you!

107

Page 227: Groovy Domain Specific Languages - SpringOne2GX 2012

Questions & Answers

108

Got questions, really?

Page 228: Groovy Domain Specific Languages - SpringOne2GX 2012

Questions & Answers

108

Got questions, really?

I might have answers!

Page 229: Groovy Domain Specific Languages - SpringOne2GX 2012

Image credits

• Wikipedia logo: http://www.geekosystem.com/wp-content/uploads/2011/01/wikipedia-logo.png• Chains: http://2.bp.blogspot.com/-GXDVqUYSCa0/TVdBsON4tdI/AAAAAAAAAW4/EgJOUmAxB28/s1600/breaking-chains5_copy9611.jpg• Space odissey: http://dearjesus.files.wordpress.com/2010/04/2001_a_space_odyssey_1.jpg• 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

109