Playing with Scalatra, Jetty and EC2

Last week I met a friend before going to a Clojure function at Skills Matter in London. He’s a Ruby fanatic – Sinatra and Rails, they’re grrreeeaaaat. I agree with him, the little bits of rails and Sinatra I have done have been enjoyable (much like Flask and Django, without the fsking white space delimiting). Somehow we got started on Scala, that led to Scalatra – the Scala version of Sinatra. It looks interesting and isn’t Lift, figured it would be worth a quick look.

I downloaded the latest sbt and created an alias in my bash_profile as sbt2 (I have an older version of sbt too).

alias sbt2=”java -Xmx1500M -jar /Users/biomunky/svn/sbt-launch.jar $@”

I then followed the instructions here to download it via sbt (not g8).

This gives you a blank-ish template. I modified src/main/scala/.scala – it should be the only file in the directory. To get something up and running quickly I made it look like this:

import org.scalatra._
import scalate.ScalateSupport

class PlayTimeServlet extends ScalatraServlet with ScalateSupport {
  get("/") {
        <h1>Hello Internet Person.</h1>
				<p>Would you like to try some <a href="form">simple maths?</a></p>
	get("/form") {
		<form action="/form/math" method="post">
				<input name="first_value"  type="text" value="" />
				<select name="operation">
				  <option value="plus">+</option>
				  <option value="minus">-</option>
				  <option value="divide">/</option>
				  <option value="multiply">*</option>
				<input name="second_value" type="text" value="" />
	      <input type="submit" value="Submit" />					

	post("/form/math") {
		val param1 = params.getOrElse("first_value",  "0")
		val param2 = params.getOrElse("second_value", "0")
		val operation = params.getOrElse("operation", "plus")
		val result = try { 
			operation match {
				case "plus"     => (param1.toDouble + param2.toDouble).toString 
				case "minus"    => (param1.toDouble - param2.toDouble).toString 
				case "multiply" => (param1.toDouble * param2.toDouble).toString 
				case "divide"   => if (param2.toDouble > 0.0) { 
						(param1.toDouble / param2.toDouble).toString
					} else {
						"Divide by Zero!"
		} catch { 
			case _ => "I can't do that math." 
		    <p>The result is: {result}</p>
  notFound {
    // Try to render a ScalateTemplate if no route matched
    findTemplate(requestPath) map { path =>
      contentType = "text/html"
    } orElse serveStaticResource() getOrElse resourceNotFound() 

If you start the Jetty and navigate to localhost:8080 you should see a page that reads “Would you like to try some simple maths?” – how very exciting!

Clicking the link will get a web form. Fill the details 2 + 2. Click submit. A POST! You should now see the result of the expression.

So that’s all fine on localhost, I wanted to see it outside sbt in another jetty server – specifically something I had on ec2. This means creating a .war file – this is achieved by running sbt(2) package. The war can be found under target/scala-2.9.1/.

Get a copy of jetty (tar zxvf ) and place the war under webapps/ in the jetty home directory. Start jetty by entering: java -jar start.jar. Browse to localhost:8080 and you will see the same site as above. Enter the same data (or whatever expression you want) and you should see the same result — i lied, it should’ve crashed, if it didn’t – goody for you.

I had to change a couple of things. I removed webapps/test.war and it’s context file found in contexts/ (don’t delete it just yet). I then created a file called playtime.xml in contexts/ -> playtime is the name of my project (the war is also called playtime.war). Rename the test.xml context to .war. Change line 23

/webapps/test.war to /webapps/playtime.war.

If you retry submitting the form, having restarted the server, you should now get the results page rather than the error.

Since I’ve been goofing about with ec2 (because you can get some stuff free for a year) I dumped it there. If you do this, you will also have to open your ec2 instance to port 8080. You can do this via: aws management console -> Network & Security -> Security Groups -> your-active-security-profile.