2. 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...
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 2
3. 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...
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 3
4. end a
The context and Ag
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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 4
7. 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
11. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 11
12. Towards more readibility (1)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 12
13. Towards more readibility (1)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 12
14. Towards more readibility (1)
20%
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 12
15. Towards more readibility (2)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 13
16. Towards more readibility (2)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 13
17. Towards more readibility (2)
80%
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 13
18. end a
The context and Ag
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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 14
19. A collection of DSLs
In our everyday life, we’re surrounded by DSLs
• Technical dialects
• Notations
• Business languages
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 15
20. Technical dialects
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 16
27. Business languages
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 23
28. 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...
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 24
29. end a
The context and Ag
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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 25
30. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 26
31. 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”
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 27
32. 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!
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 28
33. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 29
34. Optional parens & semis
Make statements and expressions
look more like natural languages
• move(left);
• move left
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 30
35. 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)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 31
36. 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)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 32
37. 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)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 33
38. Operator overloading
a + b a.plus(b) Currency amounts
a - b a.minus(b)
• 15.euros + 10.dollars
a * b a.multiply(b)
a / b a.divide(b) Distance handling
a % b a.modulo(b) • 10.kilometers - 10.meters
a ** b a.power(b)
a | b a.or(b) Workflow, concurrency
a & b a.and(b) • taskA | taskB & taskC
a ^ b a.xor(b)
a[b] a.getAt(b) Credit an account
a << b a.leftShift(b)
•account << 10.dollars
account += 10.dollars
a >> b a.rightShift(b) account.credit 10.dollars
+a a.positive()
-a a.negative()
~a a.bitwiseNegate()
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 34
40. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 36
41. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 37
42. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 38
43. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 39
45. A builder for HR
softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 41
46. A builder for HR
softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 41
47. 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...
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 42
48. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 43
49. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 44
50. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 45
51. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 46
53. AST Transformations
Two kinds of transformations
• Global transformations
• applicable to all compilation units
• Local transformations
• applicable to marked program elements
• using specific marker annotations
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 48
54. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 49
55. 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(
[quot;fqn.MyTransformationquot;])
public @interface WithLogging {...}
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 50
56. Example: the Spock framework
Changing the semantics of the original code
But keeping a valid Groovy syntax
@Speck
class HelloSpock {
def quot;can you figure out what I'm up to?quot;() {
expect:
name.size() == size
where:
name << [quot;Kirkquot;, quot;Spockquot;, quot;Scottyquot;]
size << [4, 5, 6]
}
}
Check out http://www.spockframework.org
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 51
57. end a
The context and Ag
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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 52
58. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 53
59. 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”);
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 54
60. 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=quot;eventsquot;
script-source=quot;classpath:dsl/
eventsChart.groovyquot;
customizer-ref=quot;eventsMetaClassquot; />
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 55
61. Groovy’s own mechanisms
Eval
• for evaluating simple expressions
GroovyShell
• for more complex scripts and DSLs
GroovyClassLoader
• the most powerful mechanism
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 56
63. 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”)
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 58
64. 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);
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 59
65. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 60
66. end a
The context and Ag
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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 61
74. Various levels
of sandboxing
Groovy supports the usual Java Security Managers
Use metaprogramming tricks to prevent calling /
instanciating 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 69
75. Test, test, test!
Don’t just test for nominal cases
• Explicitely test for errors!
Ensure end-users get meaninful error messages
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 70
76. end a
Ag
Summary
Questions & Answers
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 71
77. 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
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 72
78. ?
I kan haz my cheezburgr naw?
Or do ya reely haz keshtionz?
79. Appendix
2009 CommunityOne Conference: EAST | developers.sun.com/events/communityone 74