#playframework, #scala, #siena, #gae fun

In my last post, I mentioned that I created a simple (very simple!) Google App Engine hosted Play! webapp, written using Scala.  The webapp simply tracks people who visit my blog.  More accurately, it tracks people who view a little PNG that one of the methods renders.  If someone GETs the image, I capture the visitor’s IP address and the time.  This won’t be replacing anyone’s use of Google Analytics anytime soon, I can assure you.

But for the edification of those who may want to play with Play! and Scala, this is what I did:

  • using Play! 1.1, create a new module: play new —with=scala
  • also add siena and gae to your application.conf
  • define the paths in your controller.  I just have two, the root path lists the recent visitors, and “/track” which renders the image and captures the visitor’s information.
controllers.scala:

def index {
val visits = Visit.recent
render(visits)
}

def track {
val trackedIP = new Visit
trackedIP.remoteAddress = request.remoteAddress
trackedIP.referer = request.headers.getOrElse("referer", None)
// store it in The Cloud(tm)
trackedIP.insert()
// this is the image they see
val file = Play.getFile("/public/images/favicon.png")
renderBinary(file)
}

Note the “getOrElse” call above. This is one of the nice features of Scala. This is there because request.headers is a Map and the referer key isn’t guaranteed to be there. In Java, we would do this:

Http.Header refererHeader = request.headers.get("referer");
String referer = "";
if (refererHeader != null) {
referer = refererHeader.toString();
}
trackedIP.referer = referer;

That’s quite a mouthful, and the Scala version is much more readable.

  • now, define the model.  Because we are deploying to Google App Engine, we will use the Siena module as Siena works much more naturally with that type of datastore than Hibernate.  I’ve never been able to get JPA with Hibernate to work on Google App Engine anyway.

The Visit class extends siena.Model and declares the properties that we will store on the database. The Visit object is a singleton; if we were writing a Java object, the static/class methods we put on the Visit.class are what we are putting in the Visit object here.  In our case, we simply create a method “recent” which returns up to 20 of the most recent visitors. The @Index on the date field is necessary so Siena can order by date, which we will do in reverse order (because we want the most RECENT visitors.)

models.scala:

import play._
import play.data.validation._
import siena._
import java.util.Date

class Visit extends Model {
@Id var id:Long = _
var remoteAddress : String = _
var referer: String = _
@Index(Array("date_index")) var date : Date = new Date
}

object Visit {
def recent:java.util.List[Visit] = Model.all(classOf[Visit]).order("-date").fetch(20)
}

Your views are done in the same manner that you would do them in a Java-based Play! application: via Groovy templates.  Of course, you can always replace the template engine with Scalate or whatever else you prefer, via Play! plugins.

One handy trick I learned was the use of the JavaConversions helper class, introduced in Scala 2.8, which adds some implicit conversions between Java and Scala collections classes. This way, if you want to process the result of our call to Visit.recent using Scala style closures, you could do this:

import scala.collection.JavaConversions._

Visit.recent.foreach(v => println(v.remoteAddress))

This isn’t the most useful application, but it does provide a nice starting point for toying with the language.