- •brief contents
- •contents
- •foreword
- •preface
- •acknowledgments
- •about this book
- •Roadmap
- •Code conventions and downloads
- •Author Online
- •About the author
- •about the cover illustration
- •1 Why add Groovy to Java?
- •1.1 Issues with Java
- •1.1.1 Is static typing a bug or a feature?
- •1.1.2 Methods must be in a class, even if you don’t need or want one
- •1.1.3 Java is overly verbose
- •1.1.4 Groovy makes testing Java much easier
- •1.1.5 Groovy tools simplify your build
- •1.2 Groovy features that help Java
- •1.3 Java use cases and how Groovy helps
- •1.3.1 Spring framework support for Groovy
- •1.3.2 Simplified database access
- •1.3.3 Building and accessing web services
- •1.3.4 Web application enhancements
- •1.4 Summary
- •2 Groovy by example
- •2.1 Hello, Groovy
- •2.2 Accessing Google Chart Tools
- •2.2.1 Assembling the URL with query string
- •2.2.2 Transmitting the URL
- •2.2.3 Creating a UI with SwingBuilder
- •2.3 Groovy Baseball
- •2.3.1 Database data and Plain Old Groovy Objects
- •2.3.2 Parsing XML
- •2.3.3 HTML builders and groovlets
- •2.4 Summary
- •3 Code-level integration
- •3.1 Integrating Java with other languages
- •3.2 Executing Groovy scripts from Java
- •3.2.1 Using JSR223 scripting for the Java Platform API
- •3.2.2 Working with the Groovy Eval class
- •3.2.3 Working with the GroovyShell class
- •3.2.4 Calling Groovy from Java the easy way
- •3.2.5 Calling Java from Groovy
- •3.3 Summary
- •4 Using Groovy features in Java
- •4.1 Treating POJOs like POGOs
- •4.2 Implementing operator overloading in Java
- •4.3 Making Java library classes better: the Groovy JDK
- •4.4 Cool AST transformations
- •4.4.1 Delegating to contained objects
- •4.4.2 Creating immutable objects
- •4.4.3 Creating singletons
- •4.5 Working with XML
- •4.6 Working with JSON data
- •4.7 Summary
- •5 Build processes
- •5.1 The build challenge
- •5.2 The Java approach, part 1: Ant
- •5.3 Making Ant Groovy
- •5.3.1 The <groovy> Ant task
- •5.3.2 The <groovyc> Ant task
- •5.3.3 Writing your build in Groovy with AntBuilder
- •5.3.4 Custom build scripts with Gant
- •5.3.5 Ant summary
- •5.4 The Java approach, part 2: Maven
- •5.4.2 The GMaven project
- •5.4.3 Maven summary
- •5.5 Grapes and @Grab
- •5.6 The Gradle build system
- •5.6.1 Basic Gradle builds
- •5.6.2 Interesting configurations
- •5.7 Summary
- •6 Testing Groovy and Java projects
- •6.1 Working with JUnit
- •6.1.1 A Java test for the Groovy implementation
- •6.1.2 A Groovy test for the Java implementation
- •6.1.3 A GroovyTestCase test for a Java implementation
- •6.2 Testing scripts written in Groovy
- •6.2.1 Useful subclasses of GroovyTestCase: GroovyShellTestCase
- •6.2.2 Useful subclasses of GroovyTestCase: GroovyLogTestCase
- •6.3 Testing classes in isolation
- •6.3.1 Coerced closures
- •6.3.2 The Expando class
- •6.3.3 StubFor and MockFor
- •6.4 The future of testing: Spock
- •6.4.1 The Search for Spock
- •6.4.2 Test well, and prosper
- •6.4.4 The trouble with tribbles
- •6.4.5 Other Spock capabilities
- •6.5 Summary
- •7 The Spring framework
- •7.1 A Spring application
- •7.2 Refreshable beans
- •7.3 Spring AOP with Groovy beans
- •7.4 Inline scripted beans
- •7.5 Groovy with JavaConfig
- •7.6 Building beans with the Grails BeanBuilder
- •7.7 Summary
- •8 Database access
- •8.1 The Java approach, part 1: JDBC
- •8.2 The Groovy approach, part 1: groovy.sql.Sql
- •8.3 The Java approach, part 2: Hibernate and JPA
- •8.4 The Groovy approach, part 2: Groovy and GORM
- •8.4.1 Groovy simplifications
- •8.5 Groovy and NoSQL databases
- •8.5.1 Populating Groovy vampires
- •8.5.2 Querying and mapping MongoDB data
- •8.6 Summary
- •9 RESTful web services
- •9.1 The REST architecture
- •9.3 Implementing JAX-RS with Groovy
- •9.4 RESTful Clients
- •9.5 Hypermedia
- •9.5.1 A simple example: Rotten Tomatoes
- •9.5.2 Adding transitional links
- •9.5.3 Adding structural links
- •9.5.4 Using a JsonBuilder to control the output
- •9.6 Other Groovy approaches
- •9.6.1 Groovlets
- •9.6.2 Ratpack
- •9.6.3 Grails and REST
- •9.7 Summary
- •10 Building and testing web applications
- •10.1 Groovy servlets and ServletCategory
- •10.2 Easy server-side development with groovlets
- •10.2.1 A “Hello, World!” groovlet
- •10.2.2 Implicit variables in groovlets
- •10.3.2 Integration testing with Gradle
- •10.3.3 Automating Jetty in the Gradle build
- •10.4 Grails: the Groovy “killer app”
- •10.4.1 The quest for the holy Grails
- •10.5 Summary
- •A.1 Installing a JDK
- •A.2 Installing Groovy
- •A.3 Testing your installation
- •A.4 IDE support
- •A.5 Installing other projects in the Groovy ecosystem
- •B.1 Scripts and the traditional example
- •B.2 Variables, numbers, and strings
- •B.2.1 Numbers
- •B.2.2 Strings and Groovy strings
- •B.3 Plain Old Groovy Objects
- •B.4 Collections
- •B.4.1 Ranges
- •B.4.2 Lists
- •B.4.3 Maps
- •B.5 Closures
- •B.6 Loops and conditionals
- •B.6.1 Loops
- •B.6.2 Conditionals
- •B.6.3 Elvis
- •B.6.4 Safe de-reference
- •B.7 File I/O
- •B.8.1 Parsing and slurping XML
- •B.8.2 Generating XML
- •B.8.3 Validation
- •B.9 JSON support
- •B.9.1 Slurping JSON
- •B.9.2 Building JSON
- •index
- •Symbols
Using Groovy features in Java
This chapter covers
■Basic code-level simplifications
■Useful AST transformations
■XML processing
In chapter 1 I reviewed many of Java’s arguable weaknesses and drawbacks and suggested ways that Groovy might help ameliorate them. Because that chapter was intended to be introductory I only suggested how Groovy can help, without showing a lot of code examples. Now that I’ve established how easy it is to add Groovy classes to Java applications, when is it helpful to do so? What features, if any, does Groovy bring to Java systems that make them easier to develop?
A guide to the techniques covered in this chapter is shown in figure 4.1. I’ll review several Groovy advantages, like POGOs, operator overloading, the Groovy JDK, AST transformations, and how to use Groovy to work with XML and JSON data. To start, I’ll show that from Groovy code POJOs can be treated as though they were POGOs.
64
www.it-ebooks.info
|
Treating POJOs like POGOs |
65 |
|
POGOs |
Operator |
AST |
|
overloading |
transformations |
|
|
|
|
||
Groovy |
|
|
|
features |
|
|
|
Groovy |
XML parsers |
JSON parsers |
|
JDK |
and builders |
and builders |
|
Figure 4.1 Groovy features that can be added to Java classes
4.1Treating POJOs like POGOs
POGOs have more capabilities than POJOs. For example, all POGOs have a map-based constructor that’s very convenient for setting properties. The interesting thing is that even if a class is written in Java, many of the same conveniences apply as long as it’s accessed from Groovy.
Consider a simple POJO representing a person, possibly created as part of a domain model in Java, shown in the next listing. To keep it simple I’ll only include an ID and a name. I’ll put in a toString override as well but won’t include the inevitable equals and hashCode overrides.
Listing 4.1 A simple POJO representing a person
public class Person { private int id; private String name;
public Person() {}
public Person(int id, String name) { this.id = id;
this.name = name;
}
public void setId(int id) { this.id = id; } public int getId() { return id; }
public void setName(String name) { this.name = name; } public String getName() { return name; }
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
Any typical Java persistence layer has dozens of classes just like this, which map to relational database tables (figure 4.2).
If I instantiate this class from Groovy I can use a map-based1 constructor to do so, even though the Java version already specifies two constructors and neither is the one
1The term map-based refers to the fact that the attributes are set using the key-value notation used in Groovy maps. The constructor doesn’t actually use a map to do its job.
www.it-ebooks.info
66 |
CHAPTER 4 Using Groovy features in Java |
new A(prop1:..., Groovy adds map-based constructor
prop2:...)
Groovy
Java Class A with typical constructors
Figure 4.2 Groovy adds a map-based constructor to Java classes, regardless of what constructors are already included.
I want. The following Groovy script creates some Person instances using three different mechanisms, none of which appear in the Java class:
def buffy = new Person(name:'Buffy') assert buffy.id == 0
assert buffy.name == 'Buffy'
def faith = new Person(name:'Faith',id:1) assert faith.id == 1
assert faith.name == 'Faith'
def willow = [name:'Willow',id:2] as Person assert willow.getId() == 2
assert willow.getName() == 'Willow'
The instances buffy and faith are created using the map-based constructor, first setting only the name, and then setting both the name and the id. I’m then able to verify, using Groovy’s built-in assert method (omitting its optional parentheses), that the person’s properties are set correctly.
Incidentally, all the assert statements that seem to be accessing private properties of the class directly really aren’t. Groovy goes through the getter and setter methods provided in the Java class when it looks like properties are being accessed or assigned. I can prove this by modifying the implementation of the getter method to return more than just the name:
public String getName() {
return "from getter: " + name;
}
Now I have to modify each of the asserts to include the string "from getter: " for them to still return true.
The third person, willow, is constructed using the as operator in Groovy. This operator has several uses, one of which is to coerce a map into an object as shown here. In this case the operator instantiates a person and supplies the map as properties for the resulting instance.
Moving on, I can also add the person instances to a Groovy collection, which isn’t all that surprising but has some nice additional benefits. For example, Groovy collections support operator overloading, making it easy to add additional persons and have additional methods for searching:
www.it-ebooks.info
Implementing operator overloading in Java |
67 |
def slayers = [buffy, faith]
assert ['Buffy','Faith'] == slayers*.name assert slayers.class == java.util.ArrayList
def characters = slayers + willow
assert ['Buffy','Faith','Willow'] == characters*.name
def doubles = characters.findAll { it.name =~ /([a-z])\1/ } assert ['Buffy','Willow'] == doubles*.name
Groovy has a native syntax for collections, which simplifies Java code. Putting the references inside square brackets creates an instance of the java.util.ArrayList class and adds each element to the collection. Then, in the assert statement, I used the socalled “spread-dot” operator to extract the name property from each instance and return a list of the results (in other words, the spread-dot operator behaves the same way collect does). By the way, I restored the getName method to its original form, which returns just the attribute value.
I was able to use operator overloading to add willow to the slayers collection, resulting in the characters collection. Finally, I took advantage of the fact that in Groovy, the java.util.Collection interface has been augmented to have a findAll method that returns all instances in the collection matching the condition in the provided closure. In this case the closure contains a regular expression that matches any repeated lowercase letter.
Many existing Java applications have extensive domain models. As you can see, Groovy code can work with them directly, even treating them as POGOs and giving you a poor-man’s search capability.
Now to demonstrate a capability Groovy can add to Java that Java doesn’t even support: operator overloading.
4.2Implementing operator overloading in Java
So far I’ve used the fact that both the + and – operators have been overloaded in the String class. The overloaded + operator in String should be familiar to Java developers, because it’s the only overloaded operator in all of Java; it does concatenation for strings and addition for numerical values. Java developers can’t overload operators however they want.
That’s different in Groovy. In Groovy all operators are represented by methods, like the plus method for + or the minus method for—. You can overload2 any operator by implementing the appropriate method in your Groovy class. What isn’t necessarily obvious, though, is that you can implement the correct method in a Java class, too, and if an instance of that class is used in Groovy code, the operator will work there as well (see figure 4.3).
2Incidentally, changing the behavior of operators this way is normally called operator overloading, because the same operator has different behavior in different classes. Arguably, though, what I’m actually doing is operator overriding. Effectively they’re the same thing here, so I’ll use the terms interchangeably.
www.it-ebooks.info
68 |
CHAPTER 4 Using Groovy features in Java |
Groovy uses methods for operators
A1 + A2 + A3 + ...
Groovy
Java Class A with plus() method
Figure 4.3 Groovy operators are implemented as methods, so if the Java class contains the right methods, Groovy scripts can use the associated operators on their instances.
To demonstrate this I’ll create a Java class that wraps a map. A Department contains a collection of Employee instances and will have a hire method to add them and a layOff method to remove them (hopefully not very often). I’ll implement operator overloading through three methods: plus, minus, and leftShift. Intuitively, plus will add a new employee, minus will remove an existing employee, and leftShift will be an alternative way to add. All three methods will allow chaining, meaning that they’ll return the modified Department instance.
Here’s the Employee class, which is just the Person POJO by another name:
public class Employee { private int id; private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; } public int getId() { return id; }
public void setId(int id) { this.id = id; }
}
Now for the Department class, shown in the following listing, which maintains the employee collection in a Map keyed to the employee id values.
Listing 4.2 A Department with a map of Employees and operator overriding
public class Department { private int id; private String name;
private Map<Integer, Employee> empMap = new HashMap<Integer, Employee>();
Employees indexed by ID
public int getId() { return id; }
public void setId(int id) { this.id = id; } public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Collection<Employee> getEmployees() { return empMap.values(); }
public void hire(Employee e) { empMap.put(e.getId(), e); } public void layOff(Employee e) { empMap.remove(e.getId()); }
public Department plus(Employee e) {
hire(e); |
Overriding operator |
return this; |
methods |
}
Business methods to add and remove Employees
www.it-ebooks.info
Implementing operator overloading in Java |
69 |
||
public Department minus(Employee e) { |
|
|
|
|
|
|
|
layOff(e); |
|
Overriding |
|
return this; |
|
operator |
|
} |
|
methods |
|
public Department leftShift(Employee e) { hire(e);
return this;
}
}
By the way, notice that the plus method doesn’t add two Department instances; rather, it adds an Employee to a Department. Groovy only cares about the name of the method for the operator.3
To test this I’ll use the Spock testing framework. As in chapter 1, I’ll present the test without going into much detail about the Spock framework itself, which I’ll deal with in chapter 6. Fortunately, Spock tests are easy to read even if you don’t know the details. The next listing shows a Spock test that’s focused on just the operator methods.
Listing 4.3 A Spock test to check the operator overloading methods in a Java class
class DepartmentTest extends Specification { private Department dept;
def setup() { dept = new Department(name:'IT') }
def "add employee to dept should increase total by 1"() { given: Employee fred = new Employee(name:'Fred',id:1)
when: dept = dept + fred
then:
dept.employees.size() == old(dept.employees.size()) + 1
}
def "add two employees via chained plus"() { given:
Employee fred = new Employee(name:'Fred',id:1) Employee barney = new Employee(name:'Barney',id:2)
when:
dept = dept + fred + barney
then: dept.employees.size() == 2
}
def "subtract emp from dept should decrease by 1"() { given:
Employee fred = new Employee(name:'Fred',id:1) dept.hire fred
3As an example from the Groovy JDK, the java.util.Date class has a plus method that takes an integer representing the number of days. See also the multiply method in Collection that takes an integer.
www.it-ebooks.info
70 |
CHAPTER 4 Using Groovy features in Java |
when:
dept = dept - fred
then:
dept.employees.size() == old(dept.employees.size()) - 1
}
def "remove two employees via chained minus"() { given:
Employee fred = new Employee(name:'Fred',id:1) Employee barney = new Employee(name:'Barney',id:2) dept.hire fred; dept.hire barney
when: dept = dept - fred - barney
then: dept.employees.size() == 0
}
def "left shift should increase employee total by 1"() { given:
Employee fred = new Employee(name:'Fred',id:1)
when:
dept = dept << fred
then:
dept.employees.size() == old(dept.employees.size()) + 1
}
def "add two employees via chained left shift"() { given:
Employee fred = new Employee(name:'Fred',id:1) Employee barney = new Employee(name:'Barney',id:2)
when:
dept = dept << fred << barney
then: dept.employees.size() == 2
}
}
The Spock test is written in Groovy, so I can use +, –, and << and know that the associated methods will be used, even though they’re implemented in a Java class.
The list of operators that can be overridden in Groovy includes plus, minus, and leftShift, as shown in the listing, and many others as well. You can implement array-like access through an index by implementing getAt, for example. Preand post-increment are implemented through the next and previous methods, respectively. The spaceship operator, <=>, is implemented through compareTo. You can even override the dot operator, believe it or not. The cool part is that you can implement these methods in either POJOs or POGOs, and Groovy will take advantage of them either way.
The next feature of Groovy that simplifies Java is one I’ve taken advantage of several times already: the Groovy JDK.
www.it-ebooks.info