SlideShare a Scribd company logo
1 of 116
Download to read offline
Iván López @ilopmar 
METAPROGRAMMING 
WITH GROOVY
Hello! 
I am Iván López 
@ilopmar
Groovy is dynamic 
▷ “Delay” to runtime some decisions 
▷ Add properties/behaviours in 
runtime 
▷ Wide range of applicability
What is 
metaprogramming?
“ Metaprogramming is the writing 
of computer programs that write 
or manipulate other programs (or 
themselves) as their data. 
- Wikipedia
1. 
Runtime 
metaprogramming
Runtime metaprogramming 
▷ Groovy provides this through 
Meta-Object Protocol (MOP) 
▷ Use MOP to: 
– Invoke methods dynamically 
– Synthesize classes and methods on 
the fly
What is the Meta Object Protocol? 
Groovy 
Groovy 
Java 
Java 
MOP
Intercepting methods 
using MOP
Groovy Interceptable 
▷ GroovyObject interface 
▷ Implement GroovyInterceptable to 
hook into the execution 
public interface GroovyObject { 
Object invokeMethod(String name, Object args) 
Object getProperty(String propertyName) 
void setProperty(String propertyName, Object newValue) 
MetaClass getMetaClass() 
void setMetaClass(MetaClass metaClass) 
}
GroovyInterceptable example 
class Person implements GroovyInterceptable { 
String name 
Integer age 
public Object getProperty(String propertyName) { 
println "Getting property '${propertyName}'" 
return this.@"${propertyName}" 
} 
public void setProperty(String propertyName, Object newValue) { 
println "Setting property '${propertyName}' with value '${newValue}'" 
this.@"${propertyName}" = newValue 
} 
// Execution 
Setting property 'name' with value 'Iván' 
Setting property 'age' with value '34' 
Getting property 'name' 
Getting property 'age' 
Hello Iván, you're 34 
} 
def person = new Person() 
person.name = "Iván" 
person.age = 34 
println "Hello ${person.name}, you're ${person.age}"
GroovyInterceptable example 
class Person implements GroovyInterceptable { 
String name 
Integer age 
public Object getProperty(String propertyName) { 
println "Getting property '${propertyName}'" 
return this.@"${propertyName}" 
} 
public void setProperty(String propertyName, Object newValue) { 
println "Setting property '${propertyName}' with value '${newValue}'" 
this.@"${propertyName}" = newValue 
} 
// Execution 
Setting property 'name' with value 'Iván' 
Setting property 'age' with value '34' 
Getting property 'name' 
Getting property 'age' 
Hello Iván, you're 34 
} 
def person = new Person() 
person.name = "Iván" 
person.age = 34 
println "Hello ${person.name}, you're ${person.age}"
GroovyInterceptable example 
class Person implements GroovyInterceptable { 
String name 
Integer age 
public Object getProperty(String propertyName) { 
println "Getting property '${propertyName}'" 
return this.@"${propertyName}" 
} 
public void setProperty(String propertyName, Object newValue) { 
println "Setting property '${propertyName}' with value '${newValue}'" 
this.@"${propertyName}" = newValue 
} 
// Execution 
Setting property 'name' with value 'Iván' 
Setting property 'age' with value '34' 
Getting property 'name' 
Getting property 'age' 
Hello Iván, you're 34 
} 
def person = new Person() 
person.name = "Iván" 
person.age = 34 
println "Hello ${person.name}, you're ${person.age}"
GroovyInterceptable example 
class Person implements GroovyInterceptable { 
String name 
Integer age 
public Object getProperty(String propertyName) { 
println "Getting property '${propertyName}'" 
return this.@"${propertyName}" 
} 
public void setProperty(String propertyName, Object newValue) { 
println "Setting property '${propertyName}' with value '${newValue}'" 
this.@"${propertyName}" = newValue 
} 
// Execution 
Setting property 'name' with value 'Iván' 
Setting property 'age' with value '34' 
Getting property 'name' 
Getting property 'age' 
Hello Iván, you're 34 
} 
def person = new Person() 
person.name = "Iván" 
person.age = 34 
println "Hello ${person.name}, you're ${person.age}"
GroovyInterceptable example 
class Person implements GroovyInterceptable { 
String name 
Integer age 
public Object getProperty(String propertyName) { 
println "Getting property '${propertyName}'" 
return this.@"${propertyName}" 
} 
public void setProperty(String propertyName, Object newValue) { 
println "Setting property '${propertyName}' with value '${newValue}'" 
this.@"${propertyName}" = newValue 
} 
// Execution 
Setting property 'name' with value 'Iván' 
Setting property 'age' with value '34' 
Getting property 'name' 
Getting property 'age' 
Hello Iván, you're 34 
} 
def person = new Person() 
person.name = "Iván" 
person.age = 34 
println "Hello ${person.name}, you're ${person.age}"
GroovyInterceptable example (II) 
class Hello implements GroovyInterceptable { 
public Object invokeMethod(String methodName, Object args) { 
System.out.println "Invoking method '${methodName}' with args '${args}'" 
def method = metaClass.getMetaMethod(methodName, args) 
method?.invoke(this, args) 
} 
void sayHi(String name) { 
System.out.println "Hello ${name}" 
} 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
GroovyInterceptable example (II) 
class Hello implements GroovyInterceptable { 
public Object invokeMethod(String methodName, Object args) { 
System.out.println "Invoking method '${methodName}' with args '${args}'" 
def method = metaClass.getMetaMethod(methodName, args) 
method?.invoke(this, args) 
} 
void sayHi(String name) { 
System.out.println "Hello ${name}" 
} 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
GroovyInterceptable example (II) 
class Hello implements GroovyInterceptable { 
public Object invokeMethod(String methodName, Object args) { 
System.out.println "Invoking method '${methodName}' with args '${args}'" 
def method = metaClass.getMetaMethod(methodName, args) 
method?.invoke(this, args) 
} 
void sayHi(String name) { 
System.out.println "Hello ${name}" 
} 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
GroovyInterceptable example (II) 
class Hello implements GroovyInterceptable { 
public Object invokeMethod(String methodName, Object args) { 
System.out.println "Invoking method '${methodName}' with args '${args}'" 
def method = metaClass.getMetaMethod(methodName, args) 
method?.invoke(this, args) 
} 
void sayHi(String name) { 
System.out.println "Hello ${name}" 
} 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
MetaClass 
▷ MetaClass registry for each class 
▷ Collection of methods/properties 
▷ We can always modify the metaclass 
▷ Intercept methods implementing 
invokeMethod on metaclass
MetaClass example 
class Hello { 
void sayHi(String name) { 
println "Hello ${name}" 
} 
} 
Hello.metaClass.invokeMethod = { String methodName, args -> 
println "Invoking method '${methodName}' with args '${args}'" 
def method = Hello.metaClass.getMetaMethod(methodName, args) 
method?.invoke(delegate, args) 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
MetaClass example 
class Hello { 
void sayHi(String name) { 
println "Hello ${name}" 
} 
} 
Hello.metaClass.invokeMethod = { String methodName, args -> 
println "Invoking method '${methodName}' with args '${args}'" 
def method = Hello.metaClass.getMetaMethod(methodName, args) 
method?.invoke(delegate, args) 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
MetaClass example 
class Hello { 
void sayHi(String name) { 
println "Hello ${name}" 
} 
} 
Hello.metaClass.invokeMethod = { String methodName, args -> 
println "Invoking method '${methodName}' with args '${args}'" 
def method = Hello.metaClass.getMetaMethod(methodName, args) 
method?.invoke(delegate, args) 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
MetaClass example 
class Hello { 
void sayHi(String name) { 
println "Hello ${name}" 
} 
} 
Hello.metaClass.invokeMethod = { String methodName, args -> 
println "Invoking method '${methodName}' with args '${args}'" 
def method = Hello.metaClass.getMetaMethod(methodName, args) 
method?.invoke(delegate, args) 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
MetaClass example 
class Hello { 
void sayHi(String name) { 
println "Hello ${name}" 
} 
} 
Hello.metaClass.invokeMethod = { String methodName, args -> 
println "Invoking method '${methodName}' with args '${args}'" 
def method = Hello.metaClass.getMetaMethod(methodName, args) 
method?.invoke(delegate, args) 
} 
def hello = new Hello() 
hello.sayHi("GeeCon Prague!") 
hello.anotherMethod() 
// Execution 
Invoking method 'sayHi' with args '[GeeCon Prague!]' 
Hello GeeCon Prague! 
Invoking method 'anotherMethod' with args '[]'
MOP method 
injection
MOP Method Injection 
▷ Injecting methods at code-writing time 
▷ We can “open” a class any time 
▷ Different techniques: 
– MetaClass 
– Categories 
– Extensions 
– Mixins vs Traits
Adding methods using MetaClass 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
String chuckIpsum = "If you can see Chuck Norris, he can see you. 
If you can not see Chuck Norris you may be only seconds away from death" 
println StringUtils.truncate(chuckIpsum, 72) 
println StringUtils.truncate(chuckIpsum, 72, true) 
// Execution 
If you can see Chuck Norris, he can see you. If you can not see Chuck No 
If you can see Chuck Norris, he can see you. If you can not see Chuck No... 
String.metaClass.truncate = { Integer length, Boolean overflow = false -> 
delegate.take(length) + (overflow ? '...' : '') 
} 
assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
Adding methods using MetaClass 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
String chuckIpsum = "If you can see Chuck Norris, he can see you. 
If you can not see Chuck Norris you may be only seconds away from death" 
println StringUtils.truncate(chuckIpsum, 72) 
println StringUtils.truncate(chuckIpsum, 72, true) 
// Execution 
If you can see Chuck Norris, he can see you. If you can not see Chuck No 
If you can see Chuck Norris, he can see you. If you can not see Chuck No... 
String.metaClass.truncate = { Integer length, Boolean overflow = false -> 
delegate.take(length) + (overflow ? '...' : '') 
} 
assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
Adding methods using MetaClass 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
String chuckIpsum = "If you can see Chuck Norris, he can see you. 
If you can not see Chuck Norris you may be only seconds away from death" 
println StringUtils.truncate(chuckIpsum, 72) 
println StringUtils.truncate(chuckIpsum, 72, true) 
// Execution 
If you can see Chuck Norris, he can see you. If you can not see Chuck No 
If you can see Chuck Norris, he can see you. If you can not see Chuck No... 
String.metaClass.truncate = { Integer length, Boolean overflow = false -> 
delegate.take(length) + (overflow ? '...' : '') 
} 
assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
Adding methods using MetaClass 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
String chuckIpsum = "If you can see Chuck Norris, he can see you. 
If you can not see Chuck Norris you may be only seconds away from death" 
println StringUtils.truncate(chuckIpsum, 72) 
println StringUtils.truncate(chuckIpsum, 72, true) 
// Execution 
If you can see Chuck Norris, he can see you. If you can not see Chuck No 
If you can see Chuck Norris, he can see you. If you can not see Chuck No... 
String.metaClass.truncate = { Integer length, Boolean overflow = false -> 
delegate.take(length) + (overflow ? '...' : '') 
} 
assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
Adding methods using MetaClass 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
String chuckIpsum = "If you can see Chuck Norris, he can see you. 
If you can not see Chuck Norris you may be only seconds away from death" 
println StringUtils.truncate(chuckIpsum, 72) 
println StringUtils.truncate(chuckIpsum, 72, true) 
// Execution 
If you can see Chuck Norris, he can see you. If you can not see Chuck No 
If you can see Chuck Norris, he can see you. If you can not see Chuck No... 
String.metaClass.truncate = { Integer length, Boolean overflow = false -> 
delegate.take(length) + (overflow ? '...' : '') 
} 
assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
Adding methods using MetaClass 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
String chuckIpsum = "If you can see Chuck Norris, he can see you. 
If you can not see Chuck Norris you may be only seconds away from death" 
println StringUtils.truncate(chuckIpsum, 72) 
println StringUtils.truncate(chuckIpsum, 72, true) 
// Execution 
If you can see Chuck Norris, he can see you. If you can not see Chuck No 
If you can see Chuck Norris, he can see you. If you can not see Chuck No... 
String.metaClass.truncate = { Integer length, Boolean overflow = false -> 
delegate.take(length) + (overflow ? '...' : '') 
} 
assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
Adding properties using MetaClass 
class Utils { 
} 
def utilsInstance = new Utils() 
Utils.metaClass.version = "3.0" 
utilsInstance.metaClass.released = true 
assert utilsInstance.version == "3.0" 
assert utilsInstance.released == true
Adding properties using MetaClass 
class Utils { 
} 
def utilsInstance = new Utils() 
Utils.metaClass.version = "3.0" 
utilsInstance.metaClass.released = true 
assert utilsInstance.version == "3.0" 
assert utilsInstance.released == true
Adding properties using MetaClass 
class Utils { 
} 
def utilsInstance = new Utils() 
Utils.metaClass.version = "3.0" 
utilsInstance.metaClass.released = true 
assert utilsInstance.version == "3.0" 
assert utilsInstance.released == true
Adding properties using MetaClass 
class Utils { 
} 
def utilsInstance = new Utils() 
Utils.metaClass.version = "3.0" 
utilsInstance.metaClass.released = true 
assert utilsInstance.version == "3.0" 
assert utilsInstance.released == true
Overriding methods using MetaClass 
// Integer 
assert '42' == 42.toString() 
Integer.metaClass.toString = { 
delegate == 42 ? 
'The answer to life, the universe and everything' : 
String.valueOf(delegate) 
} 
assert 42.toString() == 'The answer to life, the universe and everything' 
assert 100.toString() == '100' 
// Boolean 
assert false.toBoolean() == false 
Boolean.metaClass.toBoolean = { !delegate } 
assert false.toBoolean() == true
Overriding methods using MetaClass 
// Integer 
assert '42' == 42.toString() 
Integer.metaClass.toString = { 
delegate == 42 ? 
'The answer to life, the universe and everything' : 
String.valueOf(delegate) 
} 
assert 42.toString() == 'The answer to life, the universe and everything' 
assert 100.toString() == '100' 
// Boolean 
assert false.toBoolean() == false 
Boolean.metaClass.toBoolean = { !delegate } 
assert false.toBoolean() == true
Overriding methods using MetaClass 
// Integer 
assert '42' == 42.toString() 
Integer.metaClass.toString = { 
delegate == 42 ? 
'The answer to life, the universe and everything' : 
String.valueOf(delegate) 
} 
assert 42.toString() == 'The answer to life, the universe and everything' 
assert 100.toString() == '100' 
// Boolean 
assert false.toBoolean() == false 
Boolean.metaClass.toBoolean = { !delegate } 
assert false.toBoolean() == true
Overriding methods using MetaClass 
// Integer 
assert '42' == 42.toString() 
Integer.metaClass.toString = { 
delegate == 42 ? 
'The answer to life, the universe and everything' : 
String.valueOf(delegate) 
} 
assert 42.toString() == 'The answer to life, the universe and everything' 
assert 100.toString() == '100' 
// Boolean 
assert false.toBoolean() == false 
Boolean.metaClass.toBoolean = { !delegate } 
assert false.toBoolean() == true
Overriding methods using MetaClass 
// Integer 
assert '42' == 42.toString() 
Integer.metaClass.toString = { 
delegate == 42 ? 
'The answer to life, the universe and everything' : 
String.valueOf(delegate) 
} 
assert 42.toString() == 'The answer to life, the universe and everything' 
assert 100.toString() == '100' 
// Boolean 
assert false.toBoolean() == false 
Boolean.metaClass.toBoolean = { !delegate } 
assert false.toBoolean() == true
Overriding methods using MetaClass 
// Integer 
assert '42' == 42.toString() 
Integer.metaClass.toString = { 
delegate == 42 ? 
'The answer to life, the universe and everything' : 
String.valueOf(delegate) 
} 
assert 42.toString() == 'The answer to life, the universe and everything' 
assert 100.toString() == '100' 
// Boolean 
assert false.toBoolean() == false 
Boolean.metaClass.toBoolean = { !delegate } 
assert false.toBoolean() == true
Categories 
▷ MetaClass changes are “persistent” 
▷ Change metaclass in confined code 
▷ MOP modified only in the closure
Categories example 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
use (StringUtils) { 
println "Lorem ipsum".truncate(5) 
} 
try { 
println "Lorem ipsum".truncate(5) 
} catch (MissingMethodException mme) { 
println mme 
} 
// Execution 
Lorem 
groovy.lang.MissingMethodException: No 
signature of method: 
java.lang.String.truncate() is 
applicable for argument types: 
(java.lang.Integer) values: [5] 
Possible solutions: 
concat(java.lang.String), take(int)
Categories example 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
use (StringUtils) { 
println "Lorem ipsum".truncate(5) 
} 
try { 
println "Lorem ipsum".truncate(5) 
} catch (MissingMethodException mme) { 
println mme 
} 
// Execution 
Lorem 
groovy.lang.MissingMethodException: No 
signature of method: 
java.lang.String.truncate() is 
applicable for argument types: 
(java.lang.Integer) values: [5] 
Possible solutions: 
concat(java.lang.String), take(int)
Categories example 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
use (StringUtils) { 
println "Lorem ipsum".truncate(5) 
} 
try { 
println "Lorem ipsum".truncate(5) 
} catch (MissingMethodException mme) { 
println mme 
} 
// Execution 
Lorem 
groovy.lang.MissingMethodException: No 
signature of method: 
java.lang.String.truncate() is 
applicable for argument types: 
(java.lang.Integer) values: [5] 
Possible solutions: 
concat(java.lang.String), take(int)
Categories example 
class StringUtils { 
static String truncate(String text, Integer length, Boolean overflow = false) { 
text.take(length) + (overflow ? '...' : '') 
} 
} 
use (StringUtils) { 
println "Lorem ipsum".truncate(5) 
} 
try { 
println "Lorem ipsum".truncate(5) 
} catch (MissingMethodException mme) { 
println mme 
} 
// Execution 
Lorem 
groovy.lang.MissingMethodException: No 
signature of method: 
java.lang.String.truncate() is 
applicable for argument types: 
(java.lang.Integer) values: [5] 
Possible solutions: 
concat(java.lang.String), take(int)
Categories example (II) 
class FileBinaryCategory { 
def static leftShift(File file, URL url) { 
def input 
def output 
try { 
input = url.openStream() 
output = new BufferedOutputStream(new FileOutputStream(file)) 
output << input 
} finally { 
input?.close() 
output?.close() 
} 
} 
}
Categories example (II) 
class FileBinaryCategory { 
def static leftShift(File file, URL url) { 
def input 
def output 
try { 
input = url.openStream() 
output = new BufferedOutputStream(new FileOutputStream(file)) 
output << input 
} finally { 
input?.close() 
output?.close() 
} 
} 
}
Categories example (II) 
class FileBinaryCategory { 
def static leftShift(File file, URL url) { 
def input 
def output 
try { 
File tmpFile = File.createTempFile('tmp_', '') 
use (FileBinaryCategory) { 
input = url.openStream() 
output = new BufferedOutputStream(new FileOutputStream(file)) 
output << input 
tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() 
} 
} finally { 
println tmpFile 
// Execution 
/tmp/tmp_7428855173238452155 
input?.close() 
output?.close() 
} 
} 
}
Categories example (II) 
class FileBinaryCategory { 
def static leftShift(File file, URL url) { 
def input 
def output 
try { 
File tmpFile = File.createTempFile('tmp_', '') 
use (FileBinaryCategory) { 
input = url.openStream() 
output = new BufferedOutputStream(new FileOutputStream(file)) 
output << input 
tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() 
} 
} finally { 
println tmpFile 
// Execution 
/tmp/tmp_7428855173238452155 
input?.close() 
output?.close() 
} 
} 
}
Categories example (II) 
class FileBinaryCategory { 
def static leftShift(File file, URL url) { 
def input 
def output 
try { 
File tmpFile = File.createTempFile('tmp_', '') 
use (FileBinaryCategory) { 
input = url.openStream() 
output = new BufferedOutputStream(new FileOutputStream(file)) 
output << input 
tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() 
} 
} finally { 
println tmpFile 
// Execution 
/tmp/tmp_7428855173238452155 
input?.close() 
output?.close() 
} 
} 
}
Categories example (II) 
class FileBinaryCategory { 
def static leftShift(File file, URL url) { 
def input 
def output 
try { 
File tmpFile = File.createTempFile('tmp_', '') 
use (FileBinaryCategory) { 
input = url.openStream() 
output = new BufferedOutputStream(new FileOutputStream(file)) 
output << input 
tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() 
} 
} finally { 
println tmpFile 
// Execution 
/tmp/tmp_7428855173238452155 
input?.close() 
output?.close() 
} 
} 
}
Extension modules 
▷ JAR file that provides extra methods 
▷ Meta-information file 
▷ Put jar in classpath to enhance classes
Extension modules example 
// src/main/groovy/geecon2014/StringUtilsExtension.groovy 
package geecon2014 
class StringUtilsExtension { 
static String truncate(String self, Integer length, Boolean overflow = false) { 
self.take(length) + (overflow ? '...' : '') 
} 
} 
# src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule 
moduleName = string-utils-module 
moduleVersion = 0.1 
extensionClasses = geecon2014.StringUtilsExtension 
package geecon2014 
import spock.lang.Specification 
class StringUtilsExtensionSpec extends Specification { 
void 'test trucate'() { 
expect: 
"Lorem" == "Lorem ipsum".truncate(5) 
"Lorem..." == "Lorem ipsum".truncate(5, true) 
} 
} 
// Execute with: 
// gradle build 
// groovy -cp 
build/libs/string-extensions-1.0.jar 
ExtensionExample1.groovy 
assert "Lorem..." == "Lorem ipsum". 
truncate(5, true)
Extension modules example 
// src/main/groovy/geecon2014/StringUtilsExtension.groovy 
package geecon2014 
class StringUtilsExtension { 
static String truncate(String self, Integer length, Boolean overflow = false) { 
self.take(length) + (overflow ? '...' : '') 
} 
} 
# src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule 
moduleName = string-utils-module 
moduleVersion = 0.1 
extensionClasses = geecon2014.StringUtilsExtension 
package geecon2014 
import spock.lang.Specification 
class StringUtilsExtensionSpec extends Specification { 
void 'test trucate'() { 
expect: 
"Lorem" == "Lorem ipsum".truncate(5) 
"Lorem..." == "Lorem ipsum".truncate(5, true) 
} 
} 
// Execute with: 
// gradle build 
// groovy -cp 
build/libs/string-extensions-1.0.jar 
ExtensionExample1.groovy 
assert "Lorem..." == "Lorem ipsum". 
truncate(5, true)
Extension modules example 
// src/main/groovy/geecon2014/StringUtilsExtension.groovy 
package geecon2014 
class StringUtilsExtension { 
static String truncate(String self, Integer length, Boolean overflow = false) { 
self.take(length) + (overflow ? '...' : '') 
} 
} 
# src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule 
moduleName = string-utils-module 
moduleVersion = 0.1 
extensionClasses = geecon2014.StringUtilsExtension 
package geecon2014 
import spock.lang.Specification 
class StringUtilsExtensionSpec extends Specification { 
void 'test trucate'() { 
expect: 
"Lorem" == "Lorem ipsum".truncate(5) 
"Lorem..." == "Lorem ipsum".truncate(5, true) 
} 
} 
// Execute with: 
// gradle build 
// groovy -cp 
build/libs/string-extensions-1.0.jar 
ExtensionExample1.groovy 
assert "Lorem..." == "Lorem ipsum". 
truncate(5, true)
Extension modules example 
// src/main/groovy/geecon2014/StringUtilsExtension.groovy 
package geecon2014 
class StringUtilsExtension { 
static String truncate(String self, Integer length, Boolean overflow = false) { 
self.take(length) + (overflow ? '...' : '') 
} 
} 
# src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule 
moduleName = string-utils-module 
moduleVersion = 0.1 
extensionClasses = geecon2014.StringUtilsExtension 
package geecon2014 
import spock.lang.Specification 
class StringUtilsExtensionSpec extends Specification { 
void 'test trucate'() { 
expect: 
"Lorem" == "Lorem ipsum".truncate(5) 
"Lorem..." == "Lorem ipsum".truncate(5, true) 
} 
} 
// Execute with: 
// gradle build 
// groovy -cp 
build/libs/string-extensions-1.0.jar 
ExtensionExample1.groovy 
assert "Lorem..." == "Lorem ipsum". 
truncate(5, true)
Extension modules example 
// src/main/groovy/geecon2014/StringUtilsExtension.groovy 
package geecon2014 
class StringUtilsExtension { 
static String truncate(String self, Integer length, Boolean overflow = false) { 
self.take(length) + (overflow ? '...' : '') 
} 
} 
# src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule 
moduleName = string-utils-module 
moduleVersion = 0.1 
extensionClasses = geecon2014.StringUtilsExtension 
package geecon2014 
import spock.lang.Specification 
class StringUtilsExtensionSpec extends Specification { 
void 'test trucate'() { 
expect: 
"Lorem" == "Lorem ipsum".truncate(5) 
"Lorem..." == "Lorem ipsum".truncate(5, true) 
} 
} 
// Execute with: 
// gradle build 
// groovy -cp 
build/libs/string-extensions-1.0.jar 
ExtensionExample1.groovy 
assert "Lorem..." == "Lorem ipsum". 
truncate(5, true)
Mixins 
▷ “Bring in” or “mix in” implementations 
from multiple classes 
▷ Calls first routed to mixed-in class 
▷ Last mixin wins 
▷ Can't override methods in metaclass 
▷ Not easily un-done
Mixins example 
class SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
} 
} 
class SupermanPower { 
String fly() { 
"Flying..." 
} 
} 
@Mixin([SpidermanPower]) 
class Person {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert !(person instanceof SpidermanPower) 
Person.mixin SupermanPower 
assert person.fly() == "Flying..." 
assert !(person instanceof SupermanPower)
class SupermanPower { 
String fly() { 
"Flying..." 
} 
} 
Mixins example 
class SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
@Mixin([SpidermanPower]) 
class Person {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert !(person instanceof SpidermanPower) 
Person.mixin SupermanPower 
assert person.fly() == "Flying..." 
assert !(person instanceof SupermanPower) 
} 
}
class SupermanPower { 
String fly() { 
"Flying..." 
} 
} 
Mixins example 
class SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
@Mixin([SpidermanPower]) 
class Person {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert !(person instanceof SpidermanPower) 
Person.mixin SupermanPower 
assert person.fly() == "Flying..." 
assert !(person instanceof SupermanPower) 
} 
}
class SupermanPower { 
String fly() { 
"Flying..." 
} 
} 
Mixins example 
class SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
@Mixin([SpidermanPower]) 
class Person {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert !(person instanceof SpidermanPower) 
Person.mixin SupermanPower 
assert person.fly() == "Flying..." 
assert !(person instanceof SupermanPower) 
} 
}
Mixins example 
class SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
@Mixin([SpidermanPower]) 
class Person {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert !(person instanceof SpidermanPower) 
Person.mixin SupermanPower 
assert person.fly() == "Flying..." 
assert !(person instanceof SupermanPower) 
} 
} 
class SupermanPower { 
String fly() { 
"Flying..." 
} 
}
Mixins example 
class SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
@Mixin([SpidermanPower]) 
class Person {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert !(person instanceof SpidermanPower) 
Person.mixin SupermanPower 
assert person.fly() == "Flying..." 
assert !(person instanceof SupermanPower) 
} 
} 
class SupermanPower { 
String fly() { 
"Flying..." 
} 
}
Traits 
▷ Groovy 2.3+ 
▷ Similar to Java 8 default methods 
▷ Supported in JDK 6, 7 and 8 
▷ Stateful 
▷ Composition over inheritance 
▷ Documentation
Traits example 
trait SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
class Person implements SpidermanPower {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert person instanceof SpidermanPower 
def person2 = person.withTraits SupermanPower 
assert person2.fly() == "Flying..." 
assert person2 instanceof SupermanPower 
} 
} 
trait SupermanPower { 
String fly() { 
"Flying..." 
} 
}
Traits example 
trait SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
class Person implements SpidermanPower {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert person instanceof SpidermanPower 
def person2 = person.withTraits SupermanPower 
assert person2.fly() == "Flying..." 
assert person2 instanceof SupermanPower 
} 
} 
trait SupermanPower { 
String fly() { 
"Flying..." 
} 
}
Traits example 
trait SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
class Person implements SpidermanPower {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert person instanceof SpidermanPower 
def person2 = person.withTraits SupermanPower 
assert person2.fly() == "Flying..." 
assert person2 instanceof SupermanPower 
} 
} 
trait SupermanPower { 
String fly() { 
"Flying..." 
} 
}
Traits example 
trait SpidermanPower { 
String spiderSense() { 
"Using spider-sense..." 
class Person implements SpidermanPower {} 
def person = new Person() 
assert person.spiderSense() == "Using spider-sense..." 
assert person instanceof SpidermanPower 
def person2 = person.withTraits SupermanPower 
assert person2.fly() == "Flying..." 
assert person2 instanceof SupermanPower 
} 
} 
trait SupermanPower { 
String fly() { 
"Flying..." 
} 
}
MOP method 
synthesis
MOP Method Synthesis 
▷ Dynamically figure out behaviour upon 
invocation 
▷ It may not exist until it's called/executed 
▷ “Intercept, Cache, Invoke” pattern
Check for methods and properties 
def p = new Person(name: 'Iván', age: 34) 
assert p.respondsTo('sayHi') 
assert p.respondsTo('sayHiTo', String) 
assert !p.respondsTo('goodbye') 
assert p.hasProperty('name') 
assert !p.hasProperty('country') 
class Person { 
String name 
Integer age 
String sayHi() { 
"Hi, my name is ${name} and I'm ${age}" 
} 
String sayHiTo(String name) { 
"Hi ${name}, how are you?" 
} 
}
Check for methods and properties 
def p = new Person(name: 'Iván', age: 34) 
assert p.respondsTo('sayHi') 
assert p.respondsTo('sayHiTo', String) 
assert !p.respondsTo('goodbye') 
assert p.hasProperty('age') 
assert !p.hasProperty('country') 
class Person { 
String name 
Integer age 
String sayHi() { 
"Hi, my name is ${name} and I'm ${age}" 
} 
String sayHiTo(String name) { 
"Hi ${name}, how are you?" 
} 
}
Check for methods and properties 
def p = new Person(name: 'Iván', age: 34) 
assert p.respondsTo('sayHi') 
assert p.respondsTo('sayHiTo', String) 
assert !p.respondsTo('goodbye') 
assert p.hasProperty('age') 
assert !p.hasProperty('country') 
class Person { 
String name 
Integer age 
String sayHi() { 
"Hi, my name is ${name} and I'm ${age}" 
} 
String sayHiTo(String name) { 
"Hi ${name}, how are you?" 
} 
}
Check for methods and properties 
class Person { 
String name 
Integer age 
String sayHi() { 
"Hi, my name is ${name} and I'm ${age}" 
} 
String sayHiTo(String name) { 
"Hi ${name}, how are you?" 
} 
} 
def p = new Person(name: 'Iván', age: 34) 
assert p.respondsTo('sayHi') 
assert p.respondsTo('sayHiTo', String) 
assert !p.respondsTo('goodbye') 
assert p.hasProperty('age') 
assert !p.hasProperty('country')
MethodMissing example 
▷ Requirements: 
– Send notifications to users by different 
channels 
– +50 notifications 
– Not all notifications by all channels 
– Extensible and open to future 
modifications
MethodMissing example 
abstract class Channel { 
void sendNewFollower(String username, String follower) { } 
void sendNewMessage(String username, String msg) { } 
... 
} 
class EmailChannel extends Channel { 
void sendNewFollower(String username, String follower) { 
println "Sending email notification to '${username}' for new follower '${follower}'" 
} 
void sendNewMessage(String username, String msg) { 
println "Sending email notification to '${username}' for new message '${msg}'" 
} 
} 
class MobilePushChannel extends Channel { 
void sendNewFollower(String username, String follower) { 
println "Sending mobile push notification to '${username}' for new follower '${follower}'" 
} 
}
MethodMissing example 
abstract class Channel { 
void sendNewFollower(String username, String follower) { } 
void sendNewMessage(String username, String msg) { } 
... 
} 
class EmailChannel extends Channel { 
void sendNewFollower(String username, String follower) { 
println "Sending email notification to '${username}' for new follower '${follower}'" 
} 
void sendNewMessage(String username, String msg) { 
println "Sending email notification to '${username}' for new message '${msg}'" 
} 
} 
class MobilePushChannel extends Channel { 
void sendNewFollower(String username, String follower) { 
println "Sending mobile push notification to '${username}' for new follower '${follower}'" 
} 
}
MethodMissing example 
abstract class Channel { 
void sendNewFollower(String username, String follower) { } 
void sendNewMessage(String username, String msg) { } 
... 
} 
class EmailChannel extends Channel { 
void sendNewFollower(String username, String follower) { 
println "Sending email notification to '${username}' for new follower '${follower}'" 
} 
void sendNewMessage(String username, String msg) { 
println "Sending email notification to '${username}' for new message '${msg}'" 
} 
} 
class MobilePushChannel extends Channel { 
void sendNewFollower(String username, String follower) { 
println "Sending mobile push notification to '${username}' for new follower '${follower}'" 
} 
}
MethodMissing example 
class NotificationService { 
List channels = [] 
def methodMissing(String name, args) { 
System.out.println "...methodMissing called for ${name} with args ${args}" 
// Generate the implementation 
def implementation = { Object[] methodArgs -> 
channels.each { channel -> 
def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) 
return metaMethod.invoke(channel, methodArgs) 
} 
} 
// Cache the implementation in the metaClass 
NotificationService instance = this 
instance.metaClass."$name" = implementation 
// Execute it! 
implementation(args) 
} 
}
MethodMissing example 
class NotificationService { 
List channels = [] 
def methodMissing(String name, args) { 
System.out.println "...methodMissing called for ${name} with args ${args}" 
// Generate the implementation 
def implementation = { Object[] methodArgs -> 
channels.each { channel -> 
def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) 
return metaMethod.invoke(channel, methodArgs) 
} 
} 
// Cache the implementation in the metaClass 
NotificationService instance = this 
instance.metaClass."$name" = implementation 
// Execute it! 
implementation(args) 
} 
}
MethodMissing example 
class NotificationService { 
List channels = [] 
def methodMissing(String name, args) { 
System.out.println "...methodMissing called for ${name} with args ${args}" 
// Generate the implementation 
def implementation = { Object[] methodArgs -> 
channels.each { channel -> 
def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) 
return metaMethod.invoke(channel, methodArgs) 
} 
} 
// Cache the implementation in the metaClass 
NotificationService instance = this 
instance.metaClass."$name" = implementation 
// Execute it! 
implementation(args) 
} 
} 
notificationService.sendNewFollower(...) 
notificationService.sendNewMessage(...)
MethodMissing example 
class NotificationService { 
List channels = [] 
def methodMissing(String name, args) { 
System.out.println "...methodMissing called for ${name} with args ${args}" 
// Generate the implementation 
def implementation = { Object[] methodArgs -> 
channels.each { channel -> 
def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) 
return metaMethod.invoke(channel, methodArgs) 
} 
} 
// Cache the implementation in the metaClass 
NotificationService instance = this 
instance.metaClass."$name" = implementation 
// Execute it! 
implementation(args) 
} 
}
MethodMissing example 
class NotificationService { 
List channels = [] 
def methodMissing(String name, args) { 
System.out.println "...methodMissing called for ${name} with args ${args}" 
// Generate the implementation 
def implementation = { Object[] methodArgs -> 
channels.each { channel -> 
def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) 
return metaMethod.invoke(channel, methodArgs) 
} 
} 
// Cache the implementation in the metaClass 
NotificationService instance = this 
instance.metaClass."$name" = implementation 
// Execute it! 
implementation(args) 
} 
}
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("class EmailChannel exItveánnd"s, C"hHaenlnleol! "{) 
void sendNewFollower(String username, String follower) {…} 
void sendNewMessage(String username, String msg) {…} 
// Execution 
...} 
methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending class mobile MobilePushChannel push notification extends to Channel 'John' { 
for new follower 'Peter' 
void sendNewFollower(String username, String follower) {…} 
Sending } 
email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
MethodMissing example 
def notificationService = new NotificationService( 
channels: [new EmailChannel(), new MobilePushChannel()] 
) 
assert !notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("John", "Peter") 
assert notificationService.respondsTo('sendNewFollower', String, String) 
notificationService.sendNewFollower("Mary", "Steve") 
notificationService.sendNewMessage("Iván", "Hello!") 
// Execution 
...methodMissing called for sendNewFollower with args [John, Peter] 
Sending email notification to 'John' for new follower 'Peter' 
Sending mobile push notification to 'John' for new follower 'Peter' 
Sending email notification to 'Mary' for new follower 'Steve' 
Sending mobile push notification to 'Mary' for new follower 'Steve' 
...methodMissing called for sendNewMessage with args [Iván, Hello!] 
Sending email notification to 'Iván' for new message 'Hello!'
2. 
Compile-time 
metaprogramming
Compile-time metaprogramming 
▷ Advance feature 
▷ Analyze/modify program structure at 
compile time 
▷ Cross-cutting features 
▷ Write code that generates bytecode
AST and compilation 
▷ AST: Abstract Syntax Tree 
▷ AST modified during compilation 
▷ Hook into the phases 
▷ Initialization, Parsing, Conversion, 
Semantic analysis, Canonicalization, 
Instruction selection, Class 
generation, Output, Finalization
Groovy AST Transformations 
▷ Lot of AST transformations out-of-the-box 
▷ @EqualsAndHashCode, @ToString, 
@TuppleConstructor, @Canonical, @Grab, 
@Immutable, @Delegate, @Singleton, 
@Category, @Log4j, @CompileStatic, 
@TypeChecked, @Synchronized 
...
Global AST 
transformations
Global AST Transformations 
▷ No annotation 
▷ Meta-information file 
▷ Applied to all code during compilation 
▷ Any compilation phase 
▷ Grails uses intensively in GORM
Local AST 
transformations
Local AST Transformations 
▷ Annotate code 
▷ No meta-information file 
▷ Easy to debug
Steps to create local AST 
Interface AST Enjoy!
Local AST example 
import geecon2014.Version 
@Version('1.0') 
class VersionedClass { 
} 
package geecon2014 
import ... 
@Retention(RetentionPolicy.SOURCE) 
@Target([ElementType.TYPE]) 
@GroovyASTTransformationClass("geecon2014.VersionASTTransformation") 
@interface Version { 
String value() 
} 
class VersionedClass { 
public static final String VERSION = "1.0" 
}
Local AST example 
import geecon2014.Version 
@Version('1.0') 
class VersionedClass { 
} 
package geecon2014 
import ... 
@Retention(RetentionPolicy.SOURCE) 
@Target([ElementType.TYPE]) 
@GroovyASTTransformationClass("geecon2014.VersionASTTransformation") 
@interface Version { 
String value() 
} 
class VersionedClass { 
public static final String VERSION = "1.0" 
}
Local AST example 
import geecon2014.Version 
@Version('1.0') 
class VersionedClass { 
} 
package geecon2014 
import ... 
@Retention(RetentionPolicy.SOURCE) 
@Target([ElementType.TYPE]) 
@GroovyASTTransformationClass("geecon2014.VersionASTTransformation") 
@interface Version { 
String value() 
} 
class VersionedClass { 
public static final String VERSION = "1.0" 
}
Local AST example 
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) 
class VersionASTTransformation extends AbstractASTTransformation { 
@Override 
public void visit(final ASTNode[] nodes, final SourceUnit source) { 
if (nodes.length != 2) { 
return 
} 
if (nodes[0] instanceof AnnotationNode && nodes[1] instanceof ClassNode) { 
def annotation = nodes[0] 
def version = annotation.getMember('value') 
if (version instanceof ConstantExpression) { 
nodes[1].addField('VERSION', ACC_PUBLIC | ACC_STATIC | ACC_FINAL, 
ClassHelper.STRING_TYPE, version) 
} else { 
source.addError(new SyntaxException("Invalid value for annotation", annotation.lineNumber, 
annotation.columnNumber)) 
} 
} 
} 
}
Local AST example 
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) 
class VersionASTTransformation extends AbstractASTTransformation { 
@Override 
public void visit(final ASTNode[] nodes, final SourceUnit source) { 
if (nodes.length != 2) { 
return 
} 
if (nodes[0] instanceof AnnotationNode && nodes[1] instanceof ClassNode) { 
def annotation = nodes[0] 
def version = annotation.getMember('value') 
if (version instanceof ConstantExpression) { 
nodes[1].addField('VERSION', ACC_PUBLIC | ACC_STATIC | ACC_FINAL, 
ClassHelper.STRING_TYPE, version) 
} else { 
source.addError(new SyntaxException("Invalid value for annotation", annotation.lineNumber, 
annotation.columnNumber)) 
} 
} 
} 
}
Local AST example 
// Execute with: 
// gradle build 
// groovy -cp build/libs/add-version-1.0.jar LocalASTExample.groovy 
import geecon.Version 
@Version('1.0') 
class VersionedClass { 
} 
println VersionedClass.VERSION 
// Execution 
1.0
Local AST example 
// Execute with: 
// gradle build 
// groovy -cp build/libs/add-version-1.0.jar LocalASTExample.groovy 
import geecon.Version 
@Version('1.0') 
class VersionedClass { 
} 
println VersionedClass.VERSION 
// Execution 
1.0
Local AST example 
// Execute with: 
// gradle build 
// groovy -cp build/libs/add-version-1.0.jar LocalASTExample.groovy 
import geecon.Version 
@Version('1.0') 
class VersionedClass { 
} 
println VersionedClass.VERSION 
// Execution 
1.0
3. 
Recap 
Why we should use 
metaprogramming?
Let’s review some concepts 
Metaprogramming 
out-of-the box 
Easy and very 
powerfull 
Write better code 
Add behaviour 
easily 
Take advantage of 
this power 
Because Groovy, 
it's groovy
With great power 
comes great 
responsibility
Thanks! 
Any questions? 
Iván López 
@ilopmar 
lopez.ivan@gmail.com 
https://github.com/lmivan 
http://kcy.me/1bzj7

