Skip to content

Instantly share code, notes, and snippets.

@havocp
Created August 31, 2011 16:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save havocp/1184028 to your computer and use it in GitHub Desktop.
Save havocp/1184028 to your computer and use it in GitHub Desktop.
Dreamforce Open Source Lab

This quickstart will get you going with Scala and the twitter-finagle web library on the Cedar stack. Prerequisites

  • Basic Scala knowledge, including an installed version of sbt 0.10.x and a JVM.
  • Basic Git knowledge, including an installed version of Git.
  • Your application must be compatible with sbt 0.10.1 or higher and scala 2.8.1.
  • Your application must run on the OpenJDK version 6.
  • An installed version of Ruby.

Install the Heroku Command-line Client

If you have Rubygems on your system, you can install the Heroku client with:

$ gem install heroku

Otherwise, download this tarball, extract it, and put the resulting directory into your $PATH:

$ wget http://assets.heroku.com/heroku-client/heroku-client.tgz
Saving to: `heroku-client.tgz'
100%[==================================================================>] 412,661      535K/s   in 0.8s

$ tar xzf heroku-client.tgz && echo "Add $PWD/heroku-client to your \$PATH."
Add /Users/adam/heroku-client to your $PATH.

You will need ruby in your path, which is available by default on Mac OS X, can be installed on Ubuntu with apt-get install ruby-dev, or on Windows with RubyInstaller. Write Your App

You may be starting from an existing app. If not, here’s a simple “hello, world” sourcefile you can use: src/main/scala/Web.scala

import com.twitter.finagle.builder.ServerBuilder
import com.twitter.finagle.http.Http
import com.twitter.finagle.Service
import com.twitter.util.Future
import java.net.InetSocketAddress
import org.jboss.netty.buffer.ChannelBuffers
import org.jboss.netty.handler.codec.http.{DefaultHttpResponse, HttpResponse, HttpRequest, HttpVersion, HttpResponseStatus}
import util.Properties

object Web {
  def main(args: Array[String]) {
    val address = new InetSocketAddress(Properties.envOrElse("PORT", "8080").toInt)
    println("Starting on port:"+address.getPort)
    val server = ServerBuilder().codec(Http()).name("hello-server").bindTo(address).build(new Hello)
    println("Started.")
  }
}

class Hello extends Service[HttpRequest, HttpResponse] {
  def apply(req: HttpRequest): Future[HttpResponse] = {
    val response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
    response.setContent(ChannelBuffers.wrappedBuffer("Hello World".getBytes))
    Future.value(response)
  }
}

##Declare the sbt version in project/build.properties

sbt.version=0.10.1

Cedar recognizes an app as Scala by the existence of a project/build.properties file.

##Add the Typesafe start-script plugin to your project.

This plugin will add a stage task to sbt that generates start scripts for your application. Heroku's Scala support runs the tasks clean compile stage to build your SBT application. While the start-script plugin provides an implementation of the stage task, you are free to provide your own.

./project/plugins/build.sbt

resolvers += {
    val typesafeRepoUrl = new java.net.URL("http://repo.typesafe.com/typesafe/ivy-snapshots")
    val pattern = Patterns(false, "[organisation]/[module]/[sbtversion]/[revision]/[type]s/[module](-[classifier])-[revision].[ext]")
    Resolver.url("Typesafe Ivy Snapshot Repository", typesafeRepoUrl)(pattern)
}

libraryDependencies <<= (libraryDependencies, sbtVersion) { (deps, version) =>
    deps :+ ("com.typesafe.startscript" %% "xsbt-start-script-plugin" % "0.1-SNAPSHOT" extra("sbtversion" -> version))
}

##Declare Dependencies In build.sbt

Here’s an example build.sbt for the Scala/Finagle app we created above: build.sbt

import com.typesafe.startscript.StartScriptPlugin

seq(StartScriptPlugin.startScriptForClassesSettings: _*)

name := "hello"

version := "1.0"

scalaVersion := "2.8.1"

resolvers += "twitter-repo" at "http://maven.twttr.com"

libraryDependencies += "com.twitter" % "finagle-core" % "1.7.5"

Prevent build artifacts from going into revision control by creating this file: .gitignore

target
project/boot
project/target

Build your app locally

Run the following command to build your app locally. Again this assumes you have the typesafe xsbt-start-script-plugin installed.

$sbt clean compile stage
...
[info] Compiling 2 Scala sources to .../target/scala-2.8.1.final/classes...
[success] Total time: 7 s, completed Aug 18, 2011 12:23:04 PM
[info] Aliasing start-script to start-script-for-jar in hello
[info] Set current project to default-f0c56e (in build file:...)
[info] Packaging .../target/scala-2.8.1.final/hello_2.8.1-1.0.jar ...
[info] Done packaging.
[info] Wrote start script for jar .../target/scala-2.8.1.final/hello_2.8.1-1.0.jar to.../target/start
[success] Total time: 0 s, completed Aug 18, 2011 12:23:05 PM

##Declare Process Types With Foreman/Procfile

To run your web process, you need to declare what command to use. In this case, we simply need to execute our Web main method. We’ll use Procfile to declare how our web process type is run.

The typesafe xsbt-start-script-plugin will generate a start script which will set up your runtime classpath, and execute the main class you specify.

Here’s a Procfile for the sample app we’ve been working on:

web: target/start Web

Now that you have a Procfile, you can start your application with the Foreman gem:

$ gem install foreman
$ foreman start

Your app will come up on port 5000. Test that it’s working with curl or a web browser, then Ctrl-C to exit.

##Store Your App in Git

We now have the three major components of our app: dependencies in build.sbt, process types in Procfile, and our application source in src/main/Web.scala. Let’s put it into Git:

$ git init
$ git add .
$ git commit -m "init"

##Deploy to Heroku/Cedar

Create the app on the Cedar stack:

$ heroku create --stack cedar
Creating glowing-snow-27... done
Created http://glowing-snow-27.herokuapp.com/ | git@heroku.com:glowing-snow-27.git
Git remote heroku added

Deploy your code:

$ git push heroku master
Counting objects: 31, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (24/24), done.
Writing objects: 100% (31/31), 5.42 KiB, done.
Total 31 (delta 14), reused 0 (delta 0)

-----> Heroku receiving push
-----> Updating alpha language packs... done
-----> Scala (SBT) app detected
-----> Selecting SBT 0.10
-----> Installing SBT from http://s3.amazonaws.com/sclasen-langpack-scala/sbt-launch-0.10.1.jar... done
-----> Building app with SBT
-----> executing sbt clean compile stage
       Getting net.java.dev.jna jna 3.2.3 ...
       downloading http://s3pository.heroku.com/maven-central/net/java/dev/jna/jna/3.2.3/jna-3.2.3.jar
...
       [info] Compiling 1 Scala source to /tmp/build_2hojbcloqzzaf/target/scala-2.8.1.final/classes...
       [success] Total time: 25 s, completed Aug 18, 2011 12:08:52 AM
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 23.8MB
-----> Launching... done, v5
       http://gentle-autumn-906.herokuapp.com deployed to Heroku

To git@heroku.com:gentle-autumn-906.git
 * [new branch]      master -> master
 $

Now, let’s check the state of the app’s processes:

$ heroku ps
Process       State               Command
------------  ------------------  --------------------------------------------
web.1         up for 10s          target/start Web 

The web process is up. Review the logs for more information:

$ heroku logs
2011-08-18T00:13:41+00:00 heroku[web.1]: Starting process with command `target/start Web `
2011-08-18T00:14:18+00:00 app[web.1]: Starting on port:28328
2011-08-18T00:14:18+00:00 app[web.1]: Started.
2011-08-18T00:14:19+00:00 heroku[web.1]: State changed from starting to up
...

Looks good. We can now visit the app with heroku open.

##Console

Cedar allows you to launch a REPL process attached to your local terminal for experimenting in your app’s environment:

$ heroku run sbt console
Running sbt console attached to terminal... up, run.1
[info] Set current project to default (in build file:/app/)
[info] Updating...
[info] Done updating.
[info] Compiling 1 Scala source to /app/target/scala-2.8.1.final/classes...
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.8.1.final (OpenJDK 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

This console has your application code available. For example:

scala> Web.main(Array.empty[String])
Starting on port:33418
Started.

scala>

##One-off Processes

You can run a one-off Processes attached to the terminal in the same way, as long as the Main Class exists in your deployed app. Try making a small class that prints to the console and exits: src/main/scala/Demo.scala

object Demo {
    def main(args:Array[String]){
      println("Hello From Demo")
    }
}

Commit and deploy this new code:

$ git add src/main/scala/Demo.scala
$ git commit -m "hi"
$ git push heroku master

Run with heroku run:

$heroku run 'target/start Demo'
Running target/start Demo attached to terminal... up, run.6
Hello From Demo
$
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment