Any similarity the following slides may have with the actual presentation are purely coincidental.
The Play Framework is a clean alternative to the legacy Enterprise Java stacks. It focuses on developer productivity, modern web and mobile applications, and predictable, minimal resource consumption (CPU, memory, threads) resulting in highly performant, highly scalable applications.
Play is based on a lightweight, stateless, web-friendly architecture and features predictable and minimal resource consumption (CPU, memory, threads) for highly-scalable applications thanks to its reactive model, based on Iteratee IO.
Play supports building web services at massive scale using Java and Scala, while maintaining performance, reliability, and developer productivity.
activator new
activator ui
...or you could just use your IDE
# Home page
GET / controllers.Application.index()
GET /hello controllers.Application.hello(name)
GET /hello/:name controllers.Application.hello(name)
# Map static resources from the /public folder to the /aServer-Sent Eventsts URL path
GET /aServer-Sent Eventsts/*file controllers.AServer-Sent Eventsts.at(path="/public", file)
package controllers;
import play.*;
import play.mvc.*;
import views.html.*;
public class Application extends Controller {
public static Result index() {
return ok(index.render("Your new application is ready."));
}
public static Result hello(String name) {
return ok("Hello, " + name);
}
}
@(name: String)
<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<img src="images/jaxjug.png" alt="">
<p>Hello, @name!</p>
</body>
</html>
# Database configuration
# ~~~~~
# You can declare as many datasources as you want.
# By convention, the default datasource is named `default`
#
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
# db.default.user=sa
# db.default.password=""
conf/application.conf
Play Framework ORM for Play/Java Projects
Define Models
ebean.default="models.*"
Create classes
package models;
import java.util.*;
import javax.persistence.*;
import play.db.ebean.*;
import play.data.format.*;
import play.data.validation.*;
@Entity
public class Task extends Model {
@Id
@Constraints.Min(10)
public Long id;
@Constraints.Required
public String name;
public boolean done;
@Formats.DateTime(pattern="dd/MM/yyyy")
public Date dueDate = new Date();
public static Finder<Long,Task> find = new Finder<Long,Task>( Long.class, Task.class );
}
Simple lookups
Task t = Task.find.byId(4l);
List<Task> ts = Task.find.all();
Powerful query language
List<Task> tasks = find.where()
.ilike("name", "%coco%")
.orderBy("dueDate asc")
.findPagingList(25)
.setFetchAhead(false)
.getPage(1)
.getList();
import anorm._
import play.api.db.DB
DB.withConnection { implicit c =>
val result: Boolean = SQL("Select 1").execute()
}
Use {} for variable substitution
SQL(
"""
select * from Country c
join CountryLanguage l on l.CountryCode = c.Code
where c.code = {countryCode};
"""
).on("countryCode" -> "FRA")
Retrieve data and process with pattern matching
case class SmallCountry(name:String)
case class BigCountry(name:String)
case class France
val countries = SQL("Select name,population from Country")().collect {
case Row("France", _) => France()
case Row(name:String, pop:Int) if(pop > 1000000) => BigCountry(name)
case Row(name:String, _) => SmallCountry(name)
}
public static F.Promise<Result> timing() {
long start = System.currentTimeMillis();
F.Function<WSResponse, Long> timing = response -> System.currentTimeMillis() - start;
F.Promise<Long> yahoo = WS.url("http://www.yahoo.com").get().map(timing);
F.Promise<Long> google = WS.url("http://www.google.com").get().map(timing);
F.Promise<Long> bing = WS.url("http://www.bing.com").get().map(timing);
return F.Promise.sequence(yahoo, google, bing).map(timings -> {
Map<String, Long> map = new HashMap<>();
map.put("yahoo", yahoo.get(10));
map.put("google", google.get(10));
map.put("bing", bing.get(10));
map.put("total", System.currentTimeMillis() -start);
return ok(Json.toJson(map));
});
}
Play contains the play.libs.ws.WS
library for interfacing with web services
JSON parsing provided through the Jackson library
public static Result sayHello() {
ObjectNode result = Json.newObject();
result.put("exampleField1", "foobar");
result.put("exampleField2", "Hello world!");
return ok(result);
}
public static Result sayHello() {
JsonNode json = request().body().asJson();
if(json == null) {
return badRequest("Expecting Json data");
} else {
String name = json.findPath("name").textValue();
if(name == null) {
return badRequest("Missing parameter [name]");
} else {
return ok("Hello " + name);
}
}
}
JSON processing provided by the Jerkson library
WS.url(s"https://api.twitter.com/1.1/search/tweets.json").
sign(signature).
withQueryString(
"q" -> query,
"count" -> 20.toString,
"result_type" -> "mixed",
"lang" -> "en").
get.map { response =>
val json = Json.parse(response.body)
val tweets = (json \ "statuses").as[JsArray].value.
map(simplifyResponse)
Ok(views.html.twittersearch.render(query,
tweets.map(tweet =>
s"${tweet \ "tweet"} - ${tweet \ "name"}")))
}
val (stream, channel) = Concurrent.broadcast[JsValue]
WS.url(s"https://stream.twitter.com/1.1/statuses/filter.json").
sign(signature).
withQueryString("track" -> URLDecoder.decode(query, "UTF-8")).
postAndRetrieveStream("")(_ => Iteratee.foreach[Array[Byte]] {
data => channel.push(Json.parse(data))
};
Ok.feed(stream &>
Concurrent.buffer(100) &>
Enumeratee.map[JsValue](simplifyResponse) &>
EventSource()).as("text/event-stream")
def filter(query: String) = WebSocket.using[JsValue] { request =>
auth.map { signature =>
val (stream, channel) = Concurrent.broadcast[JsValue]
var chunkCache = ""
WS.url(s"https://stream.twitter.com/1.1/statuses/filter.json").
sign(signature).
withQueryString("track" -> URLDecoder.decode(query, "UTF-8")).
postAndRetrieveStream("")(_ => Iteratee.foreach[Array[Byte]] { data => channel.push(Json.parse(data)) })
val in = Iteratee.ignore[JsValue]
val out = stream &>
Concurrent.buffer(100) &>
Enumeratee.take(100) &>
Enumeratee.map[JsValue](simplifyResponse)
(in, out)
} getOrElse {
val response: JsValue = Json.obj("error" ->
"Please configure twitter authentication using the twitter.conf file")
(Iteratee.ignore[JsValue], Enumerator(response) >>> Enumerator.eof)
}
}