Chapter 2. Groovy by example

This chapter covers

  • Basic Groovy syntax
  • Collections and closures
  • Using the Groovy JDK

As the previous chapter stated, this book isn’t intended to be an exhaustive reference for Groovy, but a certain minimum level of Groovy proficiency is necessary. While some people learn best through short, simple code examples illustrating each concept, others prefer to see basic concepts combined to solve actual problems. For those who prefer snippets of code for each feature I’ve provided appendix B, a Groovy tutorial arranged by feature.

In this chapter, I’ll instead walk through a few small but non-trivial Groovy examples. Hopefully this will help communicate not only the syntax of the language, but some of the standard Groovy idioms as well. Some of the examples will be used again in other chapters in the book, but are used here as illustrations of basic Groovy practices.

2.1. Hello, Groovy

Because every programming language book is required by law to include a “Hello, World!” program, here’s Groovy’s version:

println 'Hello, World!'

In Java you compile with javac and execute the resulting bytecodes with java. In Groovy you can compile with groovyc and execute with groovy, but you don’t actually have to compile first. The groovy command can run with a source code argument, and it will compile first and then execute. Groovy is a compiled language, but you don’t have to separate the steps, though most people do. When you use an IDE, for example, every time you save a Groovy script or class, it is compiled.

The single line shown earlier is a complete program. Unlike with Java, you don’t need to put all Groovy code into a class. Groovy supports running scripts. Everything is still Java bytecodes under the hood, so what happens is that Groovy scripts eventually become the body of the main method in a class that extends groovy.lang.Script.

Note two additional differences in syntax between Groovy and Java:

  • Semicolons are optional. You can add them, and it’s appropriate to use them if you have more than one statement on a line, but they’re not normally necessary.
  • Parentheses are often optional. The println command is actually a method call, and the String is the argument to the method. Because there’s no ambiguity, you can leave out the parentheses. It’s not wrong to include them, though, if you want.
Optional Parentheses

Parentheses are optional until they aren’t. Simple method calls normally omit them, but if there’s any uncertainty, add them. Groovy is all about simplicity and understandability.

Now that the “Hello, World!” example is out of the way, I can move on to something a bit more interesting. One helpful use case for Groovy is that it makes a nice client for RESTful web services like Google Chart.

2.2. Accessing Google Chart Tools

One of the APIs that Google makes available is a RESTful web service known as the Chart API, or, more formally, Google Chart Tools Image API.[1] The documentation is located at https://developers.google.com/chart/image/. The chart tools provide a rich API for JavaScript users, but the inputs are ultimately URLs with query parameters.

1 Google officially deprecated the image charts portion of Google Chart Tools on April 20, 2012. As of summer, 2013, the API still works. It is used here both as a nice, self-contained example and as a simple application that illustrates many Groovy features. Other examples of accessing publicly available services are given throughout the book.

A developer sends a request to the base URL https://chart.apis.google.com/chart and appends query parameters to specify the type of chart, its size, the data, and any labels. Because that API also needs a “Hello, World” example, here’s the URL for a three-dimensional pie chart:

https://chart.apis.google.com/chart?
       cht=p3&
       chs=250x100&
       chd=t:60,40&
       chl=Hello|World

This URL would be all on one line but is written out here (and in the documentation) for illustration purposes. After the base URL, the parameters list the chart type (cht) as a 3D pie chart, the chart size (chs) as 250 by 100 pixels, the chart data (chd) as 60 and 40 in simple text format, and the chart labels (chl) “Hello” and “World.” Type that URL into a browser and the resulting image is returned, as shown in figure 2.1.

Figure 2.1. The Google Chart API “Hello, World” example

The URL shown is hard-wired to produce the chart in figure 2.1. To make this more general, I’ll show how to produce the URL from strings, lists, maps, closures, and builders.

GOAL

Write a Groovy script to generate the “Hello, World” 3D pie chart as a desktop application.

In the process, I’ll discuss

  • String manipulation
  • Lists and maps
  • Processing data using closures
  • Groovy builder classes

In this case I’ll implement the steps in a simple script; later, it could be converted to a class for integration purposes.

2.2.1. Assembling the URL with query string

To start, I need a variable to represent the base URL. 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,” which is discussed in the next chapter. Here, because I know the URL will be contained in a string before I convert it, I’ll declare the variable to be of type java.lang.String:

String base = 'http://chart.apis.google.com/chart?'

Groovy is optionally typed. This means you can specify a type if you want to, or you can use the keyword def if you don’t know or care. There’s some debate among developers about when to use def and when to specify a type. Dierk Koenig, lead author on the superb Groovy in Action (Manning, 2007), says it this way:

Using def

If you think of a type, type it (from Dierk Koenig). In other words, if you know a variable will be a String, or a Date, or an Employee, use that type of variable.

In my own experience, I used to use def a lot, but as time goes by I use it less and less. I agree with Dierk, with the addition that when I’m tempted to use def I often pause a moment and try to think of an actual type before using it. Other developers have other styles, though. That’s the beauty of an optionally typed language: there’s room for everybody.

I now need to append the query parameters to this URL. Rather than write the query string directly I’m going to use a typical idiom for this type of application, which is to build a map and then generate the query string from the map parameters. With that in mind, here’s the map of parameters:

def params = [cht:'p3',chs:'250x100',
              chd:'t:60,40',chl:'Hello|World']

In Groovy you create a map with square brackets, and each entry consists of keys and values separated by a colon. The keys are assumed to be strings by default. The values can be anything. By default, the params variable is an instance of java.util .LinkedHashMap.

