Vous êtes sur la page 1sur 67

Play!

framework – version 2
Expériences diverses
Version 2.6.X du framework (28.12.2017)

Jean-Claude Stritt
Table des matières
1 Introduction ...................................................................................................................................1
1.1 Play fram ew ork version 2 .................................................................................................1
1.2 Schém a général de fonctionnem ent...............................................................................2
2 Téléchargement et installation de Play version 2 ..................................................................3
2.1 Téléchargement de l’outil « SBT » ..................................................................................3
2.2 Téléchargement « IVY » des librairies de Play .............................................................4
2.3 Cache « IVY » local ............................................................................................................4
2.4 Téléchargement des fichiers « source » de Play fram ew ork....................................5
2.5 Création de variables d’environnement.........................................................................6
2.6 Vérification du bon fonctionnem ent de Play ................................................................9
3 Création d’une nouvelle application avec Play version 2 ...................................................11
3.1 Travailler sur un projet Play version 2 avec Net Beans .............................................13
3.2 Prem iers tests avec Play version 2 ..............................................................................14
3.3 Les fichiers de « build »..................................................................................................20
4 Projet Play « con seillers ».........................................................................................................21
4.1 Schém a de la BD « parlement » ....................................................................................22
4.2 Librairie daolayer.jar .......................................................................................................22
4.3 Structure du projet Play « con seillers » ......................................................................25
4.4 Fichier « route »................................................................................................................28
4.5 Contrôleurs .......................................................................................................................28
4.6 Configurer et utiliser une base de données MySQL .................................................29
4.7 Création d’une couche « métier » (DbWorker) ...........................................................30
4.8 Tests unitaires du w orker avec Play ............................................................................33
4.9 Création d’un « contrôleur » Play pour les conseillers ............................................34
4.10 Utilisation des méthodes du contrôleur ......................................................................35
4.11 Modèles pour les vues HTML et XML...........................................................................38
5 Création et lancem ent d’une version de production ...........................................................41
5.1 Préparer une version de production avec « stage » .................................................41
5.2 Lancem ent de la version « staged » sur MacOS........................................................42
5.3 Arrêt d’une application en production .........................................................................42
5.4 Installation sur un serveur Linux ..................................................................................42
6 Migration d’applications ............................................................................................................45
7 La gestion de la session et de la sécurité..............................................................................46
7.1 Exem ple de classe « LoginCtrl »...................................................................................46
7.2 Exem ple de classe « SessionManager » .....................................................................47
7.3 Gestion de la sécurité .....................................................................................................49
8 Publication d’une version de production sur Heroku..........................................................50
8.1 Inform ation essentielles .................................................................................................50
8.2 Connexion au com pte .....................................................................................................51
8.3 Installation de l’outil Toolbelt ........................................................................................53
8.4 Utiliser l’outil Toolbelt .....................................................................................................54
8.5 Déploiem ent d’une application Play existante ...........................................................56
8.6 Add-ons..............................................................................................................................59
8.7 Optim isation du slug déployé........................................................................................63
9 Conclusion ...................................................................................................................................65

Table des matières I


Play! framework – version 2 - Expériences diverses

1 Introduction
Play est un framework Java destinée à créer des applications web de type REST (Representational
State Transfer). Cette architecture, qui est celle originale du Web, est basée sur quelques principes
simples :
 Un composant lit ou modifie une ressource sur un serveur par le biais d’une représenta-
tion de cette ressource (un objet ou une liste d’objets en XML ou autre).
 L’URI (Uniform Resource Identifier) doit suffire pour identifier une ressource.
 Le protocole HTTP fournit toutes les opérations nécessaires (principalement GET et
POST).
 Chaque opération est autosuffisante (sans état de session sur le serveur, on dit que
l’application est « stateless »). L’application « cliente » peut bien sûr stocker un état de
session via un cookie ou par le stockage local présent maintenant dans HTML5. Une re-
quête peut facilement être balancé sur un autre serveur, si le premier est par exemple
surchargé (on parle de « load balancing »).
 On utilise des standards pour le transfert de données (texte, HTML, XML, JSON, AMF3).
Des exemples de requêtes avec retour d’information de chacun de ces standards seront
détaillés dans ce document.
Play ne concerne que la partie « serveur » d’une telle application. Pour la partie cliente, de l’HTML5
(HTML+CSS3+JavaScript) en co-utilisation avec une librairie telle que JQuery ou VueJS, ou encore
le framework Angular sont les tendances du moment.
D’abord mis au point avec les versions 1.1.1 jusqu’à 1.2.6, ce document a été remis à jour pour sup-
porter les dernières versions de Play.

1.1 Play framework version 2


La version 2.0 de Play est sortie en mars 2012. Elle correspond à une release majeure de Play, car
elle intègre le langage Scala en plus de Java et également les modifications importantes suivantes :
 La couche JPA est libre (au lieu d’Hibernate, on peut utiliser EclipseLink ou les Ebeans
par exemple).
 Scala est utilisé comme moteur de « template » HTML au lieu de Groovy ;
 Play supporte « OpenID » pour l’identification des utilisateurs et donc SSO (Single Sign-
On) sur un réseau d’entreprise.
 Play utilise « sbt » (Simple Build Tool) au lieu de scripts python pour monter des projets
Scala ou Java avec leurs dépendances.
 Les modules ne sont plus stockés localement, mais peuvent être n’importe où sur un ser-
veur de code de type « Ivy », « Maven » ou « Git » (un dépôt local sur son PC est tout de
même créé). N’importe quelle application peut faire office de « module », il suffit de men-
tionner sa dépendance avec « sbt ».
 Dans Play 2, chaque requête est potentiellement asynchrone, ce qui permet de créer des
applications Web non bloquantes et particulièrement fluides. On parle alors de « real
time web applications ». Dans une requête asynchrone, la connexion sera libérée sitôt la
requête reçue par le serveur et une autre sera créée lorsque la réponse sera prête à être
envoyée du serveur au client.
 Depuis Play 2.5.X, celui-ci utilise beaucoup « l’injection de dépendance » en collaboration
avec le module « Guice » de Google. On verra donc souvent des « @Inject » dans le code.

JCS / EM F Page 1 sur 65


Play! framework – version 2 - Expériences diverses

1.2 Schéma général de fonctionnement


Play, par le serveur web intégré « Netty » ou « Akka » (conseillé dans les dernières versions), ana-
lyse une demande HTTP d’un client et lance la méthode appropriée d’un contrôleur (une « action »
dans le jargon de Play). Ce contrôleur n’est rien d’autre qu’une classe Java qui regroupe le traite-
ment de plusieurs services-actions. Un service peut mettre éventuellement à jour des données dans
une base de donnés et/ou créer un rendu HTML, XML ou autre par une méthode de « render ». Il
peut aussi directement renvoyer du JSON qui est devenu le type de référence dans les dernières
versions de Play.

Note : cette illustration provient directement du site http://www.playframework.org

JCS / EM F Page 2 sur 65


Play! framework – version 2 - Expériences diverses

2 Téléchargement et installation de Play version 2


Avant Play 2.3, celui-ci était distribué depuis http://www.playframework.org comme un fichier zip
que l’on décompressait dans un dossier de son disque dur (voir ci-dessous). On conservait éven-
tuellement une ou l’autre version précédente :

2.1 Téléchargement de l’outil « SBT »


Depuis la version 2.3 (2014), les librairies qui constituent « Play framework » étaient distribuées via
un utilitaire appelé « TypeSafe Activator » et étaient stockées dans un dossier que l’outil choisissait
lui-même (un dossier « .ivy2 » dans le profil de l’utilisateur). Depuis la release 2.6.x (2017), ce
comportement est toujours le même, mais cet outil n’est plus téléchargeable. Il faut plutôt charger
l’utilitaire SBT (Simple Build Tool) depuis : http://www.scala-sbt.org/downlo ad.html :

Ensuite, on installe l’outil normalement et la commande « sbt » doit normalement être accessible
depuis la console du système d’exploitation (le chemin est ajouté automatiquement dans les v a-
riables d’environnement).

Attention, ce logiciel se met à jour automatiquement pour peu que le numéro de release « ma-
jeur » reste le même. Autrement, il faut désinstaller l’ancienne version et réinstaller.

JCS / EM F Page 3 sur 65


Play! framework – version 2 - Expériences diverses

2.2 Téléchargement « IVY » des librairies de Play


Lorsqu’on « monte » (build) un premier projet Play ou lorsqu’on lance simplement « sbt » dans la
console, celui-ci copie déjà un certains nombres de librairies dans un cache nommé « IVY local
cache ».

IVY est un outil de la fondation Apache pour gérer des dépendances logicielles (librairies) de-
puis différents dépôts sur Internet.

Pour plus de détails, voir sous : http://ant.apache.org/ivy/. Voici un exemple de vue pendant le
téléchargement sur une machine Windows après avoir tapé la commande « sbt » :

Notez ci-dessus qu’un certain nombre de librairies propres à « sbt » sont aussi copiées dans un
autre cache spécifique (.sbt).

2.3 Cache « IVY » local


Le cache IVY local se trouve dans la racine du profil de l’utilisateur courant sous « .ivy2 ». C’est donc
ici que se trouvent les principales librairies constituant « Play framework ». C’est également ici que
seront stockés les librairies que l’on crée et publie en local :

JCS / EM F Page 4 sur 65


Play! framework – version 2 - Expériences diverses

2.4 Téléchargement des fichiers « source » de Play framework


Cette étape n’est pas absolument nécessaire, mais montre que « Play framework » est un projet
« Open Source » et de temps en temps, il est intéressant d’aller fouiller dans les sources. Il est acces-
sible depuis le lien suivant :
https://github.com/playframework/playfr amework
On peut télécharger un fichier « .zip » du projet entier en cliquant sur le bouton vert
« Clone or download », puis sur « Download ZIP » ou avec le lien direct ci-dessous :
https://github.com/playframework/playfr amework/archive/master.zip
Les dossiers et fichiers décompressés :

JCS / EM F Page 5 sur 65


Play! framework – version 2 - Expériences diverses

2.5 Création de variables d’environnement


La commande « sbt » (anciennement « play » ou « activator ») aura besoin d’être retrouvée automa-
tiquement dans la console Windows ou MacOS. Les installateurs de « sbt », tant sous Windows que
sous MacOS, ajoutent automatiquement ce chemin dans la « variable d’environnement » PATH de
chacun des systèmes. « Play framework » utilise également Java et la commande « javac » (compila-
teur Java du JDK). Il faut vérifier que ce genre de référencement soit également fait correctement.

2.5.1 Ajout de variables d’environnem ent sous MacOS


Le plus simple sous MacOS est de créer ou d’ouvrir un fichier existant « .bash_profile » à la racine
du profile de l’utilisateur courant. Ce fichier est normalement caché.

… et d’y introduire par exemple les lignes suivantes (en rouge les variables essentielles).
# Setting default charset
export LC_ALL=fr_CH.UTF-8
export LANG=fr_CH.UTF- 8

# Setting Java options Attention, pour compiler


export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) sans erreur, JAVA_OPTS
export JAVA_OPTS=" -server -Xss4M" avec une valeur de pile
#export JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF -8" importante (Xss) semble
nécessaire dans les der-
# Setting MySQL nières versions de Play
export MYSQL_HOME=/usr/local/mysql/bin

# Setting playframework variables


export PLAY2_REPO=$HOME/.ivy2
export PLAY2_LOCAL =$HOME/.ivy2/local

# Setting LibreOffice variables


export LIBREOFFICE_HOME=/Applications/LibreOffice.app/Contents/MacOS
export UNO_PATH=/Applications/LibreOffice.app/Contents/program

# Setting Maven variable


export MAVEN_HOME= $HOME/Dropbox/_DEV/Java/Utilitaires/apache-maven -3.3.9/bin
#export MAVEN_OPTS="-Duser.language=fr -Dfile.encoding=UTF -8"

# Setting Python variables


export PYTHON_HOME =/Library/Frameworks/Python.framework/Versions/3.6/bin
alias python='python3'

# Setting OS and global path


export OS_PATH =/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
export PATH=$OS_PATH:$MYSQL_HOME:$LIBREOFFICE_HOME :$PYTHON_HOME:$MAVEN_HOME

# Projects variables
export JAVA_PROJECTS=$HOME/Dropbox/_DEV/Java/Projets/NetBeans
export PLAY_PROJECTS=$HOME/Dropbox/_DEV/Java/Projets/NetBeans/Play
export MAVEN_PROJECTS= $HOME/Dropbox/_DEV/Java/Projets/Maven
export SQLITE_DB=$HOME /Dropbox/_DEV/DBMS/SQLite

Attention, les fins de lignes doivent être de type « linefeed » (LF) uniquement.

JCS / EM F Page 6 sur 65


Play! framework – version 2 - Expériences diverses

Il faut relancer l’outil « Terminal » pour vérifier les modifications. Ensuite, on peut taper « env » ou
des commandes « echo » comme ci-dessous :

2.5.2 Ajout de variables d’environnem ent sous Window s (ici la version 10)
Pour créer une variable d’environnement sous Windows, on peut partir de l’icône « Ordinateur » :
 Clic-droit sur « Ordinateur », puis clic-gauche sur « Propriétés » ;
 Clic-gauche sur « Paramètres système avancés » ;

 Clic-gauche sur le bouton « Variables d’environnement » ;

JCS / EM F Page 7 sur 65


Play! framework – version 2 - Expériences diverses

Il faut cliquer sur le bouton « Nouvelle » des « variables système » ou des « variables utilisa-
teur », suivant que vous disposez des droits sur toute la machine ou non et si vous désirez ins-
taller pour tous les utilisateurs ou non.

On voit bien ci-dessous la variable PLAY_PROJECTS ajoutée pour avoir accès facilement au dossier
des projets personnels écrits pour Play. Attention de ne pas oublier aussi JAVA_OPTS pour disposer
d’assez de mémoire pour la pile. Remarquez aussi le « preferIPv4Stack=true » qui a semblé obliga-
toire sous Windows pour qu’sbt retrouve sur Internet les librairies de Play :

JAVA_HOME n’est pas absolument nécessaire pour Play, mais cette variable est souvent utilisée par
d’autres utilitaires comme MAVEN (pour la commande « mvn »).
Il faut encore modifier la variable PATH, pour y ajouter le chemin vers la commande « javac » du
JDK (à télécharger et installer, si cela n’a pas encore été fait).
On sépare les différents chemins par des « ; » (points-virgules). Si on le fait dans les variables « utili-
sateurs » comme ci-dessus, ces chemins sont ajoutés au chemin (PATH) déjà exist ant.
Sur ma machine, le chemin ajouté a été par exemple :
 C:\Program Files (x86)\Java\jdk1.8.0_151\bin

Ce JDK est une version 32 bits pour assurer la compatibilité avec OpenOffice (ou
LibreOffice) qui n’existent qu’en 32 bits et qui sont utilisés éventuellement pour le
« reporting ».

JCS / EM F Page 8 sur 65


Play! framework – version 2 - Expériences diverses

2.6 Vérification du bon fonctionnement de Play


Voyons d’abord le test sur MacOS, ensuite sous Windows. Pour faire ce test, on lance simplement :
 sbt -help sur MacOS
 sbt help sur Windows

2.6.1 Vérification sous MacOS

Il est intéressant de constater que cet outil utilise plusieurs variables d’environnement comme :
 JAVA_HOME
 JAVA_OPTS
 SBT_OPTS

JCS / EM F Page 9 sur 65


Play! framework – version 2 - Expériences diverses

2.6.2 Vérification sous Window s

Contrôle :

Pour la suite de ce document, tous les exemples et illustrations fournis seront tirés indiffé-
remment de tests sous MacOS ou Windows, car tout se passe le plus souvent de la même fa-
çon.

JCS / EM F Page 10 sur 65


Play! framework – version 2 - Expériences diverses

3 Création d’une nouvelle application avec Play version 2


Tout est expliqué ici : https://www.playframework.com/do cument ation/2.6.x/NewApplic ation.
Depuis Play 2.6.x, le principe est que l’on crée une nouvelle application basée sur un modèle de type
« g8 » (pour plus de détails, voir ici : http://www.foundweekends.org/giter8/). Exemple pour créer
un projet Java de type « Welcome to Play ! » :

On voit qu’il a fallu préciser :


 le nom du projet : demo_play ;
 quelle organisation travaille sur ce projet: ch.emf.info ;
 la version de scala à utiliser : prendre le défaut ;
 la version de play que l’on désire utiliser : prendre le défaut ;
 la version de sbt à utiliser : prendre le défaut.
On peut ensuite lancer la commande « sbt depuis le dossier du projet pour voir s’il y a des erreurs
mentionnées. On entre alors dans un mode interactif :
 run lancera l’application sur « localhost:9000 »
 enter stoppera l’application
 exit permettra de sortir du mode interactif

JCS / EM F Page 11 sur 65


Play! framework – version 2 - Expériences diverses

Le résultat est affiché automatiquement dans le navigateur par défaut :

Avec le Finder (ou l’Explorer sur Windows), on se place ensuite dans le dossier en question pour
comprendre un peu la structure.
app  dossier principal des sources de l’application Java qui
contient le package « controllers » pour le ou les différents con-
trôleurs et le package « views » pour les modèles de vues ren-
voyés aux clients
conf  contient le fichier « application.conf » de configuration
de l’application sur le serveur, le fichier « routes » qui définit les
liens entre une URI et une méthode Java dans un contrôleur,
ainsi que « logback.xml » un fichier qui définit comment on va
gérer les informations sur les erreurs ou les messages de
l’application
project  contient les fichiers de configuration du projet
public  contient la partie cliente de l’application Play (géné-
ralement les fichiers html, css, js et autres qui seront donc éga-
lement sur le serveur)
target  contient les fichiers compilés de l’application
test  contient les classes de test

JCS / EM F Page 12 sur 65


Play! framework – version 2 - Expériences diverses

3.1 Travailler sur un projet Play version 2 avec NetBeans


NetBeans est un IDE apprécié et sous Play 1.x, on pouvait facilement « netbeansifier » un projet
grâce à une commande « netbeansify ». Avec Play 2, cette commande n’existant plus, c’est la com-
mande « activator eclipse » qui permettait de créer les fichiers de base pour Eclipse, puis d’importer
ce projet Play-Eclipse dans NetBeans. Mais cela fonctionnait assez mal et il fallait une bonne dose de
compréhension pour arriver à un projet sans erreur.
Heureusement, depuis NetBeans 8.2 (nov. 2016), un développeur a sorti quasi en même temps un
plugin qui nous simplifie grandement la vie, c’est « Pleasure Play Framework Plugin ». Le lien est ici :
http://plugins.netbeans.org/plugin/61050 .
Installez ce plugin, avec l’aide de cette vidéo :
https://www.youtube.com/watch?v=Fw_w Q8Rjz7k
Créez ensuite un nouveau projet ou ouvrez un projet existant et c’est le
rêve par rapport à avant (NetBeans 8.x sans plugin). C’est tout de même
pas facile d’avoir l’aide (code completion) partout.
Pour plus d’informations sur « l’anatomie » d’une application Play 2.6.X,
vous pouvez aussi consultez la page :
https://www.playframework.com/documentation/2.6.x/A nato my
Pour qu’un projet s’affiche aussi bien dans NetBeans et fonctionne, il
nécessite les conditions suivantes :
1. Le fichier « plugins.sbt » contient les plugins essentiels du pro-
jet, généralement au moins le plugin « Play » lui-même et le plu-
gin « Eclipse ».
2. Le fichier « build.sbt » est le fichier qui monte (build) un projet
comme le fait « ant » pour les autres projets NetBeans.
3. Un dossier « conf » et un fichier « application.conf » qui contient
toute la configuration du projet (par exemple la BD à utiliser).

A noter que NetBeans possède un « cache » et que celui-ci bogue parfois après des im-
portations de projets Eclipse. Si vous êtes dans ce cas (par exemple, si un point rouge
sur le nom de projet indique encore des erreurs dans certains fichiers, alors que mani-
festement il n’y en a plus dans la compilation Play, il faut :
 quitter NetBeans ;
 supprimer le cache manuellement (effacer le dossier « index ») dans :
/Users/<username>/Library/Caches/NetBeans/8.x/index  MacOs
C:\Users\<username>\AppData\Local\NetBeans\Cache\8.x\index  Windows

 relancer NetBeans

JCS / EM F Page 13 sur 65


Play! framework – version 2 - Expériences diverses

3.2 Premiers tests avec Play version 2


Dans ce chapitre, nous allons modifier le projet « demo_play » pour tester et montrer ce que l’on
peut faire avec Play du point de vue des requêtes HTTP et des résultats escomptés. Premier essai :
franciser le texte du message en passant ce texte en paramètre d’une méthode Java dans le contrô-
leur.

3.2.1 Une vue HTML codé en SCALA (« index.scala.html »)


On trouvera beaucoup d’explications (en anglais) sur le site de PLAY :
https://www.playframework.com/documentation/2.6.x/Jav aTemplat es
Pour simplifier, disons que les vues (dans le package « /app/views ») sont des fichiers de type
« texte » à notation « Scala » et qui permettent de préparer très aisément du HTML ou du XML de
manière programmée. Ces fichiers sont compilés sous la forme de fichiers « .class » par PLAY dans
un dossier spécifique sous « target/scala-2.x/classes/views » (plus « html » ou « xml » selon le type
de vue).
Ainsi, le fichier « index.scala.html » produit une classe « views.html.index » :

@(message: String)
@main("Welcome to Play") {
<h1>@message</h1> 
}

3.2.2 Le contrôleur
Un contrôleur n’est rien d’autre qu’une classe Java étendant « play.mvc.Controller » et qui regroupe
plusieurs méthodes de « services » (ci-dessous une seule méthode « index ») :
package controllers;
import play.mvc.*;

public class HomeController extends Controller {

public Result index() {


return ok("Bienvenue dans Play!");
}
}

Dans cet exemple, la fonction « ok » permet de renvoyer une réponse HTTP de type « 200 OK » avec
un texte de type « text/plain ». La méthode de rendu « index.render » a été créé à partir de la vue
SCALA compilé (voir page précédente).

JCS / EM F Page 14 sur 65


Play! framework – version 2 - Expériences diverses

3.2.3 Le fichier de « routes »


Un client web (un navigateur) a directement accès aux méthodes
d’un contrôleur via un fichier de routage appelé « routes ». Ce fichier
met en relation une requête HTTP et une méthode d’un contrôleur.
Pour plus d’informations et d’exemples sur la manière de gérer le
fichier « routes », vous pouvez vous rendre à l’adresse suivante :
https://www.playframework.com/documentation/2.6.x/Jav aRouting
Exemple :
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# An example controller showing a sample home page


GET / controllers.HomeController.index

# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public ", file :Asset)

Chaque ligne de « routes » est composée de 3 parties :


 la méthode HTTP qui peut être : GET, POST, PUT, DELETE ou OPTIONS ;
 l’URI (en français « identifiant uniforme de ressource ») qui est un identificateur unique de la
ressource depuis la racine du serveur ;
 la méthode « action » Java à appeler (elle est identifiée encore par le package et la classe où
elle se trouve).

3.2.4 Les types de retour possibles (classe Result)


Un retour HTTP comporte normalement 3 éléments essentiels :
 un code de retour (status code) ;
 un ou plusieurs en-têtes HTTP (headers) ;

 un corps de message (body).

Dans Play 2, l’objet de retour est de type « Result » (play.mvc.Result). Une autre classe au nom
quasi identique « play.mvc.Results » contient plusieurs méthodes statiques simples comme la
méthode « ok (…) » pour retourner des messages HTTP correctement encapsulés. Voir les
exemples qui suivent …

JCS / EM F Page 15 sur 65


Play! framework – version 2 - Expériences diverses

3.2.5 Exem ples de m éthodes de retour de la classe « play.mvc.Results »

a) ok(…) qui retourne un texte sim ple


public Result index() {
Result result = ok( "Le serveur Play version "
+ play.core.PlayVersion.current()
+ " est fonctionnel !" );
return r esult;
}

b) ok(…) qui retourne le type de la requête


public Result index() {
return ok("Requête : " + request()); // dernière requête reçue
}

c) ok(…) qui retourne un param ètre fourni dans la requête


Si dans le fichier « route », on introduit ceci :
# Test JC
GET / :nom controllers.HomeController.renvoyerMonNom( nom: String)

On peut ensuite ajouter une méthode dans le contrôleur qui récupérera ce paramètre. Dans cet
exemple, celui-ci sera renvoyé vers l’utilisateur avec la méthode ok(…) :
public Result renvoyerMonNom(String nom) {
return ok("Bonjour " + nom + " !");
}

JCS / EM F Page 16 sur 65


Play! framework – version 2 - Expériences diverses

d) ok(…) qui retourne du JSON


public Result index() {
ObjectNode jsonObj = Json.newObject();
jsonObj.put( "format" , "JSON" );
jsonObj.put( "message", "C'est un message JSON");
return ok(jsonObj).as("application/json" );
}

e) ok(…) qui retourne un m essage avec un « header » spécifique


public Result index() {
response().setHeader(CACHE_CONTROL, "max -age=3600");
response().setHeader(ETAG, "xxx" );
return ok("Il y a une entête cachée dans ce message !");
}

f) ok(…) qui retourne un m essage avec un cookie


public Result index() {
Cookie cookie = Cookie.builder("theme", "blue")
.withMaxAge(Duration.ofSeconds(3600))
.withPath( "/some/path" )
.withDomain(".example.com" )
.withSecure(false)
.withHttpOnly( true )
.withSameSite(Cookie.SameSite.STRICT)
.build();
response().setCookie(cookie);
return ok("Un cookie accompagne ce message !");
}

JCS / EM F Page 17 sur 65


Play! framework – version 2 - Expériences diverses

g) ok(…) en effaçant une valeur de cookie


public Result index() {
response().discardCookie("theme" );
return ok("Une valeur de cookie a été supprimée !");
}

h) notFound(…) qui retourne une erreur 404


public Result index() {
return notFound( "<h4>Erreur 404 (page non trouvée) !</h4>", "utf -8")
.as("text/html; charset=utf-8" );
}

Il est important dans les réponses de type « text/html » de spécifier l’encodage autant
pour le texte HTML que pour le content-type de la réponse.

i) internalServerError(…) qui retourne une erreur 500


public Result index() {
return internalServerError("Oops");
}

j) status(…) qui retourne un status personnalisé


public Result index() {
return status(488, "Une réponse avec un status personnalisé" !);
}

JCS / EM F Page 18 sur 65


Play! framework – version 2 - Expériences diverses

k) redirect(…) qui redirige sur une autre « route »


Avec la « route » suivante :
# Test JC
GET /user/home controllers.HomeController.redirigerSurAccueil()

… et les « méthodes » suivantes dans le contrôleur :


public Result index() {
return redirect( "/user/home" );
}

public Result redirigerSurAccueil() {


return ok("<h1>Page d'accueil</h1>").as( "text/html") ;
}

L’appel de l’adresse http://localhost:9000 va engendrer une redirection sur :

Remarquez le code de statut 303, symbole d’une redirection !

l) tem poraryRedirect(…) qui redirige aussi sur une autre « route »


En remplaçant « redirect(…) » par « temporaryRedirect(…) », on retourne le code de status 307.
public Result index() {
return temporaryRedirect("/user/home");
}

JCS / EM F Page 19 sur 65


Play! framework – version 2 - Expériences diverses

3.3 Les fichiers de « build »


Le framework Play a maintes fois été modifié dans sa façon de monter un projet pour en faire une
application exécutable. Les outils également ont changé (play  activator  sbt).

Depuis la version 2, c’est principalement l’outil « sbt » (Simple Build Tool) et des fichiers de
configuration « .sbt », « .properties » et « .scala » qui l’aident dans cette tâche. Le système de
« build » est documenté (en anglais) ici :
https://www.playframework.com/documentation/2.6.x/Build

Tirés de l’application « demo_play », les 3 fichiers les plus importants dans un projet sont :
 project/build.properties  contient la version de l’outil SBT à utiliser :
sbt.version=1.0.4 (en date du 22.12.2017)
 project/plugins.sbt  contient la définition des plugins « sbt » nécessaires par le projet,
y compris la version de Play lui-même. Voici un exemple :
// comment to get more information during initialization
logLevel := Level.Warn

// use the Play sbt plugin for Play projects (22.12.2017)


https://www.playframework.com/
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.10" )

// add eclipse -plugin (20.12.2017)


https://github.com/typesafehub/sbteclipse
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin"%"5.2.4")

// pour auto-load d'un changement dans Google Chrome


https://github.com/jamesward/play- auto -refresh
addSbtPlug in("com.jamesward" % "play-auto- refresh" % "0.0.16")

// pour faire des packages light sous heroku (20.12.2017)


https://github.com/heroku/sbt- heroku
addSbtPlugin("com.heroku" % "sbt-heroku" % "2.1.1" )

 build.sbt  (à la racine du projet) contient toutes les informations de dépendances (li-


brairies non comprises dans Play) à d’autres modules.
name := """demo_play"""

organization := "ch.emf.info"

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayJava)

scalaVersion := "2.12.4"

libraryDependencies += guice

Pour trouver quelques exemples de la syntaxe « sbt », vous pouvez consulter la page :
http://www.scala-sbt.org/1.x/do cs/G etting-St arted.html
Par exemple, la syntaxe avec ++= indique une concaténation d’une séquence de librairies avec une
autre déjà existante (par exemple celle de Play par défaut), tandis que += indique l’ajout d’un seul
élément à la séquence.

JCS / EM F Page 20 sur 65


Play! framework – version 2 - Expériences diverses

4 Projet Play « conseillers »


Dans ce chapitre, nous allons détailler la création d’un nouveau projet Play « conseillers » utilisant
une base de données MySQL « parlement ». Ce projet est aussi différent du projet « demo_play », par
la variation des requêtes HTTP utilisées (GET et POST). Voici la vue par défaut de ce projet :

Le projet entier peut être téléchargé depuis ici : https://github.com/emfinfo/conseillers. Le dossier


« data » contient un script qui permet de monter la base de donnée sur un serveur MySQL local ou
distant.
Ces mêmes services REST peuvent être testés sur le site http://conseillers.herokuapp.co m.

JCS / EM F Page 21 sur 65


Play! framework – version 2 - Expériences diverses

4.1 Schéma de la BD « parlement »


Cette BD contient une liste de tous les conseillers nationaux suisses depuis 1848. Elle est construite
sur la base des données d’une exportation Excel que l’on peut effectuer ici :
https://www.parlament.ch/fr/r atsmit glieder?k=*

4.2 Librairie daolayer.jar


Play pourrait très bien se satisfaire de travailler uniquement avec du JDBC pour l ’accès à la base de
données. Mais dans la section informatique de l’EMF, nous aimons bien JPA et la surcouche
d’abstraction « daolayer.jar » développé en interne. Elle est utilisée dans la couche « métier » d’une
application, généralement une classe nommée « DbWorker ».
Cette surcouche à JPA amène simplicité et sécurité aux développements Java avec une BD :
 simplicité, car on ne s’occupe pas de la connexion à la BD (c’est automatique avec le fi-
chier « persistence.xml ») ;
 simplicité, car un seul objet « dao » permet d’effectuer toutes sortes de requêtes sur
n’importe quel objet « métier » (un canton, un parti, un conseiller, etc) ou même un mé-
lange avec des jointures ; on reçoit en retour un objet ou une liste d’objets ;
 simplicité encore, car les requêtes JPQL nécessaires, peuvent être construites sans trop
de connaissance de ce langage, grâce à deux classes « Search » (une seule classe-entité) et
« Search2 » (jointure possible sur plusieurs classes-entités). Des exemples peuvent être
trouvés dans les méthodes de test se trouvant dans « JpaDaoTest.java ».
 sécurité grâce à une trentaine de tests unitaires assurant sa stabilité.
 sécurité, car on a suivi les conseils donnés dans ce lien à propos de l’injection SQL :
https://www.playframework.com/documentation/1.2/s ecurity .

JCS / EM F Page 22 sur 65


Play! framework – version 2 - Expériences diverses

C’est dans la phase d’initialisation des tests unitaires (dans « JpaDaoTest.java »), que la liste des
« conseillers nationaux suisses » peut être importée depuis un fichier « .csv » vers la BD MySQL
nommée « parlement ». Pour cela, il faut mettre à true la valeur de la propriété « IMPORT_DB » dans
le fichier « JpaDaoTest.java ». On peut aussi directement « monter » la BD avec l’outil « MySQLWork-
bench » et le script « db-create-parlement-all.sql » fourni dans le dossier « data ».

4.2.1 Tests unitaires


>>> tests.JpaConnectionTest
<<<
DaoLayer V5.1.5 / 3.10.2017

*** test01_connect ...


- source: parlementPU
- result: true

*** test02_deconnect ...


- source: parlementPU
- result: true

*** test03_reconnect ...


- source: parlementPU
- result: true

>>> tests.JpaDaoTest <<<


DaoLayer V5.1.5 / 3.10.2017

*** test01_create ...


- source: Tartampion Jules
- result: 3542 (pk)

*** test02_read ...


- source: 3542 (pk)
- result: Tartampion Jules,
attached: false

*** test03_update ...


- source: 3542 (pk)
- result: Tartampion
Juliette,
attached: true

*** test04_delete ...


- source: 3542 (pk),
Tartampion Juliette
- result: true

JCS / EM F Page 23 sur 65


Play! framework – version 2 - Expériences diverses

4.2.3 JpaDaoAPI

JCS / EM F Page 24 sur 65


Play! framework – version 2 - Expériences diverses

4.3 Structure du projet Play « conseillers »


Le projet Play « conseillers » était pendant longtemps scindé en un projet principal « conseillers » et
un sous-projet Play appelé « conseillers-models ».
Mais pourquoi donc stocker les classes-entités JPA dans un sous-projet annexe ?
 Réunir toutes les classes-entités JPA dans un même sous-projet permet de limiter les dé-
pendances du sous-projet (généralement juste un lien sur EclipseLink ou Hibernate).
 On s’assure aussi rapidement que les classes-entités sont correctes du point de vue JPA,
car le sous-projet est compilé avant le reste du projet principal.
 Cela permet d’utiliser une librairie Java appelée « lombok » pour créer automatiquement
tous les getters/setters des classes-entités et de les garder ainsi aussi simples que pos-
sible en pouvant se concentrer sur les annotations JPA.
NetBeans 8.2 et le plugin « Pleasure Play Framework Plugin » ont changé la donne, en ce sens que le
plugin, s’il apporte beaucoup, ne supporte pas les sous-projets tels qu’ils sont attendus par Play. Le
palliatif à ce problème est de créer effectivement un sous-projet, mais de type Maven. Cela permet
de récupérer le code compilé très facilement avec une ligne spécifique dans le fichier « build.sbt » du
projet. On peut aussi, si on le désire, récupérer les fichiers « sources » et « javadoc » d’une librairie.
// dépendences (voir dernières versions sur http://mvnrepository.com )
libraryDependencies ++= Seq(
javaJpa,
"ch.emf.info " % "conseillers -models" % " 1.0.5",
"ch.emf.info" % "conseillers -models" % "1.0.5" classifier "sources",
"ch.emf.info" % "conseillers -models" % "1.0.5" classifier "javadoc",
"ch.emf.info" % "basiclib" % "1.0.13",
"ch.emf.info" % "basiclib" % "1.0.13" classifier "sources",
"ch.emf.info" % "basiclib" % "1.0.13" classifier "javadoc",
"ch.emf.info" % "daolayer" % "5.1.5" ,
"ch.emf.info" % "daolayer" % "5.1.5" classifier "sources",
"ch.emf.info" % "daolayer" % "5.1.5" classifier "javadoc",
"mysql" % "mysql -connector-java" % "5.1.38").map(_.force())

4.3.1 Intégration de librairies non m anagées par Play


Un dossier « lib » dans Play peut contenir des librairies « .jar », dont la dépendance n’est pas gérée
automatiquement par Play. Dans le projet « conseillers », il n’y a actuellement plus aucune dépen-
dance pour gérer les librairies « maison » que sont BasicLib (méthodes basiques) et DaoLayer (li-
brairie JPA), car ces projets ont été déplacés sur « github ».
 https://github.com/emfinfo/emfinfo.github.io  dépôt des binaires « .jar » ;
 https://github.com/emfinfo/basiclib  pour les sources de la librairie BasicLib ;
 https://github.com/emfinfo/daolayer  pour les sources de la librairie DaoLayer.
Mais c’est bien dans « lib » que le logiciel GABI 2 stocke par exemple sa dépendance à « DocMosis »
(librairie pour imprimer des PDF).

JCS / EM F Page 25 sur 65


Play! framework – version 2 - Expériences diverses

4.3.2 Sous-projet Maven « Conseillers-models »


Un projet Maven peut être créé avec NetBeans. Un fichier « pom.xml » permet de désigner les diffé-
rences dépendances (compilation ou pour les tests) du projet envers différentes librairies. Conseil-
lers-models mémorise toutes les classes-entités de type JPA.

On vous propose de vous référer au projet sur GitHub pour visionner les classes-entités :
https://github.com/emfinfo/co nseillers-mo dels
Ces classes-entités ont été générées automatiquement par NetBeans avec le fichier « persis-
tence.xml » selon une procédure bien établie dans le module 223 de l’EMF. Deux annotations lom-
bok permettent d’utiliser toutes les méthodes de « getters-setters », les méthodes « equals » et
« hashcode » (sans que ces méthodes ne soient visibles dans le code).
@Entit y
@Table(name = "t_conseiller")
@Data
@EqualsAndHashCode (of = "pkConseiller" , callSuper = false)
public class Conseiller implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "pkConseiller")
@JsonProperty("id")
private Integer pkConseiller;

@Basic(optional = false)
@Column(name = "actif")
private boolean actif;

@Column(name = "dateNaissance")
@Temporal(TemporalType.DATE)
@JsonSerialize(using = CustomDateSerializer. class)
private Date dateNaissance;
...

@Override
public String toString() {
return nom + " " + prenom;
}
}

JCS / EM F Page 26 sur 65


Play! framework – version 2 - Expériences diverses

4.3.3 Fichier « build.sbt » dans Conseillers


Ce fichier permet de préparer l’application « conseillers » avec toutes ses dépendances :
import com.typesafe.config._

routesGenerator := InjectedRoutesGenerator

val conf = ConfigFactory.parseFile(new


File("conf/application.conf")).resolve()

libraryDependencies += guice

// pour récupérer conseillers- models dans le dépôt local Maven


resolvers += "Local Maven Repository" at Path.userHome.asFile.toURI.toURL +
".m2/repository"

// pour récupérer éventuellement basiclib et daolayer sur github (si pas dans .m2)
resolvers += "EMF- info Repository" at
"http://emfinfo.github.io/javalibs/releases"

// dépendences (voir dernières versions sur http://mvnrepository.com )


libraryDependencies ++= Seq(
javaJpa,
"ch.emf.info" % "conseillers -models" % "1.0.5",
"ch.emf.info" % "basiclib" % "1.0.13",
"ch.emf.info" % "daolayer" % "5.1.5" ,
"mysql" % "mysql -connector-java" % "5.1.38").map(_.force())

// à cause d'une "warning" : class path contains multiple SLF4J bindings


libraryDependencies ~= { _ .map(_.exclude("org.slf4j", "slf4j-log4j12")) }

// récupération du nom de l'application et de la version depuis le .conf


lazy val commonSettings = Seq(
name := conf.getString("application.name"),
version := conf.getString("application.version") ,
scalaVersion := "2.12.4" ,
scalacOptions ++= Seq("- unchecked", "-feature", "-deprecation"),
javacOptions += "-Xlint:unchecked"
)

// monte l'application avec le plugin Java, les fichiers dans le projet


lazy val main = (project in file("."))
.enablePlugins(PlayJava)
.settings(commonSettings: _*)

// ... la suite éventuellement à voir directement sur :


// https://github.com/emfinfo/conseillers/blob/master/build.sbt

Remarquez que les nom et version de l’application sont récupérés depuis le fichier « applica-
tion.conf » pour être utilisés ici. Remarquez la définition d’un « resolver » pour récupérer cor-
rectement le jar du projet « conseillers-models » depuis le dépôt local « Maven ».

JCS / EM F Page 27 sur 65


Play! framework – version 2 - Expériences diverses

4.4 Fichier « route »


Le fichier « route » fait la liaison entre l’URL d’une requête (service web de type REST) et la mé-
thode Java correspondante d’un contrôleur. Voici un extrait :
# application
GET / controllers.ApplicationCtrl. index()
OPTIONS /*path controllers.ApplicationCtrl.checkPreFlight(path)
GET /version controllers.ApplicationCtrl.lireVersion()

# requêtes sur les "etats civils"


GET /etats-civils controllers.ConseillerCtrl.chargerEtatsCivils(fmt = "html")
GET /etats-civils.xml controllers.ConseillerCtrl.chargerEtatsCivils(f mt = "xml")
GET /etats-civils.json controllers.ConseillerCtrl.chargerEtatsCivils(fmt = "json")

# requêtes de session
GET /session/login/:user/:pwd controllers.LoginCtrl.login( user :String, pwd :String)
GET /session/logout controllers.LoginCtrl.logout
GET /session/status controllers.LoginCtrl.status
GET /createLogin controllers.Assets.at(path="/public", file="/info.html")
POST /createLogin controllers.LoginCtrl.createLogin
GET /unauthorizedAccess controllers.LoginCtrl.unauthorizedAccess()

# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)

On peut remarquer que certaines « routes » possèdent un ou plusieurs paramètres, comme


« :user/:pwd » par exemple. Attention, l’appel à la méthode Java correspondante se fait à la
mode « Scala », c’est-à-dire avec le type du paramètre qui est spécifié après le nom du par a-
mètre (ex : « user :String »), donc le contraire de Java. Remarquez aussi que l’on peut direct e-
ment assigner des valeurs par défaut à certains paramètres (comme « fmt = "html" »).

4.5 Contrôleurs
Trois contrôleurs sont utilisés par l’application :
 ApplicationCtrl contiendra les méthodes utiles à la
gestion des services génériques de l’application
(comme « lireVersionServeur »).
 ConseillerCtrl contiendra les méthodes utiles à la ges-
tion des services sur les « conseillers nationaux ».
 LoginCtrl contiendra les méthodes utiles à la gestion
des services de « login ».

JCS / EM F Page 28 sur 65


Play! framework – version 2 - Expériences diverses

4.6 Configurer et utiliser une base de données MySQL


Un fichier « application.conf » permet de paramétrer tout l’environnement de Play, dont une ou
plusieurs bases de données MySQL (ou autre). La base de données par défaut est définie par la clé
« db.default », mais nous pourrions en avoir une deuxième, par exemple avec la clé « db.tests ».

4.6.1 Fichiers de configuration


Dans le répertoire « conf », on retrouve les 2 fichiers essentiels qu’il
faut modifier pour une base de données :
 persistence.xml  fichier traditionnel de configuration de
la persistance JPA.
 application.conf  paramétrage générique de l’application
Play.

4.6.2 Fichier application.conf (vue partielle)


jpa.default="prodPU"
db.default.jndiName="DefaultDS"
db.default.driver="com.mysql.jdbc.Driver"

# local DB
db.default.url="mysql://root:emf@localhost:3306/parlement"

4.6.3 Fichier « persistence.xml »


<?xml version="1.0" encoding="UTF-8"?>
<persistence ... >
<persistence -unit name="prodPU" transaction- type ="RESOURCE_LOCAL">
<provider> org.eclipse.persistence.jpa.PersistenceProvider</provider>
<non-jta-data- source>DefaultDS </non-jta-data-source>
<class>models.Conseiller</class>
<class>models.Activite </class>
<class>models.Conseil</class>
<class>models.Groupe</class>
<class>models.EtatCivil</class>
<class>models.Canton</class>
<class>models.Parti</class>
<class>models.Login</class>
<shared-cache- mode>NONE</shared-cache- mode>
</persistence-unit>
</persistence>

JCS / EM F Page 29 sur 65


Play! framework – version 2 - Expériences diverses

4.7 Création d’une couche « métier » (DbWorker)


Dans notre application « conseillers » et pour la vue prévue, nous avons surtout besoin de charger
des petites listes (cantons, conseils, partis), ainsi que la liste des conseillers, éventuellement filtrée
par un ou plusieurs critères. Nous allons donc créer une couche « métier » dans un package
« workers ». On commence par une interface (API) :
public interface DbWorkerAPI {
Login rechercherLogin(String nom);
Login ajouterLogin(Login login);

List<EtatCivil> chargerEtatsCivils();
List<Canton> chargerCantons();
List<Parti> chargerPartis();
List<Conseil> chargerConseils();
List<Groupe> chargerGroupes();

List<Conseiller> chargerConseillers(
String canton, String conseil, String parti, boolean actif);

boolean bdOuverte();
boolean fermerBd ();
}

4.7.1 Classe « DbWorkerFactory » pour instancier un singleton de « DbWorker »


Le code peut être visionné en entier ici :
https://github.com/emfinfo/co nseillers/blob/master /app/workers/DbWorkerFactory.java
Son but est de fournir « l’entity-manager » JPA (l’objet « em » créé par Play) à la couche « dao », ceci
de manière totalement transparente pour chaque méthode de « DbWorker » :
...
if (okClass && jpa != null) {
if (!method.isAnnotationPresent(NoJpaTransaction.class )) {

// récupération de l'entity- manager de Play


EntityManager em = jpa.em();

// injection de l'entity -manager de Play dans la couche dao


dao.setEntityManager(em) ;
...

4.7.2 Classe « DbWorker »


L’implémentation utilise notre couche d’accès aux données « maison » (objet « dao ») à partir de son
interface « JpaDaoAPI » :
package workers;

import ch.emf.dao.JpaDao;
import ch.emf.dao.JpaDaoAPI;
import ch.emf.dao.filtering.Search2;
import java.util.A rrayList;
import java.util.List;
import models.Canton;
import models.Conseil;
import models.Conseiller;
import models.EtatCivil;
import models.Groupe;
import models.Login;
import models.Parti;

JCS / EM F Page 30 sur 65


Play! framework – version 2 - Expériences diverses

public class DbWorker implements DbWorkerAPI {


private final JpaDaoAPI dao;

public DbWorker(JpaDaoAPI dao) {


this.dao = dao;
}

public DbWorker(String pu) {


dao = new JpaDao();
dao.open(pu);
}

@Override
public Login rechercherLogin (String nom) {
Login login = dao.getSingleResult(Login.class, "nom", nom);
if (login != null) {
dao.detach(login);
}
return login;
}

@Override
public Login ajouterLogin(Login login) {
if (dao.create(login) == 1 ) {
dao.detach(login);
}
return login;
}

@Override
public List<EtatCivil> chargerEtatsCivils() {
List<EtatCivil> etatsCivils = dao.getList(EtatCivil.class, "abrev" );
etatsCivils.add(new EtatCivil( "tous", "Tous les états civils"));
return etatsCivils;
}

@Override
public List<Canton> chargerCantons() {
List<Canton> cantons = dao .getList(Canton. class, "abrev");
cantons.add(new Canton("CH", "Suisse"));
return cantons;
}

@Override
public List<Parti> chargerPartis () {
List<Parti> partis = dao.getList(Parti.class, "abrev") ;
partis.add(new Parti("tous", "Tous les partis" ));
return partis;
}

@Override
public List<Conseil> chargerConseils () {
List<Conseil> conseils = dao.getList(Conseil.class , "abrev");
conseils.add(new Conseil("tous", "Tous les conseils"));
return conseils;
}

@Override
public List<Groupe> chargerGroupes() {
List<Groupe> groupes = dao .getList(Groupe. class, "abrev");

JCS / EM F Page 31 sur 65


Play! framework – version 2 - Expériences diverses

groupes.add(new Groupe("tous", "Tous les groupes"));


return groupes;
}

@Override
public List<Conseiller> chargerConseillers(
String canton, String conseil, String parti, boolean actif)
{
String jpql = "SELECT distinct c FROM Conseiller c "
+ "LEFT JOIN c.activites a WHERE a.conseiller=c";
Search2 search = new Search2(jpql);
if (!canton.isEmpty()) {
Canton ct = dao.getSingleResult(Canton.class , "abrev", canton);
search.addFilterEqual("c.canton" , ct);
}
if (!conseil.isEmpty()) {
Conseil co = dao .getSingleResult(Conseil.class, "abrev", conseil);
search.addFilterEqual("a.conseil", co);
}
if (!parti.isEmpty()) {
Parti pa = dao.getSingleResult(Parti.class, "abrev", parti);
search.addFilterEqual("c.parti", pa);
}
if (actif) {
search.addFilterEqual("c.actif", true);
search.addFilterIsNull("a.dateSortie");
}
search.addSortFields("c.nom", "c.prenom");
List<Conseiller> conseillers = dao .getList(search);
return conseillers;
}

@Override
public boolean bdOuverte () {
return dao .isOpen();
}

@Override
public boolean fermerBd() {
dao.close();
return !dao.isOpen();
}
}

JCS / EM F Page 32 sur 65


Play! framework – version 2 - Expériences diverses

4.8 Tests unitaires du worker avec Play


Les tests unitaires se font maintenant directement dans l’environnement de développement (par
exemple NetBeans) avec la librairie standard JUnit et non plus dans le navigateur comme dans la
version 1.x de Play. On peut tester de deux façons différentes :
 la première en fournissant au worker plutôt le nom de l’unité de persistance (grâce un fi-
chier « META-INF/persistence.xml » spécifique) et c’est la couche « dao » dans le worker
qui va créer un « entity-manager » local pour faire les tests ;
 la seconde en simulant une application Play entrain de tourner (en étendant la classe
« WithApplication ») et dont on récupère « l’entity-manager » par une injection de dé-
pendance de l’objet « JPAApi », ce qui est nouveau depuis Play 2.6.X.

4.8.1 Classes de tests


public class FunctionalTest1 {

@Test
public void test01_DbOpen() {
DbWorkerAPI dbWrk = new DbWorker("testPU") ;
boolean ok = dbWrk.bdOuverte();
Logger.warn(StackTracer.getCurrentClassMethod()
+ ">>> DB open = " + ok + " <<<" );
assertTrue(ok);
dbWrk.fermerBd();
}
}

public class FunctionalTest2 extends WithApplication {


private JPAApi jpa;
private DbWorkerAPI dbWrk;

@Override
public void startPlay() {
super.startPlay();
jpa = app.injector().instanceOf(JPAApi.class);
dbWrk = app.injector().instanceOf(DbWorkerFactory. class).getDbWorker();
}

@Override
public void stopPlay() {
super.stopPlay();
}

@Test
public void test02_DbOpen() {
String cur = StackTracer.getCurrentMethod();

// exécuter la requête avec une transaction JPA


jpa.withTransaction(() -> {
boolean ok = dbWrk.bdOuverte();
Logger.warn(cur + ">>> DB open = " + ok + " <<<" );
assertTrue(ok);
});
}
}

JCS / EM F Page 33 sur 65


Play! framework – version 2 - Expériences diverses

4.8.2 Fichier « M ETA-INF/persistence.xml » spécifique pour les tests


<?xml version="1.0" encoding="UTF-8"?>
<persistence ... >
<persistence -unit name="testPU" transaction- type ="RESOURCE_LOCAL">
<provider> org.eclipse.persistence.jpa.PersistenceProvider</provider>
<! --<non-jta-data- source>DefaultDS</non-jta-data-source>-- >
<class>models.Conseiller</class>
<class>models.Canton</class>
<class>models.Parti</class>
<class>models.Activite </class>
<class>models.Conseil</class>
<class>models.Groupe</class>
<class>models.EtatCivil</class>
<class>models.Login</class>
<shared-cache- mode>NONE</shared-cache- mode>
<properties>
<property name="javax.persistence.jdbc.url"
value= "jdbc:mysql://localhost:3306/parlement"/ >
<property name="javax.persistence.jdbc.driver"
value= "com.mysql.jdbc.Driver"/ >
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="emf" />
<property name="eclipselink.logging.level" value ="OFF" / >
</properties>
</persistence-unit>
</persistence>

4.9 Création d’un « contrôleur » Play pour les conseillers


Le but du contrôleur est d’offrir les méthodes qui renverront des données dans l’un des formats
HTML, JSON ou XML. Voici un extrait de cette classe, car toutes les méthodes se ressemblent :
public class ConseillerCtrl extends Controller {
private DbWorkerAPI dbWrk;

@Inject
public ConseillerCtrl(DbWorkerFactory factory) {
this.dbWrk = factory.getDbWorker() ;
}

private Result logError(Exception e) {


String msg = "Probleme avec la BD : \n " + e.getMessage();
Logger.error(msg);
Result httpResult = badRequest(msg);
return httpResult;
}

@Transactional
@With(BeforeAfterAction. class)
public Result chargerCantons(String fmt) {
Result httpResult;

// on récupère la liste des cantons


List<Canton> cantons = dbWrk.chargerCantons();

// on fait le rendu en xml, json ou html


try {
switch (fmt.toUpperCase()) {
case "XML" :
httpResult = ok(views.xml.cantons.render(cantons))
.as("application/xml") ;

JCS / EM F Page 34 sur 65


Play! framework – version 2 - Expériences diverses

break;
case "JSON":
httpResult = Utils.toJson(cantons);
break;
default:
httpResult = ok(views.html.cantons.render(cantons));
break;
}
} catch (Exception e) {
httpResult = logError(e);
}
return httpResult;
}
...

Remarques importantes :
 Remarquez l’instruction « Inject » (en rouge). Elle permet « l’injection de dépendance »
d’un objet depuis la version 2.6.X de Play. Ce patron de conception permet :
 d’injecter du code compilé au dernier moment, c’est-à-dire lorsqu’on en a vraiment be-
soin, sans dépendance immédiate sur une librairie ;
 de charger éventuellement une implémentation différente d’un composant ;
 de mieux tester notre propre code, car il n’y a plus d’état qui serait lié à une librairie par-
ticulière.
Toute une documentation est fournie à ce sujet sur le site du framework :
https://www.playframework.com/documentation/2.6.x/Jav aDependencyInjection
 Si une annotation « @Transactional » n’était pas présente avant une méthode du contrôleur
où JPA est utilisé, une erreur sera levée, car aucun « entity manager » ne sera alors fourni.

4.10 Utilisation des méthodes du contrôleur


Les méthodes « chargerCantons » ou « chargerConseillers » dans le contrôleur peuvent être con-
sommés par une requête HTTP directement depuis un navigateur Web, par une application Flex ou
encore par une requête AJAX dans une page HTML. Exemples depuis un navigateur :

4.10.1 Cantons avec rendu HTML (défaut)

JCS / EM F Page 35 sur 65


Play! framework – version 2 - Expériences diverses

4.10.2 Cantons avec rendu XML

4.10.3 Cantons avec rendu JSON

4.10.4 Conseillers avec rendu HTML (défaut)


Par défaut, un rendu HTML sera effec-
tué, car aucune information sur le
format n’a été précisée. Pour un rendu
HTML ou XML, un modèle de vue
« scala template » doit être préparé.

JCS / EM F Page 36 sur 65


Play! framework – version 2 - Expériences diverses

4.10.5 Conseillers avec rendu XML

4.10.6 Conseillers avec rendu JSON

JCS / EM F Page 37 sur 65


Play! framework – version 2 - Expériences diverses

4.11 Modèles pour les vues HTML et XML


Pour les vues HTML et XML présentées dans les pages précédentes, des fichiers « modèles » (tem-
plates) ont été créés. Ils utilisent la librairie « Twirl » de Play framework et sont basés sur le langage
Scala. La documentation se trouve ici :
https://www.playframework.com/documentation/2.6.x/ScalaTemplates

4.11.1 conseillers.scala.htm l
@(canton: String, conseillers: List[models.Conseiller])
<style>
h1, li { color:green}
</style>
<h1>Liste des conseillers (@canton)</h1>
<ol>
@for (conseiller <- conseillers) {
<li>
@conseiller.getNom()
@conseiller.getPrenom()
@conseiller.getCanton().getAbrev()
(@if(conse iller.getDateNaissance()==null) {
?
} else {
@conseiller.getDateNaissance().format("yyyy")
}
@if( conseiller.getDateDeces()!=null) {
- @conseiller.getDateDeces().format("yyyy")
}
), @conseiller.getParti().getAbrev()
<ul>
@for (activite <- conseiller.getActivites()) {
<li style="color:gray">
@activite.getConseil().getAbrev(),
@if(activite.getDateEntree()==null) {
?
} else {
@activite.getDateEntree().format("yyyy")
}
-
@if(activite.getDateSortie()==null) {
...
} else {
@activite.getDateSortie().format("yyyy")
}
@if(activite.getGroupe().getAbrev()!="?") {
, @activite.getGroupe().getNom()
}
</li>}
</ul>
</li>}
</ol>

Utilisation Java :
httpResult = ok(views.html.conseillers.render(canton, conseillers));

JCS / EM F Page 38 sur 65


Play! framework – version 2 - Expériences diverses

4.11.2 conseillers.scala.xm l (extrait)


@(conseillers: List[models.Conseiller])
<conseillers>@ for(conseiller < - conseillers) {
<conseiller>
<id>@conseiller.getPkConseiller()</id>
<nom>@conseiller.getNom()</nom>
<prenom>@conseiller.getPrenom()</prenom>
<sexe> @conseiller.getSexe()</sexe>
<etatcivil>@conseiller.getEtatCivil().getAbrev </etatcivil>
@if(conseiller.getCitoyennete==null) {
<citoyennete>null</citoyennete>
} else {
<citoyennete>@conseiller.getCitoyennete()</citoyennete>
}
@if(conseiller.getLieuNaissance==null) {
<lieunaissance>null</lieunaissance>
} else {
<lieunaissance>@conseiller.getLieuNaissance()</lieunaissance>
}
@if(conseiller.getDateNaissance==null) {
<dateNaissance>null</dateNaissance>
} else {
<dateNaissance>
@conseiller.getDateNaissance.format("yyyy- MM-dd")
</dateNaissance>
}
@if(conseiller.getDateDeces==null) {
<dateDeces>null</dateDeces>
} else {
<dateDeces>@conseiller.getDateDeces().format("yyyy-M M-dd")</dateDeces>
}
<canton>
<id> @conseiller.getCanton().getPkCanton()</id>
<abrev>@conseiller.getCanton().getAbrev()</abrev>
<nom >@conseiller.getCanton().getNom()</nom>
</canton>
<parti>
<id> @conseiller.getParti().getPkParti()</id>
<abrev>@conseiller.getParti().getAbrev() </abrev>
<nom>@conseiller.getParti().getAbrev()</nom>
</parti>
<activites>@for(activite < - conseiller.getActivites()) {
<activite>
<id>@activite.getPkActivite()</id>
<conseil>
<id> @activite.getConseil().getPkConseil()</id>
<abrev>@activite.getConseil().getAbrev() </abrev>
<nom>@activite.getConseil().getAbrev()</nom>
</conseil>
</groupe>}
</activite>}
</activites>
</conseiller>}
</conseillers>

Utilisation Java :
httpResult = ok(views.xml.conseillers.render(conseillers)).as("application/xml");

JCS / EM F Page 39 sur 65


Play! framework – version 2 - Expériences diverses

4.11.3 cantons.scala.htm l
@(cantons: List[models.Canton])
<style>
h1, li { color:green}
li {font -family: monospace; list -style-type: none;}
</style>
<h1>Liste des cantons</h1>
<ul>
@for (canton <- cantons) {
<li>@canton.getAbrev(3) @canton.getNom()</li>
}
</ul>

Utilisation Java :
httpResult = ok(views.html.cantons.render(cantons));

4.11.4 cantons.scala.xm l

@(cantons: List[models.Canton])
<cantons>
@for (canton <- cantons) {
<canton>
<id> @canton.getPkCanton()</id>
<abrev>@canton.getAbrev()</abrev>
<nom>@canton.getNom()</nom>
</canton>
}
</cantons>

Utilisation Java :
httpResult = ok(views.xml.cantons.render(cantons)).as("application/xml");

JCS / EM F Page 40 sur 65


Play! framework – version 2 - Expériences diverses

5 Création et lancement d’une version de production


Le mode production a beaucoup changé au fil des versions de Play framework. On trouve la docu-
mentation actuelle sur cette problématique ici :
https://www.playframework.com/documentation/2.6.x/Productio n

Le mode « production » est passablement plus rapide que le mode « développement », car Play
n’a pas besoin de tester (avec SBT) si un fichier a été modifié à chaque requête web.

5.1 Préparer une version de production avec « stage »


Depuis la version 2.2.x, on peut préparer une version « staged » d’une application, c’est-à-dire une
version qui peut être « mise en scène » immédiatement. Elle contient toutes les librairies nécessaires
pour faire tourner l’application sur un serveur.

On peut aussi utiliser successivement clean, compile, stage pour disposer d’une version « staged »
toute neuve. SBT effectuera les opérations suivantes :
 effacer d’abord le contenu du dossier « target » ;
 recompiler l’application de zéro ;
 préparer une nouvelle distribution « staged ».
L’application est alors préparée dans le sous-dossier « target/universal/stage » du dossier du
projet en cours. Deux scripts sont préparés pour lancer l’application dans le sous-dossier « bin » de
« stage » : un pour Windows : [nom_app].bat et l’autre pour le monde Unix : [nom_app].
À noter que dans l’exemple ci-contre (logiciel « gabiplay2 »), nous avons
rajouté manuellement un dossier « docmosis » qui contient dans « in »
toutes sortes de modèles de document Word utiles à l’application pour
générer des PDF avec la librairie Java « docmosis ». Le dossier « out » ré-
ceptionnera tous les PDF générés et fournis à l’utilisateur.

A noter que le dossier « public » d’une application « Play frame-


work 2 » est compilé dans un fichier spécifique du genre
« lib/gabiplay2.gabiplay2-2.xx-assets.jar ». Il s’agit par exemple de la
vue « index.html » et d’autres fichiers typiques d’une application
cliente. Attention donc de ne pas vouloir y mettre des fichiers dy-
namiques comme des PDF créés sur le serveur (fichiers docmosis
par exemple).

JCS / EM F Page 41 sur 65


Play! framework – version 2 - Expériences diverses

5.2 Lancement de la version « staged » sur MacOS


On utilise l’un des 2 scripts préparés avec « stage » en spécifiant le numéro du port à utiliser :

Il est souvent préférable que l’application serveur soit lancée depuis l’intérieur du dossier
« stage » qui représente la racine de l’application serveur, car cette application accède certai-
nement à des dossiers relativement à cette racine.

Par exemple, pour le logiciel « gabiplay2 », celui-ci utilise des fichiers dans le dossier « docmosis/in »
à la racine du serveur. Il faut donc le lancer comme suit en production (ici sur le port 80 d’un sys-
tème d’exploitation MacOS) :

5.3 Arrêt d’une application en production


Pour arrêter une application, il faut faire Ctrl+c (et non Ctrl+d) et l’application est normalement
immédiatement supprimée de la mémoire. Pour vérifier l’utilisation d’un port (par exemple 9002),
on utilise la commande « lsof » (List Open Files) :

Sur un système Linux (comme « raspbian » sur un Raspberry Pi2), cette commande n’existe pas. On
peut trouver le PID de l’application play avec la commande « ps » (processus) utilisée comme suit :

5.4 Installation sur un serveur Linux


Une application Play contenant déjà un serveur WEB, il n’est pas nécessaire d’installer un server
Apache ou un serveur de JSP comme Tomcat. Il suffit que Java soit installée.
Au lieu de préparer une distribution avec « stage », on peut la préparer avec « dist » avec comme
seul avantage le fait que la version de l’application fasse partie du nom de l’application compressée
(zip). Exemple :

JCS / EM F Page 42 sur 65


Play! framework – version 2 - Expériences diverses

Une distribution zippée peut ensuite être copiée sur une clé USB (dans l’exemple ci-dessous « hello-
play-java-1.01 ») et être décompressée dans une zone de son choix sur un serveur quelconque, par
exemple dans un dossier « PlayApps » sur un Raspberry Pi.

5.4.1 Script de param étrage


On peut aussi remarquer dans l’illustration ci-dessus qu’un fichier de script « run-hello » a été créé
dans « PlayApps » pour faciliter le paramétrage de l’application. On fait ceci avec un éditeur de texte
quelconque. Voici le contenu :
# Script pour lancer l'application hello -play-java avec des paramètres pour
la JVM
#nohup /home/pi/PlayApps/hello-play-java-1.01/bin/hello-play-java -J-server
-J-Xms32M -J-Xmx64M -Dhttp.port=9002 &
# ne fonctionne pas avec Play 2.4.x : -Dconfig.resource=app-prod.conf
# fonctionne avec Play 2.4.x: -Dconfig.file=/home/pi/PlayApps/
hello-play-java-1.01/conf/app-prod.conf
/home/pi/PlayApps/hello-play-java-1.01/bin/hello-play-java -J-server -J-
Xms32M -J-Xmx64M -Dhttp.port=9002 &

Pour qu’un tel script fonctionne, il faut encore modifier ses caractéristiques de sécurité (+x pour
pouvoir lancer le script) en faisant :
sudo chmod +x run- hello

5.4.2 Lancem ent du script au dém arrage de l’OS


Ce script permet également un lancement automatique au démarrage de l’OS. Le contenu du lien ci-
dessous résume bien comment on peut le faire sur Linux et ici en particulier sous « raspbian » :
http://raspberrywebserver.com/s erveradmin/run-a-script-on-start-up.html

JCS / EM F Page 43 sur 65


Play! framework – version 2 - Expériences diverses

On peut donc le faire avec la commande « crontab » comme indiquer dans l’article :

et voici l’ajout de la dernière ligne avec « @reboot » :

On sauve avec Ctrl+o, Entrée, puis on quitte avec Ctrl+x.


Puis on reboote la machine et on peut tester si l’application « play » fonctionne dès le démarrage en
lançant un navigateur sur le serveur :

… ou sur un client :

JCS / EM F Page 44 sur 65


Play! framework – version 2 - Expériences diverses

6 Migration d’applications
Les différentes versions de Play Framework ont toutes amené leur lot de modifications à apporter
aux applications établies. Il faut généralement bien lire la documentation mise à notre disposition
pour une migration.
https://www.playframework.com/documentation/2.6.x/Migr ation26
Il est parfois intéressant de faire un résumé des nouveautés d’une release, par exemple dans un
mindmap :

A part les modifications nécessaires parfois dans les sources, rap-


pelons les 3 fichiers qui devront certainement être ajustés :
 project/build.properties définit la version de sbt à utili-
ser pour monter un projet avec la dernière version.
 project/plugins.sbt définit tous les plugins nécessaires au
développement d’une application. Cela englobe la version
de « Play framework » à utiliser, mais aussi si on veut tra-
vailler avec Eclipse comme IDE (même plugin nécessaire
pour NetBeans).
 build.sbt définit toutes sortes de caractéristiques du projet
et surtout les dépendances à des librairies externes
On peut contrôler si une dépendance possède une nouvelle version sur le site suivant :
http://mvnrepository.com/
Les librairies Play sont automatiquement téléchargées depuis :
http://repo.typesafe.co m/types afe/r eleases

JCS / EM F Page 45 sur 65


Play! framework – version 2 - Expériences diverses

7 La gestion de la session et de la sécurité


La session est gérée dans Play sous la forme d’un cookie PLAY_SESSION qui est envoyé à chaque
requête et réponse HTTP si ce cookie contient quelque chose. C’est à l’utilisateur du framework
Play de gérer un contrôleur « LoginCtrl » avec une méthode « login » qui écrit quelque chose dans la
session et une méthode « logout » qui nettoie son contenu. C’est encore mieux de déléguer la ges-
tion de la session dans une classe séparée « SessionManager ».

7.1 Exemple de classe « LoginCtrl »


public class LoginCtrl extends Controller {
private DbWorkerAPI dbWrk;

@Inject
public LoginCtrl(DbWorkerFactory factory) {
this.dbWrk = factory.getDbWorker();
}

public Result index() {


return ok(index.render("Vous devez vous loguer !" ));
}

public Result unauthorizedAccess() {


return ok(index.render("Accès non autorisé !"));
}

@With(BeforeAfterAction.class)
@Transactional
public Result login(String nom, String motDePasse) {

// on exécute la requête demandée (recherche de l'utilisateur)


Login login = wrk.rechercherLogin(nom);

// on essaye d'ouvrir la session


SessionManager.clear();
if (!SessionManager.create(nom, motDePasse, "", login)) {
login = new Login();
}

// on retourne le résultat
return Utils.toJson(login);
}

@With(BeforeAfterAction.class)
public Result logout() {
SessionManager.clear();
return Utils.toJson("ok", !SessionManager.isOpen());
}

@With(BeforeAfterAction.class)
public Result status() {
return Utils.toJson("open", SessionManager.isOpen());
}

@With(BeforeAfterAction.class)
@Transactional
public Result createLogin() {
boolean ok = false;

// on stocke dans un objet Login


Login login = Utils.toObject(request(), new TypeReference<Login>(){});
login.setPkLogin(1);

// si le loginName n'existe pas, on sauve dans la DB


Login unLogin = wrk.rechercherLogin(login.getNom());

JCS / EM F Page 46 sur 65


Play! framework – version 2 - Expériences diverses

if (unLogin == null) {
unLogin = wrk.ajouterLogin(login);
ok = unLogin != null && unLogin.getPkLogin() > 0;
}
BooleanResult booleanResult = new BooleanResult(ok, login.getNom());
return Utils.toJson(booleanResult);
}
}

7.2 Exemple de classe « SessionManager »


public class SessionManager {
public final static String SESSION_USER_ID = "user-id";
public final static String SESSION_DB_ID = "db-id";
public final static String SESSION_LANG = "fr";
public final static String SESSION_TIMESTAMP = "timestamp";

/**
* Méthode de création d'une session par a uthentification de l'utilisateur
*
* @return true si l'utilisateur est reconnu
*/
@SuppressWarnings("null")
public static boolean create(
String userName, String pwd, String domain, Login login) {
boolean ok = false;
if (session().get(SESSION_USER_ID) == null) {

// teste si l'utilisateur a été trouvé auparavant


if (login != null) {

// si password dans la table ...


if (login.getMotDePasse() != null) {
ok = pwd != null && login.getMotDePasse().equals(pwd);
} else { // autrement on teste avec AD
// ok = authenticateOnActiveDirectory(userName, pwd, domain);
ok = false;
}
}

// enregistrement de la session si identification correcte


if (ok) {
long start = System.currentTimeMillis();
session(SESSION_USER_ID, "" + login.getPkLogin());
session(SESSION_DB_ID, "1");
session(SESSION_TIMESTAMP, "" + start);
} else {
session().clear();
}
}
return ok;
}

/**
* Efface le contenu de la session en cours.
*
* @return true si la session est maintenant vide
*/
public static boolean clear() {
boolean ok = session().get(SESSION_USER_ID) != null;
if (ok) {
session().clear();
}
return !isOpen();
}

JCS / EM F Page 47 sur 65


Play! framework – version 2 - Expériences diverses

/**
* Teste si la session est ouverte.
*
* @return true si une session est ouverte
*/
public static boolean isOpen() {
boolean ok = session().get(SESSION_USER_ID) != null;
if (ok) {
session(SESSION_TIMESTAMP, "" + System.currentTimeMillis());
}
return ok;
}

/**
* Teste si un timeout de session doit intervenir.
*
* @param ms le temps en [ms] pour qu'un timeout intervienne
* @return true si la session peut être fermée
*/
public static boolean isTimeout(int ms) {
long cTime = System.currentTimeMillis();
long sTime = ConvertLib.stringToLong(session().get( SESSION_TIMESTAMP));
System.out.println("cTime: " + cTime + " sTime: " + sTime
+ " diff: " + (cTime-sTime));
return (cTime-sTime) >= ms;
}

/**
* Récupérer le "user-id" de l'utilisateur logué.
*
* @return l'identifiant de la personne loguée ou 0 si non trouvé
*/
public static int getUserId() {
String userId = session().get( SESSION_USER_ID);
return ConvertLib.stringToInt(userId);
}

