Scala

Scala REST Example

In this Example series, we will see how to have a REST (Web) Application in Scala.

The following table shows an overview of the entire article:
 
 
 
 
 
 
 
 
 

1. Pre-requisites

The readers are expected to know the following pre-requisites for this Scala-REST example.

  1. Hands on Java Programming
  2. Hands on Scala Programming – Basics
  3. Hands on Java Web (Servlet) Programming – At least for the basic HTTP Methods (Get/Post and the Routing)
  4. Exposure to REST Style / Architecture – We will cover the minimal introduction in this article
  5. JSON data format

2. Environment

The examples in this article are executed in the Command Prompt / Shell with the Scalatra with the REST Client – an Add-on for Firefox browser. But they are guaranteed to work with any of the IDEs having a proper support for Scala, Scalatra and REST (like Eclipse – Scala IDE, IntelliJ IDEA etc.,) through the respective plugins. The examples are executed with the Scalatra Version 2.4.0.RC1 and Scala Version: 2.11.7 in Windows 7.

You may refer to my previous tutorial here on how to install Scala, if you are a beginner with Scala.

3. REST

REST stands for Representational State Transfer, which is a new buzzword and an architectural pattern for the request/response paradigm for the Service Oriented Architecture.

3.1 What is REST

According to Wikipedia, the definition of REST is as follows.

In computing, representational state transfer (REST) is the software architectural style of the World Wide Web. The purpose of REST architecture is to induce performance, scalability, simplicity, modifiability, visibility, portability, and reliability. More precisely, REST is an architectural style consisting of a coordinated set of components, connectors, and data elements within a distributed hypermedia system, where the focus is on component roles and a specific set of interactions between data elements rather than implementation details.

The term representational state transfer (REST) was introduced and defined in 2000 by Roy Fielding in his doctoral dissertation at UC Irvine. REST has been applied to describe desired web architecture, to identify existing problems, to compare alternative solutions and to ensure that protocol extensions would not violate the core constraints that make the web successful. Fielding used REST to design HTTP 1.1 and Uniform Resource Identifiers (URI).

To the extent that systems conform to the constraints of REST they can be called RESTful. RESTful systems typically, but not always, communicate over Hypertext Transfer Protocol (HTTP) with the same HTTP verbs (GET, POST, PUT, DELETE, etc.) that web browsers use to retrieve web pages and to send data to remote servers. REST systems interface with external systems as web resources identified by Uniform Resource Identifiers (URIs), for example /people/tom, which can be operated upon using standard verbs such as GET /people/tom.

In Web application programming, we have most of the interactions happen through Request-Response mechanism. REST has made it much simplified and meaningful to have a better understanding of the client requests by simply looking at the URL. For example, the requests will be confirming to one of the HTTP methods which look like verbs in Simple English as follows.

  1. GET to retrieve an existing resource from the System. Example /customer/1 will retrieve the customer whose Id is 1, whereas /customers will retrieve all in the System.
  2. POST to submit a resource (mostly for creating a new resource). Example /customer/create can create a new customer when submitted the request via HTTP Post method
  3. PUT for putting back with changes (for Update). Example /customer/1 with a HTTP method PUT will update the customer with Id 1 with the data submitted along with this request.
  4. DELETE for removing an existing resource. Example cusomter/1 with a HTTP method DELETE will remove the customer with the Id 1 from the System.
  5. OPTIONS is to retrieve the list of operations supported by the Server
  6. Few other Verbs – HEAD, TRACE, CONNECT etc., which are not very common as those of the above

For more detailed understanding of the HTTP Verbs, please refer to this Wikipeida – HTTP Verbs

3.2 REST Client

For the most famous HTTP verbs like GET and POST, we can trigger the requests with a Web application using a direct hyperlink which defaults to GET or with a HTML form by placing the appropriate HTTP verb for the method attribute of the form element as below.

<form name=’myForm’ action=’/MyAction’ method=’POST’>

However, for the other HTTP methods like PUT, DELETE etc., we should better use a HTTP client that facilitates not just the passing of HTTP request with an appropriate method, but also to capture the response retrieved in both RAW HTML output as well as the formatted/highlighted one (post rendering). This way the HTTP client will be a simple and better tool that would eradicate the need of a separate web application with the HTML form that again requires an extra work of desining the form and deploying it in the Web Server.

One such client we will use is REST Client, which is an add-on for the Firefox browser. REST Client is a simple and easy to use add on (tool) to work with Firefox. It is more similar to the Postman extension to the Google chrome browser. Every browser will have its own compatible add-on/extensions to work with. The readers can do a Google search to find the appropriate HTTP client. There are few CLI (Command Line) tools like Httpie (prounced as Http Pie, which is a CLI, cURL-like tool for humans) – that will work like a command line application in your desktop.

The REST client Addon for Firefox looks like below.

REST Client Addon for Firefox

The REST Client Add-on is pretty simple and the sections are self-explanatory. The input section starts with a drop down on the top left which has the list of HTTP verbs – by default GET selected. Next to that on the right is the actual URL to be triggered , for example http://localhost:8080/customers, if your server is running on the localhost listening to the port number 8080. You can replace the values accordingly.

The bottom text area has the Request Body, which is to have the request data to be sent along with the Request. It may not be needed for GET requests, but will be very much useful and needed for the POST and PUT methods. Once we are good with our request particulars, we can click the Send button on the right side with the red background color, which will take the request information (URL, Body, HTTP Method) and trigger an appropriate request to the Server (that is taken from the URL itself).

Installing the RESTClient add-on is a pretty simple task. You can hit the URL – RESTClient – Firefox Addon on your firefox browser and then follow the onscreen installation to get the extension added to your browser. You may need to restart your browser for this extension to get this running post installation. Please read the documentation for further information and customization if any on the extension.

3.3. Common Use Cases

For our example, we will work with the most common use cases which are GET, POST and DELETE. You can use the same style for the rest of the HTTP methods if you wish to extend your application further with the support of those other HTTP verbs/methods.

We will have a customer management application in which we will expose our services as a RESTful API that will have – GET, POST and DELETE using which the clients (can be a human or any other system/machine) will have an opportunity to retrieve all or specific customer, create a new customer and remove a customer from the System.

In a real world example, the REST APIs may be followed by an Authentication mechanism to validate the clients thereby preventing the unauthorized access to the System.

As it is beyond the scope of this article, we will not have any authentication mechanism for our example.

4. Scalatra

Scalatra is a simple, accessible and free web micro-framework. It combines the power of the JVM with the beauty and brevity of Scala, helping you quickly build high-performance web sites and APIs.

A little more detailed introduction from Wikipedia goes as follows.

Scalatra is a free and open source web application framework written in Scala. It is a port of the Sinatra framework written in Ruby. Scalatra is an alternative to the Lift, Play!, and Unfiltered frameworks.

Scalatra is an example of a microframework, a web software development framework which attempts to be as minimal as possible.

We will see how to install and work with Scalatra below.

4.1 Installing Scalatra

Installing Scalatra is very simple. It requires Scala as a pre-requisite. You may have Java JDK installed in your machine prior to this installation.

As per this URL, this is a two step process. Installation of Scalatra depends on giter8 which is again depending upon Conscript which are simple utilities facilitating the installation of Scalatra from Github repositories at the latest branch. Post which we will use giter8 to install Scalatra-SBT which is a Simple Build Tool (SBT) for Scala that helps us to build any Scala application.

These tools need an active Internet connection as they need to grab the source code and binaries from GitHub repositories.

4.2 Pre-requisites – Installing Conscript and Giter8

Conscript is a tool for installing and updating Scala code and giter8 is a tool for downloading the project templates from the Github Repositories. It is the recommended way to generate Scalatra project skeletons.

Please read this URL for downloading and installing Conscript in your System. It requires an executable .jar file to be downloaded first.

For the first time, execution of the Jar file it takes a good amount of time, depending upon your Internet Connection speed, to pull the binaries from GitHub repositories. Please be informed of this and be patient until the installation gets completed as per the instruction given in the Concscript website.

By default it gets installed in the %USER_HOME%/.conscript directory. In case of Windows, for example, it gets installed in to C:\Users\rags\.conscript in my Windows 7 Desktop. You may have to add the installation directory of conscript to your PATH environment variable, in case you wish to execute the cs executable command from anywhere in your desktop, else you need to navigate to the bin directory of the conscript installation directory to execute cs command to install the other tools/dependencies via Conscript.

Once the Conscript is successfully installed, it will generate a cs.bat (or cs.sh if you are using a *nix/Mac OS) file in the Conscript installation directory (C:\Users\rags\.conscript).

Post successful installation of Conscript tool, you can refer to this URL to install giter8 in your System.

The official definition of the giter8 tool from the GitHub project home page is as follows.

Giter8 is a command line tool to generate files and directories from templates published on github or any other git repository. It’s implemented in Scala and runs through the sbt launcher, but it can produce output for any purpose.

Please remember that Conscript is a pre-requisite for installing giter8. You need to navigate to the %CONSCRIPT_HOME%/bin directory of Conscript Installation directory and issue the following command in the terminal. cs foundweekends/giter8 to install giter8 in your System. This will invoke the cs – conscript executable by passing the giter8 which is present in the foundweekends navigation path.

Once again, installing giter8 might take a bit of time depending on your Internet Connection speed, as giter8 will download its own Scala libraries as its pre-requisites.

Once the giter8 tool is successfully installed, it will generate a g8.bat (or g8.sh if you use *nix/Mac OS) file in the Conscript Installation directory (C:\Users\rags\.conscript).

4.3 SBT (Scala Build Tool)

SBT stands for Simple Build Tool. It is an interactive build tool (like Ant, Maven etc.,) for Scala with the task definition (build.scala) file to determine the dependencies required for a project to be set up.

It is much like Maven where you can configure your project dependencies and the corresponding version for each of the dependency libraries. While executing, SBT will pull all the dependent libraries from the Internet (mostly from Maven or GitHub repositories) of the appropriate version. At the end it will also give you a status of whether it was able to download all the dependencies successfully or it had procuded any warnings/errors so as to indicate the user to take any corrective meausures against the odds.

Tip
For our example, we do NOT need to install SBT separately. Using giter8, it will have scalatra-sbt downloaded as part of the Scalatra project from the template. The downloaded and configured project will have the ‘sbt’ installed implicitly.

4.4 Configuring Scalatra via SBT

Now that we had installed the required dependencies, let us go ahead and download the Scalatra via SBT using giter8.

You can refer this URL for installing scalatra-sbt using giter8. The installation will ask several questions as the user’s choice for the significant properties, being an interactive tool. In case you don’t want to specify an explicit value, you can press enter to go with the default value supplied within the square brackets.

For example, the scalata-sbt tool is configured as follows.

Please remember that I have added the bin directory of %CONSCRIPT_HOME% directory which is C:\Users\rags\.conscript in my machine to the PATH environment variable. With which I am free to invoke the g8 command anywhere in my System.

Installing scalatra-sbt

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra> g8 scalatra/scalatra-sbt
organization [com.example]: com.example.javacodegeeks.scala.rest
name [My Scalatra Web App]: ScalaRESTWebApp
version [0.1.0-SNAPSHOT]:
servlet_name [MyScalatraServlet]:
package [com.example.app]: com.example.javacodegeeks.scala.rest
scala_version [2.11.8]:
sbt_version [0.13.11]:
scalatra_version [2.4.1]: 2.4.0.RC1

Template applied in .\scalarestwebapp


C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra

Please be informed that wherever I have required a specific version/value I have entered them in the command line. Rest of them had taken the default values supplied by the scalatra-sbt tool. The options are self-explanatory. However you may read this URL for a detailed explanation of the terms.

This will download the project with the given name scalarestwebapp in our case, in the directory in which the g8 command was issued with the options specified.

I have given 2.4.0.RC1 as a Scalatra version for using JSON format in our example, because as on date, the latest version has an issue with JSON. You can refer this URL for the same.

4.5 Customizing the SBT build file for JSON libraries

In order to work with JSON outputs, we need to download few JSON libraries explicitly, as they are not available by default.

