Groovy Tutorial
Upcoming SlideShare
Loading in...5
×
 

Groovy Tutorial

on

  • 12,023 views

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.

Statistics

Views

Total Views
12,023
Views on SlideShare
11,979
Embed Views
44

Actions

Likes
42
Downloads
614
Comments
3

6 Embeds 44

http://www.slideshare.net 19
http://haneef-ali.blogspot.com 16
http://www.techgig.com 5
http://lab.gilbird.com 2
http://gilbird.com 1
http://www.gilbird.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Groovy Tutorial Groovy Tutorial Presentation Transcript

  • © ASERT 2006-2010 Groovy Tutorial Scripting on the JVM Dr Paul King paulk@asert.com.au @paulk_asert ASERT, Australia
  • 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
  • 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
  • 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
  • Growing Acceptance … A slow and steady start but now gaining in momentum, maturity and mindshare Now free
  • … Growing Acceptance … © ASERT 2006-2010 ESDC 2010 - 6
  • … Growing Acceptance … © ASERT 2006-2010 Groovy and Grails downloads: 70-90K per month and growing ESDC 2010 - 7
  • … Growing Acceptance … © ASERT 2006-2010 Source: http://www.micropoll.com/akira/mpresult/501697-116746 Source: http://www.grailspodcast.com/ ESDC 2010 - 8
  • … Growing Acceptance … © ASERT 2006-2010 http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes http://www.java.net ESDC 2010 - 9
  • … Growing Acceptance … What alternative JVM language are you using or intending to use © ASERT 2006-2010 http://www.leonardoborges.com/writings ESDC 2010 - 10
  • … Growing Acceptance … © ASERT 2006-2010 http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com) ESDC 2010 - 11
  • … Growing Acceptance … © ASERT 2006-2010 http://pollpigeon.com/jsf-grails-wicket/r/25665/ ESDC 2010 - 12
  • … Growing Acceptance © ASERT 2006-2010 ESDC 2010 - 13
  • 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
  • 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
  • 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
  • ...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
  • ...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
  • ...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
  • ...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
  • ...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
  • ...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
  • ...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
  • ...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
  • ...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
  • 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
  • ...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
  • ... 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
  • ... 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • …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
  • …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
  • …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
  • …Strings... • javadoc © ASERT 2006-2010 ESDC 2010 - 38
  • …Strings • groovy-jdk (just the additions) © ASERT 2006-2010 ESDC 2010 - 39
  • 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
  • ...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
  • 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
  • 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
  • ... 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
  • ... 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
  • Operator Overloading... © ASERT 2006-2010 ESDC 2010 - 46
  • ...Operator Overloading... © ASERT 2006-2010 ESDC 2010 - 47
  • ...Operator Overloading © ASERT 2006-2010 • Can be used with your own types too shoppingCart << clickedItem ESDC 2010 - 48
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • ...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
  • ...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
  • ...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
  • ...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
  • ...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
  • 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
  • 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
  • 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
  • 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
  • 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
  • ...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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • ...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
  • ...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
  • 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
  • ...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
  • 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
  • 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
  • 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
  • ...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
  • ...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
  • ...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
  • 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
  • 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
  • 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
  • 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
  • …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
  • 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
  • …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
  • 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
  • …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
  • …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
  • 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
  • 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
  • ...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
  • ...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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • …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
  • 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
  • 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
  • ...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
  • ...Better Design Patterns: Immutable @Immutable class Punter { String first, last © ASERT 2006-2010 } ESDC 2010 - 109
  • 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
  • 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
  • …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
  • …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
  • 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
  • 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
  • ...More Information © ASERT 2006-2010 116