Let's
all day

Anton Sarov
DevOpenSpace 2015
About me
Java and Eclipse RCP developer by day
Play Framework developer by night
@duxanton

Workshop PREREQUIREMENTs
- JDK 8
- Play Framework 2.3.x
- IDE (IntelliJ preferred)
it is 2015
- many-core CPUs are here
- everything is distributed
- IoT around the corner
- our traditional approach does not work here
thanks to @elmanu for the slide
and the solution?

functional programming
the actor model
evented server model
(Train station vs. Restaurant)
stateless architecture
functional programming
the actor model
evented server model
stateless architecture



object-oriented and functional programming

interoperability with java

ideas from Erlang and the Actor Model
toolkit and runtime for building highly concurrent, distributed and resilient message-driven applications on the JVM

non-blocking i/o
MVC
everything is compiled
Anatomy
- app
- conf
- project
- public
- test
OUr goal for today
Create a real-time auction platform
But with some limitations (due to time pressure, like in real life)
Basic operations
- Users have to be logged in to place bids
- Users place bids on items (highest bid wins)
- Bids from other users are visible automatically
models, views, CONtrollers
+
Actions & Routes
The ROUTES FILE
# Homepage
GET / controllers.Application.index()
# Display a person
GET /persons/:id controllers.Persons.show(id: Long)task: index page should return properly - with OK (HTTP 200)

task: define the LOGIN page (route, action, view)

views & templates
- Scala-based template engine
- plain HTML(5)
- the magic '@' character
task: REnder a template on the login page

hint: the action renders the view
Template engine: REVERSE Lookup
- Retrieves URL for a controller's action from routes file
- @routes.<Controller>.<Action>
<a href="@routes.Application.index()">Home</a>
<!-- yields: <a href="/">... -->task: Add a link in the login page pointing to the home page

Global error handling
- Override the methods which you need
public class ErrorHandler extends DefaultHttpErrorHandler {
// Execute default error handling
@Inject
public ErrorHandler(Configuration configuration, Environment environment, OptionalSourceMapper sourceMapper, Provider<Router> routes) {
super(configuration, environment, sourceMapper, routes);
}
// Override page not found behavior
@Override
protected Promise<Result> onNotFound(Http.RequestHeader requestHeader, String s) {
return Promise.pure(badRequest(
views.html.notFound.render(requestHeader.uri())));
}
}Global error handling
Place your ErrorHandler in the project's root or register it in the application.conf
play.http.errorHandler = "my.own.ErrorHandler"task: DEFINE a Custom error handler (only for 404 requests)

Forms
task: render an html login form at the login page

- username and password fields
- think about the action URL
Form submission handling I
// bind the form to the data coming from the request
DynamicForm dynamicForm = Form.form().bindFromRequest();
// extract single fields
String username = dynamicForm.get("username");
^-- matches the name of the HTML fieldtask: handle login form submission (route, action)

form data validation
task: basic form data validation

Form submission handling II
public static final Form<LoginData> loginForm = Form.form(LoginData.class);
Form<LoginData> submittedForm = loginForm.bindFromRequest();
if (submittedForm.hasErrors()) {
return badRequest("Username and password validation failed.");
} else {
String username = submittedForm.get().getUsername();
return ok("Hello, " + username);
}Play data validation constraints
- @Required
- @MinLength(...)
- See Constraints
task: proper form data validation

- Required fields
- Minimum length
- Proper validation message
form helpers
task: enhance form handling

- Username preserved even if there was a validation error
- Show the validation errors along with the form
Sessions
overview
-
Signed cookies
- application.secret key in application.conf is used
- Cookie is part of HTTP header
operations on cookies
session().clear();| Operation | Code |
|---|---|
| Add | |
| Get | |
| Remove | |
| Clear |
session("someKey");session().remove("someKey");session("someKey", someValue);Think "Map"
Action composition
a.k.a. Action Chaining
Action composition
@Security.Authenticated(Secured.class)
public Result index() {
return ok(views.html.index.render());
}Action composition
import play.mvc.*;
import play.mvc.Http.*;
public class Secured extends Security.Authenticator {
@Override
public String getUsername(Context ctx) {
return ctx.session().get("username");
}
@Override
public Result onUnauthorized(Context ctx) {
return redirect(routes.Application.login());
}
}task: session handling

- Secure the index page
- Redirect to login page if no session present
- Show the username in the index page if user is logged in
- Implement a logout action
web Services
http get
import play.libs.ws.*;
import static play.libs.F.Promise;
WSRequest request = WS.url("http://google.com");
Promise<WSResponse> promiseResponse = request.get();
//NOTICE: HTTP GET ^customizing WSRequest
WSRequest request =
WS.url("http://google.com")
.setRequestTimeout(1000)
.setQueryParameter("q", "Play Framework");http post
import play.libs.ws.*;
import static play.libs.F.Promise;
Promise<WSResponse> promiseResponse =
WS.url("http://example.com")
.setContentType("application/x-www-form-urlencoded")
.post("param1=value1");Retrieve response (blocking)
import play.libs.ws.*;
import java.util.concurrent.TimeUnit;
import static play.libs.F.Promise;
Promise<WSResponse> promiseResponse =
WS.url("http://google.com").get();
WSResponse response = promiseResponse.get(10, TimeUnit.SECONDS);
// ^^ Blocks thread until service respondstask: Authenticate via web services

- No more hard-coded validation
- Auth against http://devbay-auth.herokuapp.com
- Make a POST request to /auth with the credentials (urlFormEncoded)
- Wait for the response and react properly:
- 200 - OK, return null
- other - NOT OK, return "Wrong credentials"
json
handling json request
public Result auth() {
JsonNode json = request().body().asJson();
if (json==null) {
return badRequest("Oh, I actually expected JSON data");
} else {
// happy path
}
}
// or even better
@BodyParser.Of(BodyParser.Json.class)
public Result auth() {
JsonNode json = request().body().asJson();
}serving json response
public Result sayHello() {
ObjectNode result = Json.newObject();
result.put("exampleField1", "foobar");
result.put("exampleField2", "Hello world!");
return ok(result);
}task: authentication with json data via web service

- Rewrite the authentication check to send JSON data instead of form data.
- Examine the "status" field in the returned JSON object
models
ebean orm
ebean orm in play
project/plugins.sbt
build.sbt
lazy val myProject = (project in file(".")).enablePlugins(PlayJava, PlayEbean)addSbtPlugin("com.typesafe.sbt" % "sbt-play-ebean" % "1.0.0")conf/application.conf
ebean.default = ["models.*"]task: DEFINE a model for representing items to bid on

Quiz: what happens when Evolutions are enabled and we change the model?
scheduling async tasks
(with Akka)
// given an ActorSystem system
system.scheduler().scheduleOnce(
Duration.create(10, TimeUnit.MILLISECONDS),
() -> file.delete(), // Runnable object here
system.dispatcher()
);task: populate the database with initial data - when the application controller is initialized

task: render all items on the index page

task: render a template which serves another page with html and angularjs markup

"server sends [...] to connected clients and clients reply"
two-way http connection
task: define A Websocket-endpoint (route, action)

angularjs
-
data binding
-
directives
-
backend
group task: examine the javascript code


who handles the Websocket connection?
an akka actor!
akka in 30 seconds
- Akka
- Actor system
- Actors
- Messages
actors in java (not that fun as in scala)
public class HelloActorProtocol {
public static class SayHello {
public final String name;
public SayHello(String name) {
this.name = name;
}
}
}
import akka.actor.*;
public class HelloActor extends UntypedActor {
public static Props props = Props.create(HelloActor.class);
public void onReceive(Object msg) throws Exception {
if (msg instanceof SayHello) {
sender().tell("Hello, " + ((SayHello) msg).name, self());
}
}
}actors in scala (more fun)
import akka.actor._
object HelloActor {
def props = Props[HelloActor]
case class SayHello(name: String)
}
class HelloActor extends Actor {
import HelloActor._
def receive = {
case SayHello(name: String) =>
sender() ! "Hello, " + name
}
}task: define an actor for handling the websocket connection

live badass demo

Play Framework
next steps:
-
visit www.playframework.com
-
Work further on this demo project
Thank you
Now ask all the questions :)
Play Framework Workshop at DevOpenSpace
By duxanton
Play Framework Workshop at DevOpenSpace
Presentation for the Play Framework workshop at the Developer Open Space conference
- 928