$30 off During Our Annual Pro Sale. View Details »

Groovy in the light of Java 8 -- SpringOne2GX 2014

Groovy in the light of Java 8 -- SpringOne2GX 2014

How can Groovy developers can benefit from Java 8?
What are the differences between Java 8 and Groovy?
How does Groovy go beyond what Java 8?
Is Groovy still relevant now that we have Java 8?

Guillaume Laforge

September 09, 2014
Tweet

More Decks by Guillaume Laforge

Other Decks in Technology

Transcript

  1. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Groovy, in the light of Java 8
    Guillaume Laforge — Groovy project lead / Pivotal
    @glaforge

    View Slide

  2. Stay up-to-date
    Groovy Weekly Newsletter (Every Tuesday)
    http://beta.groovy-lang.org/groovy-weekly.html
    2

    View Slide

  3. Stay up-to-date
    Google+ Groovy Page
    https://google.com/+groovy
    3

    View Slide

  4. Stay up-to-date
    Google+ Groovy Community
    http://bit.ly/g-community
    4

    View Slide

  5. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Goal of this talk

    View Slide

  6. A recurring question…
    Do we still need
    Groovy now that we
    have Java 8?
    6

    View Slide

  7. To those who said no…
    7

    View Slide

  8. But more precisely…
    • Will Java lambdas replace Groovy closures?
    • What are the differences between them?
    !
    • Will Groovy support all the new Java 8 language constructs?
    • lambdas, default methods, method references…
    !
    • How Groovy developers can benefit from Java 8?
    !
    • What does Groovy offer beyond Java 8?
    8

    View Slide

  9. What about redundancy?
    !
    !
    !
    !
    !
    • Closures
    • Traits
    • Truth & null handling
    • Functional with collections
    • Method closures
    !
    !
    !
    !
    !
    !
    !
    • Lambdas
    • Default methods
    • Optional
    • Stream API
    • Method references
    9

    View Slide

  10. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Agenda

    View Slide

  11. What we’re going to talk about
    • What’s new in Java 8
    • new syntax constructs
    • new APIs
    !
    • Similar concepts in Groovy
    • and how they compare or complement
    !
    • What Groovy offers in addition
    • beyond Java, Groovy adds its own twist!
    11

    View Slide

  12. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    What’s new in Java 8?

    View Slide

  13. View Slide

  14. This is not a Java 8 crash courses
    with all the details :-)

    View Slide

  15. This is not a Java 8 crash courses
    with all the details :-)
    We want to understand the
    implications with regards to Groovy

    View Slide

  16. What’s new in Java 8?
    !
    • New syntax
    • Streams
    • Profiles
    • Security enhancements
    • JavaFX
    • Tools
    • i18n
    • Date / time API
    • Nashorn / JavaScript
    !
    !
    !
    • Pack200
    • IO / NIO improvements
    • New utility classes
    • JDBC
    • Networking
    • Concurrency
    • JAXP
    • Hotspot
    • Java Mission Control
    14

    View Slide

  17. What’s new in Java 8?
    !
    • New syntax
    • Streams
    • Profiles
    • Security enhancements
    • JavaFX
    • Tools
    • i18n
    • Date / time API
    • Nashorn / JavaScript
    !
    !
    !
    • Pack200
    • IO / NIO improvements
    • New utility classes
    • JDBC
    • Networking
    • Concurrency
    • JAXP
    • Hotspot
    • Java Mission Control
    14
    Great concise resource with the whole list:
    http://bit.ly/new-in-java-8

    View Slide

  18. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Support the
    new Java 8
    language features
    New Java 8 syntax

    View Slide

  19. New Java 8 syntax constructs
    • Lambda expressions
    • Method references
    • Static & default methods in interfaces
    • Repeating annotations
    • Annotations on types
    • Improved type inference
    • Method parameter reflection
    16

    View Slide

  20. New Java 8 syntax constructs
    • Lambda expressions
    • Method references
    • Static & default methods in interfaces
    • Repeating annotations
    • Annotations on types
    • Improved type inference
    • Method parameter reflection
    17

    View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. Lambda expressions
    19
    double highestScore = students
    .filter(Student s -> s.getGradYear() == 2011)
    .map(Student s -> s.getScore())
    .max();

    View Slide

  25. Lambda expressions
    20
    double highestScore = students
    .filter(Student s -> s.getGradYear() == 2011)
    .map(Student s -> s.getScore())
    .max();

    View Slide

  26. Lambda expressions
    20
    double highestScore = students
    .filter(Student s -> s.getGradYear() == 2011)
    .map(Student s -> s.getScore())
    .max();
    Coercion into a Predicate
    « functional interface »

    View Slide

  27. Lambda expressions
    20
    double highestScore = students
    .filter(Student s -> s.getGradYear() == 2011)
    .map(Student s -> s.getScore())
    .max();
    There’s no function type!

    View Slide

  28. Lambda expressions
    20
    double highestScore = students
    .filter(Student s -> s.getGradYear() == 2011)
    .map(Student s -> s.getScore())
    .max();
    Pipeline being built
    Single pass!

    View Slide

  29. Lambda expressions
    20
    double highestScore = students
    .filter(Student s -> s.getGradYear() == 2011)
    .map(Student s -> s.getScore())
    .max();

    View Slide

  30. Lambda expressions
    21
    double highestScore = students
    .findAll { it.gradYear == 2011 }
    .collect { it.score}
    .max()

    View Slide

  31. Lambda expressions
    21
    double highestScore = students
    .findAll { it.gradYear == 2011 }
    .collect { it.score}
    .max()
    In Groovy, that would be…

    View Slide

  32. Lambda expressions
    21
    double highestScore = students
    .findAll { it.gradYear == 2011 }
    .collect { it.score}
    .max()
    Drawback: intermediary data structures
    Unless you use iterator variants

    View Slide

  33. Lambda expressions
    21
    double highestScore = students
    .findAll { it.gradYear == 2011 }
    .collect { it.score}
    .max()

    View Slide

  34. The various lambda syntaxes
    22
    String name -> name.length()
    !
    (int left, int right) -> left + right
    !
    (String left, String sep, String right) ->
    { System.out.println(left + sep + right) }

    View Slide

  35. The various lambda syntaxes
    22
    String name -> name.length()
    !
    (int left, int right) -> left + right
    !
    (String left, String sep, String right) ->
    { System.out.println(left + sep + right) }
    One parameter: no parens
    Expression on right: no curly

    View Slide

  36. The various lambda syntaxes
    22
    String name -> name.length()
    !
    (int left, int right) -> left + right
    !
    (String left, String sep, String right) ->
    { System.out.println(left + sep + right) }
    Parentheses required for more
    that one parameter

    View Slide

  37. The various lambda syntaxes
    22
    String name -> name.length()
    !
    (int left, int right) -> left + right
    !
    (String left, String sep, String right) ->
    { System.out.println(left + sep + right) }
    Statements require curly braces
    Return keyword required if non-void returning

    View Slide

  38. The various lambda syntaxes
    22
    String name -> name.length()
    !
    (int left, int right) -> left + right
    !
    (String left, String sep, String right) ->
    { System.out.println(left + sep + right) }

    View Slide

  39. The various lambda syntaxes
    23
    name -> name.length()
    !
    (left, right) -> left + right
    !
    (left, sep, right) ->
    { System.out.println(left + sep + right) }

    View Slide

  40. The various lambda syntaxes
    23
    name -> name.length()
    !
    (left, right) -> left + right
    !
    (left, sep, right) ->
    { System.out.println(left + sep + right) }
    Clever type inference can
    help get rid of parameter
    type declarations

    View Slide

  41. Closures vs lambdas
    24
    IntStream.range(1, 100).forEach(s ->
    System.out.println(s));
    !
    Files.lines(Paths.get('README.adoc'))
    .map(it -> it.toUpperCase())
    .forEach(it -> System.out.println(it));

    View Slide

  42. Closures vs lambdas
    24
    IntStream.range(1, 100).forEach(s ->
    System.out.println(s));
    !
    Files.lines(Paths.get('README.adoc'))
    .map(it -> it.toUpperCase())
    .forEach(it -> System.out.println(it));
    IntStream.range(1, 100).forEach { println it }
    !
    Files.lines(Paths.get('README.adoc'))
    .map { it.toUpperCase() }
    .forEach { println it }

    View Slide

  43. Closures vs lambdas
    24
    IntStream.range(1, 100).forEach(s ->
    System.out.println(s));
    !
    Files.lines(Paths.get('README.adoc'))
    .map(it -> it.toUpperCase())
    .forEach(it -> System.out.println(it));
    IntStream.range(1, 100).forEach { println it }
    !
    Files.lines(Paths.get('README.adoc'))
    .map { it.toUpperCase() }
    .forEach { println it }
    Use Groovy closures
    wherever you pass
    lambdas in Java 8

    View Slide

  44. Closures vs lambdas
    24
    IntStream.range(1, 100).forEach(s ->
    System.out.println(s));
    !
    Files.lines(Paths.get('README.adoc'))
    .map(it -> it.toUpperCase())
    .forEach(it -> System.out.println(it));
    IntStream.range(1, 100).forEach { println it }
    !
    Files.lines(Paths.get('README.adoc'))
    .map { it.toUpperCase() }
    .forEach { println it }
    Groovy coerces to SAM types
    (Single Abstract Method)

    View Slide

  45. Closures vs lambdas
    24
    IntStream.range(1, 100).forEach(s ->
    System.out.println(s));
    !
    Files.lines(Paths.get('README.adoc'))
    .map(it -> it.toUpperCase())
    .forEach(it -> System.out.println(it));
    IntStream.range(1, 100).forEach { println it }
    !
    Files.lines(Paths.get('README.adoc'))
    .map { it.toUpperCase() }
    .forEach { println it }

    View Slide

  46. Beyond: Closure default parameters
    25
    def mult = { int a, int b = 10 -> a * b }
    !
    assert mult(2, 3) == 6
    assert mult(5) == 50

    View Slide

  47. Beyond: Closure default parameters
    25
    def mult = { int a, int b = 10 -> a * b }
    !
    assert mult(2, 3) == 6
    assert mult(5) == 50
    Default value if the
    parameter is not specified

    View Slide

  48. Beyond: Duck typing polymorphism
    26
    def adder = { a, b -> a + b }
    !
    assert adder(100, 200) == 300
    assert adder('X', 'Y') == 'XY'

    View Slide

  49. Beyond: Duck typing polymorphism
    26
    def adder = { a, b -> a + b }
    !
    assert adder(100, 200) == 300
    assert adder('X', 'Y') == 'XY'
    Works both for numbers
    and for strings

    View Slide

  50. Builders

    View Slide

  51. Builders
    What would Java lambda
    builders look like?

    View Slide

  52. Builders
    What would Java lambda
    builders look like?
    Aren’t Groovy builders
    more powerful?

    View Slide

  53. Lambda-based builders uglier and less powerful
    28
    MarkupBuilder pom = new XmlMarkupBuilder()
    pom.el("modelVersion", "4.0.0");
    pom.el("groupId", "com.github");
    pom.el("artifactId", "lambda-builder");
    pom.el("version", "1.0-SNAPSHOT");
    pom.el("dependencies", () -> {
    pom.el("dependency", () -> {
    pom.el("groupId", "junit");
    pom.el("artifactId", "junit");
    pom.elx("version", "4.11");
    });
    pom.el("dependency", () -> {
    pom.el("groupId", "commons-beanutils");
    pom.el("artifactId", "commons-beanutils");
    pom.elx("version", "1.7.0");
    });
    });

    View Slide

  54. Lambda-based builders uglier and less powerful
    28
    MarkupBuilder pom = new XmlMarkupBuilder()
    pom.el("modelVersion", "4.0.0");
    pom.el("groupId", "com.github");
    pom.el("artifactId", "lambda-builder");
    pom.el("version", "1.0-SNAPSHOT");
    pom.el("dependencies", () -> {
    pom.el("dependency", () -> {
    pom.el("groupId", "junit");
    pom.el("artifactId", "junit");
    pom.elx("version", "4.11");
    });
    pom.el("dependency", () -> {
    pom.el("groupId", "commons-beanutils");
    pom.el("artifactId", "commons-beanutils");
    pom.elx("version", "1.7.0");
    });
    });
    Repeated « pom »:
    No delegate like in
    Groovy’s closures

    View Slide

  55. Lambda-based builders uglier and less powerful
    28
    MarkupBuilder pom = new XmlMarkupBuilder()
    pom.el("modelVersion", "4.0.0");
    pom.el("groupId", "com.github");
    pom.el("artifactId", "lambda-builder");
    pom.el("version", "1.0-SNAPSHOT");
    pom.el("dependencies", () -> {
    pom.el("dependency", () -> {
    pom.el("groupId", "junit");
    pom.el("artifactId", "junit");
    pom.elx("version", "4.11");
    });
    pom.el("dependency", () -> {
    pom.el("groupId", "commons-beanutils");
    pom.el("artifactId", "commons-beanutils");
    pom.elx("version", "1.7.0");
    });
    });
    Zero-arg lamdas
    not as lean

    View Slide

  56. Lambda-based builders uglier and less powerful
    28
    MarkupBuilder pom = new XmlMarkupBuilder()
    pom.el("modelVersion", "4.0.0");
    pom.el("groupId", "com.github");
    pom.el("artifactId", "lambda-builder");
    pom.el("version", "1.0-SNAPSHOT");
    pom.el("dependencies", () -> {
    pom.el("dependency", () -> {
    pom.el("groupId", "junit");
    pom.el("artifactId", "junit");
    pom.elx("version", "4.11");
    });
    pom.el("dependency", () -> {
    pom.el("groupId", "commons-beanutils");
    pom.el("artifactId", "commons-beanutils");
    pom.elx("version", "1.7.0");
    });
    });
    Generic method +
    string: No dynamic
    method

    View Slide

  57. Neater Groovy builder!
    29
    def pom = new PomBuilder().project {
    modelVersion "4.0.0"
    groupId "com.github"
    artifactId "lambda-builder"
    version "1.0-SNAPSHOT"
    dependencies {
    dependency {
    groupId "junit"
    artifactId "junit"
    version "4.11"
    }
    dependency {
    groupId "commons-beanutils"
    artifactId "commons-beanutils"
    version "1.7.0"
    }
    }
    }

    View Slide

  58. Neater Groovy builder!
    29
    def pom = new PomBuilder().project {
    modelVersion "4.0.0"
    groupId "com.github"
    artifactId "lambda-builder"
    version "1.0-SNAPSHOT"
    dependencies {
    dependency {
    groupId "junit"
    artifactId "junit"
    version "4.11"
    }
    dependency {
    groupId "commons-beanutils"
    artifactId "commons-beanutils"
    version "1.7.0"
    }
    }
    }

    View Slide

  59. View Slide

  60. Aren’t Groovy builders
    more readable and lean?

    View Slide

  61. To those who said no…
    31

    View Slide

  62. Memoization

    View Slide

  63. Closure and method memoization
    33
    def fib2 = { long n ->
    if (n < 2) 1
    else call(n - 1) + call(n - 2)
    }.memoize()

    View Slide

  64. Closure and method memoization
    33
    def fib2 = { long n ->
    if (n < 2) 1
    else call(n - 1) + call(n - 2)
    }.memoize()
    Closures:
    memoize()

    View Slide

  65. Closure and method memoization
    33
    @Memoized
    long fib(long n) {
    if (n < 2) 1
    else fib(n - 1) + fib(n - 2)
    }
    def fib2 = { long n ->
    if (n < 2) 1
    else call(n - 1) + call(n - 2)
    }.memoize()
    Closures:
    memoize()

    View Slide

  66. Closure and method memoization
    33
    @Memoized
    long fib(long n) {
    if (n < 2) 1
    else fib(n - 1) + fib(n - 2)
    }
    def fib2 = { long n ->
    if (n < 2) 1
    else call(n - 1) + call(n - 2)
    }.memoize()
    Closures:
    memoize()
    Methods:
    memoization AST
    transformation

    View Slide

  67. View Slide

  68. Tail
    recursion

    View Slide

  69. Closure and method tail recursion
    35
    def fact = { n, accu = 1G ->
    if (n < 2) accu
    else fact.trampoline(n - 1, n * accu)
    }.trampoline()

    View Slide

  70. Closure and method tail recursion
    35
    def fact = { n, accu = 1G ->
    if (n < 2) accu
    else fact.trampoline(n - 1, n * accu)
    }.trampoline()
    Closures:
    Tail recursion with
    trampoline()

    View Slide

  71. Closure and method tail recursion
    35
    @groovy.transform.TailRecursive
    def fact(n, accu = 1G) {
    if (n < 2) accu
    else fact(n - 1, n * accu)
    }
    def fact = { n, accu = 1G ->
    if (n < 2) accu
    else fact.trampoline(n - 1, n * accu)
    }.trampoline()
    Closures:
    Tail recursion with
    trampoline()

    View Slide

  72. Closure and method tail recursion
    35
    @groovy.transform.TailRecursive
    def fact(n, accu = 1G) {
    if (n < 2) accu
    else fact(n - 1, n * accu)
    }
    def fact = { n, accu = 1G ->
    if (n < 2) accu
    else fact.trampoline(n - 1, n * accu)
    }.trampoline()
    Closures:
    Tail recursion with
    trampoline()
    Methods:
    Tail recursion with
    @TailRecursive
    transformation

    View Slide

  73. Method references
    36
    button.setOnAction(event -> System.out.println(event));

    View Slide

  74. Method references
    36
    button.setOnAction(event -> System.out.println(event));
    button.setOnAction(System.out::println);

    View Slide

  75. Method references — 3 main cases
    37
    instance::instanceMethod
    !
    SomeClass::staticMethod
    !
    SomeClass::instanceMethod

    View Slide

  76. Method references — 3 main cases
    37
    instance::instanceMethod
    !
    SomeClass::staticMethod
    !
    SomeClass::instanceMethod
    Not covered by
    Groovy method
    closures yet!

    View Slide

  77. Groovy’s method closure
    38
    instance.&instanceMethod
    !
    SomeClass.&staticMethod
    !
    SomeClass.&instanceMethod

    View Slide

  78. Groovy’s method closure
    38
    instance.&instanceMethod
    !
    SomeClass.&staticMethod
    !
    SomeClass.&instanceMethod
    Choices:
    Adopt ::
    Deprecate .&
    Enhance .&

    View Slide

  79. Static methods in interfaces
    • You can put static utility methods in interfaces, 

    instead of in companion classes (like « Collections »)
    39
    public interface Stream {
    // ...
    static Stream empty() {
    return new Stream { ... }
    }
    }

    View Slide

  80. Default methods in interfaces
    • Define default behavior
    • possibly to enrich existing interfaces
    40
    public interface Stream {
    // ...
    default Builder add(T t) { ... }
    }

    View Slide

  81. Groovy Traits
    • Like interfaces, but with method bodies
    • similar to Java 8 interface default methods
    • Elegant way to compose behavior
    • multiple inheritance without the « diamond » problem
    • Traits can also be stateful
    • traits can have properties like normal classes
    • Compatible with static typing and static compilation
    • class methods from traits also visible from Java classes
    • Also possible to implement traits at runtime 41

    View Slide

  82. Simple trait
    42
    trait FlyingAbility {
    String fly() { "I'm flying!" }
    }
    !
    class Bird implements FlyingAbility {}
    def b = new Bird()
    !
    assert b.fly() == "I'm flying!"

    View Slide

  83. Trait with state
    43
    trait Named {
    String name
    }
    !
    class Bird implements Named {}
    def b = new Bird(name: 'Colibri')
    !
    assert b.name == 'Colibri'

    View Slide

  84. Multiple inheritance
    44
    trait KiteSurfer { String surf() { 'kite' } }
    !
    trait WebSurfer { String surf() { 'web' } }
    !
    class Person { String name }
    !
    class Hipster extends Person
    implements KiteSurfer, WebSurfer {}
    !
    def h = new Hipster()
    assert h.surf() == 'web'

    View Slide

  85. To know all about traits!
    45
    Rethinking API 

    design with traits
    by Cédric Champeau
    Tue 2:30pm / Trinity 3

    View Slide

  86. Annotations
    on types
    Repeating
    annotations

    View Slide

  87. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }

    View Slide

  88. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Schedule annotation
    repeated twice

    View Slide

  89. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }

    View Slide

  90. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
    Container annotation for
    the repeated annotationd

    View Slide

  91. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
    public @interface Schedules {
    Schedule[] value();
    }

    View Slide

  92. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
    public @interface Schedules {
    Schedule[] value();
    }
    The container
    annotation iteself

    View Slide

  93. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
    public @interface Schedules {
    Schedule[] value();
    }

    View Slide

  94. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
    public @interface Schedules {
    Schedule[] value();
    }
    Not yet supported

    View Slide

  95. Repeating annotations
    47
    @Schedule(dayOfMonth = "last")
    @Schedule(dayOfWeek = "Fri", hour = 23)
    public void doPeriodicCleanup() { ... }
    @Repeatable(Schedules.class)
    public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
    }
    public @interface Schedules {
    Schedule[] value();
    }

    View Slide

  96. Annotations on types
    • Everywhere you can put a type, you can put an annotation
    48
    @NonNull String name;
    !
    email = (@Email String) input;
    !
    List<@NonNull String> names;
    !
    new @Interned MyObject();
    !
    void monitorTemperature() throws
    @Critical TemperatureException { ... }
    !
    class UnmodifiableList implements
    @Readonly List<@Readonly T> {...}

    View Slide

  97. Annotations on types
    • Everywhere you can put a type, you can put an annotation
    48
    @NonNull String name;
    !
    email = (@Email String) input;
    !
    List<@NonNull String> names;
    !
    new @Interned MyObject();
    !
    void monitorTemperature() throws
    @Critical TemperatureException { ... }
    !
    class UnmodifiableList implements
    @Readonly List<@Readonly T> {...}
    Not yet supported

    View Slide

  98. Annotations on types
    • Everywhere you can put a type, you can put an annotation
    48
    @NonNull String name;
    !
    email = (@Email String) input;
    !
    List<@NonNull String> names;
    !
    new @Interned MyObject();
    !
    void monitorTemperature() throws
    @Critical TemperatureException { ... }
    !
    class UnmodifiableList implements
    @Readonly List<@Readonly T> {...}
    Imagine the potential for
    targets for local AST
    transformations?

    View Slide

  99. Annotations on types
    • Everywhere you can put a type, you can put an annotation
    48
    @NonNull String name;
    !
    email = (@Email String) input;
    !
    List<@NonNull String> names;
    !
    new @Interned MyObject();
    !
    void monitorTemperature() throws
    @Critical TemperatureException { ... }
    !
    class UnmodifiableList implements
    @Readonly List<@Readonly T> {...}

    View Slide

  100. Groovy compile-time meta-annotations
    49
    @Service
    @Transactional
    class MyTransactionalService {}

    View Slide

  101. Groovy compile-time meta-annotations
    49
    @Service
    @Transactional
    class MyTransactionalService {}
    import groovy.transform.AnnotationCollector
    !
    @Service
    @Transactional
    @AnnotationCollector
    public @interface TransactionalService {}

    View Slide

  102. Groovy compile-time meta-annotations
    49
    import groovy.transform.AnnotationCollector
    !
    @Service
    @Transactional
    @AnnotationCollector
    public @interface TransactionalService {}
    !
    @TransactionalService
    class MyTransactionalService {}

    View Slide

  103. Groovy compile-time meta-annotations
    49
    import groovy.transform.AnnotationCollector
    !
    @Service
    @Transactional
    @AnnotationCollector
    public @interface TransactionalService {}
    !
    @TransactionalService
    class MyTransactionalService {}
    Can handle parameters (even
    conflicting), or you can create
    your own « processor »

    View Slide

  104. Groovy compile-time meta-annotations
    49
    import groovy.transform.AnnotationCollector
    !
    @Service
    @Transactional
    @AnnotationCollector
    public @interface TransactionalService {}
    !
    @TransactionalService
    class MyTransactionalService {}

    View Slide

  105. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    New Java 8 APIs

    View Slide

  106. Date
    and
    Time
    API

    View Slide

  107. Date / Time API
    52
    LocalDate.now();
    today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2);
    !
    LocalDate.of(2012, Month.MAY, 14);
    dateOfBirth.plusYears(1);
    !
    LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
    LocalDate nextWed =
    date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));

    View Slide

  108. Date / Time API
    52
    LocalDate.now();
    today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2);
    !
    LocalDate.of(2012, Month.MAY, 14);
    dateOfBirth.plusYears(1);
    !
    LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
    LocalDate nextWed =
    date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
    Groovy could add some
    operator overloading?

    View Slide

  109. Date / Time API
    52
    LocalDate.now();
    today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2);
    !
    LocalDate.of(2012, Month.MAY, 14);
    dateOfBirth.plusYears(1);
    !
    LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
    LocalDate nextWed =
    date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));

    View Slide

  110. Groovy’s date / time handling
    53
    import static java.util.Calendar.*
    import groovy.time.*
    import org.codehaus.groovy.runtime.TimeCategory
    !
    def cal = Calendar.instance
    cal.set(year: 2010, month: JULY, date: 9)
    !
    assert FRIDAY == cal[DAY_OF_WEEK]
    !
    use (TimeCategory) {
    2.years + 3.months + 15.days + 23.minutes + 2.seconds
    !
    1.week - 1.day
    new Date() + 6.days
    !
    3.days.ago
    new Date() - 3
    }

    View Slide

  111. Groovy’s date / time handling
    53
    import static java.util.Calendar.*
    import groovy.time.*
    import org.codehaus.groovy.runtime.TimeCategory
    !
    def cal = Calendar.instance
    cal.set(year: 2010, month: JULY, date: 9)
    !
    assert FRIDAY == cal[DAY_OF_WEEK]
    !
    use (TimeCategory) {
    2.years + 3.months + 15.days + 23.minutes + 2.seconds
    !
    1.week - 1.day
    new Date() + 6.days
    !
    3.days.ago
    new Date() - 3
    }
    Groovy could provide the
    same for Date / Time

    View Slide

  112. Groovy’s date / time handling
    53
    import static java.util.Calendar.*
    import groovy.time.*
    import org.codehaus.groovy.runtime.TimeCategory
    !
    def cal = Calendar.instance
    cal.set(year: 2010, month: JULY, date: 9)
    !
    assert FRIDAY == cal[DAY_OF_WEEK]
    !
    use (TimeCategory) {
    2.years + 3.months + 15.days + 23.minutes + 2.seconds
    !
    1.week - 1.day
    new Date() + 6.days
    !
    3.days.ago
    new Date() - 3
    }

    View Slide

  113. Stream
    API

    View Slide

  114. map
    map
    map
    map
    map
    reduce
    reduce reduce
    reduce
    reduce

    View Slide

  115. map
    map
    map
    map
    map
    reduce
    reduce reduce
    reduce
    reduce
    Map / filter / reduce
    explained to
    your 6 year old

    View Slide

  116. map
    map
    map
    map
    map
    reduce
    reduce reduce
    reduce
    reduce

    View Slide

  117. Stream
    56
    persons.stream()
    .filter( p -> p.getAge() < 18 )
    .map( p -> p.getName().toUpperCase() )
    .sorted()
    .collect(Collectors.joining(", "));

    View Slide

  118. Groovy’s functional style with the GDK methods
    57
    persons
    .findAll { it.getAge() < 18 }
    .collect { it.name.toUpperCase() }
    .sort()
    .joining(", ")

    View Slide

  119. Groovy using streams too!
    58
    persons.stream()
    .filter { it.age < 18 }
    .map { it.name.toUpperCase() }
    .sorted()
    .collect(Collectors.joining(", "))

    View Slide

  120. Groovy using streams too!
    58
    persons.stream()
    .filter { it.age < 18 }
    .map { it.name.toUpperCase() }
    .sorted()
    .collect(Collectors.joining(", "))
    Leveraging closure to
    « SAM » type coercion

    View Slide

  121. Groovy using streams too!
    58
    persons.stream()
    .filter { it.age < 18 }
    .map { it.name.toUpperCase() }
    .sorted()
    .collect(Collectors.joining(", "))

    View Slide

  122. Optional
    59
    Optional maybeName = Optional.of("Guillaume");
    !
    String result = maybeName.orElse("unknown")
    !
    if (maybeName.ifPresent()) {
    System.out.println(maybeName.get());
    } else {
    System.out.println("unknown");
    }

    View Slide

  123. Optional
    59
    Optional maybeName = Optional.of("Guillaume");
    !
    String result = maybeName.orElse("unknown")
    !
    if (maybeName.ifPresent()) {
    System.out.println(maybeName.get());
    } else {
    System.out.println("unknown");
    }
    Wrap something that can
    be potentially null

    View Slide

  124. Optional
    59
    Optional maybeName = Optional.of("Guillaume");
    !
    String result = maybeName.orElse("unknown")
    !
    if (maybeName.ifPresent()) {
    System.out.println(maybeName.get());
    } else {
    System.out.println("unknown");
    }

    View Slide

  125. Optional
    59
    Optional maybeName = Optional.of("Guillaume");
    !
    String result = maybeName.orElse("unknown")
    !
    if (maybeName.ifPresent()) {
    System.out.println(maybeName.get());
    } else {
    System.out.println("unknown");
    }
    Force
    handling
    the null
    value

    View Slide

  126. Optional
    59
    Optional maybeName = Optional.of("Guillaume");
    !
    String result = maybeName.orElse("unknown")
    !
    if (maybeName.ifPresent()) {
    System.out.println(maybeName.get());
    } else {
    System.out.println("unknown");
    }

    View Slide

  127. View Slide

  128. You know you can customize
    the truth in Groovy?

    View Slide

  129. You know you can customize
    the truth in Groovy?
    Just implement a custom
    asBoolean() method!

    View Slide

  130. The law of Groovy Truth
    61

    View Slide

  131. The law of Groovy Truth
    61
    Everything that’s null,
    empty, zero-sized, equal
    to zero is false

    View Slide

  132. The law of Groovy Truth
    61
    Everything that’s null,
    empty, zero-sized, equal
    to zero is false
    Otherwise,
    it’s true!

    View Slide

  133. ?:

    View Slide

  134. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()

    View Slide

  135. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()
    Elvis!

    View Slide

  136. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()
    Groovy
    Truth!

    View Slide

  137. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()
    Safe navigation

    View Slide

  138. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()
    Interesting
    discussions on
    the mailing-list
    to further
    enhance usage of
    Optional from
    Groovy

    View Slide

  139. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()
    Not Yet
    Implemented

    View Slide

  140. Groovy truth, null handling, Elvis with Optional
    63
    def maybeName = Optional.of("Guillaume")
    !
    def result = maybeName ?: "unknown"
    !
    if (maybeName) {
    println maybeName.get()
    } else {
    println "unknown"
    }
    !
    maybeName?.toUpperCase()

    View Slide

  141. Nashorn

    View Slide

  142. Nashorn
    Speak German?

    View Slide

  143. Call JavaScript from Groovy with JSR-223
    65
    def manager = new ScriptEngineManager()
    def engine =
    manager.getEngineByName("nashorn")
    !
    assert engine.eval("{} + []") == 0

    View Slide

  144. Call JavaScript from Groovy with JSR-223
    65
    def manager = new ScriptEngineManager()
    def engine =
    manager.getEngineByName("nashorn")
    !
    assert engine.eval("{} + []") == 0
    Sane JavaScript logic :-)

    View Slide

  145. GrooScript — http://grooscript.org/
    66
    JS

    View Slide

  146. GrooScript — http://grooscript.org/
    66
    JS
    Gradle & Grails plugins available
    Examples available with Ratpack, Node.JS…

    View Slide

  147. Turtles all
    the way
    down!

    View Slide

  148. Turtles all
    the way
    down!
    Full Groovy!

    View Slide

  149. Turtles all
    the way
    down!
    Full Groovy!
    Back to front

    View Slide

  150. Turtles all
    the way
    down!
    Full Groovy!
    Back to front
    Front to back

    View Slide

  151. View Slide

  152. Jackson
    Pollock

    View Slide

  153. JavaFx…
    !
    !
    …the new Swing

    View Slide

  154. GroovyFX
    70
    import javafx.scene.Scene
    import static groovyx.javafx.GroovyFX.start
    !
    def chamber = ["5 Stelle": 108, "Italia.\nBene commune": 340,
    "Con Monti per l'Italia": 45, "Berlusconi": 124, "others": 4]
    !
    start {
    stage(title: 'Italian chamber of Deputies', width: 1024, height: 700, visible: true) {
    Scene s = scene {
    tilePane {
    barChart(barGap: 10, categoryGap: 20, title: "Italy's election in February 2013") {
    series(name: 'Chamber (seats)',
    data: ['5 Stelle', 17.5, 'Monti', 7.8, 'Bene commune', 55, 'Berlusconi', 19.9])
    series(name: 'Senate (seats)',
    data: ['5 Stelle', 17.4, 'Monti', 6.1, 'Bene commune', 39.1, 'Berlusconi', 37.1])
    }
    pieChart(data: chamber, title: "Chamber of Deputies")
    }
    }
    s.stylesheets.add("Chart.css")
    }
    }

    View Slide

  155. GroovyFX
    70
    import javafx.scene.Scene
    import static groovyx.javafx.GroovyFX.start
    !
    def chamber = ["5 Stelle": 108, "Italia.\nBene commune": 340,
    "Con Monti per l'Italia": 45, "Berlusconi": 124, "others": 4]
    !
    start {
    stage(title: 'Italian chamber of Deputies', width: 1024, height: 700, visible: true) {
    Scene s = scene {
    tilePane {
    barChart(barGap: 10, categoryGap: 20, title: "Italy's election in February 2013") {
    series(name: 'Chamber (seats)',
    data: ['5 Stelle', 17.5, 'Monti', 7.8, 'Bene commune', 55, 'Berlusconi', 19.9])
    series(name: 'Senate (seats)',
    data: ['5 Stelle', 17.4, 'Monti', 6.1, 'Bene commune', 39.1, 'Berlusconi', 37.1])
    }
    pieChart(data: chamber, title: "Chamber of Deputies")
    }
    }
    s.stylesheets.add("Chart.css")
    }
    }
    Groovy builder to
    the rescue!

    View Slide

  156. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Beyond Java

    View Slide

  157. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Easy scripting

    View Slide

  158. Scripting
    73
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc"
    .execute().waitFor()
    tmpHtml.delete()
    }
    }

    View Slide

  159. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'

    View Slide

  160. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    Use @Grab to download
    the htmlcleaner library

    View Slide

  161. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'

    View Slide

  162. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    Setup HtmlCleaner

    View Slide

  163. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'

    View Slide

  164. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    Recursively find all the
    files to transform

    View Slide

  165. Ant scripting
    74
    @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4')
    import org.htmlcleaner.*
    !
    def src = new File('html').toPath()
    def dst = new File('asciidoc').toPath()
    !
    def cleaner = new HtmlCleaner()
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'

    View Slide

  166. Ant scripting
    75
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )

    View Slide

  167. Ant scripting
    75
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    Clean the HTML

    View Slide

  168. Ant scripting
    75
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )

    View Slide

  169. Ant scripting
    75
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    Some more clean-up

    View Slide

  170. Ant scripting
    75
    def props = cleaner.properties
    props.translateSpecialEntities = false
    def serializer = new SimpleHtmlSerializer(props)
    !
    src.toFile().eachFileRecurse { f ->
    def relative = src.relativize(f.toPath())
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )

    View Slide

  171. Ant scripting
    76
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc"
    .execute().waitFor()
    tmpHtml.delete()
    }
    }

    View Slide

  172. Ant scripting
    76
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc"
    .execute().waitFor()
    tmpHtml.delete()
    }
    }
    Write the result

    View Slide

  173. Ant scripting
    76
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc"
    .execute().waitFor()
    tmpHtml.delete()
    }
    }

    View Slide

  174. Ant scripting
    76
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc"
    .execute().waitFor()
    tmpHtml.delete()
    }
    }
    Call an external process to convert
    and wait for its execution

    View Slide

  175. Ant scripting
    76
    def target = dst.resolve(relative)
    if (f.isDirectory()) {
    target.toFile().mkdir()
    } else if (f.name.endsWith('.html')) {
    def tmpHtml = File.createTempFile('clean', 'html')
    println "Converting $relative"
    def result = cleaner.clean(f)
    result.traverse({ tagNode, htmlNode ->
    tagNode?.attributes?.remove 'class'
    if ('td' == tagNode?.name || 'th'==tagNode?.name) {
    tagNode.name='td'
    String txt = tagNode.text
    tagNode.removeAllChildren()
    tagNode.insertChild(0, new ContentNode(txt))
    }
    true
    } as TagNodeVisitor)
    serializer.writeToFile(
    result, tmpHtml.absolutePath, "utf-8"
    )
    "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc"
    .execute().waitFor()
    tmpHtml.delete()
    }
    }

    View Slide

  176. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Android

    View Slide

  177. Android support
    • You can use Groovy to code Android apps!
    • use Groovy 2.4.0-beta-1+
    • prefer @CompileStatic
    !
    • Two great posts to get started:
    • http://melix.github.io/blog/2014/06/grooid.html
    • http://melix.github.io/blog/2014/06/grooid2.html
    78

    View Slide

  178. New York Times — Getting Groovy with Android
    79

    View Slide

  179. New York Times — Getting Groovy with Android
    79
    http://bit.ly/nyt-groovy

    View Slide

  180. What does NYT likes about Groovy on Android?
    • No Java 8, no lambda on Android…
    80
    Func0 func = new Func0() {
    @Override
    public String call() {
    return "my content";
    }
    };
    Async.start(func);

    View Slide

  181. What does NYT likes about Groovy on Android?
    • No Java 8, no lambda on Android…
    81
    !
    !
    !
    !
    !
    !
    Async.start { "my content" }

    View Slide

  182. What does NYT likes about Groovy on Android?
    • No Java 8, no lambda on Android…
    81
    !
    !
    !
    !
    !
    !
    Async.start { "my content" }
    Good bye annonymous
    inner classes!

    View Slide

  183. What does NYT likes about Groovy on Android?
    !
    • Groovy code more concise and more readable
    !
    • but just as type-safe as needed!

    (with @TypeChecked)
    !
    • but just as fast as needed!

    (with @CompileStatic)
    82

    View Slide

  184. Android support
    83

    View Slide

  185. Android support
    83

    View Slide

  186. Android support
    83
    Source code available:
    https://github.com/melix/gr8confagenda

    View Slide

  187. To know all about Android support
    84
    Groovy & Android, 

    a winning pair?
    by Cédric Champeau
    Thu 12:45pm / Trinity 3

    View Slide

  188. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    A rich Groovy ecosystem

    View Slide

  189. Grails — web framework
    86
    @Grab("com.h2database:h2:1.3.173")
    import grails.persistence.*
    !
    @Entity
    @Resource(uri='/books')
    class Book {
    String title
    }

    View Slide

  190. Grails — web framework
    86
    @Grab("com.h2database:h2:1.3.173")
    import grails.persistence.*
    !
    @Entity
    @Resource(uri='/books')
    class Book {
    String title
    }
    One class, one command,
    and you’ve got a full REST
    CRUD application!

    View Slide

  191. Ratpack — web framework
    87
    @GrabResolver("https://oss.jfrog.org/artifactory/repo")
    @Grab("org.ratpack-framework:ratpack-groovy:0.9.8")
    import static org.ratpackframework.groovy.RatpackScript.ratpack
    import static org.ratpackframework.groovy.Template.groovyTemplate
    !
    ratpack {
    handlers {
    get {
    response.send "Welcome!"
    }
    !
    get("date") {
    render groovyTemplate("date.html")
    }
    !
    assets "public"
    }
    }

    View Slide

  192. Ratpack — web framework
    87
    @GrabResolver("https://oss.jfrog.org/artifactory/repo")
    @Grab("org.ratpack-framework:ratpack-groovy:0.9.8")
    import static org.ratpackframework.groovy.RatpackScript.ratpack
    import static org.ratpackframework.groovy.Template.groovyTemplate
    !
    ratpack {
    handlers {
    get {
    response.send "Welcome!"
    }
    !
    get("date") {
    render groovyTemplate("date.html")
    }
    !
    assets "public"
    }
    }
    Lightweight Netty-based
    web application toolkit

    View Slide

  193. @ArtifactProviderFor(GriffonModel)
    class ConsoleModel {
    String scriptSource
    @Observable Object scriptResult
    @Observable boolean enabled = true
    }
    Griffon — rich desktop applications
    88

    View Slide

  194. @ArtifactProviderFor(GriffonModel)
    class ConsoleModel {
    String scriptSource
    @Observable Object scriptResult
    @Observable boolean enabled = true
    }
    Griffon — rich desktop applications
    88
    Model

    View Slide

  195. @ArtifactProviderFor(GriffonModel)
    class ConsoleModel {
    String scriptSource
    @Observable Object scriptResult
    @Observable boolean enabled = true
    }
    Griffon — rich desktop applications
    88
    Model
    @ArtifactProviderFor(GriffonController)
    class ConsoleController {
    def model
    !
    @Inject
    Evaluator evaluator
    !
    void executeScript() {
    model.enabled = false
    def result
    try {
    result = evaluator.evaluate(model.scriptSource)
    } finally {
    model.enabled = true
    model.scriptResult = result
    }
    }
    }
    Controller

    View Slide

  196. @ArtifactProviderFor(GriffonModel)
    class ConsoleModel {
    String scriptSource
    @Observable Object scriptResult
    @Observable boolean enabled = true
    }
    Griffon — rich desktop applications
    88
    Model
    @ArtifactProviderFor(GriffonController)
    class ConsoleController {
    def model
    !
    @Inject
    Evaluator evaluator
    !
    void executeScript() {
    model.enabled = false
    def result
    try {
    result = evaluator.evaluate(model.scriptSource)
    } finally {
    model.enabled = true
    model.scriptResult = result
    }
    }
    }
    Controller
    application(title: application.configuration['application.title'],
    pack: true, locationByPlatform: true, id: 'mainWindow',
    iconImage: imageIcon('/griffon-icon-48x48.png').image,
    iconImages: [imageIcon('/griffon-icon-48x48.png').image,
    imageIcon('/griffon-icon-32x32.png').image,
    imageIcon('/griffon-icon-16x16.png').image]) {
    panel(border: emptyBorder(6)) {
    borderLayout()
    scrollPane(constraints: CENTER) {
    textArea(text: bind(target: model, 'scriptSource'),
    enabled: bind { model.enabled },
    columns: 40, rows: 10)
    }
    !
    hbox(constraints: SOUTH) {
    button(executeScriptAction)
    hstrut(5)
    label('Result:')
    hstrut(5)
    textField(editable: false,
    text: bind { model.scriptResult })
    }
    }
    }
    View

    View Slide

  197. Spock — unit testing & specification
    89
    @Grab('org.spockframework:spock-core:0.7-groovy-2.0')
    import spock.lang.*
    !
    class MathSpec extends Specification {
    def "maximum of two numbers"() {
    expect:
    Math.max(a, b) == c
    !
    where:
    a | b || c
    1 | 3 || 3
    7 | 4 || 7
    0 | 0 || 0
    }
    }

    View Slide

  198. Spock — unit testing & specification
    89
    @Grab('org.spockframework:spock-core:0.7-groovy-2.0')
    import spock.lang.*
    !
    class MathSpec extends Specification {
    def "maximum of two numbers"() {
    expect:
    Math.max(a, b) == c
    !
    where:
    a | b || c
    1 | 3 || 3
    7 | 4 || 7
    0 | 0 || 0
    }
    }
    Readable & concise expectations

    View Slide

  199. Spock — unit testing & specification
    89
    @Grab('org.spockframework:spock-core:0.7-groovy-2.0')
    import spock.lang.*
    !
    class MathSpec extends Specification {
    def "maximum of two numbers"() {
    expect:
    Math.max(a, b) == c
    !
    where:
    a | b || c
    1 | 3 || 3
    7 | 4 || 7
    0 | 0 || 0
    }
    }
    Readable & concise expectations
    Awesome data-driven tests
    with a wiki-like notation

    View Slide

  200. Geb — browser automation
    90
    import geb.Browser
    !
    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"
    }

    View Slide

  201. Geb — browser automation
    90
    import geb.Browser
    !
    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"
    }
    Drive your browser

    View Slide

  202. Geb — browser automation
    90
    import geb.Browser
    !
    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"
    }
    Drive your browser
    JQuery-like selectors

    View Slide

  203. Geb — browser automation
    90
    import geb.Browser
    !
    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"
    }
    Drive your browser
    JQuery-like selectors
    Fill & submit forms

    View Slide

  204. Gradle — build automation
    91
    apply plugin: 'java'
    apply plugin: 'eclipse'
    !
    sourceCompatibility = 1.5
    version = '1.0'
    jar {
    manifest {
    attributes 'Implementation-Title': 'Gradle Quickstart'
    }
    }
    !
    repositories {
    mavenCentral()
    }
    !
    dependencies {
    compile ‘commons-collections:commons-collections:3.2’
    testCompile 'junit:junit:4.+'
    }
    !
    uploadArchives {
    repositories {
    flatDir {
    dirs 'repos'
    }
    }
    }

    View Slide

  205. Gradle — build automation
    91
    apply plugin: 'java'
    apply plugin: 'eclipse'
    !
    sourceCompatibility = 1.5
    version = '1.0'
    jar {
    manifest {
    attributes 'Implementation-Title': 'Gradle Quickstart'
    }
    }
    !
    repositories {
    mavenCentral()
    }
    !
    dependencies {
    compile ‘commons-collections:commons-collections:3.2’
    testCompile 'junit:junit:4.+'
    }
    !
    uploadArchives {
    repositories {
    flatDir {
    dirs 'repos'
    }
    }
    }
    Powerful and readable DSL
    for automating your builds
    and deployments

    View Slide

  206. GPars — concurrency / parallelism / async / …
    92
    import static groovyx.gpars.actor.Actors.actor
    !
    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
    }
    }
    !
    [decryptor, console]*.join()

    View Slide

  207. GPars — concurrency / parallelism / async / …
    92
    import static groovyx.gpars.actor.Actors.actor
    !
    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
    }
    }
    !
    [decryptor, console]*.join()
    Actors…

    View Slide

  208. GPars — concurrency / parallelism / async / …
    92
    import static groovyx.gpars.actor.Actors.actor
    !
    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
    }
    }
    !
    [decryptor, console]*.join()
    Actors…
    But also:
    Dataflow concurrency, CSP, agents,
    concurrent collection processing,
    fork / join, composable async
    functions, STM

    View Slide

  209. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Groovy modules

    View Slide

  210. Groovy modules
    • Ant scripting
    • JMX
    • JSON
    • Servlet
    • Swing
    • SQL
    • Templating
    • Testing
    • XML
    94

    View Slide

  211. Groovy modules
    • Ant scripting
    • JMX
    • JSON
    • Servlet
    • Swing
    • SQL
    • Templating
    • Testing
    • XML
    95

    View Slide

  212. Ant + XML
    96
    def writer = new StringWriter()
    def mkp = new MarkupBuilder(writer)
    !
    mkp.html {
    head { title 'Build notification' }
    body { p 'Your build was successful' }
    }
    !
    new AntBuilder().mail(mailhost: 'localhost',
    messagemimetype: 'text/html',
    subject: 'Build successful') {
    !
    from address: '[email protected]'
    to address: '[email protected]'
    message writer
    attchments {
    fileset(dir: 'dist') {
    include name: '**/logs*.txt'
    }
    }
    }

    View Slide

  213. Ant + XML
    96
    def writer = new StringWriter()
    def mkp = new MarkupBuilder(writer)
    !
    mkp.html {
    head { title 'Build notification' }
    body { p 'Your build was successful' }
    }
    !
    new AntBuilder().mail(mailhost: 'localhost',
    messagemimetype: 'text/html',
    subject: 'Build successful') {
    !
    from address: '[email protected]'
    to address: '[email protected]'
    message writer
    attchments {
    fileset(dir: 'dist') {
    include name: '**/logs*.txt'
    }
    }
    }
    Generate some HTML
    with the XML support

    View Slide

  214. Ant + XML
    96
    def writer = new StringWriter()
    def mkp = new MarkupBuilder(writer)
    !
    mkp.html {
    head { title 'Build notification' }
    body { p 'Your build was successful' }
    }
    !
    new AntBuilder().mail(mailhost: 'localhost',
    messagemimetype: 'text/html',
    subject: 'Build successful') {
    !
    from address: '[email protected]'
    to address: '[email protected]'
    message writer
    attchments {
    fileset(dir: 'dist') {
    include name: '**/logs*.txt'
    }
    }
    }
    Generate some HTML
    with the XML support
    Use the Ant builder
    and the mail task

    View Slide

  215. Ant + XML
    96
    def writer = new StringWriter()
    def mkp = new MarkupBuilder(writer)
    !
    mkp.html {
    head { title 'Build notification' }
    body { p 'Your build was successful' }
    }
    !
    new AntBuilder().mail(mailhost: 'localhost',
    messagemimetype: 'text/html',
    subject: 'Build successful') {
    !
    from address: '[email protected]'
    to address: '[email protected]'
    message writer
    attchments {
    fileset(dir: 'dist') {
    include name: '**/logs*.txt'
    }
    }
    }
    Generate some HTML
    with the XML support
    Use the Ant builder
    and the mail task
    Use the Ant’s fileset

    View Slide

  216. JSON support — creating JSON
    97
    import groovy.json.*
    !
    def json = new JsonBuilder()
    json.person {
    name 'Guillaume'
    age 37
    daughters 'Marion', 'Erine'
    address {
    street '1 Main Street'
    zip 75001
    city 'Paris'
    }
    }

    View Slide

  217. JSON support — creating JSON
    97
    import groovy.json.*
    !
    def json = new JsonBuilder()
    json.person {
    name 'Guillaume'
    age 37
    daughters 'Marion', 'Erine'
    address {
    street '1 Main Street'
    zip 75001
    city 'Paris'
    }
    }
    {
    "person": {
    "name": "Guillaume",
    "age": 37,
    "daughters": [
    "Marion",
    "Erine"
    ],
    "address": {
    "street": "1 Main Street",
    "zip": 75001,
    "city": "Paris"
    }
    }
    }

    View Slide

  218. JSON support — parsing JSON
    98
    import groovy.json.*
    !
    def url = 

    "https://api.github.com/repos/groovy/groovy-core/commits"
    !
    def commits = new JsonSlurper().parseText(url.toURL().text)
    !
    assert commits[0].commit.author.name == 'Cedric Champeau'

    View Slide

  219. JSON support — parsing JSON
    98
    import groovy.json.*
    !
    def url = 

    "https://api.github.com/repos/groovy/groovy-core/commits"
    !
    def commits = new JsonSlurper().parseText(url.toURL().text)
    !
    assert commits[0].commit.author.name == 'Cedric Champeau'
    The power of a dynamic language!

    View Slide

  220. Template module — new markup template engine
    • Based on the principles of Groovy’s « builders »
    • and particularly the MarkupBuilder class

    for generating arbitrary XML / HTML payloads
    !
    • Compiled statically for fast template rendering
    !
    • Internationalization aware
    • provide the desired Locale in the configuration object
    • usual suffix notation template_fr_FR.tpl
    !
    • Custom base template class
    • ability to provide reusable methods across your templates 99

    View Slide

  221. Template module — new markup template engine
    • Based on the principles of Groovy’s « builders »
    • and particularly the MarkupBuilder class

    for generating arbitrary XML / HTML payloads
    !
    • Compiled statically for fast template rendering
    !
    • Internationalization aware
    • provide the desired Locale in the configuration object
    • usual suffix notation template_fr_FR.tpl
    !
    • Custom base template class
    • ability to provide reusable methods across your templates 99
    Spring Boot
    approved

    View Slide

  222. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }

    View Slide

  223. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }
    Your template

    View Slide

  224. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }
    model = [cars: [
    new Car(make: 'Peugeot', name: '508'),
    new Car(make: 'Toyota', name: 'Prius’)
    ]]

    View Slide

  225. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }
    model = [cars: [
    new Car(make: 'Peugeot', name: '508'),
    new Car(make: 'Toyota', name: 'Prius’)
    ]]
    Feed a model into
    your template

    View Slide

  226. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }
    model = [cars: [
    new Car(make: 'Peugeot', name: '508'),
    new Car(make: 'Toyota', name: 'Prius’)
    ]]




    View Slide

  227. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }
    model = [cars: [
    new Car(make: 'Peugeot', name: '508'),
    new Car(make: 'Toyota', name: 'Prius’)
    ]]




    Generate the
    XML output

    View Slide

  228. Markup template engine — the idea
    100
    cars {
    cars.each {
    car(make: it.make, name: it.name)
    }
    }
    model = [cars: [
    new Car(make: 'Peugeot', name: '508'),
    new Car(make: 'Toyota', name: 'Prius’)
    ]]




    View Slide

  229. Markup template engine — in action
    101
    import  groovy.text.markup.*  
    !
    def  config  =  new  TemplateConfiguration()  
    def  engine  =  new  MarkupTemplateEngine(config)  
    def  tmpl  =  engine.createTemplate('''  
           p("Hello  ${model.name}")  
    ''')  
    def  model  =  [name:  'World']  
    System.out  <<  tmpl.make(model)

    View Slide

  230. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Summary

    View Slide

  231. Back to our original question…
    103
    Do we still need
    Groovy now that we
    have Java 8?

    View Slide

  232. View Slide

  233. Longer answer: Yes, because…
    • You can benefit from Java 8 in Groovy
    105
    Synergy

    the whole is 

    greater than
    the sum of
    the parts

    View Slide

  234. Longer answer: Yes, because…
    • Groovy goes beyond what Java 8 offers
    106
    Beyond

    Groovy always
    tries to add
    something
    to the table

    View Slide

  235. Questions still open: Syntax support for…
    • lambdas
    • not necessarily,

    (confusing / duplication)
    !
    • method references
    • enhance method closures
    • or replace them with
    method references
    !
    • interface default methods
    • yes: traits methods aren’t
    default methods
    • repeated annotations
    • yes: not urgent
    • but less boilerplate is good
    !
    • annotations on type
    • yes: opens up new
    possibilities for targets of
    local AST transformation
    !
    • interface static methods
    • yes: for Java compatibility
    107

    View Slide

  236. Further possible API enhancements
    • Make Optional Groovy-friendly
    • with regards to Groovy Truth
    • accessing the wrapped value
    !
    • More Groovy methods for…
    • NIO
    • Streams
    • Date / time
    !
    • Operator overloading for Date / time
    • for arithmetics on instants and durations
    108

    View Slide

  237. Further possible API enhancements
    • Make Optional Groovy-friendly
    • with regards to Groovy Truth
    • accessing the wrapped value
    !
    • More Groovy methods for…
    • NIO
    • Streams
    • Date / time
    !
    • Operator overloading for Date / time
    • for arithmetics on instants and durations
    108
    Community
    feedback &
    contributions
    welcome!

    View Slide

  238. More Java 8 coverage
    109
    Java 8 language
    capabilities
    by V. Subramaniam
    Thu 10:30pm / D.Ball.G

    View Slide

  239. More Java 8 coverage
    110
    How to get Groovy
    with Java 8
    by Peter Ledbrook
    Thu 10:30pm / Trinity 3

    View Slide

  240. Java 8 in Action
    !
    • By Urma, Fusco and Mycrof
    • Plublished by Manning
    • http://www.manning.com/urma/
    111

    View Slide

  241. © 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
    Q & A

    View Slide

  242. View Slide

  243. Image credits
    • Question mark
    • http://www.mynamesnotmommy.com/wp-content/uploads/2013/05/question-mark.png
    • Get out!
    • http://static.comicvine.com/uploads/original/11/117995/3772037-0075368930-27616.jpg
    • Yes
    • http://soloprpro.com/wp-content/uploads/2013/08/yes.jpg
    • Synergy
    • http://www.wildblueberries.com/wp-content/uploads/blogger/_YhH8fDK5-kU/S_5kYWwgAbI/AAAAAAAAAKU/JQ_-ISfT9KY/
    s1600/teamwork.jpg
    • Buzz Lightyear
    • http://www.disneypictures.net/data/media/202/Buzz_Lightyear_hd.jpg
    • Start wars spaceship
    • http://swc.fs2downloads.com/media/screenshots/Support_Trans/Shuttle/lambda003.jpg
    • Lambda character
    • http://lambda.ninjackaton.ninja-squad.com/images/lambda.png
    • Man clock
    • http://3.bp.blogspot.com/-7hLQ9tnmA84/TuTIoLRLMTI/AAAAAAAABWM/g7ahyLCRjJQ/s1600/Harold+Lloyd+Safety+Last.jpg
    • Stream
    • http://wallpaperswide.com/forest_stream-wallpapers.html
    • Nashorn (rhino)
    • http://2010sdafrika.files.wordpress.com/2012/07/hi_257587-nashorn-c-naturepl-com-mark-carwardine-wwf-canon.jpg 114

    View Slide

  244. Image credits
    • Brain
    • http://www.semel.ucla.edu/sites/all/files/users/user-412/dreamstime_xxl_17754591%20(2).jpg
    • Many thanks
    • http://www.trys.ie/wp-content/uploads/2013/06/many-thanks.jpg
    • Swing
    • http://makemesomethingspecial.co.uk/wp-content/uploads/2012/10/Solid-Oak-Handmade-Spliced-Swing-With-Personalised-
    Engraving-10867.jpg
    • Disclaimer
    • http://3.bp.blogspot.com/-RGnBpjXTCQA/Tj2h_JsLigI/AAAAAAAABbg/AB5ZZYzuE5w/s1600/disclaimer.jpg
    • Hammer / justice / truth
    • http://www.bombayrealty.in/images/disclaimer.jpg
    • Law
    • http://www.permanentmakeupbymv.com/wp-content/uploads/2014/07/law-2.jpg
    • Glasses / readable
    • http://a.fastcompany.net/multisite_files/coexist/imagecache/1280/poster/2013/02/1681393-poster-1280-responsive-eye-chart.jpg
    • We need you
    • http://www.bostonbreakerssoccer.com/imgs/ABOUT/volopps/we%20need%20you.JPG
    115

    View Slide

  245. Image credits
    • Jackson Pollock
    • http://www.ibiblio.org/wm/paint/auth/pollock/pollock.number-8.jpg
    • Turtles
    • http://33.media.tumblr.com/77095e3a37acb2272133c405b1f7ba54/tumblr_myfydjNgXJ1s20kxvo1_1280.jpg
    • Annotations
    • http://3.bp.blogspot.com/-f94n9BHko_s/T35ELs7nYvI/AAAAAAAAAKg/qe06LRPH9U4/s1600/IMG_1721.JPG
    • Builders
    • http://detroittraining.com/wp-content/uploads/Construction-Women2.jpg
    • Sandwich ingredients
    • http://www.fromages-de-terroirs.com/IMG/MEULE_DETOUREE_72_DPI_-2.jpg
    • http://eboutique-hebergement.orange-business.com/WebRoot/Orange/Shops/Madeinalsace/4B02/6320/943E/DB4F/DDBF/
    0A0A/33E8/4BC8/iStock_000008775559XSmall.jpg
    • http://www.cmonprimeur.fr/images/25834-vegetable-pictures%5B1%5D.jpg
    • http://1.bp.blogspot.com/-27UoqYeYtY4/T4PAr4zGSnI/AAAAAAAAA3s/lleE-NOh0LE/s1600/IMG_1528.JPG
    • http://www.growingpatch.com/wp-content/uploads/2014/05/tomato-9.jpg
    • http://vitaminsandhealthsupplements.com/wp-content/uploads/2013/08/fresh-sliced-tomato.jpg
    • http://2.bp.blogspot.com/-3pPmRhBHv9s/T3D-EtLlN1I/AAAAAAAAAgg/Y5oTM84nGb8/s1600/cochon+2.jpg
    • http://www.salamarket.fr/wp-content/uploads/steak-hach%C3%A9.jpg
    • http://www.quinzanisbakery.com/images/bread-vienna.jpg
    • http://www.saucissonvaudois.qc.ca/images/produits/Jambon_blanc.jpg
    • http://www.audion.com/system/public/categories/125/images/bread-sandwich.jpg 116

    View Slide