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...
5. 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...
6. a
d
n
ge
A
• 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
9. 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
13. 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
20. a
d
n
ge
A
• 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
21. A collection of DSLs
• In our everyday life, we’re surrounded by DSLs
• Technical dialects
• Notations
• Business languages
30. 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 langua for nuclear safety simulations
• Market data feeds evolution scenarios
• and more...
31. a
d
n
ge
A
• 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
32. 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
33. 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”
34. 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!
35. 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
36. Optional parens & semis
• Make statements and expressions
look more like natural languages
• move(left);
•move left
37. 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)
38. 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)
39. 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)
40. 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)
41. Operator overloading
• Currency amounts
a+b a.plus(b)
•
a-b a.minus(b)
15.euros + 10.dollars
a*b a.multiply(b)
• Distance handling
a/b a.divide(b)
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)
•
a^b a.xor(b) taskA | taskB & taskC
a[b] a.getAt(b)
• Credit an account
a << b a.leftShift(b)
a >> b a.rightShift(b)
• account << 10.dollars
+a a.positive()
account += 10.dollars
-a a.negative()
account.credit 10.dollars
~a a.bitwiseNegate()
43. 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
44. 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
45. 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
46. 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
48. A builder for HR
• softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
49. A builder for HR
• softskills {
ideas {
capture 2
formulate 3
}
...
}
knowhow {
languages {
java 4
groovy 5
}
...
}
50. 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...
51. 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
52. 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
53. 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
54. 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
56. AST Transformations
• Two kinds of transformations
• Global transformations
• applicable to all compilation units
• Local transformations
• applicable to marked program elements
• using specific marker annotations
57. 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
58. 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 {...}
59. 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]
}
}
60. a
d
n
ge
A
• 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
61. 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
62. 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”);
63. 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; />
64. Groovy’s own mechanisms
• Eval
• for evaluating simple expressions
• GroovyShell
• for more complex scripts and DSLs
• GroovyClassLoader
• the most powerful mechanism
66. 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”)
67. 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);
68. 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
69. a
d
n
ge
A
• 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
77. 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
78. Test, test, test!
• Don’t just test for nominal cases
• Explicitely test for errors!
• Ensure end-users get meaninful error messages
80. 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
81. ?
I kan haz my cheezburgr naw?
Or do ya reely haz keshtionz?