Let's
all day

Anton Sarov
Warsjawa 2014
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)
Create a new Play project
$ cd /somewhere/where/you/have/write/permissions
$ activator new ghjm play-javaAnatomy
- app
- conf
- project
- public
- test
Launch the Play Application
$ cd ghjm
$ activator run
go to http://localhost:9000OUr goal for today
Create a really cool job portal web application
But with some limitations (due to time pressure, like in real life)
- Job search functionality only
- Simple UI
models, views, CONtrollers
+
Actions & Routes
- Avaje Ebean
- JPA
- JDBC
Working with SQL databases
AVAJE ebean Orm
- uses JPA annotations
- but much simpler
- session less
Creating a model
- extend play.db.ebean.Model
- @Entity class annotation
- create a Model.Finder
- add your business logic
Task: create a new model 'user'
hints :
use an id field of type long

Our User model
package models;
import play.db.ebean.Model;
import javax.persistence.*;
@Entity
public class User extends Model {
public static Finder<Long, User> find = new Finder<>(Long.class, User.class);
@Id
private Long id;
...
}The ROUTES FILE
# Homepage
GET / controllers.Application.index()
# Display a person
GET /persons/:id controllers.Persons.show(id: Long)let's rest
task: create a REst api for our user model
hints:
define request patterns in the routes file
implement the logic in a new controller (and in the model, if needed)

views & templates
- Scala-based template engine
- plain HTML(5)
- the magic '@' character
task: create an index page with a login form
hints:
action to render the page
a login action to handle the form submission
where should we send the login form data?

our index view
views/index.scala.html
<h1>Welcome to GHJM</h1>
<form action="@routes.Application.login()" method="post">
<input type="text" name="username"/>
<input type="submit" value="Login"/>
</form>task: create a dashboard page for the logged in user

our dashboard view
@(user: User)
<h1>Welcome @user.getName!</h1>views/dashboard.scala.html
// retrieve the request body in any controller action
request().body();
// retrieve the response in any controller action
response();
// redirect to some URL
return redirect(routes.Application.dashboard());actions and controllers
conventions and shortcuts
task: implement logout and session-awareness

// store session data
session("key", "someValue");
// retrieve session data
String value = session("key");
// clear the session
session().clear();calling webservices
libraryDependencies ++= Seq(
javaWs
)in your build.sbt file:
Promise<WSResponse> responsePromise = WS.url("http://warsjawa.pl/").get();in your code:
using from your controller:
public static Promise<Result> index() {
return WS.url(someUrl).get().map(response ->
ok(response.asJson().findPath("name").asText())
);
}task: define a new 'get' request with query parameter and a corresponding action which returns a promise

a short excursion to fp & scala
map
Evaluates a function over each element in the list, returning a list with the same number of elements.
scala> val numbers = List(1, 2, 3, 4)
scala> numbers.map((i: Int) => i * 2)
res0: List[Int] = List(2, 4, 6, 8)task: in the action make a ws request to fetch the jobs and print them on the screen/console
https://jobs.github.com/positions.json?description=javatask: for every job fetch also the coordinates of its location via the google geocode api
return the 'enhanced' job objects
http://maps.googleapis.com/maps/api/geocode/json?address=Warszawa&sensor=falseWebjars
libraryDependencies ++= Seq(
...
javaWs,
...
"org.webjars" % "angularjs" % "1.2.23"
...
)<script src="@routes.Assets.versioned("lib/angularjs/angular.js")"></script>in your views:
in your build.sbt file:
webjars
libraryDependencies ++= Seq(
javaJdbc,
javaEbean,
cache,
javaWs,
"org.webjars" % "angularjs" % "1.2.23",
"org.webjars" % "angular-leaflet-directive" % "0.7.7",
"org.webjars" % "leaflet" % "0.7.3"
)angularjs
-
data binding
-
directives
-
backend
angularjs
@(title: String)(content: Html)
<!DOCTYPE html>
<html ng-app="gitHubJobsMapApp">
...
<body>
<div>
<form ng-controller="Search">
<input type="search" ng-model="query"
placeholder="Enter a search term here"/>
<input type="submit" ng-click="search()" value="Search"/>
<hr>
<h1>All search results for {{query}}!</h1>
</form>
</div>
...
</body>
</html>main.index.scala:
angularjs
@(user: User)
@main(user.getName) {
<div ng-controller="Jobs">
<leaflet width="100%" height="500px" markers="markers"></leaflet>
<ul>
<li ng-repeat="job in jobs">{{job.location}} {{job.coordinates}}</li>
</ul>
</div>
}dashboard.scala.html:
let us be javascript ninjas!
angularjs
var app = angular.module('gitHubJobsMapApp', ["leaflet-directive"]);
app.factory('GHJM', function($http, $timeout) {
var jobsService = {
jobs: [],
query: function (query) {
$http({method: 'GET', url: '/jobs?q='+query}).
success(function (data) {
jobsService.jobs = data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
};
return jobsService;
});application.js:
angularjs
app.controller('Search', function($scope, $http, $timeout, GHJM) {
$scope.search = function() {
GHJM.query($scope.query);
};
});
app.controller('Jobs', function($scope, $http, $timeout, GHJM) {
$scope.jobs = [];
$scope.markers = [];
$scope.$watch(
// This function returns the value being watched.
function() {
return GHJM.jobs;
},
// This is the change listener, called when the value returned from the above function changes
function(jobs) {
$scope.jobs = jobs;
$scope.markers = jobs.map(function(job) {
return {
lng: job.coordinates.lng,
lat: job.coordinates.lat,
message: '<a href=\"'+job.url+'\">'+job.title+'</a>',
focus: true,
draggable: true
}
});
}
);
});application.js:
live badass demo

Play Framework
next steps:
-
visit www.playframework.com
-
Work further on this demo project
-
Help translate the documentation: see github.com/antonsarov/translation-project
Thank you
Now ask all the questions :)
Let's Play all day at Warsjawa
By duxanton
Let's Play all day at Warsjawa
Presentation for the Play Framework workshop at the Warsjawa conference
- 888