Groovy

The Dynamic Language for the JVM

 

Groovy

Groovy Uses

Hello Groovy!

Most Java code is valid Groovy...

public class Hello {
  public static void main(String[] args) {
    System.out.println("Hello World");
  }
}

Hello Groovy!

Semicolons are optional...

public class Hello {
  public static void main(String[] args) {
    System.out.println("Hello World")
  }
}

Hello Groovy!

Most modifiers are optional too...

       class Hello {
         static void main(String[] args) {
    System.out.println("Hello World")
  }
}

Hello Groovy!

System.out is optional too...

       class Hello {
         static void main(String[] args) {
               println("Hello World")
  }
}

Hello Groovy!

Actually, a lot of stuff is optional...

 

               println("Hello World")

 

Groovy Language Quick Tour

Equality

Value equality with ==, reference equality with is()

def a = "Hello Groovy"
def b = a.substring(6) + " " + a.substring(0, 5)
assert b == "Groovy Hello"
assert !b.is("Groovy Hello")
def c = a
assert c == a
assert c.is(a)

Equality

Numbers are a little special...

def l = 123
assert l.class == Integer

def d = 123.0
assert d.class == BigDecimal

assert l == d
assert !l.is(d)
assert !l.equals(d)
assert l.compareTo(d) == 0

GStrings

Strings with interpolation features...

def s = "Groovy"

assert "Hello $s" == "Hello Groovy"
assert !"Hello $s".is("Hello Groovy")

def g = "Hello ${s}"
assert "g's class is ${g.class.simpleName}" ==
  "g's class is GStringImpl"

GStrings

Strings with multiline capability...

def m = """
SELECT *
FROM   Customers
WHERE  UPPER(CustomerName) LIKE '%' + $s + '%'
"""

GStrings

Careful, they are mutable too!

def s = "Groovy"
def h = "${s}"
def k = "${->s}"

assert h == "Groovy"
assert k == "Groovy"

s = "JRuby"

assert h == "Groovy"
assert k != "Groovy"

Collection Literals

Lists are first class...

def a = ["one", "two", "three", "four", "five", "six"]
assert a[0] == "one"
assert a[-1] == "six"
assert a[1, 3, 5] == ["two", "four", "six"]
assert a.class == ArrayList

So are Maps...

def m = [one: 1, two: 2, three: 3]
assert m["one"] == 1
assert m.values() as List == [1, 2, 3]
assert m.getClass() == LinkedHashMap

Map/Bean Duality

Access beans as maps...

class Person {
  String firstName
  String lastName
}
Person person = new Person(firstName: "John", lastName: "Hurst")

assert person["firstName"] == "John"
assert person["lastName"] == "Hurst"

Access maps as beans

Map map = [firstName: "John", lastName: "Hurst"]

assert map.firstName == "John"
assert map.lastName == "Hurst"

Closures

Literal block of code...

def c = {println "Hello World"}
c.call()
c()

Can take arguments...

def doubler = {v -> 2 * v}
assert doubler(6) == 12

Can be passed to and returned from methods...

def makeMultiplier(m) {
  return {v -> m * v}
}
def tripler = makeMultiplier(3)
assert tripler(6) == 18

Closures: Common Uses

Iteration over collections...

collection.each {item ->
  // do something with each item ...
}

Resource aquisition...

file.withReader {reader ->
  // do something with Reader ...
}
file.withWriter {writer ->
  // do something with Writer ...
}

Closures: Common Uses

Provide default value for Map...

def m = [:].withDefault {[]} // default is empty list
m["a"] << 1
m["b"] << 2
m["c"] << 3
m["a"] << 4
m["b"] << 5
m["a"] << 6

assert m == [
  a: [1, 4, 6],
  b: [2, 5],
  c: [3]
]

Closures: Common Uses

Enables a "functional" style...

fibo = [:].withDefault {i ->
  if (i == 0 || i == 1) 1
  else fibo[i - 2] + fibo[i - 1]
}

assert fibs[0] == 1
assert fibs[1] == 1
assert fibs[10] == 89
assert fibs.size() == 11

Regular Expressions

def pattern = ~/^ftp:\/\/(.+):(.+)@(.*)$/
assert pattern.class == java.util.regex.Pattern

Regular Expressions

def pattern = ~/^ftp:\/\/(.+):(.+)@(.*)$/
assert pattern.class == java.util.regex.Pattern

def url = "ftp://anonymous:secret@ftp.codehaus.org"
def user = ""
def password = ""
def host = ""

(url =~ pattern).each {m ->
  user = m[1]
  password = m[2]
  host = m[3]
}

assert [user, password, host] ==
  ["anonymous", "secret", "ftp.codehaus.org"]

Dynamic Language Features

Dynamically typed...

