Jeff Devine

I only tell you what to do because I love you

Simple-Build-Tool or How I RTFM and Stopped Hating on Maven

Tonight I was supposed to see WHY? at Le Poisson Rouge but for some completely unknown reason, I felt I would be better served staying in and working.

I’m trying to prototype a Scala-based REST API that gets called by a GWT front-end. My Scala is extremely weak but Martin Kleppmann’s Yes/No/Cancel had a great write-up accomplishing the aforementioned task using Jersey and Scala. Unfortunately for me, I’ve mangled my java environment trying to get GWT to run on Snow Leopard and I was having no luck getting Martin’s Maven POM to work.

Already regretting my decision to pass on the concert, I wasn’t going to waste my entire night cursing at Maven. Instead I took this opportunity to use Martin’s examples but apply them to Simple Build Tool. The following is how I RTFM and started learned how to use SBT. I’d love feedback so please look it over or follow along and let me know if there is a better way to go about this.

First, start a new project by creating a directory for the project and running sbt from within this new directory. When SBT is run with no action specified and it doesn’t detect a project structure, it prompts to create a new project. I used the following, mostly taking the default values:

1
2
3
4
5
Name: api
Organization []: com.jeffdevine
Version [1.0]:
Scala version [2.7.5]:
sbt version [0.5.3]:

The initial process downloads any dependencies needed and creates a default project structure:

1
2
3
4
5
6
7
8
9
10
11
12
lib/
project/
      boot/
      build.properties
src/
      main
          resources/
          scala/
      test
          resources/
          scala/
target/

My initial needs for prototyping the REST API are Jetty and Jersey, so I configured SBT to manage these dependencies by adding the file ./project/build/ApiProject.scala (note: build is a new directory):

ApiProject.scala
1
2
3
4
5
6
7
8
9
10
import sbt._


class ApiProject(info: ProjectInfo) extends DefaultWebProject(info)
{
   val snapshots = "Java.net Repository" at "http://download.java.net/maven/2/"
   val jetty6 = "org.mortbay.jetty" % "jetty" % "6.1.14" % "test->default"   
   val jersey = "com.sun.jersey" % "jersey-server" % "1.1.2-ea" % "test->default"
   val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1-ea" % "compile->default"
}

From the SBT interactive session, execute reload to recompile the project definition and update to download all the specified dependencies.

Back to Martin’s example, I slightly modified his Scala code and saved it in ./src/main/scala/Hello.scala:

Hello.scala
1
2
3
4
5
6
7
8
package com.jeffdevine.api
import javax.ws.rs._

@Path("/hello")
class Hello {
  @GET @Produces(Array("text/html"))
  def doGet = "<html><body><p>Hello? You could be seeing Why? right now...</p></body></html>"
}

The last bit is to tell Jetty what to do by creating a file in ./src/main/webapp/WEB-INF/web.xml (note: you’ll need to create the directory structure webapp/WEB-INF):

webapp/WEB-INF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>Example REST API</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.jeffdevine.api</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Example REST API</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>30</session-timeout>
  </session-config>
</web-app>

Back in SBT, execute jetty-run, and within a few seconds, jetty is serving the API on http://localhost:8080/hello. Firing off a GET request returns the HTML

     Hello? You could be seeing Why? right now...

Executing jetty-stop will kill the server, and jetty-restart restarts jetty, picking up any code changes.

While this is a basic example that could use a test case or two, it has given me what I need to start prototyping and I no longer feel like a schmuck for skipping tonight’s concert… which would have gone something like this: