paulking groovy

138
© ASERT 2006-2010 Dr Paul King [email protected] @paulk_asert ASERT, Australia Groovy Power Features!

Upload: d0nn9n

Post on 10-May-2015

1.759 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: Paulking groovy

© A

SE

RT

2006-2

010

Dr Paul King

[email protected]

@paulk_asert

ASERT, Australia

Groovy Power Features!

Page 2: Paulking groovy

Topics

Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 2

© A

SE

RT

2006-2

010

Page 3: Paulking groovy

QCON 2010 - 3

© A

SE

RT

2006-2

010

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.”

Groovy = Java – boiler plate code+ mostly dynamic typing+ closures+ domain specific languages+ builders+ metaprogramming+ GDK library

Page 4: Paulking groovy

QCON 2010 - 4

© A

SE

RT

2006-2

010

Groovy Goodies Overview• Fully object oriented

• Closures: reusable

and assignable

pieces of code

• Operators can be

overloaded

• Multimethods

• Literal declaration for

lists (arrays), maps,

ranges and regular

expressions

• GPath: efficient

object navigation

• GroovyBeans

• grep and switch

• Templates, builder,

swing, Ant, markup,

XML, SQL, XML-RPC,

Scriptom, Grails,

tests, Mocks

Page 5: Paulking groovy

Growing Acceptance …

A slow and steady start but now gaining in

momentum, maturity and mindshare

Now free

Page 6: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 6

Page 7: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 7

Groovy and Grails downloads:

70-90K per month and growing

Page 8: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 8

Source: http://www.grailspodcast.com/

Source: http://www.micropoll.com/akira/mpresult/501697-116746

Page 9: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 9

http://www.java.net

http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes

Page 10: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 10http://www.leonardoborges.com/writings

What alternative JVM language are you using or intending to use

Page 11: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 11

http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com)

Page 12: Paulking groovy

… Growing Acceptance …

© A

SE

RT

2006-2

010

QCON 2010 - 12

http://pollpigeon.com/jsf-grails-wicket/r/25665/

Page 13: Paulking groovy

… Growing Acceptance

QCON 2010 - 13

© A

SE

RT

2006-2

010

Page 14: Paulking groovy

QCON 2010 - 14

© A

SE

RT

2006-2

010

The Landscape of JVM Languages

Java bytecode calls

for static types

Dynamic features call

for dynamic types

mostly

dynamic

typing

The terms “Java Virtual Machine” and “JVM” mean a Virtual Machine for the Java™ platform.

Page 15: Paulking groovy

QCON 2010 - 15

© A

SE

RT

2006-2

010

Groovy StarterSystem.out.println("Hello, World!"); // optional semicolon,println 'Hello, World!' // System.out, brackets,

// main() method, class defn

def name = 'Guillaume' // dynamic typingprintln "$name, I'll get the car." // GString

String longer = """${name}, the caris in the next row.""" // multi-line string

// with static typing

assert 0.5 == 1/2 // BigDecimal equals()

def printSize(obj) { // optional duck typingprint obj?.size() // safe dereferencing

}

def pets = ['ant', 'bee', 'cat'] // native list syntaxpets.each { pet -> // closure support

assert pet < 'dog' // overloading '<' on String} // or: for (pet in pets)...

Page 16: Paulking groovy

A Better Java...

QCON 2010 - 16

© A

SE

RT

2006-2

010

import java.util.List;import java.util.ArrayList;

class Erase {private List removeLongerThan(List strings, int length) {

List result = new ArrayList();for (int i = 0; i < strings.size(); i++) {

String s = (String) strings.get(i);if (s.length() <= length) {

result.add(s);}

}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);Erase e = new Erase();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);

}}

}

This code

is valid

Java and

valid Groovy

Based on an

example by

Jim Weirich

& Ted Leung

Page 17: Paulking groovy

...A Better Java...

QCON 2010 - 17

© A

SE

RT

2006-2

010

import java.util.List;import java.util.ArrayList;

class Erase {private List removeLongerThan(List strings, int length) {

List result = new ArrayList();for (int i = 0; i < strings.size(); i++) {

String s = (String) strings.get(i);if (s.length() <= length) {

result.add(s);}

}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);Erase e = new Erase();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);

}}

}

Do the

semicolons

add anything?

And shouldn‟t

we us more

modern list

notation?

Why not

import common

libraries?

Page 18: Paulking groovy

...A Better Java...

QCON 2010 - 18

© A

SE

RT

2006-2

010

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) {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)}

}}

Page 19: Paulking groovy

...A Better Java...

QCON 2010 - 19

© A

SE

RT

2006-2

010

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) {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)}

}}

Do we need

the static types?

Must we always

have a main

method and

class definition?

How about

improved

consistency?

Page 20: Paulking groovy

...A Better Java...

QCON 2010 - 20

© A

SE

RT

2006-2

010

def removeLongerThan(strings, length) {def result = new ArrayList()for (s in strings) {

if (s.size() <= length) {result.add(s)

}}return result

}

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)}

Page 21: Paulking groovy

...A Better Java...

QCON 2010 - 21

© A

SE

RT

2006-2

010

def removeLongerThan(strings, length) {def result = new ArrayList()for (s in strings) {

if (s.size() <= length) {result.add(s)

}}return result

}

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)}

Shouldn‟t we

have special

notation for lists?

And special

facilities for

list processing?

Is „return‟

needed at end?

Page 22: Paulking groovy

...A Better Java...

QCON 2010 - 22

© A

SE

RT

2006-2

010

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) }

Page 23: Paulking groovy

...A Better Java...

QCON 2010 - 23

© A

SE

RT

2006-2

010

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) }

Is the method

now needed?

