US & Canada: 877 849 1850
International: +1 678 648 3113

Accelebrate Blog

ACCELERATED LEARNING, CELEBRATED RESULTS

Building a Geolocation Web Application with Groovy and Grails

(Where in the World is Steve Heckler?)

 

Table of Contents

1. Introduction

When you own a world-class training company like Accelebrate, you spend much of your time on the move. The inimitable Steve Heckler is such a person, and some would say he travels way too much.

As a fellow road warrior, I can tell you that if:

  • You wake up and have no idea where you are, and it turns out you’re at home,
  • Your spouse subtracts years from your anniversary to compensate for time away,
  • The TSA “massage artists” in your home airport shake their heads when they see you coming,
  • By reflex you take off your shoes when you go to the DMV,
  • You’ve bought a minimum of five phone/tablet chargers in the last year for home, travel, and to replace all the ones you left in various hotels,
  • Ryan Bingham (George Clooney’s character from Up in the Air) thinks you really need to cut back,

then you’re probably traveling too much.

As a public service, this blog post describes an application that keeps track of Steve Heckler. Maybe this will help provide the intervention he so sorely needs.

2. The Grails Domain Model

I’m using the Grails framework to write this application, which is a stripped-down version of the one we build in Accelebrate’s Grails course. Grails is a set of Groovy-based Domain Specific Languages (DSLs) that are used to create and configure a traditional Spring-Hibernate application from Java. This post will give you enough information to follow along, but to really understand it you probably should take the training course (hint, hint).

The domain model consists of two classes, Person and Location. The former represents the traveler. Here is the complete version.

  The Person domain class
  class Person {
    String title
    String first
    String last

    String toString() { "$title $first $last" }
	  static hasMany = [locations:Location]

	  static constraints = {
        title inList: ['Mr.', 'Ms.', 'Dr.', 'Prof.',
          'King', 'Queen', 'Prince', 'Princess',
          'God-Emperor', 'Grand Poobah', 'Benevolent Dictator For Life']
        first nullable: true
        last blank: false
      }
  }

Even if you’ve never seen Groovy before, this is pretty easy to follow. The Person class has three attributes, title, first, and last. A toString method is provided to return the three together, as in Queen Anne Fernandez [1].

The hasMany statement implies that each Person instance is associated with many Location instances, which form a java.util.Set (i.e., no duplicates).

(If all you have is one class and one table, life is simple. It’s when you introduce relationships that life gets complicated. [rimshot])

The constraints block in Grails allows you to specify constraints on the attributes. Here the title must be one of the listed values, the first name can be null if unavailable, but the last name must not be null or empty.

The Location class is about as simple.

  The Location domain class
  class Location {
    String city
    String state

	// derived properties, set by a service
    double latitude
    double longitude

	String toString() { "$city,$state" }

	static constraints = {
      city blank: false
      state blank: false
      latitude min: -90d, max: 90d
      longitude()>
    }
  }

Location instances have a city and a state, which the user will specify. They also have latitude and longitude values, which will be computed by the application.

The toString method simply returns the city, state combination, as in “Atlanta,GA”.

The constraints are that the city and state values cannot be null or empty. The latitude value must be between the South Pole and the North Pole, and since it’s measured in degrees, the minimum value is -90 and the maximum value is 90. Note that because the variables are declared to be doubles (the native floating point data type in Groovy is java.math.BigDecimal, but I certainly don’t need that much precision here), the data type of the constraint values must also be doubles, thus the “d” after the numbers.

I don’t actually have a longitude constraint, since adding or subtracting enough 360s from any given value would be valid. It is only added here as a method at the end to ensure that the order in which the attributes appear in the constraints block matches the order on the generated forms and tables.

3. Grails Controllers

Grails has a useful feature called dynamic scaffolding. The PersonController class takes advantage of it:

The dynamically scaffolded PersonController class

  class PersonController {
    static scaffold = true
  }