Dynamic properties and methods...

  • Add properties and methods at runtime
  • Groovy Builders respond to structure
  • Grails and other frameworks work magic
  • Domain Specific Languages

Dynamic Language Features

def o = new Object()
o.metaClass.foo = {it.toUpperCase()}
assert o.foo("bar") == "BAR"
class Foo {
  List methodCalls = []
  Object invokeMethod(String name, Object args) {
    methodCalls << name
  }
}
def f = new Foo()
f.foo()
f.bar(1, 2, 3)
assert f.methodCalls == ["foo", "bar"]

Groovy JDK

Methods added to standard JDK classes...

Groovy JDK: Collection

def v = ["one", "two", "three", "four", "five", "six"]

v.each {println it}

Groovy JDK: Collection

def v = ["one", "two", "three", "four", "five", "six"]

v.each {println it}

assert v.collect {it.toUpperCase()} ==
  ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX"]
assert v.collect {it.size()} == [3, 3, 5, 4, 4, 3]

Groovy JDK: Collection

def v = ["one", "two", "three", "four", "five", "six"]

v.each {println it}

assert v.collect {it.toUpperCase()} ==
  ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX"]
assert v.collect {it.size()} == [3, 3, 5, 4, 4, 3]
assert v*.size() == [3, 3, 5, 4, 4, 3]

Groovy JDK: Collection

def v = ["one", "two", "three", "four", "five", "six"]

v.each {println it}

assert v.collect {it.toUpperCase()} ==
  ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX"]
assert v.collect {it.size()} == [3, 3, 5, 4, 4, 3]
assert v*.size() == [3, 3, 5, 4, 4, 3]

assert v.findAll {it.size() % 2 == 0} == ["four", "five"]

Groovy JDK: Collection

def v = ["one", "two", "three", "four", "five", "six"]

v.each {println it}

assert v.collect {it.toUpperCase()} ==
  ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX"]
assert v.collect {it.size()} == [3, 3, 5, 4, 4, 3]
assert v*.size() == [3, 3, 5, 4, 4, 3]

assert v.findAll {it.size() % 2 == 0} == ["four", "five"]

assert v*.size().sum() == 22
assert v*.size().min() == 3
assert v*.size().max() == 5

... over 70 methods added to Collection

Groovy JDK: File

file.withReader {r ->
  def line = r.readLine()
  while (line != null) {
    lines << line
    line = r.readLine()
  }
}

Groovy JDK: File

file.withReader {r ->
  def line = r.readLine()
  while (line != null) {
    lines << line
    line = r.readLine()
  }
}

file.eachLine {line ->
  lines << line
}

Groovy JDK: File

file.withReader {r ->
  def line = r.readLine()
  while (line != null) {
    lines << line
    line = r.readLine()
  }
}

file.eachLine {line ->
  lines << line
}

lines = file.readLines()

Groovy JDK: File

file.withReader {r ->
  def line = r.readLine()
  while (line != null) {
    lines << line
    line = r.readLine()
  }
}

file.eachLine {line ->
  lines << line
}

lines = file.readLines()

String text = file.text

Groovy JDK: File

file.withReader {r ->
  def line = r.readLine()
  while (line != null) {
    lines << line
    line = r.readLine()
  }
}

file.eachLine {line ->
  lines << line
}

lines = file.readLines()

String text = file.text

new File("groovy-home.html").bytes =
  new URL("http://groovy.codehaus.org").bytes

... over 70 methods added to File

Groovy JDK: String

def s = "Groovy"

assert s.center(10) == "  Groovy  "
assert s.padLeft(10) == "    Groovy"
assert s.padRight(10) == "Groovy    "

assert s[4] == "v"
assert s[2..4] == "oov"
assert s[1,3,5] == "roy"

... over 90 methods added to String

Applications

Groovy SQL

Groovy SQL

import oracle.jdbc.pool.OracleDataSource
import groovy.sql.Sql

Sql db = new Sql(
  new OracleDataSource(URL: "jdbc:oracle:thin:birt/birt@XE")
)

Groovy SQL

import oracle.jdbc.pool.OracleDataSource
import groovy.sql.Sql

Sql db = new Sql(
  new OracleDataSource(URL: "jdbc:oracle:thin:birt/birt@XE")
)

def cust = db.firstRow(
  "SELECT * FROM Customers WHERE CustomerNumber = ?",
  114
)
assert cust.customerName == "Australian Collectors, Co."
assert cust.contactLastName == "Ferguson"

Groovy SQL

import oracle.jdbc.pool.OracleDataSource
import groovy.sql.Sql

Sql db = new Sql(
  new OracleDataSource(URL: "jdbc:oracle:thin:birt/birt@XE")
)

def cust = db.firstRow(
  "SELECT * FROM Customers WHERE CustomerNumber = ?",
  114
)
assert cust.customerName == "Australian Collectors, Co."
assert cust.contactLastName == "Ferguson"

def custs = db.rows("SELECT * FROM Customers")
assert custs.size() == 122

Groovy SQL

import oracle.jdbc.pool.OracleDataSource
import groovy.sql.Sql

Sql db = new Sql(
  new OracleDataSource(URL: "jdbc:oracle:thin:birt/birt@XE")
)

def cust = db.firstRow(
  "SELECT * FROM Customers WHERE CustomerNumber = ?",
  114
)
assert cust.customerName == "Australian Collectors, Co."
assert cust.contactLastName == "Ferguson"

def custs = db.rows("SELECT * FROM Customers")
assert custs.size() == 122

db.eachRow("SELECT * FROM Customers") {row ->
  if (row.customerNumber == 114) {
    println row.customerName
  }
}

Parsing and Outputting XML

Parsing and Outputting XML

<Employees>
  <Employee EmployeeNumber='1165' LastName='Jennings' FirstName='Leslie' Extension='x3291' />
  <Employee EmployeeNumber='1166' LastName='Thompson' FirstName='Leslie' Extension='x4065' />
  <Employee EmployeeNumber='1188' LastName='Firrelli' FirstName='Julie' Extension='x2173' />
  <Employee EmployeeNumber='1216' LastName='Patterson' FirstName='Steve' Extension='x4334' />
  <Employee EmployeeNumber='1286' LastName='Tseng' FirstName='Foon Yue' Extension='x2248' />
  <Employee EmployeeNumber='1323' LastName='Vanauf' FirstName='George' Extension='x4102' />
</Employees>
 
EmployeeNumber,LastName,FirstName,Extension
1165,Jennings,Leslie,x3291
1166,Thompson,Leslie,x4065
1188,Firrelli,Julie,x2173
1216,Patterson,Steve,x4334
1286,Tseng,Foon Yue,x2248
1323,Vanauf,George,x4102

Parsing XML

def employees = new XmlParser().parse(inputFile)
outputFile.withWriter {writer ->
  writer << "EmployeeNumber,LastName,FirstName,Extension\n"
  employees.Employee.each {employee ->
    writer << employee.@EmployeeNumber << "," <<
              employee.@LastName << "," <<
              employee.@FirstName << "," <<
              employee.@Extension << "\n"
  }
}

Outputting XML

outputFile.withWriter {writer ->
  new MarkupBuilder(writer).Employees {
    inputFile.splitEachLine(",") {items ->
      if (items[0] != "EmployeeNumber") {
        Employee(
          EmployeeNumber: items[0],
          LastName: items[1],
          FirstName: items[2],
          Extension: items[3]
        )
      }
    }
  }
}

GPars Parallelism

GPars Parallelism

 

def topics = [
  "Groovy_(programming_language)", "Object-oriented_programming",
  "Programming_paradigm", "Paradigm", "Science", "Knowledge",
  "Fact", "Information", "Sequence", "Mathematics", "Quantity",
  "Property_(philosophy)", "Modern_philosophy", "Philosophy"
]

def fetch = {topic ->
  def url = "http://en.wikipedia.org/wiki/$topic"
  def filename = topic.replaceAll("[()]", "") + ".html"
  println "Fetching $url..."
  new File(filename).bytes = new URL(url).bytes
  println "Saved $filename"
}



  topics.each {topic ->
    fetch(topic)
  }

GPars Parallelism

import groovyx.gpars.GParsPool

def topics = [
  "Groovy_(programming_language)", "Object-oriented_programming",
  "Programming_paradigm", "Paradigm", "Science", "Knowledge",
  "Fact", "Information", "Sequence", "Mathematics", "Quantity",
  "Property_(philosophy)", "Modern_philosophy", "Philosophy"
]

def fetch = {topic ->
  def url = "http://en.wikipedia.org/wiki/$topic"
  def filename = topic.replaceAll("[()]", "") + ".html"
  println "Fetching $url..."
  new File(filename).bytes = new URL(url).bytes
  println "Saved $filename"
}

GParsPool.withPool() {
  def fetchAsync = fetch.async()
  topics.each {topic ->
    fetchAsync(topic)
  }
}

Gradle

Gradle

apply plugin: "java"

Gradle

apply plugin: "java"

repositories {
  mavenCentral()
}

dependencies {
  compile group: "commons-lang", name: "commons-lang", version: "2.5"
  compile group: "org.springframework", name: "spring-jdbc", version: "3.0.0.RELEASE"
}

Gradle

apply plugin: "java"

repositories {
  mavenCentral()
}