More Related Content

What's hot

TDC2016SP - Trilha .NET
TDC2016SP - Trilha .NETTDC2016SP - Trilha .NET
TDC2016SP - Trilha .NETtdc-globalcode
 
Generics and Inference
Generics and InferenceGenerics and Inference
Generics and InferenceRichard Fox
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
Spock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestSpock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestHoward Lewis Ship
 
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Stubる - Mockingjayを使ったHTTPクライアントのテスト -Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Stubる - Mockingjayを使ったHTTPクライアントのテスト -Kenji Tanaka
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeMongoDB
 
Ian 20150116 java script oop
Ian 20150116 java script oopIan 20150116 java script oop
Ian 20150116 java script oopLearningTech
 
Do you Promise?
Do you Promise?Do you Promise?
Do you Promise?jungkees
 
Herding types with Scala macros
Herding types with Scala macrosHerding types with Scala macros
Herding types with Scala macrosMarina Sigaeva
 
多治見IT勉強会 Groovy Grails
多治見IT勉強会 Groovy Grails多治見IT勉強会 Groovy Grails
多治見IT勉強会 Groovy GrailsTsuyoshi Yamamoto
 
Kotlin on Android: Delegate with pleasure
Kotlin on Android: Delegate with pleasureKotlin on Android: Delegate with pleasure
Kotlin on Android: Delegate with pleasureDmytro Zaitsev
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3Simon Su
 
Scala - den smarta kusinen
Scala - den smarta kusinenScala - den smarta kusinen
Scala - den smarta kusinenRedpill Linpro
 
JNI - Java & C in the same project
JNI - Java & C in the same projectJNI - Java & C in the same project
JNI - Java & C in the same projectKarol Wrótniak
 
Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察Tsuyoshi Yamamoto
 

What's hot (20)

TDC2016SP - Trilha .NET
TDC2016SP - Trilha .NETTDC2016SP - Trilha .NET
TDC2016SP - Trilha .NET
 
Generics and Inference
Generics and InferenceGenerics and Inference
Generics and Inference
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
Spock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestSpock: A Highly Logical Way To Test
Spock: A Highly Logical Way To Test
 
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Stubる - Mockingjayを使ったHTTPクライアントのテスト -Stubる - Mockingjayを使ったHTTPクライアントのテスト -
Stubる - Mockingjayを使ったHTTPクライアントのテスト -
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
 
Ian 20150116 java script oop
Ian 20150116 java script oopIan 20150116 java script oop
Ian 20150116 java script oop
 
Do you Promise?
Do you Promise?Do you Promise?
Do you Promise?
 
Herding types with Scala macros
Herding types with Scala macrosHerding types with Scala macros
Herding types with Scala macros
 
Typelevel summit
Typelevel summitTypelevel summit
Typelevel summit
 
多治見IT勉強会 Groovy Grails
多治見IT勉強会 Groovy Grails多治見IT勉強会 Groovy Grails
多治見IT勉強会 Groovy Grails
 
Kotlin on Android: Delegate with pleasure
Kotlin on Android: Delegate with pleasureKotlin on Android: Delegate with pleasure
Kotlin on Android: Delegate with pleasure
 
360|iDev
360|iDev360|iDev
360|iDev
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3
 
Scala - den smarta kusinen
Scala - den smarta kusinenScala - den smarta kusinen
Scala - den smarta kusinen
 
Groovy intro for OUDL
Groovy intro for OUDLGroovy intro for OUDL
Groovy intro for OUDL
 
mobl
moblmobl
mobl
 
JNI - Java & C in the same project
JNI - Java & C in the same projectJNI - Java & C in the same project
JNI - Java & C in the same project
 
Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察
 
Mattbrenner
MattbrennerMattbrenner
Mattbrenner
 

Similar to GeeCON Prague 2014 - Metaprogramming with Groovy

ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyIván López Martín
 
Kotlin, 어떻게 동작하나요
Kotlin, 어떻게 동작하나요Kotlin, 어떻게 동작하나요
Kotlin, 어떻게 동작하나요Chang W. Doh
 
CodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodecamp Romania
 
Hey Kotlin, How it works?
Hey Kotlin, How it works?Hey Kotlin, How it works?
Hey Kotlin, How it works?Chang W. Doh
 
Greach, GroovyFx Workshop
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx WorkshopDierk König
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitZachary Klein
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokusHamletDRC
 
Madrid gug - sacando partido a las transformaciones ast de groovy
Madrid gug - sacando partido a las transformaciones ast de groovyMadrid gug - sacando partido a las transformaciones ast de groovy
Madrid gug - sacando partido a las transformaciones ast de groovyIván López Martín
 
Groovy And Grails JUG Padova
Groovy And Grails JUG PadovaGroovy And Grails JUG Padova
Groovy And Grails JUG PadovaJohn Leach
 
Metaprogramming with Groovy
Metaprogramming with GroovyMetaprogramming with Groovy
Metaprogramming with GroovyAli Tanwir
 
Groovy for java developers
Groovy for java developersGroovy for java developers
Groovy for java developersPuneet Behl
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)James Titcumb
 
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、GaelykでハンズオンTsuyoshi Yamamoto
 
Introduction To Groovy
Introduction To GroovyIntroduction To Groovy
Introduction To Groovymanishkp84
 
Groovy And Grails JUG Sardegna
Groovy And Grails JUG SardegnaGroovy And Grails JUG Sardegna
Groovy And Grails JUG SardegnaJohn Leach
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle GroovyDeepak Bhagat
 
G3 Summit 2016 - Taking Advantage of Groovy Annotations
G3 Summit 2016 - Taking Advantage of Groovy AnnotationsG3 Summit 2016 - Taking Advantage of Groovy Annotations
G3 Summit 2016 - Taking Advantage of Groovy AnnotationsIván López Martín
 

Similar to GeeCON Prague 2014 - Metaprogramming with Groovy (20)

ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with Groovy
 
Kotlin, 어떻게 동작하나요
Kotlin, 어떻게 동작하나요Kotlin, 어떻게 동작하나요
Kotlin, 어떻게 동작하나요
 
CodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical GroovyCodeCamp Iasi 10 march 2012 - Practical Groovy
CodeCamp Iasi 10 march 2012 - Practical Groovy
 
Hey Kotlin, How it works?
Hey Kotlin, How it works?Hey Kotlin, How it works?
Hey Kotlin, How it works?
 
Greach, GroovyFx Workshop
Greach, GroovyFx WorkshopGreach, GroovyFx Workshop
Greach, GroovyFx Workshop
 
Grails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to OrbitGrails Launchpad - From Ground Zero to Orbit
Grails Launchpad - From Ground Zero to Orbit
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
Madrid gug - sacando partido a las transformaciones ast de groovy
Madrid gug - sacando partido a las transformaciones ast de groovyMadrid gug - sacando partido a las transformaciones ast de groovy
Madrid gug - sacando partido a las transformaciones ast de groovy
 
Groovy And Grails JUG Padova
Groovy And Grails JUG PadovaGroovy And Grails JUG Padova
Groovy And Grails JUG Padova
 
Metaprogramming with Groovy
Metaprogramming with GroovyMetaprogramming with Groovy
Metaprogramming with Groovy
 
MetaProgramming with Groovy
MetaProgramming with GroovyMetaProgramming with Groovy
MetaProgramming with Groovy
 
Groovy for java developers
Groovy for java developersGroovy for java developers
Groovy for java developers
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
名古屋SGGAE/J勉強会 Grails、Gaelykでハンズオン
 
Introduction To Groovy
Introduction To GroovyIntroduction To Groovy
Introduction To Groovy
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
 
Groovy And Grails JUG Sardegna
Groovy And Grails JUG SardegnaGroovy And Grails JUG Sardegna
Groovy And Grails JUG Sardegna
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle Groovy
 
Oops in php
Oops in phpOops in php
Oops in php
 
G3 Summit 2016 - Taking Advantage of Groovy Annotations
G3 Summit 2016 - Taking Advantage of Groovy AnnotationsG3 Summit 2016 - Taking Advantage of Groovy Annotations
G3 Summit 2016 - Taking Advantage of Groovy Annotations
 

More from Iván López Martín

SalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 TestcontainersSalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 TestcontainersIván López Martín
 
CommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 TestcontainersCommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 TestcontainersIván López Martín
 
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdfVoxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdfIván López Martín
 
VMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring BootVMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring BootIván López Martín
 
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud GatewaySpring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud GatewayIván López Martín
 
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootCodemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootIván López Martín
 
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3Iván López Martín
 
Construyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVMConstruyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVMIván López Martín
 
jLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoTjLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoTIván López Martín
 
Codemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con MicronautCodemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con MicronautIván López Martín
 
JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!Iván López Martín
 
JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3Iván López Martín
 
JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3Iván López Martín
 
Developing Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEADeveloping Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEAIván López Martín
 
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfectaCommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfectaIván López Martín
 
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!Iván López Martín
 
Greach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut ConfigurationsGreach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut ConfigurationsIván López Martín
 
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet youVoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet youIván López Martín
 
JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!Iván López Martín
 
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerteCrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerteIván López Martín
 

More from Iván López Martín (20)

SalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 TestcontainersSalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 Testcontainers
 
CommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 TestcontainersCommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 Testcontainers
 
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdfVoxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
 
VMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring BootVMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring Boot
 
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud GatewaySpring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
 
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootCodemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring Boot
 
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
 
Construyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVMConstruyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVM
 
jLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoTjLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoT
 
Codemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con MicronautCodemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con Micronaut
 
JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!
 
JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3
 
JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3
 
Developing Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEADeveloping Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEA
 
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfectaCommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
 
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
 
Greach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut ConfigurationsGreach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut Configurations
 
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet youVoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
 
JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!
 
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerteCrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
 

Recently uploaded

Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfSeasiaInfotech2
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 

Recently uploaded (20)

Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdf
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 

GeeCON Prague 2014 - Metaprogramming with Groovy

  • 1. Iván López @ilopmar METAPROGRAMMING WITH GROOVY
  • 2. Hello! I am Iván López @ilopmar
  • 3. Groovy is dynamic ▷ “Delay” to runtime some decisions ▷ Add properties/behaviours in runtime ▷ Wide range of applicability
  • 5. “ Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data. - Wikipedia
  • 7. Runtime metaprogramming ▷ Groovy provides this through Meta-Object Protocol (MOP) ▷ Use MOP to: – Invoke methods dynamically – Synthesize classes and methods on the fly
  • 8. What is the Meta Object Protocol? Groovy Groovy Java Java MOP
  • 10. Groovy Interceptable ▷ GroovyObject interface ▷ Implement GroovyInterceptable to hook into the execution public interface GroovyObject { Object invokeMethod(String name, Object args) Object getProperty(String propertyName) void setProperty(String propertyName, Object newValue) MetaClass getMetaClass() void setMetaClass(MetaClass metaClass) }
  • 11. GroovyInterceptable example class Person implements GroovyInterceptable { String name Integer age public Object getProperty(String propertyName) { println "Getting property '${propertyName}'" return this.@"${propertyName}" } public void setProperty(String propertyName, Object newValue) { println "Setting property '${propertyName}' with value '${newValue}'" this.@"${propertyName}" = newValue } // Execution Setting property 'name' with value 'Iván' Setting property 'age' with value '34' Getting property 'name' Getting property 'age' Hello Iván, you're 34 } def person = new Person() person.name = "Iván" person.age = 34 println "Hello ${person.name}, you're ${person.age}"
  • 12. GroovyInterceptable example class Person implements GroovyInterceptable { String name Integer age public Object getProperty(String propertyName) { println "Getting property '${propertyName}'" return this.@"${propertyName}" } public void setProperty(String propertyName, Object newValue) { println "Setting property '${propertyName}' with value '${newValue}'" this.@"${propertyName}" = newValue } // Execution Setting property 'name' with value 'Iván' Setting property 'age' with value '34' Getting property 'name' Getting property 'age' Hello Iván, you're 34 } def person = new Person() person.name = "Iván" person.age = 34 println "Hello ${person.name}, you're ${person.age}"
  • 13. GroovyInterceptable example class Person implements GroovyInterceptable { String name Integer age public Object getProperty(String propertyName) { println "Getting property '${propertyName}'" return this.@"${propertyName}" } public void setProperty(String propertyName, Object newValue) { println "Setting property '${propertyName}' with value '${newValue}'" this.@"${propertyName}" = newValue } // Execution Setting property 'name' with value 'Iván' Setting property 'age' with value '34' Getting property 'name' Getting property 'age' Hello Iván, you're 34 } def person = new Person() person.name = "Iván" person.age = 34 println "Hello ${person.name}, you're ${person.age}"
  • 14. GroovyInterceptable example class Person implements GroovyInterceptable { String name Integer age public Object getProperty(String propertyName) { println "Getting property '${propertyName}'" return this.@"${propertyName}" } public void setProperty(String propertyName, Object newValue) { println "Setting property '${propertyName}' with value '${newValue}'" this.@"${propertyName}" = newValue } // Execution Setting property 'name' with value 'Iván' Setting property 'age' with value '34' Getting property 'name' Getting property 'age' Hello Iván, you're 34 } def person = new Person() person.name = "Iván" person.age = 34 println "Hello ${person.name}, you're ${person.age}"
  • 15. GroovyInterceptable example class Person implements GroovyInterceptable { String name Integer age public Object getProperty(String propertyName) { println "Getting property '${propertyName}'" return this.@"${propertyName}" } public void setProperty(String propertyName, Object newValue) { println "Setting property '${propertyName}' with value '${newValue}'" this.@"${propertyName}" = newValue } // Execution Setting property 'name' with value 'Iván' Setting property 'age' with value '34' Getting property 'name' Getting property 'age' Hello Iván, you're 34 } def person = new Person() person.name = "Iván" person.age = 34 println "Hello ${person.name}, you're ${person.age}"
  • 16. GroovyInterceptable example (II) class Hello implements GroovyInterceptable { public Object invokeMethod(String methodName, Object args) { System.out.println "Invoking method '${methodName}' with args '${args}'" def method = metaClass.getMetaMethod(methodName, args) method?.invoke(this, args) } void sayHi(String name) { System.out.println "Hello ${name}" } } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 17. GroovyInterceptable example (II) class Hello implements GroovyInterceptable { public Object invokeMethod(String methodName, Object args) { System.out.println "Invoking method '${methodName}' with args '${args}'" def method = metaClass.getMetaMethod(methodName, args) method?.invoke(this, args) } void sayHi(String name) { System.out.println "Hello ${name}" } } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 18. GroovyInterceptable example (II) class Hello implements GroovyInterceptable { public Object invokeMethod(String methodName, Object args) { System.out.println "Invoking method '${methodName}' with args '${args}'" def method = metaClass.getMetaMethod(methodName, args) method?.invoke(this, args) } void sayHi(String name) { System.out.println "Hello ${name}" } } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 19. GroovyInterceptable example (II) class Hello implements GroovyInterceptable { public Object invokeMethod(String methodName, Object args) { System.out.println "Invoking method '${methodName}' with args '${args}'" def method = metaClass.getMetaMethod(methodName, args) method?.invoke(this, args) } void sayHi(String name) { System.out.println "Hello ${name}" } } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 20. MetaClass ▷ MetaClass registry for each class ▷ Collection of methods/properties ▷ We can always modify the metaclass ▷ Intercept methods implementing invokeMethod on metaclass
  • 21. MetaClass example class Hello { void sayHi(String name) { println "Hello ${name}" } } Hello.metaClass.invokeMethod = { String methodName, args -> println "Invoking method '${methodName}' with args '${args}'" def method = Hello.metaClass.getMetaMethod(methodName, args) method?.invoke(delegate, args) } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 22. MetaClass example class Hello { void sayHi(String name) { println "Hello ${name}" } } Hello.metaClass.invokeMethod = { String methodName, args -> println "Invoking method '${methodName}' with args '${args}'" def method = Hello.metaClass.getMetaMethod(methodName, args) method?.invoke(delegate, args) } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 23. MetaClass example class Hello { void sayHi(String name) { println "Hello ${name}" } } Hello.metaClass.invokeMethod = { String methodName, args -> println "Invoking method '${methodName}' with args '${args}'" def method = Hello.metaClass.getMetaMethod(methodName, args) method?.invoke(delegate, args) } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 24. MetaClass example class Hello { void sayHi(String name) { println "Hello ${name}" } } Hello.metaClass.invokeMethod = { String methodName, args -> println "Invoking method '${methodName}' with args '${args}'" def method = Hello.metaClass.getMetaMethod(methodName, args) method?.invoke(delegate, args) } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 25. MetaClass example class Hello { void sayHi(String name) { println "Hello ${name}" } } Hello.metaClass.invokeMethod = { String methodName, args -> println "Invoking method '${methodName}' with args '${args}'" def method = Hello.metaClass.getMetaMethod(methodName, args) method?.invoke(delegate, args) } def hello = new Hello() hello.sayHi("GeeCon Prague!") hello.anotherMethod() // Execution Invoking method 'sayHi' with args '[GeeCon Prague!]' Hello GeeCon Prague! Invoking method 'anotherMethod' with args '[]'
  • 27. MOP Method Injection ▷ Injecting methods at code-writing time ▷ We can “open” a class any time ▷ Different techniques: – MetaClass – Categories – Extensions – Mixins vs Traits
  • 28. Adding methods using MetaClass class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } String chuckIpsum = "If you can see Chuck Norris, he can see you. If you can not see Chuck Norris you may be only seconds away from death" println StringUtils.truncate(chuckIpsum, 72) println StringUtils.truncate(chuckIpsum, 72, true) // Execution If you can see Chuck Norris, he can see you. If you can not see Chuck No If you can see Chuck Norris, he can see you. If you can not see Chuck No... String.metaClass.truncate = { Integer length, Boolean overflow = false -> delegate.take(length) + (overflow ? '...' : '') } assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
  • 29. Adding methods using MetaClass class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } String chuckIpsum = "If you can see Chuck Norris, he can see you. If you can not see Chuck Norris you may be only seconds away from death" println StringUtils.truncate(chuckIpsum, 72) println StringUtils.truncate(chuckIpsum, 72, true) // Execution If you can see Chuck Norris, he can see you. If you can not see Chuck No If you can see Chuck Norris, he can see you. If you can not see Chuck No... String.metaClass.truncate = { Integer length, Boolean overflow = false -> delegate.take(length) + (overflow ? '...' : '') } assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
  • 30. Adding methods using MetaClass class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } String chuckIpsum = "If you can see Chuck Norris, he can see you. If you can not see Chuck Norris you may be only seconds away from death" println StringUtils.truncate(chuckIpsum, 72) println StringUtils.truncate(chuckIpsum, 72, true) // Execution If you can see Chuck Norris, he can see you. If you can not see Chuck No If you can see Chuck Norris, he can see you. If you can not see Chuck No... String.metaClass.truncate = { Integer length, Boolean overflow = false -> delegate.take(length) + (overflow ? '...' : '') } assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
  • 31. Adding methods using MetaClass class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } String chuckIpsum = "If you can see Chuck Norris, he can see you. If you can not see Chuck Norris you may be only seconds away from death" println StringUtils.truncate(chuckIpsum, 72) println StringUtils.truncate(chuckIpsum, 72, true) // Execution If you can see Chuck Norris, he can see you. If you can not see Chuck No If you can see Chuck Norris, he can see you. If you can not see Chuck No... String.metaClass.truncate = { Integer length, Boolean overflow = false -> delegate.take(length) + (overflow ? '...' : '') } assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
  • 32. Adding methods using MetaClass class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } String chuckIpsum = "If you can see Chuck Norris, he can see you. If you can not see Chuck Norris you may be only seconds away from death" println StringUtils.truncate(chuckIpsum, 72) println StringUtils.truncate(chuckIpsum, 72, true) // Execution If you can see Chuck Norris, he can see you. If you can not see Chuck No If you can see Chuck Norris, he can see you. If you can not see Chuck No... String.metaClass.truncate = { Integer length, Boolean overflow = false -> delegate.take(length) + (overflow ? '...' : '') } assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
  • 33. Adding methods using MetaClass class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } String chuckIpsum = "If you can see Chuck Norris, he can see you. If you can not see Chuck Norris you may be only seconds away from death" println StringUtils.truncate(chuckIpsum, 72) println StringUtils.truncate(chuckIpsum, 72, true) // Execution If you can see Chuck Norris, he can see you. If you can not see Chuck No If you can see Chuck Norris, he can see you. If you can not see Chuck No... String.metaClass.truncate = { Integer length, Boolean overflow = false -> delegate.take(length) + (overflow ? '...' : '') } assert chuckIpsum.truncate(72, true) == StringUtils.truncate(chuckIpsum, 72, true)
  • 34. Adding properties using MetaClass class Utils { } def utilsInstance = new Utils() Utils.metaClass.version = "3.0" utilsInstance.metaClass.released = true assert utilsInstance.version == "3.0" assert utilsInstance.released == true
  • 35. Adding properties using MetaClass class Utils { } def utilsInstance = new Utils() Utils.metaClass.version = "3.0" utilsInstance.metaClass.released = true assert utilsInstance.version == "3.0" assert utilsInstance.released == true
  • 36. Adding properties using MetaClass class Utils { } def utilsInstance = new Utils() Utils.metaClass.version = "3.0" utilsInstance.metaClass.released = true assert utilsInstance.version == "3.0" assert utilsInstance.released == true
  • 37. Adding properties using MetaClass class Utils { } def utilsInstance = new Utils() Utils.metaClass.version = "3.0" utilsInstance.metaClass.released = true assert utilsInstance.version == "3.0" assert utilsInstance.released == true
  • 38. Overriding methods using MetaClass // Integer assert '42' == 42.toString() Integer.metaClass.toString = { delegate == 42 ? 'The answer to life, the universe and everything' : String.valueOf(delegate) } assert 42.toString() == 'The answer to life, the universe and everything' assert 100.toString() == '100' // Boolean assert false.toBoolean() == false Boolean.metaClass.toBoolean = { !delegate } assert false.toBoolean() == true
  • 39. Overriding methods using MetaClass // Integer assert '42' == 42.toString() Integer.metaClass.toString = { delegate == 42 ? 'The answer to life, the universe and everything' : String.valueOf(delegate) } assert 42.toString() == 'The answer to life, the universe and everything' assert 100.toString() == '100' // Boolean assert false.toBoolean() == false Boolean.metaClass.toBoolean = { !delegate } assert false.toBoolean() == true
  • 40. Overriding methods using MetaClass // Integer assert '42' == 42.toString() Integer.metaClass.toString = { delegate == 42 ? 'The answer to life, the universe and everything' : String.valueOf(delegate) } assert 42.toString() == 'The answer to life, the universe and everything' assert 100.toString() == '100' // Boolean assert false.toBoolean() == false Boolean.metaClass.toBoolean = { !delegate } assert false.toBoolean() == true
  • 41. Overriding methods using MetaClass // Integer assert '42' == 42.toString() Integer.metaClass.toString = { delegate == 42 ? 'The answer to life, the universe and everything' : String.valueOf(delegate) } assert 42.toString() == 'The answer to life, the universe and everything' assert 100.toString() == '100' // Boolean assert false.toBoolean() == false Boolean.metaClass.toBoolean = { !delegate } assert false.toBoolean() == true
  • 42. Overriding methods using MetaClass // Integer assert '42' == 42.toString() Integer.metaClass.toString = { delegate == 42 ? 'The answer to life, the universe and everything' : String.valueOf(delegate) } assert 42.toString() == 'The answer to life, the universe and everything' assert 100.toString() == '100' // Boolean assert false.toBoolean() == false Boolean.metaClass.toBoolean = { !delegate } assert false.toBoolean() == true
  • 43. Overriding methods using MetaClass // Integer assert '42' == 42.toString() Integer.metaClass.toString = { delegate == 42 ? 'The answer to life, the universe and everything' : String.valueOf(delegate) } assert 42.toString() == 'The answer to life, the universe and everything' assert 100.toString() == '100' // Boolean assert false.toBoolean() == false Boolean.metaClass.toBoolean = { !delegate } assert false.toBoolean() == true
  • 44. Categories ▷ MetaClass changes are “persistent” ▷ Change metaclass in confined code ▷ MOP modified only in the closure
  • 45. Categories example class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } use (StringUtils) { println "Lorem ipsum".truncate(5) } try { println "Lorem ipsum".truncate(5) } catch (MissingMethodException mme) { println mme } // Execution Lorem groovy.lang.MissingMethodException: No signature of method: java.lang.String.truncate() is applicable for argument types: (java.lang.Integer) values: [5] Possible solutions: concat(java.lang.String), take(int)
  • 46. Categories example class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } use (StringUtils) { println "Lorem ipsum".truncate(5) } try { println "Lorem ipsum".truncate(5) } catch (MissingMethodException mme) { println mme } // Execution Lorem groovy.lang.MissingMethodException: No signature of method: java.lang.String.truncate() is applicable for argument types: (java.lang.Integer) values: [5] Possible solutions: concat(java.lang.String), take(int)
  • 47. Categories example class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } use (StringUtils) { println "Lorem ipsum".truncate(5) } try { println "Lorem ipsum".truncate(5) } catch (MissingMethodException mme) { println mme } // Execution Lorem groovy.lang.MissingMethodException: No signature of method: java.lang.String.truncate() is applicable for argument types: (java.lang.Integer) values: [5] Possible solutions: concat(java.lang.String), take(int)
  • 48. Categories example class StringUtils { static String truncate(String text, Integer length, Boolean overflow = false) { text.take(length) + (overflow ? '...' : '') } } use (StringUtils) { println "Lorem ipsum".truncate(5) } try { println "Lorem ipsum".truncate(5) } catch (MissingMethodException mme) { println mme } // Execution Lorem groovy.lang.MissingMethodException: No signature of method: java.lang.String.truncate() is applicable for argument types: (java.lang.Integer) values: [5] Possible solutions: concat(java.lang.String), take(int)
  • 49. Categories example (II) class FileBinaryCategory { def static leftShift(File file, URL url) { def input def output try { input = url.openStream() output = new BufferedOutputStream(new FileOutputStream(file)) output << input } finally { input?.close() output?.close() } } }
  • 50. Categories example (II) class FileBinaryCategory { def static leftShift(File file, URL url) { def input def output try { input = url.openStream() output = new BufferedOutputStream(new FileOutputStream(file)) output << input } finally { input?.close() output?.close() } } }
  • 51. Categories example (II) class FileBinaryCategory { def static leftShift(File file, URL url) { def input def output try { File tmpFile = File.createTempFile('tmp_', '') use (FileBinaryCategory) { input = url.openStream() output = new BufferedOutputStream(new FileOutputStream(file)) output << input tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() } } finally { println tmpFile // Execution /tmp/tmp_7428855173238452155 input?.close() output?.close() } } }
  • 52. Categories example (II) class FileBinaryCategory { def static leftShift(File file, URL url) { def input def output try { File tmpFile = File.createTempFile('tmp_', '') use (FileBinaryCategory) { input = url.openStream() output = new BufferedOutputStream(new FileOutputStream(file)) output << input tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() } } finally { println tmpFile // Execution /tmp/tmp_7428855173238452155 input?.close() output?.close() } } }
  • 53. Categories example (II) class FileBinaryCategory { def static leftShift(File file, URL url) { def input def output try { File tmpFile = File.createTempFile('tmp_', '') use (FileBinaryCategory) { input = url.openStream() output = new BufferedOutputStream(new FileOutputStream(file)) output << input tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() } } finally { println tmpFile // Execution /tmp/tmp_7428855173238452155 input?.close() output?.close() } } }
  • 54. Categories example (II) class FileBinaryCategory { def static leftShift(File file, URL url) { def input def output try { File tmpFile = File.createTempFile('tmp_', '') use (FileBinaryCategory) { input = url.openStream() output = new BufferedOutputStream(new FileOutputStream(file)) output << input tmpFile << "http://groovy.codehaus.org/images/groovy-logo-medium.png".toURL() } } finally { println tmpFile // Execution /tmp/tmp_7428855173238452155 input?.close() output?.close() } } }
  • 55. Extension modules ▷ JAR file that provides extra methods ▷ Meta-information file ▷ Put jar in classpath to enhance classes
  • 56. Extension modules example // src/main/groovy/geecon2014/StringUtilsExtension.groovy package geecon2014 class StringUtilsExtension { static String truncate(String self, Integer length, Boolean overflow = false) { self.take(length) + (overflow ? '...' : '') } } # src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = string-utils-module moduleVersion = 0.1 extensionClasses = geecon2014.StringUtilsExtension package geecon2014 import spock.lang.Specification class StringUtilsExtensionSpec extends Specification { void 'test trucate'() { expect: "Lorem" == "Lorem ipsum".truncate(5) "Lorem..." == "Lorem ipsum".truncate(5, true) } } // Execute with: // gradle build // groovy -cp build/libs/string-extensions-1.0.jar ExtensionExample1.groovy assert "Lorem..." == "Lorem ipsum". truncate(5, true)
  • 57. Extension modules example // src/main/groovy/geecon2014/StringUtilsExtension.groovy package geecon2014 class StringUtilsExtension { static String truncate(String self, Integer length, Boolean overflow = false) { self.take(length) + (overflow ? '...' : '') } } # src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = string-utils-module moduleVersion = 0.1 extensionClasses = geecon2014.StringUtilsExtension package geecon2014 import spock.lang.Specification class StringUtilsExtensionSpec extends Specification { void 'test trucate'() { expect: "Lorem" == "Lorem ipsum".truncate(5) "Lorem..." == "Lorem ipsum".truncate(5, true) } } // Execute with: // gradle build // groovy -cp build/libs/string-extensions-1.0.jar ExtensionExample1.groovy assert "Lorem..." == "Lorem ipsum". truncate(5, true)
  • 58. Extension modules example // src/main/groovy/geecon2014/StringUtilsExtension.groovy package geecon2014 class StringUtilsExtension { static String truncate(String self, Integer length, Boolean overflow = false) { self.take(length) + (overflow ? '...' : '') } } # src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = string-utils-module moduleVersion = 0.1 extensionClasses = geecon2014.StringUtilsExtension package geecon2014 import spock.lang.Specification class StringUtilsExtensionSpec extends Specification { void 'test trucate'() { expect: "Lorem" == "Lorem ipsum".truncate(5) "Lorem..." == "Lorem ipsum".truncate(5, true) } } // Execute with: // gradle build // groovy -cp build/libs/string-extensions-1.0.jar ExtensionExample1.groovy assert "Lorem..." == "Lorem ipsum". truncate(5, true)
  • 59. Extension modules example // src/main/groovy/geecon2014/StringUtilsExtension.groovy package geecon2014 class StringUtilsExtension { static String truncate(String self, Integer length, Boolean overflow = false) { self.take(length) + (overflow ? '...' : '') } } # src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = string-utils-module moduleVersion = 0.1 extensionClasses = geecon2014.StringUtilsExtension package geecon2014 import spock.lang.Specification class StringUtilsExtensionSpec extends Specification { void 'test trucate'() { expect: "Lorem" == "Lorem ipsum".truncate(5) "Lorem..." == "Lorem ipsum".truncate(5, true) } } // Execute with: // gradle build // groovy -cp build/libs/string-extensions-1.0.jar ExtensionExample1.groovy assert "Lorem..." == "Lorem ipsum". truncate(5, true)
  • 60. Extension modules example // src/main/groovy/geecon2014/StringUtilsExtension.groovy package geecon2014 class StringUtilsExtension { static String truncate(String self, Integer length, Boolean overflow = false) { self.take(length) + (overflow ? '...' : '') } } # src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = string-utils-module moduleVersion = 0.1 extensionClasses = geecon2014.StringUtilsExtension package geecon2014 import spock.lang.Specification class StringUtilsExtensionSpec extends Specification { void 'test trucate'() { expect: "Lorem" == "Lorem ipsum".truncate(5) "Lorem..." == "Lorem ipsum".truncate(5, true) } } // Execute with: // gradle build // groovy -cp build/libs/string-extensions-1.0.jar ExtensionExample1.groovy assert "Lorem..." == "Lorem ipsum". truncate(5, true)
  • 61. Mixins ▷ “Bring in” or “mix in” implementations from multiple classes ▷ Calls first routed to mixed-in class ▷ Last mixin wins ▷ Can't override methods in metaclass ▷ Not easily un-done
  • 62. Mixins example class SpidermanPower { String spiderSense() { "Using spider-sense..." } } class SupermanPower { String fly() { "Flying..." } } @Mixin([SpidermanPower]) class Person {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert !(person instanceof SpidermanPower) Person.mixin SupermanPower assert person.fly() == "Flying..." assert !(person instanceof SupermanPower)
  • 63. class SupermanPower { String fly() { "Flying..." } } Mixins example class SpidermanPower { String spiderSense() { "Using spider-sense..." @Mixin([SpidermanPower]) class Person {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert !(person instanceof SpidermanPower) Person.mixin SupermanPower assert person.fly() == "Flying..." assert !(person instanceof SupermanPower) } }
  • 64. class SupermanPower { String fly() { "Flying..." } } Mixins example class SpidermanPower { String spiderSense() { "Using spider-sense..." @Mixin([SpidermanPower]) class Person {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert !(person instanceof SpidermanPower) Person.mixin SupermanPower assert person.fly() == "Flying..." assert !(person instanceof SupermanPower) } }
  • 65. class SupermanPower { String fly() { "Flying..." } } Mixins example class SpidermanPower { String spiderSense() { "Using spider-sense..." @Mixin([SpidermanPower]) class Person {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert !(person instanceof SpidermanPower) Person.mixin SupermanPower assert person.fly() == "Flying..." assert !(person instanceof SupermanPower) } }
  • 66. Mixins example class SpidermanPower { String spiderSense() { "Using spider-sense..." @Mixin([SpidermanPower]) class Person {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert !(person instanceof SpidermanPower) Person.mixin SupermanPower assert person.fly() == "Flying..." assert !(person instanceof SupermanPower) } } class SupermanPower { String fly() { "Flying..." } }
  • 67. Mixins example class SpidermanPower { String spiderSense() { "Using spider-sense..." @Mixin([SpidermanPower]) class Person {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert !(person instanceof SpidermanPower) Person.mixin SupermanPower assert person.fly() == "Flying..." assert !(person instanceof SupermanPower) } } class SupermanPower { String fly() { "Flying..." } }
  • 68. Traits ▷ Groovy 2.3+ ▷ Similar to Java 8 default methods ▷ Supported in JDK 6, 7 and 8 ▷ Stateful ▷ Composition over inheritance ▷ Documentation
  • 69. Traits example trait SpidermanPower { String spiderSense() { "Using spider-sense..." class Person implements SpidermanPower {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert person instanceof SpidermanPower def person2 = person.withTraits SupermanPower assert person2.fly() == "Flying..." assert person2 instanceof SupermanPower } } trait SupermanPower { String fly() { "Flying..." } }
  • 70. Traits example trait SpidermanPower { String spiderSense() { "Using spider-sense..." class Person implements SpidermanPower {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert person instanceof SpidermanPower def person2 = person.withTraits SupermanPower assert person2.fly() == "Flying..." assert person2 instanceof SupermanPower } } trait SupermanPower { String fly() { "Flying..." } }
  • 71. Traits example trait SpidermanPower { String spiderSense() { "Using spider-sense..." class Person implements SpidermanPower {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert person instanceof SpidermanPower def person2 = person.withTraits SupermanPower assert person2.fly() == "Flying..." assert person2 instanceof SupermanPower } } trait SupermanPower { String fly() { "Flying..." } }
  • 72. Traits example trait SpidermanPower { String spiderSense() { "Using spider-sense..." class Person implements SpidermanPower {} def person = new Person() assert person.spiderSense() == "Using spider-sense..." assert person instanceof SpidermanPower def person2 = person.withTraits SupermanPower assert person2.fly() == "Flying..." assert person2 instanceof SupermanPower } } trait SupermanPower { String fly() { "Flying..." } }
  • 74. MOP Method Synthesis ▷ Dynamically figure out behaviour upon invocation ▷ It may not exist until it's called/executed ▷ “Intercept, Cache, Invoke” pattern
  • 75. Check for methods and properties def p = new Person(name: 'Iván', age: 34) assert p.respondsTo('sayHi') assert p.respondsTo('sayHiTo', String) assert !p.respondsTo('goodbye') assert p.hasProperty('name') assert !p.hasProperty('country') class Person { String name Integer age String sayHi() { "Hi, my name is ${name} and I'm ${age}" } String sayHiTo(String name) { "Hi ${name}, how are you?" } }
  • 76. Check for methods and properties def p = new Person(name: 'Iván', age: 34) assert p.respondsTo('sayHi') assert p.respondsTo('sayHiTo', String) assert !p.respondsTo('goodbye') assert p.hasProperty('age') assert !p.hasProperty('country') class Person { String name Integer age String sayHi() { "Hi, my name is ${name} and I'm ${age}" } String sayHiTo(String name) { "Hi ${name}, how are you?" } }
  • 77. Check for methods and properties def p = new Person(name: 'Iván', age: 34) assert p.respondsTo('sayHi') assert p.respondsTo('sayHiTo', String) assert !p.respondsTo('goodbye') assert p.hasProperty('age') assert !p.hasProperty('country') class Person { String name Integer age String sayHi() { "Hi, my name is ${name} and I'm ${age}" } String sayHiTo(String name) { "Hi ${name}, how are you?" } }
  • 78. Check for methods and properties class Person { String name Integer age String sayHi() { "Hi, my name is ${name} and I'm ${age}" } String sayHiTo(String name) { "Hi ${name}, how are you?" } } def p = new Person(name: 'Iván', age: 34) assert p.respondsTo('sayHi') assert p.respondsTo('sayHiTo', String) assert !p.respondsTo('goodbye') assert p.hasProperty('age') assert !p.hasProperty('country')
  • 79. MethodMissing example ▷ Requirements: – Send notifications to users by different channels – +50 notifications – Not all notifications by all channels – Extensible and open to future modifications
  • 80. MethodMissing example abstract class Channel { void sendNewFollower(String username, String follower) { } void sendNewMessage(String username, String msg) { } ... } class EmailChannel extends Channel { void sendNewFollower(String username, String follower) { println "Sending email notification to '${username}' for new follower '${follower}'" } void sendNewMessage(String username, String msg) { println "Sending email notification to '${username}' for new message '${msg}'" } } class MobilePushChannel extends Channel { void sendNewFollower(String username, String follower) { println "Sending mobile push notification to '${username}' for new follower '${follower}'" } }
  • 81. MethodMissing example abstract class Channel { void sendNewFollower(String username, String follower) { } void sendNewMessage(String username, String msg) { } ... } class EmailChannel extends Channel { void sendNewFollower(String username, String follower) { println "Sending email notification to '${username}' for new follower '${follower}'" } void sendNewMessage(String username, String msg) { println "Sending email notification to '${username}' for new message '${msg}'" } } class MobilePushChannel extends Channel { void sendNewFollower(String username, String follower) { println "Sending mobile push notification to '${username}' for new follower '${follower}'" } }
  • 82. MethodMissing example abstract class Channel { void sendNewFollower(String username, String follower) { } void sendNewMessage(String username, String msg) { } ... } class EmailChannel extends Channel { void sendNewFollower(String username, String follower) { println "Sending email notification to '${username}' for new follower '${follower}'" } void sendNewMessage(String username, String msg) { println "Sending email notification to '${username}' for new message '${msg}'" } } class MobilePushChannel extends Channel { void sendNewFollower(String username, String follower) { println "Sending mobile push notification to '${username}' for new follower '${follower}'" } }
  • 83. MethodMissing example class NotificationService { List channels = [] def methodMissing(String name, args) { System.out.println "...methodMissing called for ${name} with args ${args}" // Generate the implementation def implementation = { Object[] methodArgs -> channels.each { channel -> def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) return metaMethod.invoke(channel, methodArgs) } } // Cache the implementation in the metaClass NotificationService instance = this instance.metaClass."$name" = implementation // Execute it! implementation(args) } }
  • 84. MethodMissing example class NotificationService { List channels = [] def methodMissing(String name, args) { System.out.println "...methodMissing called for ${name} with args ${args}" // Generate the implementation def implementation = { Object[] methodArgs -> channels.each { channel -> def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) return metaMethod.invoke(channel, methodArgs) } } // Cache the implementation in the metaClass NotificationService instance = this instance.metaClass."$name" = implementation // Execute it! implementation(args) } }
  • 85. MethodMissing example class NotificationService { List channels = [] def methodMissing(String name, args) { System.out.println "...methodMissing called for ${name} with args ${args}" // Generate the implementation def implementation = { Object[] methodArgs -> channels.each { channel -> def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) return metaMethod.invoke(channel, methodArgs) } } // Cache the implementation in the metaClass NotificationService instance = this instance.metaClass."$name" = implementation // Execute it! implementation(args) } } notificationService.sendNewFollower(...) notificationService.sendNewMessage(...)
  • 86. MethodMissing example class NotificationService { List channels = [] def methodMissing(String name, args) { System.out.println "...methodMissing called for ${name} with args ${args}" // Generate the implementation def implementation = { Object[] methodArgs -> channels.each { channel -> def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) return metaMethod.invoke(channel, methodArgs) } } // Cache the implementation in the metaClass NotificationService instance = this instance.metaClass."$name" = implementation // Execute it! implementation(args) } }
  • 87. MethodMissing example class NotificationService { List channels = [] def methodMissing(String name, args) { System.out.println "...methodMissing called for ${name} with args ${args}" // Generate the implementation def implementation = { Object[] methodArgs -> channels.each { channel -> def metaMethod = channel.metaClass.getMetaMethod(name, methodArgs) return metaMethod.invoke(channel, methodArgs) } } // Cache the implementation in the metaClass NotificationService instance = this instance.metaClass."$name" = implementation // Execute it! implementation(args) } }
  • 88. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 89. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 90. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 91. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 92. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("class EmailChannel exItveánnd"s, C"hHaenlnleol! "{) void sendNewFollower(String username, String follower) {…} void sendNewMessage(String username, String msg) {…} // Execution ...} methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending class mobile MobilePushChannel push notification extends to Channel 'John' { for new follower 'Peter' void sendNewFollower(String username, String follower) {…} Sending } email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 93. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 94. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 95. MethodMissing example def notificationService = new NotificationService( channels: [new EmailChannel(), new MobilePushChannel()] ) assert !notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("John", "Peter") assert notificationService.respondsTo('sendNewFollower', String, String) notificationService.sendNewFollower("Mary", "Steve") notificationService.sendNewMessage("Iván", "Hello!") // Execution ...methodMissing called for sendNewFollower with args [John, Peter] Sending email notification to 'John' for new follower 'Peter' Sending mobile push notification to 'John' for new follower 'Peter' Sending email notification to 'Mary' for new follower 'Steve' Sending mobile push notification to 'Mary' for new follower 'Steve' ...methodMissing called for sendNewMessage with args [Iván, Hello!] Sending email notification to 'Iván' for new message 'Hello!'
  • 97. Compile-time metaprogramming ▷ Advance feature ▷ Analyze/modify program structure at compile time ▷ Cross-cutting features ▷ Write code that generates bytecode
  • 98. AST and compilation ▷ AST: Abstract Syntax Tree ▷ AST modified during compilation ▷ Hook into the phases ▷ Initialization, Parsing, Conversion, Semantic analysis, Canonicalization, Instruction selection, Class generation, Output, Finalization
  • 99. Groovy AST Transformations ▷ Lot of AST transformations out-of-the-box ▷ @EqualsAndHashCode, @ToString, @TuppleConstructor, @Canonical, @Grab, @Immutable, @Delegate, @Singleton, @Category, @Log4j, @CompileStatic, @TypeChecked, @Synchronized ...
  • 101. Global AST Transformations ▷ No annotation ▷ Meta-information file ▷ Applied to all code during compilation ▷ Any compilation phase ▷ Grails uses intensively in GORM
  • 103. Local AST Transformations ▷ Annotate code ▷ No meta-information file ▷ Easy to debug
  • 104. Steps to create local AST Interface AST Enjoy!
  • 105. Local AST example import geecon2014.Version @Version('1.0') class VersionedClass { } package geecon2014 import ... @Retention(RetentionPolicy.SOURCE) @Target([ElementType.TYPE]) @GroovyASTTransformationClass("geecon2014.VersionASTTransformation") @interface Version { String value() } class VersionedClass { public static final String VERSION = "1.0" }
  • 106. Local AST example import geecon2014.Version @Version('1.0') class VersionedClass { } package geecon2014 import ... @Retention(RetentionPolicy.SOURCE) @Target([ElementType.TYPE]) @GroovyASTTransformationClass("geecon2014.VersionASTTransformation") @interface Version { String value() } class VersionedClass { public static final String VERSION = "1.0" }
  • 107. Local AST example import geecon2014.Version @Version('1.0') class VersionedClass { } package geecon2014 import ... @Retention(RetentionPolicy.SOURCE) @Target([ElementType.TYPE]) @GroovyASTTransformationClass("geecon2014.VersionASTTransformation") @interface Version { String value() } class VersionedClass { public static final String VERSION = "1.0" }
  • 108. Local AST example @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) class VersionASTTransformation extends AbstractASTTransformation { @Override public void visit(final ASTNode[] nodes, final SourceUnit source) { if (nodes.length != 2) { return } if (nodes[0] instanceof AnnotationNode && nodes[1] instanceof ClassNode) { def annotation = nodes[0] def version = annotation.getMember('value') if (version instanceof ConstantExpression) { nodes[1].addField('VERSION', ACC_PUBLIC | ACC_STATIC | ACC_FINAL, ClassHelper.STRING_TYPE, version) } else { source.addError(new SyntaxException("Invalid value for annotation", annotation.lineNumber, annotation.columnNumber)) } } } }
  • 109. Local AST example @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) class VersionASTTransformation extends AbstractASTTransformation { @Override public void visit(final ASTNode[] nodes, final SourceUnit source) { if (nodes.length != 2) { return } if (nodes[0] instanceof AnnotationNode && nodes[1] instanceof ClassNode) { def annotation = nodes[0] def version = annotation.getMember('value') if (version instanceof ConstantExpression) { nodes[1].addField('VERSION', ACC_PUBLIC | ACC_STATIC | ACC_FINAL, ClassHelper.STRING_TYPE, version) } else { source.addError(new SyntaxException("Invalid value for annotation", annotation.lineNumber, annotation.columnNumber)) } } } }
  • 110. Local AST example // Execute with: // gradle build // groovy -cp build/libs/add-version-1.0.jar LocalASTExample.groovy import geecon.Version @Version('1.0') class VersionedClass { } println VersionedClass.VERSION // Execution 1.0
  • 111. Local AST example // Execute with: // gradle build // groovy -cp build/libs/add-version-1.0.jar LocalASTExample.groovy import geecon.Version @Version('1.0') class VersionedClass { } println VersionedClass.VERSION // Execution 1.0
  • 112. Local AST example // Execute with: // gradle build // groovy -cp build/libs/add-version-1.0.jar LocalASTExample.groovy import geecon.Version @Version('1.0') class VersionedClass { } println VersionedClass.VERSION // Execution 1.0
  • 113. 3. Recap Why we should use metaprogramming?
  • 114. Let’s review some concepts Metaprogramming out-of-the box Easy and very powerfull Write better code Add behaviour easily Take advantage of this power Because Groovy, it's groovy
  • 115. With great power comes great responsibility
  • 116. Thanks! Any questions? Iván López @ilopmar lopez.ivan@gmail.com https://github.com/lmivan http://kcy.me/1bzj7