This single line generates a controller and a set of views that allow you to create, edit, save, update, and delete individual Person instances. Dynamic scaffolding helps during development, because it allows you to modify the domain class fields all you want and the forms and SQL commands will all update automatically.

Normally the word “scaffold” is meant to be taken literally — you use it during development but remove it in favor of actual view and controller code during production. Here, however, I’ll live with the results for the Person class. The Location class, however, will be another matter.

In Grails, controllers consist of actions, which are public methods. A URL maps directly to an action in a controller, with an optional id passed as well if needed.

The pattern is:

http://<server>:<port>/appname/controller/action/id

Controller actions either render data directly (uncommon unless you’re writing a RESTful web service), redirect to another URL, or return a collection of model objects to a view.

For the LocationController, I’ll start with the generated scaffolding (since the code is explicitly added to the project it’s called static rather than dynamic) and later modify it. Here is a portion of the class:

Part of the statically scaffolded LocationController class

  @Transactional(readOnly = true)
  class LocationController {

    static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
    def index(Integer max) {
      params.max = Math.min(max ?: 10, 100)
      respond Location.list(params),
        model: [locationInstanceCount: Location.count()]
    }

    def show(Location locationInstance) {
      respond locationInstance>
    }
    def create() {
      respond new Location(params)
    }
 
  // ...
  }

The @Transactional annotation comes from the Spring framework. It means that by default, Spring intercepts the call to each method in this class, begins a transaction, invokes the method, and then commits or rolls back the transaction when the method completes. By default the transactions are read-only, but this can be customized on each method.

The index action takes an integer max as an argument. The params variable is a Groovy map (like a Java map, but way groovier) that holds all the input parameters. If no max value is supplied, it’s set to 10.

The respond method then returns a list of the first 10 location instances, or sends back their JSON form if the Accept header of the HTTP request is application/json. Also returned is a variable called locationInstanceCount, whose value is the total number of location instances in the database. Location.list() and Location.count() are examples of GORM, the Grails Object Relational Mapping API.

(The lizard creature Captain Kirk fought in the Star Trek original series episode Arena was a GORN, not a GORM, but whoever worried about Grails Object Relational Napping? (Though I think there’s a lazy loading joke in there somewhere.))

Figure 1. Captain Kirk struggles with Grails Object Relational Napping, er, Mapping

Figure 1. Captain Kirk struggles with Grails Object Relational Napping, er, Mapping

The show action looks like it takes a Location instance as an argument, but actually you supply it an id. Grails then searches for that instance, and returns it if found or null if not.

Finally, the create action instantiates a new Location object and sends it back using content negotiation again.

There are other methods in the controller, but this should give you an idea of how they work.

4. Grails Services

As developers, we’re taught about the Model-View-Controller architecture very early in our careers. In the Java world, however, services play an equally vital role. What if you need to work with multiple controllers and domain objects in the same transaction? What if you want to keep your controllers reasonably uncluttered with business logic? That code has to go somewhere.

In Grails, it goes in service classes. Grails services are transactional by default. They’re also Spring beans, so they can be injected as dependencies (whoa, dependency injection — you see what I did there?) into other artifacts, like controllers.

In this case, I need a service that can take a Location instance an populate its latitude and longitude fields based on its city and state attributes. Fortunately, Google provides a RESTful web service that can do that for me.

4.1. The Google V3 Geocoder

As part of the overall Google Maps API, Google provides a service that geocodes locations. The documentation for the service can be found at https://developers.google.com/maps/documentation/geocoding/.

Note that only GET requests are supported — you can’t POST, PUT, or DELETE anything there.

(A friend of mine refers to read-only services like that as GETful services rather than RESTful. If it’s stateless, too, does that make it a FORGETful service? [another rimshot])

One of the principles of REST is content negotiation, which is where the client specifies the form of the response that they want, like JSON or XML, and the service tries to provide it. That’s what changes a “resource” into a “representation”, actually. Normally the form is specified via a MIME type in the HTTP Request header, but that’s not how it’s handled here. The Google geocoder hard-wires the value into the URL itself.

