Background

I recently needed to learn Groovy for my job. I’m working with a system where I’m doing a lot of API integration. It’s done mainly through a web frontend, though that frontend actually interfaces with various on-prem collectors that execute the code. While I can use basically any language I want, using anything other than Groovy means that I have to mess with copying files around on the collector devices. Using Groovy means that I can just paste my code into the web portal and be done; the system is Java-based and executes the Groovy code directly from the service running on any applicable collector. There are also some added benefits, such as being able to use special syntax to pull things like API keys out of the web portal as well. While Groovy isn’t exactly a language I aspired to learn, I figured the benefits would be well worth the effort.

I spent some time looking around for learning resources; I didn’t find as much online as I had hoped. I wasn’t completely surprised, though, since Groovy isn’t exactly a new or popular language. Given that Groovy is a first-class programming language in this particular system due to the fact that it runs on the JVM, I’m hopeful they’ll eventually add Kotlin support, but it’s not there yet. Considering that I had already written some code for the system in both Python and PowerShell, I already knew more or less the skills I would need out of the gate. So I was looking for something that would be fast and relatively cheap (though cheap doesn’t matter as much since it gets expensed through work anyway.) I eventually settled upon The Complete Apache Groovy Developer Course at Udemy. It was on sale for $12 USD instead of $95 USD and clocked in at 13 hours. I figured I wouldn’t quite need to go through all of the course and could focus on just what I thought would be relevant for right now. I ended up purchasing the course on a Monday night and wrapped up on a Thursday night; so in 4 nights I went from never writing a line of Groovy to being able to get work done that I needed.

How was the course overall? It was fine. I would say it was worth the $12 to quickly get me up to speed, but under no circumstances would it be worth the full $95 price tag. It did a decent enough job to get me up and running with the basics that I can do most of what I need for my job, though there were a few key omissions. The course is also a few years old, which showed in a couple of areas that I’ll highlight later on. With what I learned in the course, though, I was able to simply look up the remaining pieces online. It’s also worth mentioning that I spent a few years writing a lot of Java in college, though I haven’t really touched it since then.

I figured it might be nice to catalog the things that weren’t covered here in case they can help out someone in a similar position in the future. I’ll reiterate again that these were the things that were important to me for what I needed to accomplish. Obviously your mileage may vary based on what you’re trying to do.

Creating a Date Object at a Particular Date and Time in UTC

Dates and times weren’t covered at all in the course, presumably because they’re basically the same as in Java. Given that I don’t remember a thing about dates and times in Java, I had to rely on DuckDuckGo-fu. A common need of mine is creating a date object at a particular date and time in UTC. I often need to filter API respones by checking if they’re newer than a particular timestamp. For example, I may need to check for all events that have been logged since the first of the month. For the sake of sanity, most APIs I’ve worked with return dates in UTC. It took me a while to figure out how to programmatically construct the full date, but I eventually found success with this like I mentioned on Mastodon:

TimeZone.setDefault(TimeZone.getTimeZone('UTC'))
Integer year = Calendar.getInstance().get(Calendar.YEAR)
Integer month = Calendar.getInstance().get(Calendar.MONTH)
def then = new GregorianCalendar(year, month, 1, 0, 0, 0).time
// Desired format: 2020-06-01T00:00:00.000000+00:00
thenISO = then.format("yyyy-MM-dd'T'HH:mm:ss.SSSZ", TimeZone.getTimeZone("UTC")) 
thenISOFixed = thenISO[0..thenISO.length() - 3] + ":00"
println thenISOFixed

The one oddity was that my timezone offset was 0000 when the API endpoint I was using at the time wanted it noted as 00:00. Since I know I’ll never have an hour or minute for that, though, it was easy to just manipulate that end of the string.

Calculating the Difference in Days Between Two Dates

