- •brief contents
- •contents
- •foreword
- •preface
- •acknowledgments
- •About the author
- •about this book
- •Roadmap
- •Code conventions and downloads
- •Author Online
- •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
The Java approach, part 2: Maven |
105 |
(continued)
The built-in Ant tasks are very low level. As a result, Ant build files quickly grow long and complex and involve a lot of repetition.
For all these reasons and others Ant was ripe for a higher-level replacement. That role was filled by the Maven project, which is either a blessing or a curse depending on your experiences with it.
5.4The Java approach, part 2: Maven
I’m going to confess up front that Maven is hard to talk about rationally. Its best features (establishing a conventional project layout, managing dependencies, providing a rich plugin architecture) are also considered some of its worst features (difficult to work outside its conventions, hard to manage transitive dependencies, the whole “download the internet” problem). I can honestly say I’ve never encountered a technology that’s both common in the industry and yet loathed with the white-hot intensity of a thousand suns.6 Bring up Maven in a group of developers, and someone will refuse to discuss “the M word.” Yet, at the same time, somebody else will quietly say that they can make it do anything and don’t understand what all the fuss is about.
My own experience isn’t so black-and-white. I find that if a project was designed using Maven from the beginning, it tends to work well with the system. It’s also hard to use that system without Maven. On the other hand, adding Maven to a system that wasn’t started with it can be quite painful. In addition, friends have also assured me that once a system gets above a certain size, the whole process becomes an unmanageable mess.
Perhaps the best way to stay above the fray is to say that Maven has a highly opinionated API. To be successful you have to do things the Maven way. Plus, like Ant, you’re coding your build in XML, which is never easy. The multi-project build capabilities are awkward, too.7
I will note that the standard Maven project layout (shown in figure 5.2) has become common throughout the industry. Also, people may complain about Maven’s approach to dependency management, but I haven’t seen anything dramatically better. Gradle (the proposed replacement, discussed later in this chapter) uses Maven repositories and Ivy dependency management and suffers from the same “download the internet” problem. Dependency management is just hard, no matter how you approach it.
Returning (at last) to the core theme of this book, the goal of this section is to show you how to incorporate Groovy into Maven builds. There are two ways to do that. I’ll start with the Groovy-Eclipse plugin and then build the same application using the GMaven project.
6Except possibly for every Microsoft technology ever.
7Admittedly that doesn’t sound terribly “above the fray,” but at least I’m trying.
www.it-ebooks.info
106 |
CHAPTER 5 Build processes |
Figure 5.2 Standard Maven project structure used for the application in this section. Compiled sources are in src/main/ java, and tests reside in src/test/java.
5.4.1The Groovy-Eclipse plugin for Maven
The Groovy-Eclipse compiler plugin (http://mng.bz/2rHY) is a standard compiler plugin for Maven. It emerged from the effort to build a good Eclipse plugin for Groovy that worked with combined Groovy and Java projects. The Maven plugin is a way to take advantage of that effort, whether you plan to use the Eclipse IDE or not.
To demonstrate its use I’ll build a small project that accesses the Yahoo! Weather web service and reports on the current conditions. This is easy enough to do in Java but becomes particularly simple in Groovy.
The Yahoo! Weather web service (http://developer.yahoo.com/weather/) provides weather information in the form of an RSS feed. The web service is accessed from a URL of the form
http://weather.yahooapis.com/forecastrss
The URL has two parameters, one required and one optional. The required parameter is w, a so-called WOEID (Where On Earth ID), that Yahoo uses to identify a location. The other parameter is u, which is used to specify the temperature units in Fahrenheit (f, the default) or Celsius (c). For unknown reasons, there’s no way to programmatically look up a WOEID. Instead Yahoo! directs you to its own weather page and suggests you search for your city.
A simple HTTP GET request to the proper URL returns an XML response in RSS form. A sample is included on Yahoo!’s web page.
Suppose I decided to build a simple application to retrieve the current weather conditions based on this service. Maven recommends that you specify a particular artifact to begin the project, so I’ll start with the classic maven-archetype-quickstart:
> mvn archetype:generate –DgroupId=mjg –DartifactId=weather –DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.0-SNAPSHOT –Dpackage=mjg
MAVEN ARCHETYPES The Groovy-Eclipse plugin uses regular Java archetypes and adds Groovy functionality. The GMaven approach in the next section includes a basic archetype to get started.
www.it-ebooks.info
The Java approach, part 2: Maven |
107 |
This generates a Java project with the standard layout, meaning the source code directory is src/main/java and the testing directory is src/test/java. The quick start archetype includes a trivial App.java and AppTest.java in those directories, respectively. The generator also adds a standard Maven POM file in the root directory, whose only dependency is on JUnit, as shown in the next listing.
Listing 5.8 The Maven pom.xml file for a standard Java project
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>mjg</groupId>
<artifactId>weather</artifactId>
<packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>weather</name> <url>http://maven.apache.org</url> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
The only change I’ve made so far from the standard is to upgrade the JUnit dependency to 4.10 from 3.8.1.
To do the actual work I need a class to send the request to Yahoo and parse the response, and a POJO to hold the resulting weather information. Starting with the POJO, for a given city, region, and country I want to store the condition, temperature, wind chill, and humidity. The web service returns a lot more information than this, but this will suffice to get started.
POJOs are simple containers for data, so the constructors, getter and setter methods, and any necessary overrides are mostly clutter. I can therefore simplify my life if I use a POGO instead, as shown in the following listing.
Listing 5.9 Weather.groovy, a POGO to hold weather results from the web service
package mjg
class Weather { String city String region String country String condition String temp
www.it-ebooks.info
108 |
CHAPTER 5 Build processes |
String chill
String humidity
String toString() {
"""
Weather for $city, $region, $country: Condition : $condition
Temperature: $temp
Wind Chill : $chill Humidity : $humidity
"""
}
}
The toString method is a way to produce formatted output. Groovy’s multiline string makes it particularly easy.
The other class I need is a parser for the web service. Because all I need is a GET request I can use the parse method in the XmlSlurper class as usual and drill down the resulting DOM tree to get the results I want. That’s pretty simple, too, as shown in the following listing.
Listing 5.10 YahooParser.groovy, which accesses and parses the weather service
package mjg
class YahooParser {
final static String BASE = 'http://weather.yahooapis.com/forecastrss?'
Weather getWeather(String woeid) {
def root = new XmlSlurper().parse(BASE + "w=$woeid") Weather w = new Weather(
city:root.channel.location.@city,
region:root.channel.location.@region,
country:root.channel.location.@country,
condition:root.channel.item.condition.@text,
temp:root.channel.item.condition.@temp,
chill:root.channel.wind.@chill,
humidity:root.channel.atmosphere.@humidity
)
}
}
Given a WOEID, the service builds the URL and accesses the web service, parses the resulting RSS, and returns an instance of the Weather class with all the relevant fields populated.
To complete the program I need a driver, which I can write as a Groovy script. That’s a one-liner, unless I want to allow the client to specify a WOEID on the command line:
def woeid = args.size() ? args[0] : '2367105' println new YahooParser().getWeather(woeid)
www.it-ebooks.info
The Java approach, part 2: Maven |
109 |
The default WOEID in the script is for Boston, MA, and it’s stored in RunDemo.groovy. In order to demonstrate the differences when both Java and Groovy sources are present together, I also added a Java class to access the web service in the file RunInJava.java:
public class RunInJava {
public static void main(String[] args) { String woeid = "2367105";
if (args.length > 0) woeid = args[0]; YahooParser yp = new YahooParser(); System.out.println(yp.getWeather(woeid));
}
}
Now comes the interesting part: how do I get Maven to handle all the Groovy code? The Groovy-Eclipse plugin requires two additions to the POM file. First I need to add Groovy as a dependency:
<dependencies>
...
<dependency>
<groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.1.5</version>
</dependency>
</dependencies>
Next I need to add the Groovy-Eclipse plugin in a build section below the dependencies:
<build>
<plugins>
<plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version>
<configuration> <compilerId>groovy-eclipse-compiler</compilerId>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId> <artifactId>groovy-eclipse-compiler</artifactId> <version>2.7.0-01</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
With both of these additions Maven will compile and use Groovy code appropriately, except for one rather strange oddity. Normally I would add my Groovy classes to src/ main/groovy and any Groovy tests to src/test/groovy. According to the plugin documentation, I can do that only if (1) there’s at least one Java class in src/main/java or
(2) I add a lot more XML to specify the additional source directories.
www.it-ebooks.info