The base URL is either http://maps.googleapis.com/maps/api/geocode/xml or the same value with json replacing xml. To send a GET request, you also need to append an address field holding the street, city, and state, joined by commas. If any of those fields have spaces or apostrophes or any other character that cannot appear in a URL, you have to encode them.

Groovy doesn’t have a URL encoder, but Java does.

Rather than show the code right away, it’s time to be a good little developer and use TDD.

4.2. Holy Test Driven Development (TDD), Batman!

In true TDD, you write tests first, watch them fail, then write the implementation that successively makes them succeed, and finally refactor your code into what you ultimately want. The phrase used is “Red-Green-Refactor”, after the colors in the JUnit test window. Rather than make this article even longer than it has already become, I’m only going to write one test and then provide the implementation right away.

(I must confess that I’m not good at pure TDD. I tend to follow what I call GDD: Guilt Driven Development. If I write code that isn’t tested, I feel guilty about it until I write a test.)

Here’s a Grails test for the GeocoderService class:

A Spock test for the GeocoderService

  @TestFor(GeocoderService)
  class GeocoderServiceSpec extends Specification {
    void "check lat/lng for Atlanta, GA"() {
      given:
      def atl_lat = 33.755
      def atl_lng = -84.390
      Location loc = new Location(city: 'Atlanta', state: 'GA')
      
	  when:
      service.fillInLatLng(loc)

	  then:
      (loc.latitude - atl_lat).abs() < 0.01
      (loc.longitude - atl_lng).abs() < 0.01
    }
  }

Grails generates a Spock test for each artifact. The @TestFor annotation shows the class being tested, and, if that class is a controller or service, instantiates it automatically. The test is checking the latitude and longitude of Atlanta, GA. Given the correct values, when the fillInLatLng method of the service is invoked using a Location instance, the test shows that the values of the latitude and longitude fields inside the Location object are close enough.

Let me explain the implementation of the service in a series of steps.

  1. Start with the base URL for the serviceString base = ‘https://maps.googleapis.com/maps/api/geocode/xml?
  2. Create a Groovy list containing the city and state
    def encoded = [loc.city, loc.state]
  3. Transform the list into a new one by URL encoding the values
    def encoded = [loc.city, loc.state].collect {
      URLEncoder.encode(it, 'UTF-8')
    }
  4. Create a string by joining the list elements with commas
    def encoded = [loc.city, loc.state].collect {
      URLEncoder.encode(it, 'UTF-8')
    }.join(',')
    
  5. The query string is assembled by plugging in the encoded address
    String qs = "address=$encoded"
  6. Using the wonderfully-named XmlSlurper class, access the full URL and parse it into a DOM tree
    def encoded = [loc.city, loc.state].collect {
      URLEncoder.encode(it, 'UTF-8')
    }.join(',')
    String qs = "address=$encoded"
    def root = new XmlSlurper().parse("$base$qs")
    
  7. Finally, walk the tree to get the values I need and update the location
    String base = '<a href="https://maps.googleapis.com/maps/api/geocode/xml?" target="_blank">https://maps.googleapis.com/maps/api/geocode/xml?</a>'
    String encoded = [loc.city, loc.state].collect {
      URLEncoder.encode(it, 'UTF-8')
    }.join(',')
    String qs = "address=$encoded"
    def root = new XmlSlurper().parse("$base$qs")
    def location = root.result[0].geometry.location
    loc.latitude = location.lat.toDouble()
    loc.longitude = location.lng.toDouble()
    

That is a serious amount of power packed into about 8 lines of code. The same process in Java would take approximately 5000 lines[2].

Placing all of this into a service results in:

  import grails.transaction.Transactional

  @Transactional
  class GeocoderService {

    void fillInLatLng(Location loc) {
      String base = 'https://maps.googleapis.com/maps/api/geocode/xml?'
      String encoded = [loc.city, loc.state].collect {
        URLEncoder.encode(it, 'UTF-8')
      }.join(',')
      String qs = "address=$encoded"
      def root = new XmlSlurper().parse("$base$qs")
      def location = root.result[0].geometry.location
      loc.latitude = location.lat.toDouble()
      loc.longitude = location.lng.toDouble()
    }
  } 

