Groovy & Grails

Groovy & Grails Advantages

Søren Berg Glasius @sbglasius

Who am I?

Søren Berg Glasius

  • Java developer since y2k
  • Hired by UC Berkeley in 2014
  • Groovy & Grails developer since 2009
    • and very passionated about it
  • GR8Conf co-founder and organizer.
  • 100% Nerd…

Shameless plug...

Groovy & Grails Advantages

Agenda

  • Groovy
    • Where does it come from?
    • Why not just Java
    • Making Java Groovy
    • Groovy code!
  • Grails
    • Just another Web framework?
    • How does it work?

Groovy & Grails Advantages

Agenda (cont'd)

  • Other tools in the box
    • Gradle - a build tool
    • GEB - web automation
    • Spock - a test framework
    • Gpars - Groovy Parallel Systems
    • Griffon - Rich application framework
  • Groovy & Grails adaption

Groovy

Groovy

Groovy adj. (or, less commonly, "Groovie" or "Groovey") It is roughly synonymous with words such as "cool", "excellent", "fashionable" or "amazing," depending on context.

- Wikipedia

Groovy

Groovy adj. (or, less commonly, "Groovie" or "Groovey") It is roughly synonymous with words such as "cool" , "excellent" , "fashionable" or "amazing" , depending on context.

- Wikipedia

Groovy

Groovy is a powerful, optionally typed and dynamic language, with static typing and static compilation capabilities, for the Java platform aimed at multiplying developers’ productivity thanks to a concise, familiar and easy to learn syntax.

It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.

- Groovy Home

Groovy

Groovy is a powerful, optionally typed and dynamic language, with static typing and static compilation capabilities, for the Java platform aimed at multiplying developers’ productivity thanks to a concise, familiar and easy to learn syntax.

It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.

- Groovy Home

Groovy

Groovy is a powerful, optionally typed and dynamic language, with static typing and static compilation capabilities, for the Java platform aimed at multiplying developers’ productivity thanks to a concise, familiar and easy to learn syntax.

It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.

- Groovy Home

Groovy history

  • Started by James Strachan
    • Groovy first mentioned August 23, 2003
    • Strachan left Groovy quietly before 1.0
  • March, 2004 JSR 241 was submitted
    • Marked dormant in 2012
  • 1.0 released January 2, 2007
  • 2.0 released July 2, 2012
  • 2.4.3 released March 23, 2015 (latest)
  • Apache Incubator project March 2015
  • 3.0 expected ultimo 2015

Why not just Java

  • Java is solid
  • Java is well known (+10M developers)
  • Java is fast

but...

  • Java is also verbose
  • Java can be cumbersome
  • and it's not dynamic

Note: I will not cover Java 8

Differences from Java

  • Groovy has operator overloading
    • + is a call to .plus() etc..
    • == is a call to .equals()
  • .is() can be used to compare object refs.
  • No primitive types - everything is an object
  • Calculations are done in BigDecimal
    • because 1F + 1.1F is 2.100000023841858
  • Methods are public by default
  • All exceptions are unchecked
  • Dynamic types defined by def.

Differences from Java

Boolean values

        

java

assert true; assert !false; // Object Object unassignedObject = null; Object assignedObject = new Object(); assert unassignedObject == null; assert assignedObject != null; // Simple type value int integer = 10; assert integer != 0; assert integer == 10;
        

groovy

assert true assert !false assert new Object() assert !null assert 10 assert !0 assert 1.0 assert !0.0 assert 'String with content' assert '' assert ['list', 'with', 'content'] assert ![] // Empty list assert [map: 'With', content: '!'] assert ![:]

Differences from Java

Closures

        

groovy

// call a closure: def closure1 = { int it -> 2 * it } // implicit return assert closure1(21) == 42 assert closure1.call(21) == 42
        

groovy

// Access context def answer = 42 def other2 = { return answer } // return value from context assert other2() == 42
        

groovy

def closure3 = { a, b, c -> a * b * c } assert closure3(2, 3, 7) == 42
        

groovy

// 'it' is implicit defined assert [1, 2, 3, 4, 5].collect { it * it } == [1, 4, 9, 16, 25]

Making Java Groovy

        

SimpleGreeter.java

public class SimpleGreeter implements Greeter { private String greeting; @Override public void sayHello(String... names) { String helloMessage = prepareHelloMessage(names); System.out.println(helloMessage); } public String getGreeting() {return greeting;} public void setGreeting(String greeting) {this.greeting = greeting;} private String prepareHelloMessage(String... names) { String delimiter = ""; StringBuilder stringBuilder = new StringBuilder(); for(String name: names) { stringBuilder.append(delimiter).append(name); delimiter = ", "; } return this.greeting + " " + stringBuilder.toString() + "!"; } public static void main(String[] args) { final Greeter greeter = new SimpleGreeter(); ((SimpleGreeter)greeter).setGreeting("Hello"); greeter.sayHello("Peter","Paul","Marry"); } }

28 loc / 704 chars

Making Java Groovy

        

SimpleGreeter.groovy

public class SimpleGreeter implements Greeter { private String greeting; @Override public void sayHello(String... names) { String helloMessage = prepareHelloMessage(names); System.out.println(helloMessage); } public String getGreeting() {return greeting;} public void setGreeting(String greeting) {this.greeting = greeting;} private String prepareHelloMessage(String... names) { String delimiter = ""; StringBuilder stringBuilder = new StringBuilder(); for(String name: names) { stringBuilder.append(delimiter).append(name); delimiter = ", "; } return this.greeting + " " + stringBuilder.toString() + "!"; } public static void main(String[] args) { final Greeter greeter = new SimpleGreeter(); ((SimpleGreeter)greeter).setGreeting("Hello"); greeter.sayHello("Peter","Paul","Marry"); } }

28 loc / 704 chars

Java code is Groovy code

...almost always

Making Java Groovy

        

SimpleGreeter.groovy

public class SimpleGreeter implements Greeter { private String greeting @Override public void sayHello(String... names) { String helloMessage = prepareHelloMessage(names) System.out.println(helloMessage) } public String getGreeting() {return greeting} public void setGreeting(String greeting) {this.greeting = greeting} private String prepareHelloMessage(String... names) { String delimiter = "" StringBuilder stringBuilder = new StringBuilder() for(String name: names) { stringBuilder.append(delimiter).append(name) delimiter = ", " } return this.greeting + " " + stringBuilder.toString() + "!" } public static void main(String[] args) { final Greeter greeter = new SimpleGreeter() ((SimpleGreeter)greeter).setGreeting("Hello") greeter.sayHello("Peter","Paul","Marry") } }

28 loc / 691 chars

Semicolons are optional

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { private String greeting @Override void sayHello(String... names) { String helloMessage = prepareHelloMessage(names) System.out.println(helloMessage) } String getGreeting() { return greeting } void setGreeting(String greeting) { this.greeting = greeting } private String prepareHelloMessage(String... names) { String delimiter = "" StringBuilder stringBuilder = new StringBuilder() for (String name : names) { stringBuilder.append(delimiter).append(name) delimiter = ", " } return this.greeting + " " + stringBuilder.toString() + "!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter() ((SimpleGreeter) greeter).setGreeting("Hello") greeter.sayHello("Peter", "Paul", "Marry") } }

28 loc / 661 chars

All classes and methods are public by default

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { String helloMessage = prepareHelloMessage(names) System.out.println(helloMessage) } private String prepareHelloMessage(String... names) { String delimiter = "" StringBuilder stringBuilder = new StringBuilder() for (String name : names) { stringBuilder.append(delimiter).append(name) delimiter = ", " } return greeting + " " + stringBuilder.toString() + "!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter() greeter.greeting = "Hello" greeter.sayHello("Peter", "Paul", "Marry") } }

25 loc / 538 chars

Variables not declared private are properties

Property accessor instead of setter and getter

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { String helloMessage = prepareHelloMessage(names) System.out.println(helloMessage) } private String prepareHelloMessage(String... names) { def joinedNames = names.join(', ') return greeting + " " + joinedNames + "!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter() greeter.greeting = "Hello" greeter.sayHello("Peter", "Paul", "Marry") } }

20 loc / 412 chars

Groovy has a nice Collections API

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { String helloMessage = prepareHelloMessage(names) println(helloMessage) } private String prepareHelloMessage(String... names) { return "$greeting ${names.join(', ')}!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter() greeter.greeting = "Hello" greeter.sayHello("Peter", "Paul", "Marry") } }

19 loc / 374 chars

Groovy has GStrings (interpolated strings)

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { String helloMessage = prepareHelloMessage(names) println(helloMessage) } private String prepareHelloMessage(String... names) { "$greeting ${names.join(', ')}!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter() greeter.greeting = "Hello" greeter.sayHello("Peter", "Paul", "Marry") } }

19 loc / 368 chars

Returns are optional

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { println(prepareHelloMessage(names)) } private String prepareHelloMessage(String... names) { "$greeting ${names.join(', ')}!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter(greeting: "Hello") greeter.sayHello("Peter", "Paul", "Marry") } }

17 loc / 329 chars

Named arguments in constructor

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { println prepareHelloMessage(names) } private String prepareHelloMessage(String... names) { "$greeting ${names.join ', '}!" } static void main(String[] args) { final Greeter greeter = new SimpleGreeter(greeting: "Hello") greeter.sayHello "Peter", "Paul", "Marry" } }

17 loc / 323 chars

Parentheses are optional

Making Java Groovy

        

SimpleGreeter.groovy

class SimpleGreeter implements Greeter { String greeting @Override void sayHello(String... names) { println prepareHelloMessage(names) } private String prepareHelloMessage(String... names) { "$greeting ${names.join ', '}!" } }

12 loc / 201 chars

        

SimpleGreeterRunner.groovy

greeter = new SimpleGreeter(greeting: "Hello") greeter.sayHello "Peter", "Paul", "Marry"

2 loc / 80 chars

Main block can be a script

Making Java Groovy

Java: 28 loc 704 chars
Groovy: 14 loc 281 chars
Difference
50% less
60% less

Groovy means

less code to maintain

The benefits of properties

Now, where did that important piece of code go?

        

PersonJVO.java

public class PersonJVO { private String firstName; private String lastName; private String streetAddress; private String zipCode; private String city; private String state; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getStreetAddress() { return streetAddress; } public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } /** * State code must be two chars long * @param state a state code */ public void setState(String state) { assert state != null && state.length() == 2; this.state = state; } }

The benefits of properties

Now, where did that important piece of code go?

Ahhh.. there it is - hiding

        

PersonJVO.java

/** * State code must be two chars long * @param state a state code */ public void setState(String state) { assert state != null && state.length() == 2; this.state = state; }

The benefits of properties

Focus on the important code - forget the boiler plate!

        

PersonGVO.groovy

class PersonGVO { String firstName String lastName String streetAddress String zipCode String city String state /** * State code must be two chars long * @param state a state code */ public void setState(String state) { assert state?.size() == 2 this.state = state } }

The benefits of properties

Getters and Setters

        

PersonScript.groovy

def groovyPerson = new PersonGVO() // Setters: groovyPerson.firstName = "Søren" groovyPerson.lastName = "Glasius" groovyPerson.setStreetAddress("40 Stevenson Ave") // Java syntax // Getters: println groovyPerson.firstName println groovyPerson.getLastName() // Java syntax // Java Object def javaPerson = new PersonJVO() // Setters: javaPerson.firstName = 'Joshua' javaPerson.lastName = 'Bloch' javaPerson.setStreetAddress("Unknown") // Java syntax // Getters: println javaPerson.firstName println javaPerson.getLastName()

The benefits of properties

Named arguments constructor

        

PersonScript.groovy

new PersonGVO(firstName: 'Guillaume', lastName: 'Laforge', city: 'Paris') new PersonJVO(firstName: 'Neil', lastName: 'Ford')

Collections

Groovy GDK enhances the Java collections API

Collections

Arrays

        

java

String[] strings = new String[]{"Peter", "Paul", "Mary"}; assert strings.length == 3; assert "Peter".equals(strings[0]); assert "Paul".equals(strings[1]); assert "Mary".equals(strings[strings.length - 1]); // Last element in list
        

groovy

String[] strings = ["Peter", "Paul", "Mary"] assert strings.size() == 3 assert "Peter" == strings[0] assert "Paul" == strings.getAt(1) assert "Mary" == strings[-1] // Last element in array assert ["Paul", 'Mary'] == strings[1..2]

Collections

Lists

        

java

List<String> strings = java.util.Arrays.asList("Peter", "Paul", "Mary"); assert strings.size() == 3; assert "Peter".equals(strings.get(0)); assert "Paul".equals(strings.get(1)); assert "Mary".equals(strings.get(strings.size() - 1)); // Last element in list
        

groovy

List<String> strings = ["Peter", "Paul", "Mary"] assert strings.size() == 3 assert "Peter" == strings[0] assert "Paul" == strings.getAt(1) assert "Mary" == strings[-1] // Last element in list assert ["Paul", 'Mary'] == strings[1..2]

Collections

Arrays and Lists

        

groovy

String[] strings = ["Peter", "Paul", "Mary"] assert strings.size() == 3 assert "Peter" == strings[0] assert "Paul" == strings.getAt(1) assert "Mary" == strings[-1] // Last element in array assert ["Paul", 'Mary'] == strings[1..2]
        

groovy

List<String> strings = ["Peter", "Paul", "Mary"] assert strings.size() == 3 assert "Peter" == strings[0] assert "Paul" == strings.getAt(1) assert "Mary" == strings[-1] // Last element in list assert ["Paul", 'Mary'] == strings[1..2]

Collections

Maps

        

java

Map<String, String> map = new HashMap<>(); map.put("firstName", "Søren"); map.put("middleName", "Berg"); map.put("lastName", "Glasius"); assert map.size() == 3; assert map.containsKey("firstName"); assert "Søren".equals(map.get("firstName")); assert map.containsValue("Berg"); assert map.values().contains("Glasius");
        

groovy

Map<String, String> map = [firstName: "Søren", middleName: "Berg", lastName: "Glasius"] assert map.size() == 3 assert map.firstName // Does the map contain key: firstName assert 'Søren' == map['firstName'] assert 'middleName' in map.keySet() assert 'Berg' == map.middleName assert 'Glasius' in map.values() assert 'Glasius' == map.getAt('lastName')

Collections

Ranges

        

java

// Java 8: int[] ints = IntStream.range(1, 10).toArray(); assert ints.length == 9; assert ints[0] == 1; assert ints[ints.length - 1] == 9;
        

groovy

int[] ints = 1..9 assert ints.size() == 9 assert ints[0] == 1 assert ints[-1] == 9 // Range exclusive int[] otherInts = 1..<10 assert ints == otherInts // Range on Stings def letters = 'a'..'z' assert letters.size() == 26

Collections

This was just a tiny sub-set of

methods added to Collections by Groovy

Enhancing Java

Groovy improves existing Java

classes with extra functionality

to save time

and reduce boilerplate

Enhancing Java

Reading content from file to String - in Java

        

java

static String readFile(File file) throws IOException { byte[] bytes = Files.readAllBytes(file.toPath()); return new String(bytes, "UTF-8"); } public static void main(String[] args) { File file = new File("file.txt"); try { String content = readFile(file); assert content.length() > 0; } catch (IOException e) { e.printStackTrace(); } }

Enhancing Java

Reading content from file to String - in Groovy

        

groovy

String content = new File('file.txt').text assert content.size() > 0

Enhancing Java

Reading content from URL to String - in Java

        

java

static String readUrl(URL url) throws IOException { try (InputStream in = url.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { StringBuilder result = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { result.append(line); } return result.toString(); } } public static void main(String[] args) { try { URL url = new URL("http://www.example.com/"); String content = readUrl(url); assert content.length() > 0; } catch (IOException e) { e.printStackTrace(); } }

Enhancing Java

Reading content from URL to String - in Groovy

        

groovy

String content = new URL("http://www.example.com/").text assert content.size() > 0

Enhancing Java

Using Strings - in Java

        

java

public static void main(String[] args) { String str = "java"; String cap = str.length() > 0 ? str.substring(0, 1).toUpperCase() + str.substring(1) : ""; assert "Java".equals(cap); String stringUrl = "http://example.com"; try { URL url = new URL(stringUrl); assert url.openConnection().getContentType().equals("text/html"); } catch (IOException e) { // Handle the error. } String cmd = "ls -l"; try { Process process = Runtime.getRuntime().exec(cmd); // Handle process } catch (IOException e) { // Handle the error. } }

Enhancing Java

Using Strings - in Groovy

        

groovy

assert "Java" == "java".capitalize(); def url = "http://example.com".toURL() assert url.openConnection().contentType == "text/html"; def process = "ls -l".execute() // Handle process

Enhancing Java

String types - in Java

        

java

int answer = 42; String regularString = "Some string"; String theAnswer = "The answer is " + answer; String xml = "<GeocodeResponse status=\"OK\">\n" + " <status>OK</status>\n" + " <result>\n" + " <type>university</type>\n" + " <type>establishment</type>\n" + " <formatted_address>\n" + " University of California, Berkeley, Berkeley, CA, USA\n" + " </formatted_address>\n" + " </result>\n" + "</GeocodeResponse";

Enhancing Java

String types - in Groovy

        

groovy

def mol = 42 def regularString = 'A string' def answer = "The Answer is $mol" def multilinedString = '''\ <GeocodeResponse status="OK"> <status>OK</status> <result> <type>university</type> <type>establishment</type> <formatted_address> University of California, Berkeley, Berkeley, CA, USA </formatted_address> <result> </GeocodeResponse> ''' def betterAnswer = """$mol is The Answer to the Ultimate Question of 'Life, the Universe, and Everything'"""

Enhancing Java

This was again a tiny sub-set of

enhancements to existing Java APIs

Parsing data

Groovy makes it easy to parse data

XML and Json

Parsing data

Parsing XML - Data from Google Maps

http://maps.google.com/maps/api/geocode/xml?address=UC%20Berkeley
        

GoogleMapsGeocode.xml

<GeocodeResponse> <status>OK</status> <result> <type>university</type> <type>establishment</type> <formatted_address> University of California, Berkeley, Berkeley, CA, USA </formatted_address> <address_component> <long_name>University of California, Berkeley</long_name> <short_name>UC Berkeley</short_name> <type>establishment</type> </address_component> <address_component> <long_name>Berkeley</long_name> <short_name>Berkeley</short_name> <type>locality</type> <type>political</type> </address_component> <address_component> <long_name>Alameda County</long_name> <short_name>Alameda County</short_name> <type>administrative_area_level_2</type> <type>political</type> </address_component> <address_component> <long_name>California</long_name> <short_name>CA</short_name> <type>administrative_area_level_1</type> <type>political</type> </address_component> <address_component> <long_name>USA</long_name> <short_name>US</short_name> <type>country</type> <type>political</type> </address_component> <geometry> <location> <lat>37.8718992</lat> <lng>-122.2585399</lng> </location> <location_type>APPROXIMATE</location_type> <viewport> <southwest> <lat>37.8623341</lat> <lng>-122.2719817</lng> </southwest> <northeast> <lat>37.8766204</lat> <lng>-122.2434660</lng> </northeast> </viewport> <bounds> <southwest> <lat>37.8623341</lat> <lng>-122.2719817</lng> </southwest> <northeast> <lat>37.8766204</lat> <lng>-122.2434660</lng> </northeast> </bounds> </geometry> <place_id>ChIJwdcixRh3j4ARiTJrO-o0gNo</place_id> </result> </GeocodeResponse>

Parsing data

Parsing XML - in Java

aka: lots of code to test and maintain

Parsing data

Parsing XML - in Groovy

        

groovy

url = "http://maps.google.com/maps/api/geocode/xml?address=UC%20Berkeley" response = new XmlSlurper().parse(url) def node = response.result // <GeocodeResponse><result> assert node.formatted_address.text() == 'University of California, Berkeley, Berkeley, CA, USA' assert node.address_component[0].long_name.text() == 'University of California, Berkeley' assert node.address_component[0].short_name.text() == 'UC Berkeley' assert node.geometry.location.lat.toDouble() == 37.8718992 assert node.geometry.location.lng.toDouble() == -122.2585399
        <GeocodeResponse>
    <result>
        <formatted_address>
            University of California, Berkeley, Berkeley, CA, USA
        </formatted_address>
        <address_component>
            <long_name>University of California, Berkeley</long_name>
            <short_name>UC Berkeley</short_name>
            <type>establishment</type>
        </address_component>
        <geometry>
            <location>
                <lat>37.8718992</lat>
                <lng>-122.2585399</lng>
            </location>
        </geometry>
    </result>
</GeocodeResponse>

      

Parsing data

Parsing Json - Data from Google Maps

http://maps.google.com/maps/api/geocode/json?address=UC%20Berkeley
        

GoogleMapsGeocode.json

{ "results": [ { "address_components": [ { "long_name": "University of California, Berkeley", "short_name": "UC Berkeley", "types": [ "establishment" ] }, { "long_name": "Berkeley", "short_name": "Berkeley", "types": [ "locality", "political" ] }, { "long_name": "Alameda County", "short_name": "Alameda County", "types": [ "administrative_area_level_2", "political" ] }, { "long_name": "California", "short_name": "CA", "types": [ "administrative_area_level_1", "political" ] }, { "long_name": "USA", "short_name": "US", "types": [ "country", "political" ] } ], "formatted_address": "University of California, Berkeley, Berkeley, CA, USA", "geometry": { "bounds": { "northeast": { "lat": 37.8766204, "lng": -122.243466 }, "southwest": { "lat": 37.8623341, "lng": -122.2719817 } }, "location": { "lat": 37.8718992, "lng": -122.2585399 }, "location_type": "APPROXIMATE", "viewport": { "northeast": { "lat": 37.8766204, "lng": -122.243466 }, "southwest": { "lat": 37.8623341, "lng": -122.2719817 } } }, "place_id": "ChIJwdcixRh3j4ARiTJrO-o0gNo", "types": [ "university", "establishment" ] } ], "status": "OK" }

Parsing data

Parsing Json - in Java

aka: more code to test and maintain

Parsing data

Parsing Json - in Groovy

        

groovy

url = "http://maps.google.com/maps/api/geocode/json?address=UC%20Berkeley".toURL() response = new JsonSlurper().parse(url) def map = response.results[0] // { "results": [ assert map.formatted_address == 'University of California, Berkeley, Berkeley, CA, USA' assert map.address_components[0].long_name == 'University of California, Berkeley' assert map.address_components[0].short_name == 'UC Berkeley' assert map.geometry.location.lat.toDouble() == 37.8718992 assert map.geometry.location.lng.toDouble() == -122.2585399
        {
    "results": [{
        "address_components": [{
            "long_name": "University of California, Berkeley",
            "short_name": "UC Berkeley",
        }],
        "formatted_address": "University of California, Berkeley, Berkeley, CA, USA",
        "geometry": {
            "location": {
                    "lat": 37.8718992, "lng": -122.2585399
            }
        }
    }]
}
      

More Groovy stuff!

Groovy is so much more

  • MetaProgramming
  • AST Transformations
  • Annotations for common patterns
  • Domain Specific Languages
  • Richer language, syntactic sugar
  • Groovy modules
    • XML and Json
    • JDBC the Groovy way
    • Template engine
    • Builders
  • Powerful scripting capabilities
  • .... and the list continues

More Groovy stuff!

Time to move on...

Grails

Grails

What is it?

Grails is a powerful web framework, for the Java platform aimed at multiplying developers’ productivity thanks to a Convention-over-Configuration, sensible defaults and opinionated APIs.

It integrates smoothly with the JVM, allowing you to be immediately productive whilst providing powerful features including integrated ORM, Domain-Specific Languages, runtime and compile-time meta-programming and Asynchronous programming.

Grails

What is it?

Grails is a powerful web framework, for the Java platform aimed at multiplying developers’ productivity thanks to a Convention-over-Configuration, sensible defaults and opinionated APIs.

It integrates smoothly with the JVM, allowing you to be immediately productive whilst providing powerful features including integrated ORM, Domain-Specific Languages, runtime and compile-time meta-programming and Asynchronous programming.

Grails

What is it?

Grails is a powerful web framework, for the Java platform aimed at multiplying developers’ productivity thanks to a Convention-over-Configuration, sensible defaults and opinionated APIs.

It integrates smoothly with the JVM, allowing you to be immediately productive whilst providing powerful features including integrated ORM, Domain-Specific Languages, runtime and compile-time meta-programming and Asynchronous programming.

Grails

What is in the "box"?

  • A Object Relational Mapping (ORM) called GORM
    • simplifies Hibernate
    • partly DB agnostic
    • provides NoSQL access
  • A controller layer built on Spring MVC
  • Groovy Server Pages view technology (GSP's)
  • An embedded Tomcat container
  • On the fly reloading when artifacts changes

Grails

What is in the "box"? (cont'd)

  • Spring dependency injection
  • Internationalization support (i18n)
  • A transactional service layer
  • Easy TagLib creation
  • A well defined plugin structure
  • A command line tool

The Grails Stack

The Grails Stack

The Grails Stack

The Grails Stack

The Grails Stack

The Grails Stack

The Grails Stack

The Grails Stack

Two principals

in Grails

DRY

Don't Repeat Yourself

Convention over Configuration

Grails has an opinion

Grails: Project

Getting started

      

grails

sbg$ grails create-app ucb-demo | Created Grails Application at /Users/sbg/projects/demos/UcbDemo sbg$ |
      

grails

. ├── grails-app │   ├── assets (images, javascript, stylesheets) │   ├── conf │   ├── controllers │   ├── domain │   ├── i18n │   ├── services │   ├── taglib │   ├── utils │   └── views │   └── layouts ├── lib ├── scripts ├── src │   ├── groovy │   └── java ├── test │   ├── integration │   └── unit ├── web-app │   ├── META-INF │   ├── WEB-INF │   │   └── tld └── wrapper

Grails: Elements

Create a domain object

        sbg$ grails create-domain-class grails.demo.Author
      

Grails: Elements

domain objects

        

grails

class Author { String name String country String email static constraints = { name nullable: false country nullable: false, inList: ['Canada', 'Denmark', 'USA'] email nullable: true, email: true } static mapping = { table name: 'AUTHORS' name column: 'AUTHOR_NAME' country type: 'VARCHAR(30)' } }

Grails: Elements

Domain objects relations

        

grails

class Author { String name String country String email static hasMany = [books: Book] static constraints = { name nullable: false country nullable: false, inList: ['Canada', 'Denmark', 'UK', 'USA'] email nullable: true, email: true } }
        

grails

class Book { String isbn String title int pages static belongsTo = [author: Author] static constraints = { isbn nullable: false title nullable: false pages min: 1 } }

Grails: Elements

Creating and validating Domain objects

        

grails

def author = new Author(name: "Douglas Adams", country: 'UK') author.addToBooks(isbn: '0-330-25864-8', title: "The Hitchhiker's Guide to the Galaxy", pages: 224) author.addToBooks(isbn: '0-345-39181-0', title: "The Restaurant at the End of the Universe", pages: 208) author.save()
        

grails

def author = new Author(name: "Ira Levin", country: 'France').validate() assert author.hasErrors()

Grails: Elements

Querying Domain objects

        

grails

assert Book.count() == 2
        

grails

def author = Author.get(1) assert author.name == "Douglas Adams" assert author.country == 'UK' assert author.books.size() == 2
        

grails

assert Book.list().size() == 2

Grails: Elements

Dynamic finders on Domain objects

        

grails

def author = Author.findByNameIlikeAndCountry('%adams', 'UK') assert author.name == "Douglas Adams"
        

grails

def book = Book.where { isbn =~ "03%" && pages > 200 }.get() assert book.isbn == '0345391810' assert book.title.startsWith('The Restaurant')

Grails: Elements

Updating and deleting

        

grails

def author = Author.get(1) author.addToBooks(isbn: '0-330-28700-1', title: 'So Long, and Thanks for All Fish', pages: 192) author.save()
        

grails

def author = Author.get(1) author.delete() assert Author.count() == 0

Grails: Elements

Create a controller

        sbg$ grails create-scaffolded-controller grails.demo.Book
      

Grails: Elements

Controllers: scaffolding

        

BookController.groovy

class BookController { static scaffold = true }

For fast prototyping

Micro-demo

Grails: Elements

Controllers: the code

        

AuthorController.groovy

@Transactional(readOnly = true) class AuthorController { static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"] def index(Integer max) { params.max = Math.min(max ?: 10, 100) respond Author.list(params), model: [authorInstanceCount: Author.count()] } def show(Author authorInstance) { respond authorInstance } def create() { respond new Author(params) } @Transactional def save(Author authorInstance) { if (authorInstance == null) { notFound() return } if (authorInstance.hasErrors()) { respond authorInstance.errors, view: 'create' return } authorInstance.save flush: true request.withFormat { form multipartForm { flash.message = message(code: 'default.created.message', args: [message(code: 'author.label', default: 'Author'), authorInstance.id]) redirect authorInstance } '*' { respond authorInstance, [status: CREATED] } } } def edit(Author authorInstance) { respond authorInstance } @Transactional def update(Author authorInstance) { if (authorInstance == null) { notFound() return } if (authorInstance.hasErrors()) { respond authorInstance.errors, view: 'edit' return } authorInstance.save flush: true request.withFormat { form multipartForm { flash.message = message(code: 'default.updated.message', args: [message(code: 'Author.label', default: 'Author'), authorInstance.id]) redirect authorInstance } '*' { respond authorInstance, [status: OK] } } } @Transactional def delete(Author authorInstance) { if (authorInstance == null) { notFound() return } authorInstance.delete flush: true request.withFormat { form multipartForm { flash.message = message(code: 'default.deleted.message', args: [message(code: 'Author.label', default: 'Author'), authorInstance.id]) redirect action: "index", method: "GET" } '*' { render status: NO_CONTENT } } } protected void notFound() { request.withFormat { form multipartForm { flash.message = message(code: 'default.not.found.message', args: [message(code: 'author.label', default: 'Author'), params.id]) redirect action: "index", method: "GET" } '*' { render status: NOT_FOUND } } } }

Grails: Elements

Controllers: the benefits

  • Convention over configuration
  • Content type aware
  • Easy access to commonly used variables
  • Easy rendering of output

Grails: Elements

Services: Overview

  • Transaction aware
  • Dependency injection

Grails: Elements

Services: Example

        

RemoteBookService.groovy

@Transactional class RemoteService { List<Book> fetchBooks() { String url = "http://bookservice.com/books.json".toURL() def json = new JsonSlurper().parse(url) json.books.collect { new Book(it.isbn, it.title, it.pages.toInteger()).save() } } }
        

RemoteBookController.groovy

class RemoteBookController { def remoteBookService // injected by Grails def index() { def books = remoteBookService.fetchBooks() // Render the books as json render books as JSON } }

Grails: Elements

Internationalization (aka: i18n)

  • Locale aware per request
  • Uses Spring Message Bundles
  • Translations in property files

Grails: Elements

Tag Libraries

  • Easy to implement
  • Reloads automatically
  • Advanced logic in simple tags

Grails: Elements

Plugins

        

grails

sbg$ grails create-app ucb-demo | Created Grails Application at /Users/sbg/projects/demos/UcbDemo sbg$ |

Grails: Elements

Testing

  • Grails has an extensive testing framework
  • Mock options for domain, controllers, services, tags etc..
  • Uses Spock as default test framework
  • When creating artifacts, tests are created along with them.

Other Groovy Frameworks

Gradle

Next generation build tool

        

build.gradle

plugins { id 'com.bluepapa32.watch' version '0.1.5' } apply plugin: 'idea' apply plugin: 'groovy' repositories { jcenter() } dependencies { // We use the latest groovy 2.x version for building this library compile 'org.codehaus.groovy:groovy-all:2.4.3' compile 'net.sourceforge.nekohtml:nekohtml:1.9.14' compile 'org.apache.commons:commons-lang3:3.3.2' // We use the awesome Spock testing and specification framework testCompile 'org.spockframework:spock-core:0.7-groovy-2.0' testCompile 'junit:junit:4.11' } task slides (dependsOn: 'classes', type: JavaExec) { main = 'Runner' classpath = sourceSets.main.runtimeClasspath } watch { slides { files files('src/main/java', 'src/main/groovy') tasks 'slides' } }

Gradle

Gradle vs Maven

        

sample-pom.xml

<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.foo</groupId> <artifactId>my-app</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>my-app</name> <url>http://maven.apache.org</url> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclipse-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>org.foo.App</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>

GEB

Browser automation

        

GebDemo.groovy

Browser.drive { go "http://myapp.com/login" assert $("h1").text() == "Please Login" $("form.login").with { username = "admin" password = "password" login().click() } assert $("h1").text() == "Admin Section" }

Spock

Next generation test framework

        

DataDrivenSpec.groovy

@Unroll class DataDrivenSpec extends Specification { def "minimum of #a and #b is #c"() { expect: Math.min(a, b) == c where: a | b || c 3 | 7 || 3 5 | 4 || 4 9 | 9 || 9 } def "#person.name is a #sex.toLowerCase() person"() { expect: person.getSex() == sex where: person || sex new Person(name: "Fred") || "Male" new Person(name: "Wilma") || "Female" } static class Person { String name String getSex() { name == "Fred" ? "Male" : "Female" } } }

GPars

Groovy Parallel Systems

        

GParsReactor.groovy

def decryptor = actor { loop { react { message -> if (message instanceof String) reply message.reverse() else stop() } } } def console = actor { decryptor.send 'lellarap si yvoorG' react { println 'Decrypted message: ' + it decryptor.send false // Stops the loop } } [decryptor, console]*.join()

Griffon

Rich Application Framework

A Grails like Framework for the Desktop

Griffon

Model

        

DemoConsoleModel.groovy

class DemoConsoleModel { String scriptSource @Bindable def scriptResult @Bindable boolean enabled = true }

Griffon

View

        

DemoConsoleView.groovy

application(title:'DemoConsole', pack:true, locationByPlatform:true) { panel(border:emptyBorder(6)) { borderLayout() scrollPane(constraints:CENTER) { textArea(text:bind(target:model, targetProperty:'scriptSource'), enabled: bind {model.enabled}, columns:40, rows:10) } hbox(constraints:SOUTH) { button("Execute", actionPerformed:controller.&executeScript, enabled: bind {model.enabled}) hstrut(5) label("Result:") hstrut(5) label(text:bind {model.scriptResult}) } } }

Griffon

Controller

        

DemoConsoleController.groovy

class DemoConsoleController { GroovyShell shell = new GroovyShell() // these will be injected by Griffon def model def view def executeScript(ActionEvent evt = null) { model.enabled = false doOutside { def result try { result = shell.evaluate(model.scriptSource) } finally { edt { model.enabled = true model.scriptResult = result } } } } }

Groovy & Grails adaption

Groovy & Grails adaption

Case 1

Groovy & Grails adaption

Case 2

CalNet

Summary

Groovy

  • Enhances Java
  • Seamlessly integrates with Java
  • Low learning curve for Java developers
  • Supports DSLs
  • Powerful scripting capabilities
  • Java on Steroids
  • Write less, do more

Summary

Grails

  • Opinionated
  • DRY: Don't Repeat Yourself
  • Focus on business logic, Grails handles the rest
  • Deploys as a regular WAR file
  • Builds on Spring, Hibernate and Sitemesh
  • Highly extensible plugin structure
  • Write less, do more

RED pill or BLUE pill?

Thank You

Søren Berg Glasius @sbglasius

Thank You

Søren Berg Glasius @sbglasius