/**
* Récupérer la langue de l'utilisateur logué.
*
* @return une langue sur 2 caractères (ex: "fr" pour le français)
*/
public static String getLang() {
String lang = session().get( SESSION_LANG);
if (lang == null || lang.isEmpty()) {
lang = "fr";
}
return lang;
}

/**
* Récupérer l'identifiant mémorisé de la BD.
* @return un identifiant de BD
*/
public static int getDbId() {
String dbId = session().get(SESSION_DB_ID);
return ConvertLib.stringToInt(dbId);
}
/**
* Mémoriser un identifiant de la BD.
* @param dbId un identifiant de BD
*/
public static void setDbId(int dbId) {
session(SESSION_DB_ID, "" + dbId);
}
}

JCS / EM F Page 48 sur 65


Play! framework – version 2 - Expériences diverses

7.3 Gestion de la sécurité


Pour avoir la permission d’exécuter telle ou telle méthode d’un contrôleur Play, ce dernier permet
d’annoter avec « @Security » une classe-contrôleur en entier (ou éventuellement une méthode en
particulier) :
@Security.Authenticated(MySecurityCtrl. class)
public class ApplicationCtrl extends Controller {

}

7.3.1 Classe « MySecurityCtrl »


La classe « SecuredCtrl » doit surcharger deux méthodes comme dans cet exemple :
public class MySecurityCtrl extends Security.Authenticator {
private final Config config;

@Inject
public MySecurityCtrl(Config config) {
this.config = config;
}

@Override
public String getUsername(Context ctx) {
return ctx.session().get(SessionManager.SESSION_USER_ID);
}

@Override
public Result onUnauthorized(Context ctx) {
return ok("?");
}

