I’ve been using Ruby on Rails almost exclusively for my web projects over the last year or two. Recently, when I had an idea for a new project, I decided to try something a little different.
My current Rails project, Rhino SchoolTracker, is a traditional CRUD-type web application that is fairly well suited to the Rails way of doing things. For this new project, however, I wanted to completely decouple my server side code from my front-end web application.
My idea is to create a simple REST API for the back-end services, and build the web UI using Backbone and Bootstrap. This also has the benefit of providing significant flexibility for possible mobile clients later. For the server side stuff, I could have turned to Rails again, but that seemed like overkill when I would only be using a subset of its features.
I stumbled upon Scala while researching alternative server-side languages. While I would never use Java if I had a choice in the matter, the idea behind Scala is a good one. Fix the basic problems with Java (the language) and add functional programming support, all while retaining compatibility with the vast Java ecosystem and the ability to run on the mature (mostly, after all these years/decades) JVM. It should also be significantly faster and scale better than anything written in interpreted languages like ruby or python.
Scalatra
Scala has a number of web frameworks available to it. Lift and Play are probably the most popular. However, I wanted something lightweight, so I looked and found a minimalistic framework called Scalatra, which attempts to mimic the excellent Sinatra framework over in Ruby-land. So, I decided to give it a shot.
Scalatra relies on the Simple Build Tool (sbt), and setting up a new project is fairly simple using g8:
g8 scalatra/scalatra-sbt
Firing up the build system is not difficult either, just execute the following in the project root directory:
g8 scalatra/scalatra-sbt
starting the build system is done by running the following in the project directory:
./sbt
I’m using IntelliJ IDEA for my development environment, and it just so happens there’s a helper plugin for sbt called gen-idea that generates all of the proper project files. I believe there is a similar plugin for eclipse users, if you’re one of those people.
Adding dependencies to the project is surprisingly easy compared to say, maven, or ivy. And when I say easy, I mean NO XML. To add support for my database and json, for example, I add the following lines to my project’s build.scala file:
"org.scalatra" %% "scalatra-json" % "2.2.1", "org.json4s" %% "json4s-jackson" % "3.2.4", "org.json4s" %% "json4s-ext" % "3.2.4", "org.squeryl" %% "squeryl" % "0.9.5-6", "postgresql" % "postgresql" % "9.1-901.jdbc4", "c3p0" % "c3p0" % "0.9.1.2",
squeryl is an ORM for Scala. Not quite as easy to work with as ActiveRecord, but at least it’s not Hibernate. C3p0 handles connection pooling.
Scalatra Routes
Scalatra handles routes much like Sinatra. Pretty easy actually, here’s a simple controller for a hypothetical record called “Person”:
import org.scalatra._ import org.json4s.{DefaultFormats, Formats} import com.caffeinatedrhino.db.DatabaseSessionSupport import com.caffeinatedrhino.testproj.models.Person import org.scalatra.json.JacksonJsonSupport import org.json4s.JsonAST.JValue class PersonsController extends ScalatraServlet with DatabaseSessionSupport with JacksonJsonSupport { protected implicit val jsonFormats: Formats = DefaultFormats before() { contentType = formats("json") } get("/") { Person.allPersons } }
What does it do? all requests to “/” — the servlet’s root, not necessarily the web root, result in a request to our Person model for all of the “Person” objects in the database. One thing that may not be obvious is that the response is sent as JSON… the before() filter automagically runs before all requests, setting the output type for each controller action to JSON. To enable this we have to mixin JacksonJsonSupport (it’s a Scala trait) and tell json4s which formats we want it to use when doing its serialization by setting that implicit variable (jsonFormats).
If you’re wondering how we register all of our servlets(i.e., controllers), Scalatra projects have a single ‘ScalatraBootstrap.scala’ file, that goes something like this:
import com.caffeinatedrhino.testproj.controllers.PersonsController import org.scalatra._ import javax.servlet.ServletContext import com.caffeinatedrhino.db.DatabaseInit class ScalatraBootstrap extends LifeCycle with DatabaseInit { override def init(context: ServletContext) { configureDb() context.mount(new PersonsController, "/persons") } override def destroy(context: ServletContext) { closeDbConnection() } }
So our Persons servlet is mounted at “/persons” — so a request to http://example.com/persons should result in retrieving our “Person” objects.
Database Support
In our ScalatraBootstrap class, you’ll also notice we call configureDb() in the init method (and a corresponding closeDbConnection() in the destroy method). The appliction is stood up and torn down here, so this is the natural place to set up our database (and close it). There’s a trait mixed into our ScalatraBootstrap class called DatabaseInit that provides these methods. Here it is:
import org.slf4j.LoggerFactory import java.util.Properties import com.mchange.v2.c3p0.ComboPooledDataSource import org.squeryl.adapters.PostgreSqlAdapter import org.squeryl.Session import org.squeryl.SessionFactory trait DatabaseInit{ val logger = LoggerFactory.getLogger(getClass) var cpds = new ComboPooledDataSource def configureDb() { val props = new Properties props.load(getClass.getResourceAsStream("/c3p0.properties")) cpds.setProperties(props) SessionFactory.concreteFactory = Some (() => connection) def connection = { logger.info("Creating connection with c3p0 connection pool") Session.create(cpds.getConnection, new PostgreSqlAdapter) } logger.info("Created c3p0 connection pool") } def closeDbConnection() { logger.info("Closing c3p0 connection pool") cpds.close } }
The usual properties needed to connect to the database are stored in a separate c3p0.properties file:
c3p0.driverClass=org.postgresql.Driver c3p0.jdbcUrl=jdbc:postgresql://localhost:5432/testdb user=testuser password=testpass c3p0.minPoolSize=1 c3p0.acquireIncrement=1 c3p0.maxPoolSize=50
Easy enough, but what about the DatabaseSessionSupport trait that we mixed into the controller? Oh, here it is, lifted almost verbatim from the scalatra documentation:
package com.caffeinatedrhino.db import org.squeryl.Session import org.squeryl.SessionFactory import org.scalatra._ object DatabaseSessionSupport { val key = { val n = getClass.getName if (n.endsWith("$")) n.dropRight(1) else n } } trait DatabaseSessionSupport { this: ScalatraBase => import DatabaseSessionSupport._ def dbSession = request.get(key).orNull.asInstanceOf[Session] before() { request(key) = SessionFactory.newSession dbSession.bindToCurrentThread } after() { dbSession.close dbSession.unbindFromCurrentThread } }
Finally, if you’re curious about our “Person” model, here it is:
package com.caffeinatedrhino.testproj.models import com.caffeinatedrhino.db.DBRecord import org.squeryl.PrimitiveTypeMode._ import org.squeryl.{Query, Schema} import org.squeryl.annotations.Column import java.sql.Timestamp class Person(val id: Long, @Column("USER_ID") val userID: Long, @Column("LAST_NAME") var lastName: String, @Column("FIRST_NAME") var firstName: String, @Column("DATE_OF_BIRTH") var dateOfBirth: Timestamp, @Column("CREATED_AT") val createdAt: Timestamp, @Column("UPDATED_AT") var updatedAt: Timestamp) extends DBRecord{ def this() = this(0, 0, "NO_NAME", "NO_NAME", new Timestamp(0), new Timestamp(0), new Timestamp(0)) } /** * Kind of a cross between a Schema and a DAO really. But I'll call it a Dao anyway * because it pleases me to do so. */ object PersonDao extends Schema { val persons = table[Person]("PERSONS") on(persons)(p => declare( p.id is(autoIncremented, primaryKey) )) } object Person{ def create(person: Person):Boolean = { inTransaction { val result = PersonDao.persons.insert(person) if(result.isPersisted){ true } else { false } } } def allPersons = { from(PersonDao.persons)(p => select(p)).toList } }
You’ll notice we’re using a Java type here, java.sql.Timestamp, as if it belonged in our scala code. Neat, eh? You also might have noticed that we have both a class and a singleton object named ‘Person’ in the same source file. In Scala, the object ‘Person’ would be said to be the companion object of class ‘Person.’ A class and its companion object can access each other’s private members (and they must both be defined in the same source file).
Well, that’s enough code for one blog entry. That wasn’t nearly as bad as I feared it would be. I’ve definitely seen more convoluted ways of accomplishing much the same thing in other languages/frameworks (*cough* Java/Spring/Hibernate *cough*). I’m enjoying Scala so far, hopefully it continues to grow on me.
this is lovely, I heard the same spirit as you, but I am coming for the PHP world and Im very happy coming to scala and scalatra.
thanks
LikeLike