Groovy Domain-Specific Languages
Andrew Eisenberg

Paul King

Guillaume Laforge

Groovy Eclipse Project Lead
SpringSource ...
Andrew Eisenberg
• Groovy-Eclipse project lead
• Senior Member of Technical Staff,VMware Tools Team
– Grails-IDE, AJDT, ST...
Paul King
• Groovy Core Committer
• Leads ASERT
– software, training, consultancy company
based in Brisbane, Australia

• ...
Guillaume Laforge
• Groovy Project Manager at VMware
• Initiator of the Grails framework
• Creator of the Gaelyk

• Co-aut...
Introduction
Definitions
Examples
Goals
Pros & cons
© 2012 SpringOne 2GX. All rights reserved. Do not distribute without p...
Domain-Specific Languages

{

A Domain-Specific Language is a programming
language or executable specification language th...
Technical examples
Glade

<?xml version="1.0"?>
<GTK-Interface>
<widget>
<class>GtkWindow</class>
<name>HelloWindow</name>...
Antimalaria drug
resistance simulation

Insurance policy risk
calculation engine

HR skills representation

Nuclear safety...
Goals of DSLs
• Use a more expressive language than a general-purpose one
• Share a common metaphor of understanding
betwe...
Pros and cons
Pros
– Domain experts can help,
validate, modify, and often
develop DSL programs
– Somewhat self-documenting...
Groovy provides...
• A flexible and malleable syntax
– scripts, native syntax constructs (list, map, ranges),
closures, les...
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)	
  {}
}

17
More explicit direction
package	
  mars	
  
class	
  Robot	
  {
	
  	
  	
  	
  void	
  move(Direction	
  dir)	
  {}
}
pac...
Now how can we control it?
import	
  static	
  mars.Direction.*;
import	
  mars.Robot;
public	
  class	
  Command	
  {
	
 ...
Now how can we control it?
import	
  static	
  mars.Direction.*;
import	
  mars.Robot;
public	
  class	
  Command	
  {
	
 ...
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 ...
Optional semicolons & parentheses / Scripts vs classes
import	
  static	
  mars.Direction.*
import	
  mars.Robot
Optional ...
Integration
GroovyShell to the rescue

22
GroovyShell to the rescue
def	
  shell	
  =	
  new	
  GroovyShell()
shell.evaluate(
	
  	
  	
  	
  new	
  File("command.g...
GroovyShell to the rescue

integration.groovy

22

def	
  shell	
  =	
  new	
  GroovyShell()
shell.evaluate(
	
  	
  	
  	...
GroovyShell to the rescue

integration.groovy

def	
  shell	
  =	
  new	
  GroovyShell()
shell.evaluate(
	
  	
  	
  	
  n...
GroovyShell to the rescue

integration.groovy

def	
  shell	
  =	
  new	
  GroovyShell()
shell.evaluate(
	
  	
  	
  	
  n...
Integration mechanisms
• Different solutions available:
– Groovy’s own mechanisms
• GroovyScriptEngine, GroovyShell,
Groov...
Integration mechanisms
• Different solutions available:
– Groovy’s own mechanisms
• GroovyScriptEngine, GroovyShell,
Groov...
What’s wrong with our DSL?

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

import	
  static	
  mars.Direction.*
import	
  mars.Robot
def	
  ...
What’s wrong with our DSL?
Can’t we hide
those imports?

import	
  static	
  mars.Direction.*
import	
  mars.Robot
def	
  ...
What’s wrong with our DSL?
Can’t we hide
those imports?

import	
  static	
  mars.Direction.*
import	
  mars.Robot
def	
  ...
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 variable name k...
Let’s inject a robot!
• We can pass data in / out of scripts through the Binding
– basically just a map of variable name k...
Let’s inject a robot!
• We can pass data in / out of scripts through the Binding
– basically just a map of variable name k...
Better?

import	
  static	
  mars.Direction.*

robot.move	
  left

28
Better?
Robot import
removed

import	
  static	
  mars.Direction.*

robot.move	
  left

28
Better?
Robot import
removed

import	
  static	
  mars.Direction.*

robot.move	
  left
Robot injected,
no ‘new’ needed
28
How to inject the direction?
• Using the
binding...

29

import	
  mars.*
def	
  binding	
  =	
  new	
  Binding([
	
  	
  ...
How to inject the direction?
• Using the
binding...

29

import	
  mars.*

Fragile in case of
new directions!

def	
  bind...
How to inject the direction?
• Using the
binding...

Spread map
operator

30

import	
  mars.*	
  
def	
  binding	
  =	
  ...
How to inject the direction?
• Using string concatenation?
• Using compiler customizers

31
String concatenation? Bad idea!

new	
  GroovyShell(new	
  Binding([robot:	
  new	
  mars.Robot()]))
	
  	
  	
  	
  .eval...
String concatenation? Bad idea!
Cheat with string
concatenation? Bad!

new	
  GroovyShell(new	
  Binding([robot:	
  new	
 ...
String concatenation? Bad idea!

new	
  GroovyShell(new	
  Binding([robot:	
  new	
  mars.Robot()]))
	
  	
  	
  	
  .eval...
String concatenation? Bad idea!
Line #1
becomes
Line #2
new	
  GroovyShell(new	
  Binding([robot:	
  new	
  mars.Robot()])...
String concatenation? Bad idea!

new	
  GroovyShell(new	
  Binding([robot:	
  new	
  mars.Robot()]))
	
  	
  	
  	
  .eval...
Compilation customizers
• Ability to apply some customization
to the Groovy compilation process
• Three available customiz...
Imports customizer
def	
  configuration	
  =	
  new	
  CompilerConfiguration()
	
  
def	
  imports	
  =	
  new	
  ImportCu...
AST transformation customizer
def	
  configuration	
  =	
  new	
  CompilerConfiguration()
	
  
def	
  imports	
  =	
  new	...
AST transformation customizer

35

def	
  configuration	
  =	
  new	
  CompilerConfiguration()
	
  
def	
  imports	
  =	
 ...
Secure the onboard
trajectory calculator
Secure AST customizer

• Let’s setup our environment
– an import customizer to import java.lang.Math.*
– prepare a secure ...
Secure AST customizer
Idea: secure the rocket’s onboard trajectory
calculation system by allowing only math
expressions to...
Secure AST customizer
...
secure.with	
  {
//	
  disallow	
  closure	
  creation
closuresAllowed	
  =	
  false	
  
//	
  d...
Secure AST customizer
Disallow closures
and methods

...
secure.with	
  {
//	
  disallow	
  closure	
  creation
closuresAl...
Secure AST customizer
Disallow closures
and methods

...
secure.with	
  {
//	
  disallow	
  closure	
  creation
closuresAl...
Secure AST customizer
...
//	
  language	
  tokens	
  allowed
tokensWhitelist	
  =	
  [
PLUS,	
  MINUS,	
  MULTIPLY,	
  DI...
Secure AST customizer

You can build a subset of
the Groovy syntax!

...
//	
  language	
  tokens	
  allowed
tokensWhiteli...
Secure AST customizer

You can build a subset of
the Groovy syntax!

...
//	
  language	
  tokens	
  allowed
tokensWhiteli...
Secure AST customizer
• Ready to evaluate our flight equations!
def	
  config	
  =	
  new	
  CompilerConfiguration()
config...
Back to our robot...

robot.move	
  left

41
Back to our robot...

Still need to get rid of
the robot prefix!

robot.move	
  left

41
Can we remove it?
Can we remove it?

Yes !
How to get rid of the ‘robot’?
• Instead of calling the move() method on the robot instance,
we should be able to call the...
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	
  mov...
Define a base script class
abstract	
  class	
  RobotBaseScriptClass	
  extends	
  Script	
  {
	
  	
  	
  	
  void	
  mov...
Define a base script class
abstract	
  class	
  RobotBaseScriptClass	
  extends	
  Script	
  {
	
  	
  	
  	
  void	
  mov...
Configure the base script class
def	
  conf	
  =	
  new	
  CompilerConfiguration()
conf.scriptBaseClass	
  =	
  RobotBaseS...
Configure the base script class
def	
  conf	
  =	
  new	
  CompilerConfiguration()
conf.scriptBaseClass	
  =	
  RobotBaseS...
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/h

50
What we could do now is...
Mix of named and
normal parameters

move	
  left,	
  at:	
  3.km/h

50
What we could do now is...
Mix of named and
normal parameters

move	
  left,	
  at:	
  3.km/h
How to support this
speed no...
Supporting the speed notation
• We need to:
– define units of distance, time and speed
• DistanceUnit and Distance
• TimeUn...
Distance unit enum and distance
enum	
  DistanceUnit	
  {
	
  	
  	
  	
  centimeter	
  ('cm',	
  	
  	
  	
  0.01),
	
  	...
Distance unit enum and distance
enum	
  DistanceUnit	
  {
	
  	
  	
  	
  centimeter	
  ('cm',	
  	
  	
  	
  0.01),
	
  	...
Time unit enum and duration
enum	
  TimeUnit	
  {
	
  	
  	
  	
  hour	
  	
  	
  	
  	
  	
  (	
  	
  'h',	
  3600),
	
  ...
Time unit enum and duration
enum	
  TimeUnit	
  {
	
  	
  	
  	
  hour	
  	
  	
  	
  	
  	
  (	
  	
  'h',	
  3600),
	
  ...
Now at (light!) speed

speed =

54

distance
speed

@TupleConstructor	
  
class	
  Speed	
  {
	
  	
  	
  	
  Distance	
  ...
First, we need the distance notation
• We add a dynamic property to numbers by adding a getter to
them and use the propert...
Techniques to add properties to numbers
• To add dynamic methods or properties,
there are several approaches at your dispo...
Using ExpandoMetaClass

Number.metaClass.getCm	
  =	
  {	
  -­‐>	
  
	
  	
  	
  	
  new	
  Distance(delegate,	
  Unit.cen...
Using ExpandoMetaClass
Add that to
integration.groovy
Number.metaClass.getCm	
  =	
  {	
  -­‐>	
  
	
  	
  	
  	
  new	
  ...
Using ExpandoMetaClass
Add that to
integration.groovy
Number.metaClass.getCm	
  =	
  {	
  -­‐>	
  
	
  	
  	
  	
  new	
  ...
Using ExpandoMetaClass
Add that to
integration.groovy
Number.metaClass.getCm	
  =	
  {	
  -­‐>	
  
	
  	
  	
  	
  new	
  ...
Distance okay, but speed?
• For distance, we just added a property access after the number,
but we now need to divide (‘di...
Distance okay, but speed?
• For distance, we just added a property access after the number,
but we now need to divide (‘di...
Distance okay, but speed?
• For distance, we just added a property access after the number,
but we now need to divide (‘di...
Inject the ‘h’ hour constant in the binding
def	
  binding	
  =	
  new	
  Binding([
	
  	
  	
  	
  robot:	
  new	
  Robot...
Inject the ‘h’ hour constant in the binding
def	
  binding	
  =	
  new	
  Binding([
	
  	
  	
  	
  robot:	
  new	
  Robot...
Operator overloading
a	
  +	
  b	
  	
  	
  //	
  a.plus(b)
a	
  -­‐	
  b	
  	
  	
  //	
  a.minus(b)
a	
  *	
  b	
  	
  	...
Operator overloading
• Update the Distance class with a div() method
following the naming convetion for operators
class	
 ...
Operator overloading
• Update the Distance class with a div() method
following the naming convetion for operators
class	
 ...
Equivalence of notation
• Those two notations are actually equivalent:

2.km/h

2.getKm().div(h)
62
Equivalence of notation
• Those two notations are actually equivalent:

2.km/h
This one might be
slightly more verbose!

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

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

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

63

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

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

64
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
the comma?

64

What about the
colon too?
Command chains

Groovy 1.8

• A grammar improvement allowing you
to drop dots & parens when chaining method calls
– an ext...
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	
  
and parameters
(even named ones)
66
Command chains

	
  move	
  left	
  	
  at	
  3.km/h	
  

66
Command chains

Equivalent to:

	
  	
  	
  	
  	
  (	
  	
  	
  	
  ).	
  	
  (	
  	
  	
  	
  	
  	
  )
	
  move	
  left...
Ma!
ook
L
ns,
pare
No
ots!
od
n
Command chains
//	
  Java	
  fluent	
  API	
  approach
class	
  Robot	
  {
	
  	
  	
  	
  ...
	
  	
  	
  	
  def	
  move...
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)

70
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 result

71
Final result

move	
  forward	
  at	
  3.km/h

71
move forward
at 3.km/h
move forward
at 3.km/h

Yes! We did it!
What about
security and
safety?
Security and Safety
JVM Security Managers
SecureASTCustomizer
Sandboxing
Controlling script execution
© 2012 SpringOne 2GX...
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
• Forbid things whic...
Security Managers
• Groovy is just a language leaving on the JVM,
so you have access to the usual Security Managers mechan...
SecureASTCustomizer
def	
  secure	
  =	
  new	
  SecureASTCustomizer()
secure.with	
  {
//	
  disallow	
  closure	
  creat...
Controlling code execution
• Your application may run user’s code
– what if the code runs in infinite loops or for too long...
@ThreadInterrupt
@ThreadInterrupt
import	
  groovy.transform.ThreadInterrupt	
  
	
  
while	
  (true)	
  {
	
  	
  	
  	
 ...
@ThreadInterrupt

{

@ThreadInterrupt
import	
  groovy.transform.ThreadInterrupt	
  
	
  
	
  
while	
  (true)	
  {
	
  	
...
@TimedInterrupt
@TimedInterrupt(10)
import	
  groovy.transform.TimedInterrupt	
  
	
  
while	
  (true)	
  {
	
  	
  	
  	
...
@ConditionalInterrupt
• Specify your own conditions to be inserted
at the start of method and closure bodies
– check for a...
@ConditionalInterrupt
• Specify your own conditions to be inserted
at the start of method and closure bodies
– check for a...
@ConditionalInterrupt
• Specify your own conditions to be inserted
at the start of method and closure bodies
– check for a...
Using compilation customizers
• In our previous examples, the usage of the interrupts
were explicit, and users had to type...
What about
tooling?
Tooling
Why tooling?
DSL descriptor
Pointcuts and contributions
Packaging DSLDs
© 2012 SpringOne 2GX. All rights reserved....
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 even better
–...
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!
Can we do better?
88
Of course!
• Eclipse is extensible
– with a plugin
architecture

Eclipse platform
WorkBench
JFace
SWT
Workspace
Platform r...
I want my DSL supported in Eclipse

90
I want my DSL supported in Eclipse
• Let’s create a plugin
–
–
–
–
–
–

90

create a plugin project
extend an extension po...
I want my DSL supported in Eclipse
• Let’s create a plugin
–
–
–
–
–
–

90

create a plugin project
extend an extension po...
I want my DSL supported in Eclipse
• Let’s create a plugin
–
–
–
–
–
–

90

create a plugin project
extend an extension po...
I want my DSL supported in Eclipse
• Let’s create a plugin
–
–
–
–
–
–

• Problems

create a plugin project
extend an exte...
Of course!
• Groovy is extensible!
– Meta-Object Protocol
– Metaprogramming
– DSLs...

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

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

• Benefits
–
–
–
–
92

Powerful
Uses Groovy syntax, semant...
Let’s start simple
• In English:
– When the type is this, add the following properties/methods
• move, deploy, h, etc from...
Let’s start simple
• In English:
– When the type is this, add the following properties/methods
• move, deploy, h, etc from...
Let’s start simple
• In English:
– When the type is this, add the following properties/methods
• move, deploy, h, etc from...
Let’s start simple
• In English:
– When the type is this, add the following properties/methods
• move, deploy, h, etc from...
DEMO
LET’S SEE THAT IN ACTION

94
Anatomy of a DSLD script
• Pointcuts
–
–
–
–

Where to do it
What is the current expression?
Current type?
Enclosing class...
Anatomy of a DSLD script
• Pointcuts
–
–
–
–

Where to do it
What is the current expression?
Current type?
Enclosing class...
Talking about « x »
class	
  Other	
  {	
  }
class	
  Foo	
  {
	
  	
  	
  	
  def	
  method()	
  {
	
  	
  	
  	
  	
  	
...
Talking about « x »
Current type

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

Enclosing class

96

class	
  Other	
  {	
  }
class	
  Foo	
  {
	
  	
  	
  	
  def	
  m...
Talking about « x »
Current type

Enclosing class

Enclosing method

96

class	
  Other	
  {	
  }
class	
  Foo	
  {
	
  	
...
Pointcuts
currentType()	
  	
  	
  	
  //	
  matches	
  on	
  current	
  declaring	
  type
enclosingClass()	
  //	
  match...
What goes in a contribution block?

98
What goes in a contribution block?
• property: “adds” a property
–
–
–
–

98

name: “myName” (REQUIRED)
type: “java.lang.S...
What goes in a contribution block?
• property: “adds” a property
–
–
–
–

name: “myName” (REQUIRED)
type: “java.lang.Strin...
What goes in a contribution block?
• property: “adds” a property
–
–
–
–

name: “myName” (REQUIRED)
type: “java.lang.Strin...
What goes in a contribution block?
• property: “adds” a property
–
–
–
–

name: “myName” (REQUIRED)
type: “java.lang.Strin...
Wait... isn’t this Aspect-Oriented Programming?

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

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

• AspectJ: pointcuts and adv...
Wait... isn’t this Aspect-Oriented Programming?
• Pointcut
– Intentionally borrowed from AOP

• AspectJ: pointcuts and adv...
Wait... isn’t this Aspect-Oriented Programming?
• Pointcut
– Intentionally borrowed from AOP

• AspectJ: pointcuts and adv...
DEMO
LET’S GET THE EDITOR REALLY WORKING

100
DEMO
HOW DO WE SHIP IT?

101
To summarize

DSLD
pointcuts

contribution
blocks

• Getting it out there
– include a dsld package in your JAR
– add the D...
What have we learnt?

© 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
Groovy Power!™
• A flexible and malleable syntax
– scripts vs classes, optional typing, colons and parens

• Groovy offers ...
Groovy Power!™
• A flexible and malleable syntax

Groovy is a
great fit for
DSLs!

– scripts vs classes, optional typing, c...
And there’s more!
• We haven’t dived into...
– How to implement your own control structures with closures
– How to create ...
Thank you!

106
Questions & Answers

Got questions,
really?

107
Questions & Answers

Got questions,
really?

107

I might have
answers!
Image credits
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•

108

Wikipedia logo: http://www.geekosystem.com/wp-content/...
Groovy ds ls_spring_one2gx_2012
Upcoming SlideShare
Loading in …5
×

Groovy ds ls_spring_one2gx_2012

383 views

Published on

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

  • Be the first to like this

No Downloads
Views
Total views
383
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
11
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Groovy ds ls_spring_one2gx_2012

  1. 1. Groovy Domain-Specific Languages Andrew Eisenberg Paul King Guillaume Laforge Groovy Eclipse Project Lead SpringSource / VMware Groovy Core Developer ASERT Groovy Project Manager SpringSource / VMware @werdnagreb @paulk_asert @glaforge © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  2. 2. Andrew Eisenberg • Groovy-Eclipse project lead • Senior Member of Technical Staff,VMware Tools Team – Grails-IDE, AJDT, STS, GGTS, Scripted, Orion project • PhD in Computer Science from University of British Columbia • Follow me: – Twitter: @werdnagreb – Blog: http://contraptionsforprogramming.blogspot.ca/ – Google+: http://gplus.to/aeisenberg 2
  3. 3. Paul King • Groovy Core Committer • Leads ASERT – software, training, consultancy company based in Brisbane, Australia • Co-author of Groovy in Action • Follow me: – Twitter: @paulk_asert 3
  4. 4. Guillaume Laforge • Groovy Project Manager at VMware • Initiator of the Grails framework • Creator of the Gaelyk • Co-author of Groovy in Action • Follow me: • My blog: http://glaforge.appspot.com • Twitter: @glaforge • Google+: http://gplus.to/glaforge 4
  5. 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 Purprose Languages • Also known as: fluent / humane interfaces, language oriented programming, little or mini languages, macros, business natural languages... 6 }
  7. 7. Technical examples Glade <?xml version="1.0"?> <GTK-Interface> <widget> <class>GtkWindow</class> <name>HelloWindow</name> <border_width>5</border_width> <Signal> <name>destroy</name> <handler>gtk_main_quit</handler> </Signal> <title>Hello</title> <type>GTK_WINDOW_TOPLEVEL</type> <position>GTK_WIN_POS_NONE</position> <allow_shrink>True</allow_shrink> <allow_grow>True</allow_grow> <auto_shrink>False</auto_shrink> <widget> <class>GtkButton</class> <name>Hello World</name> <can_focus>True</can_focus> <label>Hello World</label> </widget> </widget> </GTK-Interface> XSLT <?xml version="1.0"?> <xsl:stylesheetversion="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="*"> <xsl:element name="{name()}"> <xsl:for-each select="@*"> <xsl:element name="{name()}"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> <xsl:apply-templates select="*|text()"/> </xsl:element> </xsl:template> </xsl:stylesheet> SQL SELECT * FROM TABLE WHERE NAME LIKE '%SMI' ORDER BY NAME Regex "x.z?z{1,3}y" Fetchmail # Poll this site first each cycle. poll pop.provider.net proto pop3 user "jsmith" with pass "secret1" is "smith" here user jones with pass "secret2" is "jjones" here with options keep # Poll this site second, unless Lord Voldemort zaps us first. poll billywig.hogwarts.com with proto imap: user harry_potter with pass "floo" is harry_potter here # Poll this site third in the cycle. # Password will be fetched from ~/.netrc poll mailhost.net with proto imap: user esr is esr here
  8. 8. Antimalaria drug resistance simulation Insurance policy risk calculation engine HR skills representation Nuclear safety simulations Market data feeds analysis Loan acceptance rules engine
  9. 9. Goals of DSLs • Use a more expressive language than a general-purpose one • Share a common metaphor of understanding between developers and subject matter experts • Have domain experts help with the design of the business logic of an application • Avoid cluttering business code with too much boilerplate technical code thanks to a clean separation • Let business rules have their own lifecycle 9
  10. 10. Pros and cons Pros – Domain experts can help, validate, modify, and often develop DSL programs – Somewhat self-documenting – Enhance quality, productivity, reliability, maintainability, portability, reusability – Safety; as long as the language constructs are safe, any DSL sentence can be considered safe 10 Cons – Learning cost vs. limited applicability – Cost of designing, implementing & maintaining DSLs as well as tools/ IDEs – Attaining proper scope – Trade-offs between domain specificity and general purpose language constructs – Efficiency cost – Proliferation of similar non-standard DSLs
  11. 11. Groovy provides... • A flexible and malleable syntax – scripts, native syntax constructs (list, map, ranges), closures, less punctuation... • Compile-time and runtime meta-programming – metaclasses, AST transformations – also operator overloading • The ability to easily integrate into Java / Spring apps – also security and safety 11
  12. 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  left 20
  22. 22. Optional semicolons & parentheses / Scripts vs classes import  static  mars.Direction.* import  mars.Robot Optional typing                def      robot  =  new  Robot()                robot.move  left 20
  23. 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 rescue 22
  26. 26. GroovyShell to the rescue def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) 22
  27. 27. GroovyShell to the rescue integration.groovy 22 def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") )
  28. 28. GroovyShell to the rescue integration.groovy def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left 22
  29. 29. GroovyShell to the rescue integration.groovy def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) import  static  mars.Direction.* import  mars.Robot def  robot  =  new  Robot() robot.move  left 22 command.groovy
  30. 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  left 24
  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  left 24
  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 Do we really need to repeat ‘robot’? 24 Can’t we inject the robot?
  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 values 27
  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  left 28
  43. 43. Better? Robot import removed import  static  mars.Direction.* robot.move  left 28
  44. 44. Better? Robot import removed import  static  mars.Direction.* robot.move  left Robot injected, no ‘new’ needed 28
  45. 45. How to inject the direction? • Using the binding... 29 import  mars.* def  binding  =  new  Binding([        robot:  new  Robot(),        left:          Direction.left,        right:        Direction.right,        backward:  Direction.backward,        forward:    Direction.forward ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )
  46. 46. How to inject the direction? • Using the binding... 29 import  mars.* Fragile in case of new directions! def  binding  =  new  Binding([        robot:  new  Robot(),        left:          Direction.left,        right:        Direction.right,        backward:  Direction.backward,        forward:    Direction.forward ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )
  47. 47. How to inject the direction? • Using the binding... Spread map operator 30 import  mars.*   def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        } ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )
  48. 48. How to inject the direction? • Using string concatenation? • Using compiler customizers 31
  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 own 33
  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 35 def  configuration  =  new  CompilerConfiguration()   def  imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports,                          new  ASTTransformationCustomizer(Log))   new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                    configuration)        .evaluate("robot.move  left"  +  "n"                            "log.info  ‘Robot  moved’")                       @Log injects a logger in scripts and classes
  58. 58. Secure the onboard trajectory calculator
  59. 59. Secure AST customizer • Let’s setup our environment – an import customizer to import java.lang.Math.* – prepare a secure AST customizer def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer() 37
  60. 60. Secure AST customizer Idea: secure the rocket’s onboard trajectory calculation system by allowing only math expressions to be evaluated by the calculator • Let’s setup our environment – an import customizer to import java.lang.Math.* – prepare a secure AST customizer def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer() 37
  61. 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  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ... 39 Black / white list usage of classes
  67. 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  left 41
  69. 69. Back to our robot... Still need to get rid of the robot prefix! robot.move  left 41
  70. 70. Can we remove it?
  71. 71. Can we remove it? Yes !
  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 the binding with a method pointer 43 • Use a base script class with a ‘move’ method delegating to the robot
  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 method 44
  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 level 45
  77. 77. Define a base script class abstract  class  RobotBaseScriptClass  extends  Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } The move() method is now at the script level 45 Access the robot through the script’s binding
  78. 78. Configure the base script class def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass 46
  79. 79. Configure the base script class def  conf  =  new  CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass Scripts evaluated with this configuration will inherit from that class 46
  80. 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/h 50
  84. 84. What we could do now is... Mix of named and normal parameters move  left,  at:  3.km/h 50
  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 overloading 51
  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  {        centimeter  ('cm',        0.01),        meter            (  'm',        1      ),          kilometer    ('km',  1000      )                  String  abbreviation        double  multiplier                DistanceUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   } 52 @TupleConstructor   class  Distance  {        double  amount          DistanceUnit  unit        String  toString()  {                  "$amount  $unit"          }   }
  89. 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  {        hour            (    'h',  3600),        minute        ('min',      60),          second        (    's',        1)                  String  abbreviation        double  multiplier                TimeUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   } 53 @TupleConstructor   class  Duration  {        double  amount          TimeUnit  unit        String  toString()  {                  "$amount  $unit"          }   }
  91. 91. Now at (light!) speed speed = 54 distance speed @TupleConstructor   class  Speed  {        Distance  distance        Duration  dur        String  toString()  {                  "$distance/$dur"          }   }
  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 ExpandoMetaClass 56
  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 number 57
  97. 97. Using ExpandoMetaClass Add that to integration.groovy Number.metaClass.getCm  =  {  -­‐>          new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } ‘delegate’ is the current number 57 Usage in your DSLs 40.cm   3.5.m 4.km
  98. 98. Distance okay, but speed? • For distance, we just added a property access after the number, but we now need to divide (‘div’) by the time 2.km/h 58
  99. 99. Distance okay, but speed? • For distance, we just added a property access after the number, but we now need to divide (‘div’) by the time The div() method on Distance 2.km/h 58
  100. 100. Distance okay, but speed? • For distance, we just added a property access after the number, but we now need to divide (‘div’) by the time The div() method on Distance 2.km/h An ‘h’ duration instance in the binding 58
  101. 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) a  *  b      //  a.multiply(b) a  /  b      //  a.div(b) a  %  b      //  a.modulo(b) a  **  b    //  a.power(b) a  |  b      //  a.or(b) a  &  b      //  a.and(b) a  ^  b      //  a.xor(b) a[b]        //  a.getAt(b) a  <<  b    //  a.leftShift(b) a  >>  b    //  a.rightShift(b) a  >>>  b  //  a.rightShiftUnsigned(b) +a            //  a.unaryPlus() -­‐a            //  a.unaryMinus() ~a            //  a.bitwiseNegate() 60 • Currency amounts – 15.euros + 10.dollars • Distance handling – 10.km - 10.m • Workflow, concurrency – taskA | taskB & taskC • Credit an account – account << 10.dollars account += 10.dollars account.credit 10.dollars
  104. 104. Operator overloading • Update the Distance class with a div() method following the naming convetion for operators class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } 61
  105. 105. Operator overloading • Update the Distance class with a div() method following the naming convetion for operators class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } Optional return 61
  106. 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/h 63
  109. 109. Named parameters usage move  left,  at:  3.km/h Normal parameter 63
  110. 110. Named parameters usage move  left,  at:  3.km/h Normal parameter 63 Named parameter
  111. 111. Named parameters usage move  left,  at:  3.km/h Normal parameter Will call: def  move(Map  m,  Direction  q) 63 Named parameter
  112. 112. Named parameters usage move  left,  at:  3.km/h Normal parameter Will call: def  move(Map  m,  Direction  q) All named parameters go into the map argument 63 Named parameter
  113. 113. Named parameters usage move  left,  at:  3.km/h Named parameter Normal parameter Will call: def  move(Map  m,  Direction  q) All named parameters go into the map argument 63 Positional parameters come afterwards
  114. 114. Named parameters usage move  left,  at:  3.km/h 64
  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 the comma? 64 What about the colon too?
  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. Ma! ook L ns, pare No ots! od n
  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  -­‐>                ...        }] } Usage in your DSLs Nested maps and closures 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 result 71
  145. 145. Final result move  forward  at  3.km/h 71
  146. 146. move forward at 3.km/h
  147. 147. move forward at 3.km/h Yes! We did it!
  148. 148. What about security 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 transformations 76
  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 files 77
  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 code 79
  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 desired 81
  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 100.times  {                move  forward  at  10.km/h } 82 Can we avoid typing the conditional interrupt?
  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 100.times  {                move  forward  at  10.km/h } 83 Groovy 1.8 Yes! Using compilation customizers
  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 Customizer 84
  162. 162. What about tooling?
  163. 163. Tooling Why tooling? DSL descriptor 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 documentation 87
  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 WorkBench JFace SWT Workspace Platform runtime 89 Help Team New plugin New tool
  171. 171. I want my DSL supported in Eclipse 90
  172. 172. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – 90 create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it
  173. 173. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – 90 create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version
  174. 174. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – 90 create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it Uh oh! • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version
  175. 175. I want my DSL supported in Eclipse • Let’s create a plugin – – – – – – • Problems create a plugin project extend an extension point write the code build the plugin host on an update site convince people to install it – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version Can we do better? 90 Uh oh!
  176. 176. Of course! • Groovy is extensible! – Meta-Object Protocol – Metaprogramming – DSLs... 91
  177. 177. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL 92
  178. 178. DSL Descriptors • Teach the IDE about DSLs through a Groovy DSL • Benefits – – – – 92 Powerful Uses Groovy syntax, semantics, and APIs No knowledge of Eclipse required Can ship with Groovy libraries
  179. 179. Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer 93
  180. 180. Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 93
  181. 181. Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: Pointcut – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 93
  182. 182. Let’s start simple • In English: – When the type is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… Pointcut Contribution block property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 93
  183. 183. DEMO LET’S SEE THAT IN ACTION 94
  184. 184. Anatomy of a DSLD script • Pointcuts – – – – Where to do it What is the current expression? Current type? Enclosing class? • Contribution blocks – – – – 95 What to do « Add » method « Add » property Delegate to another type
  185. 185. Anatomy of a DSLD script • Pointcuts – – – – Where to do it What is the current expression? Current type? Enclosing class? • Contribution blocks – – – – 95 What to do « Add » method « Add » property Delegate to another type Not at runtime... only while editing
  186. 186. Talking about « x » class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } 96
  187. 187. Talking about « x » Current type class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } 96
  188. 188. Talking about « x » Current type Enclosing class 96 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }
  189. 189. Talking about « x » Current type Enclosing class Enclosing method 96 class  Other  {  } class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }
  190. 190. Pointcuts currentType()        //  matches  on  current  declaring  type enclosingClass()  //  matches  on  the  enclosing  class currentType("com.bar.Foo") currentType(subType("com.bar.Foo") currentType(method("run")) annotatedBy("org.junit.runner.RunWith") //  combining  them,  and  using  the  logical  and enclosingClass(        annotatedBy("org.junit.runner.RunWith") )  &  currentType(method("run")) 97
  191. 191. What goes in a contribution block? 98
  192. 192. What goes in a contribution block? • property: “adds” a property – – – – 98 name: “myName” (REQUIRED) type: “java.lang.String” declaringType: ”com.foo.Frumble” doc: “Some JavaDoc”
  193. 193. What goes in a contribution block? • property: “adds” a property – – – – name: “myName” (REQUIRED) type: “java.lang.String” declaringType: ”com.foo.Frumble” doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams 98
  194. 194. What goes in a contribution block? • property: “adds” a property – – – – name: “myName” (REQUIRED) type: “java.lang.String” declaringType: ”com.foo.Frumble” doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 98
  195. 195. What goes in a contribution block? • property: “adds” a property – – – – name: “myName” (REQUIRED) type: “java.lang.String” declaringType: ”com.foo.Frumble” doc: “Some JavaDoc” • method: “adds” a method contribute(...)  {    property  name:  "myName"    method  name:  "getMyName"    delegatesTo  type:              "some.other.Type" } – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 98
  196. 196. Wait... isn’t this Aspect-Oriented Programming? 99
  197. 197. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP 99
  198. 198. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime 99
  199. 199. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime • DSLD: pointcuts and contribution blocks – operates on AST in the editor org.codehaus.groovy.ast.expr.* 99
  200. 200. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime • DSLD: pointcuts and contribution blocks – operates on AST in the editor org.codehaus.groovy.ast.expr.* • Join Point Model – Join points (e.g., instructions, expressions) – Mechanism for quantifying join points (e.g., pointcuts) – Means of affect at a join point (e.g., advice, contribution blocks) 99
  201. 201. DEMO LET’S GET THE EDITOR REALLY WORKING 100
  202. 202. DEMO HOW DO WE SHIP IT? 101
  203. 203. To summarize DSLD pointcuts contribution blocks • Getting it out there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 102
  204. 204. What have we learnt? © 2012 SpringOne 2GX. All rights reserved. Do not distribute without permission.
  205. 205. Groovy Power!™ • A flexible and malleable syntax – scripts vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs – operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences – for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox • Groovy DSLs can be tooled for improved authoring capabilities 104
  206. 206. Groovy Power!™ • A flexible and malleable syntax Groovy is a great fit for DSLs! – scripts vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs – operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences – for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox • Groovy DSLs can be tooled for improved authoring capabilities 104
  207. 207. And there’s more! • We haven’t dived into... – How to implement your own control structures with closures – How to create Groovy « builders » – How to define extension modules – How to hijack the Groovy syntax to develop our own language extensions with AST Transformations – Source preprocessing for custom syntax – How to use the other metaprogramming techniques available – How to improve error reporting with customizers 105
  208. 208. Thank you! 106
  209. 209. Questions & Answers Got questions, really? 107
  210. 210. Questions & Answers Got questions, really? 107 I might have answers!
  211. 211. Image credits • • • • • • • • • • • • • • • • • • • • • • • • 108 Wikipedia logo: http://www.geekosystem.com/wp-content/uploads/2011/01/wikipedia-logo.png Chains: http://2.bp.blogspot.com/-GXDVqUYSCa0/TVdBsON4tdI/AAAAAAAAAW4/EgJOUmAxB28/s1600/breaking-chains5_copy9611.jpg Space odissey: http://dearjesus.files.wordpress.com/2010/04/2001_a_space_odyssey_1.jpg HAL red: http://2.bp.blogspot.com/-yjsyPxUFicY/TcazwAltOaI/AAAAAAAAAho/GVT7wGhnrUM/s1600/2001-a-space-odyssey-HAL.jpg USSR Space posters: http://www.flickr.com/photos/justinvg General: http://www.defense.gov/dodcmsshare/newsphoto/2009-02/hires_090217-D-7203C-004.jpg Rocket: http://astro.vision.free.fr/download/fonds/7/saturn5c.jpg Star Trek / 747: http://24.media.tumblr.com/tumblr_m35foiJI6a1qzz0iho1_1280.jpg Man in space: http://www.nasa.gov/images/content/60130main_image_feature_182_jwfull.jpg Sputnik 2: http://launiusr.files.wordpress.com/2010/06/sputnik2.jpg Lunakod: http://www.astr.ua.edu/keel/space/lunakhod_moscow.jpg Sandbox: http://www.turnbacktogod.com/wp-content/uploads/2008/09/sandbox.jpg Repair: http://www.oneangels.com/wp-content/uploads/2012/03/repair1.jpg Mars rover: http://wallpapers.free-review.net/wallpapers/49/Mars_rover%2C_Mars_-_03.jpg Mars rover 2: http://www.universetoday.com/wp-content/uploads/2011/06/551038main_pia14156-43_946-710.jpg Thumb: http://www.wpclipart.com/sign_language/thumbs_up_large.png.html Night sky: http://www.aplf-planetariums.info/galeries/ciel_profond/2004-07-01-Voie_Lactee_Scorpion-Jean-Luc_PUGLIESI.jpg Obama yes we can: http://www.dessinemoiunboulon.net/wp-content/uploads/2009/01/obama-yes-we-can_04-nov-08.jpg Hook: http://winningware.com/blog/wp-content/uploads/2009/12/FishHookXSmall.jpg HP 48 GX: http://calculators.torensma.net/files/images/hewlett-packard_hp-48g.jpg Omer: http://www.irmin.com/wallpaper/TV/Homer%20Simpson%20Oh%20No.jpg Cadenat: http://acsgsecurite.com/upl/site/cadenat.png Thanks: http://4.bp.blogspot.com/-hTdT5Ebk5ws/Tu_x2tE4ccI/AAAAAAAAAZc/pxtG8A0w7VE/s1600/thanks-digital-calligraphy-md.png Buzz Aldrin: http://2.bp.blogspot.com/-rpV5Oy5N78U/TprVli-2ZdI/AAAAAAAABN8/WiHob4rp2b8/s1600/Astronaut.jpg

×