Easier ways to

use common

methods?

Are brackets

required here?

Page 24: Paulking groovy

...A Better Java...

QCON 2010 - 24

© A

SE

RT

2006-2

010

names = ["Ted", "Fred", "Jed", "Ned"]println namesshortNames = names.findAll{ it.size() <= 3 }println shortNames.size()shortNames.each{ println it }

Page 25: Paulking groovy

...A Better Java

QCON 2010 - 25

© A

SE

RT

2006-2

010

names = ["Ted", "Fred", "Jed", "Ned"]println namesshortNames = names.findAll{ it.size() <= 3 }println shortNames.size()shortNames.each{ println it }

[Ted, Fred, Jed, Ned]3TedJedNed

Page 26: Paulking groovy

Topics

• Groovy Intro

Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 26

© A

SE

RT

2006-2

010

Page 27: Paulking groovy

Better Control Structures: Switch

• Extensible– Implement your own isCase() method

QCON 2010 - 27

© A

SE

RT

2006-2

010

switch (10) {case 0 : assert false ; breakcase 0..9 : assert false ; breakcase [8,9,11] : assert false ; breakcase Float : assert false ; breakcase {it%3 == 0} : assert false ; breakcase ~/../ : assert true ; breakdefault : assert false ; break

}

Page 28: Paulking groovy

QCON 2010 - 28

© A

SE

RT

2006-2

010

Better Control Structures: Switch – Custom isCase

enum Color {

yellow, orange, purple

def isCase(thing) { thing.color == this }

}

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) {

switch (f) {

case yellow: return 'Found something yellow'

case orange: return 'Found something orange'

default: return 'Unknown color'

}

}Found something yellowFound something orangeUnknown color

WARNING:

Advanced

Topic!

Page 29: Paulking groovy

QCON 2010 - 29

© A

SE

RT

2006-2

010

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) {

switch (f.color) {

case [yellow, orange]:

return "Found something $f.color"

default: return 'Unknown color'

}

}

Found something yellowFound something orangeUnknown color

Page 30: Paulking groovy

QCON 2010 - 30

© A

SE

RT

2006-2

010

Better Control Structures: Switch – Custom isCase

enum Color {yellow, orange, purpledef isCase(thing) {

thing?.hasProperty('color') && thing.color == this }}

enum State {peeled, unpeeleddef isCase(thing) {

try {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 }...

WARNING:

Advanced Topic!

Page 31: Paulking groovy

QCON 2010 - 31

© A

SE

RT

2006-2

010

Better Control Structures: Switch – Custom isCase

...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) {

case yellowAndPeeled:return 'Possibly found a banana split'

case yellow:return 'Found something yellow'

case peeled:return 'Found something peeled'

default:return 'No comment'

}}

WARNING:

Advanced Topic!

Possibly found a banana splitNo commentFound something peeled

Page 32: Paulking groovy

QCON 2010 - 32

© A

SE

RT

2006-2

010

Better Control Structures: Switch Poker…

suits = 'SHDC'ranks = '23456789TJQKA'suit = { String card -> suits.indexOf(card[1]) }rank = { String card -> ranks.indexOf(card[0]) }rankSizes = { List cards ->

cards.groupBy(rank).collect{ k, v -> v.size() }.sort() }rankValues = { List cards ->

cards.collect{ rank(it) }.sort() }// ...

8C TS KC 9H 4S 7D 2S 5D 3S AC

hand1 hand2

println rankSizes(["7S", "7H", "2H", "7D", "AH"])// => [1, 1, 3]

Page 33: Paulking groovy

QCON 2010 - 33

© A

SE

RT

2006-2

010

…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) }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] }// ...

Page 34: Paulking groovy

QCON 2010 - 34

© A

SE

RT

2006-2

010

… Better Control Structures: Switch Poker

// ...def rankHand(List cards) {

switch (cards) {case straightFlush : return 9case fourOfAKind : return 8case fullHouse : return 7case flush : return 6case straight : return 5case threeOfAKind : return 4case twoPair : return 3case pair : return 2default : return 1

}}// ...

Page 35: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 35

© A

SE

RT

2006-2

010

Page 36: Paulking groovy

Grapes / Grab

QCON 2010 - 36

© A

SE

RT

2006-2

010

// Google Collections exampleimport com.google.common.collect.HashBiMap

@Grab(group='com.google.collections',module='google-collections',version='1.0-rc2')

def getFruit() {[ grape:'purple',

lemon:'yellow',orange:'orange' ] as HashBiMap

}

assert fruit.lemon == 'yellow'assert fruit.inverse().yellow == 'lemon'

Page 37: Paulking groovy

Better Design Patterns: Immutable...

• Java Immutable Class– As per Joshua Bloch

Effective Java

QCON 2010 - 37

© A

SE

RT

2006-2

010

public final class Punter {private final String first;private final String last;

public String getFirst() {return first;

}

public String getLast() {return last;

}

@Overridepublic int hashCode() {

final int prime = 31;int result = 1;result = prime * result + ((first == null)

? 0 : first.hashCode());result = prime * result + ((last == null)

? 0 : last.hashCode());return result;

}

public Punter(String first, String last) {this.first = first;this.last = last;

}// ...

// ...@Overridepublic boolean equals(Object obj) {

if (this == obj)return true;

if (obj == null)return false;

if (getClass() != obj.getClass())return false;

Punter other = (Punter) obj;if (first == null) {

if (other.first != null)return false;

} else if (!first.equals(other.first))return false;

if (last == null) {if (other.last != null)

return false;} else if (!last.equals(other.last))

return false;return true;

}

@Overridepublic String toString() {

return "Punter(first:" + first+ ", last:" + last + ")";

}

}