 La première méthode essaye de lire une propriété de « username » dans la session et ren-
voie l’identifiant de la personne loguée si elle s’y trouve.
 La deuxième méthode indique quoi faire pour les personnes non autorisées : elles seront
redirigées vers la page « index.html » de démarrage de l’application.

JCS / EM F Page 49 sur 65


Play! framework – version 2 - Expériences diverses

8 Publication d’une version de production sur Heroku


Le but de ce chapitre est de montrer comment installer une application Play sur la plateforme He-
roku (https://www.heroku.com/). Nous essayerons donc avec l’application « conseillers » déjà dé-
crite dans ce document au chapitre 4 - Projet Play « conseillers ».
Heroku est l’une des rares plateformes à supporter des applications Play (Java ou Scala) sur ces
serveurs. On peut donc y créer un compte et même installer une application gratuitement. Tout e-
fois, les accès seront alors très limités. Nous avons opté d’abord pour cette solution gratuite avant
de passer à la version « Hobby » (7$ par dyno/mois) quand même un peu plus rapide. On peut pas-
ser d’une version à l’autre à tout moment.

8.1 Information essentielles


Voici quelques informations essentielles à connaître et tirées du site :
https://fr.wikipedia.org/wiki/Heroku :
L'architecture Heroku est centrée sur les processus, et non sur des machines virtuelles ou des ser-
veurs. Il s'agit donc d'un PaaS (plate-forme en tant que service). Le code déployé est analysé pour
détecter le type d'application à faire tourner, ainsi que les processus nécessaires. L'analyse dé-
clenche la mise en place d'un environnement dédié, adossé à une ou des unités d'exécution appe-

JCS / EM F Page 50 sur 65


Play! framework – version 2 - Expériences diverses

lées dynos, lesquelles font tourner les processus requis pour faire fonctionner l'application dé-
ployée.
Un dyno contient un certain nombre de processus : typiquement, un ou plusieurs processus dits «
web » (par ex. des instances d'un serveur web, pour l'exécution des requêtes routées vers l'applica-
tion déployée), un ou plusieurs worker (pour des opérations de maintenances ou de diagnostics),
ainsi qu'un nombre arbitraire de processus système.
Exemple de l’application « conseillers » sur les dernières 24h

On voit que l’application « conseillers » n’utilise que très peu de ressources en moyenne, mais
qu’elle en utilise quand même, même si aucun appel web n’est fait dessus à trois heures du matin.
L'ensemble des dynos requis pour faire tourner une application Heroku forment un slug, une unité
matérielle logique qui adresse son propre système de fichier transitoire, possède une certaine ca-
pacité en RAM exclusive, etc. Le slug peut être étendue horizontalement à l'infini (scalable) en mo-
difiant la physionomie des dynos. Le déploiement de code sur un slug ou la modification de va-
riables d'environnements occasionnent une « recompilation » du slug, de sorte que le ou les dynos
ainsi que le matériel qui le compose sont réadaptés aux nouvelles conditions d'exécution de l'appli-
cation.
L'utilisateur peut enfin, à tout moment, monter en puissance manuellement toute ou partie des
composants d'un slug, c'est-à-dire modifier le nombre de processus de chaque type intervenant
dans l'exécution. De nombreuses extensions sont par ailleurs activables pour augmenter les capaci-
tés de l'environnement (mise en place de bases de données spécifiques, monitoring, etc.).
Le modèle économique d'Heroku repose sur un adressage fin du temps-horloge dévolu aux dynos
(web et worker) d'un slug. Certains dynos sont factur és à l'heure, tandis que d'autres le sont à la
seconde près.

8.2 Connexion au compte


Une fois son compte créé et son modèle économique choisi, on peut se connecter à son compte à
tout moment et gérer son ou ses applications depuis Internet :

JCS / EM F Page 51 sur 65


Play! framework – version 2 - Expériences diverses

Par exemple, on voit ici l’application existante « conseillers » :

Mais on peut aussi en créer une nouvelle facilement, exemple :

Et l’application est tout de suite utilisable :

JCS / EM F Page 52 sur 65


Play! framework – version 2 - Expériences diverses

8.3 Installation de l’outil Toolbelt


Afin de pouvoir gérer par des commandes notre compte Heroku (par exemple créer ou renommer
des applications), il faut télécharger un outil de type CLI (Commande Line Interface) appelé « Hero-
ku Toolbelt ». On peut le faire depuis ici : https://devcenter.heroku.com/articles/heroku-command-
line.
Après installation, on doit obtenir normalement ceci :

On peut vérifier que l’outil fonctionne avec la commande suivante :

Remarque : depuis la version 2.32.0, cet outil se met à jour automatiquement


On peut vérifier où a été mémorisé l’outil :

Ou obtenir encore de l’aide avec :

JCS / EM F Page 53 sur 65


Play! framework – version 2 - Expériences diverses

8.4 Utiliser l’outil Toolbelt


8.4.1 Connexion à son com pte Heroku

8.4.2 Créer une nouvelle application sur Heroku


Les noms d’applications étant gérés de manière globale sur les serveurs Heroku, un nom
d’application simple comme « example » sera refusé :

ou
Une application tournera certainement un peu plus vite si elle se trouve plus près de nous. Pour
cela Heroku permet de préciser la région lors de la création d’une application. Les régions dispo-
nibles peuvent être connues par la commande :

Normalement, on utilise quasi toujours la commande « create » en conjonction avec un dépôt « git »
(gestionnaire de versions) initialisé. Exemple fonctionnel cette fois-ci en spécifiant encore la région
« Europe » :

JCS / EM F Page 54 sur 65


Play! framework – version 2 - Expériences diverses

8.4.3 Vérifier la création de l’application sur Heroku


On peut vérifier les informations de base de notre application sur Heroku :

Si la commande « heroku » permet de gérer notre compte Heroku depuis le « terminal » de MacOS
ou la « console » de Windows, c’est l’utilitaire « git » qui va nous permettre de copier une applica-
tion Play depuis notre disque dur vers le dépôt correspondant sur le serveur Heroku. On peut donc
aussi vérifier qu’un dépôt « git » distant (remote) ait bien été créé sur Heroku pour synchroniser
notre projet :

8.4.4 Renom m er une application

8.4.5 Migration d’une région à l’autre


Notez qu’une application existante peut être migré d’une région à l’autre. Voir le détail ici :
https://devcenter.heroku.co m/articles/app-migratio n. Exemple :

Notez ici qu’une base de données locale « CLEARDB » (compatible MySQL sur Heroku) a aussi
été migrée. L’application « conseillers » ne l’utilise pas forcément. Cela est configurable dans le
fichier « application.conf ».

JCS / EM F Page 55 sur 65


Play! framework – version 2 - Expériences diverses

8.4.6 Supprim er une application


La commande ci-dessous fait le job :

Mais on préférera, pour plus de sécurité, d’effectuer une suppression d’application depuis son
compte sur le site Internet https://id.heroku.com/login :
 sélectionner l’application à supprimer depuis le « dashboard » de votre compte Heroku ;
 cliquer sur l’onglet « Settings » ;
 tout en bas de la fenêtre, cliquer sur le bouton « Delete app… » ;
 réintroduire le nom de l’application à supprimer ;
 valider la suppression avec le bouton « Delete app » ;

8.5 Déploiement d’une application Play existante


Nous allons voir comment déployer l’application « conseillers » qui est, on le rappelle, une applica-
tion Play 2.6.X en Java utilisant une base de données MySQL. Les opérations pour faire ce travail
sont décrites en détails dans le lien suivant :
https://devcenter.heroku.co m/articles/getting-st arted-with-sc ala# introductio n.
Ce lien résume quelque peu le processus :
https://blog.knoldus.com/2012/04/02/deploying-the-play-2-0- applicat ion-on-heroku/
Dans les sous-chapitres qui suivent, voici réellement ce que nous avons fait avec les conditions de
départ suivantes :
 application « conseillers» vide déjà créée sur Heroku ;
 login déjà effectué avec la commande Toolbelt : « heroku login »

8.5.1 Créer un fichier « Procfile »


Le fichier « Procfile » à la racine du projet est un fichier « texte » à créer. Il contiendra le où les pro-
cessus que le serveur Heroku devra lancer pour démarrer notre application. Voici celui créé :

 « web : » définit le type de processus Heroku (web est le défaut)


 « target/universal/stage/bin/conseillers » est la commande qui lance notre application (no-
tez que cela doit être « conseillers.bat » sous Windows) ;
 « ${PORT} » est une variable dont la valeur est choisie automatiquement par Heroku (ne pas
s’en soucier)

JCS / EM F Page 56 sur 65


Play! framework – version 2 - Expériences diverses

8.5.2 Vérifier que le nom bre de dynos est au m oins 1

8.5.3 Com piler une version « stage » de l’application

8.5.4 Tester l’application localem ent


Cette opération permet de vérifier que notre application fonctionne réellement, comme elle fon c-
tionnera plus tard sur le serveur.

Lancez un navigateur avec l’adresse : http://localhost:5000/ et testez quelques URL …

8.5.5 Enregistrer les m odifications du projet avec « git »


Un fichier « .gitignore » a été créé pour éviter que certains fichiers ou dossiers ne soient copiés inu-
tilement. Voici son contenu :

Ensuite, on effectue un commit des modifications apportées :

JCS / EM F Page 57 sur 65


Play! framework – version 2 - Expériences diverses

8.5.6 Déployer la nouvelle release avec « git »


Cette opération peut être assez longue, car tout le projet est reconstitué.

Remarquez aussi la détection du projet comme un projet Play 2.x :

8.5.7 Ouvrir la nouvelle application sur le w eb

JCS / EM F Page 58 sur 65


Play! framework – version 2 - Expériences diverses

8.5.8 Afficher le fichier de log en tem ps réel


Voir des informations détaillées ici : https://devcenter.heroku.co m/articles /lo ggin g

Faire ctrl+c pour arrêter le flux de données en direct.

8.6 Add-ons
Les add-ons sont des modules logiciels que l’on peut ajouter à l’environnement d’une application.
On en trouve une liste officielle ici : https://elements.heroku.com/

On peut contrôler les « add-ons » installés pour une application :

 aucun add-on par défaut

8.6.1 Base de données


Même si l’application « conseillers » utilise en finalité une base de données installée sur « jcs-
tritt.emf-informatique.ch:3306/strittjc_parlement », on pourrait tout-a-fait en faire une copie dans
une base de données locale de type « ClearDB MySQL ».
Mais avant d’ajouter un add-on (dont certains sont parfois payants), Heroku veut vérifier d’abord
que vous ayez enregistré une carte de crédit valide avec : https://heroku.com/verify.

JCS / EM F Page 59 sur 65


Play! framework – version 2 - Expériences diverses

a) Création de la base de données


Heureusement, « ClearDB MYSQL » est gratuit dans sa version « ignite » la plus simple (5 Mo, 10
connexions max). On peut donc ajouter l’add-on en question par le « dashboard » sur le web :

ou par l’outil Toolbelt :

On peut vérifier l’ajout :

b) Variables d’environnem ent


L’ajout d’un add-on ajoute généralement également une ou plusieurs variables d’environnement
qui sont exploitables ensuite dans le logiciel lui-même (ici c’est CLEARDB_DATABASE_URL) .

ou

c) Utilisation de la base de données


Le lien ci-dessous explique comment utiliser cette variable en :
 JDBC
 XML pour Spring
 JAVA avec Spring
https://devcenter.heroku.co m/articles/cleardb#using-cleardb-in- a-play-framework-app

JCS / EM F Page 60 sur 65


Play! framework – version 2 - Expériences diverses

Donc, pas d’exemple pour JPA et son fichier « persistence.xml », mais pour Play 2.x, il suffit de confi-
gurer l’URL vers la DB dans le fichier « application.conf » :
# remote DB
db.default.url="mysql://b8cd17f9af7aa5:470a8fd7@eu -cdbr-west-
01.cleardb.com/heroku_34f61232b718404?reconnect=true"

d) Accès à la base de données par Workbench


Il faut créer une nouvelle connexion (par exemple « heroku »), puis configurer cette connexion
comme suit :

Ensuite, on peut tester la connexion par le bouton approprié :


ATTENTION, le port 3306 ne doit pas être blo-
qué par le routeur !

Ensuite, on peut remplir la base de données avec un script approprié :

JCS / EM F Page 61 sur 65


Play! framework – version 2 - Expériences diverses

Dans « MySQL Workbench », on importe avec l’option « Data Import/Restore » :

Une fois le fichier choisi, on clique sur le bouton « Start Import » pour obtenir :

On peut vérifier l’importation en cliquant sur le bouton à droite de « SCHEMAS » :

e) Test
On recompile avec :

On relance en mode test :

On relance un navigateur sur l’adresse http://localhost:5000

JCS / EM F Page 62 sur 65


Play! framework – version 2 - Expériences diverses

8.7 Optimisation du slug déployé


Le slug déployé peut ne pas être optimal et ne peut dépasser 300Mo après compression. Pour le
projet « conseillers », voilà ce qu’il me donne (après création du slug avec « git push heroku mas-
ter ») :

On peut fouiller dans le slug à distance avec la commande :

Typiquement, il recrée « models/target » qui n’est pas nécessaire et cela double quasiment la mé-
moire utilisée. J’ai donc suivi les conseils du lien suivant :
https://devcenter.heroku.co m/articles/r educing-the-slug-size-of-play-2-x-applicatio ns
Vérification de la version du plugin à ajouter :
https://bintray.com/heroku/sbt-plugins/sbt-heroku
Installation de ce plugin dans l’application conseillers sous « project/plugins.sbt » :
// pour faire des packages light sous heroku (20.12.2017)
// contrôler sur https://github.com/heroku/sbt -heroku
addSbtPlugin("com.heroku" % "sbt-heroku" % "2.1.1" )

Déploiement avec ce plugin (solution à privilégier) :

JCS / EM F Page 63 sur 65


Play! framework – version 2 - Expériences diverses

JCS / EM F Page 64 sur 65


Play! framework – version 2 - Expériences diverses

9 Conclusion
Dans ce document, nous avons voulu montrer combien il était facile de développer en Java des ap-
plications « serveur » avec le framework Play.
Plusieurs avantages de Play peuvent encore être mentionnés par rapport aux solutions convention-
nelles (J2EE, API servlet, Tomcat, Glassfish) :
 la compilation à la volée (on ne compile pas soi-même, play le fait pour nous) ;
 les erreurs avec n° de ligne sont directement affichées dans la page Web ;
 pas d’empaquetage compliqué à effectuer (pas de fichier « .war ») ;
 pas de déploiement long sur un serveur d’application (comme GlassFish) ;
 tests unitaires standard JUnit ou simulation réelle de services web ;
 injection de dépendances (depuis Play 2.4.x) ;
 pool de connexions pour gérer les accès à une base de données ;
 rapidité, adaptabilité, scalabilité !
On peut encore dire que Play est d’ores et déjà utilisé en production sur plusieurs sites.
L’application GABI de l’EMF a été complétement migrée avec succès sur ce framewor k installé
d’abord sur un serveur Linux, puis ensuite sur un serveur Windows. Incroyable aussi, on peut ins-
taller une application Play sur un serveur à 50 Frs (Raspberry Pi).
Play est définitivement intéressant, car …
 facile à installer ;
 facile à utiliser, car centré sur le métier (réel plaisir de développer) ;
 facile à tester ;
 facile à expliquer à des étudiants (courbe d’apprentissage très rapide) ;
 facile à déployer.

A VOUS DE « JOUER » MAINTENANT !

JCS / EM F Page 65 sur 65