Designing your ownDomain-Specific Languagewith Groovy  Guillaume Laforge  Groovy Project Manager  SpringSource / VMware
Guillaume Laforge• Groovy Project Manager• JSR-241 Spec Lead• Head of Groovy Development  at SpringSource / VMWare• Initia...
Guillaume Laforge• Groovy Project Manager• JSR-241 Spec Lead• Head of Groovy Development  at SpringSource / VMWare• Initia...
Domain-Specific Languagesare everywhere!     Technical dialects          Notations   Real-life Groovy DSLs
SQL
^[w-.]+@([w-]){2,4}$
1. e4 e52. Nf3 Nc6 3. Bb5 a6
L2   U   F -1   B   L2   F   B -1   U   L2
Music sheets:      Visual!
Dancenotation:  Visual!
Real-life Groovy examples•   Anti-malaria drug resistance simulation•   Human Resources employee skills representation•   ...
Setting the stage...
The Context
Subject Matter Expers, Business Analysts...
Developer producing LOLCODEHAICAN HAS STDIO?I HAS A VARIM IN YR LOOP   UP VAR!!1   VISIBLE VAR   IZ VAR BIGGER THAN 10?KTH...
Lots of Languages...
And in the end...nobody understands        each other
Expressing Requirements...                             17
DSL: a potential solution?• Use a more expressive language  than a general purpose one• Share a common metaphore of unders...
Towards more readibility                           19
Towards more readibility                           19
Towards more readibility                           20%                                 19
Towards more readibility                           20
Towards more readibility                           20
Towards more readibility                           80%                                 20
Three levels of techniques   Flexible &malleable syntax              Meta-             Hooking into                       ...
Level #1:A flexible and malleable syntax
Scripts vs classes• Hide all the boilerplate technical code  – an end-user doesn’t need to know about classes             ...
Scripts vs classes• Hide all the boilerplate technical code  – an end-user doesn’t need to know about classes    public
cl...
Scripts vs classes• Hide all the boilerplate technical code  – an end-user doesn’t need to know about classes    public
cl...
Optional typing• No need to bother with types or even generics  – unless you want to!  – but strongly typed if you so desi...
Optional typing• No need to bother with types or even generics   – unless you want to!   – but strongly typed if you so de...
Optional typing• No need to bother with types or even generics   – unless you want to!   – but strongly typed if you so de...
Optional typing• No need to bother with types or even generics   – unless you want to!   – but strongly typed if you so de...
Native syntax constructs                           25
Native syntax constructs//
Lists                           25
Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]                                          25
Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Maps                                          25
Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]    ...
Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]//
R...
Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]//
R...
Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]//
R...
Optional parens & semis• Make statements and expressions  look more like natural languages                                ...
Optional parens & semis• Make statements and expressions  look more like natural languages          move(left);           ...
Optional parens & semis• Make statements and expressions  look more like natural languages          move(left);          m...
Named arguments• In Groovy you can mix named and unnamed arguments for  method parameters  – named params are actually put...
Command chains expressions                             28
Command chains expressions• A grammar improvement allowing you to  drop dots & parens when chaining method calls  – an ext...
Command chains expressions• A grammar improvement allowing you to  drop dots & parens when chaining method calls  – an ext...
Command chains expressions• A grammar improvement allowing you to  drop dots & parens when chaining method calls  – an ext...
Command chains expressions• A grammar improvement allowing you to  drop dots & parens when chaining method calls  – an ext...
Command chains expressions• A grammar improvement allowing you to  drop dots & parens when chaining method calls  – an ext...
Command chains expressions      
turn
left

then
right
                                29
Command chains expressions      Alternation of      method names      
turn
left

then
right
                             ...
Command chains expressions      Alternation of      method names      
turn
left

then
right
                         and ...
Command chains expressions      
turn
left

then
right
                                29
Command chains expressions       Equivalent to:      




(



).



(




)      
turn
left

then
right
                 ...
LookM  a!N op are ns,no do ts!
Command chains expressions take
2.pills

of

chloroquinine

after

6.hours                                              31
Command chains expressions 



(






).


(












).





(






) take
2.pills

of

chloroquinine

after

6.hour...
Command chains expressions      //
environment
initialization      Integer.metaClass.getPills
{
‐>
delegate
}      Integer...
Command chains expressions take
2
.pills
of

chloroquinine
after

6.hours                                              33
Command chains expressions take
2
.pills
of

chloroquinine
after

6.hours                ... some dots remain ...         ...
Command chains expressions take
2

pills
of

chloroquinine
after

6
hours                                              34
Command chains expressions 



(
).




(

).












(




).
(




) take
2

pills
of

chloroquinine
after

6
hours ...
Command chains expressions      //
variable
injection      def
(of,
after,
hours)
=
/*...*/      
      //
implementing
th...
Command chains expressions                             36
Command chains expressions //
methods
with
multiple
arguments
(commas)                                               36
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor          ...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
levera...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(



...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(



...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(



...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(



...
Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(



...
BigDecimal by default• Main reason why financial institutions often decide  to use Groovy for their business rules!  – Alt...
BigDecimal by default• Main reason why financial institutions often decide  to use Groovy for their business rules!  – Alt...
BigDecimal by default• Main reason why financial institutions often decide  to use Groovy for their business rules!  – Alt...
Custom control structures w/ closures• When closures are last, they can be put “out” of the  parentheses surrounding param...
Custom control structures w/ closures• When closures are last, they can be put “out” of the  parentheses surrounding param...
Custom control structures w/ closures• When closures are last, they can be put “out” of the  parentheses surrounding param...
Operator overloadinga
+
b

//
a.plus(b)           • Currency amountsa
‐
b

//
a.minus(b)a
*
b

//
a.multiply(b)         – ...
Level #2:Dynamic metaprogramming
The MOP(Meta-Object Protocol)
Groovy’s MOP• All the accesses to methods, properties, constructors,  operators, etc. can be intercepted thanks to the MOP...
Hooks• GroovyObject(Support)  – invokeMethod(), get/setProperty(), get/setMetaClass()  – methodMissing(), propertyMissing(...
Hooks: ExpandoMetaClass• A DSL for MetaClasses!         MoneyAmount.metaClass.constructor
=
{
...
}         Number.metaCla...
Adding properties to numbers• Three possible approaches  – create a Category    • a category is a kind of decorator for de...
With a Category     class
DistanceCategory
{     



static
Distance
getMeters(Integer
self)
{     







new
Distance(se...
With an ExpandoMetaClass   Number.metaClass.getMeters
=
{‐>
   



new
Distance(delegate,
Unit.METERS)
   }   
   100.mete...
The Builder pattern
A builder for HR softskills
{ 



ideas
{ 







capture
2 







formulate
3
 



} 



... } knowhow
{ 



languages
{...
A builder for HR softskills
{ 



ideas
{ 







capture
2 







formulate
3
 



} 



... } knowhow
{ 



languages
{...
A builder for HR 









( softskills
{ 








( 



ideas
{ 














(
) 







capture
2 
















(
)...
Level #3:Hooking into the compiler
Compile-time metaprogramming• Groovy 1.6 introduced AST Transformations• Compile-time == No runtime performance penalty!  ...
Compile-time metaprogramming• With metaprogramming, Groovy’s able to modify the  behaviour of programs... at runtime• Groo...
AST Transformations• Two kinds of transformations  – Global transformations    • applicable to all compilation units  – Lo...
AST Transformations• Several (local) transformations available  – @ToString  – @Log  – @Delegate  – @Immutable  – and many...
@Immutable• To properly implement immutable classes  – No mutators (state musn’t change)  – Private final fields  – Defens...
Global transformations• Implement ASTTransformation• Annotate the transfo specifying a compilation phase  @GroovyASTTransf...
Local transformations• Same approach as Globale transformations• But you don’t need the META-INF file• Instead create an a...
Example: the Spock framework• Changing the semantics of the original code• But keeping a valid Groovy syntax         @Spec...
Integration mechanisms
Various integration mechanisms• Java 6’s javax.script.* APIs (aka JSR-223)• Spring’s language namespace• Groovy’s own mech...
Java 6’s javax.script.* API• Groovy provides an implementation of the javax.script.* API  ScriptEngineManager
mgr
=
new
Sc...
Spring’s lang namespace• POGOs (Plain Old Groovy Objects) can be pre-compiled as  any POJO and used interchangeably with P...
Groovy’s own mechanisms• GroovyShell, Eval, GroovyScriptEngine  – for more complex scripts and DSLs• GroovyClassLoader or ...
GroovyShell• A Binding provides a context of execution  – can implement lazy evaluation if needed• A base script class can...
Imports customizer• Inject imports in your scripts  – so that users don’t have to add imports manually def
configuration
=...
Applying an AST transformation• Apply local AST transformations transparently      def
configuration
=
new
CompilerConfigu...
Externalize business rules• Although Groovy DSLs can be embedded in normal Groovy  classes, you should externalize them• S...
Domain-Specific Language Descriptors• DSLs are nice, but what about support in my IDE?• DSLD to the rescue  – Domain-Speci...
Domain-Specific Language Descriptors                                       69
Domain-Specific Language Descriptors                                       69
Domain-Specific Language DescriptorscurrentType(
subType(
Number
)
).accept
{



[m:
"meter",
yd:
"yard",
cm:
"centimeter"...
A few considerations    Growing a language         Security        Testability
Start small, with key concepts  Beware over-engineering!
Grow your language      progressively
Get your hands dirtyPlay with the end-users
Let your DSL fly,it’s not yours, it’s theirs!
Tight feedback loop Iterative process
Stay humble,You can’t get it right the 1st time.  Don’t design alone at your deskInvolve the end users from the start
Playing it safe in a sandbox
Example use case• Biggest European travel services company• Their customers  – travel agencies, airlines, booking websites...
Sandboxing approaches• Groovy supports the usual  Java Security Managers  – avoid System.exit(0)• Use metaprogramming tric...
Secure AST customizer        Idea: Implement an «arithmetic shell»          Being able control what a user script is      ...
Secure AST customizer             Idea: Implement an «arithmetic shell»               Being able control what a user scrip...
Secure AST customizer             Idea: Implement an «arithmetic shell»               Being able control what a user scrip...
Secure AST customizer ... secure.with
{   //
disallow
closure
creation   closuresAllowed
=
false
   //
disallow
method
def...
Secure AST customizer...   //
language
tokens
allowed   tokensWhitelist
=
[      PLUS,
MINUS,
MULTIPLY,
DIVIDE,
MOD,
POWER...
Secure AST customizer• Ready to evaluate our arithmetic expressions!    ...    def
config
=
new
CompilerConfiguration()   ...
Test, test, test!• Don’t just test for nominal cases  – Explicitely test for errors!• Ensure end-users get meaninful error...
Summary
Summary• Groovy DSLs in the wild manage millions,  and even billions of Euros (one of my customers)  – so I guess we can s...
Thank you!                                 e                            aforg pment                    ume L Develo       ...
Upcoming SlideShare
Loading in...5
×

Groovy DSLs - S2GForum London 2011 - Guillaume Laforge

2,894

Published on

Design Your Own Domain Specific Language

This talk examines how dynamic languages in general and Groovy in particular provide toos to help design programming languages that are closer of the natural language of the target subject matter expert. It offers many features that allow you to create embedded DSLs: Closures, compile-time and run-time metaprogramming, operator overloading, named arguments, a more concise and expressive syntax and more.

Published in: Technology
1 Comment
10 Likes
Statistics
Notes
No Downloads
Views
Total Views
2,894
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
95
Comments
1
Likes
10
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Groovy DSLs - S2GForum London 2011 - Guillaume Laforge

    1. 1. Designing your ownDomain-Specific Languagewith Groovy Guillaume Laforge Groovy Project Manager SpringSource / VMware
    2. 2. Guillaume Laforge• Groovy Project Manager• JSR-241 Spec Lead• Head of Groovy Development at SpringSource / VMWare• Initiator of the Grails framework• Creator of the Gaelyk toolkit• Co-author of Groovy in Action• Speaker: JavaOne, QCon, JavaZone, Sun TechDays, Devoxx, The Spring Experience, SpringOne, JAX, Dynamic Language World, IJTC, and more... 2
    3. 3. Guillaume Laforge• Groovy Project Manager• JSR-241 Spec Lead• Head of Groovy Development at SpringSource / VMWare• Initiator of the Grails framework• Creator of the Gaelyk toolkit• Co-author of Groovy in Action• Speaker: JavaOne, QCon, JavaZone, Sun TechDays, Devoxx, The Spring Experience, SpringOne, JAX, Dynamic Language World, IJTC, and more... 2
    4. 4. Domain-Specific Languagesare everywhere! Technical dialects Notations Real-life Groovy DSLs
    5. 5. SQL
    6. 6. ^[w-.]+@([w-]){2,4}$
    7. 7. 1. e4 e52. Nf3 Nc6 3. Bb5 a6
    8. 8. L2 U F -1 B L2 F B -1 U L2
    9. 9. Music sheets: Visual!
    10. 10. Dancenotation: Visual!
    11. 11. Real-life Groovy examples• Anti-malaria drug resistance simulation• Human Resources employee skills representation• Insurance policies risk calculation engine• Loan acceptance rules engine for a financial platform• Mathematica-like lingua for nuclear safety simulations• Market data feeds evolution scenarios• and many more... 10
    12. 12. Setting the stage...
    13. 13. The Context
    14. 14. Subject Matter Expers, Business Analysts...
    15. 15. Developer producing LOLCODEHAICAN HAS STDIO?I HAS A VARIM IN YR LOOP UP VAR!!1 VISIBLE VAR IZ VAR BIGGER THAN 10?KTHXBYEIM OUTTA YR LOOP
    16. 16. Lots of Languages...
    17. 17. And in the end...nobody understands each other
    18. 18. Expressing Requirements... 17
    19. 19. DSL: a potential solution?• Use a more expressive language than a general purpose one• Share a common metaphore 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• Cleanly separate business logic from application code• Let business rules have their own lifecycle 18
    20. 20. Towards more readibility 19
    21. 21. Towards more readibility 19
    22. 22. Towards more readibility 20% 19
    23. 23. Towards more readibility 20
    24. 24. Towards more readibility 20
    25. 25. Towards more readibility 80% 20
    26. 26. Three levels of techniques Flexible &malleable syntax Meta- Hooking into programming the compiler• scripts• optional typing • POGO • AST traversal• native syntax • categories • local & globalconstructs • builders transformations • custom MetaClass • hooks into Antlr• parens / semi • ExpandoMetaClassommission• command chains• named arguments• BigDecimal• operator overloading• closures 21
    27. 27. Level #1:A flexible and malleable syntax
    28. 28. Scripts vs classes• Hide all the boilerplate technical code – an end-user doesn’t need to know about classes 23
    29. 29. Scripts vs classes• Hide all the boilerplate technical code – an end-user doesn’t need to know about classes public
class
Rule
{ 



public
static
void
main(String[]
args)
{ 







System.out.println("Hello"); 



} } 23
    30. 30. Scripts vs classes• Hide all the boilerplate technical code – an end-user doesn’t need to know about classes public
class
Rule
{ 



public
static
void
main(String[]
args)
{ 







System.out.println("Hello"); 



} } println
"Hello" 23
    31. 31. Optional typing• No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire 24
    32. 32. Optional typing• No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire//
given
this
API
methodpublic
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
} 24
    33. 33. Optional typing• No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire//
given
this
API
methodpublic
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
} //
verbose
Java
notation Rate<LoanType,
Duration,
BigDecimal>[]
=
lookupTable(); 24
    34. 34. Optional typing• No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire//
given
this
API
methodpublic
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
} //
verbose
Java
notation Rate<LoanType,
Duration,
BigDecimal>[]
=
lookupTable(); //
leaner
Groovy
variant def
table
=
lookupTable() 24
    35. 35. Native syntax constructs 25
    36. 36. Native syntax constructs//
Lists 25
    37. 37. Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday] 25
    38. 38. Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Maps 25
    39. 39. Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas] 25
    40. 40. Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]//
Ranges
(you
can
create
your
own) 25
    41. 41. Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]//
Ranges
(you
can
create
your
own)def
bizDays
=
Monday..Friday 25
    42. 42. Native syntax constructs//
Listsdef
days
=
[Monday,
Tuesday,
Wednesday]//
Mapsdef
states
=
[CA:
California,
TX:
Texas]//
Ranges
(you
can
create
your
own)def
bizDays
=
Monday..Fridaydef
allowedAge
=
18..65 25
    43. 43. Optional parens & semis• Make statements and expressions look more like natural languages 26
    44. 44. Optional parens & semis• Make statements and expressions look more like natural languages move(left); 26
    45. 45. Optional parens & semis• Make statements and expressions look more like natural languages move(left); move
left 26
    46. 46. Named arguments• In Groovy you can mix named and unnamed arguments for method parameters – named params are actually put in a map parameter – plus optional parens & semis 

take
1.pill,
 


of:
Chloroquinine,
 after:
6.hours //
Calls
a
method
signature
like: def
take(Map
m,
MedicineQuantity
mq) 27
    47. 47. Command chains expressions 28
    48. 48. Command chains expressions• A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println 28
    49. 49. Command chains expressions• A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println 28
    50. 50. Command chains expressions• 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) 28
    51. 51. Command chains expressions• 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) 28
    52. 52. Command chains expressions• 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)• Let’s have a look at some examples 28
    53. 53. Command chains expressions 
turn
left

then
right
 29
    54. 54. Command chains expressions Alternation of method names 
turn
left

then
right
 29
    55. 55. Command chains expressions Alternation of method names 
turn
left

then
right
 and parameters (even named ones) 29
    56. 56. Command chains expressions 
turn
left

then
right
 29
    57. 57. Command chains expressions Equivalent to: 




(



).



(




) 
turn
left

then
right
 29
    58. 58. LookM a!N op are ns,no do ts!
    59. 59. Command chains expressions take
2.pills

of

chloroquinine

after

6.hours 31
    60. 60. Command chains expressions 



(






).


(












).





(






) take
2.pills

of

chloroquinine

after

6.hours 31
    61. 61. Command chains expressions //
environment
initialization Integer.metaClass.getPills
{
‐>
delegate
} Integer.metaClass.getHours
{
‐>
delegate
} 
 //
variable
injection def
chloroquinine
=
/*...*/ 
 { } //
implementing
the
DSL
logic def
take(n)
{ 



[of:
{
drug
‐> 







[after:
{
time
‐>
/*...*/
}] 



}] } //
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ take
2.pills
of
chloroquinine
after
6.hours 32
    62. 62. Command chains expressions take
2
.pills
of

chloroquinine
after

6.hours 33
    63. 63. Command chains expressions take
2
.pills
of

chloroquinine
after

6.hours ... some dots remain ... 33
    64. 64. Command chains expressions take
2

pills
of

chloroquinine
after

6
hours 34
    65. 65. Command chains expressions 



(
).




(

).












(




).
(




) take
2

pills
of

chloroquinine
after

6
hours 34
    66. 66. Command chains expressions //
variable
injection def
(of,
after,
hours)
=
/*...*/ 
 //
implementing
the
DSL
logic { } def
take(n)
{ 



[pills:
{
of
‐> 







[chloroquinine:
{
after
‐> 











[6:
{
time
‐>
}] 







}] 



}] } //
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ take
2
pills
of
chloroquinine
after
6
hours 35
    67. 67. Command chains expressions 36
    68. 68. Command chains expressions //
methods
with
multiple
arguments
(commas) 36
    69. 69. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 36
    70. 70. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation 36
    71. 71. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good 36
    72. 72. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures 36
    73. 73. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} 36
    74. 74. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens 36
    75. 75. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens select
all

unique()
from
names 36
    76. 76. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

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 36
    77. 77. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

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 take
3

cookies 36
    78. 78. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

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 take
3

cookies 36
    79. 79. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

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 take
3

cookies 36
    80. 80. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

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 take
3

cookies 36
    81. 81. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

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 take
3

cookies 36
    82. 82. Command chains expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

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 take
3

cookies 



(
). 36
    83. 83. BigDecimal by default• Main reason why financial institutions often decide to use Groovy for their business rules! – Although these days rounding issues are overrated! 37
    84. 84. BigDecimal by default• Main reason why financial institutions often decide to use Groovy for their business rules! – Although these days rounding issues are overrated! BigDecimal
uMinusv
=
c.subtract(a);
 BigDecimal
vMinusl
=
b.subtract(c);
 BigDecimal
uMinusl
=
a.subtract(b);
 return
e.multiply(uMinusv) 

.add(d.multiply(vMinusl)) 

.divide(uMinusl; 37
    85. 85. BigDecimal by default• Main reason why financial institutions often decide to use Groovy for their business rules! – Although these days rounding issues are overrated! BigDecimal
uMinusv
=
c.subtract(a);
 BigDecimal
vMinusl
=
b.subtract(c);
 BigDecimal
uMinusl
=
a.subtract(b);
 return
e.multiply(uMinusv) 

.add(d.multiply(vMinusl)) 

.divide(uMinusl; (d
*
(b
‐
c)
+
e
*
(c
‐
a))
/
(a
‐
b) 37
    86. 86. Custom control structures w/ closures• When closures are last, they can be put “out” of the parentheses surrounding parameter unless
(account.balance
>
100.euros,
{
 



account.debit
100.euros
 }) 38
    87. 87. Custom control structures w/ closures• When closures are last, they can be put “out” of the parentheses surrounding parameter unless
(account.balance
>
100.euros,
{
 



account.debit
100.euros
 }) //
calling
the
following
method: def
unless(boolean
b,
Closure
c) 38
    88. 88. Custom control structures w/ closures• When closures are last, they can be put “out” of the parentheses surrounding parameter unless
(account.balance
>
100.euros)
{ 



account.debit
100.euros } //
calling
the
following
method: def
unless(boolean
b,
Closure
c) 38
    89. 89. Operator overloadinga
+
b

//
a.plus(b) • Currency amountsa
‐
b

//
a.minus(b)a
*
b

//
a.multiply(b) – 15.euros + 10.dollarsa
/
b

//
a.divide(b)a
%
b

//
a.modulo(b) • Distance handlinga
**
b
//
a.power(b) – 10.kilometers - 10.metersa
|
b

//
a.or(b)a
&
b

//
a.and(b) • Workflow, concurrencya
^
b

//
a.xor(b)a[b]


//
a.getAt(b) – taskA | taskB & taskCa
<<
b
//
a.leftShift(b)a
>>
b
//
a.rightShift(b) • Credit an account+a




//
a.unaryPlus() – account << 10.dollars‐a




//
a.unaryMinus() account += 10.dollars~a




//
a.bitwiseNegate() account.credit 10.dollars 39
    90. 90. Level #2:Dynamic metaprogramming
    91. 91. The MOP(Meta-Object Protocol)
    92. 92. Groovy’s MOP• All the accesses to methods, properties, constructors, operators, etc. can be intercepted thanks to the MOP• While Java’s behavior is hard-wired at compile-time in the class• Groovy’s runtime behavior is adaptable at runtime through the metaclass• Different hooks for changing the runtime behavior – GroovyObject, custom MetaClass implementation, categories, ExpandoMetaClass 42
    93. 93. Hooks• GroovyObject(Support) – invokeMethod(), get/setProperty(), get/setMetaClass() – methodMissing(), propertyMissing()• Custom MetaClasses add – invokeConstructor(), invokeMissingMethod(), invokeStaticMethod() – get/setAttribute() – respondsTo(), hasProperty()• Categories – thread-scope & lexical-scope addition of methods 43
    94. 94. Hooks: ExpandoMetaClass• A DSL for MetaClasses! MoneyAmount.metaClass.constructor
=
{
...
} Number.metaClass.getDollars
=
{
...
} Distance.metaClass.toMeters
=
{
...
} Distance.metaClass.static.create
=
{
...
}• To avoid repetition of Type.metaClass, you can pass a closure to metaClass { ... }• The delegate variable in closure represents the current instance, and it the default parameter 44
    95. 95. Adding properties to numbers• Three possible approaches – create a Category • a category is a kind of decorator for default MCs – create a custom MetaClass • a full-blown MC class to implement and to set on the POGO instance – use ExpandoMetaClass • friendlier DSL approach but with a catch 45
    96. 96. With a Category class
DistanceCategory
{ 



static
Distance
getMeters(Integer
self)
{ 







new
Distance(self,
Unit.METERS) 



} } 
 use(DistanceCategory)
{ 



100.meters }

• Interesting scope: thread-bound & lexical• Have to surround with “use” – but there are ways to hide it 46
    97. 97. With an ExpandoMetaClass Number.metaClass.getMeters
=
{‐>
 



new
Distance(delegate,
Unit.METERS)
 } 
 100.meters• But the catch is it’s really a global change, so beware EMC enhancements collisions 47
    98. 98. The Builder pattern
    99. 99. A builder for HR softskills
{ 



ideas
{ 







capture
2 







formulate
3
 



} 



... } knowhow
{ 



languages
{ 







java
4 







groovy
5 



} 



... }

 49
    100. 100. A builder for HR softskills
{ 



ideas
{ 







capture
2 







formulate
3
 



} 



... } knowhow
{ 



languages
{ 







java
4 







groovy
5 



} 



... }

 49
    101. 101. A builder for HR 









( softskills
{ 








( 



ideas
{ 














(
) 







capture
2 
















(
) 







formulate
3
 




) 



} 



... 
) } 






( knowhow
{ 












( 



languages
{ 











(
) 







java
4 













(
) 







groovy
5 




) 



} 



 



... 
) }

 49
    102. 102. Level #3:Hooking into the compiler
    103. 103. Compile-time metaprogramming• Groovy 1.6 introduced AST Transformations• Compile-time == No runtime performance penalty! Transformation 51
    104. 104. Compile-time metaprogramming• With metaprogramming, Groovy’s able to modify the behaviour of programs... at runtime• Groovy 1.6 introduced AST Transformations – AST: Abstract Syntax Tree – Ability to change what’s being compiled at compile-time! • No runtime impact! • Lets you change the semantics of your programs! • Nice way of implementing patterns and removing boiler-plate technical code • Better interoperability with Java – Jave code can call the methods / fields / etc injected in Groovy classes 52
    105. 105. AST Transformations• Two kinds of transformations – Global transformations • applicable to all compilation units – Local transformations • applicable to marked program elements • using specific marker annotations 53
    106. 106. AST Transformations• Several (local) transformations available – @ToString – @Log – @Delegate – @Immutable – and many more 54
    107. 107. @Immutable• To properly implement immutable classes – No mutators (state musn’t change) – Private final fields – Defensive copying of mutable components – Proper equals() / hashCode() / toString() for comparisons, or for keys in maps, etc. @Immutable
class
Coordinates
{ 



Double
lat,
lng } def
c1
=
new
Coordinates(lat:
48.8,
lng:
2.5) def
c2
=
new
Coordinates(48.8,
2.5) assert
c1
==
c2 55
    108. 108. Global transformations• Implement ASTTransformation• Annotate the transfo specifying a compilation phase @GroovyASTTransformation(phase
=
CompilePhase.CONVERSION) class
MyTransformation
implements
ASTTransformation
{ 



void
visit(ASTNode[]
nodes,
SourceUnit
unit)
 



{
...
} }• For discovery, create the file META-INF/services/ org.codehaus.groovy.transform.ASTTransformation• Add the fully qualified name of the class in that file 56
    109. 109. Local transformations• Same approach as Globale transformations• But you don’t need the META-INF file• Instead create an annotation to specify on which element the transformation should apply @Retention(RetentionPolicy.SOURCE) @Target([ElementType.METHOD]) @GroovyASTTransformationClass(["com.foo.MyTransformation"]) @interface
CoolTransform
{...} 57
    110. 110. Example: the Spock framework• Changing the semantics of the original code• But keeping a valid Groovy syntax @Speck class
HelloSpock
{ 



def
"can
you
figure
out
what
Im
up
to?"()
{ 







expect: 







name.size()
==
size 







where: 







name




|
size 







"Kirk"


|

4 







"Spock"

|

5 







"Scotty"
|

6 



} }• Check out http://www.spockframework.org 58
    111. 111. Integration mechanisms
    112. 112. Various integration mechanisms• Java 6’s javax.script.* APIs (aka JSR-223)• Spring’s language namespace• Groovy’s own mechanisms• But a key idea is to externalize those DSL programs – DSL programs can have their own lifecycle – no need to redeploy an application because of a rule change – business people won’t see the technical code 60
    113. 113. Java 6’s javax.script.* API• Groovy provides an implementation of the javax.script.* API ScriptEngineManager
mgr
=
new
ScriptEngineManager(); ScriptEngine
engine
=
mgr.getEngineByName("Groovy"); String
result
=
(String)engine.eval("2+3"); 61
    114. 114. Spring’s lang namespace• POGOs (Plain Old Groovy Objects) can be pre-compiled as any POJO and used interchangeably with POJOs in a Spring application• But Groovy scripts & classes can be loaded at runtime through the <lang:groovy/> namespace and tag• Reloadable on change• Customizable through a custom MetaClass• <lang:groovy id="events" script-source="classpath:dsl/bizRules.groovy" customizer-ref="rulesCustomizerMetaClass" />• More to come in the next version of Spring 62
    115. 115. Groovy’s own mechanisms• GroovyShell, Eval, GroovyScriptEngine – for more complex scripts and DSLs• GroovyClassLoader or CompilationUnit – the most powerful mechanisms 63
    116. 116. GroovyShell• A Binding provides a context of execution – can implement lazy evaluation if needed• A base script class can be specified – providing «global» methods and fields def
binding
=
new
Binding() binding.mass
=
22.3 binding.velocity
=
10.6 def
shell
=
new
GroovyShell(binding) shell.evaluate("mass
*
velocity
**
2
/
2") 64
    117. 117. Imports customizer• Inject imports in your scripts – so that users don’t have to add imports manually def
configuration
=
new
CompilerConfiguration() 
 def
custo
=
new
ImportCustomizer() custo.addStaticStar(Math.name) configuration.addCompilationCustomizers(custo)
 
 def
result
=
new
GroovyShell(configuration) 



.evaluate("
cos
PI/3
") 65
    118. 118. Applying an AST transformation• Apply local AST transformations transparently def
configuration
=
new
CompilerConfiguration() configuration.addCompilationCustomizers( 



new
ASTTransformationCustomizer(Log))
 
 new
GroovyShell(configuration).evaluate(""" 



class
Car
{







 







Car()
{











 











log.info
Car
constructed







 







}



 



}




 



log.info
Constructing
a
car



 



def
c
=
new
Car() """) 66
    119. 119. Externalize business rules• Although Groovy DSLs can be embedded in normal Groovy classes, you should externalize them• Store them elsewhere – in a database, an XML file, etc.• Benefits – Business rules are not entangled in technical application code – Business rules can have their own lifecycle, without requiring application redeployments 67
    120. 120. Domain-Specific Language Descriptors• DSLs are nice, but what about support in my IDE?• DSLD to the rescue – Domain-Specific Language Descriptors – for Eclipse STS (but GDLS concent available in IntelliJ IDEA too)• Idea: a DSL for describing DSLs, in order to provide – code-completion – code navigation – documentation hovers 68
    121. 121. Domain-Specific Language Descriptors 69
    122. 122. Domain-Specific Language Descriptors 69
    123. 123. Domain-Specific Language DescriptorscurrentType(
subType(
Number
)
).accept
{



[m:
"meter",
yd:
"yard",
cm:
"centimeter",
mi:
"mile",
km:
"kilometer"].each
{







property
name:it.key,
type:"Distance",











doc:
"""A
<code>${it.value}</code>
from
<a
href="$url">$url</a>"""



}} 69
    124. 124. A few considerations Growing a language Security Testability
    125. 125. Start small, with key concepts Beware over-engineering!
    126. 126. Grow your language progressively
    127. 127. Get your hands dirtyPlay with the end-users
    128. 128. Let your DSL fly,it’s not yours, it’s theirs!
    129. 129. Tight feedback loop Iterative process
    130. 130. Stay humble,You can’t get it right the 1st time. Don’t design alone at your deskInvolve the end users from the start
    131. 131. Playing it safe in a sandbox
    132. 132. Example use case• Biggest European travel services company• Their customers – travel agencies, airlines, booking websites...• Customers want to customize the user experience – use personalized forms and templates – configure and tweak the underlying reservation features• The company runs customers’ Groovy code on their own platform and mutualised servers! – How to secure that? 78
    133. 133. Sandboxing approaches• Groovy supports the usual Java Security Managers – avoid System.exit(0)• Use metaprogramming tricks to prevent calling or instanciating certain classes – think overriding constructors to throw exceptions• Groovy 1.8 to the rescue – compiler customizers • SecureASTCustomizer • ASTTransformCustomizer 79
    134. 134. Secure AST customizer Idea: Implement an «arithmetic shell» Being able control what a user script is allowed to do: only arithmetic expressions 80
    135. 135. Secure AST customizer Idea: Implement an «arithmetic shell» Being able control what a user script is allowed to do: only arithmetic expressions• Let’s setup our environment – some imports – an import customizer to import java.lang.Math.* – prepare a secure AST customizer 80
    136. 136. Secure AST customizer Idea: Implement an «arithmetic shell» Being able control what a user script is allowed to do: only arithmetic expressions• Let’s setup our environment – some imports – an import customizer to import java.lang.Math.* – prepare a secure AST customizerimport
org.codehaus.groovy.control.customizers.*import
org.codehaus.groovy.control.*import
static
org.codehaus.groovy.syntax.Types.*
def
imports
=
new
ImportCustomizer().addStaticStars(java.lang.Math)def
secure
=
new
SecureASTCustomizer() 80
    137. 137. 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 ... 81
    138. 138. 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
]}... 82
    139. 139. Secure AST customizer• Ready to evaluate our arithmetic expressions! ... 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) 83
    140. 140. Test, test, test!• Don’t just test for nominal cases – Explicitely test for errors!• Ensure end-users get meaninful error messages 84
    141. 141. Summary
    142. 142. Summary• Groovy DSLs in the wild manage millions, and even billions of Euros (one of my customers) – so I guess we can say Groovy can be trusted :-)• Groovy 1.8 adds some nice capabilities for writing nicer plain-English-like business rules• Tooling is improving greatly with DSL descriptors – offering nice IDE integration for authoring business rules 86
    143. 143. Thank you! e aforg pment ume L Develo Guilla Groovy om He ad of e@ gmail.c glaforg rge Email: @glafo : Twitter 87
    1. A particular slide catching your eye?

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

    ×