Page 38: Paulking groovy

...Better Design Patterns: Immutable...

QCON 2010 - 38

© A

SE

RT

2006-2

010

public final class Punter {private final String first;private final String last;

public String getFirst() {return first;

}

public String getLast() {return last;

}

@Overridepublic int hashCode() {

final int prime = 31;int result = 1;result = prime * result + ((first == null)

? 0 : first.hashCode());result = prime * result + ((last == null)

? 0 : last.hashCode());return result;

}

public Punter(String first, String last) {this.first = first;this.last = last;

}// ...

// ...@Overridepublic boolean equals(Object obj) {

if (this == obj)return true;

if (obj == null)return false;

if (getClass() != obj.getClass())return false;

Punter other = (Punter) obj;if (first == null) {

if (other.first != null)return false;

} else if (!first.equals(other.first))return false;

if (last == null) {if (other.last != null)

return false;} else if (!last.equals(other.last))

return false;return true;

}

@Overridepublic String toString() {

return "Punter(first:" + first+ ", last:" + last + ")";

}

}

• Java Immutable Class– As per Joshua Bloch

Effective Java

boilerplate

Page 39: Paulking groovy

...Better Design Patterns: Immutable

QCON 2010 - 39

© A

SE

RT

2006-2

010

@Immutable class Punter {String first, last

}

Page 40: Paulking groovy

QCON 2010 - 40

© A

SE

RT

2006-2

010

Better Design Patterns: Singleton

class Calculator {def total = 0def add(a, b) { total++; a + b }

}

def INSTANCE = new Calculator()Calculator.metaClass.constructor = { -> INSTANCE }

def c1 = new Calculator()def c2 = new Calculator()

assert c1.add(1, 2) == 3assert c2.add(3, 4) == 7

assert c1.is(c2)assert [c1, c2].total == [2, 2]

@Singleton(lazy=true)class X {

def getHello () {"Hello, World!"

}}

println X.instance.hello

Page 41: Paulking groovy

QCON 2010 - 41

© A

SE

RT

2006-2

010

Better Design Patterns: Delegate…

import java.util.Date;

public class Event {private String title;private String url;private Date when;

public String getUrl() {return url;

}

public void setUrl(String url) {this.url = url;

}

public String getTitle() {return title;

}

public void setTitle(String title) {this.title = title;

}// ...

public Date getWhen() {return when;

}

public void setWhen(Date when) {this.when = when;

}

public boolean before(Date other) {return when.before(other);

}

public void setTime(long time) {when.setTime(time);

}

public long getTime() {return when.getTime();

}

public boolean after(Date other) {return when.after(other);

}// ...

Page 42: Paulking groovy

QCON 2010 - 42

© A

SE

RT

2006-2

010

…Better Design Patterns: Delegate…

import java.util.Date;

public class Event {private String title;private String url;private Date when;

public String getUrl() {return url;

}

public void setUrl(String url) {this.url = url;

}

public String getTitle() {return title;

}

public void setTitle(String title) {this.title = title;

}// ...

public Date getWhen() {return when;

}

public void setWhen(Date when) {this.when = when;

}

public boolean before(Date other) {return when.before(other);

}

public void setTime(long time) {when.setTime(time);

}

public long getTime() {return when.getTime();

}

public boolean after(Date other) {return when.after(other);

}// ...

boilerplate

Page 43: Paulking groovy

QCON 2010 - 43

© A

SE

RT

2006-2

010

…Better Design Patterns: Delegate

class Event {String title, url@Delegate Date when

}

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)

Page 44: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 44

© A

SE

RT

2006-2

010

Page 45: Paulking groovy

SOAP Client and Server

QCON 2010 - 45

© A

SE

RT

2006-2

010

class MathService {double add(double a, double b) {

a + b}double square(double c) {

c * c}

}

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

Page 46: Paulking groovy

Better Testing: SoapUI

• Tool for testing Web Services has a built-

in Groovy editor for custom steps

QCON 2010 - 46

© A

SE

RT

2006-2

010

Page 47: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 47

© A

SE

RT

2006-2

010

Page 48: Paulking groovy

Groovy DSL Features

• Literal Syntax Conventions

• Scripts as well as Classes & Optional Syntax

• Optional Typing & Named Arguments

• Type Transformations

• Using With

• Operator Overloading

• Closures

• Runtime Metaprogramming

• Compile-time Metaprogramming (AST Macros)

• Builders

QCON 2010 - 48

© A

SE

RT

2006-2

010

Page 49: Paulking groovy

Literal Syntax Conventions

• Lists– Special syntax for list literals

– Additional common methods (operator overloading)

• Maps– Special syntax for map literals

– Additional common methods

• Ranges– Special syntax for various kinds of ranges

QCON 2010 - 49

© A

SE

RT

2006-2

010

def list = [3, new Date(), 'Jan']assert list + list == list * 2

def map = [a: 1, b: 2]assert map['a'] == 1 && map.b == 2

def letters = 'a'..'z'def numbers = 0..<10

Page 50: Paulking groovy

Scripts as well as Classes & Optional Syntax

• Java

• Groovy

QCON 2010 - 50

© A

SE

RT

2006-2

010

public class HelloJava {public static void main(String[] args) {

System.out.println("Hello World");}

}

System.out.println('Hello World');

println 'Hello World'

class HelloGroovy {static main(String[] args) {

System.out.println("Hello World");}

}

Page 51: Paulking groovy

Optional Typing & Named Params

• Java

• Groovy

QCON 2010 - 51

© A

SE

RT

2006-2

010

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"));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.firstLast name: $args.last"""}printDetails first:'John', last:'Smith'

Page 52: Paulking groovy

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

around, combined in structured ways to form more

complex algorithms and data

QCON 2010 - 52

© A

SE

RT

2006-2

010

doubleNum = { num -> num * 2 }println doubleNum(3) // => 6

processThenPrint = { num, closure ->num = closure(num); println "num is $num"

}processThenPrint(3, doubleNum) // => num is 6processThenPrint(10) { it / 2 } // => num is 5

Page 53: Paulking groovy

Static Imports

QCON 2010 - 53

© A

SE

RT

2006-2

010

import groovy.swing.SwingXBuilderimport 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()

Page 54: Paulking groovy

Using '.with' Example

QCON 2010 - 54

© A

SE

RT

2006-2

010

letters = ['a', 'b', 'c']

range = 'b'..'d'

letters.with {

add 'd'

remove 'a'

}

assert letters == range

map = [a:10, b:4, c:7]

map.with {

assert (a + b) / c == 2

}

Page 55: Paulking groovy

Coin example...

QCON 2010 - 55

© A

SE

RT

2006-2

010

enum Coin {

penny(1), nickel(5), dime(10), quarter(25)

Coin(int value) { this.value = value }

int value

}

import static Coin.*

assert 2 * quarter.value +

1 * nickel.value +

2 * penny.value == 57

Page 56: Paulking groovy

...Coin example...

QCON 2010 - 56

© A

SE

RT

2006-2

010

class CoinMath {

static multiply(Integer self, Coin c) {

self * c.value

}

}

use (CoinMath) {

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

Page 57: Paulking groovy

...Coin example

QCON 2010 - 57

© A

SE

RT

2006-2

010

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) {

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

Page 58: Paulking groovy

Game example...

QCON 2010 - 58

© A

SE

RT

2006-2

010

// 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) {

if (index == things.size() - 1) result += ' and '

else if (index < things.size() - 1) result += ', '

}

result += "${thing.toLowerCase()[0] in VOWELS ? 'an' : 'a'} $thing"

}

result ?: 'nothing'

}

}

import static GameUtils.*

...

Page 59: Paulking groovy

...Game example...

QCON 2010 - 59

© A

SE

RT

2006-2

010

...

class Room {

def description

def contents = []

}

...

Page 60: Paulking groovy

...Game example...

QCON 2010 - 60

© A

SE

RT

2006-2

010

...

class Player {

def currentRoom

def inventory = []

void look() {

println "You are in ${currentRoom?.description?:'the void'} which contains ${listItems(currentRoom?.contents)}"

}

void inv() {

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"

}

}

...

Page 61: Paulking groovy

...Game example...

QCON 2010 - 61

© A

SE

RT

2006-2

010

...

void drop(item) {

if (inventory?.remove(item)) {

currentRoom?.contents << item

println "You dropped the $item"

} else {

println "You don't have the $item"

}

}

def propertyMissing(String name) {

if (metaClass.respondsTo(this, name)) {

this."$name"()

}

name

}

}

...

Page 62: Paulking groovy

...Game example...

QCON 2010 - 62

© A

SE

RT

2006-2

010

...

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

look

take emerald

inv

look

take key

drop emerald

inv

look

}

assert player.inventory == ['dagger', 'key']

...

Page 63: Paulking groovy

...Game example

QCON 2010 - 63

© A

SE

RT

2006-2

010

...

// now try some error conditions

plainRoom.description = null

player.with {

drop gold

take gold

drop emerald

take emerald

take emerald

look

}

Page 64: Paulking groovy

Grails Criteria

QCON 2010 - 64

© A

SE

RT

2006-2

010

// 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")

}

maxResults(10)

order("holderLastName", "desc")

}// source: Grails doco: 5. Object Relational Mapping (GORM): 5.4.2 Criteria

Page 65: Paulking groovy

Grails Criteria Example

QCON 2010 - 65

© A

SE

RT

2006-2

010

// Book is a POJO in our domain/model

def book = Book.findByTitle("The Stand")

book = Book.findByTitleLike("Harry Pot%")

book = Book.findByReleaseDateBetween( firstDate, secondDate )

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

Page 66: Paulking groovy

Grails Bean Builder Example

QCON 2010 - 66

© A

SE

RT

2006-2

010

bb.beans {

marge(Person) {

name = "marge"

husband = { Person p ->

name = "homer"

age = 45

props = [overweight:true, height:"1.8m"]

}

children = [bart, lisa]

}

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

Page 67: Paulking groovy

Groovy Lab Example

QCON 2010 - 67

© A

SE

RT

2006-2

010

// 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

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))

Page 68: Paulking groovy

Operator Overloading Example

QCON 2010 - 68

© A

SE

RT

2006-2

010

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

Page 69: Paulking groovy

Type Transformation Example

QCON 2010 - 69

© A

SE

RT

2006-2

010

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) {

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() }

Page 70: Paulking groovy

AST Builder

• Numerous approaches, still evolving.

“From code” approach:

• Produces:

def result = new AstBuilder().buildFromCode {println "Hello World"

}

BlockStatement-> ReturnStatement

-> MethodCallExpression-> VariableExpression("this")-> ConstantExpression("println")-> ArgumentListExpression

-> ConstantExpression("Hello World")

Page 71: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 71

© A

SE

RT

2006-2

010

Page 72: Paulking groovy

Types of Testing

QCON 2010 - 72

© A

SE

RT

2006-2

010

Unit Testing

Mock/interaction testing

State-based testing

Integration Testing

Acceptance Testing

Web drivers

Non-web drivers

Test runners

Techniques

Testing DSLs

ATDD/BDD

Data-driven

Logic-driven

Model-driven

Performance

testing

All-pairs &

combinations

Gpars

Page 73: Paulking groovy

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

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 QCON 2010 - 73

© A

SE

RT

2006-2

010

Page 74: Paulking groovy

Groovy and Testing Tool Spectrum*

QCON 2010 - 74

© A

SE

RT

2006-2

010

Database

Drivers

DbUnit

DataSets

SqlUnit

groovy.sql

JPA

JDO

BigTable

JDBC

SOAP /

REST

Drivers

GroovyWS

XML-RPC

CXF

Axis2

JAX-WS

JAX-RS

Utilities

AllPairs, Combinations

Polyglot languages

Logic programming

Threads, Parallel /

Concurrency libraries

Data-driven libraries

Networking libraries

XML Processing

Read/write files /

Excel / Word / CSV

Reporting, Logging

Other

Drivers

FEST

Email

FTP

AntUnit

Telnet

SSH

ExecTools

iTest2, SoapUI, Twist,

IDEs, JMeter, Text

editors, Recorders,

Sahi, Build Tools, CI

Web

Drivers

WebTest

WebDriver

JWebUnit

Tellurium

Selenium

HtmlUnit

Watij

HttpBuilder

Cyberneko

Runners

Native Groovy, JUnit, TestNG, Spock, EasyB,

JBehave, Cucumber, Robot Framework

* Tools/libraries/frameworks don't always neatly fall into one category – still useful conceptually

Page 75: Paulking groovy

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)

– 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

QCON 2010 - 75

© A

SE

RT

2006-2

010

Page 76: Paulking groovy

HtmlUnit: Testing New Blog Post...

QCON 2010 - 76

© A

SE

RT

2006-2

010

@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 titleassert 'Welcome to SimpBlog' == page.titleText

// fill in blog entry and post itdef 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()

...

Page 77: Paulking groovy

...HtmlUnit: Testing New Blog Post

QCON 2010 - 77

© A

SE

RT

2006-2

010

...

// check blog post detailsassert result.getElementsByTagName('h1').item(0).

textContent.matches('Post.*: Bart was here.*')def h3headings = result.getElementsByTagName('h3')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!'

Page 78: Paulking groovy

QCON 2010 - 78

© A

SE

RT

2006-2

010

WebTest testing Web Sitesdef 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")

}

}

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}")

}

}

}

Page 79: Paulking groovy

WebTest testing Emails

QCON 2010 - 79

© A

SE

RT

2006-2

010

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")

}

}

ant.testSpec(name:'Email Test'){

steps {

emailSetConfig(server:'localhost', password:'password',

username:'[email protected]', 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')

}

}

Page 80: Paulking groovy

Spock Testing Framework...

QCON 2010 - 80

© A

SE

RT

2006-2

010

Page 81: Paulking groovy

...Spock Testing Framework

QCON 2010 - 81

© A

SE

RT

2006-2

010

• Testing framework for Java and Groovy

• Highly expressive specification language– No assertion API

– No record &

replay

mocking API

– No

superfluous

annotations

– Meaningful

assert error

messages

– Extensible

– Compatible

with JUnit

reportingwise

@Speck@RunWith(Sputnik)class PublisherSubscriberSpeck {def "events are received by all subscribers"() {def pub = new Publisher()def sub1 = Mock(Subscriber)def sub2 = Mock(Subscriber)pub.subscribers << sub1 << sub2

when:pub.send("event")

then:1 * sub1.receive("event")1 * sub2.receive("event")

}}

Page 82: Paulking groovy

Spock Example...

QCON 2010 - 82

© A

SE

RT

2006-2

010

import com.gargoylesoftware.htmlunit.WebClientimport 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 should succeed"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 was Spock)"form.getSelectByName('category').getOptions().find { it.text == category }.form.getSelectByName('author').getOptions().find { it.text == author }.setSelectedform.getTextAreaByName('content').setText(content)result = form.getInputByName('btnPost').click()subheadings = result.getElementsByTagName('h3')para = result.getByXPath('//TABLE//TR/TD/P')[0]

...

Page 83: Paulking groovy

...Spock Example

QCON 2010 - 83

© A

SE

RT

2006-2

010

...then:page.titleText == 'Welcome to SimpBlog'result.getElementsByTagName('h1').item(0).textContent.matches("Post.*: $author was here.*"subheadings.item(1).textContent == "Category: $category"subheadings.item(2).textContent == "Author: $author"

and:para.textContent == content

where:author << ['Bart', 'Homer', 'Lisa']category << ['Home', 'Work', 'Food']content << ['foo', 'bar', 'baz']

}}

// Optional use of 'and:'

Page 84: Paulking groovy

QCON 2010 - 84

© A

SE

RT

2006-2

010

EasyB

• Description: BDD, Rspec-like testing librarynarrative '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'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}

}

Page 85: Paulking groovy

EasyB Example ...

• When run will be marked as pending– perfect for ATDD

QCON 2010 - 85

© A

SE

RT

2006-2

010

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"and "I have selected 'Bart' as the author"and "I click the 'Create Post' button"then "I expect the entry to be posted"

}

Page 86: Paulking groovy

...EasyB Example...

QCON 2010 - 86

© A

SE

RT

2006-2

010

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", {

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)')}

...

Page 87: Paulking groovy

...EasyB Example...

QCON 2010 - 87

© A

SE

RT

2006-2

010

...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(true

}

and "I click the 'Create Post' button", {result = form.getInputByName('btnPost').click()

}

then "I expect the entry to be posted", {// check blog post detailsassert result.getElementsByTagName('h1').item(0).textContent.matches('Post.*: Bart was here.*'def h3headings = result.getElementsByTagName('h3')assert h3headings.item(1).textContent == 'Category: Home' // traditional styleh3headings.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.firstChildassert para.textContent == 'Cowabunga Dude!'// para.shouldHave textContent: 'Cowabunga Dude!'

}}

Page 88: Paulking groovy

...EasyB Example...

QCON 2010 - 88

© A

SE

RT

2006-2

010

Page 89: Paulking groovy

...EasyB Example

QCON 2010 - 89

© A

SE

RT

2006-2

010

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

Post Blog Entry Feature

for feature

As a Blogger

I want to be able to post a blog

So that I can keep others informed

given we are on the create blog entry page

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

easyb is preparing to process 2 file(s)

Running simp blog initial story (SimpBlogInitialStory.groovy)

Scenarios run: 1, Failures: 0, Pending: 1, Time elapsed: 1.049 sec

Running simp blog story (SimpBlogStory.groovy)

Scenarios run: 1, Failures: 0, Pending: 0, Time elapsed: 1.356 sec

2 total behaviors ran (including 1 pending behavior) with no failures

easyb execution passed

Page 90: Paulking groovy

Cucumber• Description

– Loose coupling

between text spec

and step defns

QCON 2010 - 90

© A

SE

RT

2006-2

010

# language: enFeature: AdditionIn order to avoid silly mistakesAs a math idiot I want to be told the sum of two numbers

Scenario Outline: Add two numbersGiven I have entered <input_1> into the calculatorAnd I have entered <input_2> into the calculatorWhen I press <button>Then the stored result should be <output>

Examples:| input_1 | input_2 | button | output || 20 | 30 | add | 50 || 2 | 5 | add | 7 || 0 | 40 | add | 40 |

# language: enFeature: DivisionIn order to avoid silly mistakesCashiers must be able to calculate a fraction

Scenario: Regular numbersGiven I have entered 3 into the calculatorAnd I have entered 2 into the calculatorWhen I press divideThen the stored result should be 1.5

Page 91: Paulking groovy

Cucumber Example...

QCON 2010 - 91

© A

SE

RT

2006-2

010

# 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: 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.*"

Page 92: Paulking groovy

...Cucumber Example...

QCON 2010 - 92

© A

SE

RT

2006-2

010

Page 93: Paulking groovy

...Cucumber Example

QCON 2010 - 93

© A

SE

RT

2006-2

010

import com.gargoylesoftware.htmlunit.WebClientthis.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 ->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)}

Page 94: Paulking groovy

Cucumber Data Driven Example...

QCON 2010 - 94

© A

SE

RT

2006-2

010

# language: en@newpostFeature: New Blog PostIn order to create a new blog entryBloggers should be able to select their name and category and enter text

Scenario Outline: New PostingGiven we are on the create blog entry pageWhen I have entered "<title>" as the titleAnd I have entered "<content>" as the contentAnd I have selected "<category>" from the "category" dropdownAnd I have selected "<author>" from the "author" dropdownAnd I click the 'Create Post' buttonThen 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 |

Page 95: Paulking groovy

...Cucumber Data Driven Example

QCON 2010 - 95

© A

SE

RT

2006-2

010

Page 96: Paulking groovy

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

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 clausesQCON 2010 - 96

© A

SE

RT

2006-2

010

Page 97: Paulking groovy

JBehave Example...

QCON 2010 - 97

© A

SE

RT

2006-2

010

Given we are on the create blog entry pageWhen I have entered "Bart was here" as the titleAnd I have entered "Cowabunga Dude!" as the contentAnd I have selected "Home" from the "category" dropdownAnd I have selected "Bart" from the "author" dropdownAnd I click the 'Create Post' buttonThen I should see a heading message matching "Post.*: Bart was here.*"

import org.jbehave.scenario.Scenarioimport 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.*"

Page 98: Paulking groovy

...JBehave Example

QCON 2010 - 98

© A

SE

RT

2006-2

010

import org.jbehave.scenario.steps.Stepsimport 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')}

@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

Page 99: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

Polyglot GroovyOther languages, other styles

• Parallel Processing

• Enterprise Groovy

• More InfoQCON 2010 - 99

© A

SE

RT

2006-2

010

Page 100: Paulking groovy

Polyglot Programming...

QCON 2010 - 100

© A

SE

RT

2006-2

010

@Grab('org.clojure:clojure:1.0.0')import clojure.lang.Compilerimport clojure.lang.RT

def src = new File('temp.clj')src.text = '''(ns groovy)(defn factorial [n]

(if (< n 2)1(* n (factorial (- n 1))))

'''

src.withReader { reader ->Compiler.load reader

}

def fac = RT.var('groovy', 'factorial')println fac.invoke(4)

...def jy = getEngine("jython")jy?.eval('''def factorial(n):

i=fact=1while i <= n:

fact=fact*ii=i+1

return fact

result = factorial(4)''')println jy?.result

Page 101: Paulking groovy

...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

• 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

QCON 2010 - 101

© A

SE

RT

2006-2

010

Page 102: Paulking groovy

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

• 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

QCON 2010 - 102

© A

SE

RT

2006-2

010

Page 103: Paulking groovy

QCON 2010 - 103

© A

SE

RT

2006-2

010

Functional Programming…

def fac(n) { n == 0 ? 1 : n * fac(n - 1) }assert 24 == fac(4)

// define and use infinite streamsdef integers(n) { cons(n, { integers(n+1) }) }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(' ')

Page 104: Paulking groovy

QCON 2010 - 104

© A

SE

RT

2006-2

010

…Functional Programming…

import fj.data.Stream

//some metaprogramming to make fj mesh well with GroovyStream.metaClass.filter = { Closure c ->

delegate.filter(c as fj.F) }Stream.metaClass.getAt = { n ->

delegate.index(n) }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]

Page 105: Paulking groovy

QCON 2010 - 105

© A

SE

RT

2006-2

010

…Functional Programming

import fj.data.Streamimport static fj.data.Stream.cons

// some metaprogramming to make Closures mesh well with// fj functions and productsStream.metaClass.filterTail = { Closure c ->

delegate.tail()._1().filter(c as fj.F) }Stream.metaClass.static.cons = { h, Closure c ->

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

Page 106: Paulking groovy

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:

QCON 2010 - 106

© A

SE

RT

2006-2

010

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"

Page 107: Paulking groovy

... Enabling a functional style …

• A first attempt at a more functional style:

QCON 2010 - 107

© A

SE

RT

2006-2

010

def numbers = [31,-41,59,26,-53,58,97,-93,-23,84]

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"

Page 108: Paulking groovy

... 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:

QCON 2010 - 108

© A

SE

RT

2006-2

010

assert letters.inits() == [['a'],['a', 'b'],['a', 'b', 'c'],['a', 'b', 'c', 'd']

]

letters = ['a', 'b', 'c', 'd']

assert letters.tails() == [['d'],

['c', 'd'],['b', 'c', 'd'],

['a', 'b', 'c', 'd']]

Page 109: Paulking groovy

... Enabling a functional style …

• An even more functional stylemss = max º sum* º (flatten º tails* º inits)

Notes:

– sum() is one-level flatten in Groovy, flatten() is recursive

– Metaprogramming allowed us to enhance all Lists

QCON 2010 - 109

© A

SE

RT

2006-2

010

List.metaClass {inits{ (0..<delegate.size()).collect{ delegate[0..it] } }tails{ delegate.reverse().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]

println "Maximum Segment Sum of $numbers is ${solve numbers}"

Source: http://hamletdarcy.blogspot.com/2008/07/groovy-vs-f-showdown-side-by-side.html

Page 110: Paulking groovy

...Constraint/Logic Programming...

QCON 2010 - 110

© A

SE

RT

2006-2

010

Source: http://xkcd.com/287/

Page 111: Paulking groovy

...Constraint/Logic Programming...

QCON 2010 - 111

© A

SE

RT

2006-2

010

// 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 = ['Mixed fruit' : 215,'French fries' : 275,'Side salad' : 335,'Hot wings' : 355,'Mozzarella sticks' : 420,'Sampler plate' : 580

]def numOrdered = new IntegerVariable[menu.size()]def priceEach = new int[menu.size()]def sum = 1505...

Found a solution:7 * Mixed fruit

Found a solution:1 * Mixed fruit2 * Hot wings1 * Sampler plate

Page 112: Paulking groovy

...Constraint/Logic Programming

QCON 2010 - 112

© A

SE

RT

2006-2

010

...menu.eachWithIndex { name, price, i ->

// number ordered >= 0// number ordered * price <= sumnumOrdered[i] = makeIntVar(name, 0, sum.intdiv(price))priceEach[i] = price

}m.addConstraint(eq(scalar(numOrdered, priceEach), sum))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()

}

Page 113: Paulking groovy

ModelJUnit...

QCON 2010 - 113

© A

SE

RT

2006-2

010

// require modeljunit.jarimport nz.ac.waikato.modeljunit.coverage.*import nz.ac.waikato.modeljunit.*

class VendingMachineModel implements FsmModel {def state = 0 // 0,25,50,75,100void reset(boolean testing) {state = 0}

boolean vendGuard() {state == 100}@Action void vend() {state = 0}

boolean coin25Guard() {state <= 75}@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()

Page 114: Paulking groovy

...ModelJUnit...

QCON 2010 - 114

© A

SE

RT

2006-2

010

...

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)done (0, coin50, 50)done (50, coin50, 100)done (100, vend, 0)done (0, coin25, 25)done (25, coin25, 50)done Random reset(true)done (0, coin50, 50)done (50, coin25, 75)done (75, coin25, 100)done (100, vend, 0)done (0, coin50, 50)done (50, coin25, 75)...

...

Metrics Summary:action coverage: 3/3state coverage: 5/5transition coverage: 7/8transition-pair coverage: 8/12...

Page 115: Paulking groovy

...ModelJUnit: SimpBlog Case Study

QCON 2010 - 115

© A

SE

RT

2006-2

010

Page 116: Paulking groovy

ScalaCheck

• Description– Tool for testing Scala, Java and Groovy programs

– Based on property specifications and automatic

random test data generation

QCON 2010 - 116

© A

SE

RT

2006-2

010

> scala -classpath ScalaCheck-1.5.jarWelcome to Scala version 2.7.6.final

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")

Page 117: Paulking groovy

ScalaCheck: SimpBlog Case Study...

QCON 2010 - 117

© A

SE

RT

2006-2

010

class SimpBlogChecker {static postAndCheck = nullstatic 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.WebClientdef page = new WebClient().getPage('http://localhost:8080/postForm')def form = page.getFormByName('post')form.getInputByName('title').setValueAttribute(title)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).textContentdef h3headings = result.getElementsByTagName('h3')def categoryResult = h3headings.item(1).textContentdef authorResult = h3headings.item(2).textContentdef para = result.getByXPath('//TABLE//TR/TD/P')[0]def contentResult = para.textContentreturn new ResultHolder(titleResult, contentResult, authorResult, categoryResult)''')...

Page 118: Paulking groovy

...ScalaCheck: SimpBlog Case Study...

QCON 2010 - 118

© A

SE

RT

2006-2

010

...def binding = new Binding()binding.setVariable('title', title)binding.setVariable('content', clean(content))binding.setVariable('author', author)binding.setVariable('category', category)postAndCheck.binding = bindingpostAndCheck.run()

}}

class ResultHolder {String title, content, author, categoryResultHolder(String title, String content,

String author, String category) {this.title = titlethis.content = contentthis.author = authorthis.category = category

}}

> groovyc SimpBlogChecker.groovy

Page 119: Paulking groovy

...ScalaCheck: SimpBlog Case Study...

QCON 2010 - 119

© A

SE

RT

2006-2

010

//CheckSimpBlog.scala

import org.scalacheck.Prop._import org.scalacheck.ConsoleReporter.testStatsEximport org.scalacheck.Test.checkimport org.scalacheck.Arbitrary._import org.scalacheck.Gen._

object CheckSimpBlog {

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

Page 120: Paulking groovy

...ScalaCheck: SimpBlog Case Study...

QCON 2010 - 120

© A

SE

RT

2006-2

010

...val enterFieldsAcceptedAndEchoedProperty = forAll(fieldsGen)(

fields =>{

val (title, content, author, category) = fieldsval result = SimpBlogChecker.postAndReturn(title, content, author, category)result.getTitle contains titleresult.getContent contains contentresult.getAuthor contains authorresult.getCategory contains category

})

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.

Page 121: Paulking groovy

...ScalaCheck: SimpBlog Case Study

QCON 2010 - 121

© A

SE

RT

2006-2

010

Page 122: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

Parallel Processing

• Enterprise Groovy

• More Info

QCON 2010 - 122

© A

SE

RT

2006-2

010

Page 123: Paulking groovy

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

• 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 actorsQCON 2010 - 123

© A

SE

RT

2006-2

010

Page 124: Paulking groovy

QCON 2010 - 124

© A

SE

RT

2006-2

010

Fibonacci…

START = 8END = 16fib = {n -> n < 2 ? n : fib(n - 1) + fib(n - 2) }(START..END).each {num ->

println "n:$num => ${fib(num)}"}

Page 125: Paulking groovy

QCON 2010 - 125

© A

SE

RT

2006-2

010

…Fibonacci…

import java.util.concurrent.*

CUTOFF = 12 // not worth parallelizing for small nTHREADS = 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 ->

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()

Page 126: Paulking groovy

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

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 ClojureQCON 2010 - 126

© A

SE

RT

2006-2

010

Page 127: Paulking groovy

gpars/GParallelizer...

QCON 2010 - 127

© A

SE

RT

2006-2

010

// run multiple closures in parallelAsynchronizer.withAsynchronizer {

assert [10, 20] == AsyncInvokerUtil.doInParallel({calculateA()},{calculateB()}

)}

// multiply numbers asynchronouslyParallelizer.withParallelizer(5) {

def result = [1, 2, 3, 4, 5].collectAsync {it * 2}assert result == [2, 4, 6, 8, 10]

}

Page 128: Paulking groovy

...gpars/GParallelizer...

QCON 2010 - 128

© A

SE

RT

2006-2

010

// support for dataflow to avoid doing synchronisationimport static org.gparallelizer.dataflow.DataFlow.thread

final def x = new DataFlowVariable()final def y = new DataFlowVariable()final def z = new DataFlowVariable()

thread {z << x.val + y.valprintln "Result: ${z.val}"

}

thread { x << 10 }

thread { y << 5 }

Page 129: Paulking groovy

...gpars/GParallelizer...

QCON 2010 - 129

© A

SE

RT

2006-2

010

// actor supportimport static org.gparallelizer.actors.pooledActors.PooledActors.*

def me = actor {friend.send('Hi')react(10.seconds) {

// continue conversation}

}me.metaClass.onTimeout = {->friend.send('I see, busy as usual. Never mind.')}me.start()

Page 130: Paulking groovy

// safe supportimport 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

}

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()

...gpars/GParallelizer

QCON 2010 - 130

© A

SE

RT

2006-2

010

Page 131: Paulking groovy

gpars for testing

QCON 2010 - 131

© A

SE

RT

2006-2

010

@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'],['Food', 'Lisa', 'Content 4']

]

withParallelizer(3) {testCases.eachAsync{ category, author, content ->

postAndCheck category, author, content}

}

private postAndCheck(category, author, content) {...

Page 132: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

Enterprise Groovy

• More Info

QCON 2010 - 132

© A

SE

RT

2006-2

010

Page 133: Paulking groovy

Integration with Spring...

QCON 2010 - 133

© A

SE

RT

2006-2

010

// 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

// 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

Page 134: Paulking groovy

...Integration with Spring

QCON 2010 - 134

© A

SE

RT

2006-2

010

// 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

@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

Page 135: Paulking groovy

Dependency Injection: Guice

QCON 2010 - 135

© A

SE

RT

2006-2

010

import com.google.inject.*

@ImplementedBy(CalculatorImpl)interface Calculator {

def add(a, b)}

@Singletonclass CalculatorImpl implements Calculator {

private total = 0def add(a, b) { total++; a + b }def getTotalCalculations() { 'Total Calculations: ' + total }String toString() { 'Calc: ' + hashCode()}

}

class Client {@Inject Calculator calc// ...

}

def injector = Guice.createInjector()// ...

Page 136: Paulking groovy

Topics

• Groovy Intro

• Leveraging the language

• Use and abuse of Design Patterns

• Web Services

• Writing DSLs

• Groovy Testing

• Polyglot Groovy

• Parallel Processing

• Enterprise Groovy

More Info

QCON 2010 - 136

© A

SE

RT

2006-2

010

Page 137: Paulking groovy

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– [email protected]

• 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 ...

QCON 2010 - 137

© A

SE

RT

2006-2

010

Page 138: Paulking groovy

...More Information

138

© A

SE

RT

2006-2

010