9

The following is possible using Python:

$ apt-get install python
$ easy_install Flask
$ cat > hello.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()
$ python hello.py

4 commands and 7 lines of code to get a web server running is very impressive indeed.

What's the Scala equivalent?

davidism
  • 121,510
  • 29
  • 395
  • 339
Matthew
  • 2,151
  • 2
  • 17
  • 13
  • 3
    You mean 4 commands on a system where apt-get exists, easy_install is bundled with python and using an sh shell. Do you assume having root access too? – huynhjl Nov 12 '10 at 16:25
  • 8
    Yes, I also assume that the system has a CPU, RAM, storage, an installed operating system, a keyboard, and a display ;) – Matthew Nov 12 '10 at 17:10
  • 3
    Is this a serious question, or just an attempt to show off how cool Python+Flask is? How many times do you need to set up a web server before needing to type, say, 15 commands instead of 4 is the rate-limiting step in your work? I grant that this is very cool, but the only application I can think of off the top of my head where this is required instead of merely fun is embedding an exploit in a small buffer that converts a target machine into a webserver. – Rex Kerr Nov 12 '10 at 21:45
  • 4
    It's a serious question. I couldn't find a single Scala or Java web framework that took less than two hours to get set up. I ran into some problem or other with every single one. I'd very much like to switch 100% of my coding efforts to Java/Scala, but the ease and fun of developing in Python, as exemplified by the above sequence of commands, is still compelling for most people, including myself to some extent. How can we make Scala so easy? – Matthew Nov 13 '10 at 02:42
  • 1
    Well, Lift took me under half an hour to set up (including installing maven and whatnot from the repository), but I see your point. It would have been helpful to have this motivation in the original question. – Rex Kerr Nov 13 '10 at 03:32
  • Why do you use the term *bootstrapping* in your question? I think it means something different, like installing the webserver, using the webserver itself or creating it using a webserver. – ziggystar Jun 22 '11 at 06:43

8 Answers8

11

This uses the HttpServer class that is built-in in JDK6. Feel free to suggest improvements, I'm new to Scala.

package org.test.simplehttpserver

import java.net.InetSocketAddress
import com.sun.net.httpserver.{HttpExchange, HttpHandler, HttpServer}
import collection.mutable.HashMap

abstract class SimpleHttpServerBase(val socketAddress: String = "127.0.0.1",
                                    val port: Int = 8080,
                                    val backlog: Int = 0) extends HttpHandler {
  private val address = new InetSocketAddress(socketAddress, port)
  private val server = HttpServer.create(address, backlog)
  server.createContext("/", this)

  def redirect(url: String) =
    <html>
      <head>
          <meta http-equiv="Refresh" content={"0," + url}/>
      </head>
      <body>
        You are being redirected to:
        <a href={url}>
          {url}
        </a>
      </body>
    </html>

  def respond(exchange: HttpExchange, code: Int = 200, body: String = "") {
    val bytes = body.getBytes
    exchange.sendResponseHeaders(code, bytes.size)
    exchange.getResponseBody.write(bytes)
    exchange.getResponseBody.write("\r\n\r\n".getBytes)
    exchange.getResponseBody.close()
    exchange.close()
  }

  def start() = server.start()

  def stop(delay: Int = 1) = server.stop(delay)
}

abstract class SimpleHttpServer extends SimpleHttpServerBase {
  private val mappings = new HashMap[String, () => Any]

  def get(path: String)(action: => Any) = mappings += path -> (() => action)

  def handle(exchange: HttpExchange) = mappings.get(exchange.getRequestURI.getPath) match {
    case None => respond(exchange, 404)
    case Some(action) => try {
      respond(exchange, 200, action().toString)
    } catch {
      case ex: Exception => respond(exchange, 500, ex.toString)
    }
  }
}

class HelloApp extends SimpleHttpServer {
  var count = 0

  get("/") {
    "There's nothing here"
  }

  get("/hello") {
    "Hello, world!"
  }

  get("/markup") {
    <html>
      <head>
        <title>Test Title</title>
      </head>
      <body>
        Test Body
      </body>
    </html>
  }

  def countPage = <html>
    <head>
      <title>Test Title</title>
    </head>
    <body>
      Count:
      {count}<a href="/increaseCount">++</a>
      <a href="/decreaseCount">--</a>
      <a href="/resetCount">Reset</a>
    </body>
  </html>

  get("/count") {
    countPage
  }

  get("/resetCount") {
    count = 0
    redirect("/count")
  }

  get("/increaseCount") {
    count = count + 1
    redirect("/count")
  }

  get("/decreaseCount") {
    count = count - 1
    redirect("/count")
  }

  get("/error") {
    throw new RuntimeException("Bad bad error occurred")
  }
}

object Main {

  def main(args: Array[String]) {
    val server = new HelloApp()
    server.start()
  }
}
9

You might find Unfiltered worth a look.

David Winslow
  • 8,472
  • 1
  • 31
  • 27
9

I know Max alread mentioned it, but I couldn't resist pointing out Scalatra's 6 lines hello world:

import org.scalatra._

class ScalatraExample extends ScalatraServlet {
  get("/") {
    <h1>Hello, world!</h1>
  }
}

Anyway, take a look at available Scala web frameworks.

EDIT

There's some discussion about how easy is to get the tooling ready, particularly with regards to Lift. So, here's a session on Ubuntu. Most of my time was spent trying to figure out where did Sun's Java go in the package manager. Anyway, once Java was installed, this is how it went, with all messages elided, so one can see what I actually had to type:

dcs@dcs-desktop:~$ wget -q -O bin/sbt-launch.jar http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.4.jar
dcs@dcs-desktop:~$ echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' > bin/sbt
dcs@dcs-desktop:~$ chmod u+x bin/sbt
dcs@dcs-desktop:~$ mkdir app
dcs@dcs-desktop:~$ cd app
dcs@dcs-desktop:~/app$ sbt
Project does not exist, create new project? (y/N/s) s
> *lifty is org.lifty lifty 1.4
> lifty create project-blank sample 2.1
> reload
> update
> jetty-run

There, web server running. Of course, you have to know about SBT and Lifty beforehand, to even know you'd use them to get a Scala Lift program running, but, on the other hand, I had never heard about Flask, so I'd certainly spend way more time trying to figure out how to get a web server application going in Python than I did getting a Lift one.

I also did not get it right on the first try -- I tried going for Scala 2.8.1 (the above uses a default 2.7.7 version, though 2.8.0 will work too), only to find out that there's no Lift version available for that version of Scala as yet. On the other hand, I had lifty installed already, and de-installed it just to show the command that installs it.

I do wish there was a Debian/Ubuntu package for SBT -- it's just a tiny shell script and a jar file, after all, and it takes care of downloading Scala, Lift, etc, and at whatever version you need.

It is a different model than Python and Ruby, where the language comes with a package manager which handles most things.

Community
  • 1
  • 1
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Thanks, 6 lines of Scala is awesome! Is there a way to get Scala and Scalatra installed and launch the server with just a few commands? – Matthew Nov 12 '10 at 17:11
  • See "Quick starting Scalatra": http://blog.everythings-beta.com/?p=430. This glosses over the step of installing sbt, but it isn't difficult. Of course, it would be an advantage to have a debian/ubuntu package for it. – Aaron Novstrup Nov 13 '10 at 16:43
  • @Matthew Sure. Quickstart on Scalatra's web page is very short. You just need git and sbt installed. I do wish they'd implement a processor like Lifty (https://github.com/Lifty/Lifty), so that sbt would suffice. – Daniel C. Sobral Nov 13 '10 at 16:44
3

Well, there's Scalatra, which aims to be analogous to Ruby's Sinatra in terms of functionality and ease of use.

Max A.
  • 4,842
  • 6
  • 29
  • 27
2

This solution uses a JAX-WS Endpoint:

import java.io._
import javax.xml.ws._
import javax.xml.ws.http._
import javax.xml.transform._
import javax.xml.transform.stream._

@WebServiceProvider
@ServiceMode(value=Service.Mode.PAYLOAD)
class P extends Provider[Source] {
  def invoke(source: Source) = new StreamSource( new StringReader("<p>Hello There!</p>"));
}

val address = "http://127.0.0.1:8080/"
Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address)

println("Service running at "+address)
println("Type [CTRL]+[C] to quit!")

Thread.sleep(Long.MaxValue)

You can copy it to a file WebServer.scala and run it simply by typing:

scala WebServer.scala
gruenewa
  • 1,666
  • 11
  • 16
1

You could use an embedded Jetty Server:

/*
 * Required Libs: Jetty, Servlet API
 *
 * Compile:
 *   scalac -cp jetty-6.1.14.jar:jetty-util-6.1.14.jar:servlet-api-2.5-6.1.14.jar WebServer.scala
 *
 * Run:
 *  scala -cp .:jetty-6.1.14.jar:jetty-util-6.1.14.jar:servlet-api-2.5-6.1.14.jar WebServer
 */
import org.mortbay.jetty.Server
import org.mortbay.jetty.servlet.Context
import javax.servlet.http.{HttpServlet,
                       HttpServletRequest, 
                       HttpServletResponse}

class HelloServlet extends HttpServlet {
  override def doGet(req : HttpServletRequest, resp : HttpServletResponse) =
    resp.getWriter().print("Hello There!")
}

object WebServer {
  def main(args: Array[String]) {
    val server = new Server(8080)
    val root = new Context(server, "/", Context.SESSIONS)
    root.addServlet(classOf[HelloServlet], "/*")
    server.start()

    println("Point your browser to http://localhost:8080/")
    println("Type [CTRL]+[C] to quit!")

    Thread.sleep(Long.MaxValue)
  }
}

In case you target for a LOC comparison, You use the HTTP server embedded with the Sun JDK. Another solution could be to use javax.xml.ws.Endpoint and the Provider API.

gruenewa
  • 1,666
  • 11
  • 16
1

Scala equivalent is in 6 commands:

$ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
$ ~/bin/cs n8han/giter8
$ ~/bin/g8 scalatra/scalatra-sbt --name=scalatra-example
$ cd scalatra-example
$ wget http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-tools.sbt/sbt-launch/0.11.0/sbt-launch.jar
$ java -Xmx512M -jar sbt-launch.jar ~jetty-run

Using Play,

step #1 download Play, then
$ play install scala
$ play new myproject --with scala
$ play run myproject
Elvan
  • 621
  • 3
  • 4
0

As David Winslow mentioned, Unfiltered usage code snippet

INFO: Simple API capabilities for Apache Spark word count example written in Scala using Unfiltered.

object SimplePlan extends Plan {
  def intent = {
  case req @ GET(Path("/get")) => {
    Ok ~> ResponseString(WordCount.count("Test #1: Test the Default word count program").mkString("\n"));
  }

  case req @ POST(Path("/get_custom")) => {
    val custom_string = Body.string(req)
    Ok ~> ResponseString(WordCount.count(custom_string).mkString("\n"))
  }
 }
}

object SimpleServer extends App {
  val bindingIP = SocketPortBinding(host = "localhost", port = 8080)
  unfiltered.jetty.Server.portBinding(bindingIP).plan(SimplePlan).run()
}

Complete example is here

Community
  • 1
  • 1
Sudhakar
  • 43
  • 9