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