5. Dependency Injection (for realz this time)

Assuming the tests now pass, how does Grails use the new service? By injecting it into the controller, of course.

All you non-Spring (and non-Google-Guice) people are now saying something like, “what the what?” Prepare your brain to be rocked by the miracle of dependency injection, which gained popularity (such as it is) with the Spring framework and now even exists as a specification in Java EE (known as CDI — Container Dependency Injection).

Here’s a snippet from the LocationController class containing this subtle mysticism of DI:

  @Transactional(readOnly = true)
  class LocationController {

  // Inject that dependency!
  def geocoderService

  // ...

  @Transactional
  def save(Location locationInstance) {
    if (locationInstance == null) {
      notFound()
      return
    }

    // Use the injected service!
    geocoderService.fillInLatLng(locationInstance)

    // ...

    // Save the updated location, and I mean now!
    locationInstance.save flush: true
    // ...
    }

  // ... same stuff in the `update` action
  }

Go ahead, say it with me: “Ooooooh! Aaaaaah!”

Okay, maybe that was a bit overly dramatic. In Grails, DI means you declare a reference with the right name (the service class name with a lowercase first letter), and Grails will instantiate the class if necessary and set the value for you.

That may not sound like much, but Rod Johnson and friends were able to form an entire company around the idea (SpringSource) and ultimately sell it to VMware, and you know some of the high-level people managed to pay off their mortgages based on that sale, so there’s that. Even I, a lowly trainer[3], have managed to teach a bunch of Accelebrate classes based on the results. What have you done lately?

(Btw, I totally want in on the Accelebrate IPO. “A million dollars isn’t cool. You know what’s cool? A BILLION dollars.” (Hat Tip, The Social Network))

The result is that any time a Location is created with a given city and state, the service populates the latitude and longitude fields.

6. Map-maker, Map-maker, Make Me A Map

The Grails application is now able to create, retrieve, update, and delete people and locations. At long last, it’s now time to use those locations to put Steve Heckler on the map.

(Again, to really understand all this, you should take Accelebrate’s Grails class. Don’t worry about using them up — WE’LL MAKE MORE.)

While I could add the location objects one by one, it’s easier in this case to add the last dozen or so to the initialization data. That goes into a Grails class called BootStrap, which has an init closure that is executed on start up.

class BootStrap {
  // Whoa! Dependency injection AGAIN!
  def geocoderService

  def init = { servletContext ->
    Person steve = new Person(first: 'Steve', last: 'Heckler',
      title: 'Benevolent Dictator For Life')

    def locations = [
      new Location(city: 'Atlanta', state: 'GA'),
      new Location(city: 'Waltham', state: 'MA'),
      new Location(city: 'Billerica', state: 'MA'),
      new Location(city: 'Richmond', state: 'VA'),
      new Location(city: 'Dublin', state: 'OH'),
      new Location(city: 'Montgomery', state: 'AL'),
      new Location(city: 'Albany', state: 'NY'),
      new Location(city: 'Saint Louis', state: 'MO'),
      new Location(city: 'Columbia', state: 'MO'),
      new Location(city: 'Indianapolis', state: 'IN'),
      new Location(city: 'Phoenix', state: 'AZ'),
      new Location(city: 'Palm Springs', state: 'CA'),
      new Location(city: 'Houston', state: 'TX'),
      new Location(city: 'Dallas', state: 'TX')
    ]

    // Use the service
    locations.each { loc ->
      geocoderService.fillInLatLng(loc)
      steve.addToLocations(loc)
    }

    // Save everything
    steve.save(failOnError: true)
  }
}

In our training class, we then use a Grails plugin called the Google Visualization Plugin to create and build a Google map around the locations. Here I’m going to take a slightly different approach, because I want to change the map icons. Believe me, when you see it, you’ll understand. This map is all about the icons.

