Scala

Developing Modern Applications with Scala: Web Applications with Play Framework

This article is part of our Academy Course titled Developing Modern Applications with Scala.

In this course, we provide a framework and toolset so that you can develop modern Scala applications. We cover a wide range of topics, from SBT build and reactive applications, to testing and database acceess. With our straightforward tutorials, you will be able to get your own projects up and running in minimum time. Check it out here!

1. Introduction

It is been a long time since Web became a dominant, universally and globally accessible platform for myriad of different applications: either web sites, web portals, or web APIs. Started as a simple set of static HTML pages, web applications in leaps and bounds were catching up with their desktop counterparts, transforming into new class of what we used to call rich Internet applications (or just RIA). However, most of such advances would have not been possible without evolution (and in some cases revolution) of the web browsers.

In this section of the tutorial we are going to talk about developing rich web sites and portals (or to say it simply, modern web applications) using Scala programming language and ecosystem.

2. MVC and the Power of Patterns

There are many ways one can approach the design and development of the web applications. Nonetheless, a couple of patterns have been emerged out of the crowd and gained a widespread adoption in software development community.

Model-View-Controller (or MVC) is one of the most widely applied architectural patterns used for developing maintainable UI-based applications. It is very simple but provides quite sufficient level of the separation of concerns and responsibilities.

MVC pattern and its collaborators
MVC pattern and its collaborators

In the essence, MVC outlines pretty well the collaborators and their roles. The View uses Model to render the desktop or web UI representation to the User. The User interacts with the View, which may lead to model updates (or retrievals) by means of using the Controller. In turn, Controller’s actions over Model may lead to the View being refreshed as well. In some cases, User may interact with the Controller directly, completely bypassing the View.

Many frameworks used these days for web applications development are designed around MVC pattern or one (or more) its derivatives. In Scala ecosystem, the Play Framework is the undoubtedly the best available option out there and it is what we are going to talk about in this section of the tutorial.

3. Play Framework: enjoyable and productive

Play Framework is modern, production-ready, high velocity, full-fledged web framework written in Scala (with Java-friendly API also available). It is architected to be fully asynchronous, lightweight and stateless and is built on top of Akka Toolkit, which we have discussed in details in the previous section of the tutorial. The latest released version of the Play Framework at the moment of this writing is 2.5.9.

Although Play Framework is not limited to support the development of the web applications only, we are going to focus mostly on this side of things, continuing the discussion about web APIs in the next section, dedicated specifically to that.

4. Controllers, Actions and Routes

Play Framework fully embraces the MVC model and right from the start introduces the concept of controllers. Following their responsibilities, controllers may generate some actions, returning some results back, for example:

@Singleton
class HealthController extends Controller {
  def check() = Action {
    Ok("OK")
  }
}

Please notice that by convention, controllers are stored under controllers package. Controller methods may be exposed directly as HTTP endpoints, using HTTP protocol semantics. In Play Framework such a mapping is called a route and all route definitions are placed in the conf/routes file, for example:

GET  /health        controllers.HealthController.check
GET  /assets/*file  controllers.Assets.versioned(path="/public", file: Asset)

Let the simplicity of this example not deceive you, Play Framework route definitions may include arbitrary number of quite sophisticated parameters and URI patterns as we are going to see later in the section.

The controllers in Play Framework extend Controller trait and may contain any (reasonable) number of methods which return Action instances. All actions are executed in an asynchronous, non-blocking way and it is very important to keep that in mind. Controllers should avoid execution of the blocking operations whenever possible and Action companion object offers convenient async methods family to seamlessly integrate with asynchronous execution flows, for example:

@Singleton
class UserController @Inject() (val service: UserService) extends Controller {
  import play.api.libs.concurrent.Execution.Implicits.defaultContext
  
  def getUsers = Action.async {
    service.findAll().map { users =>
      Ok(views.html.users(users))
    }
  }
}

Besides asynchronicity, this short code snippet also illustrates how controllers introduce yet another MVC collaborator, the view of the model. Let us talk about that for a moment.

5. Views and Templates

The views in Play Framework are usually based on regular HTML markup but backed by Twirl, extremely powerful Scala-based template engine. Under the hood, templates are transformed to Scala classes along with companion objects. They can have arguments and are compiled as standard Scala code.

Let us have a quick example of how model could be passed and rendered in Play Framework view template by introducing a User case class:

case class User(id: Option[Int], email: String, 
  firstName: Option[String], lastName: Option[String]) 

If it looks familiar, you are not mistaken: this is exactly the same case class we have seen in Database Access with Slick section of the tutorial.  So let us create a users.scala.html template to print out the list of users as HTML table, stored by convention in views folder (which also serves as a package name):

@(users: Seq[model.User])

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Manage Users</title>
  </head>
  <body>
    <div class="container">
      <div class="panel panel-default">
        <div class="panel-heading">Users</div>
          <table class="table">
            <thead>
              <tr>
                <th>Id</th>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Email</th>
              </tr>
            </thead>
            @for(user <- users) {
              <tr>
                <td>@user.id</td>
                <td>@user.firstName</td>
                <td>@user.lastName</td>
	          <td>@user.email</td>
		  </tr>
		}
	    </table>
	  </div>
      </div>
    </body>
</html>

By and large, this is just raw HTML markup, so close to heart of any front-end developer. The first line declares template arguments, @(users: Seq[model.User]) which is just list of users. The only place we use this argument is when render the rows of the table by applying Scala-like expressions:

@for(user <- users) {
  <tr>
    <td>@user.id</td>
    <td>@user.firstName</td>
    <td>@user.lastName</td>
    <td>@user.email</td>
  </tr>
}

And that’s it! And because all the templates are compiled down to byte code, any errors related, for example, to non-existing properties or inappropriate expressions usage will be caught at compile time! With such a help from compiler, any kind of refactorings become much easier and safer.

users-table
Users table

To close the loop, in the Controllers, Actions and Routes section we have already seen how the templates could be instantiated and send out to the browser using controller action:

def getUsers = Action.async {
  service.findAll().map { users =>
    Ok(views.html.users(users))
  }
}

Aside from simple components, templates may include HTML forms which could be backed directly by controller methods. As an example, let us implement the adding of new user functionality, as such wiring view, controller, and model together. First, on the controller side we have to add Form definition, including all validation constraints:

val userForm = Form(
  mapping(
    "id" -> ignored[Option[Int]](None),
    "email" -> email.verifying("Maximum length is 512", _.size <= 512),
    "firstName" -> optional(text(maxLength = 64)),
    "lastName" -> optional(text(maxLength = 64))
  )(User.apply)(User.unapply)
)

Second, we are going to create a dedicated endpoint in the controller to add new user as the result of form submission:

def addUser = Action.async { implicit request =>
  userForm.bindFromRequest.fold(
    formWithErrors => {
      service.findAll().map { users =>
        BadRequest(views.html.users(users)(formWithErrors))
      }
    },
    user => {
      service.insert(user).map { user =>
        Redirect(routes.UserController.getUsers)
      } recoverWith {
        case _ => service.findAll().map { users =>
          BadRequest(views.html.users(users)(userForm
            .withGlobalError(s"Duplicate email address: ${user.email}")))
        }
      }
    }
  )
}

Please notice how userForm.bindFromRequest.fold in one shot does the bindings of the form parameters from the request along with performing all validation checks. Next thing, we have to replicate the userForm into its HTML presentation, using view template for that:

@helper.form(action = routes.UserController.addUser) {
  @helper.inputText(userForm("email"), '_label -> "Email Address")
  @helper.inputText(userForm("firstName"), '_label -> "First Name")
  @helper.inputText(userForm("lastName"), '_label -> "Last Name")
  <button class="btn btn-default" type="submit">Add User</button> 
}

The usage of @helper simplifies a lot forms construction as all the relevant types and validation constraints will be taken from Form definition and hinted to the user. But as mostly everywhere in Play Framework, you are not obliged to use this approach: any JavaScript/CSS framework of your choice could be used instead.  Here is a sneak peak on how this form looks in the browser.

user-form
Add User form

And as a final step, the routing table should be updated to have this new endpoint listed as well. Luckily, it is just one liner:

POST  /  controllers.UserController.addUser

Please notice that Play Framework scaffolding takes care of all validation part and reporting it back to the view, for example, submission of the form with invalid email address will not be accepted:

email-invalid
Error in case incorrect email address is specified

Surely, Play Framework gives enough flexibility to report back other types of errors, not necessarily tied to validation. For example, in the controller we have used userForm.withGlobalError method to signal duplicate email address.

6. Action Compositions and Filters

There is often a need to perform some actions before or after controller’s method invocation, possibly even altering the response. The examples of that could be logging, security verifications or support of the cross-origin resource sharing (CORS).

Out of the box Play Framework provides a couple of ways to hijack into the processing pipeline: using filters and action compositions.


 

7. Accessing Database

Any more or less real-world web application would need to manage some data and in many cases well-known relation data stores are the perfect choice. Play Framework offers superior integration with a couple of the JDBC-based libraries but we already learned quite a lot of great things about Slick and surely, Play Framework integrates with Slick seamlessly.

To make things even simpler and familiar, we are going to reuse the same data model we have built in Database Access with Slick part, with mostly no modification whatsoever.  The minor change that we would need to do is affecting UserRepository only: injecting DatabaseConfigProvider for default database configuration and use its provider.get[JdbcProfile] method to get the corresponding JdbcProfile instance.

@Singleton
class UserRepository @Inject()  (val provider: DatabaseConfigProvider) 
    extends HasDatabaseConfig[JdbcProfile] with UsersTable {  
  val dbConfig = provider.get[JdbcProfile]
  
  import dbConfig.driver.api._
  import scala.concurrent.ExecutionContext.Implicits.global
 
  ...      
}

And we are done. Play Framework allows to manage multiple named database instances and configure them through application.conf file. For convenience, single database web applications may use the specially treated default one.

slick {
  dbs {	
	default { 
	  driver="slick.driver.H2Driver$"	   
	  db {
	    driver="org.h2.Driver"
	    url="jdbc:h2:mem:users;DB_CLOSE_DELAY=-1"
	  }
	}
  }
}

One of the most common problems which application developers face every time while dealing with relation databases is schema management. The data model evolves over time as so does the database: new tables, columns and indexes are often added, unused ones get removed. Database evolutions is yet another terrific feature Play Framework provides out of the box.

8. Using Akka

The Play Framework stands on Akka Toolkit foundation and as such actors are the first class-citizens in there. Any Play Framework web application has a dedicated actor system created right when the application starts (and restarts automatically when the application restarts).

Fully embracing the reactive paradigm since the very beginning, the Play Framework is one of the earliest adopters of the Akka Streams implementation. Going even further, Play Framework provides quite a few useful utility classes to bridge together Akka Streams with web application specific technologies, which we are going to talk about.

9. WebSockets

Arguably, WebSockets are one of the most interesting and rapidly spreading communication protocols these days. Essentially, WebSockets take the web client/web server interactions to the next level, providing the full-duplex communication channel established over HTTP protocol.

To put WebSockets in perspective of real applications, let us implement the feature to show the notification in our web application every time new user is added. Internally, this fact is represented by UserAdded event.

case class UserAdded(user: User)

Using Akka’s event stream, which we talked about in the previous part of the tutorial, we can subscribe to this event by creating a dedicated actor, let us call it UsersWebSocketActor.

class UsersWebSocketActor(val out: ActorRef) extends Actor with ActorLogging {
  override def preStart() = {
    context.system.eventStream.subscribe(self, classOf[UserAdded])
  }
  
  override def postStop() = {
    context.system.eventStream.unsubscribe(self)
  }
  
  def receive() = {
    case UserAdded(user) => out ! user
  }
}

It looks exceptionally simple but the mysterious out actor reference. Let us see where it comes from. Play Framework always had superior supports for WebSockets, however closer integration with Akka Streams made it much, much better. Here is the WebSockets endpoint to broadcast the notifications about new users to the client.

def usersWs = WebSocket.accept[User, User] { request =>
  ActorFlow.actorRef(out => Props(new UsersWebSocketActor(out)))
}

It is just a few lines of code for such a complex feature, really amazing! Please take a note that the out actor reference essentially represents the web client side and is provided by Play Framework out of the box. The changes in routing table are minimal as well.

GET  /notifications/users  controllers.NotificationController.usersWs

On the browser side of things, this is a standard piece of JavaScript code, which could be injected right into the view template.

<script type="text/javascript">
  var socket = new WebSocket(
    "@routes.NotificationController.usersWs().webSocketURL()")
  
  socket.onmessage = function(event) {
    var user = jQuery.parseJSON(event.data);
    ...
  }
</script>

As was mentioned, WebSockets are bi-direction communication channel: not only web server can send data to web client, web client can initiate some messages as well. We have not covered this part in the example here but Play Framework documentation discusses it in details.

10. Server-Sent Events

WebSockets are extremely powerful but often the web application needs could be backed by much simpler implementation. In case the full-duplex channel is not required, web server can rely on server-sent events (or SSE) to just send data to the web client in one-way fashion. In Play Framework (and many other frameworks as well), it is implemented by supporting chunked (or streaming) responses with special content type text/event-stream.

def usersSse = Action {
  Ok.chunked(
    Source.actorPublisher(Props[UsersSseActor]) via EventSource.flow[User]
  ).as(ContentTypes.EVENT_STREAM)
}

In this case the sever may use a full-fledged Akka Streams data processing pipelines and deliver data to the client using EventSource scaffolding. To illustrate yet another interesting feature of Akka Streams, we are using UsersSseActor actor, functionally similar to UsersWebSocketActor, as the stream source.

class UsersSseActor extends ActorPublisher[User] with ActorLogging {
  var buffer = Vector.empty[User]
  
  override def preStart() = {
    context.system.eventStream.subscribe(self, classOf[UserAdded])
  }
  
  override def postStop() = {
    context.system.eventStream.unsubscribe(self)
  }
  
  def receive = {
    case UserAdded(user) if buffer.size < 100 => {
        buffer :+= user
        send()
      }
    
    case Request(_) => send()
    case Cancel => context.stop(self)
  }
  
  private[this] def send(): Unit = if (totalDemand > 0) {
    val (use, keep) = buffer.splitAt(totalDemand.toInt)
    buffer = keep
    use foreach onNext
  }
}

It is a bit more complex due to the fact that we have to follow Akka Streams convention and APIs to have a well-behaved publisher, but essentially it also uses event stream to subscribe to the notifications. Again, on a view side, just bare bone JavaScript:

<script type="text/javascript">
var event = new EventSource(
  "@routes.NotificationController.usersSse().absoluteURL()");
         
event.addEventListener('message', function(event) {
  var user = jQuery.parseJSON(event.data);
  ...
});
</script>

And not to forget about adding yet another entry into routing table:

GET  /notifications/sse  controllers.NotificationController.usersSse

11. Running Play Applications

There are multiple ways to run our Play Framework application, but probably the easiest one is to use the sbt tool we already are quite familiar with:

sbt run

The better way however, still using sbt, would be to run the application in continuous edit-compile-(re)deploy cycle, to (mostly) instantaneously reflect the modifications in the source files:

sbt ~run

By default, every Play application is running on HTTP port 9000, so feel free to navigate your browser to http://localhost:9000 to play with users or to http://localhost:9000/notifications to see WebSockets and server-sent events in action.

12. Secure HTTP (HTTPS)

Using secure HTTP (HTTPS) in production is a must-have rule for modern web sites and portal these days. But very often there is a need to run your Play Framework application with HTTPS support during the development as well. It is usually being done by generating self-signed certificates and importing them into Java Key Store, which is just one command away:

keytool -genkeypair -v
  -alias localhost
  -dname "CN=localhost"
  -keystore conf/play-webapp.jks
  -keypass changeme
  -storepass changeme 
  -keyalg RSA 
  -keysize 4096 
  -ext KeyUsage:critical="keyCertSign" 
  -ext BasicConstraints:critical="ca:true" 
  -validity 365

The conf/play-webapp.jks key store could be used to configure Play Framework application to run with HTTPS support, for example:

sbt run -Dhttps.port=9443 -Dplay.server.https.keyStore.path=conf/play-webapp.jks -Dplay.server.https.keyStore.password=changeme

Now we could navigate to https://localhost:9443/ to get the list of the users (the same one we would see by using http://localhost:9000/). Very simple and easy, isn’t it?

13. Testing

In the web applications world, testing has many forms and faces, but Play Framework does a really good job by providing the necessary scaffolding to simplify those. Moreover, both ScalaTest and specs2 frameworks are equally supported.

Probably, the simplest and fastest way to approach testing in Play Framework is by using unit tests. For example, let us take a look on this specs2 suite for testing UserController methods.

class UserControllerUnitSpec extends PlaySpecification with Mockito {
  "UserController" should {
    "render the users page" in {
      val userService = mock[UserService]
      val controller = new UserController(userService)
      
      userService.findAll() returns Future.successful(Seq(
        User(Some(1), "a@b.com", Some("Tom"), Some("Tommyknocker"))))
      val result = controller.getUsers()(FakeRequest())

      status(result) must equalTo(OK)
      contentAsString(result) must contain("a@b.com")
    }
  }
}

Helpful Play Framework scaffolding for specs2 integration makes writing a unit tests as easy as breathing. Going up one level in testing pyramid, we may need to consider writing integration tests and in this case Play Framework would be better used with ScalaTest due to dedicated additional features provided out of the box.

class UserControllerSpec extends PlaySpec with OneAppPerTest with ScalaFutures {
  "UserController" should {
    "render the users page" in {
      val userService = app.injector.instanceOf[UserService]
     
      whenReady(userService.insert(User(None, "a@b.com", 
          Some("Tom"), Some("Tommyknocker")))) { user =>
        user.id mustBe defined
      }
      
      val users = route(app, FakeRequest(GET, "/")).get
      status(users) mustBe OK
      contentType(users) mustBe Some("text/html")
      contentAsString(users) must include("a@b.com")
        .and(include ("Tommyknocker"))
    }
  }
}

In this case there is a full-fledge Play Framework application being created, including database instance configured with all evolutions applied. However, if you would like to get as close as possible to real deployment, you may consider to add web UI (with or without browser) test cases, and again, ScalaTest integration is offering the necessary pieces.

class UserControllerBrowserSpec extends PlaySpec 
    with OneServerPerSuite with OneBrowserPerSuite 
      with HtmlUnitFactory {
  "Users page" must {
    "should show emtpy users" in {
      go to s"http://localhost:$port/"
      pageTitle mustBe "Manage Users"

      textField("email").value = "a@b.com"
      submit()
      
      eventually { pageTitle mustBe "Manage Users" }
      find(xpath(".//*[@class='table']/tbody/tr[1]/td[4]")) map {
        _.text mustBe ("a@b.com") 
      }
    }
  }
}

This well-known testing strategy is based on Selenium web browser automation which is nicely wrapped into OneBrowserPerSuite along with browser-less HtmlUnitFactory.

14. Conclusions

Without any doubts, Play Framework brings back the pleasure and the joy of web application development on JVM platform. With Scala and Akka in its core, modern, extremely feature-rich and productive, built on top of reactive programming paradigm, all that makes Play Framework a choice you would never regret. Not to forget that excellent separation between frontend and backend lets you to bridge the best parts of two worlds together, resulting into creation of beautiful and maintainable web applications.

15. What’s next

In the next section of the tutorial we are going to talk about REST(ful) web APIs development using Akka HTTP module.

The complete source code is available for download.

Andrey Redko

Andriy is a well-grounded software developer with more then 12 years of practical experience using Java/EE, C#/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).
Subscribe
Notify of
guest

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

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Heillemann
Heillemann
7 years ago

I’d love to see this article rewritten with java 8…

Andriy Redko
7 years ago
Reply to  Heillemann

Hi Heillemann,

Sorry :-) this is the Scala tutorial, may be someone will be able to translate it into Java version, thankfully Play has a native Java API.
Thank you for your comment!

Best Regards,
Andriy Redko

abhishek kumar
abhishek kumar
4 years ago

Hey, I am trying to import this project in eclipse IDE. However, it is showing me no project found.
I am sure I am doing something wrong. Can you please add a step to import this code in eclipse.

Thanks in Advance
Abhishek Kumar

Andriy Redko
4 years ago
Reply to  abhishek kumar

Hi Abhishek,

To import the project into Eclipse, you just need sbt-eclipse plugin (https://www.playframework.com/documentation/2.8.x/IDE#Setup-sbteclipse). It is already pre-configured for the sample Play application. When you download and unpack the project sources, just run ‘sbt eclipse’, assuming you have sbt already installed (please check https://www.scala-sbt.org/ if it is not the case). Thank you.

Best Regards,
Andriy Redko

Back to top button