A Tour Through the Groovy Ecosystem
-
Upload
leonard-axelsson -
Category
Technology
-
view
1.856 -
download
0
description
Transcript of A Tour Through the Groovy Ecosystem
![Page 1: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/1.jpg)
A Tour Through the Groovy Ecosystemat
Examples available on Githubhttps://github.com/xlson/presentations/tree/master/
groovy-ecosystem-dyncon_2011
Saturday, March 12, 2011
![Page 2: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/2.jpg)
Leonard Axelsson
• Groovy developer since 2007• Co-Founder of SweGUG
–Swedish Groovy User Group• Speaker at Javaforum,
GTUG, Agical Geeknight, JFokus, SweGUG
• Developer/Consultant at Agical
@xlsonhttp://xlson.com/
Saturday, March 12, 2011
![Page 3: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/3.jpg)
a long long time ago (4 years or so ;)
Saturday, March 12, 2011
![Page 4: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/4.jpg)
Overview
• Part 1: Groovy intro ~15 mins
• Short and sweet
• Part 2: Ecosystem ~20 mins
• Mapping the wilderness
• Part 3: Questions?!?!? :)
Saturday, March 12, 2011
![Page 5: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/5.jpg)
Groovy Overview
• Originated in 2003
• Under the Apache License
• Runs on the Java Virtual Machine
• Grammar derived from Java 1.5
Saturday, March 12, 2011
![Page 6: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/6.jpg)
Groovy Overview• Dynamic language
• Inspired by Python, Ruby and Smalltalk
• Object Oriented
• Easy to learn for Java devs
• Supports Java style code out of the box
• Scriptable
• Embeddable
Saturday, March 12, 2011
![Page 7: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/7.jpg)
Dynamic Language• No compile-time checking
• int i = “Hello” throws exception at runtime
• Ducktyping
• def keyword allows you to care about what the object does, not what it is
• Supports custom DSLsnew DateDSL().last.day.in.december( 2009 )
http://groovyconsole.appspot.com/view.groovy?id=27001
Saturday, March 12, 2011
![Page 8: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/8.jpg)
The obligatory Hello World
Saturday, March 12, 2011
![Page 9: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/9.jpg)
How many in here know Groovy?
Saturday, March 12, 2011
![Page 10: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/10.jpg)
Ok, so how many in here know some Java?
Saturday, March 12, 2011
![Page 11: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/11.jpg)
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); }}
HelloWorld.java
Saturday, March 12, 2011
![Page 12: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/12.jpg)
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); }}
HelloWorld.groovy
Saturday, March 12, 2011
![Page 13: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/13.jpg)
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); }}
Removing ...
Saturday, March 12, 2011
![Page 14: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/14.jpg)
class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!") }}
Removing ...
Saturday, March 12, 2011
![Page 15: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/15.jpg)
class HelloWorld { public static void main(String[] args) { println("Hello World!") }}
Removing ...
Saturday, March 12, 2011
![Page 16: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/16.jpg)
println("Hello World!")
Removing ...
Saturday, March 12, 2011
![Page 17: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/17.jpg)
println "Hello World!"
This is it
Saturday, March 12, 2011
![Page 18: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/18.jpg)
Feature Overview• Properties
• dynamic getters and setters
• Closures
• reusable blocks of code
• Meta Object Protocol
• rewrite behaviour at runtime
• Many additions to the JDK
Saturday, March 12, 2011
![Page 19: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/19.jpg)
Primitives are objects to// Normal stringString message = 'got $5?'
// Double quoted string supports macrosString longerMessage = "Have you $message"
// Multiline strings available as wellString email = """Hi!How are you?
/Leo"""
println messageprintln longerMessageprintln email
int a = 5long b = 1float f = 5.4double d = 5.4
Saturday, March 12, 2011
![Page 20: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/20.jpg)
Plain Old Groovy Objects
Saturday, March 12, 2011
![Page 21: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/21.jpg)
POGO’s
• Properties
• getters and setters are created automatically
Saturday, March 12, 2011
![Page 22: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/22.jpg)
POGO’s
• Properties
• getters and setters are created automatically
• Named Parameters
• clarifies intent
Saturday, March 12, 2011
![Page 23: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/23.jpg)
POGO’s
class Person { String name String lastname}
def anders = new Person(name: 'Anders', lastname: 'Andersson')assert anders.getName() == 'Anders'
// Anders gets married and changes his lastnameanders.setLastname("Sundstedt")assert anders.lastname == "Sundstedt"
Saturday, March 12, 2011
![Page 24: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/24.jpg)
POGO’s• Getters and setters can be overridden
class Person { def name def lastname String setLastname(String lastname) { this.lastname = lastname.reverse() }}
def anders = new Person(name: 'Anders', lastname: 'Andersson')
// Anders does a strange change of his lastnameanders.lastname = "Sundstedt"assert anders.lastname == "tdetsdnuS"
Saturday, March 12, 2011
![Page 25: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/25.jpg)
Built in syntax for lists and maps
Saturday, March 12, 2011
![Page 26: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/26.jpg)
Lists
def names = ['Leonard', 'Anna', 'Anders']assert names instanceof Listassert names.size() == 3assert names[0] == 'Leonard'
List syntax:
Saturday, March 12, 2011
![Page 27: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/27.jpg)
Mapsdef map = [name: 'Leonard', lastname: 'Axelsson']assert map.name == 'Leonard'assert map['lastname'] == 'Axelsson'
map.each{ key, value -> println "Key: $key, Value: $value"}
Key: name, Value: LeonardKey: lastname, Value: Axelsson
Output:
Saturday, March 12, 2011
![Page 28: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/28.jpg)
each, find and findAllclass Person { String name String lastname boolean male}
def ages = [new Person(name: 'Bo', lastname: 'Olsson', male: true), new Person(name: 'Gunn', lastname: 'Bertilsson', male: false), new Person(name: 'Britt', lastname: 'Olsson', male: false)]// Print all namesages.each { println "$it.name $it.lastname" }
// Find one maleassert ages.find{ person -> person.male }.name == 'Bo'
// or find all females assert ages.findAll{ Person p -> !p.male }.size() == 2
Saturday, March 12, 2011
![Page 29: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/29.jpg)
Putting the basics together
(0..7).collect{(('a'..'z')+('A'..'Z')+(0..9))[new Random().nextInt(62)]}.join()
Saturday, March 12, 2011
![Page 30: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/30.jpg)
AST TransformationsCompile-time meta programming
Saturday, March 12, 2011
![Page 31: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/31.jpg)
@Log
import groovy.util.logging.*
@Logclass LogDemo { def usesLogger() { log.info "Uses the log annotation." } static void main(String[] args) { new LogDemo().usesLogger() }}
Saturday, March 12, 2011
![Page 32: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/32.jpg)
@Immutableimport groovy.transform.*
@Immutableclass User { String name String lastname}
def user = new User(name: 'Leo', lastname: 'Axelsson')
try { user.name = 'Anders'} catch(ReadOnlyPropertyException e) { println("Changing a property of an Immutable object will not work.")}
println user
Saturday, March 12, 2011
![Page 33: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/33.jpg)
@EqualsAndHashCode
import groovy.transform.*
@EqualsAndHashCodeclass User { String name String lastname}
def user1 = new User(name: 'Leo', lastname: 'Axelsson')def user2 = new User(name: 'Leo', lastname: 'Axelsson')
assert user1 == user2
Saturday, March 12, 2011
![Page 34: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/34.jpg)
Mapping the ecosystem
So, we’ve already started, but let’s do some more sightseeing...
Saturday, March 12, 2011
![Page 35: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/35.jpg)
GPars• Parallelization framework
• Features
• Actors
• Agents
• Dataflow
• Parallell Collection Processing
Saturday, March 12, 2011
![Page 36: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/36.jpg)
@Grab(group='org.codehaus.gpars', module='gpars', version='0.10')import groovyx.gpars.GParsExecutorsPool
def importProcessingData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 ,18, 19, 20]
time('Parallel execution') { GParsExecutorsPool.withPool { importProcessingData.eachParallel { data -> // Insert arbitrary heavy operation here sleep 200 } }}
time('Linear execution') { importProcessingData.each { // Insert arbitrary heavy operation here sleep 200 }}
def time(String desc, task) { def startTime = new Date().time def result = task() def executionTime = new Date().time - startTime println "$desc took: $executionTime ms." result}
GPars
Saturday, March 12, 2011
![Page 37: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/37.jpg)
GPars• Output (executed on dual-core machine):Parallel execution took: 1449 ms.Linear execution took: 4006 ms.
Saturday, March 12, 2011
![Page 38: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/38.jpg)
@Grab and grape• Dependency management using Apache Ivy
• Uses Maven Central
• grape
• Commandline tool to manage dependencies
• @Grab annotation
• Provides dependcies in scripts
Saturday, March 12, 2011
![Page 39: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/39.jpg)
Web
Saturday, March 12, 2011
![Page 40: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/40.jpg)
Grails
• Convention Over Configuration
• Used on one of Great Britains biggest(?) public sites: Sky.com
• Stands upon the shoulders of giants
• Rich plugin system
Saturday, March 12, 2011
![Page 41: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/41.jpg)
Grails
• Convention Over Configuration
• Used on one of Great Britains biggest(?) public sites: Sky.com
• Stands upon the shoulders of giants
• Rich plugin system
More about Grails tomorrow at 13:40-14:20
with Henk Jurriens! :)
Saturday, March 12, 2011
![Page 42: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/42.jpg)
Testing,what to say about testing...• Spock
• EasyB
• GroovyTestCase
• MockFor()
• Expandos and Maps
• Geb
• SoapUI
• Groovy Remote Control
Saturday, March 12, 2011
![Page 43: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/43.jpg)
Spock
• Great testing framework
• Easy to read (and write)
Saturday, March 12, 2011
![Page 44: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/44.jpg)
Spock:Readable tests
@Grab('org.spockframework:spock-core:0.5-groovy-1.8')@GrabExclude('org.codehaus.groovy:groovy-all:1.8.0-beta-3')import spock.lang.*
class ReadableSpec extends Specification { def "All is well in math-land"() { expect: sum == a + b where: a | b | sum 1 | 5 | 6 6 | 2 | 8 }}
Saturday, March 12, 2011
![Page 45: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/45.jpg)
Spock:Unroll your tests
@Grab('org.spockframework:spock-core:0.5-groovy-1.8')@GrabExclude('org.codehaus.groovy:groovy-all:1.8.0-beta-3')import spock.lang.*
class UnrolledSpec extends Specification { @Unroll({"The sum of $a and $b should be $sum."}) def "All is well in math-land"() { expect: sum == a + b where: a | b | sum 1 | 5 | 6 6 | 2 | 8 10 | 2 | 11 }}
Saturday, March 12, 2011
![Page 46: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/46.jpg)
Geb• Wanna surf programmatically?
• Geb == really nice and easy browser automation
• jQuery style for navigation API
• even supports calling out to jQuery
• Page objects
Saturday, March 12, 2011
![Page 47: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/47.jpg)
Geb:Headless Browsing
@Grab('org.codehaus.geb:geb-core:0.5.1')@Grab('org.seleniumhq.selenium:selenium-htmlunit-driver:2.0a7')import geb.Browser Browser.drive("http://xlson.com/") { assert title == "Leonard Axelsson"}
Saturday, March 12, 2011
![Page 48: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/48.jpg)
Geb:Finding Dyncon using Firefox@Grab('org.codehaus.geb:geb-core:0.5.1')@Grab('org.seleniumhq.selenium:selenium-firefox-driver:2.0a7')import geb.Browser Browser.drive("http://google.se/") { assert title == 'Google' $("input", name: "q").value("Dyncon 2011") $("input", name: "btnG").click()
waitFor { title.startsWith("Dyncon 2011") }
def firstLink = $("li.g", 0).find("a.l") assert firstLink.text() == "Dyncon 2011" firstLink.click()}
Saturday, March 12, 2011
![Page 49: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/49.jpg)
Building
Saturday, March 12, 2011
![Page 50: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/50.jpg)
AntBuilder
• Gives you the power of Ant in Groovy
• ..the power of what?
• Well, platform independent file handling among other things
Saturday, March 12, 2011
![Page 51: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/51.jpg)
AntBuilderdef ant = new AntBuilder()
// lets just call one taskant.echo("hello")
// here is an example of a block of Ant inside GroovyMarkupant.sequential { echo("inside sequential") myDir = "target/AntTest/" mkdir(dir:myDir) copy(todir:myDir) { fileset(dir:"src/test") { include(name:"**/*.groovy") } } echo("done")}
// now lets do some normal Groovy againfile = new File("target/AntTest/groovy/util/AntTest.groovy")assert file.exists()
Saturday, March 12, 2011
![Page 52: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/52.jpg)
Gradle• Easy to get started with
• Bye bye ‘copy-paste’-pattern
• Declarative OR Build by Convention
• Extremely Extendable
• Integrates with Ivy for dependency management
Saturday, March 12, 2011
![Page 53: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/53.jpg)
Gradle:Highlights
• Easy for the first 80% of functionality
• ...easy for the 20% last as well
• Keeps track of what’s changed
• Works everywhere thanks to the Gradle Wrapper
Saturday, March 12, 2011
![Page 54: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/54.jpg)
Gradle:Simply Groovy
apply plugin: 'groovy'apply plugin: 'application'
repositories { mavenCentral()}
dependencies { groovy 'org.codehaus.groovy:groovy-all:1.7.9'}
mainClassName='HelloWorld'
Saturday, March 12, 2011
![Page 55: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/55.jpg)
Gradle:Multiproject
• Head out to the code... :)
Saturday, March 12, 2011
![Page 56: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/56.jpg)
Other goodies
Saturday, March 12, 2011
![Page 57: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/57.jpg)
Griffon
• Grails for the desktop
• Early days but lots of plugins
• DSL for Swing
• Extends SwingBuilder
Saturday, March 12, 2011
![Page 58: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/58.jpg)
GroovyServ
• Speeds up execution of Groovy scripts
• Groovy for small utils finally valid!
Saturday, March 12, 2011
![Page 59: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/59.jpg)
GroovyServ:Pretty XML on the commandline
#!/usr/bin/env groovyclient
println(prettyPrintXml(System.in.text))
def prettyPrintXml(text) { def xmlNode = new XmlParser().parseText(text) def writer = new StringWriter() def printer = new XmlNodePrinter(new PrintWriter(writer)) printer.preserveWhitespace = true printer.print(xmlNode) writer.toString().trim()}
Saturday, March 12, 2011
![Page 60: A Tour Through the Groovy Ecosystem](https://reader034.fdocuments.net/reader034/viewer/2022052522/554a1955b4c9058c5d8b51ca/html5/thumbnails/60.jpg)
Links• Groovy
• http://groovy.codehaus.org/
• Groovy Goodness (great tips and trix)
• http://mrhaki.blogspot.com/
• Groovy Web Console (on Appengine)
• http://groovyconsole.appspot.com/
• My blog
• http://xlson.com/
• SweGUG
• http://groups.google.com/group/swegug
Saturday, March 12, 2011