Collections

Groovy has native syntax for lists and maps. Map keys are assumed to be strings.

Each corresponding value is surrounded by single quotes. In Groovy, single-quoted strings are instances of java.lang.String. Double-quoted strings are “interpolated” strings, known (unfortunately) as GStrings. I’ll show an example of string interpolation later in this program.

To transform the map into a query string I first need to convert each of the map entries into strings of the form “key=value,” and then I need to concatenate them all together using ampersands as separators.[2] The first step is accomplished by using a special method added to all Groovy collections, known as collect. The collect method takes a closure as an argument, applies the closure to each element of the collection, and returns a new collection containing the results.

2 I also need to URL-encode the map entries, but in this case they’re already fine. In other examples of RESTful web services I’ll demonstrate the encoding process.

Closures are introduced in the next sidebar and discussed extensively throughout the book, but for the moment think of them as blocks of code representing the body of a function, which may take dummy arguments. In the case of collect, when applied to a map, the closure can take either one or two arguments. 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.

To transform the map into a list of key=value pairs, the following two-argument closure works in the collect method:

params.collect { k,v -> "$k=$v" }

In Groovy, if the last argument to any method is a closure you can put the closure outside the parentheses. In this case the only argument to collect is a closure, so even the optional parentheses are omitted.

What is a closure

A closure is a block of code, delimited by curly braces, which can be treated as an object. The arrow notation is used to specify dummy arguments. In the closure applied to the map in the current example, the two dummy arguments are k and v, which represent the key and value of each entry. The expression on the right side of the arrow says to substitute each key and value into a GString separated by an equals sign. This collect method takes each entry in the map and converts it into a string with the key assigned to the value, and produces a list of results.

The result of the operation is shown here:

["cht=p3", "chs=250x100", "chd=t:60,40", "chl=Hello|World"]

This process is illustrated in figure 2.2.

Figure 2.2. Apply collect to a map to convert it into a list, where each entry is transformed into a string.

To create the query string, use another method added by Groovy to collections, called join. The join method takes a single argument that’s used as the separator when assembling the elements into a string. To create a query string, invoke join with an ampersand as an argument:

["cht=p3", "chs=250x100", "chd=t:60,40", "chl=Hello|World"].join('&')

The result is the needed query string, as shown here:

"cht=p3&chs=250x100&chd=t:60,40&chl=Hello|World"

Here’s the entire process so far, taking the base URL and the parameter map, and building the Google Chart URL:

String base = 'http://chart.apis.google.com/chart?'
def params = [cht:'p3',chs:'250x100',
              chd:'t:60,40',chl:'Hello|World']
String qs = params.collect { k,v -> "$k=$v" }.join('&')

The result of all this manipulation is actually a string, not a URL. Before converting it to a URL, let me first verify that the process worked. Normally this would require a test, as discussed extensively in chapter 6 on testing. Here, however, I’ll just use the 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. In this case I’ll use the contains method from the Map interface to check that each of the entries from the params map appears in the query string in the proper format:

params.each { k,v ->
    assert qs.contains("$k=$v")
}
The assert keyword

Groovy asserts are an easy way to verify correctness. An assert returns nothing if the expression is true, and prints a detailed error message if it’s not.

One of the advantages of the join method is that you don’t have to worry about accidentally adding an ampersand at the beginning or end of the string. It only adds the separator internally.

Note also that this is a case where the parentheses (on the join method) are needed. 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. Because I want the join() method (and not getJoin(), which doesn’t exist), I need the parentheses.

2.2.2. Transmitting the URL

The Groovy JDK adds the toURL() method to the String class. As you might imagine, this method converts an instance of java.lang.String into an instance of java.net.URL.

The Groovy JDK

Groovy adds many helpful methods to existing Java library classes. Many, many times I’ve found methods added to, say, String, Date, or Collection that I always wished were in Java all along. The set of methods added by Groovy is known as the Groovy JDK and has its own set of JavaDocs. The Groovy JDK documentation is available via a link from the Groovy home page.

The Groovy JDK is discussed in more detail in chapter 3.

To send an HTTP GET request to a URL and retrieve the results, convert the string to a URL and invoke another Groovy JDK method, the getText() method, added to java.net.URL. In other words, the data on a web page can be retrieved from this code:

url.toURL().text

Here I’m deliberately using the text property of the URL class, knowing that the effect will be to invoke the getText() method. There’s nothing wrong with actually calling getText, but this is more idiomatic Groovy.

Normally this would be exactly the code I want, and I use this technique in some of the examples in the chapters on web services, but in this particular case the result isn’t text. Google Chart takes the URL generated here and returns a binary image, so converting it to text isn’t very helpful.

Groovy Properties

Accessing properties in Groovy automatically invokes the associated getter or setter method.

Next I’ll build a Swing user interface that includes the image in a javax.swing .ImageIcon. This will give me a chance to illustrate a builder, which is a great illustration of Groovy metaprogramming.

2.2.3. Creating a UI with SwingBuilder

In Groovy every class has a metaclass. A metaclass is another class that manages the actual invocation process. If you invoke a method on a class that doesn’t exist, the call is ultimately 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 metaprogramming is a large subject, but here I’ll demonstrate one of its helpful results: the creation of builder classes. In a builder, the call to methodMissing does something specific for that type of builder.

Here I’ll illustrate a Swing builder. This is a class that intercepts names of components and constructs a Swing user interface out of the results. This is actually easier to demonstrate than to explain. I’ll start, however, by adding some imports to the Google Chart script I’ve been constructing so far:[3]

3 That’s another one of the “Duh! Why didn’t we do that all along?” type of revelations that Java developers get all the time when they first learn Groovy. Why is it we only import java.lang in Java programs? Why not import lots of typical packages? Wouldn’t that make coding easier? Groovy says yes.

import java.awt.BorderLayout as BL
import javax.swing.WindowConstants as WC
import groovy.swing.SwingBuilder
import javax.swing.ImageIcon
Automatic imports

You may have noticed that I haven’t yet needed any import statements at all. Java automatically imports the java.lang package. Groovy imports java.lang, as well as java.util, java.io, java.net, groovy.lang, groovy.util, java.math.Big-Integer, and java.math.BigDecimal.3

In this script I’m importing three classes from the Java standard library. The first two imports use the as operator to build an alias for the respective classes. That way the code that uses BorderLayout and WindowConstants can just write BL or WC instead. I’m also adding in the ImageIcon class, which will hold the image returned by Google Chart. The import from the Groovy library is SwingBuilder, which will be used to construct the Swing UI.

The as keyword

The as keyword has several uses, one of which is to provide an alias for imported classes. The as keyword corresponds to the asType method, which was added to java.lang.Object as part of the Groovy JDK.

In the case of SwingBuilder you invoke methods that don’t exist on the builder but that are translated to the corresponding Swing API. For example, by calling the frame method you’re actually instantiating the JFrame class. Giving it a map-like argument of visible:true corresponds to calling the setVisible method with a true argument.

Here’s the code that uses the builder. Each method not in SwingBuilder is translated to the proper method call on the Swing library class:

SwingBuilder.edt {
    frame(title:'Hello, World!', visible:true, pack: true,
        defaultCloseOperation:WC.EXIT_ON_CLOSE) {
            label(icon:new ImageIcon("$base$qs".toURL()),
                constraints:BL.CENTER)
    }
}

The edt method on SwingBuilder builds a GUI using the event dispatch thread. It takes a closure as an argument, and this is where the fun starts. The first statement inside the closure is a call to the frame method, but the fact is, there’s no frame method in SwingBuilder. The builder’s metaclass intercepts that call (via methodMissing) and interprets it as a request to instantiate the javax.swing.JFrame class. The frame method here lists a series of map entries, which are intended to supply values for the title, visibility, and close operation on the JFrame. The builder interprets them as calls to setTitle, setVisible, and setDefaultCloseOperation on the JFrame instance.

After the parentheses there’s another closure. That’s interpreted to mean I’m about to supply components that will be added to the JFrame instance. The next call is to the label method, which of course doesn’t exist. The Swing builder knows to generate a JLabel instance as a result, call its setIcon method with a new ImageIcon holding the image returned by Google Chart, and place the JLabel in the center of a BorderLayout.

Finally, after the frame closure I invoke the pack method on JFrame to make the resulting GUI just big enough to hold the image. The next listing contains the complete script (without the asserts, just to keep the listing short).

Listing 2.1. Building a Swing UI 3D pie chart using Google Chart
import java.awt.BorderLayout as BL
import javax.swing.WindowConstants as WC
import groovy.swing.SwingBuilder
import javax.swing.ImageIcon

def base = 'http://chart.apis.google.com/chart?'

def params = [cht:'p3',chs:'250x100',
              chd:'t:60,40',chl:'Hello|World']

String qs = params.collect { k,v -> "$k=$v" }.join('&')

SwingBuilder.edt {
    frame(title:'Hello, Chart!', pack: true,
       visible:true, defaultCloseOperation:WC.EXIT_ON_CLOSE) {
            label(icon:new ImageIcon("$base$qs".toURL()),
                constraints:BL.CENTER)
    }
}

The resulting image is shown in figure 2.3.

Figure 2.3. The “Hello, World” Swing user interface, holding the image returned by Google Chart

Lessons Learned (Google Chart)

  1. Groovy variables can have types, or you can use the def keyword if you don’t know or don’t care. The keyword def can also be used as a method return type or argument.
  2. Groovy has native syntax for lists and maps. This example used a Groovy map; lists are used in many other examples throughout the book.
  3. Closures are like anonymous function bodies with parameters.
  4. The collect method transforms a collection by applying a closure to each element and returning the resulting list.
  5. The Groovy JDK adds many methods to the standard Java API.
  6. Groovy parsers and builders simplify working with many APIs.

The next example demonstrates Groovy’s XML parsing and generation capabilities, database manipulation, regular expressions, groovlets, and more.

2.3. Groovy Baseball

Figure 2.4 shows a web application I call Groovy Baseball. For a given date during baseball season the page creates a Google Map that displays the results of all Major League Baseball games on that day, using info markers centered on the stadium of the home team. Game results are also listed in a small table. A calendar widget is provided so the user can select an alternative date, which updates the page via an Ajax call.

Figure 2.4. Groovy Baseball is a web application that shows the results of MLB games on a given date.

Some of the functionality is provided by JavaScript via the Google Maps API, which creates the map and adds the markers. Also, the set of game results for a given day is acquired via an Ajax call using the prototype JavaScript library. I’ll show the code for that later. In the meantime I want to highlight the Groovy parts of this application.

The application is simple but it has a fair number of moving parts, so I’ll build it in stages. The first task is to collect the geographic information for the individual MLB stadiums and save it in a database, as illustrated in figure 2.5.

Figure 2.5. Building Groovy Baseball, part 1—geocoding stadium data and saving in DB

In this part of the process, I’ll cover

  • Plain Old Groovy Objects
  • Accessing a RESTful web service
  • The groovy.sql.Sql class

The next step is to access the online box scores and parse the resulting XML files, illustrated in figure 2.6.

Figure 2.6. Building Groovy Baseball, part 2—extracting box score data and creating output POGOs

During this stage, I’ll discuss

  • Reading from a database
  • Downloading information over the internet
  • Parsing XML

Finally, I need to send the resulting data to the view layer in a form it can understand, as shown in figure 2.7.

Figure 2.7. Building Groovy Baseball, part 3—drive system and generate XML

During this stage, I’ll cover

  • Using a groovlet
  • Generating XML

I’ll begin the process with part 1, creating POGOs and saving data in a database.

2.3.1. Database data and Plain Old Groovy Objects

The game results on the web page are centered on the home stadiums of each game. Google Maps places markers based on the latitude and longitude of a given location. Because stadiums don’t tend to move much, it’s worth it to compute those locations ahead of time and save them in some kind of persistence structure. In this case I used a MySQL database, but any database would do.

I’ll build a script here to collect the necessary info for each MLB stadium, compute its latitude and longitude, and store them in a database table. I’ll start with a class to represent a stadium.

The Stadium POGO

In Java we would call this class a Plain Old Java Object, or POJO. In Groovy I’ll use a Plain Old Groovy Object, or POGO, instead. The following listing shows the Stadium class.

Listing 2.2. Stadium.groovy: a POGO to hold stadium information
package beans

class Stadium {
    int id
    String name
    String city
    String state
    String team
    double latitude
    double longitude

    String toString() { "($team,$name,$latitude,$longitude)" }
}

If you’re used to Java, what’s conspicuous here is what’s absent. The lack of semicolons is probably not surprising at this point. What may be a surprise is that there are no public or private access modifiers anywhere. In Groovy, if you don’t specify an access modifier, attributes are assumed to be private, and methods are assumed to be public.[4]

4 That’s another “duh” moment. The default access in Java is “package private,” which means the member is accessible from any other class in the same subdirectory. In roughly 15 years of Java coding I’ve used this access deliberately maybe twice, and both times there were reasonable alternatives. I can understand trying to create some sort of friend access, but why make it the default? Once again, Groovy does what makes sense.

You might also note that there are no constructors in the Stadium class. In Java, if you don’t add a constructor, the compiler gives you a default constructor for free. In Groovy, however, 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.

With this in mind, here’s the first part of the script to populate a database table with the Stadium locations:

def stadiums = []
stadiums <<
    new Stadium(name:'Angel Stadium',city:'Anaheim',state:'CA',team:'ana')
stadiums <<
    new Stadium(name:'Chase Field',city:'Phoenix',state:'AZ',team:'ari')
...
stadiums <<
    new Stadium(name:'Rogers Centre',city:'Toronto',state:'ON',team:'tor')
stadiums <<
    new Stadium(name:'Nationals Park',
    city:'Washington',state:'DC',team:'was')

The stadiums variable is initialized to an empty java.util.ArrayList. The left-shift operator has been implemented in Collection to be an append method, so the rest of the listing instantiates each of the MLB stadiums and appends it to the list.

Each constructor sets the name of the stadium, as well as its city, state, and the three-letter team abbreviation. What are missing are the latitude and longitude values. To supply those I use the Google geocoder, which is another RESTful web service provided by Google, similar to the Google Chart API discussed in the previous section.

POGO

Plain Old Groovy Objects are like POJOs, but with auto-generated getters, setters, and map-based constructors.

Geocoding

The Google Geocoding API is documented at https://developers.google.com/maps/documentation/geocoding/. A geocoder transforms an address into a latitude and longitude. To use the Google geocoder you need to assemble a URL that includes the address information. According to the documentation, the URL has the form

http://maps.googleapis.com/maps/api/geocode/output?parameters

Here the value of output is either xml or json, depending on which type of data you want back.[5] The parameters property contains the address, as well as a sensor value. Here’s the sample from the documentation, which (naturally enough) uses the address of Google’s headquarters in Mountain View, CA:

5 True REST advocates prefer that content negotiation be done in an Accept header on the HTTP request. Here Google does it through separate URIs.

http://maps.googleapis.com/maps/api/geocode/
     xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true_or_
     false

If you intend to access the geocoder using JavaScript, I would say to use json (JavaScript Object Notation) for the output value. Because I’m working with Groovy, and Groovy works well with XML, I’ll use the xml value. The query string contains two parameters. The first is the address, which holds URL-encoded values of the street, city, and state (separated by “,”). The other parameter is called sensor, whose value is true if the request is coming from a GPS-enabled device and false otherwise.

I’ll start the geocoding process by setting a variable to the base URL:

def base = 'http://maps.googleapis.com/maps/api/geocode/xml?'

To assemble the query string, consider a list containing the stadium name, city, and state:

[stadium.name, stadium.city, stadium.state]

Each of these values could potentially include spaces, apostrophes, or other symbols that wouldn’t be legal in a URL. I therefore need to URL-encode each of the values. As I showed in the last section, applying the collect method to a list returns a new list containing the transformed values. In this case, the transformation I want is to use the encode method in the java.net.URLEncoder, as shown:

[stadium.name, stadium.city, stadium.state].collect {
    URLEncoder.encode(it,'UTF-8')
}.join(',')

