79
Industrial Strength Groovy Tools for the Professional Groovy Developer Dr Paul King, ASERT: @paulk_asert

Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

IndustrialStrength GroovyTools for the Professional Groovy DeveloperDr Paul King, ASERT: @paulk_asert

Page 2: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Topics

• Testing/Mocking: JUnit, TestNG, EasyB, Spock, Instinct, MockFor, Gmock, EasyMock

• Injection: Spring, Guice

• Coverage: Cobertura

• Code style: CodeNarc, IntelliJ

• Duplication: Simian

• Documentation: GroovyDoc

• Builds: Ant, Gant, GMaven, Gradle, Hudson

• Modularisation: Grapes, OSGi

Page 3: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

INTRO

Page 4: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

SpringOne2gx_Oct2009 - 4

©

ASERT

2006-2009

What is Groovy?

• ―Groovy is like a super versionof Java. It can leverage Java'senterprise capabilities but alsohas cool productivity features like closures, DSL support, builders and dynamic typing.‖

Page 5: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

SpringOne2gx_Oct2009 - 5

©

ASERT

2006-2009

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 6: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Growing Acceptance …

Making Java Groovy(soon)

Now free

Page 7: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

… Growing Acceptance …©

ASERT

2006-2009

SpringOne2gx_Oct2009 - 7http://www.java.net

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

Page 8: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

… Growing Acceptance …©

ASERT

2006-2009

SpringOne2gx_Oct2009 - 8http://www.leonardoborges.com/writings

What alternative JVM language are you using or intending to use

Page 9: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

… Growing Acceptance …©

ASERT

2006-2009

SpringOne2gx_Oct2009 - 9

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

Page 10: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

… Growing Acceptance

SpringOne2gx_Oct2009 - 10

©

ASERT

2006-2009

Page 11: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Groovy‘s Appeal

• Innovators/Thought leaders

– Ideas, power, flexibility, novelty, thinking community

• Early adopters

– Productivity benefits and collegiate community

– Leverage JVM and potential for mainstream

• Mainstream

– Leverage existing Java skills, low learning curve

– Leverage JVM and production infrastructure

– Professional community

– Tools, tools, tools

Page 12: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

SpringOne 2GX 2009. All rights reserved. Do not distribute without permission.

TESTING

Page 13: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Database Drivers

DbUnitDataSetsSqlUnitgroovy.sql JPAJDOBigTableJDBC

SOAP / REST Drivers

GroovyWSXML-RPC CXFAxis2JAX-WS JAX-RS

UtilitiesAllPairs, CombinationsPolyglot languagesLogic programmingThreads, Parallel /Concurrency librariesData-driven librariesNetworking librariesXML ProcessingRead/write files /Excel / Word / CSVReporting, Logging

Other Drivers

FESTEmailFTPAntUnitTelnetSSHExec

ToolsiTest2, SoapUI, Twist, IDEs, JMeter, Text editors, Recorders, Sahi, Build Tools, CI

WebDrivers

WebTestWebDriverJWebUnitTellurium Selenium HtmlUnitWatijHttpBuilderCyberneko

Runners

Native Groovy, JUnit, TestNG, Spock, EasyB,JBehave, Cucumber, Robot Framework

Gro

ovy a

nd T

esting T

ool Spectr

um

Page 14: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Testing: JUnit

import org.junit.Test

import static org.junit.Assert.assertEquals

class ArithmeticTest {

@Test

void additionIsWorking() {

assertEquals 4, 2+2

}

@Test(expected=ArithmeticException)

void divideByZero() {

println 1/0

}

}

Page 15: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Testing: TestNG

import ...

public class SimpleTest {

@BeforeClass

public void setUp() {

// code invoked when test is created

}

@Test(groups = [ "fast" ])

public void aFastTest() {

System.out.println("Fast test");

}

@Test(groups = [ "slow" ])

public void aSlowTest() {

System.out.println("Slow test");

}

}

Page 16: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Mockin

g:

EasyM

ock

import static org.easymock.EasyMock.*

mockControl = createStrictControl()mockReverser = mockControl.createMock(Reverser)storer = new JavaStorer(mockReverser)testStorage()

def testStorage() {expectReverse(123.456, -123.456)expectReverse('hello', 'olleh')mockControl.replay()checkReverse(123.456, -123.456)checkReverse('hello', 'olleh')mockControl.verify()

}

