Continuing with our effort to open up queues for mobile devices, we need to make one more modification to the server before we code the actual mobile interface either on the iOS or on Android. We have a Socket open on the server already that will be used for real time queue status updates to the mobile devices; but we also need a web service for dispensing tokens to the mobile devices. The web interface used Ajax to get a new token, for the mobile world the same functionality will be made available via a web service. In today’s blog we will see how easy it is to add a basic web service using Scala.
Dispensing tokens via a RESTful Web Service
We will add a web service that will dispense tokens from our solitary queue on the server – the AQueue. Lift makes it extremely easy to add a web service using a trait called RestHelper. This trait makes heavy use of pattern matching, implicit conversions, and extractors in Scala. The RestHelper trait supports both XML and JSON requests. It makes heavy use of extractors in Scala to break down a HTTP request into its constituent parts, separating not only the path of the request but also determining the verb of the HTTP request – raw GET or a GET for JSON or a GET for XML, a raw POST request, PUT request or a DELETE request. It determines the type of HTTP request by looking at the Accept header of the request as well as the suffix of the path (.xml, .json etc). Extractors in Scala are objects that can match a value and then break it apart into its constituents. An extractor object needs to provide an “unapply” method that is used to match a value and then break it apart. The RestHelper object provides such extractors.
The way RestHelper intercepts our HTTP request (in order to break it apart), is to hook itself into the custom request dispatch mechanism supported by Lift. A custom dispatch bypasses the Lift’s normal mechanism of going through a template matching and can pass the request directly to a custom method that you define. Custom dispatch in Lift is realized by a PartialFunction that takes a request and returns a function that returns the final response. The type of RestHelper is such a PartialFunction.
Once our RestHelper is hooked into the custom dispatch mechanism of Lift any HTTP request that matches the pattern that our RestHelper can handle will be forwarded to our RestHelper. We define the pattern of requests that we can handle by calling the “serve” method provided by RestHelper (as part of its constructor when the instance of our RestHelper is instantiated by the custom dispatch mechanism of Lift). This method expects a partial function that can take a http request and converts it into another function of a type that takes nothing but returns a Box of LiftResponse.
Here the code of our RestHelper which we have named WebSvcTokenDispenser.
[sourcecode language=”scala”]
package Lift.lib
import net.liftweb.http.rest.RestHelper
import Lift.model.AQueue
import Lift.model.AQueue._
import net.liftweb.json.JsonAST.JValue
import xml.Node
import net.liftweb.http.PlainTextResponse
/**
*
* User: Anupam Chandra
*/
object WebSvcTokenDispenser extends RestHelper {
serve {
case "dispense" :: "token" :: Nil JsonGet _ => AQueue.dispenseAToken: JValue
case "dispense" :: "token" :: Nil XmlGet _ => AQueue.dispenseAToken: Node
case "dispense" :: "token" :: Nil Get _ => AQueue.dispenseAToken : PlainTextResponse
}
}
[/sourcecode]
This code is very simple. It states that our WebSvcTokenDispenser is a RestHelper which means that we get all the extractors, and dispatch mechanism for free. All we define is what kind of request we can handle and the response that we will generate for that request. We do that by calling the “serve” method when our RestHelper is created. The serve method registers a partial function with the RestHelper class. When a request comes in, Lift will match the request against all the dispatchers it has. The first one that has a Partial Function that has the request pattern defined will be invoked.
The way we hook our RestHelper into the custom dispatch mechanism of Lift is by configuring it in LiftRules inside the Boot.scala class like this –
[sourcecode language=”scala”]
LiftRules.statelessDispatchTable.append(WebSvcTokenDispenser)
[/sourcecode]
Once we have our dispatch partial function WebSvcTokenDispenser, registered in the statelessdispatchTable, the dispatch mechanism will give our function an opportunity to service a request that it can.
Now let’s look at what request pattern we are registering. The partial function that we are passing to the Serve method of RestHelper class can handle three types of GET requests, a raw GET determined by the pattern
[sourcecode language=”scala”]
case "dispense" :: "token" :: Nil Get _ => PlainTextResponse(AQueue.dispenseAToken.toString)
[/sourcecode]
Which basically says that it the URI contains a path (after the context) dispense/token or dispense/token.htm, we can handle it. A JSON GET determined by the pattern
[sourcecode language=”scala”]
case "dispense" :: "token" :: Nil JsonGet _ => AQueue.dispenseAToken: JValue
[/sourcecode]
and an XML GET determined by the pattern
[sourcecode language=”scala”]
case "dispense" :: "token" :: Nil XmlGet _ => AQueue.dispenseAToken: Node
[/sourcecode]
Lift parses the http request by taking the URI between the context path and the query string and creates a List of strings (List[String]) where each path element of the URI after the context is inserted into the List, So in this case, a request
http://localhost:8080/dispense/token.htm will result in a List with two entries “dispense” and “token”. The suffix of the URI determines the type of HTTP request, GET, JSON GET or XML GET.
So http://localhost:8080/dispense/token.json will result in a JSON request and http://localhost:8080/dispense/token.xml will result in an XML request. We can use cURL to test this
[sourcecode language=”shell”]
[anupamchandra@FCOH1W-3P6TKM1: ~]> curl http://localhost:8080/dispense/token.json
{
"$outer":{
},
"number":101
}
[anupamchandra@FCOH1W-3P6TKM1: ~]> curl http://localhost:8080/dispense/token.xml
<?xml version="1.0" encoding="UTF-8"?>
<$outer></$OUTER>105
[anupamchandra@FCOH1W-3P6TKM1: ~]> curl http://localhost:8080/dispense/token.htm
Token(106)
[anupamchandra@FCOH1W-3P6TKM1: ~]>
[/sourcecode]
When the request type matches one of the entries defined in the argument to the serve method, the partial function associated with that request is invoked. So in case of raw Get, the function that will be invoked will return a PlainTextResponse that will look like Token(100). In case of JSON or XML get request, the function will convert the Token dispensed by the queue into a JValue or a xml Node.
The appropriate type of response is created by using the implicit conversion mechanism of Scala. These implicit conversions will take one type and convert it into another type implicitly (the compiler interjects the implicit functions). We define these conversions in the AQueue class like this
[sourcecode language=”scala”]
package Lift.model
import com.scoovyfoo.domobj.TokenDispenser
import Lift.comet.TokenRefresher
import net.liftweb.json.JsonAST.JValue
import xml.Node
import net.liftweb.json.{Xml, Extraction}
import net.liftweb.http.PlainTextResponse
/**
*
* User: Anupam Chandra
*/
object AQueue {
val tokenDispenser = new TokenDispenser(100)
def dispenseAToken = {
val token = tokenDispenser.dispense
TokenRefresher ! "R"
token
}
private implicit val formats = net.liftweb.json.DefaultFormats
implicit def toPlainText(token: tokenDispenser.Token): PlainTextResponse = PlainTextResponse(token.toString)
implicit def toJson(token: tokenDispenser.Token): JValue = Extraction.decompose(token)
implicit def toXml(token: tokenDispenser.Token): Node = {Xml.toXml(token)}
}
[/sourcecode]
Here we see that there are three implicit conversion defined one each for plain text, JSON and XML. Our WebSvcTokenDispenser class will use these implicit converters as a response to a HTTP request that matches the one it can service.
Conclusion
That is all there is to it. In less than 25 lines of code (ignoring comments and empty lines) we have added a fully functional web service for our Virtual Queues that can serve not only plain text tokens but also XML and JSON based tokens. Now we are fully ready to put up a mobile interface for our virtual. This is what we will do in our next blog.
