Saturday, April 19, 2014

The Simplicity of Groovy and HTTP

Do you ever have the need to do some simple tasks in your IT environment? Maybe you need to run a few checks on some URL's and ensure that everything is copasetic (returning 200's). There are plenty of tools out there that can ping URL endpoints and give you feedback, but why put a tool in place when you can write a Groovy script in under 50 lines of code that is effective? My answer is a) I think writing Groovy is fun b) it can be simple enough that I would rather spend a little time and create something myself c) why bother with implementing a tool if it is not really needed?

The script is pretty straight forward and I first start with a few imports.
import groovy.transform.ToString

Then I define a simple class, Target, to hold some state for the URL endpoints we would like to query on. In the Target class exists the name property for identification purposes, the url property to hold our endpoint and finally the status property to hold the Target status. Pretty simple, right?
class Target {
    def name
    def url
    def status

The ToString annotation is quite powerful as it makes the output of our class state nice and readable. It overrides the default String toString() {} method with a nice default. We could also override the method in the class and format the data however we would prefer. The next part of the script defines a list of new Target instances that we can iterate through and check the status of each URL.
def targetList = [
    new Target(name:"Google", url:""),
    new Target(name:"Amazon", url:""),
    new Target(name:"Oracle", url:""),
    new Target(name:"Axiomatic IT", url:""),
    new Target(name:"Bad Endpoint", url:"")

I put together some sample instances of Target with some good data and some bad data so we can make sure things are working as they are expected. The next block of Groovy iterates through the list of Target instances, attempts to connect to each URL, checks for the 200 response code, checks to ensure that data is returned when a 200 response code is returned, assigns the appropriate status to each Target instance, prints each Target using the toString() method provided by the @ToString annotation and finally disconnects the HttpURLConnection.
targetList.each { target ->

    def url = new URL(target.url)
    def huc
    try { 
        huc = url.openConnection()
        huc.requestMethod = "GET"

        if(huc.responseCode == 200) {
            def br = new BufferedReader(new InputStreamReader(huc.inputStream))
            if(br) {
                target.status = "Success"
            } else {
                target.status = "Failure"
    } catch (Exception e) {
        target.status = "Failure"

    println target


Below, you can see an example of what the output looks like when I ran the script. The @ToString annotation is producing the format below by default for each Target instance. It just makes things pretty when we call println or log the value of a Target instance.
Target(name:Google, url:, status:Success)
Target(name:Amazon, url:, status:Success)
Target(name:Oracle, url:, status:Success)
Target(name:Axiomatic IT, url:, status:Success)
Target(name:Bad Endpoint, url:, status:Failure)

Groovy makes things pretty simple and in under 50 lines of code I have a simple script I can run to check the status of some Web site URL's. I can make things a bit fancier and have the list of Target instances populated from some external data source like a file or database. I could also check the data to ensure that the response is returning valid HTML or some other valid type of data format. The possibilities are there but for now, I have what I need without too much overhead.

Having Fun with Groovy and the Ternary Operator

Groovy is a great language to compliment your Java skills and to increase your productivity on the JVM. From time to time I work on some code and enjoy sharing its likeness here so I can ensure that a) I don't forget what I have done and b) encourage other developers who are interested in the technologies I find interesting. In this script, I have a model object, Policy, that would normally be fetched from a database or some flavor of Web service.
class Policy {
    def number
    def type
    def value

Next, I define a closure the expects a policy number as input. Using the ternary operator, we verify that a valid policy number has been provided. If a policy number is provided, the closure returns a new Policy with some default values. In a normal situation the second part of the ternary operator would make a call to access a database or call a Web service to return meaningful data rather than just return some static Policy object.
// static fetch method returns a default Policy or an empty Policy
def fetchPolicy = { policyNumber ->
    policyNumber ? new Policy(number:policyNumber, type:"Life", value:10000) : new Policy()

It is pretty straight forward. When the policy number is not null or not empty we get the static Policy object. If the policy number is null or an empty string we just return an empty Policy object. Here is the rest of script that does a few assertions on a null policy number, a valid policy number and a policy number which is an empty string.
def policyNumber

// policyNumber is a null value
policyNumber = null
def policy1 = fetchPolicy(policyNumber)

assert policy1.number == null
assert policy1.type == null
assert policy1.value == null

// policyNumber is a valid value
policyNumber = "123456"
def policy2 = fetchPolicy(policyNumber)

assert policy2.number == "123456"
assert policy2.type == "Life"
assert policy2.value == 10000

// policyNumber is an empty value
policyNumber = ""
def policy3 = fetchPolicy(policyNumber)

assert policy3.number == null
assert policy3.type == null
assert policy3.value == null

There is nothing too fancy with this script but you may have noticed that there are no null checks or empty string checks within the fetchPolicy closure. It may not be a big winner for every developer, but I think that these little nuances are what make Groovy a language all Java developers should have in their toolbox.