SlideShare a Scribd company logo
Building DSLs with Groovy:
A Real World Case Study
Sten Anderson
Agenda
What is a DSL?
Groovy’s DSL-friendly language features
Case study: City of Chicago
What is a DSL?
DSL: Domain Specific Language
Small, Specific “Mini-language” geared toward a Domain
Favor conciseness and readability in a narrow domain over Turing
Completeness
External
• Involve Lexer/Parser
• Ant, SQL
Internal (Embedded DSLs/Fluent Interfaces)
• Defined inside a more general purpose language
• Gant/Rake
DSL vs. Framework vs. API?
• What is OpenGL?
Why use Groovy to Write DSLs?
Groovy is a natural extension for Java Developers
• IDE support for “free”
At a minimum Groovy is “less verbose Java”
At a maximum Groovy lets you change the language itself
• dynamic methods and properties and operator overloading
Groovy has built-in DSLs (Builders)
Groovy’s DSL-Friendly Features
Reduced Syntax
Closures
Metaprogramming
• Dynamic methods and properties
• ExpandoMetaclass
Language Feature Example: Text Adventure Interpreter
class Game {
void go(dir) {
println "You go $dir"
}
void look() {
println "You look around and see nothing"
}
void take(it) {
println "You take the $it"
}
...
}
Let’s Start with Java…
…And Make it Groovy…
new GroovyShell().evaluate(“game.${playerInput}”)
go(“north”);
look();
take(“dagger”);
Reduced Syntax
go “north”
look()
take “dagger”
Metaprogramming: Dynamic Properties
class Game {
...
def propertyMissing(String name) {
name
}
go north
look()
take dagger
Metaprogramming: Dynamic Properties
def propertyMissing(String name) {
if (metaClass.respondsTo(this, name)) {
this."$name"()
}
name
}
go north
look
take dagger
Remember This?
new GroovyShell().evaluate(“game.${playerInput}”)
Not very “Groovy”
Closures and “with”
new GroovyShell().evaluate(“game.with{${playerInput}}”)
3.times {
go north
}
look
take dagger
One more thing... (the Synonym Problem)
look
> You see a dagger on the ground
get dagger
> I don’t understand that
grab dagger
> I don’t understand that
Just let me have the %$!#@ dagger!
> I don’t understand that
Metaprogramming: Dynamic Methods
def methodMissing(String name, args) {
if (["grab", “hold", "yoink"].contains(name)) {
this.take(args[0])
}
}
look
> You see a dagger on the ground
grab dagger
> You take the dagger
You’re darn right I do!
> I don’t understand that
def methodMissing(String name, args) {
def funcName = ThesaurusLookup.contains(name)
if (funcName) {
Game.metaClass."$name" = { item ->
this.”$funcName”(item)
}
this.”$name”(args[0])
}
}
Metaprogramming: ExpandoMetaclass
How Far We’ve Come
go north
look
take dagger
grab fish
game.go(“north”);
game.look();
game.take(“dagger”);
game.take(“fish”);
From this… To this…
Case Study: Budget Books for Fun and (non) Profit
The Challenge
Replace Mainframe “Budget Book Generator” application
Did not interact with the live budget data
Over 3 hours to produce a book
Very manual process (print the book, rescan as a PDF of images)
Somewhat dated look
The Solution
Java SE Swing Application deployed locally via Webstart
Time to produce final PDF reduced from three hours to a few minutes
Operated Directly with the Budget Data
PDF as native output: searchable, indexable
Building DSLs with Groovy
Building DSLs with Groovy
BookSection
PageElement
Book Object Model
BookSection
PageElement
TextElement TableHeaderElement TableRowElement …
BookSection
BookSection
TextElement
TableHeader
Separator
TableSection
TableRow
TableTotal
How do we go from Budget Objects to Book Objects?
•Departments
•Organizations
•Appropriations
•Positions
•BookSections
•PageElements
Can we write a DSL to create a book?
Building Drafts with ContentBuilder
interface ContentBuilder
- Draft build(ContentData)
ContentBuilder: First Attempt
Java
BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
header.addHeader(new TableHeader(0.05));
header.addHeader(new TableHeader(0.3, "Position",
Formatter.LeftAligned));
bookSection.addPageElement(header);
for (TableSection section : tableSections) {
TableSectionElement sectionElement = new TableSectionElement();
sectionElement.addColumn(new TableColumn(2, section.getName());
bookSection.addPageElement(sectionElement);
...
BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
Remember to fill out your Objects in Triplicate
1 2 3
BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
header.addHeader(new TableHeader(0.05));
header.addHeader(new TableHeader(0.3, "Position",
Formatter.LeftAligned));
bookSection.addPageElement(header);
Declared here
Added here
BookSection bookSection = new BookSection ("Positions");
TableHeaderElement header = new TableHeaderElement();
header.addHeader(new TableHeader(0.05));
header.addHeader(
new TableHeader(0.3, "Position", Formatter.LeftAligned));
•What do these arguments mean?
•What is “0.05”?
•What is “Position”?
ContentBuilder: Second Attempt
Java with method chaining
Method Chaining: Return the “Current” Object
public void setWidth(double width) { this.width = width;}
public TableHeader setWidth(double width) {
this.width = width;
return this;
}
•Instead of:
•We have:
BookSection bookSection = new BookSection ("Positions");
bookSection.addPageElement(new TableHeader().
addHeader(new TableHeader().setWidth(0.05)).
addHeader(new TableHeader().
setWidth(0.3).
setName("Position").
setFormatter(Formatter.LeftAligned)));
for (TableSection section : tableSections) {
bookSection.addPageElement(new TableSectionElement().
addColumn(new TableColumn().
setColumnSpan(2).
setName(section.getName()));
...
}
Let’s Take a Step Back…
Both Java versions “work”, but are verbose and not overly readable
Need better support for building complex object graphs at the
language level
• The structure of the code should mirror the structure of the objects
(declarative syntax)
• Ceremony vs. Essence
We need a “Builder”
Well, Groovy has Builders
MarkupBuilder
• Tag-based markup (XML, HTML)
SwingBuilder
• GUIs
GraphicsBuilder
• Java2D
NodeBuilder
• General Object graph (Nodes)
MetaBuilder
• Builder of Builders
MarkupBuilder
def htmlBuilder = new MarkupBuilder()
htmlBuilder.html {
body {
h1 "Hi There!"
}
}
<html>
<body>
<h1>Hi There!</h1>
</body>
</html>
SwingBuilder
def builder = new SwingBuilder()
builder.frame (title:"Swing Builder Test",
size:[300,100],
location:[500,200]) {
flowLayout()
button(text:"Press Me")
}.show()
groovy.util.BuilderSupport
void setParent(Object parent, Object child);
Object createNode(Object name);
Object createNode(Object name, Object value);
Object createNode(Object name, Map attributes);
Object createNode(Object name, Map attributes, Object value);
createNode(Object name, Map attributes, Object value)
h1 "Hi There!"
button text:"Press Me“, visible:true
Reduced Syntax
foo [a1:“val1”, b2:true] foo a1:“val1”, b2:true
foo (a1:“val1”, b2:true, {bar()})
foo (a1:“val1”, b2:true) {bar()}
Instead of this:
frame ([title:"Swing Builder Test"], {
flowLayout()
button(text:"Press Me")
})
frame (title:"Swing Builder Test") {
flowLayout()
button(text:"Press Me")
}
You have this:
Better Still: FactoryBuilderSupport
Used by SwingBuilder
Register “Factories” that know how to create your objects
Convenience AbstractFactory requires only one method:
• Object newInstance ( FactoryBuilderSupport builder,
Object name,
Object value,
Map attributes )
A Look Inside SwingBuilder
public SwingBuilder() {
...
registerFactory("button", new RichActionWidgetFactory(JButton));
...
}
ContentBuilder: Final Attempt
BookSection
BookSection
TextElement
TableHeader
Separator
TableSection
TableRow
TableTotal
BookSectionBuilder (extends FactoryBuilderSupport)
public BookSectionBuilder() {
registerFactory("bookSection", new BookSectionFactory());
registerFactory("textElement",
new PageElementFactory(TextElement.class));
...
}
BookSectionFactory (extends AbstractFactory)
public Object newInstance(FactoryBuilderSupport builder, Object name,
Object value, Map attributes) {
return new BookSection(attributes.get(“name”).toString());
}
public void setParent( FactoryBuilderSupport builder, Object parent,
Object child ) {
((BookSection) parent).newBookSection((BookSection) child);
}
PageElementFactory (extends AbstractFactory)
private Class<? extends PageElement> clazz;
public Object newInstance(FactoryBuilderSupport builder, Object name,
Object value, Map attributes) {
return clazz.newInstance();
}
public void setParent( FactoryBuilderSupport builder, Object parent,
Object child ) {
((BookSection) parent).newPageElement((PageElement) child);
}
Groovy Builder (Final Version)
bookSection (name:"Positions") {
tableHeaderElement(styleName:”header.large”, preTablePadding:10) {
tableHeader width:0.05
tableHeader name:”Position”, width:0.3,formatter:LeftAligned
}
tableSections.each { section ->
tableSectionElement (styleName:"table.subheader.1") {
tableColumn columnSpan:2, section.name
}
...
...
positions.each { position ->
tableRowElement (styleName:"table.row") {
tableColumn position.accountId
tableColumn position.title
}
}
tableTotalElement(styleName:"table.total") {
tableColumn columnSpan:2, "Position Total"
}
} // table section
tableFooterElement()
} // book section
for (Position position : positions) {
TableRowElement row = new TableRowElement();
row.setStyleName(“table.row”);
row.addColumn(new TableColumn(position.getAccountId()));
row.addColumn(new TableColumn(position.getTitle()));
bookSection.newPageElement(row);
}
positions.each { position ->
tableRowElement (styleName:"table.row") {
tableColumn position.accountId
tableColumn position.title
}
}
Before:
After:
Free Things “Built-In” to the Builder
Wiring of map attributes to object properties
Preservation of Object hierarchy
Automatic management of closure delegates so that the “Builder” is
always in scope
Closures: What’s a Delegate?
def htmlBuilder = new MarkupBuilder()
htmlBuilder.html {
body {
h1 "Hi There!"
}
}
“html”, “body”, and “h1” don’t exist in scope, but rather are handled
by the Builder
htmlBuilder needs to be set as the closure’s Delegate
…But the Methods Don’t Exist in the Builder Either
def xmlBuilder = new MarkupBuilder()
xmoBuilder.invoices {
invoice {
lineitem ...
}
}
The Builder “pretends” to have these methods
invokeMethod is overridden to write out the markup or (in the case of
SwingBuilder/BookSectionBuilder) dispatch the method to the correct
Factory
(Metaprogramming: Dynamic Methods)
Let’s Add Charting Support
First Write a Factory
private static class ChartFactory extends AbstractFactory {
public Object newInstance(...) {
return new Chart(value.toString());
}
public void setParent(...) {
TableCell cell = (TableCell) parent;
cell.addContent(child.toString());
}
}
Then Register the Factory…
public BookSectionBuilder() {
registerFactory("bookSection", new BookSectionFactory());
registerFactory("textElement",
new PageElementFactory(TextElement.class));
...
registerFactory("chart", new ChartFactory());
}
…And It’s Ready to use in the Builder
tableRowElement {
tableColumn (columnSpan:2) {
chart dept.fundPercent
}
tableColumn {
chart allFundTotal
}
}
Is a DSL Right for Me?
Are the people who maintain your project different than those who
initially developed it?
DSLs give the “gift of documentation”
Is your project part of a domain?
Using Dynamic Languages like Groovy make DSLs more Painless
Resources
Writing an Adventure game DSL in LISP: http://www.lisperati.com/casting.html
http://groovy.codehaus.org/FactoryBuilderSupport
http://docs.codehaus.org/display/GROOVY/MetaBuilder
http://docs.codehaus.org/display/GROOVY/Writing+Domain-Specific+Languages
http://www.agiledeveloper.com/blog/PermaLink.aspx?guid=ce021e6d-b0c2-4d83-8b69-
c163c290983d
http://blogs.citytechinc.com/sanderson/
King’s Quest and Zork images taken from Wikipedia.
“Groovy is like Gravy, except that it has the „Groove‟ in it.”
-Meredith Anderson (5 Year Old)

More Related Content

PDF
Intro to MongoDB and datamodeling
rogerbodamer
 
PPTX
Getting started with Elasticsearch and .NET
Tomas Jansson
 
PPTX
Indexing and Query Optimization
MongoDB
 
PPTX
엘라스틱서치 적합성 이해하기 20160630
Yong Joon Moon
 
PDF
Practica n° 7
rafobarrientos
 
PPTX
Indexing & Query Optimization
MongoDB
 
PDF
Storing tree structures with MongoDB
Vyacheslav
 
PPT
03DOM.ppt
Bhavani Testone
 
Intro to MongoDB and datamodeling
rogerbodamer
 
Getting started with Elasticsearch and .NET
Tomas Jansson
 
Indexing and Query Optimization
MongoDB
 
엘라스틱서치 적합성 이해하기 20160630
Yong Joon Moon
 
Practica n° 7
rafobarrientos
 
Indexing & Query Optimization
MongoDB
 
Storing tree structures with MongoDB
Vyacheslav
 
03DOM.ppt
Bhavani Testone
 

What's hot (19)

PDF
groovy databases
Paul King
 
PPTX
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rick Copeland
 
PPTX
Java Development with MongoDB
Scott Hernandez
 
PPTX
Morphia: Simplifying Persistence for Java and MongoDB
Jeff Yemin
 
PDF
greenDAO
Mu Chun Wang
 
PDF
Solr's Search Relevancy (Understand Solr's query debug)
Wongnai
 
PPTX
MongoDB: Easy Java Persistence with Morphia
Scott Hernandez
 
PPTX
11. session 11 functions and objects
Phúc Đỗ
 
PDF
Spock and Geb
Christian Baranowski
 
PDF
MongoDB
Hemant Kumar Tiwary
 
PDF
jQuery Rescue Adventure
Allegient
 
PDF
Ruby Development and MongoMapper (John Nunemaker)
MongoSF
 
PDF
MongoDB With Style
Gabriele Lana
 
PPTX
Html5 Overview
Abdel Moneim Emad
 
PDF
Green dao
彥彬 洪
 
PPTX
ElasticSearch for .NET Developers
Ben van Mol
 
DOCX
descriptive programming
Anand Dhana
 
PPT
Knockout
LearningTech
 
PPTX
Simplifying Persistence for Java and MongoDB with Morphia
MongoDB
 
groovy databases
Paul King
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rick Copeland
 
Java Development with MongoDB
Scott Hernandez
 
Morphia: Simplifying Persistence for Java and MongoDB
Jeff Yemin
 
greenDAO
Mu Chun Wang
 
Solr's Search Relevancy (Understand Solr's query debug)
Wongnai
 
MongoDB: Easy Java Persistence with Morphia
Scott Hernandez
 
11. session 11 functions and objects
Phúc Đỗ
 
Spock and Geb
Christian Baranowski
 
jQuery Rescue Adventure
Allegient
 
Ruby Development and MongoMapper (John Nunemaker)
MongoSF
 
MongoDB With Style
Gabriele Lana
 
Html5 Overview
Abdel Moneim Emad
 
Green dao
彥彬 洪
 
ElasticSearch for .NET Developers
Ben van Mol
 
descriptive programming
Anand Dhana
 
Knockout
LearningTech
 
Simplifying Persistence for Java and MongoDB with Morphia
MongoDB
 
Ad

Viewers also liked (18)

KEY
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
Guillaume Laforge
 
PDF
Chooing a Broker
Eric Patrick
 
PPTX
Bab 1 TIK kelas IX smp 18 semarang
TamaMEN27
 
PPTX
Kevin kuhn: Neurophysical Fitness & Nutrition
Kevin Kuhn
 
DOC
Soybean sub sector paln_RED
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
PDF
WatSan-Technical-Guidelines_CLP
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
PPT
Urshuul uzuuleh tuhai huuliin nemelt, uurchlult
Umguullin Mongol Umguulugch
 
PDF
USING THE TENANT PORTAL
Janice Knutson
 
DOC
Draft Enterprises training_ Bangla_22.8.11
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
PDF
Developing a Plan and Discipline
Eric Patrick
 
PDF
Milk-sector-outcome-report_December-2014_CLP
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
PDF
Lokman
Lokman Hossin
 
DOC
JANS Catherine resume July 2015
Catherine Jans
 
PDF
Эдийн засгийн ил тод байдлыг дэмжих хуулийн засгийн газрын журам
Umguullin Mongol Umguulugch
 
DOC
Evaluacion sitios-web
Daniela Gomez
 
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
Guillaume Laforge
 
Chooing a Broker
Eric Patrick
 
Bab 1 TIK kelas IX smp 18 semarang
TamaMEN27
 
Kevin kuhn: Neurophysical Fitness & Nutrition
Kevin Kuhn
 
WatSan-Technical-Guidelines_CLP
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
Urshuul uzuuleh tuhai huuliin nemelt, uurchlult
Umguullin Mongol Umguulugch
 
USING THE TENANT PORTAL
Janice Knutson
 
Draft Enterprises training_ Bangla_22.8.11
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
Developing a Plan and Discipline
Eric Patrick
 
Milk-sector-outcome-report_December-2014_CLP
S. M. Mainul Islam (Nutritionist, Agriculturist)
 
JANS Catherine resume July 2015
Catherine Jans
 
Эдийн засгийн ил тод байдлыг дэмжих хуулийн засгийн газрын журам
Umguullin Mongol Umguulugch
 
Evaluacion sitios-web
Daniela Gomez
 
Ad

Similar to Building DSLs with Groovy (20)

PDF
Rich Internet Applications con JavaFX e NetBeans
Fabrizio Giudici
 
PDF
JavaScript for Flex Devs
Aaronius
 
PPTX
Introduction to Client-Side Javascript
Julie Iskander
 
PPT
JavaScript Workshop
Pamela Fox
 
KEY
jQuery - Tips And Tricks
Lester Lievens
 
PPTX
Jquery
Zoya Shaikh
 
PDF
Green dao
Droidcon Berlin
 
PPTX
jQuery
Dileep Mishra
 
KEY
Sprout core and performance
Yehuda Katz
 
PDF
jQuery
Ivano Malavolta
 
PDF
Building Apps with MongoDB
Nate Abele
 
KEY
CouchDB on Android
Sven Haiges
 
PDF
Building Go Web Apps
Mark
 
PDF
Latinoware
kchodorow
 
PDF
Data access 2.0? Please welcome: Spring Data!
Oliver Gierke
 
KEY
Getting the most out of Java [Nordic Coding-2010]
Sven Efftinge
 
PDF
Ejb3 Struts Tutorial En
Ankur Dongre
 
PDF
Ejb3 Struts Tutorial En
Ankur Dongre
 
PDF
Ajax tutorial
Kat Roque
 
Rich Internet Applications con JavaFX e NetBeans
Fabrizio Giudici
 
JavaScript for Flex Devs
Aaronius
 
Introduction to Client-Side Javascript
Julie Iskander
 
JavaScript Workshop
Pamela Fox
 
jQuery - Tips And Tricks
Lester Lievens
 
Jquery
Zoya Shaikh
 
Green dao
Droidcon Berlin
 
Sprout core and performance
Yehuda Katz
 
Building Apps with MongoDB
Nate Abele
 
CouchDB on Android
Sven Haiges
 
Building Go Web Apps
Mark
 
Latinoware
kchodorow
 
Data access 2.0? Please welcome: Spring Data!
Oliver Gierke
 
Getting the most out of Java [Nordic Coding-2010]
Sven Efftinge
 
Ejb3 Struts Tutorial En
Ankur Dongre
 
Ejb3 Struts Tutorial En
Ankur Dongre
 
Ajax tutorial
Kat Roque
 

Recently uploaded (20)

PDF
Unlocking the Future- AI Agents Meet Oracle Database 23ai - AIOUG Yatra 2025.pdf
Sandesh Rao
 
PPTX
The Future of AI & Machine Learning.pptx
pritsen4700
 
PDF
Accelerating Oracle Database 23ai Troubleshooting with Oracle AHF Fleet Insig...
Sandesh Rao
 
PDF
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
PDF
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
PDF
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Safe Software
 
PPTX
AI in Daily Life: How Artificial Intelligence Helps Us Every Day
vanshrpatil7
 
PDF
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
AI-Cloud-Business-Management-Platforms-The-Key-to-Efficiency-Growth.pdf
Artjoker Software Development Company
 
PDF
Structs to JSON: How Go Powers REST APIs
Emily Achieng
 
PDF
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PPTX
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
PPTX
Simple and concise overview about Quantum computing..pptx
mughal641
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PDF
Software Development Methodologies in 2025
KodekX
 
PDF
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PPTX
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
Unlocking the Future- AI Agents Meet Oracle Database 23ai - AIOUG Yatra 2025.pdf
Sandesh Rao
 
The Future of AI & Machine Learning.pptx
pritsen4700
 
Accelerating Oracle Database 23ai Troubleshooting with Oracle AHF Fleet Insig...
Sandesh Rao
 
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
SparkLabs Primer on Artificial Intelligence 2025
SparkLabs Group
 
Automating ArcGIS Content Discovery with FME: A Real World Use Case
Safe Software
 
AI in Daily Life: How Artificial Intelligence Helps Us Every Day
vanshrpatil7
 
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
AI-Cloud-Business-Management-Platforms-The-Key-to-Efficiency-Growth.pdf
Artjoker Software Development Company
 
Structs to JSON: How Go Powers REST APIs
Emily Achieng
 
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
Simple and concise overview about Quantum computing..pptx
mughal641
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
Software Development Methodologies in 2025
KodekX
 
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 

Building DSLs with Groovy

  • 1. Building DSLs with Groovy: A Real World Case Study Sten Anderson
  • 2. Agenda What is a DSL? Groovy’s DSL-friendly language features Case study: City of Chicago
  • 3. What is a DSL?
  • 4. DSL: Domain Specific Language Small, Specific “Mini-language” geared toward a Domain Favor conciseness and readability in a narrow domain over Turing Completeness External • Involve Lexer/Parser • Ant, SQL Internal (Embedded DSLs/Fluent Interfaces) • Defined inside a more general purpose language • Gant/Rake DSL vs. Framework vs. API? • What is OpenGL?
  • 5. Why use Groovy to Write DSLs? Groovy is a natural extension for Java Developers • IDE support for “free” At a minimum Groovy is “less verbose Java” At a maximum Groovy lets you change the language itself • dynamic methods and properties and operator overloading Groovy has built-in DSLs (Builders)
  • 6. Groovy’s DSL-Friendly Features Reduced Syntax Closures Metaprogramming • Dynamic methods and properties • ExpandoMetaclass
  • 7. Language Feature Example: Text Adventure Interpreter
  • 8. class Game { void go(dir) { println "You go $dir" } void look() { println "You look around and see nothing" } void take(it) { println "You take the $it" } ... }
  • 10. …And Make it Groovy… new GroovyShell().evaluate(“game.${playerInput}”) go(“north”); look(); take(“dagger”);
  • 12. Metaprogramming: Dynamic Properties class Game { ... def propertyMissing(String name) { name } go north look() take dagger
  • 13. Metaprogramming: Dynamic Properties def propertyMissing(String name) { if (metaClass.respondsTo(this, name)) { this."$name"() } name } go north look take dagger
  • 15. Closures and “with” new GroovyShell().evaluate(“game.with{${playerInput}}”) 3.times { go north } look take dagger
  • 16. One more thing... (the Synonym Problem) look > You see a dagger on the ground get dagger > I don’t understand that grab dagger > I don’t understand that Just let me have the %$!#@ dagger! > I don’t understand that
  • 17. Metaprogramming: Dynamic Methods def methodMissing(String name, args) { if (["grab", “hold", "yoink"].contains(name)) { this.take(args[0]) } } look > You see a dagger on the ground grab dagger > You take the dagger You’re darn right I do! > I don’t understand that
  • 18. def methodMissing(String name, args) { def funcName = ThesaurusLookup.contains(name) if (funcName) { Game.metaClass."$name" = { item -> this.”$funcName”(item) } this.”$name”(args[0]) } } Metaprogramming: ExpandoMetaclass
  • 19. How Far We’ve Come go north look take dagger grab fish game.go(“north”); game.look(); game.take(“dagger”); game.take(“fish”); From this… To this…
  • 20. Case Study: Budget Books for Fun and (non) Profit
  • 21. The Challenge Replace Mainframe “Budget Book Generator” application Did not interact with the live budget data Over 3 hours to produce a book Very manual process (print the book, rescan as a PDF of images) Somewhat dated look
  • 22. The Solution Java SE Swing Application deployed locally via Webstart Time to produce final PDF reduced from three hours to a few minutes Operated Directly with the Budget Data PDF as native output: searchable, indexable
  • 29. How do we go from Budget Objects to Book Objects? •Departments •Organizations •Appropriations •Positions •BookSections •PageElements Can we write a DSL to create a book?
  • 30. Building Drafts with ContentBuilder interface ContentBuilder - Draft build(ContentData)
  • 32. BookSection bookSection = new BookSection ("Positions"); TableHeaderElement header = new TableHeaderElement(); header.addHeader(new TableHeader(0.05)); header.addHeader(new TableHeader(0.3, "Position", Formatter.LeftAligned)); bookSection.addPageElement(header); for (TableSection section : tableSections) { TableSectionElement sectionElement = new TableSectionElement(); sectionElement.addColumn(new TableColumn(2, section.getName()); bookSection.addPageElement(sectionElement); ...
  • 33. BookSection bookSection = new BookSection ("Positions"); TableHeaderElement header = new TableHeaderElement(); Remember to fill out your Objects in Triplicate 1 2 3
  • 34. BookSection bookSection = new BookSection ("Positions"); TableHeaderElement header = new TableHeaderElement(); header.addHeader(new TableHeader(0.05)); header.addHeader(new TableHeader(0.3, "Position", Formatter.LeftAligned)); bookSection.addPageElement(header); Declared here Added here
  • 35. BookSection bookSection = new BookSection ("Positions"); TableHeaderElement header = new TableHeaderElement(); header.addHeader(new TableHeader(0.05)); header.addHeader( new TableHeader(0.3, "Position", Formatter.LeftAligned)); •What do these arguments mean? •What is “0.05”? •What is “Position”?
  • 36. ContentBuilder: Second Attempt Java with method chaining
  • 37. Method Chaining: Return the “Current” Object public void setWidth(double width) { this.width = width;} public TableHeader setWidth(double width) { this.width = width; return this; } •Instead of: •We have:
  • 38. BookSection bookSection = new BookSection ("Positions"); bookSection.addPageElement(new TableHeader(). addHeader(new TableHeader().setWidth(0.05)). addHeader(new TableHeader(). setWidth(0.3). setName("Position"). setFormatter(Formatter.LeftAligned))); for (TableSection section : tableSections) { bookSection.addPageElement(new TableSectionElement(). addColumn(new TableColumn(). setColumnSpan(2). setName(section.getName())); ... }
  • 39. Let’s Take a Step Back… Both Java versions “work”, but are verbose and not overly readable Need better support for building complex object graphs at the language level • The structure of the code should mirror the structure of the objects (declarative syntax) • Ceremony vs. Essence We need a “Builder”
  • 40. Well, Groovy has Builders MarkupBuilder • Tag-based markup (XML, HTML) SwingBuilder • GUIs GraphicsBuilder • Java2D NodeBuilder • General Object graph (Nodes) MetaBuilder • Builder of Builders
  • 41. MarkupBuilder def htmlBuilder = new MarkupBuilder() htmlBuilder.html { body { h1 "Hi There!" } } <html> <body> <h1>Hi There!</h1> </body> </html>
  • 42. SwingBuilder def builder = new SwingBuilder() builder.frame (title:"Swing Builder Test", size:[300,100], location:[500,200]) { flowLayout() button(text:"Press Me") }.show()
  • 43. groovy.util.BuilderSupport void setParent(Object parent, Object child); Object createNode(Object name); Object createNode(Object name, Object value); Object createNode(Object name, Map attributes); Object createNode(Object name, Map attributes, Object value);
  • 44. createNode(Object name, Map attributes, Object value) h1 "Hi There!" button text:"Press Me“, visible:true
  • 45. Reduced Syntax foo [a1:“val1”, b2:true] foo a1:“val1”, b2:true foo (a1:“val1”, b2:true, {bar()}) foo (a1:“val1”, b2:true) {bar()}
  • 46. Instead of this: frame ([title:"Swing Builder Test"], { flowLayout() button(text:"Press Me") }) frame (title:"Swing Builder Test") { flowLayout() button(text:"Press Me") } You have this:
  • 47. Better Still: FactoryBuilderSupport Used by SwingBuilder Register “Factories” that know how to create your objects Convenience AbstractFactory requires only one method: • Object newInstance ( FactoryBuilderSupport builder, Object name, Object value, Map attributes )
  • 48. A Look Inside SwingBuilder public SwingBuilder() { ... registerFactory("button", new RichActionWidgetFactory(JButton)); ... }
  • 52. BookSectionBuilder (extends FactoryBuilderSupport) public BookSectionBuilder() { registerFactory("bookSection", new BookSectionFactory()); registerFactory("textElement", new PageElementFactory(TextElement.class)); ... }
  • 53. BookSectionFactory (extends AbstractFactory) public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) { return new BookSection(attributes.get(“name”).toString()); } public void setParent( FactoryBuilderSupport builder, Object parent, Object child ) { ((BookSection) parent).newBookSection((BookSection) child); }
  • 54. PageElementFactory (extends AbstractFactory) private Class<? extends PageElement> clazz; public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) { return clazz.newInstance(); } public void setParent( FactoryBuilderSupport builder, Object parent, Object child ) { ((BookSection) parent).newPageElement((PageElement) child); }
  • 55. Groovy Builder (Final Version) bookSection (name:"Positions") { tableHeaderElement(styleName:”header.large”, preTablePadding:10) { tableHeader width:0.05 tableHeader name:”Position”, width:0.3,formatter:LeftAligned } tableSections.each { section -> tableSectionElement (styleName:"table.subheader.1") { tableColumn columnSpan:2, section.name } ...
  • 56. ... positions.each { position -> tableRowElement (styleName:"table.row") { tableColumn position.accountId tableColumn position.title } } tableTotalElement(styleName:"table.total") { tableColumn columnSpan:2, "Position Total" } } // table section tableFooterElement() } // book section
  • 57. for (Position position : positions) { TableRowElement row = new TableRowElement(); row.setStyleName(“table.row”); row.addColumn(new TableColumn(position.getAccountId())); row.addColumn(new TableColumn(position.getTitle())); bookSection.newPageElement(row); } positions.each { position -> tableRowElement (styleName:"table.row") { tableColumn position.accountId tableColumn position.title } } Before: After:
  • 58. Free Things “Built-In” to the Builder Wiring of map attributes to object properties Preservation of Object hierarchy Automatic management of closure delegates so that the “Builder” is always in scope
  • 59. Closures: What’s a Delegate? def htmlBuilder = new MarkupBuilder() htmlBuilder.html { body { h1 "Hi There!" } } “html”, “body”, and “h1” don’t exist in scope, but rather are handled by the Builder htmlBuilder needs to be set as the closure’s Delegate
  • 60. …But the Methods Don’t Exist in the Builder Either def xmlBuilder = new MarkupBuilder() xmoBuilder.invoices { invoice { lineitem ... } } The Builder “pretends” to have these methods invokeMethod is overridden to write out the markup or (in the case of SwingBuilder/BookSectionBuilder) dispatch the method to the correct Factory (Metaprogramming: Dynamic Methods)
  • 62. First Write a Factory private static class ChartFactory extends AbstractFactory { public Object newInstance(...) { return new Chart(value.toString()); } public void setParent(...) { TableCell cell = (TableCell) parent; cell.addContent(child.toString()); } }
  • 63. Then Register the Factory… public BookSectionBuilder() { registerFactory("bookSection", new BookSectionFactory()); registerFactory("textElement", new PageElementFactory(TextElement.class)); ... registerFactory("chart", new ChartFactory()); }
  • 64. …And It’s Ready to use in the Builder tableRowElement { tableColumn (columnSpan:2) { chart dept.fundPercent } tableColumn { chart allFundTotal } }
  • 65. Is a DSL Right for Me? Are the people who maintain your project different than those who initially developed it? DSLs give the “gift of documentation” Is your project part of a domain? Using Dynamic Languages like Groovy make DSLs more Painless
  • 66. Resources Writing an Adventure game DSL in LISP: http://www.lisperati.com/casting.html http://groovy.codehaus.org/FactoryBuilderSupport http://docs.codehaus.org/display/GROOVY/MetaBuilder http://docs.codehaus.org/display/GROOVY/Writing+Domain-Specific+Languages http://www.agiledeveloper.com/blog/PermaLink.aspx?guid=ce021e6d-b0c2-4d83-8b69- c163c290983d http://blogs.citytechinc.com/sanderson/ King’s Quest and Zork images taken from Wikipedia.
  • 67. “Groovy is like Gravy, except that it has the „Groove‟ in it.” -Meredith Anderson (5 Year Old)