If you use a closure without specifying a dummy parameter, as here, each element of the list is assigned to a variable called it. The body of the closure executes the static encode method on the name, city, and state, using the UTF-8 encoding scheme. The result is a list containing the encoded values. Finally, the values of the list are joined into a string using “,” as a separator.

That takes care of assembling the address. Forming a complete query string is done using the same closure used in the Google Chart listing. The complete process so far is shown here:

def url = base + [sensor:false,
    address: [stadium.name, stadium.city, stadium.state].collect {
        URLEncoder.encode(it,'UTF-8')
    }.join(',')
].collect {k,v -> "$k=$v"}.join('&')
Building a query string

The combination of parameter map, collect closure, and join method is a convenient way to build a query string. A developer can store the parameters in any order, or accept them from the user (as in a Grails application), and turn them into a query string with a minimum of effort.

The result of all this string manipulation is to create a full URL, similar to the one shown in the previous example, which can be transmitted to the Google geocoder.

Now comes the fun part. The geocoder returns a fairly extensive block of XML (not shown here, but available online in the Google geocoder documentation at https://developers.google.com/maps/documentation/geocoding/#XML). Processing the XML using Java would be quite verbose. Fortunately, XML is nothing to Groovy. The entire process of transmitting the URL to the Google geocoder and parsing the result into a DOM tree takes one line:

def response = new XmlSlurper().parse(url)

Groovy has two classes for parsing XML. One is called XmlParser, and the other is XmlSlurper. Both convert XML into a DOM tree. The underlying structure and process are somewhat different, but from a practical point of view the slurper is more efficient and takes less memory, so that’s what I’ll use here. Extracting the results I need is a simple matter of walking the tree. I could paste in a copy of the XML output to show you the structure, but it’s easy enough to understand if you see the Groovy parsing code:

stadium.latitude = response.result[0].geometry.location.lat.toDouble()[6]
stadium.longitude = response.result[0].geometry.location.lng.toDouble()

6 Try that in Java. Nothing sells Groovy to Java developers like working with XML.

In other words, the slurper returns the root of the DOM tree, which is assigned to a variable called response. The root has a child element called result, which has a child called geometry, which has a child called location, which then has two children, one called lat and the other called lng. Sometimes the geocoder returns multiple results, so I used the array index 0 on result to use only the first one. Because everything in XML is a String and I want to assign the results to double values in Stadium, I finally use the toDouble method added to String to do the conversion.

Parsing XML

Whether you use an XmlParser or an XmlSlurper, extracting data from XML means just walking the tree.[7]

7 Parsing (actually, slurping) JSON is just as easy. The book source code for chapter 2 includes another example that accesses and parses JSON data.

The following listing shows the complete Geocoder class, with its method fillInLatLng that takes a Stadium as an argument and fills in the latitude and longitude values.

Listing 2.3. Geocoder.groovy, which uses the Google geocoder to compute lat and lng
class Geocoder {
    def base = 'http://maps.googleapis.com/maps/api/geocode/xml?'
    def fillInLatLng(Stadium stadium) {
        def url = base + [sensor:false,
            address: [stadium.name, stadium.city, stadium.state].collect {
                URLEncoder.encode(it,'UTF-8')
            }.join(',')
        ].collect {k,v -> "$k=$v"}.join('&')
        def response = new XmlSlurper().parse(url)
        stadium.latitude =
            response.result[0].geometry.location.lat.toDouble()
        stadium.longitude =
            response.result[0].geometry.location.lng.toDouble()
        return stadium
    }
}
The groovy.sql.Sql class

Returning to the original problem, I want to store the stadium information in a database. I’m now going to take advantage of a very useful class in the Groovy library, groovy.sql.Sql. This class connects to a database and allows you to execute SQL against it. To begin the process, here’s how the Sql class is instantiated:

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, the username and password, and the driver class. The result is a connection to the database. Next, I drop the stadium table if it already exists:

db.execute "drop table if exists stadium;"

The execute method takes a SQL string and runs it against the database. Here again, I’m taking advantage of the optional parentheses.

The next step is to create the table to hold the stadium information:

db.execute '''
    create table stadium(
        id int not null auto_increment,
        name varchar(200) not null,
        city varchar(200) not null,
        state char(2) not null,
        team char(3) not null,
        latitude double,
        longitude double,
        primary key(id)
    );
'''

The three single quotes represent a multiline string in Groovy. Three double quotes would be a multiline GString, which I could use for parameter substitution, but they’re not needed in this particular case.

Now that the table has been constructed it’s time to populate the table with stadium data:

Geocoder geo = new Geocoder()
stadiums.each { s ->
    geo.fillInLatLng s
    db.execute """
        insert into stadium(name, city, state, team, latitude, longitude)
        values(${s.name},${s.city},${s.state},
               ${s.team},${s.latitude},${s.longitude});
    """
}

After instantiating the geocoder I walk through each stadium in the collection, assigning each to the dummy variable s. For each one, after computing the latitude and longitude, I execute an insert statement contained within three double quotes, where I substitute the values I need from the stadium using the standard ${...} notation.

All that remains is to do some kind of sanity check to make sure that the values received are reasonable. Here are some assert statements to do just that:

assert db.rows('select * from stadium').size() == stadiums.size()
db.eachRow('select latitude, longitude from stadium') { row ->
    assert row.latitude > 25 && row.latitude < 48
    assert row.longitude > -123 && row.longitude < -71
}

The first assert statement checks that the total number of rows in the table matches the number of stadiums in the collection. The next statement invokes the eachRow method on the connection, selecting just the latitude and longitude, and assigning the dummy variable row to each of the rows in the result set. The two contained assert statements verify that the latitudes are between 25 and 48 and that the longitudes are between -123 and -71.

The Sql class

The groovy.sql.Sql class removes almost all the ceremony surrounding raw JDBC and adds convenience methods as well.

The complete script is shown in the next listing.

Listing 2.4. populate_stadium_data.groovy

This script collects all the latitude and longitude values for each MLB stadium, creates a database table to hold them, and populates the table. It only has to be run once, and the application can then use the table. In the process of reviewing the code I used a Stadium POGO, a list, a couple of collect methods with closures, an example that used the URLEncoder class from Java in a Groovy script, and database manipulation through the groovy.sql.Sql class.

The next step is to collect box score data from a site maintained by Major League Baseball, and generate XML information that can be sent to a view page.

2.3.2. Parsing XML

Major League Baseball continuously updates the results of baseball games online. The information is kept in XML format in links descending from http://gd2.mlb.com/components/game/mlb/.

On the site the games are arranged by date. Drilling down from the base URL requires links of the form "year_${year}/month_${month}/day_${day}/", where the year is four digits, and the month and day are two digits each. The games for that date are listed as individual links. For example, figure 2.8 shows links for each game played on May 5, 2007.[8]

8 By an astonishing coincidence, May 5 is my son’s birthday.

Figure 2.8. Links to baseball games played on May 5, 2007

The link for each individual game has the form

gid_${year}_${month}_${day}_${away}mlb_${home}mlb_${num}

The year, month, and day values are as expected. The values for away and home are three-letter lowercase abbreviations for each team, and the value of num represents the game number that day (1 for the first game, 2 for the second game of a double header). The links for each game contain a series of files, but the one I’m interested in is called boxscore.xml.

To retrieve the box score information I’ll create a class called GetGameData. This class will have attributes for the base URL and the team abbreviations, as shown. The next listing shows a portion of this class.

Listing 2.5. A portion of GetGameData, showing the attributes and initialization

The key-value pairs in the abbrevs map hold the three-letter abbreviations for each team and the city name, respectively.

The next step is to process the actual box scores. Here’s some sample data, taken at random. The random date I’ve chosen is October 28, 2007.[9] The next listing shows the box score in XML form, truncated to show the typical elements without showing them all.

9 Just happens to be the day the Red Sox won the World Series in 2007.

Listing 2.6. boxscore.xml: the box score from game 4 of the 2007 World Series
<boxscore game_id="2007/10/28/bosmlb-colmlb-1" game_pk="224026"
    home_sport_code="mlb" away_team_code="bos" home_team_code="col"
    away_id="111" home_id="115" away_fname="Boston Red Sox"
    home_fname="Colorado Rockies"
    away_sname="Boston" home_sname="Colorado" date="October 28, 2007"
    away_wins="5" away_loss="0" home_wins="0" home_loss="5" status_ind="F">
    <linescore away_team_runs="4" home_team_runs="3"
        away_team_hits="9" home_team_hits="7" away_team_errors="0"
        home_team_errors="0">
        <inning_line_score away="1" home="0" inning="1" />
        <inning_line_score away="0" home="0" inning="2" />
        ...
        <inning_line_score away="0" home="0" inning="9" />
    </linescore>

    <pitching team_flag="away" out="27" h="7" r="3" er="3" bb="3"
        so="7" hr="2" bf="37" era="2.60">
        <pitcher id="452657" name="Lester" pos="P" out="17" bf="23"
            er="0" r="0" h="3" so="3" hr="0" bb="3" w="2" l="0" era="0.00"
            note="(W, 2-0)" />
        <pitcher id="434668" name="Delcarmen" pos="P" out="2" bf="4"
        er="1" r="1" h="2" so="1" hr="1" bb="0" w="0" l="0" era="9.00"
        note="(H, 2)" />
        ...
        <pitcher id="449097" name="Papelbon" pos="P" out="5" bf="5"
            er="0" r="0" h="0" so="1" hr="0" bb="0" w="0" l="0" era="0.00"
            note="(S, 4)" />
    </pitching>
    <batting team_flag="home" ab="34" r="3" h="7" d="2" t="0" hr="2"
        rbi="3" bb="3" po="27" da="18" so="7" avg=".216" lob="12">
        <batter id="430565" name="Matsui" pos="2B" bo="100" ab="4" po="3"
            r="0" bb="0" a="5" t="0" sf="0" h="1" e="0" d="1" hbp="0"
            so="1" hr="0" rbi="0" lob="2" fldg="1.000" avg=".286" />
        <batter id="466918" name="Corpas" pos="P" bo="101" ab="0" po="0"
            r="0" bb="0" a="1" t="0" sf="0" h="0" e="0" d="0" hbp="0"
            so="0" hr="0" rbi="0" lob="0" fldg="1.000" avg=".000" />
...
    </batting>
    <pitching team_flag="home" out="27" h="9" r="4" er="4" bb="1"
        so="4" hr="2" bf="34" era="6.91">
        <pitcher id="346871" name="Cook" pos="P" out="18" bf="23" er="3"
            r="3" h="6" so="2" hr="1" bb="0" w="0" l="2" era="4.50"
            note="(L, 0-2)" />
...
    </pitching>
    <batting team_flag="away" ab="33" r="4" h="9" d="2" t="0" hr="2"
        rbi="4" bb="1" po="27" da="8" so="4" avg=".322" lob="10">
        <batter id="453056" name="Ellsbury" pos="CF-LF" bo="100" ab="4"
            po="3" r="1" bb="0" a="0" t="0" sf="0" h="2" e="0" d="1"
            so="1" hr="0" rbi="0" lob="2" fldg="1.000" avg=".450" />
        <batter id="456030" name="Pedroia" pos="2B" bo="200" ab="4" po="1"
            r="0" bb="0" a="4" t="0" sf="0" h="0" e="0" d="0" hbp="0"
            so="0" hr="0" rbi="0" lob="2" fldg="1.000" avg=".227" />
...
    </batting>
...
</boxscore>

The root element is <boxscore>, which has several attributes. It has a child element called <linescore>, which shows the scoring in each inning. Then there are <pitching> and <batting> elements for the home team and away team, respectively.

This isn’t a terribly complex XML file, but if you had to process it using Java the code would quickly get involved. Using Groovy, as shown previously, all you have to do is walk the tree.

Parsing this data uses the same approach as parsing the geocoded data in the last section. Here I need to assemble the URL based on the month, day, and year and then parse the box score file:

def url = base + "year_${year}/month_${month}/day_${day}/"
def game = "gid_${year}_${month}_${day}_${away}mlb_${home}mlb_${num}/
      boxscore.xml"
def boxscore = new XmlSlurper().parse("$url$game")

After parsing the file I can walk the tree to extract the team names and scores:

def awayName = boxscore.@away_fname
def awayScore = boxscore.linescore[0].@away_team_runs
def homeName = boxscore.@home_fname
def homeScore = boxscore.linescore[0].@home_team_runs

The dots represent child elements, as before, and this time the @ symbols imply attributes.

Parsing XML

Dots traverse from parent elements to children, and @ signs represent attribute values.

XML, regular expressions, and the Groovy Truth

To do some slightly more interesting processing, consider determining the winning and losing pitchers. The XML contains that information in a note attribute of the pitcher element. I can process that using a regular expression, assuming it exists at all:

def pitchers = boxscore.pitching.pitcher
pitchers.each { p ->
    if (p.@note && p.@note =~ /W|L|S/) {
        println "  ${p.@name} ${p.@note}"
    }
}

First I select all the pitcher elements for both teams. Then I want to examine the pitcher elements to find out who won and lost and if anyone was assigned a save. In the XML this information is kept in a note annotation in the pitcher element, which may or may not exist.

In the if statement, therefore, I check to see if a note attribute is present. Here I’m using the “Groovy Truth,” which means that non-null references evaluate to true. So do non-empty strings or collections, non-zero numbers, and, of course, the boolean literal true. If the note element is present, I then use the so-called “slashy” syntax to check to see if the note matches a regular expression: p.@note =~ /W|L|S/. If there’s a match I print out the values.

Generating Game Results

Before I show the complete method I need one more section. For the Groovy Baseball application I’m not interested in console output. Rather, I want to assemble the game results into a format that can be processed in the view layer by JavaScript. That means I need to return an object that can be converted into XML (or JSON).

Here’s a class called GameResult for that purpose:

class GameResult {
    String home
    String away
    String hScore
    String aScore
    Stadium stadium

    String toString() { "$home $hScore, $away $aScore" }
}
Closure Return Values

The last expression in a closure is returned automatically.

This POGO is a simple wrapper for the home and away teams and the home and away scores, as well as for the stadium. The stadium is needed because it contains the latitude and longitude values I need for the Google Map. The following listing now shows the complete getGame method in the GetGameData class shown in listing 2.5.

Listing 2.7. The getGame method in GetGameData.groovy

The method uses an XmlSlurper to convert the XML box score into a DOM tree, extracts the needed information, and creates and returns an instance of the Game-Result class.

There’s one other method in the GetGameData class, which is the one used to parse the web page listing the games for that day. This is necessary because due to rain-outs and other postponements there’s no way to know ahead of time which games will actually be played on a given day.

Parsing HTML is always a dicey proposition, especially because it may not be well-formed. There are third-partly libraries to do it,[10] but the mechanism shown here works. It also demonstrates regular-expression mapping in Groovy. The getGames method from GetGameData is shown in the next listing.

10 See, for example, the NekoHTML parser at http://nekohtml.sourceforge.net/.

Listing 2.8. The getGames method from GetGameData

The =~ method in Groovy returns an instance of java.util.regex.Matcher. The parentheses in the regular expression are groups, which let me extract the away team abbreviation, the home team abbreviation, and the game number from the URL. I use those to call the getGames method from listing 2.7 and put the results into a collection of GameResult instances.

Testing

All that’s left is to test the complete GetGameData class. A JUnit test to do so is shown in the next listing.

Listing 2.9. GetGameDataTests.groovy: a JUnit 4 test case

This is a standard JUnit 4 test case. I have much more to say about Groovy testing capabilities in chapter 6 on testing, but here’s a simple example. There’s nothing inherently Groovy about this class except that (1) I used the map-based constructor to instantiate the fixture, (2) optional parentheses were left out wherever possible, and (3) no explicit public or private keywords were needed. Otherwise, this is just a regular test case, and it works as usual.

What have I discussed in this section?

  • Groovy has a convenient syntax for maps.
  • XML parsing and extracting data are easy, as in the previous section.
  • Groovy has a slashy syntax for regular expressions.
  • Groovy classes work with JUnit tests.

There’s one final piece of the puzzle needed, which is the driver used to call the system for each date. I use a “groovlet” for this purpose in the next section.

2.3.3. HTML builders and groovlets

The classes used so far access XML box score information and convert it into a series of game result objects. For the view layer, however, I need objects in a form that can be processed by JavaScript. There are several ways to accomplish this, but one of them is to use an XML builder to write out the information in XML form.[11]

11 The data could just as easily be written in JSON format. Other JSON examples are used throughout the book.

Generating XML

The standard Groovy library includes a class called groovy.xml.MarkupBuilder,[12] which is one of several builders (much like the SwingBuilder shown at the beginning of this chapter) in the standard library. Each of the builders intercepts method calls that don’t exist (so-called pretended methods) and constructs nodes out of them to make a tree structure. The tree is then exported appropriately for that kind of builder.

12 I would bet that if this class were created today, it would be called XmlBuilder instead.

This is actually easier to see than to explain. Consider the GameResult class from the previous section, which held the home and away team names and scores and a reference to a Stadium object. Here’s the syntax for creating XML out of that object:

MarkupBuilder builder = new MarkupBuilder()
builder.games {
    results.each { g ->
        game(
            outcome:"$g.away $g.aScore, $g.home $g.hScore",
            lat:g.stadium.latitude,
            lng:g.stadium.longitude
        )
    }
}

After instantiating the MarkupBuilder and calling the reference builder, the second line invokes the games method on it. It may not look like a method, but recall that in Groovy, if a closure is the last argument to a method it can be placed outside the parentheses, and here I’m using optional parentheses. Of course, there’s no method called games in MarkupBuilder. That makes it a pretended method, and the builder intercepts that method call and creates a node out of it. In a MarkupBuilder that means it will ultimately create an XML element called games. The closure syntax implies that the next elements will be child elements of games.

Inside the closure the code iterates over each contained result, assigning it to the dummy variable g. For each GameResult g, the builder creates an element called game. The parentheses on game imply that game will contain attributes. In this case, each game has an outcome, a lat, and a lng.

Here’s the output of the MarkupBuilder:

<games>
  <game outcome='Boston Red Sox 4, Colorado Rockies 3'
    lat='39.7564956' lng='-104.9940163' />
</games>

If there had been a dozen games that day there would a <game> element for each one of them. The bottom line is that in Groovy, generating XML is about as easy as parsing it.

Server-side processing with groovlets

To drive the whole system I need a server-side component that receives the needed date and calls the GetGameData class to retrieve the games, which are then returned in XML form. Groovy has a component known as a groovlet to make that all easy.

A groovlet is a script that is executed by a class called groovy.servlet.GroovyServlet. This class is part of the Groovy standard library. Like any servlet, it needs to be declared in the web.xml deployment descriptor for a web application and mapped to a particular URL pattern. In this case I chose the pattern *.groovy. Here’s the excerpt from the deployment descriptor:

<servlet>
    <servlet-name>GroovyServlet</servlet-name>
    <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>GroovyServlet</servlet-name>
    <url-pattern>*.groovy</url-pattern>
</servlet-mapping>

The Groovy Baseball application will therefore send all URLs ending in .groovy through the GroovyServlet, which will execute them. Groovlets executed this way are deployed as source code rather than as compiled classes under WEB-INF.[13] Groovlets also contain a set of implicit objects representing the request, response, input parameters, and more.

13 The details are discussed in chapter 10 on web applications.

The following listing contains the complete content of the groovlet that drives the Groovy Baseball system.

Listing 2.10. GameServlet.groovy: a groovlet for Groovy Baseball

The groovlet can set response headers, here setting the output to XML. Input parameters populate a map of strings called params, which can be accessed in the usual way. The URL requires two-digit days and two-digit months, so a zero is prepended when necessary. After retrieving the games for that date the output is generated using the implicit MarkupBuilder. There’s no need to instantiate a MarkupBuilder in this case, because groovlets already contain one, called html.

The groovlet is called from a regular web page, using a URL of the form http://.../groovybaseball/GroovyService.groovy?month=10&day=28&year=2007. The XML data is written to the output stream, which can then be processed by JavaScript.

Lessons learned (Groovy Baseball)

  1. POGOs have private attributes and public methods by default. Public getters and setters are auto-generated for each attribute.
  2. POGOs include a map-based constructor that can be used to set any or all of the attributes in any combination.
  3. Closures and methods in Groovy return their last evaluated expressions automatically.
  4. The XmlSlurper class makes parsing XML simple and returns the root of the resulting DOM tree. Values can be extracted by walking the tree.
  5. The MarkupBuilder class produces XML.
  6. The groovy.sql.Sql class is a simple façade for dealing with relational databases.
  7. Groovlets are simple Groovy scripts that respond to HTTP requests.
  8. All Groovy exceptions are unchecked.

The rest of the system is just HTML and JavaScript, so it’s beyond the scope of a Groovy discussion. The complete source code for the application is contained in the GitHub repository for the book.

2.4. Summary

This chapter is a tutorial on Groovy for Java developers, using example applications rather than a series of features. What’s remarkable is how much Groovy simplifies the code. POGOs are a minimal yet more flexible version of POJOs. The groovy.sql.Sql class makes JDBC practical for reasonably small applications. The Groovy JDK adds many convenience methods, like toURL and getText, which make existing Java classes easier to use. The combination of maps, closures, and the join method makes it simple to build URLs for web services. Finally, the difference between working with XML in Java and working with XML in Groovy is staggering. Whenever I have to work with XML in any form I always look for a way to add a Groovy module to handle the details.

In the next chapter we’ll examine mechanisms to integrate Java and Groovy together in more detail.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.36.38