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:
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:
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):
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:
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):
<?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: