Let's

all day

Anton Sarov

JDay 2014, Lviv

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-java

Anatomy

  • app
  • conf
  • project
  • public
  • test

Launch the Play Application

$ cd ghjm
$ activator run

go to http://localhost:9000

Setting up the IDE

- Select File -> Import Project
- In the window that opens, select the ghjm project and click OK.
- On the next page of the wizard, select 'Import project from external model'
- On the same page choose SBT project and click Next.
- On the next page of the wizard, select additional import options
- Click Finish. 

OUr 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

  1. extend play.db.ebean.Model
  2. @Entity class annotation
  3. create a Model.Finder
  4. add your business logic

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

views & templates

  • Scala-based template engine
  • plain HTML(5)

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>

our dashboard view

@(user: User)

<h1>Welcome @user.getName!</h1>

views/dashboard.scala.html

calling webservices

libraryDependencies ++= Seq(
  javaWs
)

in your build.sbt file:

Promise<WSResponse> responsePromise = WS.url("http://www.jday.com.ua/").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())
    );
}

Webjars

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:

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:

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

By duxanton

Let's Play all day

Presentation for the Play Framework workshop at the JDay 2014 conference in Lviv

  • 849