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-Speciļ¬c Languages
ā¢ Powerful processing primitives, OO abilities and an Ant DSL
ā¢ Works with existing Java objects and libraries
4. Setup
ā¢ Easy to ļ¬nd 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. Conļ¬gSlurper
ā¢ Utility class within Groovy for writing properties ļ¬le
ā¢ Unlike regular Java properties ļ¬les Conļ¬gSlurper scripts support
native Java types and are structured like a tree.
28. Reading conļ¬guration with Conļ¬gSlurper
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 conļ¬guration with Conļ¬gSlurper
//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 conļ¬guration with Conļ¬gSlurper
//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 conļ¬guration with Conļ¬gSlurper
//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"
}
}