dependencies {
  compile group: "commons-lang", name: "commons-lang", version: "2.5"
  compile group: "org.springframework", name: "spring-jdbc", version: "3.0.0.RELEASE"

  testCompile group: "junit:junit:4.8.1"
  testCompile group: "org.dbunit:dbunit:2.4.7"
  testCompile group: "org.slf4j:slf4j-simple:1.5.6"
}

Gradle

apply plugin: "java"

repositories {
  mavenCentral()
}

dependencies {
  compile group: "commons-lang", name: "commons-lang", version: "2.5"
  compile group: "org.springframework", name: "spring-jdbc", version: "3.0.0.RELEASE"

  testCompile group: "junit:junit:4.8.1"
  testCompile group: "org.dbunit:dbunit:2.4.7"
  testCompile group: "org.slf4j:slf4j-simple:1.5.6"

  testCompile files("lib/system/ojdbc6-11.2.0.1.0.jar")
}

Gradle

apply plugin: "java"

repositories {
  mavenCentral()
}

dependencies { ... }

test {
  systemProperties "jdbc.driver": "oracle.jdbc.OracleDriver"
  systemProperties "jdbc.url": "jdbc:oracle:thin:@oracle_on_vmware:1521:XE"
  systemProperties "jdbc.userid": "birt"
  systemProperties "jdbc.password": "birt"
}

Gradle

apply plugin: "jetty"

configurations {
  compileGwt.extendsFrom compile
}

dependencies {
  compileGwt group: "com.google.gwt:gwt-dev:2.2.0"
  compile group: "com.google.gwt:gwt-servlet:2.2.0"
  compile group: "com.google.gwt:gwt-user:2.2.0"
}

Gradle

apply plugin: "jetty"

configurations {
  compileGwt.extendsFrom compile
}

dependencies { ... }

task gwtc << {
  ant.java(
    classname: "com.google.gwt.dev.Compiler",
    args: "-war build/gwt org.nzoug.sample.SampleApplication"
  ) {
    classpath {
      path(path: configurations.compileGwt.asPath)
      path(location: "src/main/java")
    }
  }
}

war.dependsOn(gwtc)
war {
  from "build/gwt"
}

@Grab

@Grab

@Grab("org.apache.poi:poi:3.7")
import org.apache.poi.hssf.usermodel.HSSFWorkbook

new File(args[0]).withInputStream {is ->
  def workbook = new HSSFWorkbook(is)
  0.upto(workbook.sheetCount).each {i ->
    println workbook.getSheet(i).sheetName
  }
}

A Worksheet Builder

def dt(v) {Date.parse("yyyy-MM-dd", v)}

def worksheet = new HSSFWorkbookBuilder().workbook {
  sheet("Invoices") {
    row(["ID", "Date", "Amount"])
    row(["1", dt("2011-08-31"), 1035.00])
    row(["2", dt("2011-09-30"), 2520.00])
    row(["3", dt("2011-10-31"), 350.00])
  }
  sheet("Expenses") {
    row(["Date", "Amount"])
    row([dt("2011-08-31"), 46.38])
    row([dt("2011-09-30"), 99.45])
  }
}
assert worksheet.getSheet("Expenses")
  .getRow(1).getCell(1).numericCellValue == 46.38

A Worksheet Builder

class HSSFWorkbookBuilder {
  private Workbook workbook = new HSSFWorkbook()
  private Sheet sheet
  private int rows

  Workbook workbook(Closure closure) {
    closure.delegate = this
    closure.call()
    workbook
  }

  void sheet(String name, Closure closure) {
    sheet = workbook.createSheet(name)
    rows = 0
    closure.delegate = this
    closure.call()
  }

  void row(values) {
    Row row = sheet.createRow(rows++ as int)
    values.eachWithIndex {value, col ->
      Cell cell = row.createCell(col)
      switch (value) {
        case Date: cell.setCellValue((Date) value); break
        case Double: cell.setCellValue((Double) value); break
        case BigDecimal: cell.setCellValue(((BigDecimal) value).doubleValue()); break
        default: cell.setCellValue(new HSSFRichTextString("" + value)); break
      }
    }
  }
}

A Worksheet Builder

String connectionString = args[0]
String sql = new File(args[1]).text
File xlsFile = new File(args[2])

Sql db = new Sql(
  new OracleDataSource(URL: "jdbc:oracle:thin:${connectionString}")
)
xlsFile.withOutputStream {os ->
  def workbook = new HSSFWorkbookBuilder().workbook {
    sheet("Data") {
      db.eachRow(
        sql,
        {meta -> row(meta*.columnName)},
        {rs -> row(rs.toRowResult().values())}
      )
    }
  }
  workbook.write(os)
}

XLS Result

Groovy Ecosystem

Groovy

The Dynamic Language for the JVM

/