Skip to content

Simple Time Server

satabin edited this page Mar 25, 2013 · 3 revisions

This article is a tutorial that aims to give a simple server example.

The server serves a static html page that allows user to retrieve the server time by clicking on a button. We will explain how the application is structured to achieve this using tiscaf, so that you can get some insight on the concepts. For more details, please have a look at the getting started guide.

The code of this tutorial is available in the repository under the examples directory. You can start the code in sbt like this:

> project timeserver
> run-main tiscaf.example.TimeServer

and then access the page at http://localhost:8080/.

So how to build this server application?

Before we start, the first thing to do is

import tiscaf._

Now we can go...

First Component: Returning The Current Time

Our server must be able to return the current time when some URL is queried, let's say "/currenttime". To do this, we will implement a first application. In tiscaf, an application is done by implementing tiscaf.HApp and allows you to group actions that belong together. It can dispatch to the correct action using any information available in a request.

object TimeApp extends HApp {

  def resolve(req: HReqData): Option[HLet] = req.uriPath match {
    case "currenttime" => Some(TimeLet)
    case _             => None
  }

}

So, you only need to implement the resolve method, that returns the action to perform. In our case, this application only knows what to do if "/currenttime" is queried, and in this case returns the TimeLet which is the request handler associated to this action. In other cases, it says that it does not know what to do by returning None.

A request handler is called an HLet in tiscaf and in the case of simple handlers, you only need to implement tiscaf.HSimpleLet.

object TimeLet extends HSimpleLet {

  def act(talk: HTalk) {
    // simply return the current server time
    val time = new Date().getTime.toString
    talk.setContentLength(time.length)
      .write(time)
  }

}

The handler must implement the act method that gets a HTalk instance. HTalk exposes the interface to deal with the request data, the session data, and, what is of interest in our case, it allows us to send a response to the client. You must explicitly set the length of the answer, and then write the content (you could also set the mime type of the answer, for more details, see here).

Pretty simple, isn't it?

Second Component: Serving Static Content

Ok, we have a way to send back the current time to the client. Now, let's see how to server an HTML interface to query the time. Another application of our server is specifically to serve static contents, such as html pages and javascript files (we could add css files as well, or any other file needed). So we simple create a new application, just like the first one, but even simplier:

object StaticApp extends HApp {

  override def buffered : Boolean  = true // ResourceLet needs buffered or chunked be set

  def resolve(req: HReqData) = Some(StaticLet) // generates 404 if resource not found
}

This application returns a simple request handler named, StaticLet which will do all the work for us!

object StaticLet extends let.ResourceLet {
  protected def dirRoot = ""
  // what resources are indexes?
  override protected def indexes = List("index.html")
}

tiscaf has a default request handlers that allows to map URI to a directory in the classpath. It is called tiscaf.let.ResourceLet. If the resource is not found, it will generate an error 404. The only method to implement is dirRoot which tells tiscaf to which directory the root URL is mapped. In our case, the static resources are in the root package, so this prefix is empty.

Note: We also mentioned here that index.html is an index, so that we don't have to write it in the URL, but this is not mandatory.

The Static Content

The goal of this tutorial is not to explain how html/javascript code is written, so we won't explain it a lot here. It is pretty basic, there is just an index.html file that has a button to query the current server time and display it. And we use jquery to make an ajax request when the button is clicked and to extract the answer from the server.

The static content can be found in the repository. It is placed in the resources directory so that it is automatically included in the classpath by sbt.

Putting It All Together: The Server

First of all, we will need a server, which is quite easy to achieve by implementing the tiscaf.HServer trait.

object TimeServer extends HServer with App {

  def apps = Seq(TimeApp, StaticApp)

  def ports = Set(8080)

  // do not start the daemon stop thread
  override protected def startStopListener { }

  start

  println("press enter to stop...")
  Console.readLine

  stop
}

A server must implement two methods:

  • apps returns the list of applications on this server. Here we have two of them, one delivering the server time, and one delivering static content. The applications are searched for a matching request handler in the order they appear in the set. The first application returing Some(<hlet>) for a request is then chosen to handle that request. So the order of the applications matters! If no application is found, tiscaf answers with an error 404.
  • ports returns the list of ports the server is listening to.

Starting the server is simply done by calling the start method.

Note: we override the startStopListener method here to do nothing. Usually this method starts a thread that listens for some stop message to shut done the server, which can be useful if you start your server as a daemon.

You can now launch TimeServer That's all folks, you have your server running!