Play – a framework used to write web applications
Play is a framework used to write web applications. As shown in the following diagram, a web application is based on a client-server architecture and uses the HTTP protocol for communication:
Users have the role of clients and make HTTP requests to servers to interact with the application. The servers process their requests and send them a response. Along the way, the web application might need to make use of various databases or perhaps other web services. This entire process is depicted in the following diagram:
This book will show you how Play can help you to write such web applications. The preceding diagram shows a first big picture of the framework's overall architecture. We will refine this picture as we read through this book, but for now it is a good start. The diagram shows a web client and a Play application. This one is made of a business layer (on the right), which provides the services and resources specific to the application. These features are exposed to the HTTP world by actions, which themselves can be logically grouped within controllers. Gray boxes represent the parts of code written by the developer (you!), while the white box (the router) represents a component already provided by Play.
HTTP requests performed by the client (1) are processed by the router that calls the corresponding action (2) according to URL patterns you configured. Actions fill the gap between the HTTP world and the domain of your application. Each action maps a service or resource of the business layer (3) to an HTTP endpoint.
All the code examples in this book will be based on a hypothetical shopping application allowing users to manage and sell their items. The service layer of this application is defined by the following Shop
trait:
case class Item(id: Long, name: String, price: Double) trait Shop { def list(): Seq[Item] def create(name: String, price: Double): Option[Item] def get(id: Long): Option[Item] def update(id: Long, name: String, price: Double): Option[Item] def delete(id: Long): Boolean }
In Java, the service layer is defined by the following Shop
interface:
public class Item { public final Long id; public final String name; public final Double price; public Item(Long id, String name, Double price) { this.id = id; this.name = name; this.price = price; } } interface Shop { List<Item> list(); Item create(String name, Double price); Item get(Long id); Item update(Long id, String name, Double price); Boolean delete(Long id); }
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
The Item
type simply says that an item has a name, a price, and an ID. The Shop
type defines the typical Create, Read, Update, and Delete (CRUD) operations we want to do. Connecting with the figure that shows the architecture of Play applications, these types represent the business layer of your web service and their definition should live in a models
package in the app/
source directory. The remainder of this chapter explains how to write a controller exposing this business layer via HTTP endpoints using JSON to represent the data.
As an example, here is a possible minimalist Scala implementation of the Shop
trait:
package models import scala.collection.concurrent.TrieMap import java.util.concurrent.atomic.AtomicLong object Shop extends Shop { private val items = TrieMap.empty[Long, Item] private val seq = new AtomicLong def list(): Seq[Item] = items.values.to[Seq] def create(name: String, price: Double): Option[Item] = { val id = seq.incrementAndGet() val item = Item(id, name, price) items.put(id, item) Some(item) } def get(id: Long): Option[Item] = items.get(id) def update(id: Long, name: String, price: Double): Option[Item] = { val item = Item(id, name, price) items.replace(id, item) Some(item) } def delete(id: Long): Boolean = items.remove(id).isDefined }
This implementation stores the data in memory, so it loses everything each time the application restarts! Nevertheless, it is a sufficient business layer basis, letting us focus on the web layer. The implementation uses a concurrent collection to solve concurrency issues. Indeed, as I will explain later, the code called by the controllers must be thread safe.
For Java developers, here is a minimalist implementation of the Shop
interface:
import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.atomic.AtomicLong; new Shop() { SortedMap<Long, Item> items = new ConcurrentSkipListMap<>(); AtomicLong seq = new AtomicLong(); @Override public Collection<Item> list() { return new ArrayList<>(items.values()); } @Override public Item create(String name, Double price) { Long id = seq.incrementAndGet(); Item item = new Item(id, name, price); items.put(id, item); return item; } @Override public Item get(Long id) { return items.get(id); } @Override public synchronized Item update(Long id, String name, Double price) { Item item = items.get(id); if (item != null) { Item updated = new Item(id, name, price); items.put(id, updated); return updated; } else return null; } @Override public Boolean delete(Long id) { return items.remove(id) != null; } };
As previously mentioned, the code called by controllers must be thread safe, hence the use of Java concurrent collections.