Another common activity for me is figuring out the amount of time between two timestamps. In this case, I just wanted the number of days between a timestamp from the API and the current date to figure out approximate uptime for some devices. Here it was more important for the numbers to be easily understood by someone looking at it than to be exact. Simply trying to subtract two Date objects made Groovy pretty unhappy, but after some searches I learned about TimeCategories. That allowed me to put together the following:

import groovy.time.TimeCategory
TimeZone.setDefault(TimeZone.getTimeZone('UTC'))
Date boot = new Date( 1590280483576 )
Date now = new Date()
def duration = TimeCategory.minus(now, boot)
println "Earlier duration: ${duration.days} days."

As you can tell, in this case I was working with a different API and was (mercifully) parsing the time from the Unix time epoch instead of dealing with a string.

Creating a JSON Object

Creating JSON objects is also a pretty common task. To the course’s credit, it covered parsing incoming JSON via JsonSlurper. It also covered producing JSON with JsonBuilder, though I think this is one area where the course’s age was a problem. While following the example used, my code would not run successfully; I think the behavior of this packaged changed in the releases since the course was created. I took to the Internet to see if I could get it fixed, but I stumbled across JsonOutput, which I immediately found to be more intuitive. Using this, I was immediately able to create the JSON I needed. In this case, I’m creating a map containing one item which maps to a list of events.:

import groovy.json.JsonOutput
List eventList = []
eventList << [happenedOn: new Date(), severity: "Warn", message: "I dunno what happened."]
eventList << [happenedOn: new Date(), severity: "Critical", message: "This is crazy."]
Map overallMap = [events: eventList]
def jsonStuff = JsonOutput.toJson(overallMap)
println jsonStuff

It’s worth noting that the way this prints to the screen is not the pretty, indented JSON you might be needing. In my case, I just needed to ship it off to an API endpoint. The endpoint doesn’t care at all if it’s pretty, just that it’s valid.

Making HTTPS Connections for REST

I don’t know if this is necessarily another area where the age of the course was showing or if it was just poor judgement in the case of the creator. The course does cover how to query a REST API, but it does so through a 3rd party library which, at the time of this writing, hasn’t been updated on GitHub in 6 years. Is that the sort of thing you want to introduce as a dependency at your job? If you’re like me, the answer is a resounding “Hell no.” As soon as I saw this, I immediately stopped the tutorial and just started looking it up on my own. Fortunately, this is fairly simple to do in vanilla Java, so I’m really not sure why the creator of the course felt the need to use a library in the first place. I got it to work via:

import groovy.json.JsonSlurper
def conn = new URL(myUrl).openConnection()
conn.setReadTimeOut(2000)
conn.setRequestMethod("GET")
conn.setRequestProperty("ID", myID)
conn.setRequestProperty("Secret", mySecret)
def responseCode = conn.getResponseCode()
if( responseCode == 200 ) {
    def responseText = conn.content.text
    def responseMap = slurper.parseText(responseText)
    conn.disconnect()
    // Now do whatever with your data.
} else {
    println "Error with HTTP response. Code was $responseCode"
    conn.disconnect()
}

This is a bit more involved than the other examples, but it’s still fairly simply once you figure out the syntax. You make a connection to a URL you specify, you set the timeout (highly variable depending on the endpoint and how much data you’re pulling back), you specify your HTTP method, and append any properties you need (great for specifying any API keys or tokens you need for authorization.) If the response code is 200, meaning it was successful, then you can parse the response to text and then use a JsonSlurper to parse that to a map. If it fails, you report on the erro code to have some idea of what went wrong.

Wrap Up

Overall, the Udemy course got me where I needed to be to get work done, making my life way easier in the particular system I’m currently working with and will likely continue to be working with for quite a while. It also got me there in just 4 nights of grinding through the content. Groovy being a bit unpopular makes online searches harder than what I’m used to with Python, but it’s still fairly easy to find the information I need. Since it’s also based on the JVM, I’m now in the process of learning Kotlin through a very different method. That’ll be another post for another time, though.