Your SlideShare is downloading. ×
Groovy Tutorial
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Groovy Tutorial

9,326
views

Published on

Paul King's latest Groovy Tutorial. Feel free to use it for non-commercial use at your Groovy User Group or within your company.

Paul King's latest Groovy Tutorial. Feel free to use it for non-commercial use at your Groovy User Group or within your company.

Published in: Technology, Education

3 Comments
42 Likes
Statistics
Notes
No Downloads
Views
Total Views
9,326
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
633
Comments
3
Likes
42
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. © ASERT 2006-2010 Groovy Tutorial Scripting on the JVM Dr Paul King paulk@asert.com.au @paulk_asert ASERT, Australia
  • 2. Topics  Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 2
  • 3. What is Groovy? • “Groovy is like a super version of Java. It can leverage Java's enterprise capabilities but also has cool productivity features like closures, DSL support, builders and dynamic typing.” © ASERT 2006-2010 Groovy = Java – boiler plate code + mostly dynamic typing + closures + domain specific languages + builders + metaprogramming + GDK library ESDC 2010 - 3
  • 4. Groovy Goodies Overview • Fully object oriented • Closures: reusable and assignable pieces of code • Operators can be • GPath: efficient overloaded © ASERT 2006-2010 object navigation • Multimethods • GroovyBeans • Literal declaration for • grep and switch lists (arrays), maps, ranges and regular • Templates, builder, expressions swing, Ant, markup, XML, SQL, XML-RPC, Scriptom, Grails, tests, Mocks ESDC 2010 - 4
  • 5. Growing Acceptance … A slow and steady start but now gaining in momentum, maturity and mindshare Now free
  • 6. … Growing Acceptance … © ASERT 2006-2010 ESDC 2010 - 6
  • 7. … Growing Acceptance … © ASERT 2006-2010 Groovy and Grails downloads: 70-90K per month and growing ESDC 2010 - 7
  • 8. … Growing Acceptance … © ASERT 2006-2010 Source: http://www.micropoll.com/akira/mpresult/501697-116746 Source: http://www.grailspodcast.com/ ESDC 2010 - 8
  • 9. … Growing Acceptance … © ASERT 2006-2010 http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes http://www.java.net ESDC 2010 - 9
  • 10. … Growing Acceptance … What alternative JVM language are you using or intending to use © ASERT 2006-2010 http://www.leonardoborges.com/writings ESDC 2010 - 10
  • 11. … Growing Acceptance … © ASERT 2006-2010 http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com) ESDC 2010 - 11
  • 12. … Growing Acceptance … © ASERT 2006-2010 http://pollpigeon.com/jsf-grails-wicket/r/25665/ ESDC 2010 - 12
  • 13. … Growing Acceptance © ASERT 2006-2010 ESDC 2010 - 13
  • 14. The Landscape of JVM Languages mostly dynamic typing © ASERT 2006-2010 Dynamic features call for dynamic types Java bytecode calls for static types The terms “Java Virtual Machine” and “JVM” mean a Virtual Machine for the Java™ platform. ESDC 2010 - 14
  • 15. Groovy Starter System.out.println("Hello, World!"); // optional semicolon, println 'Hello, World!' // System.out, brackets, // main() method, class defn def name = 'Guillaume' // dynamic typing println "$name, I'll get the car." // GString String longer = """${name}, the car is in the next row.""" // multi-line string © ASERT 2006-2010 // with static typing assert 0.5 == 1/2 // BigDecimal equals() def printSize(obj) { // optional duck typing print obj?.size() // safe dereferencing } def pets = ['ant', 'bee', 'cat'] // native list syntax pets.each { pet -> // closure support assert pet < 'dog' // overloading '<' on String } // or: for (pet in pets)... ESDC 2010 - 15
  • 16. A Better Java... import java.util.List; import java.util.ArrayList; class Erase { private List removeLongerThan(List strings, int length) { This code List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { is valid String s = (String) strings.get(i); Java and if (s.length() <= length) { result.add(s); valid Groovy } } © ASERT 2006-2010 return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Based on an Erase e = new Erase(); example by List shortNames = e.removeLongerThan(names, 3); Jim Weirich System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { & Ted Leung String s = (String) shortNames.get(i); System.out.println(s); } } } ESDC 2010 - 16
  • 17. ...A Better Java... import java.util.List; import java.util.ArrayList; class Erase { private List removeLongerThan(List strings, int length) { Do the List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { semicolons String s = (String) strings.get(i); add anything? if (s.length() <= length) { result.add(s); And shouldn‟t } } we us more © ASERT 2006-2010 } return result; modern list public static void main(String[] args) { notation? List names = new ArrayList(); names.add("Ted"); names.add("Fred"); Why not names.add("Jed"); names.add("Ned"); System.out.println(names); import common Erase e = new Erase(); libraries? List shortNames = e.removeLongerThan(names, 3); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } ESDC 2010 - 17
  • 18. ...A Better Java... class Erase { private List removeLongerThan(List strings, int length) { List result = new ArrayList() for (String s in strings) { if (s.length() <= length) { result.add(s) } } return result } public static void main(String[] args) { © ASERT 2006-2010 List names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) Erase e = new Erase() List shortNames = e.removeLongerThan(names, 3) System.out.println(shortNames.size()) for (String s in shortNames) { System.out.println(s) } } } ESDC 2010 - 18
  • 19. ...A Better Java... class Erase { private List removeLongerThan(List strings, int length) { List result = new ArrayList() for (String s in strings) { if (s.length() <= length) { result.add(s) Do we need } the static types? } return result Must we always } have a main public static void main(String[] args) { method and © ASERT 2006-2010 List names = new ArrayList() names.add("Ted"); names.add("Fred") class definition? names.add("Jed"); names.add("Ned") System.out.println(names) How about Erase e = new Erase() List shortNames = e.removeLongerThan(names, 3) improved System.out.println(shortNames.size()) consistency? for (String s in shortNames) { System.out.println(s) } } } ESDC 2010 - 19
  • 20. ...A Better Java... def removeLongerThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() <= length) { result.add(s) } } return result } © ASERT 2006-2010 names = new ArrayList() names.add("Ted") names.add("Fred") names.add("Jed") names.add("Ned") System.out.println(names) shortNames = removeLongerThan(names, 3) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) } ESDC 2010 - 20
  • 21. ...A Better Java... def removeLongerThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() <= length) { result.add(s) Shouldn‟t we } } have special return result notation for lists? } And special © ASERT 2006-2010 names = new ArrayList() facilities for names.add("Ted") names.add("Fred") list processing? names.add("Jed") Is „return‟ names.add("Ned") System.out.println(names) needed at end? shortNames = removeLongerThan(names, 3) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) } ESDC 2010 - 21
  • 22. ...A Better Java... def removeLongerThan(strings, length) { strings.findAll{ it.size() <= length } } names = ["Ted", "Fred", "Jed", "Ned"] System.out.println(names) shortNames = removeLongerThan(names, 3) System.out.println(shortNames.size()) shortNames.each{ System.out.println(s) } © ASERT 2006-2010 ESDC 2010 - 22
  • 23. ...A Better Java... def removeLongerThan(strings, length) { strings.findAll{ it.size() <= length } } Is the method names = ["Ted", "Fred", "Jed", "Ned"] now needed? System.out.println(names) shortNames = removeLongerThan(names, 3) Easier ways to System.out.println(shortNames.size()) use common shortNames.each{ System.out.println(s) } methods? © ASERT 2006-2010 Are brackets required here? ESDC 2010 - 23
  • 24. ...A Better Java... names = ["Ted", "Fred", "Jed", "Ned"] println names shortNames = names.findAll{ it.size() <= 3 } println shortNames.size() shortNames.each{ println it } © ASERT 2006-2010 ESDC 2010 - 24
  • 25. ...A Better Java names = ["Ted", "Fred", "Jed", "Ned"] println names shortNames = names.findAll{ it.size() <= 3 } println shortNames.size() shortNames.each{ println it } © ASERT 2006-2010 [Ted, Fred, Jed, Ned] 3 Ted Jed Ned ESDC 2010 - 25
  • 26. Better JavaBeans... import java.math.BigDecimal; ... public class BikeJavaBean { public void setModel(String model) { private String manufacturer; this.model = model; private String model; } private int frame; private String serialNo; public int getFrame() { private double weight; return frame; private String status; } private BigDecimal cost; public String getSerialNo() { public BikeJavaBean(String manufacturer, String model, return serialNo; int frame, String serialNo, } double weight, String status) { this.manufacturer = manufacturer; public void setSerialNo(String serialNo) { this.model = model; this.serialNo = serialNo; this.frame = frame; } this.serialNo = serialNo; © ASERT 2006-2010 this.weight = weight; public double getWeight() { this.status = status; return weight; } } public String toString() { public void setWeight(double weight) { return "Bike:“ + this.weight = weight; “n manufacturer -- " + manufacturer + } "n model -- " + model + "n frame -- " + frame + public String getStatus() { "n serialNo -- " + serialNo + return status; "n"; } } public void setStatus(String status) { public String getManufacturer() { this.status = status; return manufacturer; } } public BigDecimal getCost() { public void setManufacturer(String manufacturer) { return cost; this.manufacturer = manufacturer; } } public void setCost(BigDecimal cost) { public String getModel() { this.cost = cost.setScale(3, BigDecimal.ROUND_HALF_UP); return model; } } } ... Modified example from: http://www.ibm.com/developerworks/library/j-pg09196.html ESDC 2010 - 26
  • 27. ...Better JavaBeans... import java.math.BigDecimal; ... public class BikeJavaBean { public void setModel(String model) { Auto private String manufacturer; this.model = model; private String model; private int frame; } getters? private String serialNo; private double weight; private String status; public int getFrame() { } return frame; Auto private BigDecimal cost; public String getSerialNo() { setters? public BikeJavaBean(String manufacturer, String model, return serialNo; int frame, String serialNo, double weight, String status) { } Auto this.manufacturer = manufacturer; this.model = model; this.frame = frame; } this.serialNo = serialNo; construction? public void setSerialNo(String serialNo) { this.serialNo = serialNo; © ASERT 2006-2010 this.weight = weight; public double getWeight() { this.status = status; return weight; } } public String toString() { public void setWeight(double weight) { return "Bike:“ + this.weight = weight; “n manufacturer -- " + manufacturer + } "n model -- " + model + "n frame -- " + frame + public String getStatus() { "n serialNo -- " + serialNo + return status; "n"; } } public void setStatus(String status) { public String getManufacturer() { this.status = status; return manufacturer; } } public BigDecimal getCost() { public void setManufacturer(String manufacturer) { return cost; this.manufacturer = manufacturer; } } public void setCost(BigDecimal cost) { public String getModel() { this.cost = cost.setScale(3, BigDecimal.ROUND_HALF_UP); return model; } } } ... ESDC 2010 - 27
  • 28. ... Better JavaBeans ... class BikeGroovyBean { String manufacturer, model, serialNo, status final Integer frame Double weight BigDecimal cost public void setCost(BigDecimal newCost) { cost = newCost.setScale(3, BigDecimal.ROUND_HALF_UP) © ASERT 2006-2010 } public String toString() { return """Bike: manufacturer -- $manufacturer model -- $model More complicated than frame -- $frame normal because we want: serialNo -- $serialNo * Read only frame """ * Custom setter for cost } * Custom toString() } ESDC 2010 - 28
  • 29. ... Better JavaBeans class BikeGroovyBean { String manufacturer, model, serialNo, status Integer frame Double weight BigDecimal cost } © ASERT 2006-2010 In more typical cases it is often as simple as this. ESDC 2010 - 29
  • 30. Topics • Introduction  Simple Data Types  Numbers, Other 'Primitives', Strings, Regexs, Dates & Times • Collective Data Types • Control Structures • © ASERT 2006-2010 Closures and Builders • OO • GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 30
  • 31. Numbers • Java numbers byte b1 = 42; Byte b2 = 42 short s1 = 20000; Short s2 = 20000 – But with primitive int i1 = 1000000000 types autoboxed to Integer i2 = 1000000000 def i3 = 42 equivalent wrapper long l1 = 1000000000000000000 types Long l2 = 1000000000000000000 def l3 = 42L – Sizes supported: © ASERT 2006-2010 Byte, Short, float f1 = 3.5f; Float f2 = 3.5 Integer, Long, def f3 = 3.5f double d1 = 3.5e40; Double d2 = 3.5e40 Float, Double, def d3 = 3.5d BigInteger, BigInteger bi = 30g BigDecimal BigDecimal bd1 = 3.5g def bd2 = 3.5 – Some special treatment when assert 2 instanceof Integer assert !2.equals(4) handling arrays assert 3.5 instanceof BigDecimal and in Interfaces assert (7/2) instanceof BigDecimal ESDC 2010 - 31
  • 32. Parsing Strings to Numbers def string = "300" assert string.isNumber() // check if string appears to be a number // OK, now convert to number in different ways def i4 = string.toInteger() def i5 = string as int def i6 = string as Integer def i7 = new Integer(string) def i8 = Integer.parseInt(string) def i9 = (int) java.text.NumberFormat.instance.parse(string) // Long © ASERT 2006-2010 string = "300.5" def n1 = java.text.NumberFormat.instance.parse(string) // Double def n2 = string.toFloat() def n3 = string.toDouble() def n4 = string.toBigDecimal() integerPattern = /^[+-]?d+$/ // regex notation assert '-36' ==~ integerPattern // explained later assert !('abc' =~ integerPattern) decimalPattern = /^-?(?:d+(?:.d*)?|.d+)$/ assert '37.5' ==~ decimalPattern ESDC 2010 - 32
  • 33. Other 'primitive' types • boolean and char – Again replaced by wrapper counterparts automatically – We'll see a little bit more about chars under Strings – There is a notion called 'Groovy Truth' whereby any expression can be used where Java would require a © ASERT 2006-2010 boolean and the expression will be evaluated • The numbers 0 and 0.0 and null objects return false • Empty Strings, Lists, Maps, Matchers, ... return false • Other objects return true Found 4 vowels: def vowels = 'aeiou' [e, i, o, u] def sentences = ['The quick brown fox', 'Flyby'] Nothing found sentences.each { sentence -> def found = sentence.toList().intersect(vowels.toList()) if (found) println "Found ${found.size()} vowels: $found" else println 'Nothing found' } ESDC 2010 - 33
  • 34. Strings... • Several forms // normal strings def firstname = 'Kate' – Single quotes for assert firstname instanceof String simple strings def surname = "Bush" assert firstname * 2 == 'KateKate' – Double quotes for def fullname = GStrings which firstname + ' ' + surname assert fullname == 'Kate Bush' support variable © ASERT 2006-2010 expansion // GString fullname = "$firstname $surname" – Slashy strings assert fullname instanceof GString behave like assert fullname == 'Kate Bush' GStrings but assert fullname - firstname == ' Bush' assert fullname.padLeft(10) == preserve ' Kate Bush' backslashes (great for regex and // indexing (including ranges) assert fullname[0..3] == firstname directory names) assert fullname[-4..-1] == surname – Multi-line versions assert fullname[5, 3..1] == 'Beta' ESDC 2010 - 34
  • 35. …Strings… // Multi-line strings def tt = ''' def lines = tt.split('n') She sells, sea shells assert lines.size() == 2 By the sea shore''' lines = address.readLines() assert lines.size() == 3 assert tt instanceof String assert lines[-1] == 'New York' assert tt.startsWith('She') assert tt.endsWith('shore') // slashy string: (almost) no escaping assert tt.contains('n') def path = /C:WindowsSystem32/ © ASERT 2006-2010 def address = """ def plain = 'nrtbf$' $fullname assert plain.size() == 7 123 First Ave def slashy = /nrtbf$/ New York assert slashy.size() == 14 """.trim() // late binding trick with closures println """ fullname = "${-> firstname} $surname" --------------------- assert fullname == 'Kate Bush' | $fullname | firstname = 'George' | 123 First Ave | surname = 'Clooney' | New York | assert fullname == 'George Bush' --------------------- """ ESDC 2010 - 35
  • 36. …Strings... // more substrings (minus removes first occurrence) string = 'hippopotamus' assert string - 'hippo' - 'mus' + 'to' == 'potato' assert string.replace('ppopotam','bisc') == 'hibiscus' // processing characters (more on collections later) def letters = 'apple'.toList() assert letters == ['a', 'p', 'p', 'l', 'e'] © ASERT 2006-2010 assert letters[0] instanceof String letters = 'apple'.toList()*.toCharacter() def expected = ['a', 'p', 'p', 'l', 'e'] as char[] assert letters == expected assert letters[0] instanceof Character assert letters instanceof List assert expected instanceof char[] string = "an apple a day" assert string.toList().unique().sort().join() == ' adelnpy' ESDC 2010 - 36
  • 37. …Strings... // reversing chars/words assert 'string'.reverse() == 'gnirts' assert 'two words'.split().reverse().join(' ') == 'words two' string = 'Yoda said, "can you see this?"' revwords = string.split(' ').reverse().join(' ') assert revwords == 'this?" see you "can said, Yoda' © ASERT 2006-2010 revwords = string.replaceAll(/(w*)/) { all, match -> match.reverse() } assert revwords == 'adoY dias, "nac uoy ees siht?"' revwords = string.replaceAll(/(w*) (w*)(W*)/ * 3, '$2 $1$3$8 $7$6$5 $4$9') assert revwords == 'said Yoda, "this see you can?"' • Find out more through Javadoc, GDK doc – Can also lookup GString but usually no need ESDC 2010 - 37
  • 38. …Strings... • javadoc © ASERT 2006-2010 ESDC 2010 - 38
  • 39. …Strings • groovy-jdk (just the additions) © ASERT 2006-2010 ESDC 2010 - 39
  • 40. Regular Expressions... assert "Hello World!" =~ /Hello/ // Find operator assert "Hello World!" ==~ /Hellob.*/ // Match operator def p = ~/Hellob.*/ // Pattern operator assert p.class.name == 'java.util.regex.Pattern' def input = "Voting is open between 01 Nov 2008 and 04 Nov 2008" def dateFormat = /dd? ... d{4}/ def matches = (input =~ dateFormat) © ASERT 2006-2010 assert matches[1] == '04 Nov 2008' 01 Nov 2008 matches.each { m -> println m } 04 Nov 2008 input.eachMatch(dateFormat) { m -> println m } assert input.findAll(dateFormat) == ['01 Nov 2008', '04 Nov 2008'] println input.replaceAll(dateFormat, '?/?/?') Voting is open between ?/?/? and ?/?/? ESDC 2010 - 40
  • 41. ...Regular Expressions // "Voting is open between 01 Nov 2008 and 04 Nov 2008" def dateFormatGroups = /(dd?) (...) (d{4})/ def matchingGroups = (input =~ dateFormatGroups) assert matchingGroups[1][1] == '04' matchingGroups.each { all, d, m, y -> println "$d/$m/$y" } © ASERT 2006-2010 input.eachMatch(dateFormatGroups) { all, d, m, y -> println "$d/$m/$y" } 01/Nov/2008 04/Nov/2008 assert input.findAll(dateFormatGroups) { full, d, m, y -> "$m $d of $y" } == ['Nov 01 of 2008', 'Nov 04 of 2008'] println input.replaceAll(dateFormatGroups) { full, d, m, y -> "$m $d ????" } Voting is open between Nov 01 ???? and Nov 04 ???? ESDC 2010 - 41
  • 42. Abbreviated regex notation Symbol Meaning . Any character ^ Start of line (or start of document, when in single-line mode) $ End of line (or end of document, when in single-line mode) d Digit character D Any character except digits s Whitespace character S Any character except whitespace w Word character © ASERT 2006-2010 W Any character except word characters b Word boundary () Grouping (x|y) x or y as in (Groovy|Java|Ruby) x* Zero or more occurrences of x x+ One or more occurrences of x x? Zero or one occurrence of x x{m,n} At least m and at most n occurrences of x x{m} Exactly m occurrences of x [a-f] Character class containing the characters a, b, c, d, e, f [^a] Character class containing any character except a (?=regex) Positive lookahead (?<=text) Positive lookbehind ESDC 2010 - 42
  • 43. Dates... • Similar to Java – Use java.util.Date, java.util.Calendar, java.sql.Date, ... – Can use static imports to help – Or 3rd party package like Joda Time • Special support for changing times: © ASERT 2006-2010 date2 = date1 + 1 // day date2 = date1 + 1.week – 3.days + 6.hours • Utility methods for date input and output – Date.parse(formatString, dateString) – date.format(formatString) – date.timeString, date.dateString, date.dateTimeString • Potentially will change with JSR 310 ESDC 2010 - 43
  • 44. ... Dates... import static java.util.Calendar.getInstance as now import java.util.GregorianCalendar as D import org.codehaus.groovy.runtime.TimeCategory println new D(2007,11,25).time println now().time Tue Dec 25 00:00:00 EST 2007 Thu Jun 28 10:10:34 EST 2007 def date = new Date() + 1 Fri Jun 29 10:10:35 EST 2007 © ASERT 2006-2010 println date Tue Jul 17 11:10:35 EST 2007 Date was Jun/03/1998 use(TimeCategory) { println new Date() + 1.hour + 3.weeks - 2.days } dateStr = "1998-06-03" date = Date.parse("yyyy-MM-dd", dateStr) println 'Date was ' + date.format("MMM/dd/yyyy") def holidays = boxingDay..newYearsEve ESDC 2010 - 44
  • 45. ... Dates import static java.util.Calendar.* def nowCal = Calendar.instance y = nowCal.get(YEAR) Date nowDate = nowCal.time def (m, d) = [nowDate[MONTH] + 1, nowDate[DATE]] println "Today is $d $m $y" nowCal.set DATE, 1 nowCal.set MONTH, FEBRUARY println "Changed to $nowCal.time" © ASERT 2006-2010 Today is 26 9 2008 Changed to Fri Feb 01 23:05:20 EST 2008 cal = Calendar.instance cal.set 1988, APRIL, 4, 0, 0, 0 date = cal.time def (doy, woy, y) = [DAY_OF_YEAR, WEEK_OF_YEAR, YEAR].collect{ date[it] } println "$date is day $doy and week $woy of year $y" Mon Apr 04 00:00:00 EST 1988 is day 95 and week 15 of year 1988 ESDC 2010 - 45
  • 46. Operator Overloading... © ASERT 2006-2010 ESDC 2010 - 46
  • 47. ...Operator Overloading... © ASERT 2006-2010 ESDC 2010 - 47
  • 48. ...Operator Overloading © ASERT 2006-2010 • Can be used with your own types too shoppingCart << clickedItem ESDC 2010 - 48
  • 49. Topics • Introduction • Simple Data Types  Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 49
  • 50. Better Lists, Maps, Ranges • Lists/Collections – Special literal syntax – Additional common methods (operator overloading) def list = [3, new Date(), 'Jan'] assert list + list == list * 2 • Maps © ASERT 2006-2010 – Special literal syntax & GDK methods def map = [a: 1, b: 2] assert map['a'] == 1 && map.b == 2 • Ranges – Special syntax for various kinds of ranges def letters = 'a'..'z' def numbers = 0..<10 assert letters.size() + numbers.size() == 36 ESDC 2010 - 50
  • 51. Topics • Introduction • Simple Data Types • Collective Data Types  Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 51
  • 52. Better Control Structures: Branching/Loops • Branching and loops • Loop-like Closures for (int i = 0; i < n; i++) { } myMap.each { key, value -> for (Type item : iterable) { } println "$key : $value" for (item in iterable) { } } // if (condition) ... // each, every, collect, any, // else if (condition) ... // inject, find, findAll, // else ... // upto, downto, times, grep, © ASERT 2006-2010 // throw, try...catch...finally // reverseEach, eachMatch, // for, while // eachWithIndex, eachLine // d = a ? b : c // eachFile, eachFileMatch, // a = b ?: c // eachByte, combinations ... // d = a?.b?.c • Groovy Truth words = ['bob', 'alpha', 'ehcache', if (1) // ... 'rotator', 'omega', 'reviver'] if (object) // ... bigPalindromes = words.findAll{ w -> if (collection) // ... w == w.reverse() && w.size() > 4} if (map) // ... assert bigPalindromes == if (matcher) // ... ['ehcache', 'rotator', 'reviver'] ESDC 2010 - 52
  • 53. Better Control Structures: Switch switch (10) { case 0 : assert false ; break case 0..9 : assert false ; break case [8,9,11] : assert false ; break case Float : assert false ; break case {it%3 == 0} : assert false ; break case ~/../ : assert true ; break © ASERT 2006-2010 default : assert false ; break } • Extensible – Implement your own isCase() method ESDC 2010 - 53
  • 54. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures  Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 54
  • 55. Using Closures... • Traditional mainstream languages – Data can be stored in variables, passed around, combined in structured ways to form more complex data; code stays put where it is defined • Languages supporting closures – Data and code can be stored in variables, passed © ASERT 2006-2010 around, combined in structured ways to form more complex algorithms and data doubleNum = { num -> num * 2 } println doubleNum(3) // => 6 processThenPrint = { num, closure -> num = closure(num); println "num is $num" } processThenPrint(3, doubleNum) // => num is 6 processThenPrint(10) { it / 2 } // => num is 5 ESDC 2010 - 55
  • 56. ...Using Closures... import static Math.* Algorithm piE differs by 1.0206946399193839E-11 Algorithm piF differs by 1.0070735356748628E-9 Algorithm piC differs by 2.668102068170697E-7 piA = { 22 / 7 } Algorithm piD differs by 4.813291008076703E-5 piB = { 333/106 } Algorithm piB differs by 8.321958979307098E-5 piC = { 355/113 } Algorithm piA differs by 0.001264489310206951 piD = { 0.6 * (3 + sqrt(5)) } piE = { 22/17 + 37/47 + 88/83 } piF = { sqrt(sqrt(2143/22)) } © ASERT 2006-2010 howCloseToPI = { abs(it.value() - PI) } algorithms = [piA:piA, piB:piB, piC:piC, piD:piD, piE:piE, piF:piF] findBestPI(algorithms) def findBestPI(map) { map.entrySet().sort(howCloseToPI).each { entry -> def diff = howCloseToPI(entry) println "Algorithm $entry.key differs by $diff" } } ESDC 2010 - 56
  • 57. ...Using Closures... • Used for many things in Groovy: • Iterators new File('/x.txt').eachLine { println it • Callbacks } • Higher-order functions • Specialized control structures • Dynamic method definition © ASERT 2006-2010 • Resource allocation 3.times { println 'Hi' } • Threads [0, 1, 2].each { number -> • Continuations println number } def houston(Closure doit) { (10..1).each { count -> [0, 1, 2].each { println it} doit(count) } def printit = { println it } } [0, 1, 2].each printit houston { println it } ESDC 2010 - 57
  • 58. ...Using Closures... map = ['a': 1, 'b': 2] map.each {key, value -> map[key] = value * 2} assert map == ['a': 2, 'b': 4] doubler = {key, value -> map[key] = value * 2} map.each(doubler) © ASERT 2006-2010 assert map == ['a': 4, 'b': 8] def doubleMethod(entry) { map[entry.key] = entry.value * 2 } doubler = this.&doubleMethod map.each(doubler) assert map == ['a': 8, 'b': 16] ESDC 2010 - 58
  • 59. ...Using Closures... assert [1, 2, 3].grep{ it < 3 } == [1, 2] assert [1, 2, 3].any{ it % 2 == 0 } assert [1, 2, 3].every{ it < 4 } assert (1..9).collect{it}.join() == '123456789' assert (1..4).collect{it * 2}.join() == '2468' def add = { x, y -> x + y } © ASERT 2006-2010 def mult = { x, y -> x * y } assert add(1, 3) == 4 assert mult(1, 3) == 3 def min = { x, y -> [x, y].min() } def max = { x, y -> [x, y].max() } def triple = mult.curry(3); assert triple(2) == 6 def atLeastTen = max.curry(10) assert atLeastTen(5) == 10 assert atLeastTen(15) == 15 ESDC 2010 - 59
  • 60. ...Using Closures def pairWise(list, Closure invoke) { if (list.size() < 2) return [] def next = invoke(list[0], list[1]) return [next] + pairWise(list.tail(), invoke) } © ASERT 2006-2010 // using min, max, etc. From previous slide assert pairWise(1..5, add) == [3, 5, 7, 9] assert pairWise(1..5, mult) == [2, 6, 12, 20] assert pairWise(1..5, min) == [1, 2, 3, 4] assert pairWise(1..5, max) == [2, 3, 4, 5] assert 'cbaxabc' == ['a', 'b', 'c'].inject('x') { result, item -> item + result + item } ESDC 2010 - 60
  • 61. Better Design Patterns: Builder • Markup Builder <html> import groovy.xml.* <head> def page = new MarkupBuilder() <title>Hello</title> page.html { </head> head { title 'Hello' } <body> body { © ASERT 2006-2010 <ul> ul { <li>world 1</li> for (count in 1..5) { <li>world 2</li> li "world $count" <li>world 3</li> } } } } <li>world 4</li> <li>world 5</li> </ul> </body> </html> ESDC 2010 - 61
  • 62. SwingBuilder import java.awt.FlowLayout builder = new groovy.swing.SwingBuilder() langs = ["Groovy", "Ruby", "Python", "Pnuts"] gui = builder.frame(size: [290, 100], title: 'Swinging with Groovy!') { panel(layout: new FlowLayout()) { panel(layout: new FlowLayout()) { for (lang in langs) { © ASERT 2006-2010 checkBox(text: lang) } } button(text: 'Groovy Button', actionPerformed: { builder.optionPane(message: 'Indubitably Groovy!'). createDialog(null, 'Zen Message').show() }) button(text: 'Groovy Quit', actionPerformed: {System.exit(0)}) } } gui.show() Source: http://www.ibm.com/developerworks/java/library/j-pg04125/ ESDC 2010 - 62
  • 63. SwingXBuilder import groovy.swing.SwingXBuilder © ASERT 2006-2010 import static java.awt.Color.* import static java.lang.Math.* def swing = new SwingXBuilder() def frame = swing.frame(size: [300, 300]) { graph(plots: [ [GREEN, {value -> sin(value)}], [BLUE, {value -> cos(value)}], [RED, {value -> tan(value)}] ]) }.show() ESDC 2010 - 63
  • 64. GraphicsBuilder def colors = ['red','darkOrange','blue','darkGreen'] (0..3).each { index -> star( cx: 50 + (index*110), cy: 50, or: 40, ir: 15, borderColor: 'black', count: 2+index, fill: colors[index] ) star( cx: 50 + (index*110), cy: 140, or: 40, ir: 15, borderColor: 'black‘, count: 7+index, fill: colors[index] ) } © ASERT 2006-2010 ESDC 2010 - 64
  • 65. AntBuilder... def ant = new AntBuilder() ant.echo("hello") // let's just call one task // create a block of Ant using the builder pattern ant.sequential { myDir = "target/AntTest/" mkdir(dir: myDir) © ASERT 2006-2010 copy(todir: myDir) { fileset(dir: "src/test") { include(name: "**/*.groovy") } } echo("done") } // now let's do some normal Groovy again file = new File("target/test/AntTest.groovy") assert file.exists() ESDC 2010 - 65
  • 66. ...AntBuilder def ant = new AntBuilder() ant.echo(file:'Temp.java', ''' class Temp { public static void main(String[] args) { System.out.println("Hello"); } } © ASERT 2006-2010 ''') ant.with { javac(srcdir:'.', includes:'Temp.java', fork:'true') java(classpath:'.', classname:'Temp', fork:'true') echo('Done') } // => // [javac] Compiling 1 source file // [java] Hello // [echo] Done ESDC 2010 - 66
  • 67. Using AntLibs: Maven Ant Tasks & AntUnit import static groovy.xml.NamespaceBuilder.newInstance as namespace def ant = new AntBuilder() def mvn = namespace(ant, 'antlib:org.apache.maven.artifact.ant') def antunit = namespace(ant, 'antlib:org.apache.ant.antunit') direct = [groupId:'jfree', artifactId:'jfreechart', version:'1.0.9'] indirect = [groupId:'jfree', artifactId:'jcommon', version:'1.0.12'] // download artifacts © ASERT 2006-2010 mvn.dependencies(filesetId:'artifacts') { dependency(direct) } // print out what we downloaded ant.fileScanner { fileset(refid:'artifacts') }.each { println it } // use AntUnit to confirm expected files were downloaded def prefix = System.properties.'user.home' + '/.m2/repository' [direct, indirect].each { item -> def (g, a, v) = [item.groupId, item.artifactId, item.version] antunit.assertFileExists(file:"$prefix/$g/$a/$v/$a-${v}.jar") } C:Userspaulk.m2repositoryjfreejcommon1.0.12jcommon-1.0.12.jar C:Userspaulk.m2repositoryjfreejfreechart1.0.9jfreechart-1.0.9.jar ESDC 2010 - 67
  • 68. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010  OO • GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 68
  • 69. Object Orientation • Similar capabilities to Java – Define classes, interfaces, enums, annotations • Differences to Java – Classes (and interfaces etc.) public by default – Methods public by default – © ASERT 2006-2010 Property support within classes (auto-setters/getters) – Multimethods (runtime dispatch – "duck typing") class BikeGroovyBean { String manufacturer, model, serialNo, status Integer frame Double weight BigDecimal cost } ESDC 2010 - 69
  • 70. Object Orientation – Runtime Interfaces – Java has these methods in java.util.Collections: – In Groovy, no need to create a class implementing Comparator having a compare method, just use a map © ASERT 2006-2010 of Closures instead (though Groovy has special support for this example which is even more concise): def animals = ['Antelope', 'Bee', 'Zebra'] def comparator = [ compare:{ a, b -> a.size() <=> b.size() } ] as Comparator assert comparator instanceof Comparator assert Collections.min(animals, comparator) == 'Bee' assert Collections.max(animals, comparator) == 'Antelope' ESDC 2010 - 70
  • 71. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO  GDK methods • XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 71
  • 72. Better File Manipulation... import java.util.List; import java.util.ArrayList; import java.util.Arrays; import java.io.File; // ... import java.io.FileOutputStream; private static void processFile(File file, PrintWriter out) { import java.io.PrintWriter; FileInputStream fis = null; import java.io.FileNotFoundException; InputStreamReader isr = null; import java.io.IOException; BufferedReader reader = null; import java.io.BufferedReader; try { import java.io.InputStreamReader; fis = new FileInputStream(file); import java.io.FileInputStream; isr = new InputStreamReader(fis); reader = new BufferedReader(isr); public class PrintJavaSourceFileLinesThatContainTheWordJava { String nextline; public static void main(String[] args) { int count = 0; File outfile = new File("result.txt"); while ((nextline = reader.readLine()) != null) { outfile.delete(); count++; File basedir = new File(".."); if (nextline.toLowerCase().contains("java")) { List<File> files = new ArrayList<File>(); out.println("File '" + file + files.add(basedir); "' on line " + count); © ASERT 2006-2010 FileOutputStream fos = null; out.println(nextline); PrintWriter out = null; out.println(); try { } fos = new FileOutputStream(outfile); } out = new PrintWriter(fos); } catch (FileNotFoundException e) { while (!files.isEmpty()) { e.printStackTrace(); File file = files.remove(0); } catch (IOException e) { if (file.isDirectory()) { e.printStackTrace(); files.addAll(Arrays.asList(file.listFiles())); } finally { } else { if (reader != null) { if (file.getName().endsWith(".java")) { try { reader.close(); } processFile(file, out); catch (IOException e) { e.printStackTrace(); } } } } if (isr != null) { } try { isr.close(); } } catch (FileNotFoundException e) { catch (IOException e) { e.printStackTrace(); } e.printStackTrace(); } } finally { if (fis != null) { if (out != null) { out.close(); } try { fis.close(); } if (fos != null) { catch (IOException e) { e.printStackTrace(); } try { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } } } } } // ... ESDC 2010 - 72
  • 73. ...Better File Manipulation... import java.util.List; import import java.util.ArrayList; java.util.Arrays; boilerplate import java.io.File; // ... import java.io.FileOutputStream; private static void processFile(File file, PrintWriter out) { import java.io.PrintWriter; FileInputStream fis = null; import java.io.FileNotFoundException; InputStreamReader isr = null; import java.io.IOException; BufferedReader reader = null; import java.io.BufferedReader; try { import java.io.InputStreamReader; fis = new FileInputStream(file); import java.io.FileInputStream; isr = new InputStreamReader(fis); reader = new BufferedReader(isr); public class PrintJavaSourceFileLinesThatContainTheWordJava { String nextline; public static void main(String[] args) { int count = 0; File outfile = new File("result.txt"); while ((nextline = reader.readLine()) != null) { outfile.delete(); count++; File basedir = new File(".."); if (nextline.toLowerCase().contains("java")) { List<File> files = new ArrayList<File>(); out.println("File '" + file + files.add(basedir); "' on line " + count); © ASERT 2006-2010 FileOutputStream fos = null; out.println(nextline); PrintWriter out = null; out.println(); try { } fos = new FileOutputStream(outfile); } out = new PrintWriter(fos); } catch (FileNotFoundException e) { while (!files.isEmpty()) { e.printStackTrace(); File file = files.remove(0); } catch (IOException e) { if (file.isDirectory()) { e.printStackTrace(); files.addAll(Arrays.asList(file.listFiles())); } finally { } else { if (reader != null) { if (file.getName().endsWith(".java")) { try { reader.close(); } processFile(file, out); catch (IOException e) { e.printStackTrace(); } } } } if (isr != null) { } try { isr.close(); } } catch (FileNotFoundException e) { catch (IOException e) { e.printStackTrace(); } e.printStackTrace(); } } finally { if (fis != null) { if (out != null) { out.close(); } try { fis.close(); } if (fos != null) { catch (IOException e) { e.printStackTrace(); } try { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } } } } } // ... ESDC 2010 - 73
  • 74. ...Better File Manipulation def out = new File('result.txt') out.delete() new File('..').eachFileRecurse {file -> if (file.name.endsWith('.groovy')) { file.eachLine { line, num -> if (line.toLowerCase().contains('groovy')) out << © ASERT 2006-2010 "File '$file' on line $numn$linenn" } } } File '..filessrcPrintGroovySourceLines.groovy' on line 4 if (file.name.endsWith('.groovy')) { File '..filessrcPrintGroovySourceLines.groovy' on line 6 if (line.toLowerCase().contains('groovy')) File '..jdbcsrcJdbcGroovy.groovy' on line 1 import groovy.sql.Sql … ESDC 2010 - 74
  • 75. Better Process Management... // windows version (minimal error/output) def p = "cmd /c dir *.xml".execute() p.waitFor() if (p.exitValue()) println p.err.text else println p.text ----- stdout ----- Volume in drive D is DATA Volume Serial Number is FC5C-B39D © ASERT 2006-2010 Directory of D:svntrunk-groovygroovy-core // unix version 27/06/2009 10:13 AM 38,478 build.xml def p = "ls *.xml".execute() 29/06/2009 06:53 PM 27,617 pom.xml def err = new StringBuffer() 2 File(s) 66,095 bytes 0 Dir(s) 15,503,597,568 bytes free def out = new StringBuffer() // could ignore/use Stream below ----- stderr ----- Return Code: 1 p.consumeProcessOutput(out, err) ls: *.xml: No such file or directory def exitValue = p.waitFor() if (err) println """----- stderr ----- Return Code: $exitValue $err""" if (out) println "----- stdout -----n$out" ESDC 2010 - 75
  • 76. ...Better Process Management sout: tstfl.grvy serr: def sout = new StringBuffer() def serr = new StringBuffer() proc1 = 'tr -d o'.execute() proc2 = 'tr -d e'.execute() © ASERT 2006-2010 proc3 = 'tr -d i'.execute() proc3.consumeProcessOutput(sout, serr) proc1 | proc2 | proc3 [proc1, proc2].each{ it.consumeProcessErrorStream(serr) } proc1.withWriter { writer -> writer << 'testfile.groovy' } proc3.waitForOrKill(1000) println 'sout: ' + sout println 'serr: ' + serr ESDC 2010 - 76
  • 77. Better Thread Management Thread.start{ Hello from thread 2 4.times { Hello from thread 1 println 'Hello from thread 1' Hello from main thread sleep 400 Hello from main thread } Hello from thread 2 } Hello from thread 1 Thread.start{ Hello from main thread © ASERT 2006-2010 5.times { Hello from thread 2 println 'Hello from thread 2' Hello from main thread sleep 300 Hello from thread 1 } Hello from main thread } Hello from thread 2 7.times { Hello from main thread println 'Hello from main thread' Hello from thread 2 sleep 200 Hello from thread 1 } Hello from main thread ESDC 2010 - 77
  • 78. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods  XML • Database interaction • Testing • Metaprogramming ESDC 2010 - 78
  • 79. Better XML Manipulation... <records> records.xml <car name='HSV Maloo' make='Holden' year='2006'> <country>Australia</country> <record type='speed'>Production Pickup Truck with speed of 271kph</record> </car> © ASERT 2006-2010 <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 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> ESDC 2010 - 79
  • 80. ...Better XML Manipulation... import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; public class FindYearsJava { public static void main(String[] args) { © ASERT 2006-2010 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document document = builder.parse(new File("records.xml")); NodeList list = document.getElementsByTagName("car"); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); Node year = n.getAttributes().getNamedItem("year"); System.out.println("year = " + year.getTextContent()); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } ESDC 2010 - 80
  • 81. ...Better XML Manipulation... import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.w3c.dom.Node; boilerplate import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; public class FindYearsJava { public static void main(String[] args) { © ASERT 2006-2010 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = builderFactory.newDocumentBuilder(); Document document = builder.parse(new File("records.xml")); NodeList list = document.getElementsByTagName("car"); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); Node year = n.getAttributes().getNamedItem("year"); System.out.println("year = " + year.getTextContent()); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } ESDC 2010 - 81
  • 82. ...Better XML Manipulation def records = new XmlParser().parse("records.xml") records.car.each { © ASERT 2006-2010 println "year = ${it.@year}" } year = 2006 year = 1962 year = 1931 ESDC 2010 - 82
  • 83. XML Processing Options • Reading XML – Special Groovy support: XmlParser, XmlSlurper, DOMCategory • All support GPath, XmlSlurper features Lazy loading – Or Groovy sugar for your current favorites: DOM, SAX, StAX, DOM4J, JDom, XOM, XPath, XSLT, XQuery, etc. • Creating XML – Special Groovy support: MarkupBuilder and © ASERT 2006-2010 StreamingMarkupBuilder – Or again, enhanced syntax for your current favorites • Updating XML – Using above: read followed by create – Can be done with XmlParser, XmlSlurper, DOMCategory – Or with your Java favorites • Verifying XML – Also DTD, W3C XML Schema, Relax NG in a similar fashion to Java mechanisms for these features ESDC 2010 - 83
  • 84. More Details: XmlSlurper def records = new XmlSlurper().parse("records.xml") assert 3 == records.car.size() // 3 records in total assert 10 == records.depthFirst().collect{ it }.size() // 10 nested nodes // test properties of the first record def firstRecord = records.car[0] assert 'car' == firstRecord.name() assert 'Holden' == firstRecord.@make.toString() && 'Australia' == firstRecord.cou // 2 cars have an 'e' in the make assert 2 == records.car.findAll{ it.@make.toString().contains('e') }.size() © ASERT 2006-2010 // 2 cars have an 'e' in the make assert 2 == records.car.findAll{ it.@make =~ '.*e.*' }.size() // makes of cars that have an 's' followed by an 'a' in the country assert ['Holden', 'Peel'] == records.car.findAll{ it.country =~ '.*s.*a.*' }.@make.coll // types of records assert ['speed', 'size', 'price'] == records.depthFirst().grep{ it.@type != '' }.'@type'* assert ['speed', 'size', 'price'] == records.'**'.grep{ it.@type != '' }.'@type'*.toString // check parent() operator def countryOne = records.car[1].country assert 'Peel' == countryOne.parent().@make.toString() && 'Peel' == countryOne.'. // names of cars with records sorted by year def names = records.car.list().sort{ it.@year.toInteger() }.'@name'*.toString() assert ['Royale', 'P50', 'HSV Maloo'] == names ESDC 2010 - 84
  • 85. Uniform Data Access • Not just better SQL processing and better XML processing but Uniform Data Access – i.e. Data access to Objects, XML and SQL Databases has more similarities than differences – Fluent API regardless of data source – Differences in detail about how to set up structures © ASERT 2006-2010 – Similarities in how to process data within resulting structures – Equivalent to LINQ API in .Net ESDC 2010 - 85
  • 86. Xml Data Access… import java.text.SimpleDateFormat import com.thoughtworks.xstream.XStream class Athlete { String firstname, lastname, gender, country, dateOfBirth } def asDate(dateStr) { new SimpleDateFormat("yyyy-MM-dd").parse(dateStr) } def athleteList = [ © ASERT 2006-2010 new Athlete(firstname: 'Paul', lastname: 'Tergat', dateOfBirth: '1969-06-17', gender: 'M', country: 'KEN'), new Athlete(firstname: 'Khalid', lastname: 'Khannouchi', dateOfBirth: '1971-12-22', gender: 'M', country: 'USA'), new Athlete(firstname: 'Sammy', lastname: 'Korir', dateOfBirth: '1971-12-12', gender: 'M', country: 'KEN'), new Athlete(firstname: 'Ronaldo', lastname: 'da Costa', dateOfBirth: '1970-06-07', gender: 'M', country: 'BRA'), new Athlete(firstname: 'Paula', lastname: 'Radcliffe', dateOfBirth: '1973-12-17', gender: 'F', country: 'GBR') ] // create XML as input def input = new XStream().toXML(athleteList) ESDC 2010 - 86
  • 87. …Xml Data Access def athletes = new XmlSlurper().parseText(input).Athlete def bornSince70 = { asDate(it.dateOfBirth.text()) > asDate('1970-1-1') } def excludingKh = { it.lastname.text() <= 'Kg' || it.lastname.text() >= 'Ki' } def byGenderDescThenByCountry = { a, b -> a.gender.text() == b.gender.text() ? © ASERT 2006-2010 a.country.text() <=> b.country.text() : b.gender.text() <=> a.gender.text() } def someYoungsters = athletes. findAll(bornSince70).findAll(excludingKh).list(). sort(byGenderDescThenByCountry) someYoungsters.each { def name = "$it.firstname $it.lastname".padRight(25) println "$name ($it.gender) $it.country $it.dateOfBirth" } ESDC 2010 - 87
  • 88. Object Data Access… import java.text.SimpleDateFormat class Athlete { def firstname, lastname, gender, country, dateOfBirth } def asDate(dateStr) { new SimpleDateFormat("yyyy-MM-dd").parse(dateStr) } © ASERT 2006-2010 def athletes = [ new Athlete(firstname: 'Paul', lastname: 'Tergat', dateOfBirth: '1969-06-17', gender: 'M', country: 'KEN'), new Athlete(firstname: 'Khalid', lastname: 'Khannouchi', dateOfBirth: '1971-12-22', gender: 'M', country: 'USA'), new Athlete(firstname: 'Sammy', lastname: 'Korir', dateOfBirth: '1971-12-12', gender: 'M', country: 'KEN'), new Athlete(firstname: 'Ronaldo', lastname: 'da Costa', dateOfBirth: '1970-06-07', gender: 'M', country: 'BRA'), new Athlete(firstname: 'Paula', lastname: 'Radcliffe', dateOfBirth: '1973-12-17', gender: 'F', country: 'GBR') ] ESDC 2010 - 88
  • 89. …Object Data Access def bornSince70 = { asDate(it.dateOfBirth) > asDate('1970-1-1') } def excludingKh = { it.lastname <= 'Kg' || it.lastname >= 'Ki' } def byGenderDescThenByCountry = { a, b -> a.gender == b.gender ? a.country <=> b.country : © ASERT 2006-2010 b.gender <=> a.gender } def someYoungsters = athletes. findAll(bornSince70). findAll(excludingKh). sort(byGenderDescThenByCountry) someYoungsters.each { def name = "$it.firstname $it.lastname".padRight(25) println "$name ($it.gender) $it.country $it.dateOfBirth" } ESDC 2010 - 89
  • 90. Sql Data Access… import groovy.sql.Sql dbHandle = null def getDb() { if (dbHandle) return dbHandle def source = new org.hsqldb.jdbc.jdbcDataSource() source.database = 'jdbc:hsqldb:mem:GIA' source.user = 'sa' source.password = '' dbHandle = new Sql(source) © ASERT 2006-2010 return dbHandle } db.execute ''' DROP INDEX athleteIdx IF EXISTS; DROP TABLE Athlete IF EXISTS; CREATE TABLE Athlete ( athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY, firstname VARCHAR(64), lastname VARCHAR(64), country VARCHAR(3), gender CHAR(1), dateOfBirth DATE ); CREATE INDEX athleteIdx ON Athlete (athleteId); ''' ESDC 2010 - 90
  • 91. …Sql Data Access… def athleteList = [ [firstname: 'Paul', lastname: 'Tergat', dateOfBirth: '1969-06-17', gender: 'M', country: 'KEN'], [firstname: 'Khalid', lastname: 'Khannouchi', dateOfBirth: '1971-12-22', gender: 'M', country: 'USA'], [firstname: 'Sammy', lastname: 'Korir', dateOfBirth: '1971-12-12', gender: 'M', country: 'KEN'], © ASERT 2006-2010 [firstname: 'Ronaldo', lastname: 'da Costa', dateOfBirth: '1970-06-07', gender: 'M', country: 'BRA'], [firstname: 'Paula', lastname: 'Radcliffe', dateOfBirth: '1973-12-17', gender: 'F', country: 'GBR'] ] def athletes = db.dataSet('Athlete') athleteList.each {a -> athletes.add(a)} ESDC 2010 - 91
  • 92. …Sql Data Access def bornSince70 = { it.dateOfBirth > '1970-1-1' } def excludingKh = { it.lastname <= 'Kg' || it.lastname >= 'Ki' } def someYoungsters = athletes. findAll(bornSince70). findAll(excludingKh). sort { it.gender }.reverse(). sort { it.country } © ASERT 2006-2010 println someYoungsters.sql + 'n' + someYoungsters.parameters someYoungsters.each { def name = "$it.firstname $it.lastname".padRight(25) println "$name ($it.gender) $it.country $it.dateOfBirth" } /* => select * from Athlete where dateOfBirth > ? and (lastname <= ? or lastname >= ?) order by gender DESC, country ["1970-1-1", "Kg", "Ki"] Ronaldo da Costa (M) BRA 1970-06-07 Sammy Korir (M) KEN 1971-12-12 Paula Radcliffe (F) GBR 1973-12-17 */ ESDC 2010 - 92
  • 93. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML  Database interaction • Testing • Metaprogramming ESDC 2010 - 93
  • 94. Better SQL Manipulation... import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; // ... import java.sql.ResultSet; finally { import java.sql.SQLException; // release database resources close(rs); // close the ResultSet public class JdbcJava { close(stmt); // close the Statement public static Connection getConnection() throws Exception { close(conn); // close the Connection String driver = "org.hsqldb.jdbcDriver"; } String url = "jdbc:hsqldb:file:EMPDB"; } String username = "sa"; String password = ""; Class.forName(driver); private static void close(ResultSet rs) { return DriverManager.getConnection(url, username, password); try { } if (rs != null) rs.close(); } catch (SQLException e) { © ASERT 2006-2010 public static void main(String[] args) { e.printStackTrace(); Connection conn = null; } Statement stmt = null; } ResultSet rs = null; try { private static void close(Statement st) { conn = getConnection(); try { stmt = conn.createStatement(); if (st != null) st.close(); String query = } catch (SQLException e) { "select id, lastname, firstname from Employees"; e.printStackTrace(); rs = stmt.executeQuery(query); } while (rs.next()) { } System.out.println(rs.getString("id") + ": " + rs.getString("firstname") + private static void close(Connection cn) { " " + rs.getString("lastname")); try { } if (cn != null) cn.close(); } } catch (SQLException e) { catch (Exception e) { e.printStackTrace(); // handle the exception } e.printStackTrace(); } System.err.println(e.getMessage()); } } // ... ESDC 2010 - 94
  • 95. ...Better SQL Manipulation... import import java.sql.Connection; java.sql.DriverManager; boilerplate import java.sql.Statement; // ... import java.sql.ResultSet; finally { import java.sql.SQLException; // release database resources close(rs); // close the ResultSet public class JdbcJava { close(stmt); // close the Statement public static Connection getConnection() throws Exception { close(conn); // close the Connection String driver = "org.hsqldb.jdbcDriver"; } String url = "jdbc:hsqldb:file:EMPDB"; } String username = "sa"; String password = ""; Class.forName(driver); private static void close(ResultSet rs) { return DriverManager.getConnection(url, username, password); try { } if (rs != null) rs.close(); } catch (SQLException e) { © ASERT 2006-2010 public static void main(String[] args) { e.printStackTrace(); Connection conn = null; } Statement stmt = null; } ResultSet rs = null; try { private static void close(Statement st) { conn = getConnection(); try { stmt = conn.createStatement(); if (st != null) st.close(); String query = } catch (SQLException e) { "select id, lastname, firstname from Employees"; e.printStackTrace(); rs = stmt.executeQuery(query); } while (rs.next()) { } System.out.println(rs.getString("id") + ": " + rs.getString("firstname") + private static void close(Connection cn) { " " + rs.getString("lastname")); try { } if (cn != null) cn.close(); } } catch (SQLException e) { catch (Exception e) { e.printStackTrace(); // handle the exception } e.printStackTrace(); } System.err.println(e.getMessage()); } } // ... ESDC 2010 - 95
  • 96. ...Better SQL Manipulation import groovy.sql.Sql def url = 'jdbc:hsqldb:file:EMPDB' def username = 'sa' def password = '' © ASERT 2006-2010 def driver = 'org.hsqldb.jdbcDriver' def db = Sql.newInstance(url, username, password, driver) db.eachRow("SELECT id, firstname, lastname FROM Employees") { println "$it.id: $it.firstname $it.lastname" } ESDC 2010 - 96
  • 97. More Details: Working with Databases • Using standard SQL statements import groovy.sql.Sql def foo = 'cheese' def db = Sql.newInstance("jdbc:mysql://localhost:3306/mydb", "user", "pswd", "com.mysql.jdbc.Driver") db.eachRow("select * from FOOD where type=${foo}") { println "Gromit likes ${it.name}" © ASERT 2006-2010 } • Using DataSets import groovy.sql.Sql def db = Sql.newInstance("jdbc:mysql://localhost:3306/mydb", "user", "pswd", "com.mysql.jdbc.Driver") def food = db.dataSet('FOOD') def cheese = food.findAll { it.type == 'cheese' } cheese.each { println "Gromit likes ${it.name}" } ESDC 2010 - 97
  • 98. DataSets and Lazy Evaluation athleteSet = db.dataSet('Athlete') youngsters = athleteSet.findAll{ it.dateOfBirth > '1970-1-1'} paula = youngsters.findAll{ it.firstname == 'Paula'} println paula.sql // => © ASERT 2006-2010 // select * from Athlete where dateOfBirth > ? and firstname = println paula.parameters // => // [1970-1-1, Paula] paula.each { println it.lastname } // database called here // => // Radcliffe ESDC 2010 - 98
  • 99. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML • Database interaction  Testing • Metaprogramming ESDC 2010 - 99
  • 100. Types of testing Unit Testing Mock/interaction testing Techniques State-based testing Testing DSLs ATDD/BDD Data-driven Logic-driven © ASERT 2006-2010 Integration Testing Model-driven Performance testing Acceptance Testing All-pairs & Web drivers combinations Non-web drivers Gpars Test runners ESDC 2010 - 100
  • 101. Groovy's Value Add for Testing • Unit testing – Built-in asserts, support for JUnit 3&4 and TestNG, GroovyTestCase with shouldFail and other methods – Built-in mocking and compatible with Java mocking • Integration testing – Metaprogramming allows various kinds of IOC like © ASERT 2006-2010 intercepting and hooking up of components – Wealth of GDK methods for Ant, Processes, Files, Threads, etc. make the automating part much simpler • Acceptance Testing and Generally – Allows creation of English-like testing DSLs using Closures, builders, metaprogramming – Simpler syntax great for non hard-core testers – Grapes make tests easier to share ESDC 2010 - 101
  • 102. Topics • Introduction • Simple Data Types • Collective Data Types • Control Structures • Closures and Builders © ASERT 2006-2010 • OO • GDK methods • XML • Database interaction • Testing  Metaprogramming ESDC 2010 - 102
  • 103. Metaprogramming • Runtime Metaprogramming – Categories including DGM – invokeMethod, methodMissing, getProperty – GroovyInterceptable – ExpandoMetaClass © ASERT 2006-2010 • Compile-time Metaprogramming – AST Macros – Local and Global flavors based around annotations – Reduced runtime overhead – Several built-in annotations: • @Immutable • @Delegate • @Singleton ESDC 2010 - 103
  • 104. ExpandoMetaClass… import static java.lang.Character.isUpperCase String.metaClass.swapCase = { delegate.collect { ch -> isUpperCase(ch as char) ? ch.toLowerCase() : ch.toUpperCase() }.join() © ASERT 2006-2010 } println new Date().toString().swapCase() // => sUN nOV 09 17:30:06 est 2008 List.metaClass.sizeDoubled = {-> delegate.size() * 2 } LinkedList list = [] list << 1 list << 2 assert 4 == list.sizeDoubled() ESDC 2010 - 104
  • 105. …ExpandoMetaClass class Person { String name } class MortgageLender { def borrowMoney() { "buy house" } © ASERT 2006-2010 } def lender = new MortgageLender() Person.metaClass.buyHouse = lender.&borrowMoney def p = new Person() assert "buy house" == p.buyHouse() ESDC 2010 - 105
  • 106. Grapes / Grab // Google Collections example import com.google.common.collect.HashBiMap @Grab(group='com.google.collections', module='google-collections', version='1.0-rc2') © ASERT 2006-2010 def getFruit() { [ grape:'purple', lemon:'yellow', orange:'orange' ] as HashBiMap } assert fruit.lemon == 'yellow' assert fruit.inverse().yellow == 'lemon' ESDC 2010 - 106
  • 107. Better Design Patterns: Immutable... • Java Immutable Class – As per Joshua Bloch // ... @Override Effective Java public boolean equals(Object obj) { if (this == obj) public final class Punter { return true; private final String first; if (obj == null) private final String last; return false; if (getClass() != obj.getClass()) public String getFirst() { return false; return first; Punter other = (Punter) obj; } if (first == null) { © ASERT 2006-2010 if (other.first != null) public String getLast() { return false; return last; } else if (!first.equals(other.first)) } return false; if (last == null) { @Override if (other.last != null) public int hashCode() { return false; final int prime = 31; } else if (!last.equals(other.last)) int result = 1; return false; result = prime * result + ((first == null) return true; ? 0 : first.hashCode()); } result = prime * result + ((last == null) ? 0 : last.hashCode()); @Override return result; public String toString() { } return "Punter(first:" + first + ", last:" + last + ")"; public Punter(String first, String last) { } this.first = first; this.last = last; } } // ... ESDC 2010 - 107
  • 108. ...Better Design Patterns: Immutable... • Java Immutable Class boilerplate – As per Joshua Bloch // ... @Override Effective Java public boolean equals(Object obj) { if (this == obj) public final class Punter { return true; private final String first; if (obj == null) private final String last; return false; if (getClass() != obj.getClass()) public String getFirst() { return false; return first; Punter other = (Punter) obj; } if (first == null) { © ASERT 2006-2010 if (other.first != null) public String getLast() { return false; return last; } else if (!first.equals(other.first)) } return false; if (last == null) { @Override if (other.last != null) public int hashCode() { return false; final int prime = 31; } else if (!last.equals(other.last)) int result = 1; return false; result = prime * result + ((first == null) return true; ? 0 : first.hashCode()); } result = prime * result + ((last == null) ? 0 : last.hashCode()); @Override return result; public String toString() { } return "Punter(first:" + first + ", last:" + last + ")"; public Punter(String first, String last) { } this.first = first; this.last = last; } } // ... ESDC 2010 - 108
  • 109. ...Better Design Patterns: Immutable @Immutable class Punter { String first, last © ASERT 2006-2010 } ESDC 2010 - 109
  • 110. Better Design Patterns: Singleton class Calculator { def total = 0 def add(a, b) { total++; a + b } } def INSTANCE = new Calculator() Calculator.metaClass.constructor = { -> INSTANCE } © ASERT 2006-2010 def c1 = new Calculator() def c2 = new Calculator() @Singleton(lazy=true) assert c1.add(1, 2) == 3 class X { assert c2.add(3, 4) == 7 def getHello () { "Hello, World!" assert c1.is(c2) } assert [c1, c2].total == [2, 2] } println X.instance.hello ESDC 2010 - 110
  • 111. Better Design Patterns: Delegate… public Date getWhen() { import java.util.Date; return when; } public class Event { private String title; public void setWhen(Date when) { private String url; this.when = when; private Date when; } public String getUrl() { public boolean before(Date other) { return url; return when.before(other); } © ASERT 2006-2010 } public void setUrl(String url) { public void setTime(long time) { this.url = url; when.setTime(time); } } public String getTitle() { public long getTime() { return title; return when.getTime(); } } public void setTitle(String title) { public boolean after(Date other) { this.title = title; return when.after(other); } } // ... // ... ESDC 2010 - 111
  • 112. …Better Design Patterns: Delegate… public Date getWhen() { import java.util.Date; return when; boilerplate } public class Event { private String title; public void setWhen(Date when) { private String url; this.when = when; private Date when; } public String getUrl() { public boolean before(Date other) { return url; return when.before(other); } © ASERT 2006-2010 } public void setUrl(String url) { public void setTime(long time) { this.url = url; when.setTime(time); } } public String getTitle() { public long getTime() { return title; return when.getTime(); } } public void setTitle(String title) { public boolean after(Date other) { this.title = title; return when.after(other); } } // ... // ... ESDC 2010 - 112
  • 113. …Better Design Patterns: Delegate class Event { String title, url @Delegate Date when } © ASERT 2006-2010 def gr8conf = new Event(title: "GR8 Conference", url: "http://www.gr8conf.org", when: Date.parse("yyyy/MM/dd", "2009/05/18")) def javaOne = new Event(title: "JavaOne", url: "http://java.sun.com/javaone/", when: Date.parse("yyyy/MM/dd", "2009/06/02")) assert gr8conf.before(javaOne.when) ESDC 2010 - 113
  • 114. Topics  Introduction  Simple Data Types  Collective Data Types  Control Structures  Closures and Builders © ASERT 2006-2010  OO  GDK methods  XML  Database interaction  Testing  Metaprogramming ESDC 2010 - 114
  • 115. More Information: on the web • Web sites – http://groovy.codehaus.org – http://grails.codehaus.org – http://pleac.sourceforge.net/pleac_groovy (many examples) – http://www.asert.com.au/training/java/GV110.htm (workshop) • Mailing list for users – user@groovy.codehaus.org © ASERT 2006-2010 • Information portals – http://www.aboutgroovy.org – http://www.groovyblogs.org • Documentation (1000+ pages) – Getting Started Guide, User Guide, Developer Guide, Testing Guide, Cookbook Examples, Advanced Usage Guide • Books – Several to choose from ... ESDC 2010 - 115
  • 116. ...More Information © ASERT 2006-2010 116