To accomplish the same, we need to edit the build.scala file available in the scalatrarestwebapp\project directory as follows. Add the following lines at the end of the libraryDependencies += Seq( line. Please remember to add a comma(,) to the existing last line to have a continuation, otherwise the SBT will throw a Syntax error while being executed.

changes to build.scala file for JSON libs

        "org.scalatra" %% "scalatra-json" % "2.4.0.RC1",
        "org.json4s"   %% "json4s-jackson" % "3.3.0.RC1",
        "org.json4s"   %% "json4s-native" % "3.3.0.RC2"

A pictorial representation has been given for your easy reference and identification

changes to build.scala file for JSON libs

4.6 First Project – Hello Scalatra

Now that we have configured our scalatra-sbt project successfully, let us run our first project. Beforehand, let us have a look at our Servlet – which is a scala class for MyScalatraServlet as per our choice during the installation.

MyScalatraServlet.scala

package com.example.javacodegeeks.scala.rest              
                                                          
import org.scalatra._                                     
                                                          
class MyScalatraServlet extends ScalarestwebappStack {    
                                                          
  get("/") {                                              
    <html>                                           
      <body>                                          
        <h1>Hello, world!</h1>
        Say <a href="hello-scalate">hello to Scalate</a>. 
      </body>                                             
    </html>                                               
  }                                                       
}                                                         

It is a simple Servlet class that routes the URL on the main application (“/”) with the HTTP GET method, for which it prints the typical Hello, World on the browser with a hyperlinked text to a different URL /hello-scalate.

Scalate stands for Scala Templat Engine is a template engine supporting Jade as the mark up language to be used in the HTML pages. It is purely for the Web pages and hence stands out of scope for this article.

4.7 Starting the Server (Jetty)

As it is a Web application, we need a Web Server to run this application. We have already configured a Servlet as part of scalatra-sbt. Luckily and thankfully, Scaltra comes with a built-in Web Server and a Servlet Container Jetty which is an Open Source Web Server like Apache Tomcat.

For starting our Jetty Webserver, we need to execute the SBT first, which prepares the runtime environment for Jetty along with the required libraries. Remember the build.scala file which we have edited a while ago to add the JSON library dependencies. SBT uses this build file to download and keep all the required dependencies for the application to run smoothly.

Let us run the SBT now. You need to be present in the salarestwebapp directory of your Scalatra-sbt installation directory..

Execute SBT

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp
sbt
[info] Loading project definition from C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\project
[info] Updating {file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/project/}scalarestwebapp-build...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\project\target\scala-2.10\sbt-0.13\classes...
[info] Set current project to ScalaRESTWebApp (in build file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/)
>

As you can see above, the sbt command loads the project definition from the project sub-directory and it downloads all the dependencies on the 3rd line – which will be dynamically updating as an when SBT downloads each dependency mentioned in the build.scala file. Once it has completed downloading all the dependencies and it is ready, it will give a message as Done updating inthe command line.

After which, it will compile the source code – which as of now we have only one MyScalatraServlet.scala and it will throw errors if any. If there are no errors, it does not give any intimation on the successful compilation.

Please note that the SBT tool prints out a right arrow mark > to indicate that it is waiting for your command/input further to work.

Now Let us start our Jetty Web Server, as follows.

Start Jetty Web Server

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp> sbt
[info] Loading project definition from C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\project
[info] Updating {file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/project/}scalarestwebapp-build...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\project\target\scala-2.10\sbt-0.13\classes...
[info] Set current project to ScalaRESTWebApp (in build file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/)
> jetty:start
[info] Updating {file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/}scalarestwebapp...
[info] Resolving org.scala-lang.modules#scala-parser-combinators_2.11;1.0.1 ...
[info] Done updating.
[info] Compiling Templates in Template Directory: C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\src\main\webapp\WEB-INF\templates
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[info] Compiling 1 Scala sources to C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\target\scala-2.11\classes...
[info] Packaging C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\target\scala-2.11\scalarestwebapp_2.11-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] starting server ...
2016-05-21 22:05:54.584:INFO::main: Logging initialized @117ms
2016-05-21 22:05:54.645:INFO:oejr.Runner:main: Runner
2016-05-21 22:05:54.773:INFO:oejs.Server:main: jetty-9.2.1.v20140609
[success] Total time: 22 s, completed May 21, 2016 10:05:55 PM
> 2016-05-21 22:06:01.355:WARN:oeja.AnnotationConfiguration:main: ServletContainerInitializers: detected. Class hierarchy: empty
22:06:01.928 [main] INFO  o.scalatra.servlet.ScalatraListener - The cycle class name from the config: ScalatraBootstrap
22:06:02.012 [main] INFO  o.scalatra.servlet.ScalatraListener - Initializing life cycle class: ScalatraBootstrap
22:06:02.211 [main] INFO  o.f.s.servlet.ServletTemplateEngine - Scalate template engine using working directory: C:\Users\raghs\AppData\Local\Temp\scalate-4744013053113056578-workdir
2016-05-21 22:06:02.214:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@6f4d33{/,file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/target/webapp/,AVAILABLE}{file:/C:/rags/scalaPgms/javacodegeeks/scalaRestExample/scalatra/scalarestwebapp/target/webapp/}
2016-05-21 22:06:02.215:WARN:oejsh.RequestLogHandler:main: !RequestLog
2016-05-21 22:06:02.231:INFO:oejs.ServerConnector:main: Started ServerConnector@18c149e7{HTTP/1.1}{0.0.0.0:8080}
2016-05-21 22:06:02.231:INFO:oejs.Server:main: Started @7791ms

As you see, we started with the input at first to start the Jetty with – jetty:start and the Jetty server outputs its state at regular intervals and phases, which were highlighted in the snippet above. Finally the Server is started successfully which we can see in the console as "Started @7791ms" that indicates how much time in milliseconds the Server has taken to boot up.

You may note that on line # 30, the Jetty Web Server prints out on the console that which IP address and port number it listens to. In our case, it is {HTTP/1.1}{0.0.0.0:8080} which means the localhost (127.0.0.1) indicated as 0.0.0.0 and on Port number 8080.

4.8 Test the Application – via Browser

Now that we have downloaded and configured our Scalatra Server and we also have our first Servlet Ready with our Jetty WebServer, let us go ahead and test it with our Jetty Webserver which we have started successfully.

We can trigger a request to the URL http://localhost:8080 in our browser. For our example, we use Firefox web browser.

Localhost Home Page – Firefox

Localhost - Home Page

Congratulations!. We have our Scalatra Web Server running successfully. Let us test the same with our RESTClient Addon for Firefox Browser.

4.9 Test the Application – via REST Client

As we will be using REST Client Add-on for the rest of all RESTful APIs, let us test this URL with the REST Client with which we will also explore the different sections in the add-on.

Localhost – RESTClient – Request and Response Headers

Localhost - RESTClient Request and Reponse Headers

Localhost – RESTClient – Raw HTML Response

Localhost - RESTClient Raw HTML Response

Localhost – RESTClient – Response Body HTML

Localhost - RESTClient Response Body HTML

Localhost – RESTClient – Response Preview

Localhost - RESTClient Response Preview

5. Example Application – Customer Management – Scala

In this example, we will have a Customer Management application wherein we will have our services exported as a RESTful API, as follows.

  1. List all customers – through /cusotmers via HTTP Get method
  2. List a specific customer – through /customer/<Id> via HTTP Get method
  3. Create a new customer – through /customer/create via HTTP Post method
  4. Delete a specific customer – through /customer/<Id> via HTTP Delete Method

We will first build our Server side code ready in Scala. For the sake of simple example, we will have the customer information backed up in a Map which will exist as long as the Server is running. However you can use JDBC to keep this stored permanently in a Relational Database like MySQL, Oracle etc.,

5.1. Customer class

Let us create the main domain class – Customer.scala to capture the essential information pertaining to a customer. For the sake of simplicity, we will have an Id of a customer which will be auto-generated, the first and last names of the customers.

Customer.scala

package com.example.javacodegeeks.scala.rest

//JSON related libraries
import org.json4s.{DefaultFormats, Formats}

//JSON handling support from Scalatra
import org.scalatra.json._

// JSON library for converting the POJO toString as Json
import org.json4s.native.Json

class Customer(val id:Int, var firstName:String, var lastName:String) {

        println("Customer - Constructor  BEGIN .... ")

        // overloaded or auxillary constructor that will invoke the 
        // increment method for having a new Id value
        def this(firstName:String, lastName:String) {
                this(Customer.inc, firstName, lastName)
        }

        //toString info with all fields to be printed in JSON format
        override def toString = Json(DefaultFormats).write(this)

        println("Customer - Constructor END .... ")
}

object Customer {
        private var id = 0

        // increment the id by 1 everytime and return the new value
        private def inc = {
                id += 1;
                id
        }
}

This is a simple Scala class having the attributes of Customer class with the inc method in its singleton object that will be called everytime a Customer class is instantiated, so that we get a new Id everytime to be unique for a customer.

We have an overloaded toString method that will print all the fields of this class in a JSON format, for which we import the required libraries on top of the class.

5.2. CustomerMap class

Now that we have our main Customer class ready, we will have a Map as a container to store and retrieve the Customer information. This is to simulate a database in realtime, which may otherwise be used with a JDBC Driver for the corresponding Databae of our choice – MySQL, DB2, Oracle etc.,

CustomerMap.scala

package com.example.javacodegeeks.scala.rest

import scala.collection._

class CustomerMap {

        println("CustomerMap - Constructor BEGIN... ")

        var map:Map[Int, Customer] = Map()

        //method to intialize the map with an initial entry to start with
        init()

        // method to add one Customer instance to the Map
        def init() {
                this.add("Raghavan", "Muthu")
        }

        def add(firstName:String, lastName:String):Int = {
                var c:Customer = new Customer(firstName, lastName)
                map += (c.id -> c)
                c.id
        }

        def add(c:Customer):Customer = {
                map += (c.id -> c)
                c
        }

        def get(id:Int):Customer = {
                map(id)
        }

        def remove(id:Int) = {
                map = map - id
        }

        def update(id:Int, c:Customer) {
                map += (id -> c)
        }

        override def toString = s"Map :: ${map}"

        println("CustomerMap - Constructor END....")
}

This is a scala class having a Map collection to have the Customer instances stored. It has got several self-explaining meaningful methods to add, retrieve, update and delete a Cusotmer instance from the collection. To start with, we have one Customer instance added, which will be invoked through the init() method.

6. Example Application – Customer Management – Scalatra

Now that we have all our domain classes ready, let us get into Scaltra Servlet where we can configure our URL pattern and the routing with which we can map our request to the action.

For our ease of use, Scalatra defined the Servlet methods matching with the HTTP method names – get, post, delete, put etc.,

6.1 MyScalatraServlet – GET All customers

First of all, let us have a look at a simplest of all – GET request.

In order to get the customer details in a JSON format, we will import the required JSON libraries on top of the class.

MyScalatraServlet.scala for GET all customers

package com.example.javacodegeeks.scala.rest                                         
                                                                                     
import org.scalatra._                                                                
                                                                                     
//JSON related libraries                                                             
import org.json4s.{DefaultFormats, Formats}                                          
                                                                                     
//JSON handling support from Scalatra                                                
import org.scalatra.json._                                                           
                                                                                     
class MyScalatraServlet extends ScalarestwebappStack with JacksonJsonSupport {       
                                                                                     
  // Sets up automatic case class to JSON output serialization, required by          
  // the JValueResult trait.                                                         
  protected implicit lazy val jsonFormats: Formats = DefaultFormats                  
                                                                                     
  // Before every action runs, set the content type to be in JSON format             
  before() {                                                                         
        contentType = formats("json")                                                
  }                                                                                  
                                        
  // an instance of CustomerMap to hold the collection                                                                                     
  var customerMap = new CustomerMap()                                                
                                                                                     
  get("/") {                                                                         
    <html>                                           
      <body>                                          
        <h1>Hello, world!</h1>
        Say <a href="hello-scalate">hello to Scalate</a>. 
      </body>                                             
    </html>                                                                           
  }                                                                                  
                                                                                     
  get("/customers") {                                                                
        customerMap.map                                                              
  }                                                                                  
                                                                                     
}                                                                                    

The MyScaltraServlet now has been changed to accommodate the HTTP Get method in such a way that it will list all the customers. For the very first time, we need to have some customers. That was the reason we had an init() method in the CustomerMap.scala class, otherwise this will be empty at the very first time. It is purely an user’s choice. If you dont’ want this behavior, you can take off the init() method.

The get("/customers") method will be invoked for the HTTP request ending with /customers and it will print the map instance variable of the customerMap variable which is of type CustomerMap. The CustomerMap class has an overloaded toString() that will print out the contents of the Map. As we need the contents to be displayed in JSON format, we had imported the required classes on top of the classes – line numbers 6, 9 indicate the importing and the line number 10 indicates that MyScalatraServlet class will have the support of JacksonJsonSupport trait, which is more of Java’s Interface being implemented by a class.

The line number 19 indicates the setting of the contentType to application/json MIME type. This is for setting the HTTP Header Content-Type: application/json behind the scenes. If we wish, we can have this line as the first line in every method like get, post etc., However, we can have a general method called before() to have such common lines which will be applicable for every other method invoked in this class thereby saving our time and obtaining an efficiency.

6.2 Stop and Start Jetty – for Code Changes

Now that we have made some code changes, we need to let Jetty know of this changes, otherwise the changes will not have any effect. It is like deploying the latest code to Servers location so that the Server could act on the latest changes we made.

For which we can issue the command as follows.

Stopping Jetty Web Server

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\
...
[SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[info] starting server ...
[success] Total time: 1 s, completed May 21, 2016 10:16:00 PM
> 2016-05-21 22:16:00.338:INFO::main: Logging initialized @51ms
2016-05-21 22:16:00.344:INFO:oejr.Runner:main: Runner
2016-05-21 22:16:00.455:INFO:oejs.Server:main: jetty-9.2.1.v20140609
jetty:stop
[info] waiting for server to shut down...
[success] Total time: 0 s, completed May 21, 2016 10:16:06 PM
>

As you can see, we issued a similar command jetty:stop at line numer 9 post which the Server stopped gracefully. The Jetty Server printed the successful stopping of the server in the console at line number 11.

Once again, you can issue the command jetty:start so that the server will compile the source code afresh and deploy with the latest changes.

Instead of stopping and starting the server everytime, we can issue the following command so that the Server will keep looking for changes in the source code and whenever it finds a source file being modified, it will compile that file and redeploy it without you issue the stop/start.

Facilitating Hot deployment of modified source code in Jetty

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\
.....
2016-05-21 22:22:07.521:INFO:oejs.Server:main: Started @7183ms
~;copy-resources
[info] Compiling Templates in Template Directory: C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\src\main\webapp\WEB-INF\templates
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[info] Compiling 3 Scala sources to C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\target\scala-2.11\classes...
[success] Total time: 8 s, completed May 21, 2016 10:34:15 PM
1. Waiting for source changes... (press enter to interrupt)

As you can see through the highlighted lines, we issued the command ;copy-resources to copy the resources if any being changed. At line number 11, Jetty printed out the message stating that it is waiting for the source file to be changed so that it can recompile the changed file and redeploy it with the modifications. In case you don’t want it to keep waiting, you can press Enter to come back to the SBT prompt where you can issue further commands.

6.3 REST Client – GET All customers

Now that we have restarted Jetty to take the modifications being done to the MyScalatraServlet to accommodate the GET request for /customers, let us test the same in REST Client.

RESTClient – Get All customers

RESTClient - Get All Customers

As we have got only one customer – which was initially loaded, we got that a result of /customers for HTTP Get method – which is to indicate that Get all customers. The latest code changes what we made in Scalatra is working fine now as we see in the output response below in the RESTClient tool.

6.4 MyScalatraServlet – Get a specific customer

Let us now add another piece of code to retrieve a specific customer based on the Id passed.

We need to add this piece into a different method – as in the same get() method but with a different URL pattern to match the specific customer Id being passed along with the request.

MyScalatraServlet.scala for GET a specific customer

 get("/customer/:Id") {
        var IdInt = Integer.parseInt(params("Id"))
        println("GET - Customer Id - arg. passed :: " + IdInt)
        customerMap.get(IdInt)
  }

The URL pattern here is get("/customer/:Id") – which is to say that Id will be the variable name to this method for handing over the value being passed after the string /cusotmer/ in the URL. We can make use of this Id parameter to retrieve the customer whose Id matches with this value.

Please remember that the Id parameter being passed will be a String. We need to get that converted into an Integer before we could actually make use of it for retrieving the matching customer.

Line number 3 indicates the plain old Java style conversion from a String to Integer. For the sake of simplicity it is assumed that the value passed will be only an integer and hence no exception handling is done. However, for the real examples in Production, we can’t trust the user inputs and hence it is recommended and advisable to have such validation and sanity checks.

Line number 5 fetches the matching customer by passing the numerical value of IdInt, which will handover the matching customer information if any from the get() method of the CustomerMap class.

6.5 REST Client – Get a specific customer

Let us now test the same with the RESTClient for a specific customer with his Id. Please remember to have the Server (re)started or ;copy-resources to have the latest code change for serving the requests.

RESTClient – Get a specific customer

RESTClient - Get a specific customer

We passed the customer’s Id 1 with the URL as /customer/1 in the HTTP Get method to indicate that Get the customer whose Id is 1. The output shows the successful retrieval of the customer having an Id 1.

Please pay attention to the URL patterns. For getting all customers, it is /customers – in plural, whereas to get a specific customer it is /customer/Id – in singular to indicate that it will fetch utmost ONE customer.

6.6 MyScalatraServlet – POST

Let us now have the method to create a new customer. We have already implemented the code base for CustomerMap class. We just need to have the routing done in the Servlet for mapping the request values to be used for creating a Customer.

This is achieved via HTTP Post method. However we need to do a little extra thing by passing the customer information to be created in the HTTP request, via the Request Body of the RESTClient. Inside the Servlet, we need to read and parse the HTTP Request body to read the information passed and get it as a Customer Instance. Luckily Scalatra has a way to do it easily.

MyScalatraServlet.scala for POST – create a new customer

    post("/customer/create") {
            var customer = parsedBody.extract[Customer]
            customerMap.add(customer)
      }

First of all, we added a new method named post matching with the HTTP Post verb, with an argument /cusotmer/create to indicate that a new cusotmer needs to be created.

On line number 3, we have a method named extract of the parsedBody of the Servlet that takes an instance reference – which in our case is Customer. This indicates that whatever request data was passed along, will be read and parsed as a Customer Instance. In this case, we have a new Customer instance created with the data extracted out of the request data. The newly created customer instance is received in the customer variable, which is then passed onto customerMap.add method.

At the end of the method, we return the newly added customer back to the client. This time it will have the newly created Id value in the result to distinguish and higlight that the new customer is successfully created.

6.7 REST Client – POST

Testing this POST request also is little special and different from GET method. As we are passing the data in JSON format to be compatabile with the output format, we will have to mention the ‘Request Header’ as a custom one – which otherwise defaults to plain text. RESTClient has an option to set that.

RESTClient – Set Custom Header for JSON – Menu

RESTClient - Set Custom Header for JSON - Menu

RESTClient – Custom Request Header – Dialog Box

RESTClient - Custom Request Header - Dialog Box

RESTClient – Custom Request Header – Autocompletion

RESTClient - Custom Request Header - Autocompletion

RESTClient has an option to set a Request Header to be a favorite, so that it can be fetched with a click of a mouse. You can check the box at the bottom of the Dialog box to do the same.

RESTClient – Custom Request Header – JSON – Saved as Favorite

RESTClient - Custom Request Header - JSON - Saved as Favorite

As you can see, in the Headers Menu, we can see the latest header we set for Content-Type: application/json being saved in favorites.

RESTClient – Custom Request Header – Favorite Saved in Menu

RESTClient - Custom Request Header - Favorite Saved in Menu

It is now time for us to make a create request. There are few important things to note, without which your request may fail.

  1. First and foremost, change the HTTP Request method from GET to POST on the top left in the drop down menu.
  2. Change the URL to http://localhost:8080/customer/create to hit the appropriate post() method in the MyScalatraServlet
  3. Ensure you set the appropriate request header – Content-Type: application/json. We have recently set that. You can see that on the Headers section on the request portion of the RESTClient
  4. Ensure you add the actual request data in the Request Body in JSON format. You can copy paste the output from /customers GET request, and alter the contents for the new customer to be created.
  5. You don’t need to have the Id field in the request, as that will be created newly everytime for the request

RESTClient – Create Request with POST – Request

RESTClient - Create Request with POST - Request

Once the request is triggered, watch out the output. In the post method we returned the newly created customer, with which we will get the customer details along with the newly created Id field of the customer. You can see the newly created Id 2 in our case, being highlighted in the response below.

RESTClient – Create Request with POST – Response

RESTClient - Create Request with POST - Response

We can issue the /customers with a GET request once again to verify whether we get the newly added customer in the response.

RESTClient – Verify all the customers after creation

RESTClient - Verify all the customers after creation

Yes, we are able to successfully retrieve as well as add the customers into our repository (Map) via the REST APIs so far.

6.8 MyScalatraServlet – DELETE

Now, let us try to delete a customer with his specific customer Id. Let us do the code change in the MyScalatraServlet.scala first. It is more like get() method where we need to pass a variable Id for the customer’s id which needs to be converted to an Integer first before being used for deletion.

MyScalatraServlet.scala for POST – create a new customer

      delete("/customer/:Id") {
            var IdInt = Integer.parseInt(params("Id"))
            customerMap.remove(IdInt)
            customerMap.map
      }

We have created a new method delete matching with the HTTP Verb – Delete. This method has an Id parameter which we get it converted to an Integer, using which we attempt to remove a matching cusotmer with the remove() method of the customerMap instance.

As a result, we pass back the resultant entries in Map in the response to indicate in the response showing the left out entries.

6.9 REST Client – DELETE

The Delete operation is similar to GET in RESTClient. We need to ensure we select the DELETE method on the top left corner and change the URL to customer/Id. We don’t need to pass any request body.

Let us assume that we have 3 different customers present in the collection, as follows.

RESTClient – Verify all the customers before deletion

RESTClient - Verify all the customers before deletion

Let us give a request to delete the customer with the Id 1

RESTClient – Delete a specific customer – Request

RESTClient - Delete a specific customer - Request

We can see from the response below that the specific customer with the Id 1 being removed and as a result we get the rest of the customers – matching with Ids 2 and 3 passed back.

RESTClient – Delete a specific customer – Response

RESTClient - Delete a specific customer - Response

6.10 Exit SBT

At the end, you can issue the jetty:stop command to stop the Jetty Web Server. However we may wish to stop the SBT as well to come out of execution.

Exiting SBT

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\
.........
jetty:stop
[info] waiting for server to shut down...
[success] Total time: 0 s, completed May 21, 2016 10:16:06 PM
> exit

C:\rags\scalaPgms\javacodegeeks\scalaRestExample\scalatra\scalarestwebapp\

As you can see, we issue the command exit at the prompt with which the SBT tool came out of execution and it came back to the Operating System Terminal.

7. Conclusion

Hope this example series gave you a good understanding on how to work with REST application in Scala using Scalatra. You may please refer to the documentation of Scalatra for a better understanding of the tool for the rest of REST APIs for your application.

8. References

You may please refer the following URLs for further reading.

  1. Scala Tutorial for Beginners – JCG
  2. Scalatra
  3. Wikipedia – Scalatra
  4. Infoq Article – Introduction to Scalatra (Sinatra like Web Framework for Scala)
  5. Scalatra In Action – Manning Publications
  6. Manning – Introducing Scalatra (Free PDF)
  7. Conscript – Distributed Download Mechanism for Scala
  8. Conscript – GitHub Repository
  9. giter8 – Project Template download Utility for Scalatra
  10. Scalatra 2.4 Guide – Installation
  11. Scalatra 2.4 Guide – First Project
  12. Scalatra 2.4 Guide – Project Structure
  13. Scalatra 2.4 Guide – HTTP Routes
  14. Scalatra 2.4 Guide – HTTP Request and Responses
  15. Wikipeida – HTTP Verbs
  16. Scalatra Users – Google Groups
  17. Scala SBT – Documentation
  18. Wikipedia – REST
  19. REST Client – Addon for Firefox Browser
  20. JSON website
  21. JSON4S – Github website
  22. JSON formatting in Scalatra
  23. Jetty – Wikipedia
  24. Eclipse Jetty

9. Download the Source Code

All the examples in this article are tested in the Command Prompt / Shell against with Scalatra 2.4.0.RC1 on top of Scala Version 2.11.7.

Due to size restrictions (30.1MB), the auto-generated and downloaded scala libraries are removed as they can be generated afresh during execution of SBT

Download
You can download the full source code of this example here: Scala REST Example.

Raghavan Muthu

Raghavan alias Saravanan Muthu is a seasoned IT professional having more than 2 decades of experience on Java SE/EE based Application Architecture, Design, Development, Management and Administration for Banking, Insurance, Telecom, HealthCare and Automobile Industries, having a very good hands on experience on Multi-threaded, batch processing applications and Relational Databases. He is currently working as a Director of Engineering for one of the Product based companies in India that delivers the product on Health Care and Insurance Domain. He holds a Post Graduation (Master of Science), and a PG Degree on Big Data Engineering from Birla Institute of Technology and Science (BITS), Pilani, India. He is a Founder, Chief Executive Volunteer and a Web Master of a non-profit charity organization named SHaDE (http://shade.org.in).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button