3. Guillaume Laforge
⢠Groovy Project Manager
⢠JSR-241 Spec Lead
⢠Head of Groovy Development
at SpringSource
⢠Initiator of the Grails framework
⢠Co-author of Groovy in Action
⢠Speaker: JavaOne, QCon, JavaZone, Sun TechDays,
Devoxx, The Spring Experience, JAX, Dynamic
Language World, IJTC, and more...
3
4. A few words about Groovy
⢠Groovy is a dynamic language for the JVM
â with a Meta Object Protocol
â compiles directly to bytecode, seamless Java interop
⢠Open Source ASL 2 project hosted at Codehaus
⢠Relaxed grammar derived from Java 5
â + borrowed good ideas from Ruby, Python, Smalltalk
⢠Fast... for a dynlang on the JVM
⢠Closures, properties, optional typing, BigDecimal
by default, nice wrapper APIs, and more...
4
5. nd a
Ag e
⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-Specific Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
5
8. Developer producing
LOLCODE
HAI
CAN HAS STDIO?
I HAS A VAR
IM IN YR LOOP
UP VAR!!1
VISIBLE VAR
IZ VAR BIGGER THAN 10?
KTHXBYE
IM OUTTA YR LOOP
KTHXBYE
12. 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
12
19. nd a
Ag e
⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-Specific Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
15
20. A collection of DSLs
⢠In our everyday life, weâre surrounded by DSLs
â Technical dialects
â Notations
â Business languages
16
29. 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 more...
25
30. nd a
Ag e
⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-Specific Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
26
31. A flexible & malleable syntax
⢠No need to write full-blown classes, use scripts
⢠Optional typing (def)
â in scripts, you can even omit the def keyword
⢠Native syntax constructs
⢠Parentheses & semi-colons are optional
⢠Named arguments
⢠BigDecimal by default for decimal numbers
⢠Closures for custom control structures
⢠Operator overloading
27
32. 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â
28
33. Optional typing
⢠No need to bother with types or even generics
â unless you want to!
⢠Imagine an interest rate lookup table method
returning some generified type:
âRate<LoanType, Duration, BigDecimal>[]
lookupTable() { ... }
def table = lookupTable()
⢠No need to repeat the horrible generics type info!
29
34. Native syntax constructs
⢠Lists
â [Monday, Tuesday, Wednesday]
⢠Maps
â [CA: âCaliforniaâ, TX: âTexasâ]
⢠Ranges
â def bizDays = Monday..Friday
â def allowedAge = 18..65
â You can create your own custom ranges
30
35. Optional parens & semis
⢠Make statements and expressions
look more like natural languages
â move(left);
â move left
31
36. 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
⢠Corresponds to a method signature like:
âdef take(Map m, MedicineQuantity mq)
32
37. BigDecimal by default
⢠Main reason why financial institutions often decide
to use Groovy for their business rules!
â Although these days rounding issues are overrated!
⢠Java vs Groovy for a simple interpolation equation
⢠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, 10, BigDecimal.ROUND_HALF_UP);
⢠(d * (b - c) + e * (c - a)) / (a - b)
33
38. Custom control structures
Thanks to closures
⢠When closures are last, they can be put âoutâ of
the parentheses surrounding parameters
⢠unless (account.balance > 100.euros,
{ account.debit 100.euros })
⢠unless (account.balance > 100.euros) {
account.debit 100.euros
}
⢠Signature def unless(boolean b, Closure c)
34
39. Operator overloading
a + b a.plus(b)
a - b a.minus(b) ⢠Currency amounts
a * b a.multiply(b) â 15.euros + 10.dollars
a / b a.divide(b)
a % b a.modulo(b) ⢠Distance handling
a ** b a.power(b) â 10.kilometers - 10.meters
a | b a.or(b)
a & b a.and(b) ⢠Workflow, concurrency
a ^ b a.xor(b) â taskA | taskB & taskC
a[b] a.getAt(b)
a << b a.leftShift(b) ⢠Credit an account
a >> b a.rightShift(b) â account << 10.dollars
+a a.positive() account += 10.dollars
-a a.negative() account.credit 10.dollars
~a a.bitwiseNegate()
35
41. 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
37
42. GroovyObject
⢠All instances of classes created in Groovy
implement the GroovyObject interface:
âgetProperty(String name)
âsetProperty(String name, Object value)
âinvokeMethod(String name, Object[]
params)
âgetMetaClass()
âsetMetaClass(MetaClass mc)
⢠A GO can have âpretendedâ methods and properties
38
43. MetaClass
⢠The core of Groovyâs MOP system
âinvokeConstructor()
âinvokeMethod() and invokeStaticMethod()
âinvokeMissingMethod()
âgetProperty() and setProperty()
âgetAttribute() and setAttribute()
ârespondsTo() and hasProperty()
⢠MetaClasses can change the behavior of existing
third-party classes â even from the JDK
39
44. 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
40
46. The Groovy MarkupBuilder
⢠def mkp = new MarkupBuilder()
mkp.html {
head {
title âGroovy in Actionâ
}
body {
div(width: â100â) {
p(class: âpara) {
span âBest book ever!â
}
}
}
}
42
47. A builder for HR
⢠softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
43
48. A builder for HR
⢠softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
43
49. Builders
⢠Builders are...
â a mechanism for creating any tree-structered graph
â the realization of the GoF builder pattern at the syntax
level in Groovy
â simply a clever use of chained method invocation,
closures, parentheses omission, and use of the
GroovyObject methods
⢠Existing builders
â XML, Object graph, Swing, Ant, JMX, and more...
44
50. The clever trick
⢠GroovyObject#invokeMethod() is used to catch all
non-existing method calls in the context of the
builder
⢠The nesting of closures visually shows the level of
nesting / depth in the tree
⢠builder.m1(attr1:1, attr2:2, { builder.m2(...,
{...}) } becomes equivalent to
builder.m1(attr1:1, attr2:2) { m2(...) {...} }
thanks to parens omission
45
51. 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
46
52. With a Category
⢠class DistanceCategory {
static Distance getMeters(Integer self) {
new Distance(self, Unit.METERS)
}
}
use(DistanceCategory) {
100.meters
}
⢠Interesting scope: thread-bound & lexical
⢠But doesnât work across the hierarchy of classes
â ie. subclasses wonât benefit from the new property
47
53. With an ExpandoMetaClass
⢠Number.metaClass.getMeters = {->
new Distance(delegate, Unit.METERS)
}
100.meters
⢠Works for the class hierarchy for POJOs, and a flag
exists to make it work for POGOs too
⢠But the catch is itâs really a global change, so
beware EMC enhancements collisions
48
55. AST Transformations
⢠Two kinds of transformations
â Global transformations
â˘applicable to all compilation units
â Local transformations
â˘applicable to marked program elements
â˘using specific marker annotations
50
56. Example #1: @Singleton
⢠Letâs revisit this evil (anti-)pattern
! public class Evil {
public static final Evil instance = new Evil
();
private Evil () {}
Evil getInstance() { return instance; }
}
⢠In Groovy
! @Singleton class Evil {}
⢠Also a âlazyâ version
! @Singleton(lazy = true) class Evil {}
51
57. Example #2: @Delegate
Not just for managers!
⢠You can delegate to fields of your classes
â class Employee {
def doTheWork() { âdoneâ }
}
class Manager {
@Delegate
Employee slave = new Employee()
}
def god = new Manager()
assert god.doTheWork() == âdoneâ
⢠Damn manager who will get all the praise...
52
58. Global transformations
⢠Implement ASTTransformation
⢠Annotate the transfo specifying a compilation phase
⢠@GroovyASTTransformation(phase=CompilePhase.CONVERSION)
public class MyTransformation
implements ASTTransformation {
public 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
53
59. 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(
["fqn.MyTransformation"])
public @interface WithLogging {...}
54
60. 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 I'm up to?"()
{
expect:
name.size() == size
where:
name << ["Kirk", "Spock", "Scotty"]
size << [4, 5, 6]
}
}
55
61. nd a
Ag e
⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-Specific Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
56
62. 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
57
63. Java 6âs javax.script.* API
⢠Groovy 1.6 provides its own implementation of the
javax.script.* API
⢠ScriptEngineManager mgr =
new ScriptEngineManager();
ScriptEngine engine =
mgr.getEngineByName(âGroovyâ);
String result = (String)engine.eval(â2+3â);
58
64. 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/
eventsChart.groovy"
customizer-ref="eventsMetaClass" />
59
65. Groovyâs own mechanisms
⢠Eval
â for evaluating simple expressions
⢠GroovyShell
â for more complex scripts and DSLs
⢠GroovyClassLoader
â the most powerful mechanism
60
67. GroovyShell
⢠A Binding provides a context of execution
â can implement lazy evaluation if needed
⢠A base script class can be specified
⢠def binding = new Binding()
binding.mass = 22.3
binding.velocity = 10.6
def shell = new GroovyShell(binding)
shell.evaluate(âmass * velocity ** 2 / 2â)
62
68. GroovyClassLoader
⢠Most powerful mechanism
â could also visit or change the AST
â scripts & classes can be loaded from elsewhere
â more control on compilation
⢠GroovyClassLoader gcl =
new GroovyClassLoader();
Class clazz = gcl.parseClass(
new File(âf.groovyâ));
GroovyObject instance =
(GroovyObject)clazz.newInstance();
instance.setMetaClass(customMC);
63
69. 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
64
70. nd a
Ag e
⢠The context and
the usual issues we face
⢠Some real-life examples of
Domain-Specific Languages
⢠Groovyâs DSL capabilities
⢠Integrating a DSL
in your application
⢠Considerations to remember
when designing your own DSL
65
78. Various levels of sandboxing
⢠Groovy supports the usual Java Security Managers
⢠Use metaprogramming tricks to prevent calling /
instantiating certain classes
⢠Create a special GroovyClassLoader AST code
visitor to filter only the nodes of the AST you want
to keep
â ArithmeticShell in Groovyâs samples
73
79. Test, test, test!
⢠Donât just test for nominal cases
â Explicitly test for errors!
⢠Ensure end-users get meaningful error
messages
74
80. nd a
Ag e
⢠Summary
⢠Questions & Answers
75
81. Summary
⢠Groovyâs a great fit for Domain-Specific Languages
â Malleable & flexible syntax
â Full object-orientation
⢠Metaprogramming capabilities
â Runtime metaprogramming
â Compile-time metaprogramming
⢠Groovyâs very often used for mission-critical DSLs
76
82. ?
I kan haz my cheezburgr naw?
Or do ya reely haz keshtionz?