The Google Charts API contains a way to generate a Google map based on collections of latitudes and longitudes. The documentation shows the necessary JavaScript.

Normally Grails expresses its views as GSPs (Groovy Server Pages). I can still use that. I’ll just add the required JavaScript inside it.

In this case, that starts with the import of the jQuery JavaScript library. Grails already has a tag to do that, and jQuery is built right into Grails. So in the header of the index.gsp page, I write:

  <!DOCTYPE html>
  <html>
  <head>
  <meta name="layout" content="main">
  <g:javascript library="jquery"/>
  <script type="text/javascript" src="https://www.google.com/jsapi"></script>
  <!-- ... -->

The <meta> tag is used by the SiteMesh plugin to enforce a common look and feel for the site. The <g:javascript> tag imports the jQuery library. The following <script> tag import the Google JavaScript API.

The challenge is, how do I get the data from Grails objects (Groovy POGOs) into something that can be processed by JavaScript?

I know what you’re thinking.

If only there was some way to convert Grails domain classes into JSON objects. Wouldn’t that be great? But that’s probably just a dream.

— You thinking to yourself

Remember that scaffolded LocationController class? Remember that respond method used inside it[4]? Remember how I vaguely referred to content negotiation at the time?

Well, remember no longer[5]. The respond method in Grails 2.3 and 2.4 controllers checks the requested MIME type and returns the proper response automatically. All you have to do is say:

http://<server>:<port>/whereintheworld/location/index.json

and you get back all the Location instances in JSON format. All I have to do is use them in the proper JavaScript code, which is based on the example shown in the documentation.

  <script type="text/javascript">
    google.load('visualization', '1', {'packages': ['map']});
    google.setOnLoadCallback(drawVisualization);

    function drawVisualization() {
      visualization_data = new google.visualization.DataTable();
      visualization_data.addColumn('number', 'Lat');
      visualization_data.addColumn('number', 'Lng');
      visualization_data.addColumn('string', 'Name');

      var locations = '/whereintheworld/location/index.json?max=20'
      $.getJSON(locations)
        .done(function (data) {
          $.each(data, function (i, loc) {
            visualization_data.addRow(
              [loc.latitude, loc.longitude, loc.city + "," + loc.state]
            );
          });
        });

       var options = {
        showTip: true,
        icons: {
          default: {
            normal: '/whereintheworld/assets/steve_heckler.png',
            selected: '/whereintheworld/assets/steve_heckler_selected.png'
          }
        }
      };
      visualization = new
        google.visualization.Map(document.getElementById('map'));
      visualization.draw(visualization_data, options);
    }
  </script>

The resulting map looks like:

Figure 2. Where in the World is Steven Heckler?

Figure 2. Where in the World is Steven Heckler?

And now you know.

7. What have we learned?

So, what have we learned?

  1. Grails is a Groovy-based framework for creating traditional MVC applications based on Spring and Hibernate
  2. Grails uses transactional services to perform business logic
  3. Grails uses dependency injection to provide services to controllers and other artifacts
  4. Grails controllers can provide JSON data on demand (whoa)
  5. Grails views have jQuery built in
  6. Steve Heckler travels way too much, and to far less exotic places than you would expect or hope for
  7. The Accelebrate IPO will be worth billions. Billions!

Respectfully submitted by Ken Kousen, who teaches this stuff

Footnotes

  1. Defender of the Accelebrate Blog, Keeper of the Travel Arrangements, and All Around Good Person
  2. Rough estimate, but if you’ve ever parsed XML using Java, you know what a joy it can be
  3. Author, speaker, and failed wannabe comedian
  4. Seriously, remember that. There will be a quiz later. (Spoiler: No, there won’t.)
  5. Wait, no, it’s okay, you can remember if you want.
Categories: Java Articles
Tags: , , , ,

4 Responses to "Building a Geolocation Web Application with Groovy and Grails"

Leave a Reply

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

*



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Please contact us for GSA pricing.
Contract #GS-35F-0307T

Please see our complete list of
Microsoft Official Courses