Professional Documents
Culture Documents
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
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.
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.
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).
… 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
# 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.
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 » ;
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 ».
Il est intéressant de constater que cet outil utilise plusieurs variables d’environnement comme :
JAVA_HOME
JAVA_OPTS
SBT_OPTS
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.
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
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
@(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.*;
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).
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public ", file :Asset)
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 …
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 + " !");
}
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.
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
organization := "ch.emf.info"
version := "1.0-SNAPSHOT"
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.
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.3 JpaDaoAPI
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;
}
}
routesGenerator := InjectedRoutesGenerator
libraryDependencies += guice
// 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"
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 ».
# 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)
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 ».
# local DB
db.default.url="mysql://root:emf@localhost:3306/parlement"
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 ();
}
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;
@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");
@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();
}
}
@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();
}
}
@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();
@Inject
public ConseillerCtrl(DbWorkerFactory factory) {
this.dbWrk = factory.getDbWorker() ;
}
@Transactional
@With(BeforeAfterAction. class)
public Result chargerCantons(String fmt) {
Result httpResult;
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.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));
Utilisation Java :
httpResult = ok(views.xml.conseillers.render(conseillers)).as("application/xml");
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");
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.
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.
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) :
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 :
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.
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
On peut donc le faire avec la commande « crontab » comme indiquer dans l’article :
… ou sur un client :
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 :
@Inject
public LoginCtrl(DbWorkerFactory factory) {
this.dbWrk = factory.getDbWorker();
}
@With(BeforeAfterAction.class)
@Transactional
public Result login(String nom, String motDePasse) {
// 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;
if (unLogin == null) {
unLogin = wrk.ajouterLogin(login);
ok = unLogin != null && unLogin.getPkLogin() > 0;
}
BooleanResult booleanResult = new BooleanResult(ok, login.getNom());
return Utils.toJson(booleanResult);
}
}
/**
* 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) {
/**
* 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();
}
/**
* 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);
}
}
@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.
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.
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 » :
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 :
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 ».
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.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/
ou
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"
Une fois le fichier choisi, on clique sur le bouton « Start Import » pour obtenir :
e) Test
On recompile avec :
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" )
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.