Groovy Weather: A New Groovy Example at Java.net

One of the main goals of Making Java Groovy is to show Java developers how much Groovy can make their lives easier. To that end, I just published a blog post (through Manning’s account) over a Java.net entitled, Groovy Weather: POGOs, Gson, and Open Weather. The blog post comes with a coupon code for 45% off the book. 🙂

(Spoiler: it’s “kousenjn“, but if you’re going to use it at least drop by the blog link.)

Another spoiler: it’s freakin’ cold outside. That’s partly what my blog post is about — consuming JSON data from Open Weather Map and displaying it at the console. That’s not terribly difficult, but the real value added comes from using Google’s Gson parser to convert the JSON objects into Groovy.

If you’re new to Groovy, the blog post shows a lot of detail. Regular readers of my blog, however, probably are at least comfortable with the language, so I thought I’d summarize the good parts here.

First, I need to show the JSON data, so I know what to map to.

(Er, “to what to map”? Ugh. Winston Churchill was once criticized for ending a sentence with a preposition. His reply was, “Madam, that is nonsense up with which I will not put.” That’s one of those possibly apocryphal stories that I don’t want to try to verify because I like it too much.)

Here’s the trivial Groovy expression to download the data:

import groovy.json.*

String url = 'http://api.openweathermap.org/data/2.5/weather?q=marlborough,ct'
String jsonTxt = url.toURL().text
println JsonOutput.prettyPrint(jsonTxt)

I’m using the static prettyPrint method of JsonOutput to make viewing the results easier. As a minor complaint, I have to mention that every time I use prettyPrint, I’m surprised it doesn’t actually print. It just formats the JSON data. I still need to print the result. That seems rather misleading to me.

Anyway, here’s the result:

{
    "coord": {
        "lon": -72.46,
        "lat": 41.63
    },
    "sys": {
        "message": 0.193,
        "country": "United States of America",
        "sunrise": 1389096985,
        "sunset": 1389130583
    },
    "weather": [
        {
            "id": 800,
            "main": "Clear",
            "description": "Sky is Clear",
            "icon": "01d"
        }
    ],
    "base": "gdps stations",
    "main": {
        "temp": 260.41,
        "humidity": 33,
        "pressure": 1025,
        "temp_min": 258.71,
        "temp_max": 262.15
    },
    "wind": {
        "speed": 1.54,
        "deg": 0
    },
    "clouds": {
        "all": 0
    },
    "dt": 1389130569,
    "id": 4835003,
    "name": "Hartford",
    "cod": 200
}

The current temperature is buried inside the object, in the “temp” property of the “main” subobject. I could just parse this using a JsonSlurper, but instead I decided to map the whole thing using Gson.

Gson wants a class structure that maps to the JSON hierarchy. Here’s mine, which I just stuffed into a single Groovy class called Model.groovy.

class Model {
    Long dt
    Long id
    String name
    Integer cod

    Coordinates coord
    Main main
    System sys
    Wind wind
    Clouds clouds
    Weather[] weather
}

class Main {
    BigDecimal temp
    BigDecimal humidity
    BigDecimal pressure
    BigDecimal temp_min
    BigDecimal temp_max
}

class Coordinates {
    BigDecimal lat
    BigDecimal lon

    String toString() { "($lat, $lon)" }
}

class Weather {
    Integer id
    String main
    String description
    String icon
}

class System {
    String message
    String country
    Long sunrise
    Long sunset
}

class Wind {
    BigDecimal speed
    BigDecimal deg
}

class Clouds {
    BigDecimal all
}

I added the data types based on reading the docs, which are pretty thin, and following the nested structure of the JSON data. The names of the classes aren’t important. It’s the property names that have to match the keys in the JSON maps for deserialization to work.

Using Gson is almost trivial. All I need is the fromJson method in the Gson class:

import groovy.transform.*
import com.google.gson.Gson

@ToString(includeNames=true)
class Model {
  ...
}

String url = 'http://api.openweathermap.org/data/2.5/weather?q=marlborough,ct'
String jsonTxt = url.toURL().text
Gson gson = new Gson()
println gson.fromJson(jsonTxt, Model)

Everything is converted to the Groovy class structure, just as expected.

Before printing the results, though, I need to do some data manipulation. If you looked at the current temperature value, you might have noticed it’s in Kelvin, of all things. As a US-based developer, I need to convert that to Fahrenheit.

def convertTemp(temp) {
    9 * (temp - 273.15) / 5 + 32
}

The time fields are based on “Unix time”, which measures seconds in the current epoch (beginning January 1, 1970 GMT). Java’s Date class has a constructor that takes a long representing milliseconds from the beginning of the epoch.

def convertTime(t) {
    new Date(t*1000)  // Unix time in sec, Java time in ms
}

Finally, the wind speed is in “meters per second” and I want “miles per hour”. When I was in high school (and dinosaurs roamed the Earth), I learned about the Factor Label method, which meant I could memorize a single length conversion and calculate any other.

def convertSpeed(mps) {
    // 1 m/sec * 60 sec/min * 60 min/hr * 100 cm/m * 1 in/2.54 cm * 1 ft/12 in * 1 mi/5280 ft
    mps * 60 * 60 * 100 / 2.54 / 12 / 5280
}

I added all of these to the Model class, and some getters to use them.

class Model {
  // ... as before ...

  def getTime() { convertTime dt }
  def getTemperature() { convertTemp main.temp }
  def getLow() { Math.floor(convertTemp(main.temp_min)) }
  def getHigh() { Math.ceil(convertTemp(main.temp_max)) }
  def getSunrise() { convertTime sys.sunrise }
  def getSunset() { convertTime sys.sunset }
  def getSpeed() { convertSpeed wind.speed }
}

Finally, here’s my nice, formatted toString method to print the results (also a part the Model class):

String toString() {
    """
    Name         : $name
    Time         : $time
    Location     : $coord
    Weather      : ${weather[0].main} (${weather[0].description})
    Icon         : http://openweathermap.org/img/w/${weather[0].icon}.png
    Current Temp : $temperature F (high: $high F, low: $low F)
    Humidity     : ${main.humidity}%
    Sunrise      : $sunrise
    Sunset       : $sunset
    Wind         : $speed mph at ${wind.deg} deg
    Cloudiness   : ${clouds.all}%
    """
}

I should mention that the Weather attribute of the Model class is a collection. Presumably that’s for when there are multiple weather stations associated with a given location. In the source code repository (linked below), I used Groovy’s each method to iterate over them all. Here I’m just using the first one.

The driver for the system is:

import com.google.gson.Gson

class OpenWeather {
    String base = 'http://api.openweathermap.org/data/2.5/weather?q='
    Gson gson = new Gson()

    String getWeather(city='Marlborough', state='CT') {
        String jsonTxt = "$base$city,$state".toURL().text
        gson.fromJson(jsonTxt, Model).toString()
    }
}

I like Groovy’s default arguments for methods. If I invoke the getWeather method without arguments (or as the weather property in the usual idiom), then the result is for “Marlborough, CT”. Otherwise I supply a city and a state and they’re used instead.

Clearly this needs to be tested, or at least the converters so. Here’s a Spock test for them:

import spock.lang.Specification

class ModelSpec extends Specification {
    Model model = new Model()

    def 'convertTemp converts from Kelvin to F'() {
        expect:
        32 == model.convertTemp(273.15)
        212 == model.convertTemp(373.15)
    }

    def 'convertSpeed converts from meters/sec to miles/hour'() {
        expect:
        (2.23694 - model.convertSpeed(1)).abs() < 0.00001
    }

    def 'convertTime converts from Unix time to java.util.Date'() {
        given:
        Calendar cal = Calendar.instance
        cal.set(1992, Calendar.MAY, 5)
        Date d = cal.time
        long time = d.time / 1000  // Java time in ms, Unix time in sec

        when:
        Date date = model.convertTime(time)

        then:
        d - date < 1
    }
}

The mps to mph value I got from putting “1 meter per second in mph” into Google, which gave me the proper result.

Just to make sure the calls are working properly, here are a couple of tests for the Model class.

import spock.lang.Specification

class OpenWeatherSpec extends Specification {
    OpenWeather ow = new OpenWeather()

    def 'default city and state return weather string'() {
        when:
        String result = ow.weather
        println result

        then:
        result  // not null is true in Groovy
        result.contains('41.63')
        result.contains('-72.46')
    }

    def "The weather is always great in Honolulu"() {
        when:
        String result = ow.getWeather('Honolulu', 'HI')
        println result

        then:
        result
        result.contains('21.3')
        result.contains('-157.86')
    }
}

Here’s a script to run the whole system:

OpenWeather ow = new OpenWeather()
println ow.weather  // called Marlborough, CT, but really Hartford

// Home of Paul King, co-author of _Groovy in Action_ and my personal hero
println ow.getWeather('Brisbane','Australia')

// Home of Guillaume Laforge, head of the Groovy project
// (also one of my heroes, along with Dierk Koenig, Graeme Rocher, Tom Brady, David Ortiz, ...)
println ow.getWeather('Paris','France')

// Have to check the weather in Java, right?
println ow.getWeather('Java','Indonesia')

// Any weather stations in Antarctica?
println ow.getWeather('', 'Antarctica')

Here are the results at the moment (Jan 7, 2014, at about 5:45pm EST):

Name         : Marlborough
Time         : Tue Jan 07 17:43:10 EST 2014
Location     : (41.63, -72.46)
Weather      : Clear (Sky is Clear)
Icon         : http://openweathermap.org/img/w/01n.png
Current Temp : 8.312 F (high: 11.0 F, low: 5.0 F)
Humidity     : 35%
Sunrise      : Tue Jan 07 07:16:25 EST 2014
Sunset       : Tue Jan 07 16:36:23 EST 2014
Wind         : 3.4448818898 mph at 258 deg
Cloudiness   : 0%

Name         : Brisbane
Time         : Tue Jan 07 17:36:03 EST 2014
Location     : (-27.47, 153.02)
Weather      : Clouds (broken clouds)
Icon         : http://openweathermap.org/img/w/04n.png
Current Temp : 78.566 F (high: 82.0 F, low: 77.0 F)
Humidity     : 46%
Sunrise      : Mon Jan 06 14:00:36 EST 2014
Sunset       : Tue Jan 07 03:47:48 EST 2014
Wind         : 10.51360057266 mph at 121 deg
Cloudiness   : 80%

Name         : Paris
Time         : Tue Jan 07 17:39:23 EST 2014
Location     : (48.85, 2.35)
Weather      : Clear (Sky is Clear)
Icon         : http://openweathermap.org/img/w/01n.png
Current Temp : 52.682 F (high: 54.0 F, low: 51.0 F)
Humidity     : 81%
Sunrise      : Tue Jan 07 02:42:36 EST 2014
Sunset       : Tue Jan 07 11:11:33 EST 2014
Wind         : 8.052970651396 mph at 220 deg
Cloudiness   : 0%

Name         : Batununggal
Time         : Tue Jan 07 17:43:35 EST 2014
Location     : (-6.96, 107.65)
Weather      : Clouds (scattered clouds)
Icon         : http://openweathermap.org/img/w/03n.png
Current Temp : 68.5904 F (high: 69.0 F, low: 68.0 F)
Humidity     : 91%
Sunrise      : Mon Jan 06 17:40:32 EST 2014
Sunset       : Tue Jan 07 06:10:58 EST 2014
Wind         : 2.9303865426 mph at 200 deg
Cloudiness   : 44%

Name         :
Time         : Tue Jan 07 17:43:35 EST 2014
Location     : (-78.33, 20.61)
Weather      : Clear (Sky is Clear)
Icon         : http://openweathermap.org/img/w/01d.png
Current Temp : -20.2936 F (high: -20.0 F, low: -21.0 F)
Humidity     : 38%
Sunrise      : Tue Jan 07 16:43:35 EST 2014
Sunset       : Wed Jan 08 04:43:35 EST 2014
Wind         : 15.770400858984 mph at 15.0058 deg
Cloudiness   : 0%

So, yeah, it’s cold outside. I also freely admit it’s a little weird seeing those non-US temperatures converted to Fahrenheit.

The Gradle build file for this system is almost trivial:

apply plugin:'groovy'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.2.1'
    compile 'com.google.code.gson:gson:2.2.4'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
}

All of this code can be found in the book’s Github repo. I added this to Chapter 2: Groovy by Example. Chapter 2 now uses a multi-project Gradle build, so it doesn’t look exactly that the one shown here, but it works the same way.

This process is easy enough to replicate for any RESTful service that returns JSON data. The time consuming part, if any, is writing the POGO structure to capture the JSON tree, but at least with POGOs the result is pretty simple.

————-

Now for a few personal notes. First, the latest Amazon review is simply beautiful. It’s from a person who knew Java and didn’t expect much from Groovy, but found out how much it could help. That’s exactly what I’m trying to achieve. In fact, I’ve been amazed that every single Amazon reviewer, no matter how much he or she did or didn’t like specific aspects of the book, definitely understood the goal and appreciated it.

Second, I have a few new projects in the works, but I think I can mention one of them. I’m currently generating a series of screencasts for the book, which I’m calling “Making Java Groovy: The Director’s Cut”. The goal is to discuss why the book was written as it was — what was included, what was left out, what has changed since its publication in the Fall of 2013, and anything else I think might be interesting or helpful. Everyone at Manning has been very supportive of the effort so far. I hope to make at least the first set of screencasts available soon.

Third, I’ll be giving a talk at the Boston Grails Users’ Group on Thursday, January 9, at 7pm. I hope to bring some books to add to the raffle, along with an admission to the NFJS event in the Boston area in March. If you’re in the area, please drop by and say hello.

Fourth, if you’ve read this far, you deserve a reward! If you use the coupon code “kousen37” at Manning.com, you can get 37% off any book at Manning. 🙂

Oh, and in case you didn’t hear me celebrating, I’m now officially a 2013 JavaOne Rock Star. Thank you very much to everyone who recommended me.

Finally, those of you battling the so-called “polar vortex”, stay warm! It’s cold here, but at least we’re still above 0 (F, not C). Next week I’m planning to brave the winds of Caradhras, otherwise known as central Minnesota. Brr.

As the saying goes, follow the great advice found on the side of a mayonnaise jar: “Keep cool but don’t freeze”.

Making Java Groovy: A Celebrity (Non-)Endorsement

Several of my book author friends on the No Fluff, Just Stuff tour told me that writing a book would open doors for me. That doesn’t explain, though, why I seem to insist on climbing through windows.

I mean, writing Making Java Groovy put me on the NFJS tour, helped me become a speaker at Gr8 conf, DevNexus, and JavaOne*, gave me lots of new professional contacts, and means my author page at Amazon has some actual content. Sweet. But I’m always wondering what else I can do with it that most authors are too experienced, too normal, or maybe just too sane to consider.

*My evals so far from my presentation at JavaOne have been really good, so I’m still hoping that I’m awarded JavaOne Rock Star status. I totally want to party like a JavaOne rock star, party like a JavaOne rock star, etc.

For example, there are certain people that I only know through Twitter or other online media who I would live to meet. As one of my Silly Marketing Ideas (abbreviated SMI, as opposed to my Serious Marketing Ideas, abbreviated SMI), I thought if I could get one or more of them to endorse my book, that would be seriously cool, not to mention whatever it did for sales.

Of course, the silly part is that most of the people I want to approach aren’t even developers, to say nothing of Java people. If you place all this in a larger context, I call this entire effort an “experiment in accidental celebrity”, meaning I want to achieve D-List Twitter Celebrity status, but only if I can do the whole thing as a gag and blog about it here.

To that end, a couple weeks ago I chose four of the people I would love to meet somehow and sent them individualized emails.

That’s a long shot, since I’m sure the people I contacted get flooded with emails on a regular basis. Still, I had to start somewhere, and it’s not like I have a budget for this or anything.

What I do have is a set of author copies for my book. I showed the set in my last blog post when I arranged them on the coffee table in our living room:
coffeetablebook

After giving away a few to friends and family, I decided that I could part with a few more if they acted as my introduction to unsuspecting victims I wanted to meet. If they ultimately decided they loved the book, or even just the joke itself, and wanted to endorse it, so much the better.

One of the people I contacted was a person I truly enjoy and admire, Wil Wheaton. If you lost track of him after Stand By Me and his classic role as the much-loathed-but-not-his-fault-he-was-stuck-with-those-lines Wesley Crusher from Star Trek: The Next Generation, then you’ve missed a lot (here’s his IMDB page). His blog is excellent. I read his Just a Geek book, which I really enjoyed, and his Memories of the Future, Volume 1 book is among the funniest I’ve ever read. My wife and I have also enjoyed his guest appearances on Eureka, Criminal Minds, Leverage, and, of course, The Big Bang Theory.

He’s also great on Twitter, under the handle @wilw. If you follow him, be sure to also follow his wife, @AnneWheaton. I often say that following Wil is fun and following Anne is fun, but following both together is wonderful.

As I said, I ultimately mustered up my courage (which meant suppressing the nauseous feeling that this was not only particularly silly, but actively embarrassing), dug up his email address, and sent him a message. To summarize the message, I:

  • explained who I was and that I had just published a book
  • mentioned I was a fan and personalized the evidence enough to prove I wasn’t kidding
  • explained that this was a Silly Marketing Idea which he was free to ignore but gave me a hopefully non-creepy opportunity to contact him directly
  • asked him for a mailing address so I could send him a signed copy of my book

I didn’t get anything back right away, but I didn’t really expect to. I imagine he is inundated with email, and I’m sure I was one of millions. I expected that I would have to resend it roughly once a week to see what would happen**.

**I got that idea from The Shawshank Redemption. Andy sent a request for money to the town council once a week for two years until they finally realized he wouldn’t go away and sent some money for his prison library. I’ve always wondered about that, by the way. Did he send the same letter, or did he rewrite it each time? Did he vary the wording or the basic content? How did he keep from letting his annoyance or anger at being ignored seep into the letter? Neither the movie nor the original Stephen King story get into details.

Then, however, fate took hold. On 10/17, Anne Wheaton tweeted:

Anne Witchon ‏@AnneWheaton 17 Oct
Texas tweety buddies! 10/17 Austin: bit.ly/WilPSAustin
10/18 Houston: bit.ly/WilPSHouston
10/20 Dallas: bit.ly/WilPSDallas”

I knew I was traveling to Dallas this week, but it suddenly occurred to me his show was on Sunday and I was flying in on the same day. I checked my flight arrangements and realized I could actually make the show. I quickly bought a ticket online (not many were left, but I only needed one), donned my Star Trek polo shirt (the blue science officer one I use when I’m making Groovy presentations about the Spock testing framework) and drove over to the Granada Theater.

I brought a book with me, of course, but I didn’t sign it because I wasn’t sure I actually was going to meet Wil. I’m not the sort of person who goes to conventions, or even events like this, so I don’t really know how they work. In fact, I almost didn’t go at all***, because I was very tired after the flights and didn’t particularly want to leave my hotel room after I arrived. I went anyway, of course, or this would be a much shorter blog post.

***I know my wife Ginger will be shocked, SHOCKED! to hear that.

The show was called Wil Wheaton vs Paul and Storm. Here’s the poster:

wilwheatonpaulandstorm_400

I have to admit that I didn’t know Paul and Storm at all, except from Wil or Anne’s tweets about them. I knew they were a musical / comedy duo who wrote their own funny songs, but that’s about it.

Here’s the marquee from the theater:
granadatheater

The show was a blast. Before the show, the theater used a Twitter site that showed all tweets directed to the theater. I don’t remember many, but among the funniest were things like:

– What does the FOX say? Sorry we canceled Firefly

– Told my blind date I was the guy with the beard wearing a funny T-shirt. Crap.

– [Paul of Paul and Storm] Hanging out with this girl who was supposed to meet some guy with a beard and a funny T-shirt. Playing it cool…

Wil introduced the show, saying that any and all recordings were allowed as long as they were released under the Creative Commons license (“and if you don’t know what that is, ask a nerd and he’ll explain it to you in far more detail than you ever wanted”), but not to use flash photography because that distracted the performers and not to do anything to interfere with other audience members.

“In short,” he said, using one of his signature catch phrases, “Don’t Be A Dick, and you’ll be fine”.

Paul and Storm did their set first, and it was fantastic. Intelligent people tend to be quite clever but not comedian-level funny****. Both Paul and Storm were both. I highly recommend that you check out their website, listen to their music, buy their stuff, and go see them if at all possible. They are masters of the “X is my Y cover band” style of joke. I’d mention some here, but I don’t want to include any spoilers and I can’t remember them anyway. I guess I’ll just have to go see them again.

****I often put myself in that category.

After the intermission, Wil came out and told personal stories in the form of a comedy monologue. There were great (and no spoilers here), but the biggest impression I get from him is I never realized, even reading his Twitter feed, just how filthy he is. Even in his 40s he still looks like this pleasant guy next door, and you don’t expect him to be quite so foul. It works, though. 🙂

Wil and Paul and Storm then came out together and did a few songs filled with random riffs that the audience loved. Here is a link to some pictures from the show.

Here’s a Vine video Paul posted.

Finally, here’s a screen capture from that video showing the audience, with a circle showing me:

audience_at_wilvps

During the entire show, I was holding my book, hoping that I’d get a chance to meet Wil and explain why I was foisting it on him. I also had idle daydreams of him letting me take a picture of him holding it and tweeting it to his 2.4 million followers (!), thinking if only 1% of them bought it…

After the show, a line quickly formed in the lobby. It turned out that the rules were that Wil and Paul and Storm would sign autographs for everybody who wanted one, but that if you wanted a posed picture you’d have to get in the back of the line again.

I didn’t have anything to sign, but the couple in front of me in line gave me a few blank cards from a game called The World of Munchkin, which now gives me a perfect opportunity to show the difference between Paul and Storm and Wil Wheaton. Here are my autographs from Paul and Storm:
paulandstorm

They’re basically illegible, sure, but I’m not one to talk. My signature is appalling. Anyway, here’s what Wil drew:
bagof
Yup. That’s the Wil Wheaton Experience right there.

While he was signing, I gave him my book. I told him I had just published it (“Hey, congratulations!” “Thanks!”), showed him where I had already signed it for him and Anne (“Cool!”), told him that I had sent him email if he was curious about me, and told him what a fan I was (yeah, I talked fast).

The rest of the conversation went something like this:

Wil: Will this help me learn this stuff if I don’t know anything about Java?

Me: Uh, some. Maybe. I’ll be happy to answer questions about it online if you like. That’s what I do. I teach technical training classes. I’ll come back around for a picture, but I was wondering if maybe I could get a picture with the book in it…

Wil: No, I can’t do that. That would be like an endorsement.

Me: (sound of internal imaginary bubble popping) I completely understand.

And I do understand. As I say, this is for fun, and if something else comes of it, fine, but if not, maybe I made an impression on him that the average fan doesn’t.

I did come back around for a picture. As we were setting up, I said:

Me: I meant to tell you that the front matter is pretty funny.

Wil: Great!

Me: Also, do you mind if I blog about this?

Wil: Of course!

Here’s my picture with my new buddies Paul and Storm and Wil and Anne Wheaton:
wilandanneandme
From left to right, that’s Storm, Anne, Me, Wil, and Paul. Or maybe Paul, Anne, Me, Wil, and Storm. (Just kidding. It’s the former. I think. Hey, I bought the USB drive with all their music on it, so I can make one joke at their expense.)

I have to say, I’m still basking in the afterglow. They show was fun, meeting them was fun, and getting the picture was even better. I’m really glad I gave him the book, too, though I have to point out that this does not in any way constitute an endorsement of Making Java Groovy (which is getting really great reviews at Amazon).

Yet. Now if I can only get him to read it…

(Upon re-reading this, it’s starting to sound a bit too mercenary. Look, all I want is my book to last a while, and maybe to make enough money that I can keep doing what I’m doing. It took me 40 years to find my dream job, and now that I have it I don’t want to give it up. Also, it’s kind of fun coming up with silly ideas like this, just to see if I can bring myself to actually do some of them. My next post will get back to technical issues, I promise.)

Making Java Groovy, the Listicle

[Note: I accidentally published this the first time before it was ready. It’s updated now.]

Unlike my previous posts about Making Java Groovy, this post concerns one of my Silly Marketing Ideas (SMI)*. The goal is to break out from the Groovy community and start selling to the larger Java community. Actually, I’m already doing that, so the real goal is to sell to developers in general. Of course, that’s not really enough. The real goal is to have my book become the first Java/Groovy integration book ever to make the New York Times Best Sellers list.

*Naturally, those are as opposed to my Serious Marketing Ideas (SMI).

That’s a somewhat ambitious goal, and to achieve it I’m going to have to appeal to the public at large, including people who have no interest whatsoever in a developer book. The goal of this particular post is to give people who never plan to write a line of code a reason to buy my book anyway.

That brings me to the listicle.

What is a listicle, you say? According to Google, which in this case means Wikipedia, a listicle is “a short form of writing using a list as its thematic structure, but is fleshed out with sufficient copy to be published as an article”. It also has some pretty serious negative connotations on the web, as they’re often displayed one item at a time in order to increase page views, which is basically evil.

This post won’t be like that. Or, rather, it does use a list as the thematic structure, but I’ll put the whole thing here and I’m not making any money from it no matter how it’s presented. In fact, this post was inspired by a tweet in my timeline:

Dan Woods (@danveloper)
“Groovy and Java are like Frodo and Samwise Gamgee, [headed into Mordor] … trying to defeat … clueless managers …” – @kenkousen lol
10/10/13 3:44 PM

(See https://twitter.com/danveloper/status/388389569189539840 for the full version.)

That’s an extract from the preface to my book. At some point I’ll post the whole preface here, but for the record, the full version of that quote is:

Groovy and Java are like Frodo and Samwise Gamgee, headed into the black depths of Mordor, battling privation and despair, desperately trying to defeat the horrible programming challenges that await them, as well as any orcs, Nazgul, or clueless managers they might encounter along the way.

That’s a little dark. Plus, I have no idea what the Ring of Power is in this analogy, or why you’d want to destroy it*.

*I do hope that if you’re holding a print copy of the book (that is, dead-treeware), no Ents were involved.

To segue into the actual purpose of this post, later in the front matter there is a section called “about this book”, and in it I say that, “I expect you are a developer and are at least comfortable with Java”. This is followed by an extensive footnote where I claim that you don’t really need to be a developer, since there are many reasons to buy my book that don’t involve actually reading it.

At long last, that brings me to the listicle that is the real subject of this post. In case you’re already thinking about what to buy your mom for the holidays, let me present:

The Top Ten Ways To Take Advantage Of Making Java Groovy
That Don’t Involve Actually Reading It

10. Prop open a door (a classic)

doorstop

That’s not a great picture of propping open a door, but hopefully it worked for you.

9. Level a table

propuptable

Note especially the use of a coaster to keep from damaging the book cover.

8. Shield against assorted non-lethal weaponry

shield

This is my son Xander’s first appearance in my blog, and note that he managed to stay hidden. The nerf weapon is visible, though.

7. Looks good on your bookshelf

bookshelf

I don’t collect print books like I used to. Most of my books are ebooks these days. Still, I was able to fill out the shelf with as many Groovy-related books as I could find, some of which are getting rather dated. I have the updated books, they’re not in print yet. If you’re in the Groovy community, how many can you identify?

6. Excellent coffee table book

coffeetablebook

This involved actually clearing off the coffee table in the living room, which made my wife Ginger happy.

5. Ugly bug / insect / big, hairy arachnid pulverizer

I’ve never taken a Vine video before. This is my first attempt, which I downloaded as an mp4 file. Hopefully it’s visible.

4. Make a book fort

bookfort

I got Xander to build the fort around a black cat we have on display for Halloween.

3. Learn balance by walking with it on your head

Remember when that used to be a thing? Neither do I, never having been to a “finishing” school, but it made for a decent video.

2. Fan yourself by riffling through the pages

To be honest, Ginger said it was better to just wave the book at herself than breeze through the pages, but so be it.

1. Impress your parents

This one requires some explanation. As the parent of a young adult (and as the child of high-achieving parents), I understand that parents are sometimes more concerned with your financial future than you are. You and I are often more interested in what we’re doing than whether it’s for a productive future.

The point is, sometimes we need a way to convince our parents that we’re making progress, thereby getting the time and space we need to do what we have to do. I’d like to believe my book can help you do that. My resume has always opened doors for me, and if somehow you can take advantage of it, so much the better. So use my book as a way to convince whoever needs convincing that you’re moving into a bold, new, financially lucrative area, whether you really are or not.

Besides, the footnotes are still pretty funny. 🙂

Making Java Groovy: ratpack, MongoDB, and Chuck Norris

Before I get to the good parts of this post (the technical content), let me take care of a few marketing issues.

First, as I mentioned in my last post, I gave my “Making Java Groovy” presentation at JavaOne last week. If you were there and you haven’t completed your Session Surveys, please do so. I really want to Party Like a (JavaOne) Rock Star, Party Like a (JavaOne) Rock Star, etc., though I expect in reality that would involve sharing a quiet dinner at home with my wife. She probably wouldn’t appreciate it if I trashed the room, too, at least not more than it’s already trashed*.

*Yeah, my 21 year old son still does live at home, why do you ask?

That did give me the opportunity to see my book on the shelf of a book store for the first time:

book_shelf_photo

I should also mention that as of 9/30, if you buy the book at Manning, you can now get all the electronic formats (pdf, mobi, and epub).

Finally, I got my first Amazon review today, and was so good I walked around with a smile all day.

Now on to the real content. In my Groovy presentations, I often like to access a RESTful web service and show (1) how easy it is to make a GET request using Groovy, and (2) how to use a JsonSlurper to parse a JSON response to get the information inside it. For this purpose my default site is ICNDB, the Internet Chuck Norris Database.

That site has caused me problems, though. First, it resulted in my first ever take down notice from a lawyer, which I’ll show in a moment. Seriously. As part of my Android presentations at No Fluff, Just Stuff conferences I build a simple app to access that site, parse the results (using a GSON parser, since I don’t have Groovy available) and update the display. When running, the app looks like:

icndb_emulator_image

To make it easy for the attendees of my Android talks to play with, I uploaded the app to the Google Play store. That was fun until I received the following email, from an address I’ll leave out:

Dear Sir/Madam:

Patton Boggs LLP represents Carlos Ray Norris, aka Chuck Norris, the famous actor and celebrity.

We are contacting you because we recently learned that you have developed and are distributing a software application that uses Mr. Norris’s name and/or image without authorization on Google Play.

Mr. Norris appreciates all of his fans. However, the unauthorized use of his name and/or image severely harms my client and jeopardizes his existing business relationships.

Mr. Norris owns legal rights in his name and image which includes copyright, trademark, and publicity rights (the “Norris Properties”). He uses the Norris Properties regularly in his own business endeavors. Therefore we have asked Google to remove your application from Google Play because it violates Mr. Norris’s intellectual property rights.

We request that you (1) immediately stop developing and distributing “Chuck Norris” applications; (2) remove all “Chuck Norris” applications that you have developed or control from all websites under your control; and (3) do not use Mr. Norris’s name or image, or any cartoon or caricature version of Mr. Norris’s name or image for any endeavor, including in connection with software applications, without Mr. Norris’s permission.

Thank you for honoring Mr. Norris’s legal rights. Please contact me if you have questions.

Sincerely, …

A few points immediately come to mind:

  • Carlos Ray Norris? Seriously? I had no idea.
  • Does it matter that my app is free and only consumes data from a publicly available web site? Apparently not.
  • If I changed the icon and replaced the words “Chuck” and “Norris” everywhere with the words “Patton” and “Boggs” (the name of the law firm :)), could I upload it again?

I’m still not exactly sure what I was doing wrong, but of course I did not want to cross Carlos Ray “Chuck” Norris, to say nothing of his lawyers. But after talking to a few people, I decided to ignore the letter, at least until any money was involved.

I haven’t heard anything since, but about a week later Google Play took down my app.

So be it. If you want the source code, though, check out the ICNDB project in my GitHub repository. It’s mostly just a demonstration of how to Spring’s RestTemplate, Google’s GSON parser, and an Android AsyncTask together. In the repo is another branch that uses a Java Timer to refresh the joke every 10 seconds.

The real question is, why haven’t the barracudas lawyers gone after the original ICNDB site? Worse, what happens to my poor app (and, more importantly, my presentation) when they do?

I decided my best course of action was to download as many of the jokes as possible and be ready to serve them up locally in case I need them. That, at long last, brings me to the technical part of this blog post.

The original web site returns jokes in JSON format. That means storing them in a MongoDB database is trivial, because Mongo’s native format is BSON (binary JSON) and I can even query on the joke properties later.

How do I grab all the jokes? There’s no obvious query in the API for that, but there is a work around. If I first access http://api.icndb.com/jokes/count, I can get the total number of jokes. Then there is a URL called http://api.icndb.com/jokes/random/:num, which fetches num random jokes. According to the web site, it returns them in the form:

{ "type": "success", "value": [ { "id": 1, "joke": "Joke 1" }, { "id": 5, "joke": "Joke 5" }, { "id": 9, "joke": "Joke 9" } ] }

The value property is a list containing all the individual jokes.

To work with MongoDB, I’ll use the driver from the GMongo project. It follows the typical Groovy idiom, in that it takes an existing Java API (in this case, the ugly and awkward Java driver for Mongo) and wraps it in a much simpler Groovy API. As a beautiful illustration of the process, here’s an excerpt from the com.gmongo.GMongo class:

class GMongo {

  @Delegate
  Mongo mongo

  // ... lots of overloaded constructors ...

  DB getDB(String name) {
    patchAndReturn mongo.getDB(name)
  }

  static private patchAndReturn(db) {
    DBPatcher.patch(db); return db
  }
}

Note the use of the @Delegate annotation, which exposes all the methods on the existing Java-based Mongo class through the GMongo wrapper.

Based on that, here’s my script to download all the jokes and store them in a local MongoDB database:

import groovy.json.*
import com.gmongo.GMongo

GMongo mongo = new GMongo()
def db = mongo.getDB('icndb')
db.cnjokes.drop()

String jsonTxt = 'http://api.icndb.com/jokes/count'.toURL().text
def json = new JsonSlurper().parseText(jsonTxt)
int total = json.value.toInteger()

jsonTxt = "http://api.icndb.com/jokes/random/${total}".toURL().text
json = new JsonSlurper().parseText(jsonTxt)
def jokes = json.value
jokes.each {
    db.cnjokes << it
}
println db.cnjokes.find().count()

Note the nice overloaded left-shift operator to add each joke to the collection.

I’ve run this script several times and I consistently get 546 total jokes. That means the entire collection easily fits in memory, a fact I take advantage of when serving them up myself.

My client needs to request a random joke from the server. I want to do this in a public forum, so I’m not interested in any off-color jokes. Also, the ICNDB server itself offers a nice option that I want to duplicate. If you specify a firstName or lastName property on the URL, the joke will replace the words “Chuck” and “Norris” with what you specify. I like this because, well, the more I find out about Carlos Ray the more I wish I didn’t know.

Here’s my resulting JokeServer class:

package com.kousenit

import java.security.SecureRandom
import com.gmongo.GMongo
import com.mongodb.DB

@Singleton
class JokeServer {
    GMongo mongo = new GMongo()
    Map jokes = [:]
    List ids = []
    
    JokeServer() {
        DB db = mongo.getDB('icndb')
        def jokesInDB = db.cnjokes.find([categories: [$ne : 'explicit']])
        jokesInDB.each { j ->
            jokes[j.id] = j.joke
        }
        ids = jokes.keySet() as List
    }
    
    String getJoke(String firstName = 'Chuck', String lastName = 'Norris') {
        Collections.shuffle(ids)
        String joke = jokes[ids[0]]
        if (!joke) println "Null joke at id=$id"
        if (firstName != 'Chuck')
            joke = joke.replaceAll(/Chuck/, firstName)
        if (lastName != 'Norris')
            joke = joke.replaceAll(/Norris/, lastName)
        return joke
    }
}

MongoDB has a JavaScript API which uses qualifiers like $ne for “not equals”. The Groovy API wrapper lets me add those as keys in a map supplied to the find method.

I added the @Singleton annotation on the class, though that may be neither necessary or appropriate. I may want multiple instances of this class for scaling purposes. I’ll have to think about that. Let me know if you have an opinion.

I’m sure there’s an easier way to get a random joke out of the collection, but I kept running into issues when I tried using random integers. This way works, though it’s kind of ugly.

The getJoke method uses Groovy’s cool optional arguments capability. If getJoke is invoked with no arguments, I’ll use the original version. If firstName and/or lastName are specified, I use the replaceAll method from the Groovy JDK to change the value in the joke. Strings are still immutable, though, so the replaceAll method returns a new object and I have to reuse my joke reference to point to it. I actually missed that the first time (sigh), but that’s what test cases are for.

Speaking of which, here’s the JUnit test (written in Groovy) to verify the JokeServer is working properly:

package com.kousenit

import static org.junit.Assert.*

import org.junit.Before
import org.junit.Test

class JokeServerTest {
    JokeServer server = JokeServer.instance

    @Test
    public void testGetJokeFirstNameLastName() {
        String joke = server.getJoke('Patton', 'Boggs')
        assert !joke.contains('Chuck')
        assert !joke.contains('Norris')
        assert joke.contains('Patton')
        assert joke.contains('Boggs')
    }

    @Test
    public void testGetJoke() {
        assert server.joke
    }
}

I can invoke the getJoke method with zero, one, or two arguments. The basic testGetJoke method uses zero arguments. Or, rather, it accesses the joke property, which then calls getJoke() using the usual Groovy idiom.

Now I need to use this class inside a web server, and that gave me a chance to dig into the ratpack project. Ratpack is a Groovy project and I’ve checked out the source code from GitHub, but there’s a simpler way to create a new ratpack application based on the Groovy enVironment Manager (gvm) tool.

First I used gvm to install lazybones:

> gvm install lazybones

Then I created my ratpack project:

> lazybones create ratpack icndb

That creates a simple structure (shown in the ratpack manual) with a Gradle build file. The only modification I made to build.gradle was to add “compile 'com.gmongo:gmongo:1.0'” to the dependencies block.

Here’s the file ratpack.groovy, the script that configures the server:

import static org.ratpackframework.groovy.RatpackScript.ratpack

import com.kousenit.JokeServer

JokeServer server = JokeServer.instance

ratpack {
    handlers {
        get {
            String message
            if(request.queryParams.firstName || request.queryParams.lastName) {
                message = server.getJoke(
                    request.queryParams.firstName, 
                    request.queryParams.lastName)
            } else {
                message = server.joke
            }
            response.headers.set 'Content-Type', 'application/json'
            response.send message
        }
    }
}

I configured only a single “get” handler, which is invoked on an HTTP GET request. I check to see if either a firstName or lastName query parameter is supplied, and, if so, I invoke the full getJoke method. Otherwise I just access the joke property. I made sure to set the Content-Type header in the response to indicate I was returning JSON data, and sent the message.

If I type ./gradlew run from the command line, ratpack starts up its embedded Netty server on port 5050 and serves jokes on demand. Here’s a picture of the default request and response (using the Postman plugin in Chrome):

postman_icndb_default

Here is the response if I specify a first and last name:

postman_patton_boggs

There you have it. I wanted to write a test inside ratpack to check my hander, but the infrastructure for that appears to still be under development. I tried to imitate one of the samples and extend the ScriptAppSpec class, but that class isn’t in my dependent jars. I also tried to follow the Rob Fletcher’s mid-century ipsum example, but while I was able to create the test, it failed when running because it couldn’t find the ratpack.groovy file inside the ratpack folder, since it expected it to be in the root. The right approach would probably be to turn the contents of my get block into a separate Handler, because the manual talks about how to test them, but I haven’t done that yet.

As they say, if you live on the cutting edge, sometimes you get cut. I expect that will all settle down by 1.0.

For my presentations, I now need to update my Android client so that it accesses http://localhost:5050 and understands that the JSON coming back is a bit simpler than that served up by the original server. That’s beyond the scope of this (way too long) blog post, however.

I haven’t yet committed the application to GitHub, but I will eventually. In the meantime I just have to hope that my new app also doesn’t run afoul of Carlos, Ray, Patton, and Boggs, Esq.

Making Java Groovy: JavaRanch this week

This week Making Java Groovy is the featured book at JavaRanch, where I’ve been a member for many years. JavaRanch is yet another of Kathy Sierra’s (and Bert Bates’s) contributions to the community, which started out as a certification study site and evolved into an excellent message board. Until StackOverflow came along, it was my favorite place for getting answers to technical questions. It’s still an excellent example of how a friendly non-trolling technical community can operate.

If you drop by and ask a question in the Groovy forum, you can win a copy of my book.

Last week was a very busy one for me. I gave four talks at the NFJS event in Seattle, then went from there to the SpringOne2GX conference in Santa Clara, and then did the NFJS event in Boston area (Framingham, to be specific). I had a great time at all three events.

One of my favorite moments was in my “Advanced Groovy: Tips and Tricks” talks, when I noticed Paul King walk into the room. If you’ve read any of my book, you know I’m a big fan of his. In addition to being a Groovy committer, he’s also a co-author on Groovy in Action. If you’re interested in anything Groovy, be sure to check out his presentations at SlideShare.net.

While I was able to handle most of the questions, Paul did show me a few items after I was finished. For example, running collect on a map is even easier than I thought. To build a query string, I used to do this:

String qs = [a:1, b:2, c:3].collect { k,v -> "$k=$v" }.join('&')
assert qs == 'a=1&b=2&c=3'

Paul noticed I did that in a blog post and reminded me that the toString method on Map.Entry returns “key=value”, so I can reduce that to:

String qs = [a:1, b:2, c:3].collect { it }.join('&')
assert qs == 'a=1&b=2&c=3'

It turns out that now there’s even a default collect method that returns it, so I can make this even simpler:

String qs = [a:1, b:2, c:3].collect().join('&')
assert qs == 'a=1&b=2&c=3'

I need the parentheses on the collect method (or the compiler will think it’s a property), but it’s hard to get any simpler than that.

Another interesting question came up when I was talking about metaprogramming. For years I knew you could add a method to a class by adding a new property to its metaclass. For example, I can add a cook method to Map:

Map.metaClass.cook = { BigDecimal purity -> "$purity% pure meth" }
assert '85% pure meth' == [a:1, b:2].cook(85)

(Sorry about the drug example. Like so many people, I’m totally caught up in the last few episodes of Breaking Bad. And btw, I have no idea what the actual ingredients are for meth, though I know one of them involves ephedra because I have to sign for my allergy medication at the pharmacy now.)

The question that came up was, can I overload that method using the same technique? I can certainly create a closure with different arguments, but can I assign it to the same property without messing up the original assignment?

Here’s an alternative implementation of the cook method:

import java.awt.Color

Map.metaClass.cook = { Color c -> "$c-tinted meth" }
assert 'java.awt.Color[r=0,g=0,b=255]-tinted meth' == 
    [a:1, b:2].cook(Color.blue)

I can assign the two different implementations to the same property:

import java.awt.Color

Map.metaClass.cook = { Color c -> "$c-tinted meth" }
Map.metaClass.cook = { BigDecimal purity -> "$purity% pure meth" }
assert '85% pure meth' == [a:1, b:2].cook(85)
assert 'java.awt.Color[r=0,g=0,b=255]-tinted meth' == 
    [a:1, b:2].cook(Color.blue)

It still looks funny to me, but it works under the hood. Good to know.

Paul would probably appreciate it if I also mention that the example I chose was my own. He didn’t suggest drugs at any stage of development.

If you get a chance, please drop by the Groovy forum at JavaRanch this week and say hi. As another reminder, my Making Java Groovy presentation at JavaOne is Monday morning at 10am, so if you’re there, please drop by. I gave away over 20 print copies of my book at SpringOne2GX, and I’m sure I’ll have more at JavaOne. I’ll even deface sign one for you if you like. 🙂

Trains on the Range

I’m at SpringOne2GX this week, but this morning before my first presentation I noticed the following in my Twitter feed:

DailyGrailsTip_ranges

First of all, I think that’s the first time anything of mine has ever appeared in @DailyGrailsTip, so yay for me. On the other hand, the link is to a script that isn’t terribly self explanatory. At the risk of spoiling part of my Advanced Groovy Tips and Tricks talk, I thought I’d discuss it here.

The basic idea is that any Groovy class can be made into a range if it:

  1. implements java.util.Comparable
  2. has a next method
  3. has a previous method

My example uses a TrainStation class, which forms a doubly-linked list when I place them along a track. So part of the implementation is trivial:

class TrainStation implements Comparable<TrainStation> {    
    // Links to next and previous stations
    TrainStation next
    TrainStation previous
    
    TrainStation next() { next }
    TrainStation previous() { previous }    

    @Override  // implement Comparable
    int compareTo(TrainStation ts) {
        // Hmm. What to do here?
    }    
}

The TrainStation has a reference to the next one and the previous one, and the implementations of next and previous just return them. (I considered making a wrapper class called Track, but kept it simple here.)

The question is, how am I going to implement Comparable? Since one important characteristic of train stations is their physical location, I decided to use latitude and longitude, and made the comparison based on latitude.

The result is:

import java.text.NumberFormat

class TrainStation implements Comparable<TrainStation> {
    
    // Links to next and previous stations
    TrainStation next
    TrainStation previous
    
    // location attributes
    String city
    String state
    
    // set by Geocoder service
    BigDecimal latitude
    BigDecimal longitude

    TrainStation next() { next }
    TrainStation previous() { previous }
        
    @Override
    int compareTo(TrainStation ts) {
        this.latitude <=> ts.latitude
    }
    
    String toString() { 
        NumberFormat nf = NumberFormat.instance
        "$city, $state \t (${nf.format(latitude)}, ${nf.format(longitude)})" 
    }
}

Each train station has a city and state, which I set, and a latitude and longitude, which are computed from a geocoder. My geocoder is based on Google’s RESTful web service, which I’ve used many times before:

class Geocoder {
    public static final String BASE = 'http://maps.google.com/maps/api/geocode/xml?'

    void fillInLatLng(TrainStation station) {
        String encoded = 
            [station.city, station.state].collect { 
                URLEncoder.encode(it,'UTF-8')
            }.join(',') 
        String qs = [sensor:false, address: encoded].collect { it }.join('&')
        def response = new XmlSlurper().parse("$BASE$qs")
        station.latitude = response.result[0].geometry.location.lat.toBigDecimal()
        station.longitude = response.result[0].geometry.location.lng.toBigDecimal()
    }
}

The fillInLatLng method takes a train station and URL encodes its city and state, so that they can be sent as part of a URL. It then builds a query string by converting the map entries in the form “key:value” into list entries in the form “key=value”.

(Note: I used to write that as [a:1, b:2].collect { k,v -> "$k=$v" }. Then Paul King pointed out that the default toString method on Map.Entry is in fact “key=value”. Therefore I can just write [a:1, b:2].collect { it } and it does the same thing. Live and learn.)

After building the query string, I use the parse method from XmlSlurper to access the web service, download the resulting XML, parse it into a DOM tree, and hand me the root. The rest is just walking the tree, keeping in mind that sometimes Google returns more than one request, so I made sure to use the first one only by writing result[0].

To use the new class, I need to create some train station and set their next and previous properties. Then I can use them in a range. Here’s a script to do all that:

// Amtrak, NE corridor (mostly)
TrainStation was = new TrainStation(city:'Washington', state:'DC')
TrainStation bal = new TrainStation(city:'Baltimore', state:'MD')
TrainStation wil = new TrainStation(city:'Wilmington', state:'DE')
TrainStation phl = new TrainStation(city:'Philadelphia', state:'PA')
TrainStation nwk = new TrainStation(city:'Newark', state:'NJ')
TrainStation nyc = new TrainStation(city:'New York', state:'NY')
TrainStation nhv = new TrainStation(city:'New Haven', state:'CT')
TrainStation pvd = new TrainStation(city:'Providence', state:'RI')
TrainStation bos = new TrainStation(city:'Boston', state:'MA')

// Arrange the stations on the track
was.next = bal; bal.previous = was
bal.next = wil; wil.previous = bal
wil.next = phl; phl.previous = wil
phl.next = nwk; nwk.previous = phl
nwk.next = nyc; nyc.previous = nwk
nyc.next = nhv; nhv.previous = nyc
nhv.next = pvd; pvd.previous = nhv
pvd.next = bos; bos.previous = pvd

def ne_corridor = [was, bal, wil, phl, nwk, nyc, nhv, pvd, bos]

// Fill in all the latitudes and longitudes
Geocoder geo = new Geocoder()
ne_corridor.each { station -> 
    geo.fillInLatLng(station)
}

// range heading north
println '\nNorthbound from WAS to BOS'
(was..bos).each { println it }

// range heading south
println '\nSouthbound from BOS to WAS'
(bos..was).each { println it }

// subrange from nyc to bos
println '\nNYC to BOS'
(nyc..bos).each { println it.city }

The resulting output is:

Northbound from WAS to BOS
Washington, DC (38.907, -77.036)
Baltimore, MD (39.29, -76.612)
Wilmington, DE (39.746, -75.547)
Philadelphia, PA (39.952, -75.164)
Newark, NJ (40.736, -74.172)
New York, NY (40.714, -74.006)
New Haven, CT (41.308, -72.928)
Providence, RI (41.824, -71.413)
Boston, MA (42.358, -71.06)

Southbound from BOS to WAS
Boston, MA (42.358, -71.06)
Providence, RI (41.824, -71.413)
New Haven, CT (41.308, -72.928)
New York, NY (40.714, -74.006)
Newark, NJ (40.736, -74.172)
Philadelphia, PA (39.952, -75.164)
Wilmington, DE (39.746, -75.547)
Baltimore, MD (39.29, -76.612)
Washington, DC (38.907, -77.036)

NYC to BOS
New York
New Haven
Providence
Boston

There you have it. I’ve taken an arbitrary class, TrainStation, and enabled it to be used in a Groovy range. This whole demo is part of the code I use in my “Advanced Groovy” talk, stored in my GitHub repository at https://github.com/kousen/AdvancedGroovy .

Quick book update: The ebook version of Making Java Groovy should be released today. The print book should be available from Manning on 9/16 or 9/17. The remaining mobile formats (epub and mobi) will be released on 9/27. I’m still hoping to have some physical copies here at the conference to give away, but I haven’t seen them yet.

Btw, it’s wonderful seeing so many of the Groovy and Grails team members at the conference. I seriously doubt, however, that any of them took the train to get here. 🙂

Making Java Groovy: Operator Overloading

This is partly a spoiler for my “Making Java Groovy” talk that I normally do at NFJS events and plan to use in my similar talk at JavaOne, but to celebrate the book going to the printer I thought I’d try to contribute something technical here.

I’ve always felt that one of the keys to understanding Groovy was operator overloading. Operator overloading has a mixed reputation, conjuring up horror stories of developers changing + to be *, which might be good for job security but not good in general.

(Ultimately it’s not be good for job security, either.)

To be honest, I don’t do a lot of operator overloading in my own code, but I take advantage of it all the time. Most Groovy developers don’t realize how thoroughly operator overloading occurs throughout the Groovy JDK, among other places.

This web page lists all the operators in Groovy and their corresponding methods. What’s easy to overlook, however, is that every operator in Groovy actually invokes a method. That table doesn’t just provide you with options — it’s what’s actually happening under the hood. Note that it’s not just operators like + or * that call methods. Array-like access (the [] operator) calls getAt and putAt on maps, and even == invokes the equals method.

(Actually, == invokes the compareTo method if the class happens to implement java.util.Comparable, but according to the Comparable contract that ought to be equivalent.)

The code simplifications that result are dramatic. For example, consider the plus method in java.util.Date, which takes an int representing the number of days:

Date now = new Date()
Date then = now + 3
assert 4 == (now..then).size()

It’s also why you can not only concatenate with strings using +, you can also subtract substrings from them using the minus operator:

String s = 'silly string'
assert 'more silly string' == 'more ' + s
assert 'string' == s - 'silly '

Note that in each case, the operator is returning a new string rather than modifying the original. Strings are still immutable, even in Groovy.

Let me give a larger example that uses Groovy / Java integration to make coding easier. If you’ve ever done financial calculations in Java (or if you’ve ever seen Office Space), you know you need to use java.math.BigDecimal for the amounts rather than just doubles. But BigDecimal in Java is an annoying class to work with, because (1) you have to instantiate it, (2) you have to remember to use the constructor that takes a String, or the results are not predictable (see the JavaDocs for details), and (3) you lose all your operators and have to use all the associated method calls, keeping in mind that (4) all instances of the class are immutable. That makes for a rather verbose class when all you want is to wrap a numerical value.

You can do all that, or you can take advantage of the fact that the native data type for floating point values in Groovy is already BigDecimal. Not only that, but the Groovy JDK includes methods like plus, minus, and multiply in the java.lang.Number class (the superclass of BigDecimal), so you can use your normal operators. The result is a class like:

package com.mjg

import groovy.transform.Canonical

@Canonical
class Account {
    BigDecimal balance = 0.0
    
    BigDecimal deposit(BigDecimal amount) {
        balance += amount
    }
    
    BigDecimal withdraw(BigDecimal amount) {
        balance -= amount
    }
}

The only attribute is the balance, stored as a BigDecimal, initialized to zero. Since the attribute lacks a public or private modifier, Groovy automatically generates public getter and setter methods for it. The deposit and withdraw methods take advantage of operator overloading to update the balance using simple + and – operators. Finally, the Canonical AST transformation adds equals, hashCode, and toString methods to Account, as well as a “tuple” constructor that can be called from Java. That’s a lot of mileage for 16 lines of code.

Before I use it from Java, let me verify that it works so far:

package com.mjg

// Normal Groovy ctor that sets the property
Account acct = new Account(balance:250)
assert 250 == acct.balance
assert 300 == acct.deposit(50)
assert 200 == acct.withdraw(100)

// check the tuple ctor
acct = new Account(150)
assert 150 == acct.balance

// toString method
assert 'com.mjg.Account(150)' == acct.toString()

// equals and hashCode
Set accounts = [new Account(balance:250), new Account(250)]
assert accounts.size() == 1

Accounts can be created using the normal map-based constructor or the tuple constructor, which in this case has only a single argument (because the class has a single attribute). Accessing the balance property actually invokes the getBalance method, which is supplied by Groovy automatically. The generated toString method is as shown, and to prove that both equals and hashCode are working I added two identical accounts (instantiated by either constructor) into a Set and verified that Groovy added only one of them.

That’s all well and good, but if I can’t verify a Groovy class using Groovy tests, I might as well give it up. Instead, I want to access the Groovy class from Java. Here, therefore, is a highly contrived Bank class, in Java:

package com.mjg;

import java.math.BigDecimal;

public class Bank {
    public Account openAccount(double initialBalance) {
        Account acct = new Account();
        acct.deposit(new BigDecimal(initialBalance + ""));
        return acct;
    }

    public BigDecimal getBalance(Account acct) {
        return acct.getBalance();
    }
    
    public BigDecimal addFunds(Account acct, double amount) {
        return acct.deposit(new BigDecimal(amount + ""));
    }
    
    public BigDecimal removeFunds(Account acct, double amount) {
        return acct.withdraw(new BigDecimal(amount + ""));
    }
}

The Bank has a method to open an account using a double, which is wrapped in a BigDecimal using the String-based constructor. Then addFunds and removeFunds just delegate to the associated methods in Account.

To test this, I might as well switch back to Groovy again, but in a normal JUnit test:

package com.mjg

import static org.junit.Assert.*;

import org.junit.Test;

class BankTests {
    Bank bank = new Bank()
    Account acct = bank.openAccount(250);
    
    @Test
    void testOpenAccount() {
        assert acct
    }
    
    @Test
    void testGetBalance() {
        assert 250 == bank.getBalance(acct)
    }
    
    @Test
    void testAddFunds() {
        bank.addFunds(acct, 50)
        assert 300 == bank.getBalance(acct)
    }
    
    @Test
    void testRemoveFunds() {
        bank.removeFunds(acct, 100)
        assert 150 == bank.getBalance(acct)
    }
}

All the tests work without a problem, demonstrating how easy it is to switch from Groovy to Java and back again. If you ever have to add code to an existing Java system that involves BigDecimal, writing a class in Groovy is a very easy way to do it.

In my book, incidentally, the chapter on Spring and Groovy uses a similar Account class, but with an added id. Then I implement an AccountDAO interface (written in Java, of course) using the a Groovy class that takes advantage of Spring’s JdbcTemplate (again from Java), complete with Spock tests. If you want to see the source code now, just check out ch07 in the GitHub repository for the book.

As in this example, when I code in Groovy I don’t often do operator overloading, but the effects of it show up everywhere. Hopefully these simple examples will help you take advantage of it in the future.

BTW, I received notice from Manning that I will have actual hard copies of Making Java Groovy at the SpringOne2GX conference next week. Even if you don’t want one, please come by and say hello. Don’t be surprised if I can’t stop grinning the entire time.

%d bloggers like this: