reading notes on
Making Java Groovy
- Chapter 1, Why add Groovy to Java?
- Chapter 2, Groovy by example
- Chapter 3, Code-level integration
- Chapter 4, Using Groovy features in Java
- Chapter 5, Build processes
- Chapter 6, Testing Groovy and Java projects
- Chapter 7, The Spring framework
- Chapter 8, Database access
- Chapter 9, RESTful web services
- Chapter 10, Building and testing web applications
Chapter 1, Why add Groovy to Java?
(no notes)
Chapter 2, Groovy by example
Hello, Groovy
two additional differences in syntax between groovy and java:
- Semicolons are options.
- Parentheses are often optional.
Accessing Google Chart Tools
using https://developers.google.com/chart/image/
In a groovy script you don't actually have to declare any types at all. If you declare a type the variable becomes local to the script. If not, it becomes part of the "binding"
String base = 'http://chart.apis.google.com/chart?'
groovy is optionally typed, you can specify a type or you can use the def
keyword if you don't know or care.
"If you think of a type, type it"
def params = [cht:'p3',chs:'250x100',chd:'t:60,40',chl:'Hello|World']
in groovy you create a map with []
, and each entry consists of keys and values separated by :
// init a map
Map somemap = [:]
the keys are assumed to be strings by default. the values can be anything.
by default params
is an instance of `java.util.LinkedHashMap'
single-quoted strings are instance of java.lang.String
; double-quoted strings are "interpolated" strings (known as GStrings
params.collect { k,v -> "$k=$v" }
collect
method takes a closure as an argument, applies the closure to each element of the collection, and return a new collection containing the results.
closure is a block of code, delimited by {}
, which can be treated as an object.
if the closure takes one argument, the argument represents a Map.Entry
; with two arguments, the first is the key and the second is the value for each entry.
if the last argument to any methods is a closure you can put the closure outside the parentheses.
// above actually is:
params.collect ( { k,v -> "$k=$v" } )
join
method takes a single argument that's used as the separator when assembling the elements into a String
params.collect { k,v -> "$k=$v" }.join('&')
note that there is ()
on join method above. in groovy, if you leave off the parentheses when calling a method with no arguments the compiler assumes you are asking for the corresponding getter or setter method.
groovy assert
keyword, which takes a boolean expression as an argument. if the expression is true, nothing is returned. but if not, you get the error printed to the console.
params.each { k,v ->
assert qs.contains("$k=$v")
}
groovy jdk adds toURL()
method to the String
class, converts an instance of java.lang.String
into java.net.URL
url.toURL().text
accessing properties in groovy automatically invokes the associated getter or setter method.
above, .text
is actually invoking getText()
method
In groovy every class has a metaclass. A metaclass is another class that manages the actual invocation process.
if you invoke a method on class that doesn't exists, the call is untimately intercepted by a method in the metaclass called methodMissing
. likewise, accessing a property that doesn't exist eventually calls propertyMissing
in the metaclass.
customizing the behavior of methodMissing
and propertyMissing
is the heart of groovy runtime metaprogramming.
Groovy automatically imports:
java.lang
java.util
java.io
java.net
groovy.lang
groovy.util
java.math.BigInteger
java.math.BigDecimal
the as
keyword has several uses, one of which is to provide an alias for imported classes.
in groovy, if you don't specify an access modifier, attributes are assumed to be provate, and methods are assumed to be public.
if you don't add a constructor, you get not only the default, but also a map-based constructor that allows you to set any combination of attribute values by supplying them as key-value pairs.
[stadium.name, stadium.city, stadium.state].collect {
URLEncoder.encode(it, 'UTF-8')
}.join(',')
if you use a closure without specifying a dummy parameter, each element of the list is assigned to a variable called it
.
the last expression in a closure is returned automatically.
parse xml
def response = new XmlSlurper().parse(url)
def homeName = response.result[0].boxscore.@home_name
groovy has two classes for parsing XML. One is XmlParser
and the other is XmlSlurper
. both convert XML into a DOM tree. from a practical point of view the slurper is more efficient and takes less memory.
whether you use an XmlParser
or an XmlSlurper
, exracting data from XML means just walking the dom tree. (slurping JSON is just as easy)
Dots .
traverse from parent elements to children, and @
signs represent attriute values.
work with regular expressions
def pitchers = boxscore.pitching.pitchers
pitchers.each { p ->
if (p.@note && p.@note =~ /W|L|S/) {
println " ${p.@name} ${p.@note}"
}
}
the =~
method in groovy returns an instance of java.util.regex.Matcher
for parsing HTML, use some third-partly library to do it, like NekoHTML parser
generating XML
the standard groovy library includes a class groovy.xml.MarkupBuilder
MarkupBuilder builder = new MarkupBuilder()
builder.games {
results.each { g ->
game(
outcome:"...",
lat:g.stadium.latitude,
lng:g.stadium.longitude
)
}
}
there is no game
method in MarkupBuilder
, the builder intercepts that method call and creates a node out of it.
query database
Sql db = Sql.newInstance(
'jdbc:mysql://localhost:3306/baseball',
'...username...',
'...password...',
'com.mysql.jdbc.Driver'
)
The Sql
class has a static newInstance
method, whose arguments are the JDBC URL, username, password, and the driver class.
db.execute "drop table if exists stadium;"
db.execute '''
create table statidum(
...
);
'''
the three single quotes '''
represent a multiline string. three double quotes """
would be a multiline GString
stadium.each { s ->
db.execute """
insert into stadium (name, ...)
values (${s.name}, ...);
"""
}
use ${...}
notation to substitute values.
assert db.rows('select * from stadium').size() == stadium.size()
db.eachRow('select latitude, longitude from stadium') { row ->
assert row.latitude ...
assert row.longitude ...
}
groovlets
a groovlet is a script that is executed by a class called groovy.servlet.GroovyServlet
groovlets executed this way are deployed as source code rather than as compiled classes under WEB-INF
more about groovlets in chapter 10
Chapter 3, Code-level integration
Groovy and Java together at runtime
at runtime, compiled groovy and compiled java both result in bytecodes for the JVM. to execute code that combines them, all that's necessary is to add a single JAR file to the system. compiling and testing your code requires the Groovy compiler and libraries, but at runtime all you need is one JAR.
> groovyc hello_world.groovy
> java -cp .:$GROOVY_HOME/embeddable/groovy-all-2.1.5.jar hellow_world
using JSR223 scripting for the JAVA platform API
build into Java SE 6 and above, the API for JSR223, Scripting for the Java Platform, is a standard mechanism you can use to call scripts written in other languages.
the JSR223 allows you to call Groovy scripts using purely Java classes
public class ExecuteGroovyFromJSR223 {
public static void main(String[] args) {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
try {
engine.eval("println 'Hello, Groovy!'");
engine.eval(new FileReader("src/hello_world.groovy"));
} catch (ScriptException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
supplying parameters to a groovy script
a binding is a collection of variables at a scope that makes them visible inside a script.
String address = [street,city,state].collect {
URLEncoder.encode(it, 'UTF-8')
}.join(',')
street,city,state
are not declared in the script, this adds them to the binding, making them available to the caller. (something like global variables)
// set binding variables
engine.put("street", "...")
engine.put("city", "...")
engine.put("state", "...")
(Groovy Eval and GroovyShell classes sections are skipped)
so use ScriptEngine
class from Java, or the Eval
and GroovyShell
classes from Groovy, along with a Binding
if necessary, to call Groovy scripts from Java.
and this this the hard way
Calling Groovy from Java the easy way
the easy way: put the Groovy code in a class, compile it as usual, and then instantiate it and invoke methods as thought it was Java.
Calling Java from Groovy
String address = [street,city,state].collect {
URLEncoder.encode(it, 'UTF-8')
}.join(',')
here, URLEncoder
is Java library code.
whenever you mix Java and Groovy, compile everything with groovyc
, let groovyc
handle all the cross-compiler issues. any compiler flags you would normally send to javac
work just fine in groovyc
as well.
Chapter 4, Using Groovy features in Java
def slayers = [buffy, faith]
assert ['Buffy', 'Failth'] == slayers*.name
spread-dot operator extracts name
property from each instance and return a list of the results (same as collect
does)
in groovy all operators are represented by methods, you can overload any operator by Implementing the appropriate method in your groovy class.
public class Department {
// overriding +
public Department plus(Employee e) {
hire(e);
return this;
}
// overriding -
public Department minus(Employee e) {
layOff(e);
return this;
}
}
check doc for list of operators that can be overridden in groovy.
the groovy jdk
every groovy class contains a metaclass. the metaclass contains methods that come into play if a method or property that doesn't exist is accessed through an instance.
for example, groovy adds many methods to the java.util.Collection
interface, including collect,count,find,findAll,leftShift,max,min,sort,sum
check doc for more API of groovy jdk
AST transformations
groovy 1.6 introduced Abstract Syntax Tree (AST) transformations.
@Delegate
public class Camera {
public String takePicture() {
return "taking picture";
}
}
public class Phone {
public String dial(String number) {
return "dialing " + number;
}
}
class SmartPhone {
@Delegate Camera camera = new Camera()
@Delegate Phone phone = new Phone()
}
@Immutable
making a class support immutability requires that
- all mutable methods (setters) must be removed
- the class should be marked
final
- any contained fields should be
private
andfinal
- mutable components like arrays should defensively be copied on the way in (through constructors) and the way out (through getters)
equals,hashCode,toString
should all be implemented through fields
@Immutable
AST transformation does everything for you.
the @Immutable
transformation can only be applied to Groovy classes, but those classes can then be used in Java applications.
@Immutable
class ImmutablePoint {
double x
double y
String toString() { "($x,$y)" }
}
it allows the properties to be set through a constructor, but once set the properties can no longer be modified.
the annotation has its limitations: you can only apply it to classes that contain primitives or certain library classes, like String
or Date
. It also works on classes what contain properties that are also immutable.
@Singleton
@Singleton
class ImmutablePointFactory {
ImmutablePoint newImmutablePoint(xval, yval) {
return new ImmutablePoint(x:xval, y:yval)
}
}
the class now contains a static property called instance
which contains the instance of the class.
ImmutablePoint p = ImmutablePointFactory.instance.newImmutablePoint(3,4)
Working with XML
(already covered above)
Working with JSON data
String url = 'http://api.icndb.com/jokes/random?limitTo=[nerdy]'
String jsonTxt = url.toURL().text
def json = new JsonSlurper().parseText(jsonTxt)
def joke = json?.value?.joke
println joke
JsonBuilder
class generates JSON strings using the same mechanism as XmlSlurper
Chapter 5, Build processes
The build challenge The Java approach, part 1: Ant Making Ant Groovy The Java approach, part 2: Maven Grapes and @Grab The Gradle build system
Chapter 6, Testing Groovy and Java projects
Working with JUnit Testing scripts written in Groovy Testing classes in isolation The future of testing: Spock
Chapter 7, The Spring framework
A Spring application Refreshable beans Spring AOP with Groovy beans Inline scripted beans Groovy with JavaConfig Building beans with the Grails BeanBuilder
Chapter 8, Database access
The Java approach, part 1: JDBC The Groovy approach, part 1: groovy.sql.Sql The Java approach, part 2: Hibernate and JPA The Groovy approach, part 2: Groovy and GORM Groovy and NoSQL databases
Chapter 9, RESTful web services
The REST architecture The Java approach: JAX-RS Implementing JAX-RS with Groovy RESTful Clients Hypermedia Other Groovy approaches
Chapter 10, Building and testing web applications
Groovy servlets and ServletCategory Easy server-side development with groovlets Unit- and integration-testing web components Grails: the Groovy "killer app"