2. Outline
• Java vs Groovy short cuts
• XML parsing with Groovy
• XML generating with Groovy
• How to access EO inside Groovy
3. Java vs Groovy
• Java Virtual Machine thinks Groovy is Java
• Inspired by Python, Ruby and Smalltalk
• Java developers will have almost-zero learning curve
• Supports Domain-Specific Languages
• Powerful processing primitives, OO abilities and an Ant DSL
• Works with existing Java objects and libraries
4. Setup
• Easy to find and learn at: http://groovy.codehaus.org/
• Eclipse plug-in at: http://groovy.codehaus.org/Eclipse+Plugin
• groovyShell
• groovy-all-1.7.0.jar
7. Closures
Functions that are first class objects - a chunk of code that can be passed around as if it
were a string or an integer.
square = { it * it }
square(4)
//RESULT
16
[ 1, 2, 3, 4 ].collect(square)
//RESULT
[ 1, 4, 9, 16 ]
printMapClosure = { key, value -> println key + "t= " + value }
[ "Name" : "John", "Address" : "Here", "Likes" : "WOWODC" ].each(printMapClosure)
//RESULT
Name = John
Address = Here
Likes = WOWODC
9. Meta Class - Properties
Intercepting Property Accesses
class MyClass{
def greeting = 'accessed greeting directly'
Object getProperty(String property) { "read from property $property" }
void setProperty(String property, Object newValue) { throw new Exception("wrote to property $property") }
}
def mine = new MyClass()
//try to read ‘greeting’ property
println mine.greeting
//RESULT
read from property greeting
//try to set ‘greeting’ property
try { mine.greeting = 'hi' } catch(e) { println e.message }
//RESULT
wrote to property greeting
//we can access a property directly using .@ syntax
println mine.@greeting
//RESULT
accessed greeting directly
10. Meta Class - At Run-time
Adding new property and method to a class during Run-time
class A{}
//add new property ‘hi’
A.metaClass.hi = 'Hi!!!'
def a1 = new A()
println a1.hi
//RESULT
Hi!!!
//add new method ‘hello’
A.metaClass.hello = {-> "Hello!"}
def a2 = new A()
println a2.hello()
//RESULT
Hello!
11. XML Parsing
• XmlParser - supports GPath expressions for XML documents
• XmlSlurper - lower overheads than XmlParser due to lazy
evaluation
12. XML Input sample
class XmlExamples {
static def CAR_RECORDS = '''
<records>
<car name='HSV Maloo' make='Holden' year='2006'>
<country>Australia</country>
<record type='speed'>Production Pickup Truck with speed of 271kph</record>
</car>
<car name='P50' make='Peel' year='1962'>
<country>Isle of Man</country>
<record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg in weight</record>
</car>
<car name='Royale' make='Bugatti' year='1931'>
<country>France</country>
<record type='price'>Most Valuable Car at $15 million</record>
</car>
</records>
'''
}
13. XmlParser vs XmlSlurper
def records_p = new XmlParser().parseText(XmlExamples.CAR_RECORDS)
def records_s = new XmlSlurper().parseText(XmlExamples.CAR_RECORDS)
def allRecords_p = records_p.car.size()
assert allRecords_p == 3
def allRecords_s = records_s.car.size()
assert allRecords_s == 3
def allNodes = records_p.depthFirst().size()
assert allNodes_p == 10
def allNodes_s = records_s.depthFirst().collect{ it }.size()
assert allNodes_s == 10 <records>
<car name='HSV Maloo' make='Holden' year='2006'>
<country>Australia</country>
def firstRecord_p = records_p.car[0] <record type='speed'>
def firstRecord_s = records_s.car[0] Production Pickup Truck with speed of 271kph
</record>
</car>
assert 'car' == firstRecord_p.name() <car name='P50' make='Peel' year='1962'>
<country>Isle of Man</country>
assert 'car' == firstRecord_s.name()
<record type='size'>
Smallest Street-Legal Car at 99cm wide and 59 kg in weight
assert 'Holden' == firstRecord_p.'@make' </record>
</car>
assert 'Holden' == firstRecord_s.@make.text() <car name='Royale' make='Bugatti' year='1931'>
<country>France</country>
assert 'Australia' == firstRecord_p.country.text() <record type='price'>
Most Valuable Car at $15 million
assert 'Australia' == firstRecord_s.country.text() </record>
</car>
</records>
14. XmlParser vs XmlSlurper
// 2 cars have an 'e' in the make
assert records_p.car.findAll{ it.'@make'.contains('e') }.size() == 2
// option 1
assert records_s.car.findAll{ it.@make.text().contains('e') }.size() == 2
// option 2
assert records_s.car.findAll{ it.@make =~ '.*e.*' }.size() == 2
<records>
<car name='HSV Maloo' make='Holden' year='2006'>
<country>Australia</country>
<record type='speed'>
Production Pickup Truck with speed of 271kph
</record>
</car>
<car name='P50' make='Peel' year='1962'>
<country>Isle of Man</country>
<record type='size'>
Smallest Street-Legal Car at 99cm wide and 59 kg in weight
</record>
</car>
<car name='Royale' make='Bugatti' year='1931'>
<country>France</country>
<record type='price'>
Most Valuable Car at $15 million
</record>
</car>
</records>
15. XmlSlurper
println records.depthFirst().grep{ it.@type != '' }.'@type'*.text()
//Result
[speed, size, price]
println records.'**'.grep{ it.@type != '' }.'@type'*.text()
//Result
[speed, size, price]
def countryOne = records.car[1].country
println countryOne.parent().@make.text()
//Result
Peel
<records>
println countryOne.'..'.@make.text() <car name='HSV Maloo' make='Holden' year='2006'>
<country>Australia</country>
//Result
<record type='speed'>
Peel Production Pickup Truck with speed of 271kph
</record>
// names of cars with records sorted by year </car>
<car name='P50' make='Peel' year='1962'>
println records.car.list().sort{
<country>Isle of Man</country>
it.@year.toInteger() <record type='size'>
}.'@name'*.text() Smallest Street-Legal Car at 99cm wide and 59 kg in weight
//Result </record>
</car>
[Royale, P50, HSV Maloo]
<car name='Royale' make='Bugatti' year='1931'>
<country>France</country>
// names of countries with ‘s’ in the name <record type='price'>
println records.'**'.grep{ Most Valuable Car at $15 million
</record>
it.@type =~ 's.*'
</car>
}*.parent().country*.text() </records>
//Result
[Australia, Isle of Man]
16. XmlSlurper
class XmlExamples {
static def CF_MESSAGE = '''
<cf:Message xmlns:cf='http://controlsforce.com/schema/message.xsd'>
<cf:Properties>
<cf:Name>ck_mstr</cf:Name>
<cf:Process>FREUDENBERG_DEMO</cf:Process>
<cf:User></cf:User>
<cf:Date>Fri Dec 25 17:37:55 EST 2009</cf:Date>
<cf:MessageCount>0</cf:MessageCount>
<cf:UID>uniqueId-0</cf:UID>
</cf:Properties>
</cf:Message>
'''
}
def xml = new XmlSlurper().parseText(XmlExamples.CF_MESSAGE)
xml.Properties.children().each {
println "${it.name()} -> ${it.text()}"
}
Name -> ck_mstr
Process -> FREUDENBERG_DEMO
User ->
Date -> Fri Dec 25 17:37:55 EST 2009
MessageCount -> 0
UID -> uniqueId-0
17. XML Generation
• Building XML using simple Java
• StreamingMarkupBuilder - A builder class for creating XML markup
18. Java
//assuming we have a Writer created already
writer.write("<root>");
writer.write(“<a a1=‘one’>”);
writer.write("<b>3 < 5</b>");
writer.write("<c a2=‘two’>blah</c>");
writer.write("<d>");
writer.write("<f>hello</f>");
writer.write("</d>");
writer.write("</a>");
writer.write("</root>");
//Result
<root>
<a a1='one'>
<b>3 < 5</b>
<c a2='two'>blah</c>
<d>
<f>hello</f>
</d>
</a>
</root>
23. Setup
• Main Component with a set of tables that will list the data from
each table
• Main.java replaced with Main.groovy (just for some extra fun)
• MySQL Database with three tables:
• Employee Employee
•
age Job
Manager job 0..1* employees
0..1*
•
manager Manager name
Job name employees owner
salary jobs 0..1 *
name
27. ConfigSlurper
• Utility class within Groovy for writing properties file
• Unlike regular Java properties files ConfigSlurper scripts support
native Java types and are structured like a tree.
28. Reading configuration with ConfigSlurper
def config = new ConfigSlurper().parse (configFile) def configFile = '''
//get the all of the config data WOWODC {
println config.WOWODC Bonus {
testId = 1
//Result
testName = "Hello Bonus Test string"
["Bonus":["testId":1, "testName":"Hello Bonus Test string"],
"BonusList":["key1":"val1", "hi":"hello"]] }
//get only the Bonus section BonusList {
println config.WOWODC.Bonus key1 = 'val1'
hi = 'hello'
//Result }
["testId":1, "testName":"Hello Bonus Test string"]
}
//get only the testId value '''
println config.WOWODC.Bonus.testId
//Result
1
//list keys and values under BonusList section
config.WOWODC.BonusList.each {println "${it.key} = ${it.value}"}
//Result
key1 = val1
hi = hello
29. Updating configuration with ConfigSlurper
//get the all of the config data WOWODC {
config.Hi.GoodBuy.say = 'Hello' Bonus {
testId = 1
def sw = new StringWriter() testName = "Hello Bonus Test string"
println config?.writeTo(sw).toString() }
//Result --> BonusList {
WOWODC { key1 = 'val1'
Bonus { hi = 'hello'
testId=1 }
testName="Hello Bonus Test string" }
}
BonusList {
key1="val1"
hi="hello"
}
}
Hi.GoodBuy.say="Hello" // got updated
30. Updating configuration with ConfigSlurper
//get the all of the config data WOWODC {
config.Hi.GoodBuy.tell = 'Hi' Bonus {
testId=1
def sw = new StringWriter() testName="Hello Bonus Test string"
println config?.writeTo(sw).toString() }
BonusList {
//Result key1="val1"
WOWODC { hi="hello"
Bonus { }
testId=1 }
testName="Hello Bonus Test string" Hi.GoodBuy.say="Hello"
}
BonusList {
key1="val1"
hi="hello"
}
}
Hi {
GoodBuy {
say="Hello"
tell="Hi" // got updated
}
}
31. Updating configuration with ConfigSlurper
//get the all of the config data WOWODC {
config.WOWODC.Bonus.testId = 2 Bonus {
testId=1
def sw = new StringWriter() testName="Hello Bonus Test string"
println config?.writeTo(sw).toString() }
BonusList {
//Result --> key1="val1"
WOWODC { hi="hello"
Bonus { }
testId=2 // got updated }
testName="Hello Bonus Test string" Hi {
} GoodBuy {
BonusList { say="Hello"
key1="val1" tell="Hi"
hi="hello" }
} }
}
Hi {
GoodBuy {
say="Hello"
tell="Hi"
}
}