Practical Groovy DSL
Upcoming SlideShare
Loading in...5
×
 

Practical Groovy DSL

on

  • 19,862 Views

 

Statistiken

Views

Gesamtviews
19,862
Views auf SlideShare
19,724
Views einbetten
138

Actions

Gefällt mir
30
Downloads
625
Kommentare
1

3 Einbettungen 138

http://www.slideshare.net 84
http://blog.jaffamonkey.com 51
http://www.mefeedia.com 3

Zugänglichkeit

Kategorien

Details hochladen

Uploaded via as Adobe PDF

Benutzerrechte

© Alle Rechte vorbehalten

Report content

Als unangemessen gemeldet Als unangemessen melden
Als unangemessen melden

Wählen Sie Ihren Grund, warum Sie diese Präsentation als unangemessen melden.

Löschen

11 of 1

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Ihre Nachricht erscheint hier
    Processing...
Kommentar posten
Kommentar bearbeiten

    Practical Groovy DSL Practical Groovy DSL Presentation Transcript

    • Guillaume Laforge / Groovy Project Manager / SpringSource Practical Domain-Specific Languages with Groovy All the techniques to create your own DSL. 1
    • 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...
    • 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...
    • a d n e g 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
    • The context
    • Subject Matter Experts, Business analysts...
    • 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
    • Lots of languages...
    • And in the end... ...nobody understands each other
    • Expressing requirements...
    • 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
    • Towards more readibility (1)
    • Towards more readibility (1)
    • Towards more readibility (1) 20%
    • Towards more readibility (2)
    • Towards more readibility (2)
    • Towards more readibility (2) 80%
    • a d n e g 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
    • A collection of DSLs • In our everyday life, we’re surrounded by DSLs • Technical dialects • Notations • Business languages
    • Technical dialects
    • SQL
    • ^[w-.]+@([w-]){2,4}$
    • Notations
    • 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6
    • L2 U F-1 B L2 F B -1 U L2
    • Visual!
    • Business languages
    • 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...
    • a d n e g 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
    • 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
    • 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”
    • 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!
    • 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
    • Optional parens & semis • Make statements and expressions look more like natural languages • move(left); •move left
    • 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)
    • 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)
    • 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)
    • 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()
    • Groovy’s dynamic heart: The MOP! MetaObject Protocol
    • 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
    • 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
    • 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
    • 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
    • The Builder pattern
    • 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!” } } } }
    • A builder for HR • softskills { ideas { capture 2 formulate 3 } ... } knowhow { languages { java 4 groovy 5 } ... }
    • A builder for HR • softskills { ideas { capture 2 formulate 3 } ... } knowhow { languages { java 4 groovy 5 } ... }
    • 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...
    • 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
    • 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
    • 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
    • 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
    • Compile-time metaprogramming • Groovy 1.6 introduced AST Transformations • Compile-time == No runtime performance penalty! Transformation
    • AST Transformations • Two kinds of transformations • Global transformations • applicable to all compilation units • Local transformations • applicable to marked program elements • using specific marker annotations
    • 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 {} 
    • 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 praiser...
    • 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
    • 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 {...}
    • 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] } }
    • a d n e g 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
    • 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
    • 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”);
    • 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; />
    • Groovy’s own mechanisms • Eval • for evaluating simple expressions • GroovyShell • for more complex scripts and DSLs • GroovyClassLoader • the most powerful mechanism
    • Eval • Simple mechanism to evaluate math-like formulas •Eval.me ( ‘3*4’) Eval.x (1, ‘3*x + 4’) Eval.xy (1, 2, ‘x + y’) Eval.xyz(1, 2, 3, ‘x * y - z’)
    • 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”)
    • 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);
    • 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
    • a d n e g 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
    • Start small, with key concepts Beware overengineering!
    • Grow your language progressively
    • Get your hands dirty Play 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 first time. Don’t design alone at your desk Involve the end users from the start
    • Playing it safe in a sandbox
    • 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
    • Test, test, test! • Don’t just test for nominal cases • Explicitely test for errors! • Ensure end-users get meaninful error messages
    • a d n e g A • Summary • Questions & Answers
    • 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
    • ? I kan haz my cheezburgr naw? Or do ya reely haz keshtionz?
    • Appendix
    • • http://www.flickr.com/photos/wheatfields/420088151/sizes/l/ • http://www.flickr.com/photos/therefromhere/518053737/sizes/l/ • http://www.flickr.com/photos/romainguy/230416692/sizes/l/ • http://www.flickr.com/photos/addictive_picasso/2874279971/sizes/l/ • http://www.flickr.com/photos/huangjiahui/3127634297/sizes/l/ • http://www.flickr.com/photos/25831000@N08/3064515804/sizes/o/ • http://www.flickr.com/photos/lanier67/3147696168/sizes/l/ • http://www.flickr.com/photos/ktb/4916063/sizes/o/ • http://www.flickr.com/photos/nathonline/918128338/sizes/l/ • http://www.flickr.com/photos/kevinsteele/39300193/sizes/l/ • http://commons.wikimedia.org/wiki/File:Brueghel-tower-of-babel.jpg • http://commons.wikimedia.org/wiki/File:Platypus.jpg • http://www.flickr.com/photos/joaomoura/2317171808/sizes/l/ • http://www.flickr.com/photos/wiccked/132687067/ • http://www.flickr.com/photos/xcbiker/386876546/sizes/l/ • http://www.flickr.com/photos/pietel/152403711/sizes/o/
    • • http://www.flickr.com/photos/forezt/192554677/sizes/o/ • http://keremkosaner.files.wordpress.com/2008/04/softwaredevelopment.gif • http://www.jouy.inra.fr • http://www.flickr.com/photos/ejpphoto/408101818/sizes/o/ • http://www.flickr.com/photos/solaro/2127576608/sizes/l/ • http://www.flickr.com/photos/biggreymare/2846899405/sizes/l/ • http://www.flickr.com/photos/timsamoff/252370986/sizes/l/ • http://www.flickr.com/photos/29738009@N08/2975466425/sizes/l/ • http://www.flickr.com/photos/howie_berlin/180121635/sizes/o/ • http://www.flickr.com/photos/yogi/1281980605/sizes/l/ • http://www.flickr.com/photos/dorseygraphics/1336468896/sizes/l/