SlideShare uma empresa Scribd logo
1 de 151
Baixar para ler offline
Dr Paul King
Groovy Lead for Object Computing Inc.
@paulk_asert
http:/slideshare.net/paulk_asert/awesome-groovy
https://github.com/paulk-asert/awesome-groovy
Apache Groovy:
The Awesome Parts
FRIEND OF GROOVY
Part 1: Introducing Groovy?
What is Groovy?
4
Groovy = Java – boiler plate code
+ closures (1st class FP)
+ extensible type system
+ runtime & compile-time metaprogramming
+ flexible language grammar (DSLs)
+ scripting
+ GDK library
“Groovy is like a super version of Java.
It leverages Java features but adds productivity features
and provides great flexibility and extensibility.”
Why Groovy?
Java
Why Groovy?
Too verbose
Support
functional
programming
Better
concurrency
Evolves
too slowly
I need
feature
XXX
Too complex
for some
situations
More
flexibility
Java
Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Java
integration
Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Good IDE
support
Java
integration
Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Good IDE
support
Custom
features
Java
integration
Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Support
concurrency
Good IDE
support
Custom
features
Java
integration
Why Groovy?
Groovy
Make it
simplerMake it
dynamic
Support
simple
scripting
Support
functional
style
Support
concurrency
Good IDE
support
Custom
features
Java
integration
Java code for list manipulation
import java.util.List;
import java.util.ArrayList;
class Main {
private List keepShorterThan(List strings, int length) {
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
String s = (String) strings.get(i);
if (s.length() < length) {
result.add(s);
}
}
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Main m = new Main();
List shortNames = m.keepShorterThan(names, 4);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
Groovy code for list manipulation
import java.util.List;
import java.util.ArrayList;
class Main {
private List keepShorterThan(List strings, int length) {
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
String s = (String) strings.get(i);
if (s.length() < length) {
result.add(s);
}
}
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Main m = new Main();
List shortNames = m.keepShorterThan(names, 4);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
Rename
Main.java
to
Main.groovy
Some Java Boilerplate identified
import java.util.List;
import java.util.ArrayList;
class Main {
private List keepShorterThan(List strings, int length) {
List result = new ArrayList();
for (int i = 0; i < strings.size(); i++) {
String s = (String) strings.get(i);
if (s.length() < length) {
result.add(s);
}
}
return result;
}
public static void main(String[] args) {
List names = new ArrayList();
names.add("Ted"); names.add("Fred");
names.add("Jed"); names.add("Ned");
System.out.println(names);
Main m = new Main();
List shortNames = m.keepShorterThan(names, 4);
System.out.println(shortNames.size());
for (int i = 0; i < shortNames.size(); i++) {
String s = (String) shortNames.get(i);
System.out.println(s);
}
}
}
Are the semicolons
needed?
And shouldn’t
we us more
modern list
notation?
Why not
import common
libraries?
Do we need
the static types?
Must we always
have a main
method and
class definition?
How about
improved
consistency?
Java Boilerplate removed
def keepShorterThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() < length) {
result.add(s)
}
}
return result
}
names = new ArrayList()
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned")
System.out.println(names)
shortNames = keepShorterThan(names, 4)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
More Java Boilerplate identified
def keepShorterThan(strings, length) {
def result = new ArrayList()
for (s in strings) {
if (s.size() < length) {
result.add(s)
}
}
return result
}
names = new ArrayList()
names.add("Ted"); names.add("Fred")
names.add("Jed"); names.add("Ned")
System.out.println(names)
shortNames = keepShorterThan(names, 4)
System.out.println(shortNames.size())
for (s in shortNames) {
System.out.println(s)
}
Shouldn’t we
have special
notation for lists?
And special
facilities for
list processing?
Is ‘return’
needed at end?
Is the method
now needed?
Simplify common
methods?
Remove unambiguous
brackets?
Boilerplate removed = nicer Groovy version
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames = names.findAll{ it.size() < 4 }
println shortNames.size()
shortNames.each{ println it }
["Ted", "Fred", "Jed", "Ned"]
3
Ted
Jed
Ned
Output:
Or Groovy DSL version if required
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
// plus a DSL implementation
Or Groovy DSL version if required
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
names = []
def of, having, less
def given(_the) { [names:{ Object[] ns -> names.addAll(ns)
[and: { n -> names += n }] }] }
def the = [
number: { _of -> [names: { _having -> [size: { _less -> [than: { size ->
println names.findAll{ it.size() < size }.size() }]}] }] },
names: { _having -> [size: { _less -> [than: { size ->
names.findAll{ it.size() < size }.each{ println it } }]}] }
]
def all = [the: { println it }]
def display(arg) { arg }
Or Groovy DSL version if required
• Or use GDSL (IntelliJ IDEA) or DSLD (Eclipse)
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
Or typed Groovy DSL version if required
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
…
enum The { the }
enum Having { having }
enum Of { of }
…
class DisplayThe {
DisplayTheNamesHaving names(Having having) {
new DisplayTheNamesHaving()
}
DisplayTheNumberOf number(Of of) {
new DisplayTheNumberOf()
}
}
…
// plus 50 lines
Or typed Groovy DSL version if required
Groovy DSL being debugged
Or typed Groovy DSL version if required
@TypeChecked(extensions='EdChecker.groovy')
def method() {
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
}
Or typed Groovy DSL version if required
@TypeChecked(extensions='EdChecker.groovy')
def method() {
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
}
afterMethodCall { mc ->
mc.arguments.each {
if (isConstantExpression(it)) {
if (it.value instanceof String && !it.value.endsWith('ed')) {
addStaticTypeError("I don't like the name '${it.value}'", mc)
}
}
}
}

@TypeChecked(extensions='EdChecker.groovy')
def method() {
given the names "Ted", "Fred", "Jed" and "Ned"
display all the names
display the number of names having size less than 4
display the names having size less than 4
}
Or typed Groovy DSL version if required
afterMethodCall { mc ->
mc.arguments.each {
if (isConstantExpression(it)) {
if (it.value instanceof String && !it.value.endsWith('ed')) {
addStaticTypeError("I don't like the name '${it.value}'", mc)
}
}
}
}
@TypeChecked(extensions='EdChecker.groovy')
def method() {
given the names "Ted", “Mary", "Jed" and “Pete"
display all the names
display the number of names having size less than 4
display the names having size less than 4
}
afterMethodCall { mc ->
mc.arguments.each {
if (isConstantExpression(it)) {
if (it.value instanceof String && !it.value.endsWith('ed')) {
addStaticTypeError("I don't like the name '${it.value}'", mc)
}
}
}
}
Or typed Groovy DSL version if required
@TypeChecked(extensions='EdChecker.groovy')
def method() {
given the names "Ted", “Mary", "Jed" and “Pete"
display all the names
display the number of names having size less than 4
display the names having size less than 4
}
afterMethodCall { mc ->
mc.arguments.each {
if (isConstantExpression(it)) {
if (it.value instanceof String && !it.value.endsWith('ed')) {
addStaticTypeError("I don't like the name '${it.value}'", mc)
}
}
}
}
Or typed Groovy DSL version if required

What style of language is Groovy?
Groovy Style
• Imperative/OO but somewhat
paradigm agnostic
•Dynamic, optionally static (gradual
extensible typing)
• Extensible language features
through metaprogramming
Part 2: What makes Groovy Awesome?
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain specific
language (DSL) support
• Ecosystem
• Community/Team
Awesome
What makes Groovy Awesome?
• Java integration
• Builds up on Java
• Tight integration
• Polyglot friendly
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
Java Integration
• Groovy is Java’s friend
Java
Groovy
Java Integration
• Standing on the shoulders of Giants
• Some limitations inherited but much
gained through new releases of Java
• Rock solid foundation
• Can ease migration to new versions
of Java
Java
Groovy
Java Integration
• Seamless integration
• IDEs provide cross-language compile,
navigation, and refactoring
• Arbitrarily mix source language
• Drop-in replace
any class
• Overloaded methods
• Syntax alignment
• Shared data types
Java
Groovy
Java Integration
• Seamless integration
• IDEs provide cross-language compile,
navigation, and refactoring
• Arbitrarily mix source language
• Drop-in replace
any class
• Overloaded methods
• Syntax alignment
• Shared data types
Java
Groovy
Java Integration
• Polyglot friendly
• Typically integrates well with other
languages which integrate with Java
• JRuby
• Jython
• Scala
• Frege
• Clojure
• R through Renjin
• JavaScript
(Rhino/Nashorn)
Java Integration
• Polyglot friendly
• JSR-223 scripting
to talk to JavaScript
import javax.script.ScriptEngineManager
def mgr = new ScriptEngineManager()
def engine = mgr.getEngineByName('nashorn')
assert engine.eval('''
function factorial(n) {
if (n == 0) { return 1; }
return n * factorial(n - 1);
}
factorial(4)
''') == 24.0
Java
GroovyJavaScript
Java Integration
• Polyglot friendly: R integration
@GrabResolver('https://nexus.bedatadriven.com/content/groups/public')
@Grab('org.renjin:renjin-script-engine:0.7.0-RC7')
import javax.script.ScriptEngineManager
def mgr = new ScriptEngineManager()
def engine = mgr.getEngineByName('Renjin')
engine.with {
eval '''
factorial <- function(x) {
y <- 1
for (i in 1:x) { y <- y * i }
return(y)
}
'''
assert eval('factorial(4)')[0] == 24
}
Java
GroovyR
Grooscript
@Grab('org.grooscript:grooscript:1.2.0')
import org.grooscript.GrooScript
import javax.script.ScriptEngineManager
def groojs = GrooScript.convert '''
def sayHello = { println "Hello ${it}!" }
['Groovy','JavaScript','GrooScript'].each sayHello''',
[initialText: 'var console = { log: print }',
addGsLib: 'grooscript.min']
new ScriptEngineManager().getEngineByName("nashorn").eval(groojs)
Hello Groovy!
Hello JavaScript!
Hello GrooScript!
Jorge Franco, grooscript.org
Convert Groovy subset to JavaScript
Support: Gradle, Grails, NPM
Demos: react.js, node.js, Pollock
Converts: Classes, closures, Date
primitives, ranges, String/GString
Special: builder, templates, gQuery
What makes Groovy Awesome?
• Java integration
• Builds up on Java
• Tight integration
• Polyglot friendly
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
Awesome
• Friend of Java
• Integration
• Good JVM Citizen
What makes Groovy Awesome?
• Java integration
• Scripting support
• Simplified scripting
• Separation of concerns
• Security sandbox
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
Awesome
Scripting
• Terse format
• Isolation
• Domain-Specific
Languages (DSLs)
• Security
• Flexibility
Scripting
• Leave out surrounding class
• Entire source file:
public class Script {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
println "Hello world"
Scripting
• Various approaches supported
• GroovyShell
• GroovyScriptEngine
• GroovyClassLoader
• Spring
• JSR-223
• Base scripts
• allows predefined methods
• Binding
• allows predefined variables
• Customizers
• add imports, static imports
• lock down AST to a subset
• apply any AST transform transparently
Scripting
Scripting
Scripting
Scripting
class Robot {
void move(dir) {
println "robot moved $dir"
}
}
def robot = new Robot()
Scripting
import static Direction.*
enum Direction {
left, right, forward, backward
}
class Robot {
void move(Direction dir) {
println "robot moved $dir"
}
}
def robot = new Robot()
robot.move left
Scripting
move left
def robot = new Robot()
def binding = new Binding(
robot: robot,
move: robot.&move,
*: Direction.values().collectEntries {
[(it.name()): it]
}
)
def shell = new GroovyShell(binding)
shell.evaluate new File(args[0]).text
Scripting
move left
def robot = new Robot()
def binding = new Binding(/* ... */)
def imports = new ImportCustomizer().addStaticStars('java.lang.Math')
def timeout = new ASTTransformationCustomizer(value: 2, TimedInterrupt)
def secure = new SecureASTCustomizer().with {
closuresAllowed = false
importsWhitelist = []
tokensWhitelist = [ PLUS, MINUS, MULTIPLY, DIVIDE, /*...*/ ]
//...
}
def sysExit = new CompilationCustomizer(CANONICALIZATION) {
void call(SourceUnit src, GeneratorContext ctxt, ClassNode cn) {
new ClassCodeVisitorSupport() {
void visitMethodCallExpression(MethodCallExpression call) {
// check for System.exit()
}
// ...
}.visitClass(cn)
}
}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(imports, timeout, secure, sysExit)
def shell = new GroovyShell(binding, config)
Scripting
move left
move forward at 3.km/h
More
examples
later
What makes Groovy Awesome?
• Java integration
• Scripting support
• Simplified scripting
• Separation of concerns
• Security sandbox
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
Awesome
• Simpler scripting
• Separation of concerns
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Object-oriented
• Functional, Closures
• Logic/Dataflow programming
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
Multiparadigm
• Imperative roots from Java
• OO abstractions: classes,
interfaces, inheritance
• Properties
• Traits
• First class functional support via closures
• External libraries for advanced features
• Other paradigms via libraries
• Logic, dataflow, reactive
Awesome
Multiparadigm
Imperative/OO
Logic programming
Functional
Rich OO features
• Classes (and scripts, GroovyBeans)
• Fields (and properties)
• Methods, constructors, multi-methods, named and default parameters
• Inheritance (behavior and implementation), interfaces, traits
• Type aliases
• Packages
• Special accessor notation, spread operators, GPath, safe navigation
Traits
/**
* Things that hop
* @author Grace
*/
trait Hopper {
String jump() { "I'm jumping!" }
}
class Kangaroo implements Hopper {}
def skip = new Kangaroo()
assert skip.jump() == "I'm jumping!"
Rich functional features
• Closures
• First-class functions
• Higher-order functions
• Map, reduce, filter
• Mutable & immutable data
• Recursion
• Lazy & eager evaluation
• Advanced FP techniques
• Memoization, Trampolines, Composition, Curry
• Concurrency
map/filter/reduce
@Canonical
class Person {
String name
int age
}
def people = [
new Person('Peter', 45),
new Person('Paul', 35),
new Person('Mary', 25)
]
assert people
.findAll{ it.age < 40 }
.collect{ it.name.toUpperCase() }
.sort()
.join(', ') == 'MARY, PAUL'
map/filter/reduce (+ functional style)
@Canonical
class Person {
String name
int age
}
def people = [
new Person('Peter', 45),
new Person('Paul', 35),
new Person('Mary', 25)
]
assert people
.findAll{ it.age < 40 }
.collect{ it.name.toUpperCase() }
.sort()
.join(', ') == 'MARY, PAUL'
def young = { person, threshold ->
person.age < threshold
}.rcurry(40).memoize()
assert people
.findAll(young)
.collect{ it.name.toUpperCase() }
.sort()
.join(', ') == 'MARY, PAUL'
map/filter/reduce (+ with streams)
@Canonical
class Person {
String name
int age
}
def people = [
new Person('Peter', 45),
new Person('Paul', 35),
new Person('Mary', 25)
]
assert people
.findAll{ it.age < 40 }
.collect{ it.name.toUpperCase() }
.sort()
.join(', ') == 'MARY, PAUL'
// requires JRE 8
def commaSep = Collectors.joining(", ")
assert people.stream()
.filter{ it.age < 40 }
.map{ it.name.toUpperCase() }
.sorted()
.collect(commaSep) == 'MARY, PAUL'
Logic/Constraint programming
• Logic programming
• Declarative style
• Logic clauses for example Prolog
• Constraint programming
• Declarative style similar to logic programming but contain constraints which
must be satisfied
• Relations between variables are stated as constraints
• Not a step or sequence of steps to execute, but rather the properties of a
solution to be found
Logic programming example
cranes have 2 legs
tortoises have 4 legs
there are 7 animals
there are 20 legs
How many of each animal?
Constraint programming example
@Grab('org.choco-solver:choco-solver:4.0.4')
import org.chocosolver.solver.Model
def m = new Model()
def totalAnimals = 7
def totalLegs = 20
def numCranes = m.intVar('Cranes', 0, totalAnimals, true)
def numTortoises = m.intVar('Tortoises', 0, totalAnimals, true)
def numCraneLegs = m.intScaleView(numCranes, 2)
def numTortoiseLegs = m.intScaleView(numTortoises, 4)
m.arithm(numCranes, '+', numTortoises, '=', totalAnimals).post()
m.arithm(numCraneLegs, '+', numTortoiseLegs, '=', totalLegs).post()
if (m.solver.solve())
println "$numCranesn$numTortoises"
else
println "No Solutions" Cranes = 4
Tortoises = 3
ChocoCraneTortoise.groovy
See also: JSR-331 Constraint Programming API
Dataflow programming
• Declarative style
• Emphasizes the movement of data
• models programs as a series of connected tasks
• A task has explicitly defined inputs and outputs
• runs as soon as all of its inputs become available
• Inherently parallel
GPars
• Library classes and DSL allowing
you to handle tasks concurrently:
• Data Parallelism map, filter, reduce functionality
in parallel with parallel array support
• Asynchronous functions extend the Java executor
services to enable multi-threaded closure processing
• Dataflow Concurrency supports natural
shared-memory concurrency model, using
single-assignment variables
• Actors provide Erlang/Scala-like actors
including "remote" actors on other machines
• Safe Agents provide a non-blocking mt-safe
reference to mutable state; like "agents" in Clojure
72
Awesome
Concurrency challenge
• We can analyse the example’s task graph:
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
def a = 5
def b = f1(a)
def c = f2(a)
def d = f3(c)
def f = f4(b, d)
assert f == 10
Concurrency challenge
• We can analyse the example’s task graph:
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
def a = 5
def b = f1(a)
def c = f2(a)
def d = f3(c)
def f = f4(b, d)
assert f == 10
f2
f3
f1
f4
aa
b
c
d
f
Concurrency challenge
• Manually using asynchronous functions:
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
import static groovyx.gpars.GParsPool.withPool
withPool(2) {
def a = 5
def futureB = f1.callAsync(a)
def c = f2(a)
def d = f3(c)
def f = f4(futureB.get(), d)
assert f == 10
}
f2
f3
f1
f4
aa
futureB
c
d
f
Concurrency challenge
• And with GPars Dataflows:
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task
new Dataflows().with {
task { a = 5 }
task { b = f1(a) }
task { c = f2(a) }
task { d = f3(c) }
task { f = f4(b, d) }
assert f == 10
}
f2
f3
f1
f4
aa
b
c
d
f
Concurrency challenge
• And with GPars Dataflows:
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task
new Dataflows().with {
task { f = f4(b, d) }
task { d = f3(c) }
task { c = f2(a) }
task { b = f1(a) }
task { a = 5 }
assert f == 10
}
f2
f3
f1
f4
aa
b
c
d
f
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Object-oriented
• Functional, Closures
• Logic/dataflow programming
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
• Ability to use imperative
when needed for speed
• Numerous declarative
approaches available
when it makes sense
Awesome
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Dynamic strong typing
• Static type checking
• Extensible
• Type inference
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
Gradual typing
• Dynamic by default
• Gradual typing
• Static type checking
• Extensible type system
Awesome
Dynamic vs static
82
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
Dynamic vs static
83
Gradual
Type-time?
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
Dynamic vs static
84
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
def adder = { a, b -> a + b }
assert adder(100, 200) == 300
assert adder('X', 'Y') == 'XY'
Dynamic vs static
85
def adder = { a, b -> a + b }
assert adder(100, 200) == 300
assert adder('X', 'Y') == 'XY'
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
Duck-typing
Dynamic vs static
86
def adder = { a, b -> a + b }
assert adder(100, 200) == 300
assert adder('X', 'Y') == 'XY'
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
@TypeChecked
def myMethod() {
def myPets = ['Lassie', 'Skip']
List yourPets = ['Lassie', 'Skip']
}
shouldFail(CompilationFailedException) {
assertScript '''
@groovy.transform.TypeChecked
def yourMethod() {
List ourPets = new Date()
}
'''
}
Dynamic vs static
87
def adder = { a, b -> a + b }
assert adder(100, 200) == 300
assert adder('X', 'Y') == 'XY'
def myPets = ['Lassie', 'Skip']
List<String> yourPets = ['Lassie', 'Skip']
shouldFail(ClassCastException) {
List ourPets = new Date()
}
@TypeChecked
def myMethod() {
def myPets = ['Lassie', 'Skip']
List yourPets = ['Lassie', 'Skip']
}
shouldFail(CompilationFailedException) {
assertScript '''
@groovy.transform.TypeChecked
def yourMethod() {
List ourPets = new Date()
}
'''
}
Extensible
Inference
Extensible type checking
import groovy.transform.TypeChecked
import experimental.SprintfTypeChecker
@TypeChecked(extensions=SprintfTypeChecker)
void main() {
sprintf('%s will turn %d on %tF', 'John', new Date(), 21)
}
[Static type checking] - Parameter types didn't match types
expected from the format String:
For placeholder 2 [%d] expected 'int' but was 'java.util.Date'
For placeholder 3 [%tF] expected 'java.util.Date' but was 'int'
sprintf has an Object varargs parameter, hence not normally
amenable to further static checking but for constant Strings
we can do better using a custom type checking plugin.
Extensible type checking
import groovy.sql.Sql
import groovy.transform.TypeChecked
@TypeChecked(extensions='SQLExtension.groovy')
findAthletes(Sql sql) {
sql.eachRow('select * frm Athlete') { row -> println row }
}
SqlTC.groovy: 7: [Static type checking] - SQL query is not
valid: net.sf.jsqlparser.JSQLParserException
@ line 6, column 15.
sql.eachRow('select * frm Athlete') { row -> println row }
^
1 error
Static compilation
import groovy.transform.*
class Actor {
String firstName, lastName
@CompileStatic
String getFullName() { "$firstName $lastName" }
void makePeace() {
new AntBuilder().echo('Peace was never an option')
}
}
def magneto = new Actor(firstName: 'Ian', lastName: 'McKellen')
assert magneto.fullName == 'Ian McKellen'
magneto.makePeace()
Static compilation speed
0
5
10
15
20
25
30
Fibonacci micro-benchmark
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Dynamic strong typing
• Static type checking
• Extensible
• Type inference
• Metaprogramming
• Domain Specific Language support
• Ecosystem
• Community/Team
• Maximise duck-typing
• Minimise noise
• yet type inference
• Flexibility at runtime
• As strict as you want
when needed
• Fast when needed
Awesome
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Runtime
• Compile-time
• Macros
• Domain Specific Language support
• Ecosystem
• Community/Team
Runtime metaprogramming
• Add instance & static methods, constructors,
properties at runtime
• Intercept method/property access
• Catch missing methods, properties
• Used for dynamic builders, aspect-oriented
programming, test stubs, mocks & dummies
Awesome*
* Individual levels of awesomeness may vary
Runtime metaprogramming
• Adding methods at runtime
assert 'Hello'.reverse() == 'olleH'
String.metaClass.swapCase = { delegate.collect{
it in 'A'..'Z' ? it.toLowerCase() : it.toUpperCase()
}.join() }
assert 'Hello'.swapCase() == 'hELLO'
Runtime metaprogramming
• Intercepting methods
class Foo {
def one() { println "Called one()" }
def methodMissing(String name, params) {
println "Attempted $name($params)"
}
}
def f = new Foo()
f.one()
f.two()
f.three('Some Arg')
Called one()
Attempted two([])
Attempted three([Some Arg])
XML Parsing/GPath expressions
def xml = '''
<hosts>
<host name='MyHost'>
<service name='MyMicroService'/>
<service name='MyNanoService'/>
</host>
</hosts>
'''
def hosts = new XmlParser().parseText(xml)
assert hosts.host.service[0].@name=='MyMicroService'
def xml = '''
<hosts>
<host name='MyHost'>
<service name='MyMicroService'/>
<service name='MyNanoService'/>
</host>
</hosts>
'''
def hosts = new XmlParser().parseText(xml)
assert hosts.host.service[0].@name=='MyMicroService'
XML Parsing/GPath expressions
Compile-time metaprogramming
• Gives you the ability to change the language by augmenting the
compilation process
Awesome
Compile-time metaprogramming
• Modify the program
at compile-time
@ToString
class Person {
String first, last
}
println new Person(first: 'John',
last: 'Smith')
// => Person(John, Smith)
Compile-time metaprogramming
• Modify the program
at compile-time
class Person {
String first, last
String toString() {
"Person($first, $last)"
}
}
println new Person(first: 'John',
last: 'Smith')
// => Person(John, Smith)
Parsing Summary
public run()
...
L1
ALOAD 1
LDC 1
AALOAD
ALOAD 0
LDC "Howdy Y'all"
INVOKEINTERFACE callCurrent()
ARETURN
...
println "Howdy Y'all"
BlockStatement
-> ReturnStatement
-> MethodCallExpression
-> VariableExpression("this")
-> ConstantExpression("println")
-> ArgumentListExpression
-> ConstantExpression("Howdy Y'all")
MyScript.groovy
> groovy MyScript.groovy
> groovyc MyScript.groovy
> groovysh
> groovyConsole
• 9 phase compiler
– Early stages: read source code
and convert into a sparse syntax
tree
– Middle stages: iteratively build up
a more dense and information rich
version of the syntax tree
– Later stages: check the tree and
convert it into byte code/class files
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Parsing Summary
Parsing - Early Stages
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
@ToString
class Greeter {
String message = "Howdy Y'all"
void greet() {
println message
}
}
ClassNode: Greeter
MethodNode: greet
Property: message
type: unresolved(String)
AnnotationNode:
ToString
type:
unresolved(ToString)
methods:
properties:
annotations:
BlockStatement
MethodCall:
this.println(message)
Parsing - Middle Stages
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
ClassNode: Greeter
MethodNode: greet
FieldNode: message
type: resolved(String)
methods:
fields:
constructors:
ConstructorNode
MethodNode:
getMessageMethodNode:
setMessageMethodNode: toString
MethodNode:
getMetaClass…
Parsing - Final Stages
106
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
public greet()V
...
L1
...
ALOAD 0
GETFIELD Greeter.message
INVOKEINTERFACE callCurrent()
POP
...
Immutable Classes
• Some Rules
• Don’t provide mutators
• Ensure that no methods can
be overridden
o Easiest to make the class final
o Or use static factories & non-public
constructors
• Make all fields final
• Make all fields private
o Avoid even public immutable constants
• Ensure exclusive access to any mutable components
o Don’t leak internal references
o Defensive copying in and out
• Optionally provide equals and hashCode methods
• Optionally provide toString method
@Immutable...
• Java Immutable Class
• As per Joshua Bloch Effective Java
public final class Person {
private final String first;
private final String last;
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)
? 0 : first.hashCode());
result = prime * result + ((last == null)
? 0 : last.hashCode());
return result;
}
public Person(String first, String last) {
this.first = first;
this.last = last;
}
// ...
// ...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
@Override
public String toString() {
return "Person(first:" + first
+ ", last:" + last + ")";
}
}
...@Immutable...
public final class Person {
private final String first;
private final String last;
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)
? 0 : first.hashCode());
result = prime * result + ((last == null)
? 0 : last.hashCode());
return result;
}
public Person(String first, String last) {
this.first = first;
this.last = last;
}
// ...
// ...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
@Override
public String toString() {
return "Person(first:" + first
+ ", last:" + last + ")";
}
}
boilerplate• Java Immutable Class
• As per Joshua Bloch Effective Java
...@Immutable
@Immutable class Person {
String first, last
}
Awesome
With Macros (Groovy 2.5+)
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.stmt.*
import org.codehaus.groovy.ast.expr.*
def ast = new ReturnStatement(
new ConstructorCallExpression(
ClassHelper.make(Date),
ArgumentListExpression.EMPTY_ARGUMENTS
)
)
def ast = macro {
return new Date()
}
With Macros (Groovy 2.5+)
Awesome
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.stmt.*
import org.codehaus.groovy.ast.expr.*
def ast = new ReturnStatement(
new ConstructorCallExpression(
ClassHelper.make(Date),
ArgumentListExpression.EMPTY_ARGUMENTS
)
)
def ast = macro {
return new Date()
}
With Macros (Groovy 2.5+)
• Variations:
• Expressions, Statements, Classes
• Supports variable substitution,
specifying compilation phase
def ast = macro {
return new Date()
}
With Macros (Groovy 2.5+)
• AST Matching:
• Selective transformations, filtering, testing
• Supports placeholders
Expression transform(Expression exp) {
Expression ref = macro { 1 + 1 }
if (ASTMatcher.matches(ref, exp)) {
return macro { 2 }
}
return super.transform(exp)
}
Macro method examples
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
def getName(Z z) {
z.y.list[0].name
}
• nullSafe
Macro method examples
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
def getName(Z z) {
z.y.list[0].name
}
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
• nullSafe
Macro method examples
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
def getName(Z z) {
z.y.list[0].name
}
Prone to NPE
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
• nullSafe
Macro method examples
• nullSafe
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
def getName(Z z) {
def result = null
if (z != null && z.y != null &&
z.y.list != null && z.y.list[0] != null) {
result = z.y.list[0].name
}
result
}
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
Macro method examples
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
def getName(Z z) {
def result = null
if (z != null && z.y != null &&
z.y.list != null && z.y.list[0] != null) {
result = z.y.list[0].name
}
result
}
Verbose
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
• nullSafe
Macro method examples
def getName(Z z) {
z?.y?.list?[0]?.name
}
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
• nullSafe
class X {
String name
}
class Y {
List<X> list
}
class Z {
Y y
}
Macro method examples
Adapted from https://github.com/touchez-du-bois/akatsuki
but adapted for the experimental Antlr4 “Parrot” parser.
• nullSafe
def getName(Z z) {
nullSafe(z.y.list[0].name)
}
Macro method examples
@Macro
static Expression nullSafe(MacroContext macroContext, Expression expression) {
if (expression instanceof PropertyExpression) {
// exp.prop -> exp?.prop …
} else if (expression instanceof MethodCallExpression) {
// exp.method() -> exp?.method() …
}
return expression
}
• nullSafe
def getName(Z z) {
nullSafe(z.y.list[0].name)
}
Macro method examples
def fact(num) {
return match(num) {
when String then fact(num.toInteger())
when(0 | 1) then 1
when 2 then 2
orElse num * fact(num - 1)
}
}
assert fact("5") == 120
See: https://github.com/touchez-du-bois/akatsuki
Macro method examples
• Spock inspired
@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
import spock.lang.Specification
class MathSpec extends Specification {
def "maximum of two numbers"(int a, int b, int c) {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
Macro method examples
See: https://github.com/touchez-du-bois/akatsuki
doWithData {
dowith:
assert a + b == c
where:
a | b || c
1 | 2 || 3
4 | 5 || 9
7 | 8 || 15
}
What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Runtime
• Compile-time
• Macros
• Domain Specific Language support
• Ecosystem
• Community/Team
• Ability to change the language
at runtime
• Ability to change the language
during the compilation process
• Macros provide a
homogeneous form
for writing AST
transformations
Awesome
Part 2: What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain Specific Language support
 Command chains
• Cranes and tortoises revisited
• Ecosystem
• Community/Team
Command Chains
• Ability to chain method calls without parentheses and dots
Awesome
Command Chains
• Ability to chain method calls without parentheses and dots
move forward at 3.km/h
Command Chains
• Ability to chain method calls without parentheses and dots
• Equivalent to:
move forward at 3.km/h
move(forward).at(3.getKm().div(h))
Command chains in DSLs
Number.metaClass.getShares = { delegate }
Number.metaClass.getDollars = { delegate }
String GOOG = 'Google'
def sell(int nShares) {
[of: { String ticker ->
[at: { int price ->
println "Sold $nShares $ticker at $$price"
}]
}]
}
sell 100.shares of GOOG at 1000.dollars
Command chains in DSLs
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
}]
}
please show the square_root of 100
// ==> 10.0
Command chains in DSLs
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
}]
}
please(show).the(square_root).of(100)
// ==> 10.0
Command chains in DSLs
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
}]
}
please(show).the(square_root).of(100)
// ==> 10.0
… and again in another language …
Command chains in DSLs
// Japanese DSL
Object.metaClass.を =
Object.metaClass.の =
{ clos -> clos(delegate) }
まず = { it }
表示する = { println it }
平方根 = { Math.sqrt(it) }
まず 100 の 平方根 を 表示する
// First, show the square root of 100
// => 10.0 // source: http://d.hatena.ne.jp/uehaj/20100919/1284906117
// http://groovyconsole.appspot.com/edit/241001
Part 3: What makes Groovy Awesome?
• Java integration
• Scripting support
• Multiparadigm
• Gradual typing
• Metaprogramming
• Domain Specific Language support
• Command chains
 Cranes and tortoises revisited
• Ecosystem
• Community/Team
Logic programming example
cranes have 2 legs
tortoises have 4 legs
there are 7 animals
there are 20 legs
How many of each animal?
Recall: Constraint programming example
@Grab('org.choco-solver:choco-solver:4.0.0')
import org.chocosolver.solver.Model
def m = new Model()
def totalAnimals = 7
def totalLegs = 20
def numCranes = m.intVar('Cranes', 0, totalAnimals, true)
def numTortoises = m.intVar('Tortoises', 0, totalAnimals, true)
def numCraneLegs = m.intScaleView(numCranes, 2)
def numTortoiseLegs = m.intScaleView(numTortoises, 4)
m.arithm(numCranes, '+', numTortoises, '=', totalAnimals).post()
m.arithm(numCraneLegs, '+', numTortoiseLegs, '=', totalLegs).post()
if (m.solver.solve())
println "$numCranesn$numTortoises"
else
println "No Solutions" Cranes = 4
Tortoises = 3
ChocoCraneTortoise.groovy
Logic programming example
dslUntyped/ChocoCraneTortoiseDSL.groovy
50 lines to define the DSL
Logic programming example
cranes have 2 legs
tortoises have 4 legs
there are 7 animals
there are 20 legs
display solution
Cranes 4
Tortoises 3
dslUntyped/ChocoCraneTortoiseDSL.groovy
50 lines to define the DSL
Logic programming example
cranes have 2 legs
tortoises have 4 legs
millipedes have 1000 legs
there are 8 animals
there are 1020 legs
display solution
Cranes 4
Tortoises 3
Millipedes 1
dslUntyped/ChocoCraneTortoiseDSL.groovy
50 lines to define the DSL
Logic programming example
Crane(2), Tortoise(4), Beetle(6), Centipede(100), Millipede(1000)
Solution: Tortoise = 3, Beetle = 23
Solution: Crane = 1, Tortoise = 1, Beetle = 24
Solution: Crane = 25, Centipede = 1
dslTyped/ChocoCraneTortoiseDSL.groovy
80 lines to define the DSL
@TypeChecked
def main() {
animals seen include Crane, Tortoise, Beetle, Centipede
leg count is 150
head count is 26
display solution
}
@TypeChecked
def main() {
animals seen include Crane, Tortoise, Beetle, Centipede
leg count is 150
head count is 26
display solution
}
Logic programming example
dslTyped/ChocoCraneTortoiseDSL.groovy
80 lines to define the DSL
DSL Type Provider
unresolvedVariable { var ->
if (!cachedAnimalNames) {
def accessKey = '72ddf45a-c751-44c7-9bca-8db3b4513347'
// for illustrative purposes, just download xml for a few animals
def uid = 'ELEMENT_GLOBAL.2.104550,ELEMENT_GLOBAL.2.105196,ELEMENT_GLOBAL.2.120227'
def base = "https://services.natureserve.org/idd/rest/ns/v1.1/globalSpecies"
def url = "$base/comprehensive?uid=$uid&NSAccessKeyId=$accessKey"
def root = new XmlParser().parse(url)
def names = root.globalSpecies.classification.names
cachedAnimalNames = names.natureServePrimaryGlobalCommonName*.text()*.replaceAll(' ','')
}
if (var.name in cachedAnimalNames) {
storeType(var, STRING_TYPE)
handled = true
enclosingClassNode.addField(var.name, 0, STRING_TYPE, new ConstantExpression(var.name))
}
}
DSL Type Provider
@TypeChecked(extensions='NatureServeAnimalProvider.groovy')
def main() {
animals seen include SandhillCrane, GopherTortoise, ChihuahuanMillipede
leg count is 1020
head count is 8
display solution
}
Custom checker
provider/ChocoCraneTortoiseProvider.groovy
80 lines to define the DSL
25 lines to define the provider
Awesome
Awesome
Awesome
Awesome
Ecosystem
Groovy's Awesome Team
Groovy's Awesome Team
Groovy's Awesome Team
+100's of others
Direct downloads:
2013: approx. 3 million
2014: 4+ million
2015: 12+ million
now: 2+ million/month
Groovy's Awesome Team
+100's of others just like YOU!
http://www.manning.com/koenig2
Groovy in Action 2nd edition
Manning promo code:
ctwg3sum
(39% off all Manning books)
152

Mais conteúdo relacionado

Mais procurados

Exception Handling in JAVA
Exception Handling in JAVAException Handling in JAVA
Exception Handling in JAVASURIT DATTA
 
Presentation on-exception-handling
Presentation on-exception-handlingPresentation on-exception-handling
Presentation on-exception-handlingNahian Ahmed
 
Oop’s Concept and its Real Life Applications
Oop’s Concept and its Real Life ApplicationsOop’s Concept and its Real Life Applications
Oop’s Concept and its Real Life ApplicationsShar_1
 
JavaScript Programming
JavaScript ProgrammingJavaScript Programming
JavaScript ProgrammingSehwan Noh
 
Object-oriented programming
Object-oriented programmingObject-oriented programming
Object-oriented programmingNeelesh Shukla
 
Introduction to java
Introduction to java Introduction to java
Introduction to java Sandeep Rawat
 
Pass by value and pass by reference
Pass by value and pass by reference Pass by value and pass by reference
Pass by value and pass by reference TurnToTech
 
JavaScript - Chapter 8 - Objects
 JavaScript - Chapter 8 - Objects JavaScript - Chapter 8 - Objects
JavaScript - Chapter 8 - ObjectsWebStackAcademy
 
Database Connectivity in PHP
Database Connectivity in PHPDatabase Connectivity in PHP
Database Connectivity in PHPTaha Malampatti
 
C# lecture 2: Literals , Variables and Data Types in C#
C# lecture 2: Literals , Variables and Data Types in C#C# lecture 2: Literals , Variables and Data Types in C#
C# lecture 2: Literals , Variables and Data Types in C#Dr.Neeraj Kumar Pandey
 
Oops concepts || Object Oriented Programming Concepts in Java
Oops concepts || Object Oriented Programming Concepts in JavaOops concepts || Object Oriented Programming Concepts in Java
Oops concepts || Object Oriented Programming Concepts in JavaMadishetty Prathibha
 
Introduction to java (revised)
Introduction to java (revised)Introduction to java (revised)
Introduction to java (revised)Sujit Majety
 
Genesis and Overview of Java
Genesis and Overview of Java Genesis and Overview of Java
Genesis and Overview of Java Ravi_Kant_Sahu
 

Mais procurados (20)

Exception Handling in JAVA
Exception Handling in JAVAException Handling in JAVA
Exception Handling in JAVA
 
Presentation on-exception-handling
Presentation on-exception-handlingPresentation on-exception-handling
Presentation on-exception-handling
 
Oop’s Concept and its Real Life Applications
Oop’s Concept and its Real Life ApplicationsOop’s Concept and its Real Life Applications
Oop’s Concept and its Real Life Applications
 
Java program structure
Java program structureJava program structure
Java program structure
 
JavaScript Basic
JavaScript BasicJavaScript Basic
JavaScript Basic
 
07. Virtual Functions
07. Virtual Functions07. Virtual Functions
07. Virtual Functions
 
Arrays in Java
Arrays in JavaArrays in Java
Arrays in Java
 
JavaScript Programming
JavaScript ProgrammingJavaScript Programming
JavaScript Programming
 
Java Servlets
Java ServletsJava Servlets
Java Servlets
 
Object-oriented programming
Object-oriented programmingObject-oriented programming
Object-oriented programming
 
Introduction to java
Introduction to java Introduction to java
Introduction to java
 
Pass by value and pass by reference
Pass by value and pass by reference Pass by value and pass by reference
Pass by value and pass by reference
 
JavaScript - Chapter 8 - Objects
 JavaScript - Chapter 8 - Objects JavaScript - Chapter 8 - Objects
JavaScript - Chapter 8 - Objects
 
Database Connectivity in PHP
Database Connectivity in PHPDatabase Connectivity in PHP
Database Connectivity in PHP
 
C# lecture 2: Literals , Variables and Data Types in C#
C# lecture 2: Literals , Variables and Data Types in C#C# lecture 2: Literals , Variables and Data Types in C#
C# lecture 2: Literals , Variables and Data Types in C#
 
Getters_And_Setters.pptx
Getters_And_Setters.pptxGetters_And_Setters.pptx
Getters_And_Setters.pptx
 
Oops concepts || Object Oriented Programming Concepts in Java
Oops concepts || Object Oriented Programming Concepts in JavaOops concepts || Object Oriented Programming Concepts in Java
Oops concepts || Object Oriented Programming Concepts in Java
 
Methods in Java
Methods in JavaMethods in Java
Methods in Java
 
Introduction to java (revised)
Introduction to java (revised)Introduction to java (revised)
Introduction to java (revised)
 
Genesis and Overview of Java
Genesis and Overview of Java Genesis and Overview of Java
Genesis and Overview of Java
 

Destaque

Gr8conf - The Groovy Ecosystem Revisited
Gr8conf - The Groovy Ecosystem RevisitedGr8conf - The Groovy Ecosystem Revisited
Gr8conf - The Groovy Ecosystem RevisitedAndres Almiray
 
groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
groovy databases
groovy databasesgroovy databases
groovy databasesPaul King
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy TutorialPaul King
 
Going to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGoing to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGuillaume Laforge
 
Greach - The Groovy Ecosystem
Greach - The Groovy EcosystemGreach - The Groovy Ecosystem
Greach - The Groovy EcosystemAndres Almiray
 
Making the Most of Your Gradle Build
Making the Most of Your Gradle BuildMaking the Most of Your Gradle Build
Making the Most of Your Gradle BuildAndres Almiray
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing GroovyPaul King
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gparsPaul King
 
Practical Tips for Oracle Business Intelligence Applications 11g Implementations
Practical Tips for Oracle Business Intelligence Applications 11g ImplementationsPractical Tips for Oracle Business Intelligence Applications 11g Implementations
Practical Tips for Oracle Business Intelligence Applications 11g ImplementationsMichael Rainey
 
GoldenGate and ODI - A Perfect Match for Real-Time Data Warehousing
GoldenGate and ODI - A Perfect Match for Real-Time Data WarehousingGoldenGate and ODI - A Perfect Match for Real-Time Data Warehousing
GoldenGate and ODI - A Perfect Match for Real-Time Data WarehousingMichael Rainey
 
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...Michael Rainey
 
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration
A Walk Through the Kimball ETL Subsystems with Oracle Data IntegrationA Walk Through the Kimball ETL Subsystems with Oracle Data Integration
A Walk Through the Kimball ETL Subsystems with Oracle Data IntegrationMichael Rainey
 
Oracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data Streaming
Oracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data StreamingOracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data Streaming
Oracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data StreamingMichael Rainey
 
그루비 소개 발표자료 - 김연수
그루비 소개 발표자료 - 김연수그루비 소개 발표자료 - 김연수
그루비 소개 발표자료 - 김연수Yeon Soo Kim
 
GoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12c
GoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12cGoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12c
GoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12cMichael Rainey
 
Real-time Data Warehouse Upgrade – Success Stories
Real-time Data Warehouse Upgrade – Success StoriesReal-time Data Warehouse Upgrade – Success Stories
Real-time Data Warehouse Upgrade – Success StoriesMichael Rainey
 
GoldenGate and Oracle Data Integrator - A Perfect Match...
GoldenGate and Oracle Data Integrator - A Perfect Match...GoldenGate and Oracle Data Integrator - A Perfect Match...
GoldenGate and Oracle Data Integrator - A Perfect Match...Michael Rainey
 
Real-Time Data Replication to Hadoop using GoldenGate 12c Adaptors
Real-Time Data Replication to Hadoop using GoldenGate 12c AdaptorsReal-Time Data Replication to Hadoop using GoldenGate 12c Adaptors
Real-Time Data Replication to Hadoop using GoldenGate 12c AdaptorsMichael Rainey
 
Vert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVMVert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVMMassimiliano Dessì
 

Destaque (20)

Gr8conf - The Groovy Ecosystem Revisited
Gr8conf - The Groovy Ecosystem RevisitedGr8conf - The Groovy Ecosystem Revisited
Gr8conf - The Groovy Ecosystem Revisited
 
groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
groovy databases
groovy databasesgroovy databases
groovy databases
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy Tutorial
 
Going to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGoing to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific Languages
 
Greach - The Groovy Ecosystem
Greach - The Groovy EcosystemGreach - The Groovy Ecosystem
Greach - The Groovy Ecosystem
 
Making the Most of Your Gradle Build
Making the Most of Your Gradle BuildMaking the Most of Your Gradle Build
Making the Most of Your Gradle Build
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing Groovy
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
 
Practical Tips for Oracle Business Intelligence Applications 11g Implementations
Practical Tips for Oracle Business Intelligence Applications 11g ImplementationsPractical Tips for Oracle Business Intelligence Applications 11g Implementations
Practical Tips for Oracle Business Intelligence Applications 11g Implementations
 
GoldenGate and ODI - A Perfect Match for Real-Time Data Warehousing
GoldenGate and ODI - A Perfect Match for Real-Time Data WarehousingGoldenGate and ODI - A Perfect Match for Real-Time Data Warehousing
GoldenGate and ODI - A Perfect Match for Real-Time Data Warehousing
 
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration - Coll...
 
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration
A Walk Through the Kimball ETL Subsystems with Oracle Data IntegrationA Walk Through the Kimball ETL Subsystems with Oracle Data Integration
A Walk Through the Kimball ETL Subsystems with Oracle Data Integration
 
Oracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data Streaming
Oracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data StreamingOracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data Streaming
Oracle GoldenGate and Apache Kafka A Deep Dive Into Real-Time Data Streaming
 
그루비 소개 발표자료 - 김연수
그루비 소개 발표자료 - 김연수그루비 소개 발표자료 - 김연수
그루비 소개 발표자료 - 김연수
 
GoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12c
GoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12cGoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12c
GoldenGate and Oracle Data Integrator - A Perfect Match- Upgrade to 12c
 
Real-time Data Warehouse Upgrade – Success Stories
Real-time Data Warehouse Upgrade – Success StoriesReal-time Data Warehouse Upgrade – Success Stories
Real-time Data Warehouse Upgrade – Success Stories
 
GoldenGate and Oracle Data Integrator - A Perfect Match...
GoldenGate and Oracle Data Integrator - A Perfect Match...GoldenGate and Oracle Data Integrator - A Perfect Match...
GoldenGate and Oracle Data Integrator - A Perfect Match...
 
Real-Time Data Replication to Hadoop using GoldenGate 12c Adaptors
Real-Time Data Replication to Hadoop using GoldenGate 12c AdaptorsReal-Time Data Replication to Hadoop using GoldenGate 12c Adaptors
Real-Time Data Replication to Hadoop using GoldenGate 12c Adaptors
 
Vert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVMVert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVM
 

Semelhante a awesome groovy

groovy rules
groovy rulesgroovy rules
groovy rulesPaul King
 
2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Wsloffenauer
 
Building a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceBuilding a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceMaarten Balliauw
 
concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GParsPaul King
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle GroovyDeepak Bhagat
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVMAndres Almiray
 
The Great Scala Makeover
The Great Scala MakeoverThe Great Scala Makeover
The Great Scala MakeoverGarth Gilmour
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Jonathan Felch
 
Top 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdetTop 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdetDevLabs Alliance
 
Writing Groovy DSLs
Writing Groovy DSLsWriting Groovy DSLs
Writing Groovy DSLsadam1davis
 
Polyglot Programming @ Jax.de 2010
Polyglot Programming @ Jax.de 2010Polyglot Programming @ Jax.de 2010
Polyglot Programming @ Jax.de 2010Andres Almiray
 
Introduction To Scala
Introduction To ScalaIntroduction To Scala
Introduction To ScalaPeter Maas
 
The Curious Clojurist - Neal Ford (Thoughtworks)
The Curious Clojurist - Neal Ford (Thoughtworks)The Curious Clojurist - Neal Ford (Thoughtworks)
The Curious Clojurist - Neal Ford (Thoughtworks)jaxLondonConference
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005Tugdual Grall
 
Groovy for Java Developers
Groovy for Java DevelopersGroovy for Java Developers
Groovy for Java DevelopersAndres Almiray
 
Groovy
GroovyGroovy
Groovyatonse
 
Apache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystemApache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystemKostas Saidis
 

Semelhante a awesome groovy (20)

groovy rules
groovy rulesgroovy rules
groovy rules
 
Groovy
GroovyGroovy
Groovy
 
Einführung in TypeScript
Einführung in TypeScriptEinführung in TypeScript
Einführung in TypeScript
 
2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws2007 09 10 Fzi Training Groovy Grails V Ws
2007 09 10 Fzi Training Groovy Grails V Ws
 
Building a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceBuilding a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to Space
 
concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GPars
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle Groovy
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVM
 
The Great Scala Makeover
The Great Scala MakeoverThe Great Scala Makeover
The Great Scala Makeover
 
TDD with PhpSpec
TDD with PhpSpecTDD with PhpSpec
TDD with PhpSpec
 
Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)Groovy On Trading Desk (2010)
Groovy On Trading Desk (2010)
 
Top 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdetTop 20 java programming interview questions for sdet
Top 20 java programming interview questions for sdet
 
Writing Groovy DSLs
Writing Groovy DSLsWriting Groovy DSLs
Writing Groovy DSLs
 
Polyglot Programming @ Jax.de 2010
Polyglot Programming @ Jax.de 2010Polyglot Programming @ Jax.de 2010
Polyglot Programming @ Jax.de 2010
 
Introduction To Scala
Introduction To ScalaIntroduction To Scala
Introduction To Scala
 
The Curious Clojurist - Neal Ford (Thoughtworks)
The Curious Clojurist - Neal Ford (Thoughtworks)The Curious Clojurist - Neal Ford (Thoughtworks)
The Curious Clojurist - Neal Ford (Thoughtworks)
 
Introduction To Groovy 2005
Introduction To Groovy 2005Introduction To Groovy 2005
Introduction To Groovy 2005
 
Groovy for Java Developers
Groovy for Java DevelopersGroovy for Java Developers
Groovy for Java Developers
 
Groovy
GroovyGroovy
Groovy
 
Apache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystemApache Groovy: the language and the ecosystem
Apache Groovy: the language and the ecosystem
 

Mais de Paul King

tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovyPaul King
 
functional groovy
functional groovyfunctional groovy
functional groovyPaul King
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing GroovyPaul King
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing PracticesPaul King
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language PracticesPaul King
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More GroovyPaul King
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power FeaturesPaul King
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009Paul King
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...Paul King
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Paul King
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with GroovyPaul King
 

Mais de Paul King (15)

tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovy
 
functional groovy
functional groovyfunctional groovy
functional groovy
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing Practices
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language Practices
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More Groovy
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power Features
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with Groovy
 

Último

Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdfAndrey Devyatkin
 
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesAmazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesKrzysztofKkol1
 
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4jGraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4jNeo4j
 
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonApplitools
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxAndreas Kunz
 
Keeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository worldKeeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository worldRoberto Pérez Alcolea
 
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...OnePlan Solutions
 
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingShane Coughlan
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...Bert Jan Schrijver
 
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdfPros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdfkalichargn70th171
 
Data modeling 101 - Basics - Software Domain
Data modeling 101 - Basics - Software DomainData modeling 101 - Basics - Software Domain
Data modeling 101 - Basics - Software DomainAbdul Ahad
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfRTS corp
 
Zer0con 2024 final share short version.pdf
Zer0con 2024 final share short version.pdfZer0con 2024 final share short version.pdf
Zer0con 2024 final share short version.pdfmaor17
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?Alexandre Beguel
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLionel Briand
 
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...OnePlan Solutions
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingShane Coughlan
 
Ronisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited CatalogueRonisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited Catalogueitservices996
 

Último (20)

Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
2024-04-09 - From Complexity to Clarity - AWS Summit AMS.pdf
 
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilitiesAmazon Bedrock in Action - presentation of the Bedrock's capabilities
Amazon Bedrock in Action - presentation of the Bedrock's capabilities
 
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4jGraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
 
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
 
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptxUI5ers live - Custom Controls wrapping 3rd-party libs.pptx
UI5ers live - Custom Controls wrapping 3rd-party libs.pptx
 
Keeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository worldKeeping your build tool updated in a multi repository world
Keeping your build tool updated in a multi repository world
 
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
 
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
 
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
JavaLand 2024 - Going serverless with Quarkus GraalVM native images and AWS L...
 
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdfPros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
Pros and Cons of Selenium In Automation Testing_ A Comprehensive Assessment.pdf
 
Data modeling 101 - Basics - Software Domain
Data modeling 101 - Basics - Software DomainData modeling 101 - Basics - Software Domain
Data modeling 101 - Basics - Software Domain
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
 
Zer0con 2024 final share short version.pdf
Zer0con 2024 final share short version.pdfZer0con 2024 final share short version.pdf
Zer0con 2024 final share short version.pdf
 
SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?SAM Training Session - How to use EXCEL ?
SAM Training Session - How to use EXCEL ?
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and Repair
 
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
Revolutionizing the Digital Transformation Office - Leveraging OnePlan’s AI a...
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
 
Ronisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited CatalogueRonisha Informatics Private Limited Catalogue
Ronisha Informatics Private Limited Catalogue
 

awesome groovy

  • 1. Dr Paul King Groovy Lead for Object Computing Inc. @paulk_asert http:/slideshare.net/paulk_asert/awesome-groovy https://github.com/paulk-asert/awesome-groovy Apache Groovy: The Awesome Parts FRIEND OF GROOVY
  • 2.
  • 4. What is Groovy? 4 Groovy = Java – boiler plate code + closures (1st class FP) + extensible type system + runtime & compile-time metaprogramming + flexible language grammar (DSLs) + scripting + GDK library “Groovy is like a super version of Java. It leverages Java features but adds productivity features and provides great flexibility and extensibility.”
  • 5.
  • 7. Why Groovy? Too verbose Support functional programming Better concurrency Evolves too slowly I need feature XXX Too complex for some situations More flexibility Java
  • 8. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting
  • 9. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Java integration
  • 10. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Good IDE support Java integration
  • 11. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Good IDE support Custom features Java integration
  • 12. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Support concurrency Good IDE support Custom features Java integration
  • 13. Why Groovy? Groovy Make it simplerMake it dynamic Support simple scripting Support functional style Support concurrency Good IDE support Custom features Java integration
  • 14.
  • 15. Java code for list manipulation import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main m = new Main(); List shortNames = m.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } }
  • 16. Groovy code for list manipulation import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main m = new Main(); List shortNames = m.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Rename Main.java to Main.groovy
  • 17. Some Java Boilerplate identified import java.util.List; import java.util.ArrayList; class Main { private List keepShorterThan(List strings, int length) { List result = new ArrayList(); for (int i = 0; i < strings.size(); i++) { String s = (String) strings.get(i); if (s.length() < length) { result.add(s); } } return result; } public static void main(String[] args) { List names = new ArrayList(); names.add("Ted"); names.add("Fred"); names.add("Jed"); names.add("Ned"); System.out.println(names); Main m = new Main(); List shortNames = m.keepShorterThan(names, 4); System.out.println(shortNames.size()); for (int i = 0; i < shortNames.size(); i++) { String s = (String) shortNames.get(i); System.out.println(s); } } } Are the semicolons needed? And shouldn’t we us more modern list notation? Why not import common libraries? Do we need the static types? Must we always have a main method and class definition? How about improved consistency?
  • 18. Java Boilerplate removed def keepShorterThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() < length) { result.add(s) } } return result } names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) shortNames = keepShorterThan(names, 4) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) }
  • 19. More Java Boilerplate identified def keepShorterThan(strings, length) { def result = new ArrayList() for (s in strings) { if (s.size() < length) { result.add(s) } } return result } names = new ArrayList() names.add("Ted"); names.add("Fred") names.add("Jed"); names.add("Ned") System.out.println(names) shortNames = keepShorterThan(names, 4) System.out.println(shortNames.size()) for (s in shortNames) { System.out.println(s) } Shouldn’t we have special notation for lists? And special facilities for list processing? Is ‘return’ needed at end? Is the method now needed? Simplify common methods? Remove unambiguous brackets?
  • 20. Boilerplate removed = nicer Groovy version names = ["Ted", "Fred", "Jed", "Ned"] println names shortNames = names.findAll{ it.size() < 4 } println shortNames.size() shortNames.each{ println it } ["Ted", "Fred", "Jed", "Ned"] 3 Ted Jed Ned Output:
  • 21. Or Groovy DSL version if required given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 // plus a DSL implementation
  • 22. Or Groovy DSL version if required given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 names = [] def of, having, less def given(_the) { [names:{ Object[] ns -> names.addAll(ns) [and: { n -> names += n }] }] } def the = [ number: { _of -> [names: { _having -> [size: { _less -> [than: { size -> println names.findAll{ it.size() < size }.size() }]}] }] }, names: { _having -> [size: { _less -> [than: { size -> names.findAll{ it.size() < size }.each{ println it } }]}] } ] def all = [the: { println it }] def display(arg) { arg }
  • 23. Or Groovy DSL version if required • Or use GDSL (IntelliJ IDEA) or DSLD (Eclipse) given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4
  • 24. Or typed Groovy DSL version if required given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 … enum The { the } enum Having { having } enum Of { of } … class DisplayThe { DisplayTheNamesHaving names(Having having) { new DisplayTheNamesHaving() } DisplayTheNumberOf number(Of of) { new DisplayTheNumberOf() } } … // plus 50 lines
  • 25. Or typed Groovy DSL version if required
  • 26. Groovy DSL being debugged
  • 27. Or typed Groovy DSL version if required @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 }
  • 28. Or typed Groovy DSL version if required @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 } afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } } 
  • 29. @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", "Fred", "Jed" and "Ned" display all the names display the number of names having size less than 4 display the names having size less than 4 } Or typed Groovy DSL version if required afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } }
  • 30. @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", “Mary", "Jed" and “Pete" display all the names display the number of names having size less than 4 display the names having size less than 4 } afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } } Or typed Groovy DSL version if required
  • 31. @TypeChecked(extensions='EdChecker.groovy') def method() { given the names "Ted", “Mary", "Jed" and “Pete" display all the names display the number of names having size less than 4 display the names having size less than 4 } afterMethodCall { mc -> mc.arguments.each { if (isConstantExpression(it)) { if (it.value instanceof String && !it.value.endsWith('ed')) { addStaticTypeError("I don't like the name '${it.value}'", mc) } } } } Or typed Groovy DSL version if required 
  • 32. What style of language is Groovy?
  • 33. Groovy Style • Imperative/OO but somewhat paradigm agnostic •Dynamic, optionally static (gradual extensible typing) • Extensible language features through metaprogramming
  • 34. Part 2: What makes Groovy Awesome?
  • 35. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain specific language (DSL) support • Ecosystem • Community/Team Awesome
  • 36. What makes Groovy Awesome? • Java integration • Builds up on Java • Tight integration • Polyglot friendly • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
  • 37. Java Integration • Groovy is Java’s friend Java Groovy
  • 38. Java Integration • Standing on the shoulders of Giants • Some limitations inherited but much gained through new releases of Java • Rock solid foundation • Can ease migration to new versions of Java Java Groovy
  • 39. Java Integration • Seamless integration • IDEs provide cross-language compile, navigation, and refactoring • Arbitrarily mix source language • Drop-in replace any class • Overloaded methods • Syntax alignment • Shared data types Java Groovy
  • 40. Java Integration • Seamless integration • IDEs provide cross-language compile, navigation, and refactoring • Arbitrarily mix source language • Drop-in replace any class • Overloaded methods • Syntax alignment • Shared data types Java Groovy
  • 41. Java Integration • Polyglot friendly • Typically integrates well with other languages which integrate with Java • JRuby • Jython • Scala • Frege • Clojure • R through Renjin • JavaScript (Rhino/Nashorn)
  • 42. Java Integration • Polyglot friendly • JSR-223 scripting to talk to JavaScript import javax.script.ScriptEngineManager def mgr = new ScriptEngineManager() def engine = mgr.getEngineByName('nashorn') assert engine.eval(''' function factorial(n) { if (n == 0) { return 1; } return n * factorial(n - 1); } factorial(4) ''') == 24.0 Java GroovyJavaScript
  • 43. Java Integration • Polyglot friendly: R integration @GrabResolver('https://nexus.bedatadriven.com/content/groups/public') @Grab('org.renjin:renjin-script-engine:0.7.0-RC7') import javax.script.ScriptEngineManager def mgr = new ScriptEngineManager() def engine = mgr.getEngineByName('Renjin') engine.with { eval ''' factorial <- function(x) { y <- 1 for (i in 1:x) { y <- y * i } return(y) } ''' assert eval('factorial(4)')[0] == 24 } Java GroovyR
  • 44. Grooscript @Grab('org.grooscript:grooscript:1.2.0') import org.grooscript.GrooScript import javax.script.ScriptEngineManager def groojs = GrooScript.convert ''' def sayHello = { println "Hello ${it}!" } ['Groovy','JavaScript','GrooScript'].each sayHello''', [initialText: 'var console = { log: print }', addGsLib: 'grooscript.min'] new ScriptEngineManager().getEngineByName("nashorn").eval(groojs) Hello Groovy! Hello JavaScript! Hello GrooScript! Jorge Franco, grooscript.org Convert Groovy subset to JavaScript Support: Gradle, Grails, NPM Demos: react.js, node.js, Pollock Converts: Classes, closures, Date primitives, ranges, String/GString Special: builder, templates, gQuery
  • 45. What makes Groovy Awesome? • Java integration • Builds up on Java • Tight integration • Polyglot friendly • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team Awesome • Friend of Java • Integration • Good JVM Citizen
  • 46. What makes Groovy Awesome? • Java integration • Scripting support • Simplified scripting • Separation of concerns • Security sandbox • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team Awesome
  • 47. Scripting • Terse format • Isolation • Domain-Specific Languages (DSLs) • Security • Flexibility
  • 48. Scripting • Leave out surrounding class • Entire source file: public class Script { public static void main(String[] args) { System.out.println("Hello world"); } } println "Hello world"
  • 49. Scripting • Various approaches supported • GroovyShell • GroovyScriptEngine • GroovyClassLoader • Spring • JSR-223 • Base scripts • allows predefined methods • Binding • allows predefined variables • Customizers • add imports, static imports • lock down AST to a subset • apply any AST transform transparently
  • 53. Scripting class Robot { void move(dir) { println "robot moved $dir" } } def robot = new Robot()
  • 54. Scripting import static Direction.* enum Direction { left, right, forward, backward } class Robot { void move(Direction dir) { println "robot moved $dir" } } def robot = new Robot() robot.move left
  • 55. Scripting move left def robot = new Robot() def binding = new Binding( robot: robot, move: robot.&move, *: Direction.values().collectEntries { [(it.name()): it] } ) def shell = new GroovyShell(binding) shell.evaluate new File(args[0]).text
  • 56. Scripting move left def robot = new Robot() def binding = new Binding(/* ... */) def imports = new ImportCustomizer().addStaticStars('java.lang.Math') def timeout = new ASTTransformationCustomizer(value: 2, TimedInterrupt) def secure = new SecureASTCustomizer().with { closuresAllowed = false importsWhitelist = [] tokensWhitelist = [ PLUS, MINUS, MULTIPLY, DIVIDE, /*...*/ ] //... } def sysExit = new CompilationCustomizer(CANONICALIZATION) { void call(SourceUnit src, GeneratorContext ctxt, ClassNode cn) { new ClassCodeVisitorSupport() { void visitMethodCallExpression(MethodCallExpression call) { // check for System.exit() } // ... }.visitClass(cn) } } def config = new CompilerConfiguration() config.addCompilationCustomizers(imports, timeout, secure, sysExit) def shell = new GroovyShell(binding, config)
  • 57. Scripting move left move forward at 3.km/h More examples later
  • 58. What makes Groovy Awesome? • Java integration • Scripting support • Simplified scripting • Separation of concerns • Security sandbox • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team Awesome • Simpler scripting • Separation of concerns
  • 59. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Object-oriented • Functional, Closures • Logic/Dataflow programming • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
  • 60. Multiparadigm • Imperative roots from Java • OO abstractions: classes, interfaces, inheritance • Properties • Traits • First class functional support via closures • External libraries for advanced features • Other paradigms via libraries • Logic, dataflow, reactive Awesome
  • 62. Rich OO features • Classes (and scripts, GroovyBeans) • Fields (and properties) • Methods, constructors, multi-methods, named and default parameters • Inheritance (behavior and implementation), interfaces, traits • Type aliases • Packages • Special accessor notation, spread operators, GPath, safe navigation
  • 63. Traits /** * Things that hop * @author Grace */ trait Hopper { String jump() { "I'm jumping!" } } class Kangaroo implements Hopper {} def skip = new Kangaroo() assert skip.jump() == "I'm jumping!"
  • 64. Rich functional features • Closures • First-class functions • Higher-order functions • Map, reduce, filter • Mutable & immutable data • Recursion • Lazy & eager evaluation • Advanced FP techniques • Memoization, Trampolines, Composition, Curry • Concurrency
  • 65. map/filter/reduce @Canonical class Person { String name int age } def people = [ new Person('Peter', 45), new Person('Paul', 35), new Person('Mary', 25) ] assert people .findAll{ it.age < 40 } .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL'
  • 66. map/filter/reduce (+ functional style) @Canonical class Person { String name int age } def people = [ new Person('Peter', 45), new Person('Paul', 35), new Person('Mary', 25) ] assert people .findAll{ it.age < 40 } .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL' def young = { person, threshold -> person.age < threshold }.rcurry(40).memoize() assert people .findAll(young) .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL'
  • 67. map/filter/reduce (+ with streams) @Canonical class Person { String name int age } def people = [ new Person('Peter', 45), new Person('Paul', 35), new Person('Mary', 25) ] assert people .findAll{ it.age < 40 } .collect{ it.name.toUpperCase() } .sort() .join(', ') == 'MARY, PAUL' // requires JRE 8 def commaSep = Collectors.joining(", ") assert people.stream() .filter{ it.age < 40 } .map{ it.name.toUpperCase() } .sorted() .collect(commaSep) == 'MARY, PAUL'
  • 68. Logic/Constraint programming • Logic programming • Declarative style • Logic clauses for example Prolog • Constraint programming • Declarative style similar to logic programming but contain constraints which must be satisfied • Relations between variables are stated as constraints • Not a step or sequence of steps to execute, but rather the properties of a solution to be found
  • 69. Logic programming example cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs How many of each animal?
  • 70. Constraint programming example @Grab('org.choco-solver:choco-solver:4.0.4') import org.chocosolver.solver.Model def m = new Model() def totalAnimals = 7 def totalLegs = 20 def numCranes = m.intVar('Cranes', 0, totalAnimals, true) def numTortoises = m.intVar('Tortoises', 0, totalAnimals, true) def numCraneLegs = m.intScaleView(numCranes, 2) def numTortoiseLegs = m.intScaleView(numTortoises, 4) m.arithm(numCranes, '+', numTortoises, '=', totalAnimals).post() m.arithm(numCraneLegs, '+', numTortoiseLegs, '=', totalLegs).post() if (m.solver.solve()) println "$numCranesn$numTortoises" else println "No Solutions" Cranes = 4 Tortoises = 3 ChocoCraneTortoise.groovy See also: JSR-331 Constraint Programming API
  • 71. Dataflow programming • Declarative style • Emphasizes the movement of data • models programs as a series of connected tasks • A task has explicitly defined inputs and outputs • runs as soon as all of its inputs become available • Inherently parallel
  • 72. GPars • Library classes and DSL allowing you to handle tasks concurrently: • Data Parallelism map, filter, reduce functionality in parallel with parallel array support • Asynchronous functions extend the Java executor services to enable multi-threaded closure processing • Dataflow Concurrency supports natural shared-memory concurrency model, using single-assignment variables • Actors provide Erlang/Scala-like actors including "remote" actors on other machines • Safe Agents provide a non-blocking mt-safe reference to mutable state; like "agents" in Clojure 72 Awesome
  • 73. Concurrency challenge • We can analyse the example’s task graph: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10
  • 74. Concurrency challenge • We can analyse the example’s task graph: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10 f2 f3 f1 f4 aa b c d f
  • 75. Concurrency challenge • Manually using asynchronous functions: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import static groovyx.gpars.GParsPool.withPool withPool(2) { def a = 5 def futureB = f1.callAsync(a) def c = f2(a) def d = f3(c) def f = f4(futureB.get(), d) assert f == 10 } f2 f3 f1 f4 aa futureB c d f
  • 76. Concurrency challenge • And with GPars Dataflows: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { a = 5 } task { b = f1(a) } task { c = f2(a) } task { d = f3(c) } task { f = f4(b, d) } assert f == 10 } f2 f3 f1 f4 aa b c d f
  • 77. Concurrency challenge • And with GPars Dataflows: def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { f = f4(b, d) } task { d = f3(c) } task { c = f2(a) } task { b = f1(a) } task { a = 5 } assert f == 10 } f2 f3 f1 f4 aa b c d f
  • 78. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Object-oriented • Functional, Closures • Logic/dataflow programming • Gradual typing • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team • Ability to use imperative when needed for speed • Numerous declarative approaches available when it makes sense Awesome
  • 79. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Dynamic strong typing • Static type checking • Extensible • Type inference • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team
  • 80. Gradual typing • Dynamic by default • Gradual typing • Static type checking • Extensible type system Awesome
  • 81. Dynamic vs static 82 def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() }
  • 82. Dynamic vs static 83 Gradual Type-time? def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() }
  • 83. Dynamic vs static 84 def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY'
  • 84. Dynamic vs static 85 def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } Duck-typing
  • 85. Dynamic vs static 86 def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } @TypeChecked def myMethod() { def myPets = ['Lassie', 'Skip'] List yourPets = ['Lassie', 'Skip'] } shouldFail(CompilationFailedException) { assertScript ''' @groovy.transform.TypeChecked def yourMethod() { List ourPets = new Date() } ''' }
  • 86. Dynamic vs static 87 def adder = { a, b -> a + b } assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' def myPets = ['Lassie', 'Skip'] List<String> yourPets = ['Lassie', 'Skip'] shouldFail(ClassCastException) { List ourPets = new Date() } @TypeChecked def myMethod() { def myPets = ['Lassie', 'Skip'] List yourPets = ['Lassie', 'Skip'] } shouldFail(CompilationFailedException) { assertScript ''' @groovy.transform.TypeChecked def yourMethod() { List ourPets = new Date() } ''' } Extensible Inference
  • 87. Extensible type checking import groovy.transform.TypeChecked import experimental.SprintfTypeChecker @TypeChecked(extensions=SprintfTypeChecker) void main() { sprintf('%s will turn %d on %tF', 'John', new Date(), 21) } [Static type checking] - Parameter types didn't match types expected from the format String: For placeholder 2 [%d] expected 'int' but was 'java.util.Date' For placeholder 3 [%tF] expected 'java.util.Date' but was 'int' sprintf has an Object varargs parameter, hence not normally amenable to further static checking but for constant Strings we can do better using a custom type checking plugin.
  • 88. Extensible type checking import groovy.sql.Sql import groovy.transform.TypeChecked @TypeChecked(extensions='SQLExtension.groovy') findAthletes(Sql sql) { sql.eachRow('select * frm Athlete') { row -> println row } } SqlTC.groovy: 7: [Static type checking] - SQL query is not valid: net.sf.jsqlparser.JSQLParserException @ line 6, column 15. sql.eachRow('select * frm Athlete') { row -> println row } ^ 1 error
  • 89. Static compilation import groovy.transform.* class Actor { String firstName, lastName @CompileStatic String getFullName() { "$firstName $lastName" } void makePeace() { new AntBuilder().echo('Peace was never an option') } } def magneto = new Actor(firstName: 'Ian', lastName: 'McKellen') assert magneto.fullName == 'Ian McKellen' magneto.makePeace()
  • 91. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Dynamic strong typing • Static type checking • Extensible • Type inference • Metaprogramming • Domain Specific Language support • Ecosystem • Community/Team • Maximise duck-typing • Minimise noise • yet type inference • Flexibility at runtime • As strict as you want when needed • Fast when needed Awesome
  • 92. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Runtime • Compile-time • Macros • Domain Specific Language support • Ecosystem • Community/Team
  • 93. Runtime metaprogramming • Add instance & static methods, constructors, properties at runtime • Intercept method/property access • Catch missing methods, properties • Used for dynamic builders, aspect-oriented programming, test stubs, mocks & dummies Awesome* * Individual levels of awesomeness may vary
  • 94. Runtime metaprogramming • Adding methods at runtime assert 'Hello'.reverse() == 'olleH' String.metaClass.swapCase = { delegate.collect{ it in 'A'..'Z' ? it.toLowerCase() : it.toUpperCase() }.join() } assert 'Hello'.swapCase() == 'hELLO'
  • 95. Runtime metaprogramming • Intercepting methods class Foo { def one() { println "Called one()" } def methodMissing(String name, params) { println "Attempted $name($params)" } } def f = new Foo() f.one() f.two() f.three('Some Arg') Called one() Attempted two([]) Attempted three([Some Arg])
  • 96. XML Parsing/GPath expressions def xml = ''' <hosts> <host name='MyHost'> <service name='MyMicroService'/> <service name='MyNanoService'/> </host> </hosts> ''' def hosts = new XmlParser().parseText(xml) assert hosts.host.service[0].@name=='MyMicroService'
  • 97. def xml = ''' <hosts> <host name='MyHost'> <service name='MyMicroService'/> <service name='MyNanoService'/> </host> </hosts> ''' def hosts = new XmlParser().parseText(xml) assert hosts.host.service[0].@name=='MyMicroService' XML Parsing/GPath expressions
  • 98. Compile-time metaprogramming • Gives you the ability to change the language by augmenting the compilation process Awesome
  • 99. Compile-time metaprogramming • Modify the program at compile-time @ToString class Person { String first, last } println new Person(first: 'John', last: 'Smith') // => Person(John, Smith)
  • 100. Compile-time metaprogramming • Modify the program at compile-time class Person { String first, last String toString() { "Person($first, $last)" } } println new Person(first: 'John', last: 'Smith') // => Person(John, Smith)
  • 101. Parsing Summary public run() ... L1 ALOAD 1 LDC 1 AALOAD ALOAD 0 LDC "Howdy Y'all" INVOKEINTERFACE callCurrent() ARETURN ... println "Howdy Y'all" BlockStatement -> ReturnStatement -> MethodCallExpression -> VariableExpression("this") -> ConstantExpression("println") -> ArgumentListExpression -> ConstantExpression("Howdy Y'all") MyScript.groovy > groovy MyScript.groovy > groovyc MyScript.groovy > groovysh > groovyConsole
  • 102. • 9 phase compiler – Early stages: read source code and convert into a sparse syntax tree – Middle stages: iteratively build up a more dense and information rich version of the syntax tree – Later stages: check the tree and convert it into byte code/class files Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization Parsing Summary
  • 103. Parsing - Early Stages Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization @ToString class Greeter { String message = "Howdy Y'all" void greet() { println message } } ClassNode: Greeter MethodNode: greet Property: message type: unresolved(String) AnnotationNode: ToString type: unresolved(ToString) methods: properties: annotations: BlockStatement MethodCall: this.println(message)
  • 104. Parsing - Middle Stages Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization ClassNode: Greeter MethodNode: greet FieldNode: message type: resolved(String) methods: fields: constructors: ConstructorNode MethodNode: getMessageMethodNode: setMessageMethodNode: toString MethodNode: getMetaClass…
  • 105. Parsing - Final Stages 106 Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization public greet()V ... L1 ... ALOAD 0 GETFIELD Greeter.message INVOKEINTERFACE callCurrent() POP ...
  • 106. Immutable Classes • Some Rules • Don’t provide mutators • Ensure that no methods can be overridden o Easiest to make the class final o Or use static factories & non-public constructors • Make all fields final • Make all fields private o Avoid even public immutable constants • Ensure exclusive access to any mutable components o Don’t leak internal references o Defensive copying in and out • Optionally provide equals and hashCode methods • Optionally provide toString method
  • 107. @Immutable... • Java Immutable Class • As per Joshua Bloch Effective Java public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }
  • 108. ...@Immutable... public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } } boilerplate• Java Immutable Class • As per Joshua Bloch Effective Java
  • 109. ...@Immutable @Immutable class Person { String first, last } Awesome
  • 110. With Macros (Groovy 2.5+) import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.stmt.* import org.codehaus.groovy.ast.expr.* def ast = new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(Date), ArgumentListExpression.EMPTY_ARGUMENTS ) ) def ast = macro { return new Date() }
  • 111. With Macros (Groovy 2.5+) Awesome import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.stmt.* import org.codehaus.groovy.ast.expr.* def ast = new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(Date), ArgumentListExpression.EMPTY_ARGUMENTS ) ) def ast = macro { return new Date() }
  • 112. With Macros (Groovy 2.5+) • Variations: • Expressions, Statements, Classes • Supports variable substitution, specifying compilation phase def ast = macro { return new Date() }
  • 113. With Macros (Groovy 2.5+) • AST Matching: • Selective transformations, filtering, testing • Supports placeholders Expression transform(Expression exp) { Expression ref = macro { 1 + 1 } if (ASTMatcher.matches(ref, exp)) { return macro { 2 } } return super.transform(exp) }
  • 114. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. def getName(Z z) { z.y.list[0].name } • nullSafe
  • 115. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { z.y.list[0].name } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  • 116. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { z.y.list[0].name } Prone to NPE Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  • 117. Macro method examples • nullSafe class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { def result = null if (z != null && z.y != null && z.y.list != null && z.y.list[0] != null) { result = z.y.list[0].name } result } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser.
  • 118. Macro method examples class X { String name } class Y { List<X> list } class Z { Y y } def getName(Z z) { def result = null if (z != null && z.y != null && z.y.list != null && z.y.list[0] != null) { result = z.y.list[0].name } result } Verbose Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  • 119. Macro method examples def getName(Z z) { z?.y?.list?[0]?.name } class X { String name } class Y { List<X> list } class Z { Y y } Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe
  • 120. class X { String name } class Y { List<X> list } class Z { Y y } Macro method examples Adapted from https://github.com/touchez-du-bois/akatsuki but adapted for the experimental Antlr4 “Parrot” parser. • nullSafe def getName(Z z) { nullSafe(z.y.list[0].name) }
  • 121. Macro method examples @Macro static Expression nullSafe(MacroContext macroContext, Expression expression) { if (expression instanceof PropertyExpression) { // exp.prop -> exp?.prop … } else if (expression instanceof MethodCallExpression) { // exp.method() -> exp?.method() … } return expression } • nullSafe def getName(Z z) { nullSafe(z.y.list[0].name) }
  • 122. Macro method examples def fact(num) { return match(num) { when String then fact(num.toInteger()) when(0 | 1) then 1 when 2 then 2 orElse num * fact(num - 1) } } assert fact("5") == 120 See: https://github.com/touchez-du-bois/akatsuki
  • 123. Macro method examples • Spock inspired @Grab('org.spockframework:spock-core:1.0-groovy-2.4') import spock.lang.Specification class MathSpec extends Specification { def "maximum of two numbers"(int a, int b, int c) { expect: Math.max(a, b) == c where: a | b | c 1 | 3 | 3 7 | 4 | 7 0 | 0 | 0 } }
  • 124. Macro method examples See: https://github.com/touchez-du-bois/akatsuki doWithData { dowith: assert a + b == c where: a | b || c 1 | 2 || 3 4 | 5 || 9 7 | 8 || 15 }
  • 125. What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Runtime • Compile-time • Macros • Domain Specific Language support • Ecosystem • Community/Team • Ability to change the language at runtime • Ability to change the language during the compilation process • Macros provide a homogeneous form for writing AST transformations Awesome
  • 126. Part 2: What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support  Command chains • Cranes and tortoises revisited • Ecosystem • Community/Team
  • 127. Command Chains • Ability to chain method calls without parentheses and dots Awesome
  • 128. Command Chains • Ability to chain method calls without parentheses and dots move forward at 3.km/h
  • 129. Command Chains • Ability to chain method calls without parentheses and dots • Equivalent to: move forward at 3.km/h move(forward).at(3.getKm().div(h))
  • 130. Command chains in DSLs Number.metaClass.getShares = { delegate } Number.metaClass.getDollars = { delegate } String GOOG = 'Google' def sell(int nShares) { [of: { String ticker -> [at: { int price -> println "Sold $nShares $ticker at $$price" }] }] } sell 100.shares of GOOG at 1000.dollars
  • 131. Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please show the square_root of 100 // ==> 10.0
  • 132. Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please(show).the(square_root).of(100) // ==> 10.0
  • 133. Command chains in DSLs show = { println it } square_root = { Math.sqrt(it) } def please(action) { [the: { what -> [of: { n -> action(what(n)) }] }] } please(show).the(square_root).of(100) // ==> 10.0 … and again in another language …
  • 134. Command chains in DSLs // Japanese DSL Object.metaClass.を = Object.metaClass.の = { clos -> clos(delegate) } まず = { it } 表示する = { println it } 平方根 = { Math.sqrt(it) } まず 100 の 平方根 を 表示する // First, show the square root of 100 // => 10.0 // source: http://d.hatena.ne.jp/uehaj/20100919/1284906117 // http://groovyconsole.appspot.com/edit/241001
  • 135. Part 3: What makes Groovy Awesome? • Java integration • Scripting support • Multiparadigm • Gradual typing • Metaprogramming • Domain Specific Language support • Command chains  Cranes and tortoises revisited • Ecosystem • Community/Team
  • 136. Logic programming example cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs How many of each animal?
  • 137. Recall: Constraint programming example @Grab('org.choco-solver:choco-solver:4.0.0') import org.chocosolver.solver.Model def m = new Model() def totalAnimals = 7 def totalLegs = 20 def numCranes = m.intVar('Cranes', 0, totalAnimals, true) def numTortoises = m.intVar('Tortoises', 0, totalAnimals, true) def numCraneLegs = m.intScaleView(numCranes, 2) def numTortoiseLegs = m.intScaleView(numTortoises, 4) m.arithm(numCranes, '+', numTortoises, '=', totalAnimals).post() m.arithm(numCraneLegs, '+', numTortoiseLegs, '=', totalLegs).post() if (m.solver.solve()) println "$numCranesn$numTortoises" else println "No Solutions" Cranes = 4 Tortoises = 3 ChocoCraneTortoise.groovy
  • 139. Logic programming example cranes have 2 legs tortoises have 4 legs there are 7 animals there are 20 legs display solution Cranes 4 Tortoises 3 dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
  • 140. Logic programming example cranes have 2 legs tortoises have 4 legs millipedes have 1000 legs there are 8 animals there are 1020 legs display solution Cranes 4 Tortoises 3 Millipedes 1 dslUntyped/ChocoCraneTortoiseDSL.groovy 50 lines to define the DSL
  • 141. Logic programming example Crane(2), Tortoise(4), Beetle(6), Centipede(100), Millipede(1000) Solution: Tortoise = 3, Beetle = 23 Solution: Crane = 1, Tortoise = 1, Beetle = 24 Solution: Crane = 25, Centipede = 1 dslTyped/ChocoCraneTortoiseDSL.groovy 80 lines to define the DSL @TypeChecked def main() { animals seen include Crane, Tortoise, Beetle, Centipede leg count is 150 head count is 26 display solution }
  • 142. @TypeChecked def main() { animals seen include Crane, Tortoise, Beetle, Centipede leg count is 150 head count is 26 display solution } Logic programming example dslTyped/ChocoCraneTortoiseDSL.groovy 80 lines to define the DSL
  • 143. DSL Type Provider unresolvedVariable { var -> if (!cachedAnimalNames) { def accessKey = '72ddf45a-c751-44c7-9bca-8db3b4513347' // for illustrative purposes, just download xml for a few animals def uid = 'ELEMENT_GLOBAL.2.104550,ELEMENT_GLOBAL.2.105196,ELEMENT_GLOBAL.2.120227' def base = "https://services.natureserve.org/idd/rest/ns/v1.1/globalSpecies" def url = "$base/comprehensive?uid=$uid&NSAccessKeyId=$accessKey" def root = new XmlParser().parse(url) def names = root.globalSpecies.classification.names cachedAnimalNames = names.natureServePrimaryGlobalCommonName*.text()*.replaceAll(' ','') } if (var.name in cachedAnimalNames) { storeType(var, STRING_TYPE) handled = true enclosingClassNode.addField(var.name, 0, STRING_TYPE, new ConstantExpression(var.name)) } }
  • 144. DSL Type Provider @TypeChecked(extensions='NatureServeAnimalProvider.groovy') def main() { animals seen include SandhillCrane, GopherTortoise, ChihuahuanMillipede leg count is 1020 head count is 8 display solution } Custom checker provider/ChocoCraneTortoiseProvider.groovy 80 lines to define the DSL 25 lines to define the provider
  • 148. Groovy's Awesome Team +100's of others Direct downloads: 2013: approx. 3 million 2014: 4+ million 2015: 12+ million now: 2+ million/month
  • 149. Groovy's Awesome Team +100's of others just like YOU!
  • 150.
  • 151. http://www.manning.com/koenig2 Groovy in Action 2nd edition Manning promo code: ctwg3sum (39% off all Manning books) 152