def expectReverse(input, output) {expect(mockReverser.reverse(input)).andReturn(output)

}

def checkReverse(value, reverseValue) {storer.put(value)assert value == storer.get()assert reverseValue == storer.getReverse()

}

Page 17: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

class a_default_storer {def storer

@initially void create_new_storer() {storer = new Storer()

}

private check_persist_and_reverse(value, expectedReverse) {storer.put(value)def persisted = storer.get()assert persisted == valuedef reversed = storer.reverseassert reversed == expectedReverse

}

@spec def should_reverse_numbers() {check_persist_and_reverse 123.456, -123.456

}

@spec def should_reverse_strings() {check_persist_and_reverse 'hello', 'olleh'

}

@spec def should_reverse_lists() {check_persist_and_reverse([1, 3, 5], [5, 3, 1])

}}

check_specs_for a_default_storerTesting:

Instinct

Page 18: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Mockin

g:

MockFor

import groovy.mock.interceptor.MockFor

def mocker = new MockFor(Collaborator.class) // create the Mock support

mocker.demand.one(1..2) { 1 } // demand the 'one' method one

// or two times, returning 1

mocker.demand.two() { 2 } // demand the 'two' method

// exactly once, returning 2

mocker.use { // start using the Mock

def caller = new Caller() // caller will call Collaborator

assertEquals 1, caller.collaborateOne() // will call Collaborator.one

assertEquals 1, caller.collaborateOne() // will call Collaborator.one

assertEquals 2, caller.collaborateTwo() // will call Collaborator.two

} // implicit verify here

import groovy.mock.interceptor.MockFor

def mocker = new MockFor(Collaborator.class)

mocker.demand.one(1..2) { 1 }

mocker.demand.two() { 2 }

mocker.use {

def caller = new Caller()

assertEquals 1, caller.collaborateOne()

assertEquals 1, caller.collaborateOne()

assertEquals 2, caller.collaborateTwo()

}

Page 19: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Mocking: Gmock ...

• Method mocking: mockLoader.load("fruit").returns("apple")

• Exception mocking: mockLoader.load("unknown").raises(new RuntimeException())

• Stub mocking: mockLoader.load("fruit").returns("apple").stub()

• Static method mocking: mockMath.static.random().returns(0.5)

• Property mocking: mockLoader.name.returns("loader")

• Constructor mocking: def mockFile = mock(File, constructor('/a/path/file.txt'))

• Partial mocking: mock(controller).params.returns([id: 3])

• Times expectation: mockLoader.load("fruit").returns("apple").atLeastOnce()

• Custom matcher: mockLoader.load(match{ it.startsWith("fru") })

• Strict ordering: ordered { ... }

• Optional support for Hamcrest matcher: mockLoader.put("test", is(not(lessThan(5))))

• GMockController if you can't extend GMockTestCase in your test

Page 20: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Mocking: Gmock

import org.gmock.GMockTestCase

class LoaderTest extends GMockTestCase {

void testLoader(){

def mockLoader = mock()

mockLoader.load('key').returns('value')

play {

assertEquals "value", mockLoader.load('key')

}

}

}

Page 21: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Testing:

Spock .

..

Page 22: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Testing: Spock

@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 23: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Testing: EasyB ...

given "an invalid zip code", {

invalidzipcode = "221o1"

}

and "given the zipcodevalidator is initialized", {

zipvalidate = new ZipCodeValidator()

}

when "validate is invoked with the invalid zip code", {

value = zipvalidate.validate(invalidzipcode)

}

then "the validator instance should return false", {

value.shouldBe false

}

Page 24: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Testing:

EasyB .

..before "start selenium", {given "selenium is up and running", {// start selenium

}}

scenario "a valid person has been entered", {

when "filling out the person form with a first and last name", {selenium.open("http://acme.racing.net/greport/personracereport.html")selenium.type("fname", "Britney")selenium.type("lname", "Smith")

}

and "the submit link has been clicked", {selenium.click("submit")

}

then "the report should have a list of races for that person", {selenium.waitForPageToLoad("5000")values = ["Mclean 1/2 Marathon", "Reston 5K", "Herndon 10K", "Leesburg 10K"]for(i in 0..<values.size()){

selenium.getText("//table//tr[${(i+3)}]/td").shouldBeEqualTo values[i]}

}}

after "stop selenium" , {then "selenium should be shutdown", {// stop selenium

}}

Page 25: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Dependency Injection

• Hollywood Principle

–Don‘t call us, we‘ll call you

• “All problems in computer science can be solved by another level of indirection”

• "...except for the problem of too many layers of indirection“– For attributions, see

http://en.wikipedia.org/wiki/Inversion_of_control

Page 26: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Dependency Injection

• Pattern for loosely coupled & testable objects

class Client {Calculator calcdef executeCalc(a, b) {

calc.add(a, b)}

}

class Client {Calculator calc =

new CalculatorImpl()def executeCalc(a, b) {

calc.add(a, b)}

}

Service locator/factory

Tightly coupled?

Hard to test?

Easy to understand?

Refactoring/navigation?

Need to select setter, constructor, field style

Can add complexity

Manage configuration

Direct or framework

Consistency/lifecycle

Page 27: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Dependency Injection: Spring ...

• Several flavors

– let‘s look at Annotation and BeanBuilder flavors

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

}

Page 28: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Dependency Injection: Spring

import org.springframework.context.support.GenericApplicationContext

import

org.springframework.context.annotation.ClassPathBeanDefinitionScanner

def ctx = new GenericApplicationContext()

new ClassPathBeanDefinitionScanner(ctx).scan('')

ctx.refresh()

def calc = ctx.getBean('calcImpl3')

println calc.doAdd(3, 4) // => 7

def bb = new grails.spring.BeanBuilder()

bb.beans {

adder(AdderImpl)

calcBean(CalcImpl2) { delegate.adder = adder }

}

def ctx = bb.createApplicationContext()

def calc = ctx.getBean('calcBean')

println calc.doAdd(3, 4) // => 7

Page 29: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Dependency I

nje

ction:

Guic

e 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 30: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Dependency Injection: Metaprogramming Style

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]

Page 31: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

CODE QUALITY

Page 32: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Covera

ge:

Cobert

ura

...

Ant

Page 33: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...C

overa

ge:

Cobert

ura

...

Maven

grails install-plugin code-coverage --globalGrails

Command-line

cobertura-instrumentjava –cp ...cobertura-reportcobertura-checkcobertura-merge

Page 34: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...C

overa

ge:

Cobert

ura

Page 35: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Rem

em

ber

100%

covera

ge r

ule

...

// 100%

• Necessary but not sufficient condition

Page 36: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...R

em

em

ber

100%

covera

ge r

ule

...

// 100%

• Necessary but not sufficient condition

// Fail

Page 37: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...R

em

em

ber

100%

covera

ge r

ule

• Necessary but not sufficient condition

Page 38: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Code style: CodeNarc ...

About 50 rules (1 broken?)

Page 39: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Code style: CodeNarc ...

Page 40: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Code style: CodeNarc

Page 41: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Code style: IntelliJ

Page 42: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Duplication: Simian ...

Simian fully supports the following languages:

• Java

• C#

• C++

• C

• Objective-C

• JavaScript (ECMAScript)

• COBOL, ABAP

• Ruby

• Lisp

• SQL

• Visual Basic

• Groovy

with partial support for:

• JSP

• ASP

• HTML

• XML

Page 43: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Duplication: Simian ...

Page 44: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Duplication: Simian ...

Page 45: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Duplication: Simian

Similarity Analyser 2.2.23 -http://www.redhillconsulting.com.au/products/simian/index.html

Copyright (c) 2003-08 RedHill Consulting Pty. Ltd. All rights reserved.

Simian is not free unless used solely for non-commercial or evaluation purposes.

{failOnDuplication=true, ignoreCharacterCase=true, ignoreCurlyBraces=true, ignoreIdentifierCase=true, ignoreModifiers=true, ignoreStringCase=true, threshold=6}

Found 6 duplicate lines in the following files:

Between lines 201 and 207 in /Users/haruki_zaemon/Projects/redhill/simian/build/dist/src/java/awt/image/WritableRaster.java

...

Found 66375 duplicate lines in 5949 blocks in 1260 files

Processed a total of 390309 significant (1196065 raw) lines in 4242 files

Processing time: 9.490sec

Page 46: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Documentation: GroovyDoc ...

<taskdef name="groovydoc"classname="org.codehaus.groovy.ant.Groovydoc">

<classpath><path path="${mainClassesDirectory}" /><path refid="compilePath" />

</classpath></taskdef>

<groovydoc destdir="${docsDirectory}/gapi"sourcepath="${mainSourceDirectory}"packagenames="**.*" use="true" windowtitle="${title} "doctitle="${title}" header="${title}" footer="${docFooter}"overview="src/main/overview.html" private="false">

<link packages="java.,org.xml.,javax.,org.xml."href="http://java.sun.com/j2se/1.5.0/docs/api" />

<link packages="org.apache.ant.,org.apache.tools.ant."href="http://www.dpml.net/api/ant/1.7.0" />

<link packages="org.junit.,junit.framework."href="http://junit.sourceforge.net/junit3.8.1/javadoc/" />

</groovydoc>

Page 47: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Documentation: GroovyDoc

Page 48: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

BUILDS

Page 49: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: Groovy from Ant

• Need groovy jar on your Ant classpath

<taskdef name="groovy"classname="org.codehaus.groovy.ant.Groovy"classpathref="my.classpath"/>

<target name="printXmlFileNamesFromJar"><zipfileset id="found" src="foobar.jar"

includes="**/*.xml"/><groovy>

project.references.found.each {println it.name

}</groovy>

</target>

Page 50: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: Ant from Groovy

• Built-in (need ant.jar on your Groovy classpath)

new AntBuilder().with {

echo(file:'Temp.java', '''

class Temp {

public static void main(String[] args) {

System.out.println("Hello");

}

}

''')

javac(srcdir:'.', includes:'Temp.java', fork:'true')

java(classpath:'.', classname:'Temp', fork:'true')

echo('Done')

}

// =>

// [javac] Compiling 1 source file

// [java] Hello

// [echo] Done

Page 51: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: Gant

• lightweight façade on Groovy's AntBuilder

• target def‘ns, pre-defined ‗ant‘, operations on predefined objects

includeTargets << gant.targets.CleancleanPattern << [ '**/*~' , '**/*.bak' ]cleanDirectory << 'build'

target ( stuff : 'A target to do some stuff.' ) {println ( 'Stuff' )depends ( clean )echo ( message : 'A default message from Ant.' )otherStuff ( )

}

target ( otherStuff : 'A target to do some other stuff' ) {println ( 'OtherStuff' )echo ( message : 'Another message from Ant.' )clean ( )

}

Page 52: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: GMaven

• Implementing Maven plugins has never been Groovier!

• Groovy Mojos

– A Simple Groovy Mojo

• Building Plugins

– Project Definition

– Mojo Parameters

• Putting More Groove into your Mojo

– Using ant, Using fail()

• gmaven-archetype-mojo Archetype

• gmaven-plugin Packaging

Page 53: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: GMaven

<plugin><groupId>org.codehaus.mojo.groovy</groupId><artifactId>groovy-maven-plugin</artifactId><executions>

<execution><id>restart-weblogic</id><phase>pre-integration-test</phase><goals>

<goal>execute</goal></goals><configuration>

<source>${pom.basedir}/src/main/script/restartWeblogic.groovy</source>

</configuration></execution>

...

Page 54: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: GMaven

def domainDir =project.properties['weblogic.domain.easyimage.dir']

stopWebLogic()copyFiles(domainDir)startWebLogic(domainDir)waitForWebLogicStartup()

def stopWebLogic() {weblogicServerDir = project.properties['weblogic.server.dir']adminUrl = project.properties['easyimage.weblogic.admin.t3']userName = 'weblogic'password = 'weblogic'ant.exec(executable: 'cmd', failonerror: 'false') {

arg(line: "/C ${wlsDir}/bin/setWLSEnv.cmd && java ..." ...}

...

Page 55: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: Gradle ...

• A very flexible general purpose build tool like Ant

• Switchable, build-by-convention frameworks a la Maven. But we never lock you in!

• Powerful support for multi-project builds

• Powerful dependency management (Apache Ivy based)

• Full support for your existing Maven or Ivy repository infrastructure

• Support for transitive dependency management without the need for remote repositories or pom.xml/ivy.xml files

• Ant tasks as first class citizens

• Groovy build scripts

• A rich domain model for describing your build

Page 56: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

... Builds: Gradle

Page 57: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Builds: Hudson

• Gant Plugin — This plugin allows Hudson to invoke Gant build script as the main build step

• Gradle Plugin — This plugin allows Hudson to invoke Gradle build script as the main build step

• Grails Plugin — This plugin allows Hudson to invoke Grails tasks as build steps

• Hudson CLI and GroovyShell Usage pattern?

Source: http://weblogs.java.net/blog/kohsuke/archive/2009/05/hudson_cli_and.html

Page 58: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Ant

Build F

ile..

.<project name="StringUtilsBuild" default="package"

xmlns:ivy="antlib:org.apache.ivy.ant" xmlns="antlib:org.apache.tools.ant">

<target name="clean">

<delete dir="target"/>

<delete dir="lib"/>

</target>

<target name="compile" depends="-init-ivy">

<mkdir dir="target/classes"/>

<javac srcdir="src/main"

destdir="target/classes"/></target>

<target name="compileTest" depends="compile">

<mkdir dir="target/test-classes"/>

<javac srcdir="src/test"

destdir="target/test-classes">

<classpath>

<pathelement location="target/classes"/>

<fileset dir="lib" includes="*.jar"/>

</classpath>

</javac></target>

...

Page 59: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...A

nt

Build F

ile

...

<target name="test" depends="compileTest">

<mkdir dir="target/test-reports"/>

<junit printsummary="yes" fork="yes" haltonfailure="yes">

<classpath>

<pathelement location="target/classes"/>

<pathelement location="target/test-classes"/>

<fileset dir="lib" includes="*.jar"/>

</classpath>

<formatter type="plain"/>

<formatter type="xml"/>

<batchtest fork="yes" todir="target/test-reports">

<fileset dir="target/test-classes"/>

</batchtest>

</junit>

</target>

<target name="package" depends="test">

<jar destfile="target/stringutils-1.0-SNAPSHOT.jar"

basedir="target/classes"/>

</target>

<target name="-init-ivy" depends="-download-ivy">

<taskdef resource="org/apache/ivy/ant/antlib.xml"

uri="antlib:org.apache.ivy.ant" classpath="lib/ivy.jar"/>

<ivy:settings file="ivysettings.xml"/>

<ivy:retrieve/>

</target>

<target name="-download-ivy">

<property name="ivy.version" value="2.1.0-rc2"/>

<mkdir dir="lib"/>

<get

src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.version}/ivy-

${ivy.version}.jar"

dest="lib/ivy.jar" usetimestamp="true"/>

</target>

</project>

Page 60: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Maven P

OM

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>org.groovycookbook.builds</groupId>

<artifactId>stringutils</artifactId>

<packaging>jar</packaging>

<version>1.0-SNAPSHOT</version>

<name>stringutils</name>

<url>http://maven.apache.org</url>

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<plugins>

<!-- this is a java 1.5 project -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<configuration>

<source>1.5</source>

<target>1.5</target>

</configuration>

</plugin>

</plugins>

</build>

</project>

Page 61: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

build.g

roovy .

..import static groovy.xml.NamespaceBuilder.newInstance as namespace

ant = new AntBuilder()

clean()

doPackage()

def doPackage() {

test()

ant.jar destfile: 'target/stringutils-1.0-SNAPSHOT.jar',

basedir: 'target/classes'

}

private dependencies() {

def ivy_version = '2.1.0-rc2'

def repo = 'http://repo2.maven.org/maven2'

ant.mkdir dir: 'lib'

ant.get dest: 'lib/ivy.jar',

usetimestamp: 'true',

src: "$repo/org/apache/ivy/ivy/$ivy_version/ivy-${ivy_version}.jar"

ant.taskdef classpath: 'lib/ivy.jar',

uri: 'antlib:org.apache.ivy.ant',

resource: 'org/apache/ivy/ant/antlib.xml'

def ivy = namespace(ant, 'antlib:org.apache.ivy.ant')

ivy.settings file: 'ivysettings.xml'

ivy.retrieve()

}

def clean() {

ant.delete dir: 'target'

ant.delete dir: 'lib'

}

...

Page 62: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...

build.g

roovy

...

def compile() {

dependencies()

ant.mkdir dir: 'target/classes'

ant.javac destdir: 'target/classes', srcdir: 'src/main',

includeantruntime: false

}

def compileTest() {

compile()

ant.mkdir dir: 'target/test-classes'

ant.javac(destdir: 'target/test-classes', srcdir: 'src/test',

includeantruntime: false) {

classpath {

pathelement location: 'target/classes'

fileset dir: 'lib', includes: '*.jar'

}

}

}

def test() {

compileTest()

ant.mkdir dir: 'target/test-reports'

ant.junit(printsummary: 'yes', haltonfailure: 'yes', fork: 'yes') {

classpath {

pathelement location: 'target/classes'

pathelement location: 'target/test-classes'

fileset dir: 'lib', includes: '*.jar'

}

formatter type: 'plain'

formatter type: 'xml'

batchtest(todir: 'target/test-reports', fork: 'yes') {

fileset dir: 'target/test-classes'

}

}

}

Page 63: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

build.g

ant

...

import static groovy.xml.NamespaceBuilder.newInstance as namespace

target('package': '') {

depends 'test'

jar destfile: 'target/stringutils-1.0-SNAPSHOT.jar',

basedir: 'target/classes'

}

target('-download-ivy': '') {

def ivy_version = '2.1.0-rc2'

def repo = 'http://repo2.maven.org/maven2'

mkdir dir: 'lib'

get dest: 'lib/ivy.jar', usetimestamp: 'true',

src: "$repo/org/apache/ivy/ivy/$ivy_version/ivy-${ivy_version}.jar"

}

target(clean: '') {

delete dir: 'target'

delete dir: 'lib'

}

target(compile: '') {

depends '-init-ivy'

mkdir dir: 'target/classes'

javac destdir: 'target/classes', srcdir: 'src/main'

}

...

Page 64: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...

build.g

ant

...

target('-init-ivy': '') {

depends '-download-ivy'

taskdef classpath: 'lib/ivy.jar', uri: 'antlib:org.apache.ivy.ant',

resource: 'org/apache/ivy/ant/antlib.xml'

def ivy = namespace(ant, 'antlib:org.apache.ivy.ant')

ivy.settings file: 'ivysettings.xml'

ivy.retrieve()

}

target(compileTest: '') {

depends 'compile'

mkdir dir: 'target/test-classes'

javac(destdir: 'target/test-classes', srcdir: 'src/test') {

classpath {

pathelement location: 'target/classes'

fileset dir: 'lib', includes: '*.jar'

}

}

}

target(test: '') {

depends 'compileTest'

mkdir dir: 'target/test-reports'

junit(printsummary: 'yes', haltonfailure: 'yes', fork: 'yes') {

classpath {

pathelement location: 'target/classes'

pathelement location: 'target/test-classes'

fileset dir: 'lib', includes: '*.jar'

}

formatter type: 'plain'

formatter type: 'xml'

batchtest(todir: 'target/test-reports', fork: 'yes') {

fileset dir: 'target/test-classes'

}

}

}

setDefaultTarget 'package'

Page 65: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

build.g

radle

usePlugin 'java'

sourceCompatibility = 1.5

version = '1.0-SNAPSHOT'

repositories {

mavenCentral()

}

dependencies {

testCompile 'junit:junit:4.7'

}

Page 66: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

MODULAR GROOVY

Page 67: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Modularisation: Grapes

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

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

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

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

}

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

Page 68: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Modularisation: OSGi

This is Apache Sling in five bullets:

• REST based web framework

• Content-driven, using a JCR content repository

• Powered by OSGi

• Scripting inside, multiple languages

• Apache Open Source project

See also: Grails JCR plugin

See also: http://hamletdarcy.blogspot.com

Page 69: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

PATTERNS

Page 70: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Better Design Patterns: Immutable...

• Java Immutable Class

– As per Joshua BlochEffective Java

AUG 2009 - 70

©

ASERT

2006-2009

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 71: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...Better Design Patterns: Immutable...

AUG 2009 - 71

©

ASERT

2006-2009

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

}

}

boilerplate

• Java Immutable Class

Page 72: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

...Better Design Patterns: Immutable

AUG 2009 - 72

©

ASERT

2006-2009

@Immutable class Punter {String first, last

}

Page 73: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

AUG 2009 - 73

©

ASERT

2006-2009

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 74: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

AUG 2009 - 74

©

ASERT

2006-2009

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 75: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

AUG 2009 - 75

©

ASERT

2006-2009

…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 76: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

AUG 2009 - 76

©

ASERT

2006-2009

…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 77: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

FURTHER INFO

Page 78: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

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

AUG 2009 - 78

©

ASERT

2006-2009

Page 79: Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

More Information: Groovy in Action

AUG 2009 - 79

©

ASERT

2006-2009

Second edition of GinA, ‘ReGinA’ now under development