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

  1. many-core CPUs are here
  2. everything is distributed
  3. IoT around the corner
  4. 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 field

task: 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

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 responds

task: 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