Your SlideShare is downloading. ×
Groovy Power Features
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 Power Features

6,244
views

Published on

Leveraging the language, Use and abuse of Design Patterns, Web Services, Writing DSLs, Groovy Testing, Polyglot Groovy, Parallel Processing, Enterprise Groovy …

Leveraging the language, Use and abuse of Design Patterns, Web Services, Writing DSLs, Groovy Testing, Polyglot Groovy, Parallel Processing, Enterprise Groovy

Published in: Technology

3 Comments
20 Likes
Statistics
Notes
No Downloads
Views
Total Views
6,244
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
351
Comments
3
Likes
20
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 Power Features! Dr Paul King paulk@asert.com.au @paulk_asert ASERT, Australia
  • 2. Topics Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy • More Info 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. Topics • Groovy Intro Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 26
  • 27. 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 - 27
  • 28. Better Control Structures: Switch – Custom isCase WARNING: enum Color { Advanced yellow, orange, purple Topic! def isCase(thing) { thing.color == this } } import static Color.* class Banana { def color = yellow } class Orange { def color = orange } class Grape { def color = purple } © ASERT 2006-2010 fruits = [new Banana(), new Orange(), new Grape()] fruits.each { println inspectFruit(it) } def inspectFruit(f) { switch (f) { case yellow: return 'Found something yellow' case orange: return 'Found something orange' default: return 'Unknown color' } } Found something yellow Found something orange Unknown color ESDC 2010 - 28
  • 29. Better Control Structures: Switch – Custom isCase enum Color { yellow, orange, purple } import static Color.* class Banana { def color = yellow } class Orange { def color = orange } class Grape { def color = purple } fruits = [new Banana(), new Orange(), new Grape()] fruits.each { println inspectFruit(it) } def inspectFruit(f) { © ASERT 2006-2010 switch (f.color) { case [yellow, orange]: return "Found something $f.color" default: return 'Unknown color' } } Found something yellow Found something orange Unknown color ESDC 2010 - 29
  • 30. Better Control Structures: Switch – Custom isCase enum Color { WARNING: yellow, orange, purple Advanced Topic! def isCase(thing) { thing?.hasProperty('color') && thing.color == this } } enum State { peeled, unpeeled def isCase(thing) { try { © ASERT 2006-2010 return thing.color == this } catch (MissingPropertyException) { return false } } import static Color.* import static State.* class Banana { def color = yellow; def state = peeled } class Orange { def color = orange } class Grape { def state = peeled } ... ESDC 2010 - 30
  • 31. Better Control Structures: Switch – Custom isCase WARNING: ... Advanced Topic! fruits = [new Banana(), new Orange(), new Grape()] fruits.each { println inspectFruit(it) } def inspectFruit(f) { def yellowAndPeeled = { yellow.isCase(it) && peeled.isCase(it) } switch (f) { © ASERT 2006-2010 case yellowAndPeeled: return 'Possibly found a banana split' case yellow: return 'Found something yellow' case peeled: return 'Found something peeled' default: return 'No comment' } Possibly found a banana split } No comment Found something peeled ESDC 2010 - 31
  • 32. Better Control Structures: Switch Poker… hand1 hand2 8C TS KC 9H 4S 7D 2S 5D 3S AC suits = 'SHDC' ranks = '23456789TJQKA' suit = { String card -> suits.indexOf(card[1]) } rank = { String card -> ranks.indexOf(card[0]) } rankSizes = { List cards -> © ASERT 2006-2010 cards.groupBy(rank).collect{ k, v -> v.size() }.sort() } rankValues = { List cards -> cards.collect{ rank(it) }.sort() } // ... println rankSizes(["7S", "7H", "2H", "7D", "AH"]) // => [1, 1, 3] ESDC 2010 - 32
  • 33. …Better Control Structures: Switch Poker… // ... flush = { List cards -> cards.groupBy(suit).size() == 1 } straight = { def v = rankValues(it); v == v[0]..v[0]+4 } straightFlush = { List cards -> straight(cards) && flush(cards) } © ASERT 2006-2010 fourOfAKind = { List cards -> rankSizes(cards) == [1, 4] } fullHouse = { List cards -> rankSizes(cards) == [2, 3] } threeOfAKind = { List cards -> rankSizes(cards) == [1, 1, 3] } twoPair = { List cards -> rankSizes(cards) == [1, 2, 2] } pair = { List cards -> rankSizes(cards) == [1, 1, 1, 2] } // ... ESDC 2010 - 33
  • 34. … Better Control Structures: Switch Poker // ... def rankHand(List cards) { switch (cards) { case straightFlush : return 9 case fourOfAKind : return 8 case fullHouse : return 7 case flush : return 6 © ASERT 2006-2010 case straight : return 5 case threeOfAKind : return 4 case twoPair : return 3 case pair : return 2 default : return 1 } } // ... ESDC 2010 - 34
  • 35. Topics • Groovy Intro • Leveraging the language Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 35
  • 36. 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 - 36
  • 37. 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 - 37
  • 38. ...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 - 38
  • 39. ...Better Design Patterns: Immutable @Immutable class Punter { String first, last © ASERT 2006-2010 } ESDC 2010 - 39
  • 40. 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 - 40
  • 41. 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 - 41
  • 42. …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 - 42
  • 43. …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 - 43
  • 44. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 44
  • 45. SOAP Client and Server class MathService { double add(double a, double b) { a + b } double square(double c) { c * c } } © ASERT 2006-2010 import groovy.net.soap.SoapServer def server = new SoapServer('localhost', 6789) server.setNode('MathService') server.start() import groovy.net.soap.SoapClient def math = new SoapClient('http://localhost:6789/MathServiceInterface?wsdl') assert math.add(1.0, 2.0) == 3.0 assert math.square(3.0) == 9.0 ESDC 2010 - 45
  • 46. Better Testing: SoapUI • Tool for testing Web Services has a built- in Groovy editor for custom steps © ASERT 2006-2010 ESDC 2010 - 46
  • 47. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 47
  • 48. Groovy DSL Features • Literal Syntax Conventions • Scripts as well as Classes & Optional Syntax • Optional Typing & Named Arguments • Type Transformations • Using With © ASERT 2006-2010 • Operator Overloading • Closures • Runtime Metaprogramming • Compile-time Metaprogramming (AST Macros) • Builders ESDC 2010 - 48
  • 49. Literal Syntax Conventions • Lists – Special syntax for list literals – Additional common methods (operator overloading) def list = [3, new Date(), 'Jan'] assert list + list == list * 2 • Maps © ASERT 2006-2010 – Special syntax for map literals – Additional common 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 ESDC 2010 - 49
  • 50. Scripts as well as Classes & Optional Syntax • Java public class HelloJava { public static void main(String[] args) { System.out.println("Hello World"); } } © ASERT 2006-2010 • Groovy class HelloGroovy { static main(String[] args) { System.out.println("Hello World"); } } System.out.println('Hello World'); println 'Hello World' ESDC 2010 - 50
  • 51. Optional Typing & Named Params • Java import java.util.Date; import java.util.HashMap; import java.util.Map; public class OptionalSyntax { public static void printDetails(Map<String, String> args) { System.out.println("Details as at: " + new Date()); String first = args.get("first"); System.out.println("First name: " + (first != null ? first : "unknown")); © ASERT 2006-2010 String last = args.get("last"); System.out.println("Last name: " + (last != null ? last : "unknown")); } public static void main(String[] args) { Map<String, String> details = new HashMap<String, String>(); details.put("first", "John"); details.put("last", "Smith"); printDetails(details); } } def printDetails(args) { println """Details as at: ${new Date()} First name: $args.first • Groovy Last name: $args.last""" } printDetails first:'John', last:'Smith' ESDC 2010 - 51
  • 52. 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 - 52
  • 53. Static Imports 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 - 53
  • 54. Using '.with' Example letters = ['a', 'b', 'c'] range = 'b'..'d' letters.with { add 'd' remove 'a' } assert letters == range © ASERT 2006-2010 map = [a:10, b:4, c:7] map.with { assert (a + b) / c == 2 } ESDC 2010 - 54
  • 55. Coin example... enum Coin { penny(1), nickel(5), dime(10), quarter(25) Coin(int value) { this.value = value } int value } © ASERT 2006-2010 import static Coin.* assert 2 * quarter.value + 1 * nickel.value + 2 * penny.value == 57 ESDC 2010 - 55
  • 56. ...Coin example... class CoinMath { static multiply(Integer self, Coin c) { self * c.value } } use (CoinMath) { © ASERT 2006-2010 assert 2 * quarter + 1 * nickel + 2 * penny == 57 } // EMC equivalent Integer.metaClass.multiply = { Coin c -> delegate * c.value } assert 2 * quarter + 1 * nickel + 2 * penny == 57 ESDC 2010 - 56
  • 57. ...Coin example class CoinValues { static get(Integer self, String name) { self * Coin."${singular(name)}".value } static singular(String val) { val.endsWith('ies') ? val[0..-4] + 'y' : val.endsWith('s') ? val[0..-2] : val } } use (CoinValues) { © ASERT 2006-2010 assert 2.quarters + 1.nickel + 2.pennies == 57 } // EMC equivalent Integer.metaClass.getProperty = { String name -> def mp = Integer.metaClass.getMetaProperty(name) if (mp) return mp.getProperty(delegate) def singular = name.endsWith('ies') ? name[0..-4] + 'y' : name.endsWith('s') ? name[0..-2] : name delegate * Coin."$singular".value } assert 2.quarters + 1.nickel + 2.pennies == 57 ESDC 2010 - 57
  • 58. Game example... // Trying out the game DSL idea by Sten Anderson from: // http://blogs.citytechinc.com/sanderson/?p=92 class GameUtils { static VOWELS = ['a', 'e', 'i', 'o', 'u'] static listItems(things) { def result = '' things.eachWithIndex{ thing, index -> if (index > 0) { © ASERT 2006-2010 if (index == things.size() - 1) result += ' and ' else if (index < things.size() - 1) result += ', ' } result += "${thing.toLowerCase()[0] in VOWELS ? 'an' : 'a'} $thin } result ?: 'nothing' } } import static GameUtils.* ... ESDC 2010 - 58
  • 59. ...Game example... ... class Room { def description def contents = [] } ... © ASERT 2006-2010 ESDC 2010 - 59
  • 60. ...Game example... ... class Player { def currentRoom def inventory = [] void look() { println "You are in ${currentRoom?.description?:'the void'} which contains ${listItems(currentRoom?.contents)} } void inv() { © ASERT 2006-2010 println "You are holding ${listItems(inventory)}" } void take(item) { if (currentRoom?.contents?.remove(item)) { inventory << item println "You took the $item" } else { println "I see no $item here" } } ESDC 2010 - 60
  • 61. ...Game example... ... void drop(item) { if (inventory?.remove(item)) { currentRoom?.contents << item println "You dropped the $item" } else { println "You don't have the $item" } © ASERT 2006-2010 } def propertyMissing(String name) { if (metaClass.respondsTo(this, name)) { this."$name"() } name } } ... ESDC 2010 - 61
  • 62. ... ...Game example... Room plainRoom = new Room(description:'a plain white room', contents:['dagger', 'emerald', 'key']) Player player = new Player(currentRoom:plainRoom) player.with{ inv look take dagger inv © ASERT 2006-2010 look take emerald inv look take key drop emerald inv look } assert player.inventory == ['dagger', 'key'] ESDC 2010 - 62
  • 63. ...Game example ... // now try some error conditions plainRoom.description = null player.with { drop gold take gold drop emerald © ASERT 2006-2010 take emerald take emerald look } ESDC 2010 - 63
  • 64. Grails Criteria // Account is a POJO in our domain/model def c = Account.createCriteria() def results = c { like("holderFirstName", "Fred%") and { between("balance", 500, 1000) eq("branch", "London") © ASERT 2006-2010 } maxResults(10) order("holderLastName", "desc") } // source: Grails doco: 5. Object Relational Mapping (GORM): 5.4.2 Criteria ESDC 2010 - 64
  • 65. Grails Criteria Example // Book is a POJO in our domain/model def book = Book.findByTitle("The Stand") book = Book.findByTitleLike("Harry Pot%") book = Book.findByReleaseDateBetween( firstDate, secondDate ) © ASERT 2006-2010 book = Book.findByReleaseDateGreaterThan( someDate ) book = Book.findByTitleLikeOrReleaseDateLessThan( "%Something%", someDate ) books = Book.findAllByTitleLikeAndReleaseDateGreaterThan( "%Java%", new Date()-30) // source: Grails doco: 5. Object Relational Mapping (GORM): 5.4.1 Dynamic Finders ESDC 2010 - 65
  • 66. Grails Bean Builder Example bb.beans { marge(Person) { name = "marge" husband = { Person p -> name = "homer" age = 45 props = [overweight:true, height:"1.8m"] } children = [bart, lisa] © ASERT 2006-2010 } bart(Person) { name = "Bart" age = 11 } lisa(Person) { name = "Lisa" age = 9 } } // source: 14. Grails and Spring: 14.3 Runtime Spring with the Beans DSL ESDC 2010 - 66
  • 67. Groovy Lab Example // require GroovyLab import static org.math.array.Matrix.* import static org.math.plot.Plot.* def A = rand(10,3) // random Matrix of 10 rows and 3 columns def B = fill(10,3,1.0) // one Matrix of 10 rows and 3 columns def C = A + B // support for matrix addition with "+" or "-" def D = A - 2.0 // support for number addition with "+" or "-" def E = A * B // support for matrix multiplication or division © ASERT 2006-2010 def F = rand(3,3) def G = F**(-1) // support for matrix power (with integers only) println A // display Matrix content plot("A",A,"SCATTER") // plot Matrix values as ScatterPlot def M = rand(5,5) + id(5) //Eigenvalues decomposition println "M=n" + M println "V=n" + V(M) println "D=n" + D(M) println "M~n" + (V(M) * D(M) * V(M)**(-1)) ESDC 2010 - 67
  • 68. Operator Overloading Example BigDecimal a = new BigDecimal(3.5d); BigDecimal b = new BigDecimal(4.0d); assert a.multiply(b).compareTo(new BigDecimal(14.0d)) == 0; assert a.multiply(b).equals(new BigDecimal(14.0d).setScale(1)); def c = 3.5, d = 4.0 assert c * d == 14.0 © ASERT 2006-2010 ESDC 2010 - 68
  • 69. Type Transformation Example class InventoryItem { def weight, name InventoryItem(Map m) { this.weight = m.weight; this.name = m.name } InventoryItem(weight, name) { this.weight = weight; this.name = name } InventoryItem(String s) { © ASERT 2006-2010 s.find(/weight=(d*)/) { all, w -> this.weight = w } s.find(/name=(.*)/) { all, n -> this.name = n } } } def room = [:] def gold = [weight:50, name:'Gold'] as InventoryItem def emerald = [10, 'Emerald'] as InventoryItem def dagger = ['weight=5, name=Dagger'] as InventoryItem room.contents = [gold, emerald, dagger] room.contents.each{ println it.dump() } ESDC 2010 - 69
  • 70. AST Builder • Numerous approaches, still evolving. “From code” approach: def result = new AstBuilder().buildFromCode { println "Hello World" } • Produces: BlockStatement -> ReturnStatement -> MethodCallExpression -> VariableExpression("this") -> ConstantExpression("println") -> ArgumentListExpression -> ConstantExpression("Hello World")
  • 71. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 71
  • 72. 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 - 72
  • 73. 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 - 73
  • 74. Groovy and Testing Tool Spectrum* Utilities Runners AllPairs, Combinations Native Groovy, JUnit, TestNG, Spock, EasyB, Polyglot languages JBehave, Cucumber, Robot Framework Logic programming Threads, Parallel / Web Database SOAP / Other Concurrency libraries Drivers Drivers REST Drivers Data-driven libraries Drivers Networking libraries WebTest DbUnit FEST © ASERT 2006-2010 XML Processing GroovyWS WebDriver DataSets Email Read/write files / JWebUnit SqlUnit XML-RPC FTP Excel / Word / CSV Reporting, Logging Tellurium groovy.sql CXF AntUnit Selenium JPA Axis2 Telnet HtmlUnit JDO JAX-WS SSH Tools Watij BigTable JAX-RS Exec iTest2, SoapUI, Twist, HttpBuilder JDBC IDEs, JMeter, Text Cyberneko editors, Recorders, Sahi, Build Tools, CI * Tools/libraries/frameworks don't always neatly fall into one category – still useful conceptually ESDC 2010 - 74
  • 75. HtmlUnit • 100% Java-based headless browser emulator – Can test any Web site: Java, .Net, PHP, Rails, ... • Open Source – Apache 2 license – Hosted at SourceForge – 7 committers (3 very active) – © ASERT 2006-2010 Very mature • Useful for: – Integration and acceptance testing – Screen scraping, deployment automation, ... • Used by other drivers: – Canoo WebTest , JWebUnit , WebDriver , JSFUnit , Celerity • Special features: – Easy ajax mode, emulation of multiple browsers ESDC 2010 - 75
  • 76. HtmlUnit: Testing New Blog Post... @Grab('net.sourceforge.htmlunit:htmlunit:2.6') import com.gargoylesoftware.htmlunit.WebClient def client = new WebClient() def page = client.getPage('http://localhost:8080/postForm') // check page title assert 'Welcome to SimpBlog' == page.titleText © ASERT 2006-2010 // fill in blog entry and post it def form = page.getFormByName('post') form.getInputByName('title'). setValueAttribute('Bart was here (and so was HtmlUnit)') form.getSelectByName('category').getOptions().find{ it.text == 'Home' }.setSelected(true) form.getTextAreaByName('content').setText('Cowabunga Dude!') def result = form.getInputByName('btnPost').click() ... ESDC 2010 - 76
  • 77. ...HtmlUnit: Testing New Blog Post ... // check blog post details assert result.getElementsByTagName('h1').item(0). textContent.matches('Post.*: Bart was here.*') def h3headings = result.getElementsByTagName('h3') © ASERT 2006-2010 assert h3headings.item(1).textContent == 'Category: Home' assert h3headings.item(2).textContent == 'Author: Bart' // expecting: // <table><tr><td><p>Cowabunga Dude!</p></td></tr></table> def cell = result.getByXPath('//TABLE//TR/TD')[0] def para = cell.getFirstChild() assert para.textContent == 'Cowabunga Dude!' ESDC 2010 - 77
  • 78. WebTest testing Web Sites def ant = new AntBuilder() def webtest_home = System.properties.'webtest.home' ant.taskdef(resource:'webtest.taskdef') { classpath { pathelement(location:"$webtest_home/lib") fileset(dir:"$webtest_home/lib", includes:"**/*.jar") } } © ASERT 2006-2010 def config_map = [:] ['protocol','host','port','basepath','resultfile', 'resultpath', 'summary', 'saveresponse','defaultpropertytype'].each { config_map[it] = System.properties['webtest.'+it] } ant.testSpec(name:'groovy: Test Groovy Scripting at creation time') { config(config_map) steps { invoke(url:'linkpage.html') for (i in 1..10) { verifyText(description:"verify number ${i} is on pages", text:"${i}") } } } ESDC 2010 - 78
  • 79. WebTest testing Emails def ant = new AntBuilder() def webtest_home = System.properties.'webtest.home' ant.taskdef(resource:'webtest.taskdef'){ classpath(){ pathelement(location:"$webtest_home/lib") fileset(dir:"$webtest_home/lib", includes:"**/*.jar") } } © ASERT 2006-2010 ant.testSpec(name:'Email Test'){ steps { emailSetConfig(server:'localhost', password:'password', username:'devteam@mycompany.org', type:'pop3') emailStoreMessageId(subject:'/Build notification/', property:'msg') emailStoreHeader(property:'subject', messageId:'#{msg}', headerName:'Subject') groovy('''def subject = step.webtestProperties.subject assert subject.startsWith('Build notification')''') emailMessageContentFilter(messageId:'#{msg}') verifyText(text:'Failed build') } } ESDC 2010 - 79
  • 80. Spock Testing Framework... © ASERT 2006-2010 ESDC 2010 - 80
  • 81. ...Spock Testing Framework • Testing framework for Java and Groovy • Highly expressive specification language – No assertion API – No record & replay @Speck mocking API @RunWith(Sputnik) – No class PublisherSubscriberSpeck { superfluous def "events are received by all subscribers"() { © ASERT 2006-2010 annotations def pub = new Publisher() – Meaningful def sub1 = Mock(Subscriber) assert error def sub2 = Mock(Subscriber) messages pub.subscribers << sub1 << sub2 when: pub.send("event") then: – Extensible 1 * sub1.receive("event") – Compatible 1 * sub2.receive("event") with JUnit } reportingwise } ESDC 2010 - 81
  • 82. Spock Example... import com.gargoylesoftware.htmlunit.WebClient import spock.lang.* import org.junit.runner.RunWith @Speck () @RunWith (Sputnik) class TestSimpBlogSpock { def page, subheadings, para, form, result @Unroll("When #author posts a #category blog with content '#content' it shoul © ASERT 2006-2010 def "when creating a new blog entry"() { given: page = new WebClient().getPage('http://localhost:8080/postForm') form = page.getFormByName('post') when: form.getInputByName('title').setValueAttribute("$author was here (and so form.getSelectByName('category').getOptions().find { it.text == category form.getSelectByName('author').getOptions().find { it.text == author }.se form.getTextAreaByName('content').setText(content) result = form.getInputByName('btnPost').click() subheadings = result.getElementsByTagName('h3') para = result.getByXPath('//TABLE//TR/TD/P')[0] ... ESDC 2010 - 82
  • 83. ...Spock Example ... then: page.titleText == 'Welcome to SimpBlog' result.getElementsByTagName('h1').item(0).textContent.matches("Post.*: $auth subheadings.item(1).textContent == "Category: $category" subheadings.item(2).textContent == "Author: $author" and: // Optional use of 'and:' para.textContent == content © ASERT 2006-2010 where: author << ['Bart', 'Homer', 'Lisa'] category << ['Home', 'Work', 'Food'] content << ['foo', 'bar', 'baz'] } } ESDC 2010 - 83
  • 84. EasyB • Description: BDD, Rspec-like testing library narrative 'segment flown', { as_a 'frequent flyer' i_want 'to accrue rewards points for every segment I fly' so_that 'I can receive free flights for my dedication to the airline' } scenario 'segment flown', { given 'a frequent flyer with a rewards balance of 1500 points' © ASERT 2006-2010 when 'that flyer completes a segment worth 500 points' then 'that flyer has a new rewards balance of 2000 points' } scenario 'segment flown', { given 'a frequent flyer with a rewards balance of 1500 points', { flyer = new FrequentFlyer(1500) } when 'that flyer completes a segment worth 500 points', { flyer.fly(new Segment(500)) } then 'that flyer has a new rewards balance of 2000 points', { flyer.pointsBalance.shouldBe 2000 } } ESDC 2010 - 84
  • 85. EasyB Example ... • When run will be marked as pending – perfect for ATDD scenario "Bart posts a new blog entry", { given "we are on the create blog entry page" when "I have entered 'Bart was here' as the title" and "I have entered 'Cowabunga Dude!' into the content" and "I have selected 'Home' as the category" © ASERT 2006-2010 and "I have selected 'Bart' as the author" and "I click the 'Create Post' button" then "I expect the entry to be posted" } ESDC 2010 - 85
  • 86. ...EasyB Example... description "Post Blog Entry Feature" narrative "for feature", { as_a "Blogger" i_want "to be able to post a blog" so_that "I can keep others informed" } before "posting blog", { given "we are on the create blog entry page", { © ASERT 2006-2010 webClient = new com.gargoylesoftware.htmlunit.WebClient() page = webClient.getPage('http://localhost:8080/postForm') } } scenario "Bart was here blog", { when "I have entered 'Bart was here' as the title", { form = page.getFormByName('post') form.getInputByName('title').setValueAttribute( 'Bart was here (and so was EasyB)') } ... ESDC 2010 - 86
  • 87. ...EasyB Example... ... and "I have entered 'Cowabunga Dude!' into the content", { form.getTextAreaByName('content').setText('Cowabunga Dude!') } and "I have selected 'Home' as the category", { form.getSelectByName('category').getOptions().find { it.text == 'Home' }.setSelected( } and "I click the 'Create Post' button", { result = form.getInputByName('btnPost').click() } © ASERT 2006-2010 then "I expect the entry to be posted", { // check blog post details assert result.getElementsByTagName('h1').item(0).textContent.matches('Post.*: Bart wa def h3headings = result.getElementsByTagName('h3') assert h3headings.item(1).textContent == 'Category: Home' // traditional style h3headings.item(2).textContent.shouldBe 'Author: Bart' // BDD style // expecting: <table><tr><td><p>Cowabunga Dude!</p></td></tr></table> def cell = result.getByXPath('//TABLE//TR/TD')[0] def para = cell.firstChild assert para.textContent == 'Cowabunga Dude!' // para.shouldHave textContent: 'Cowabunga Dude!' } } ESDC 2010 - 87
  • 88. ...EasyB Example... © ASERT 2006-2010 ESDC 2010 - 88
  • 89. ...EasyB Example 2 scenarios (including 1 pending) executed successfully. Story: simp blog initial scenario Bart posts a new blog entry [PENDING] given we are on the create blog entry page when I have entered 'Bart was here' as the title when I have entered 'Cowabunga Dude!' into the content [PENDING] when I have selected 'Home' as the category [PENDING] when I have selected 'Bart' as the author [PENDING] when I click the 'Create Post' button [PENDING] then I expect the entry to be posted [PENDING] Story: simp blog easyb is preparing to process 2 file(s) © ASERT 2006-2010 Post Blog Entry Feature Running simp blog initial story (SimpBlogInitialStory.groovy) for feature Scenarios run: 1, Failures: 0, Pending: 1, Time elapsed: 1.049 sec As a Blogger Running simp blog story (SimpBlogStory.groovy) I want to be able to post a blog Scenarios run: 1, Failures: 0, Pending: 0, Time elapsed: 1.356 sec So that I can keep others informed given we are on the create blog entry page 2 total behaviors ran (including 1 pending behavior) with no failures easyb execution passed scenario Bart was here blog when I have entered 'Bart was here' as the title when I have entered 'Cowabunga Dude!' into the content when I have selected 'Home' as the category when I click the 'Create Post' button then I expect the entry to be posted ESDC 2010 - 89
  • 90. Cucumber # language: en • Description Feature: Addition In order to avoid silly mistakes – Loose coupling As a math idiot between text spec I want to be told the sum of two numbers and step defns Scenario Outline: Add two numbers Given I have entered <input_1> into the calculator And I have entered <input_2> into the calculator When I press <button> Then the stored result should be <output> Examples: © ASERT 2006-2010 | input_1 | input_2 | button | output | | 20 | 30 | add | 50 | | 2 | 5 | add | 7 | | 0 | 40 | add | 40 | # language: en Feature: Division In order to avoid silly mistakes Cashiers must be able to calculate a fraction Scenario: Regular numbers Given I have entered 3 into the calculator And I have entered 2 into the calculator When I press divide Then the stored result should be 1.5 ESDC 2010 - 90
  • 91. Cucumber Example... # language: en @newpost Feature: New Blog Post In order to create a new blog entry Bloggers should be able to select their name and category and enter text © ASERT 2006-2010 Scenario: New Posting Given we are on the create blog entry page When I have entered "Bart was here" as the title And I have entered "Cowabunga Dude!" as the content And I have selected "Home" from the "category" dropdown And I have selected "Bart" from the "author" dropdown And I click the 'Create Post' button Then I should see a heading message matching "Post.*: Bart was here.*" ESDC 2010 - 91
  • 92. ...Cucumber Example... © ASERT 2006-2010 ESDC 2010 - 92
  • 93. ...Cucumber Example import com.gargoylesoftware.htmlunit.WebClient this.metaClass.mixin(cuke4duke.GroovyDsl) Given ~/we are on the create blog entry page/, { -> page = new WebClient().getPage('http://localhost:8080/postForm') } When(~/I have entered "(.*)" as the title/) {String title -> form = page.getFormByName('post') form.getInputByName('title').setValueAttribute(title + ' (and so was Cucumber)') } When(~'I have entered "(.*)" as the content') {String content -> © ASERT 2006-2010 form.getTextAreaByName('content').setText(content) } When(~'I have selected "(.*)" from the "(.*)" dropdown') {String option, String name -> form.getSelectByName(name).getOptions().find { it.text == option }.setSelected(true) } When(~"I click the 'Create Post' button") { -> result = form.getInputByName('btnPost').click() } Then(~'I should see a heading message matching "(.*)"') {String pattern -> // ensureThat result.getElementsByTagName('h1').item(0).textContent.matches(pattern) assert result.getElementsByTagName('h1').item(0).textContent.matches(pattern) } ESDC 2010 - 93
  • 94. Cucumber Data Driven Example... # language: en @newpost Feature: New Blog Post In order to create a new blog entry Bloggers should be able to select their name and category and enter text Scenario Outline: New Posting Given we are on the create blog entry page © ASERT 2006-2010 When I have entered "<title>" as the title And I have entered "<content>" as the content And I have selected "<category>" from the "category" dropdown And I have selected "<author>" from the "author" dropdown And I click the 'Create Post' button Then I should see a heading message matching "Post.*: <title>.*" Examples: | title | content | category | author | | Title 1 | Content 1 | Home | Bart | | Title 2 | Content 2 | Work | Homer | | Title 3 | Content 3 | Food | Marge | ESDC 2010 - 94
  • 95. ...Cucumber Data Driven Example © ASERT 2006-2010 ESDC 2010 - 95
  • 96. JBehave • Description – Behaviour-driven development in Java • Also works out of the box for Groovy – Behavior scenarios written in text • Use the words Given, When, Then and And. – Mapped using regular expressions and annotations © ASERT 2006-2010 to step methods – Web Runner available for non-technical users to easily run tests – Hooks to Selenium available in JBehave Web • Other Java libraries (e.g. HtmlUnit) easy to use too – Supports parameter converters • Getting 'String' parameters into appropriate Object values – Supports a 'StepDoc' function • For listing available scenario clauses ESDC 2010 - 96
  • 97. JBehave Example... Given we are on the create blog entry page When I have entered "Bart was here" as the title And I have entered "Cowabunga Dude!" as the content And I have selected "Home" from the "category" dropdown And I have selected "Bart" from the "author" dropdown And I click the 'Create Post' button Then I should see a heading message matching "Post.*: Bart was here.*" import org.jbehave.scenario.Scenario © ASERT 2006-2010 import org.jbehave.scenario.steps.Steps class NewPostScenario extends Scenario { NewPostScenario() { super([new CreateBlogSteps()] as Steps[]) } } Scenario: Given we are on the create blog entry page When I have entered "Bart was here" as the title And I have entered "Cowabunga Dude!" as the content And I have selected "Home" from the "category" dropdown And I have selected "Bart" from the "author" dropdown And I click the 'Create Post' button Then I should see a heading message matching "Post.*: Bart was here.*" ESDC 2010 - 97
  • 98. ...JBehave Example import org.jbehave.scenario.steps.Steps import org.jbehave.scenario.annotations.* import com.gargoylesoftware.htmlunit.WebClient class CreateBlogSteps extends Steps { def page, form, result @Given("we are on the create blog entry page") void gotoEntryPage() { page = new WebClient().getPage('http://localhost:8080/postForm') } © ASERT 2006-2010 @When('I have entered "$title" as the title') void enterTitle(String title) { form = page.getFormByName('post') form.getInputByName('title'). setValueAttribute(title + ' (and so was JBehave)') } @When('I have entered "$content" as the content') void enterContent(String content) { form.getTextAreaByName('content').setText(content) } ... Example uses HtmlUnit which must be added to CLASSPATH ESDC 2010 - 98
  • 99. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing Polyglot Groovy  Other languages, other styles • Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 99
  • 100. Polyglot Programming... @Grab('org.clojure:clojure:1.0.0') import clojure.lang.Compiler import clojure.lang.RT ... def jy = getEngine("jython") def src = new File('temp.clj') jy?.eval(''' src.text = ''' def factorial(n): (ns groovy) i=fact=1 © ASERT 2006-2010 (defn factorial [n] while i <= n: (if (< n 2) fact=fact*i 1 i=i+1 (* n (factorial (- n 1)))) return fact ''' result = factorial(4) src.withReader { reader -> ''') Compiler.load reader println jy?.result } def fac = RT.var('groovy', 'factorial') println fac.invoke(4) ESDC 2010 - 100
  • 101. ...Polyglot Programming • But so what? – I can use Groovy for Scripting my environment and or leveraging its runners and other testing capabilities – I can call out to other languages when needed © ASERT 2006-2010 • Cucumber via JRuby for more native control • Watir instead of Watij • ScalaCheck for test data generation • Jython for Robot Framework for more native control • Rhino for JavaScript testing • Rules engine integration ESDC 2010 - 101
  • 102. Functional Programming in Groovy • Java functional programming goodness – Normal OO methods – Ability to have immutable types – Some concurrency building blocks • Closures for greater flexibility – Enabler for concurrency © ASERT 2006-2010 • Lazy evaluation with Closures – Built in to some libraries, e.g. data access • Metaprogramming to enhance existing libraries • Third party Java libraries – Functional Java, FunctionalJ, Commons Functor, Scala ESDC 2010 - 102
  • 103. Functional Programming… def fac(n) { n == 0 ? 1 : n * fac(n - 1) } assert 24 == fac(4) // define and use infinite streams def integers(n) { cons(n, { integers(n+1) }) } © ASERT 2006-2010 def naturalnumbers = integers(1) assert '1 2 3 4 5 6 7 8 9 10' == naturalnumbers.take(10).join(' ') def evennumbers = naturalnumbers.filter{ it % 2 == 0 } assert '2 4 6 8 10 12 14 16 18 20' == evennumbers.take(10).join(' ') ESDC 2010 - 103
  • 104. …Functional Programming… import fj.data.Stream //some metaprogramming to make fj mesh well with Groovy Stream.metaClass.filter = { Closure c -> delegate.filter(c as fj.F) } Stream.metaClass.getAt = { n -> delegate.index(n) } © ASERT 2006-2010 Stream.metaClass.getAt = { Range r -> r.collect{ delegate.index(it)}} Stream.metaClass.asList = { -> delegate.toCollection().asList() } def evens = Stream.range(0).filter{ it % 2 == 0 } assert evens.take(5).asList() == [0, 2, 4, 6, 8] assert (8..12).collect{ evens[it] } == [16, 18, 20, 22, 24] assert evens[3..6] == [6, 8, 10, 12] ESDC 2010 - 104
  • 105. …Functional Programming import fj.data.Stream import static fj.data.Stream.cons // some metaprogramming to make Closures mesh well with // fj functions and products Stream.metaClass.filterTail = { Closure c -> delegate.tail()._1().filter(c as fj.F) } Stream.metaClass.static.cons = { h, Closure c -> © ASERT 2006-2010 delegate.cons(h, ['_1':c] as fj.P1) } def sieve(nums) { cons nums.head(), { sieve nums.filterTail{ n -> n % nums.head() != 0 } } } println sieve(Stream.range(2)).index(100) // => 547 ESDC 2010 - 105
  • 106. Enabling a functional style … • Consider the Maximum Segment Sum (MSS) problem – Take a list of integers; the MSS is the maximum of the sums of any number of adjacent integers • Imperative solution: © ASERT 2006-2010 def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] def size = numbers.size() def max = null (0..<size).each { from -> (from..<size).each { to -> def sum = numbers[from..to].sum() if (max == null || sum > max) max = sum } } println "Maximum Segment Sum of $numbers is $max" ESDC 2010 - 106
  • 107. ... Enabling a functional style … • A first attempt at a more functional style: def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] © ASERT 2006-2010 def size = numbers.size() def max = [0..<size, 0..<size].combinations().collect{ numbers[it[0]..it[1]].sum() }.max() println "Maximum Segment Sum of $numbers is $max" ESDC 2010 - 107
  • 108. ... Enabling a functional style … • An even more functional style – A known solution using functional composition: mss = max º sum* º (flatten º tails* º inits) – Where inits and tails are defined as follows: © ASERT 2006-2010 letters = ['a', 'b', 'c', 'd'] assert letters.inits() == [ assert letters.tails() == [ ['a'], ['d'], ['a', 'b'], ['c', 'd'], ['a', 'b', 'c'], ['b', 'c', 'd'], ['a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd'] ] ] ESDC 2010 - 108
  • 109. ... Enabling a functional style … • An even more functional style mss = max º sum* º (flatten º tails* º inits) def segs = { it.inits()*.tails().sum() } def solve = { segs(it)*.sum().max() } def numbers = [31,-41,59,26,-53,58,97,-93,-23,84] © ASERT 2006-2010 println "Maximum Segment Sum of $numbers is ${solve numbers}" Notes: – sum() is one-level flatten in Groovy, flatten() is recursive – Metaprogramming allowed us to enhance all Lists List.metaClass { inits{ (0..<delegate.size()).collect{ delegate[0..it] } } tails{ delegate.reverse().inits() } } Source: http://hamletdarcy.blogspot.com/2008/07/groovy-vs-f-showdown-side-by-side.html ESDC 2010 - 109
  • 110. ...Constraint/Logic Programming... © ASERT 2006-2010 Source: http://xkcd.com/287/ ESDC 2010 - 110
  • 111. ...Constraint/Logic Programming... // requires choco 2.1.0-basic.jar from http://choco.emn.fr/ import static choco.Choco.* import choco.kernel.model.variables.integer.IntegerVariable def m = new choco.cp.model.CPModel() def s = new choco.cp.solver.CPSolver() def menu = [ Found a solution: © ASERT 2006-2010 'Mixed fruit' : 215, 7 * Mixed fruit 'French fries' : 275, Found a solution: 'Side salad' : 335, 1 * Mixed fruit 'Hot wings' : 355, 2 * Hot wings 'Mozzarella sticks' : 420, 1 * Sampler plate 'Sampler plate' : 580 ] def numOrdered = new IntegerVariable[menu.size()] def priceEach = new int[menu.size()] def sum = 1505 ... ESDC 2010 - 111
  • 112. ...Constraint/Logic Programming ... menu.eachWithIndex { name, price, i -> // number ordered >= 0 // number ordered * price <= sum numOrdered[i] = makeIntVar(name, 0, sum.intdiv(price)) priceEach[i] = price } m.addConstraint(eq(scalar(numOrdered, priceEach), sum)) © ASERT 2006-2010 s.read(m) def more = s.solve() while (more) { println "Found a solution:" numOrdered.each { def v = s.getVar(it) if (v.val) println " $v.val * $v.name" } more = s.nextSolution() } ESDC 2010 - 112
  • 113. ModelJUnit... // require modeljunit.jar import nz.ac.waikato.modeljunit.coverage.* import nz.ac.waikato.modeljunit.* class VendingMachineModel implements FsmModel { def state = 0 // 0,25,50,75,100 void reset(boolean testing) {state = 0} boolean vendGuard() {state == 100} @Action void vend() {state = 0} boolean coin25Guard() {state <= 75} © ASERT 2006-2010 @Action void coin25() {state += 25} boolean coin50Guard() {state <= 50} @Action void coin50() {state += 50} } def tester = new RandomTester(new VendingMachineModel()) tester.buildGraph() def metrics = [new ActionCoverage(), new StateCoverage(), new TransitionCoverage(), new TransitionPairCoverage()] metrics.each { tester.addCoverageMetric it } tester.addListener "verbose" tester.generate 20 println 'nMetrics Summary:' tester.printCoverage() ESDC 2010 - 113
  • 114. ...ModelJUnit... ... done (0, coin50, 50) done (50, coin25, 75) done (75, coin25, 100) done Random reset(true) done (0, coin50, 50) done (50, coin25, 75) done (75, coin25, 100) done (100, vend, 0) ... © ASERT 2006-2010 done (0, coin50, 50) done (50, coin50, 100) Metrics Summary: done (100, vend, 0) action coverage: 3/3 done (0, coin25, 25) state coverage: 5/5 done (25, coin25, 50) transition coverage: 7/8 done Random reset(true) transition-pair coverage: 8/12 ... done (0, coin50, 50) done (50, coin25, 75) done (75, coin25, 100) done (100, vend, 0) done (0, coin50, 50) done (50, coin25, 75) ... ESDC 2010 - 114
  • 115. ...ModelJUnit: SimpBlog Case Study © ASERT 2006-2010 ESDC 2010 - 115
  • 116. ScalaCheck • Description – Tool for testing Scala, Java and Groovy programs – Based on property specifications and automatic random test data generation > scala -classpath ScalaCheck-1.5.jar Welcome to Scala version 2.7.6.final © ASERT 2006-2010 scala> import org.scalacheck.Prop.forAll scala> val propConcatLists = forAll { (l1: List[Int], l2: List[Int]) => | l1.size + l2.size == (l1 ::: l2).size } propConcatLists: org.scalacheck.Prop = Prop scala> propConcatLists.check + OK, passed 100 tests. scala> val propSqrt = forAll { (n: Int) => scala.Math.sqrt(n*n) == n } propSqrt: org.scalacheck.Prop = Prop scala> propSqrt.check ! Falsified after 2 passed tests. > ARG_0: "-1" (1 shrinks, original arg: "-2") ESDC 2010 - 116
  • 117. ScalaCheck: SimpBlog Case Study... class SimpBlogChecker { static postAndCheck = null static clean(s) { s.replace('', '').replace('n', 'n') } static ResultHolder postAndReturn(String title, String content, String author, String category) { if (!postAndCheck) postAndCheck = new GroovyShell().parse(''' @Grab('net.sourceforge.htmlunit:htmlunit:2.5') import com.gargoylesoftware.htmlunit.WebClient def page = new WebClient().getPage('http://localhost:8080/postForm') def form = page.getFormByName('post') form.getInputByName('title').setValueAttribute(title) © ASERT 2006-2010 form.getSelectByName('category').getOptions().find { it.text == category }.setSelected(true) form.getSelectByName('author').getOptions().find { it.text == author }.setSelected(true) form.getTextAreaByName('content').setText(content) def result = form.getInputByName('btnPost').click() def titleResult = result.getElementsByTagName('h1').item(0).textContent def h3headings = result.getElementsByTagName('h3') def categoryResult = h3headings.item(1).textContent def authorResult = h3headings.item(2).textContent def para = result.getByXPath('//TABLE//TR/TD/P')[0] def contentResult = para.textContent return new ResultHolder(titleResult, contentResult, authorResult, categoryResult) ''') ... ESDC 2010 - 117
  • 118. ...ScalaCheck: SimpBlog Case Study... ... def binding = new Binding() binding.setVariable('title', title) binding.setVariable('content', clean(content)) binding.setVariable('author', author) binding.setVariable('category', category) postAndCheck.binding = binding postAndCheck.run() } } © ASERT 2006-2010 class ResultHolder { String title, content, author, category ResultHolder(String title, String content, String author, String category) { this.title = title this.content = content this.author = author this.category = category } } > groovyc SimpBlogChecker.groovy ESDC 2010 - 118
  • 119. ...ScalaCheck: SimpBlog Case Study... //CheckSimpBlog.scala import org.scalacheck.Prop._ import org.scalacheck.ConsoleReporter.testStatsEx import org.scalacheck.Test.check import org.scalacheck.Arbitrary._ import org.scalacheck.Gen._ object CheckSimpBlog { © ASERT 2006-2010 val fieldsGen = for { title <- elements("Title 1", "Title 2") content <- arbitrary[String] author <- elements("Bart", "Homer", "Lisa", "Marge", "Maggie") category <- elements("Home", "Work", "Food", "Travel") } yield (title, content, author, category) ... > scalac -classpath .;../lib/ScalaCheck-1.5.jar; ../../groovy-1.7-beta-2-SNAPSHOT/embeddable/groovy-all- 1.7-beta-2-SNAPSHOT.jar CheckSimpBlog.scala ESDC 2010 - 119
  • 120. ...ScalaCheck: SimpBlog Case Study... ... val enterFieldsAcceptedAndEchoedProperty = forAll(fieldsGen)( fields => { val (title, content, author, category) = fields val result = SimpBlogChecker.postAndReturn(title, content, author, category) result.getTitle contains title result.getContent contains content result.getAuthor contains author result.getCategory contains category } ) © ASERT 2006-2010 val tests = scala.List( ("enterFieldsAcceptedAndEchoedProperty", enterFieldsAcceptedAndEchoedProperty) ) def main(args: scala.Array[String]) = tests foreach { case (name, p) => testStatsEx(name, check(p)) } } > scala -classpath .;../lib/ScalaCheck-1.5.jar;../../groovy-1.7-beta -2-SNAPSHOT/embeddable/groovy-all-1.7-beta-2-SNAPSHOT.jar;../../ groovy-1.7-beta-2-SNAPSHOT/lib/ivy-2.1.0-rc2.jar CheckSimpBlog + OK, passed 100 tests. ESDC 2010 - 120
  • 121. ...ScalaCheck: SimpBlog Case Study © ASERT 2006-2010 ESDC 2010 - 121
  • 122. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy Parallel Processing • Enterprise Groovy • More Info ESDC 2010 - 122
  • 123. Concurrent Programming in Groovy • Java concurrent programming goodness – Normal OO methods – Ability to have immutable types – Some concurrency building blocks • Process Enhancements – AntBuilder and GDK methods © ASERT 2006-2010 • Closures for greater flexibility – Enabler for concurrency – Closure is Runnable and Callable • Third party Java libraries – Functional Java (Actors) – Cascading.groovy subproject for Hadoop clusters – Jetlang – GridGain – Groovy actors ESDC 2010 - 123
  • 124. Fibonacci… START = 8 END = 16 fib = {n -> n < 2 ? n : fib(n - 1) + fib(n - 2) } (START..END).each {num -> println "n:$num => ${fib(num)}" } © ASERT 2006-2010 ESDC 2010 - 124
  • 125. …Fibonacci… import java.util.concurrent.* CUTOFF = 12 // not worth parallelizing for small n THREADS = 100 println "Calculating Fibonacci sequence in parallel..." serialFib = {n -> (n < 2) ? n : serialFib(n - 1) + serialFib(n - 2) } pool = Executors.newFixedThreadPool(THREADS) defer = {c -> pool.submit(c as Callable) } fib = {n -> © ASERT 2006-2010 if (n < CUTOFF) return serialFib(n) def left = defer { fib(n - 1) } def right = defer { fib(n - 2) } left.get() + right.get() } (8..16).each {n -> println "n=$n => ${fib(n)}" } pool.shutdown() ESDC 2010 - 125
  • 126. gpars (formerly GParallelizer) • http://gpars.codehaus.org/ • Library classes and DSL sugar providing intuitive ways for Groovy developers to handle tasks concurrently. Logical parts: – Actors provide a Groovy implementation of Scala-like actors including "remote" actors on other machines – Dataflow Concurrency supports natural shared-memory © ASERT 2006-2010 concurrency model, using single-assignment variables – Asynchronizer extends the Java 1.5 built-in support for executor services to enable multi-threaded collection and closure processing – Parallelizer uses JSR-166y Parallel Arrays to enable multi-threaded collection processing – Safe a non-blocking mt-safe reference to mutable state that is inspired by "agents" in Clojure ESDC 2010 - 126
  • 127. gpars/GParallelizer... // run multiple closures in parallel Asynchronizer.withAsynchronizer { assert [10, 20] == AsyncInvokerUtil.doInParallel( {calculateA()}, {calculateB()} ) © ASERT 2006-2010 } // multiply numbers asynchronously Parallelizer.withParallelizer(5) { def result = [1, 2, 3, 4, 5].collectAsync {it * 2} assert result == [2, 4, 6, 8, 10] } ESDC 2010 - 127
  • 128. ...gpars/GParallelizer... // support for dataflow to avoid doing synchronisation import static org.gparallelizer.dataflow.DataFlow.thread final def x = new DataFlowVariable() final def y = new DataFlowVariable() final def z = new DataFlowVariable() © ASERT 2006-2010 thread { z << x.val + y.val println "Result: ${z.val}" } thread { x << 10 } thread { y << 5 } ESDC 2010 - 128
  • 129. ...gpars/GParallelizer... // actor support import static org.gparallelizer.actors.pooledActors.PooledActors.* def me = actor { friend.send('Hi') react(10.seconds) { © ASERT 2006-2010 // continue conversation } } me.metaClass.onTimeout = {-> friend.send('I see, busy as usual. Never mind.')} me.start() ESDC 2010 - 129
  • 130. ...gpars/GParallelizer // safe support import org.gparallelizer.actors.pooledActors.SafeVariable def jugMembers = new SafeVariable<List>(['Me']) //add Me jugMembers.send {it.add 'James'} //add James final Thread t1 = Thread.start { jugMembers.send {it.add 'Joe'} //add Joe © ASERT 2006-2010 } final Thread t2 = Thread.start { jugMembers << {it.add 'Dave'} //add Dave jugMembers << {it.add 'Alice'} //add Alice } [t1, t2]*.join() println jugMembers.val jugMembers.valAsync {println "Current members: $it"} System.in.read() jugMembers.stop() ESDC 2010 - 130
  • 131. gpars for testing @Grab('net.sourceforge.htmlunit:htmlunit:2.6') import com.gargoylesoftware.htmlunit.WebClient @Grab('org.gparallelizer:GParallelizer:0.8.3') import static org.gparallelizer.Parallelizer.* def testCases = [ ['Home', 'Bart', 'Content 1'], ['Work', 'Homer', 'Content 2'], ['Travel', 'Marge', 'Content 3'], © ASERT 2006-2010 ['Food', 'Lisa', 'Content 4'] ] withParallelizer(3) { testCases.eachAsync{ category, author, content -> postAndCheck category, author, content } } private postAndCheck(category, author, content) { ... ESDC 2010 - 131
  • 132. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing Enterprise Groovy • More Info ESDC 2010 - 132
  • 133. Integration with Spring... // traditional approach using a beans xml file import org.springframework.context.support.ClassPathXmlApplicationContext def ctx = new ClassPathXmlApplicationContext('calcbeans.xml') def calc = ctx.getBean('calcBean') println calc.doAdd(3, 4) // => 7 © ASERT 2006-2010 // using BeanBuilder def bb = new grails.spring.BeanBuilder() bb.beans { adder(AdderImpl) calcBean(CalcImpl2) { adder = adder } } def ctx = bb.createApplicationContext() def calc = ctx.getBean('calcBean') println calc.doAdd(3, 4) // => 7 ESDC 2010 - 133
  • 134. ...Integration with Spring // using annotations import org.springframework.stereotype.Component @Component class AdderImpl { def add(x, y) { x + y } } import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component © ASERT 2006-2010 @Component class CalcImpl3 { @Autowired private AdderImpl adder def doAdd(x, y) { adder.add(x, y) } } import org.springframework.context.support.GenericApplicationContext import org.springframework.context.annotation.ClassPathBeanDefinitionScanner def ctx = new GenericApplicationContext() new ClassPathBeanDefinitionScanner(ctx).scan('') // scan root package for components ctx.refresh() def calc = ctx.getBean('calcImpl3') println calc.doAdd(3, 4) // => 7 ESDC 2010 - 134
  • 135. Dependency Injection: Guice import com.google.inject.* @ImplementedBy(CalculatorImpl) interface Calculator { def add(a, b) } @Singleton class CalculatorImpl implements Calculator { private total = 0 © ASERT 2006-2010 def add(a, b) { total++; a + b } def getTotalCalculations() { 'Total Calculations: ' + total } String toString() { 'Calc: ' + hashCode()} } class Client { @Inject Calculator calc // ... } def injector = Guice.createInjector() // ... ESDC 2010 - 135
  • 136. Topics • Groovy Intro • Leveraging the language • Use and abuse of Design Patterns • Web Services • Writing DSLs © ASERT 2006-2010 • Groovy Testing • Polyglot Groovy • Parallel Processing • Enterprise Groovy More Info ESDC 2010 - 136
  • 137. 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 - 137
  • 138. ...More Information © ASERT 2006-2010 138