 
                                PLAY FRAMEWORK
Webnesday #5
Mirko Stocker
Institut für Software
Rapperswil, 13. Mai, 2015
Mirko Stocker
 InfoQ Editor
 Wissenschaftlicher Mitarbeiter
 Application Architecture
 Scala, Ruby
 Cloud Solutions
 www.infoq.com/author/Mirko-Stocker
 Parallele Programmierung
 Web-Entwicklung
 User Interfaces
 www.burg-au.ch
 appquest.hsr.ch
 www.free-enterprise.ch
 Product Manager
 Consulting und Projekt-Begleitung
 Eclipse Plug-ins für C++/Scala
 Forschungsprojekte
github.com/misto | misto.ch | @m_st | facebook.com/misto | ch.linkedin.com/in/misto
Seite 2
© Mirko Stocker 2014
Play Framework
Seite 3
© Mirko Stocker 2014
Warum Play?
 JVM, aber kein JEE/Servlet
 HTTP Interface, non-blocking I/O
 Typsicherheit: Templates, URLs
 Entwicklungszyklus: Edit -> Refresh
Seite 4
© Mirko Stocker 2014
Grundlagen des Webs
HTTP
Back
Reload
URLs
Seite 5
© Mirko Stocker 2014
Grundlagen des Webs – Bezogen auf Play
HTTP nicht verstecken
Stateless
Reload
URLs sprechen
 Stateless: Der Back-Button funktioniert
 HTTP ist nicht nur ein Transport für RPC (REST Maturity Model)
 URLs sollten sprechen (Level 1, Links zu verschiedenen Ressourcen)
 Reload: Als Entwickler will ich Änderungen sofort sehen
Seite 6
© Mirko Stocker 2014
Play ist Stateless
 Kein Zustand im Applikations Tier
 Im Unterschied zu Java Servlet HttpSession
 Zustand ist also eine Kombination aus
 Client Session State
 Database Session State
 Vorteile
 Einfach horizontal skalierbar, da egal ist, welche Instanz einen Request
behandelt (Cloud-Ready)
 Kein Synchronisationsaufwand in der Applikation
 Entwicklung und Test vereinfacht, da Verhalten reproduzierbar ist
Seite 8
© Mirko Stocker 2014
Play macht dem Entwickler Spass
 Play kommt ohne Applikationsserver aus
 Basiert auf Netty
 Kann aber auch als WAR deployed werden
 Kein Deployment während der Entwicklung
 Beim Reload werden die geänderten Klassen und Templates neu kompiliert
und geladen
 Gute Fehlermeldungen direkt im Browser
 Gute Unterstützung von Unit, Functional und Integration Testing
 Sehr guter JSON und JavaScript Support
 JSON Parsen, Manipulieren und Generieren
 Webjars Support und Require-JS eingebaut
 Minimization, LESS/CoffeeScript Compilation, etc.
Seite 9
© Mirko Stocker 2014
Play macht dem Entwickler Spass
Seite 10
© Mirko Stocker 2014
Play Komponenten
Integrierter Webserver (Netty)
Webservices
API
HTTP Interface
Template
Engine
Asset
Compilation
Async I/O
HTML Form
Validation
JSON
Akka Actors
Diverse (NO-) SQL Anbindungen
Seite 11
© Mirko Stocker 2014
Interaktives
Buildsystem
und
Projekttemplates
Play User
Seite 12
© Olaf Zimmermann, Mirko Stocker 2014
Neues Play Projekt Erstellen
 Play wird von Typesafe (Scala, Akka) entwickelt
 Neue Play Projekte erstellt man am einfachsten mit dem Activator
 Activator: Build-Tool und Projekt-Generator von Typesafe
 Enthält diverse Beispielprojekte und Templates
Seite 13
© Mirko Stocker 2014
Neues Play Projekt Erstellen
 Verfügbare Templates auflisten
 ./activator
list-templates
 Neues Projekt aus Template play-java erstellen
 ./activator
new
my-play-java
play-java
 Browser-basiertes UI starten
 ./activator ui
 Interaktives CLI starten
 ./activator
Im Projektordner
 Eclipse Projekt erstellen
 ./activator eclipse
 IntelliJ IDEA Projekt erstellen
 ./activator idea
Seite 14
© Mirko Stocker 2014
Projektlayout
Sourcecode
Buildfile
Static Assets
Tests
├──
├──
│
│
│
│
│
├──
├──
│
│
├──
│
├──
│
│
│
│
│
│
└──
activator
app
├── controllers
│
└── Application.java
└── views
├── index.scala.html
└── main.scala.html
build.sbt
conf
├── application.conf
└── routes
logs
└── application.log
public
├── images
│
└── favicon.png
├── javascripts
│
└── hello.js
└── stylesheets
└── main.css
test
├── ApplicationTest.java
└── IntegrationTest.java
Seite 15
© Mirko Stocker 2014
MVC in Play
 MVC Pattern lässt sich aus der Projektstruktur erahnen:
├──
│
│
│
│
│
Model
app
├── controllers
│
└── Application.java
└── views
├── index.scala.html
└── main.scala.html
Controller
View
 Models sind typischerweise im app/models Ordner zu finden
Seite 16
© Mirko Stocker 2014
URLs und Routing in Play
 Sprechende URLs sehen nicht nur schöner aus
 sie sind auch stabiler
 wir können sie Bookmarken, Twittern
 und als Entwickler auch direkt manipulieren
 URLs in Play sind first class Citizens (und nicht bloss Strings)
 Compiler prüft Verwendung
 Interne tote Links sind nicht möglich
 Alle URLs werden in conf/routes zentral deklariert
Klasse.Methode
GET
GET
GET
POST
/
/computers
/computers/new
/computers
controllers.Application.index
controllers.Application.list
controllers.Application.create
controllers.Application.save
GET
POST
/computers/:id
/computers/:id
controllers.Application.edit(id:Long)
controllers.Application.update(id:Long)
Seite 17
© Mirko Stocker 2014
Controller: Actions
 Ein (Page) Controller behandelt die eingehenden Requests
 Controller ist eine Sammlung von Actions
package controllers;
import play.*;
import play.mvc.*;
public class Application extends Controller {
public static Result index() {
return ok("It works!");
}
}
% GET -s 'http://localhost:9000'
200 OK
It works!%
Seite 18
© Mirko Stocker 2014
Controller: Actions
 Der Play Router mappt die URLs zu den Controller Methoden
GET
/
controllers.Application.index()
GET
/assets/*file
controllers.Assets.at(path="/public", file)
 Vordefinierte Hilfsmethoden für gängige HTTP Status Codes
 Beispielsweise auch für Redirects
public static Result index() {
return redirect(routes.Application.index());
}
% GET -e 'http://localhost:9000'
303 See Other
Location: /
Seite 19
© Mirko Stocker 2014
Controller: Routing
 Verschiedenste URL Pattern möglich
GET
/computers/:id
controllers.App.edit(id: Long)
GET
/computers/search
controllers.App.search(query: String)
GET
/computers
controllers.App.list(page: Int ?= 1)
GET
/computers
controllers.App.show(page: Page)
Eigene
Klasse
 Entsprechen Methoden-Parameter und müssen nicht selber geparsed
werden (sogar mit eigenen Klassen):
public static Result edit(long id) { … }
public static Result search(String query) { … }
public static Result list(int page) { … }
public static Result show(Page page) { … }
Seite 20
© Mirko Stocker 2014
Controller: Routing prüft Parameter
% GET -e 'http://localhost:9000/computers/blabla'
400 Bad Request
…
Seite 21
© Mirko Stocker 2014
JSON im Request Body
@BodyParser.Of(BodyParser.Json.class)
public static Result sayHello() {
JsonNode json = request().body().asJson();
String name = json.findPath("name").textValue();
if(name == null) {
return badRequest("Missing parameter [name]");
} else {
return ok("Hello " + name);
}
}
Jackson
% curl
--header "Content-type: application/json" --request POST
--data '{"name": "HSR"}' http://localhost:9000/sayHello
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 9
Hello HSR
Seite 28
© Mirko Stocker 2014
JSON im Request Body – Scala
case class Named(name: String)
def sayHello() = Action(parse.json[Named]) { request =>
val named = request.body // named hat Typ Named
Ok(s"Hello ${named.name}")
}
% curl
--header "Content-type: application/json" --request POST
--data '{"name": "HSR"}' http://localhost:9000/sayHello
Bad Request wenn fehlt
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 9
Generiert JSON Parser
Hello HSR
Seite 29
© Mirko Stocker 2014
JSON als Result
 JSON erstellen
public static Result sayHello() {
ObjectNode result = Json.newObject();
result.put("message", "Hello Webnesday!");
return ok(result);
}
 ok() ist überladen und setzt automatisch den richtigen Content-Type
% curl --request GET http://localhost:9000/sayHello
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"message":"Hello Webnesday!"}
Seite 30
© Mirko Stocker 2014
Models
 Typischerweise POJOs in app/models
 Play ist nicht auf einen spezifischen OR-Mapper festgelegt
 JPA, JDBC, etc.
 Auf der Scala Seite auch Anorm und Slick (ähnlich wie MS LINQ)
 Beispiel mit JPA:
@Transactional
public static Result list() {
EntityManager em = play.db.jpa.JPA.em();
TypedQuery<Computer> query = em.createQuery(…)
Collection<Computers> computers = query.getResultList();
return ok(views.html.Application.index.render(user, computers));
}
Seite 31
© Mirko Stocker 2014
Non-Blocking I/O
Seite 32
© Olaf Zimmermann, Mirko Stocker 2014
Das Problem mit Threads
 Threads sind teuer in der Erstellung
 Lösung: Wiederverwenden, Threadpool verwenden
 Wie gross soll der Threadpool sein?
 Aus CPU-Sicht: Ein Thread / Core
 Zu gross
Memory-Overhead
Context-Switching
 Zu klein
Abhängigkeit von Downstream-Latenz
Requests blockieren wenn keine Threads mehr vorhanden sind
 Lösung:
 Keine Threads blockieren! Ein Thread / Core dauernd beschäftigt
 Non-Blocking I/O (NIO in Java, select / poll / epoll)
Seite 33
© Olaf Zimmermann, Mirko Stocker 2014
Latenz in einem verteilten System mit Blocking
DB
Backend
Service
Web
Server
Load
Balancer
Web
Server
S3
DB
Backend
Service
DB
Backend
Service
3rd Party
Service
Seite 34
© Olaf Zimmermann, Mirko Stocker 2014
DB Latenz
geht hoch
Latenz in einem verteilten System mit Blocking
Threads werden «gestaut»
Backend
Service
Web
Server
Load
Balancer
Web
Server
DB
S3
DB
Backend
Service
DB
Backend
Service
3rd Party
Service
Seite 35
© Olaf Zimmermann, Mirko Stocker 2014
Evented vs. Threaded Web Server
 Play basiert auf Netty, einem evented Web Server
 Traditionell sind Web Server eher Thread based: pro Request ein Thread
 Event basierend: Pro CPU ein Thread, Threads werden nicht blockiert.
 Auch asynchron oder non-blocking genannt
 Siehe auch Reactor Pattern (Pattern Oriented Software Architecture 2)
 Sobald bei der Bearbeitung des Requests auf eine downstream
Datenquelle (WS, DB) gewartet werden muss, wird der Thread freigeben
und ein Callback registriert
 Event-based skaliert besser bei vielen Requests
 Thread ist nur besetzt, wenn es etwas zu tun gibt
 Unabhängigkeit von der Downstream Latenz
Seite 36
© Mirko Stocker 2014
Non-Blocking Web Services
 Controller Action in Play kann auch ein Promise<Result> zurückgeben
 Promise: analog zu Future (Proxy Objekt für ein zukünftiges Resultat)
 Aufruf der Action beendet so schnell wie möglich
 Thread wird für nächsten Request frei
 Resultat wird ausgeliefert, wenn die Berechnung fertig ist
public static Promise<Result> longRunningAction() {
System.out.println("Action entry");
String url = "http://a.really.slow.server";
}
Promise<Result> result = WS.url(url).get().map(response -> {
System.out.println("Processing result");
return ok(response.getBody()).as("text/html");
});
Action entry
System.out.println("Action exit");
Action exit
return result;
Processing result
Seite 37
© Mirko Stocker 2014
Play Zusammenfassung
 Play ist eine leichtgewichtige moderne Alternative für Java Web
 Stateless, RESTful (Cloud-Ready)
 Embrace HTTP, nicht verstecken und wegabstrahieren
 Schneller Entwicklungszyklus, kein langwieriges Deployment
 Non-blocking und evented, läuft auf Netty
 Intern in Scala geschrieben, mit Java und Scala API
 Typsicher: Routing, URLs und Templates
 LESS, CoffeeScript, React, etc. unterstützt im Buildsystem
 JSON, Websockets, SSE Support
Seite 38
© Mirko Stocker 2014
Danke für eure Aufmerksamkeit!
Seite 39
© Olaf Zimmermann, Mirko Stocker 2014