Play, du Web Java simplement

lun. 25 juin 2012

« The Play framework makes it easier to build web applications with Java & Scala ». Ce slogan qui figure sur leur page d'accueil décrit l'objectif et la philosophie du Framework : la simplicité. Exit donc les architectures, certes solides, mais également très lourdes du monde JEE. Bonjour à la facilité d'accès pour ceux qui veulent construire des applications Web. Play est un peu l'équivalent Java d'un Django ou d'un Ruby On Rails.

Cet article se rapporte à la version 2 du Framework.

J'ai eu l'occasion de l'expérimenter un peu dans le cadre personnel, mais aussi professionnel. Je vous synthétise donc ici ce que je pense de cet outil. Vous saurez ainsi à quoi vous en tenir si vous décidez de l'utiliser. Je commencerai donc par présenter son fonctionnement, puis je compléterai par les avantages et défauts de la solution. Je n'ai pas prévu d'écrire un tutoriel complet, toutefois si le sujet vous intéresse, n'hésitez pas à me le dire. Si vous êtes suffisamment nombreux à solliciter une explication plus complète, je m'engagerai dans sa rédaction. Attention, cette présentation s'applique principalement à Play version Java. Je ne me suis pas intéressé à la version Scala, je ne connais donc pas les différences.

Principes de fonctionnements

Comprendre le fonctionnement de Play commence par comprendre les différentes briques logiciels utilisées et leurs enchaînements. Je vais, pour vous expliquer tout cela, m'appuyer sur un petit schéma très simple :

Retraçons donc le chemin d'une simple requête Web et voyons voir quelles couches elle traverse.

Le Serveur d'Application

Play fourni lui-même son propre serveur d'application. Il ne s'agit pas d'un Jetty ou d'un Tomcat, comme l'on rencontre habituellement pour les applications Java. L'applicatif Web est donc complètement indépendant et peut se lancer sans installation complémentaire. La seule exception est bien sur la base de données (sauf si vous utilisez une base HsqlDB).

Pratique quand on a seulement une application à déployer, mais vous pensez surement que la multiplication des applications va être difficile à gérer. Un plugin Play existe permettant la génération d'un WAR standard qui sera déployé sur un serveur d'application Java classique. Ainsi si jamais la solution proposée par défaut vous gêne, vous avez votre échappatoire.

Routes

Le serveur d'application récupère donc la requête, la parse, puis la transfère à un objet appelé Routes. Cette brique a pour objectif de transféré la requête et ses paramètres au reste de l'application. Il s'agit donc d'un mapping URL/Type d'opération -> Controller Java. Routes se configure via un fichier qui ressemble à ceci :

  # Home page
  GET     /                       controllers.Application.index()

  # Tasks
  GET     /tasks                  controllers.Application.tasks()
  POST    /tasks                  controllers.Application.newTask()
  POST    /tasks/:id/delete       controllers.Application.deleteTask(id: Long)

Chaque lignes représentent donc un mapping différent. Le premier mot indique le type d'opération (GET/POST/PUT...), le second mot indique l'url relié à la ligne, et enfin le dernier paramètre à donner indique le Controller mais aussi la fonction à appeler. Vous remarquerez dans la dernière ligne de l'exemple l'utilisation d'un paramètre id. Vous pouvez grâce à cet outil composez les urls de votre application comme vous le désirez.

Controller Play

Vous arrivez ensuite sur le Controller play et la fonction que vous avez indiquée dans routes. Le Controller est un objet Java simple héritant de l'objet mère "Controller". L'héritage vous permet d'accéder facilement à la session utilisateur, aux fonctions de rendu et de routing,... En dehors de ça, c'est un objet classique, enfin presque ! Les fonctions appelées directement par routes sont des fonctions statiques !

Ah, je sens quelques architectes se remuer sur leurs chaises. Ce choix pourrait paraitre étonnant mais est guidé par la volonté de simplicité. Ici pas de gestion de singleton mais uniquement des fonctions statiques. Avant d'être Thread-Safe, chaque fonction devra être donc totalement indépendante. Pas de données stockées sur l'objet. Ce choix permet à l'application d'être au final extrêmement scalable.

Un petit exemple de Controller :

  public class Application extends Controller {

          public static Result index() {
                  return ok(index.render("Your new application is ready."));
          }

  }

Templates

Reste à gérer le rendu, ce qui va être renvoyé à l'utilisateur. Pour créer le squelette graphique de l'application, Play utilise directement Scala. Ce choix peut paraitre atypique, certain aurait attendu un groovy ou un velocity que l'on retrouve fréquemment dans ce rôle. Toutefois il s'avère plutôt efficace ! Ce moteur de template nous permet de créer une myriade de petite fonction pour capitaliser son rendu. Il est au final très agréable à utiliser pour nous autres programmeurs. Un designer aura, par contre, surement un autre avis...

Un template peut donc ressembler à ceci :

  @(tasks: List[Task], taskForm: Form[Task])

  @import helper._

  @main("Todo list") {

      <h1>@tasks.size() task(s)</h1>
      <ul>
          @for(task <- tasks) {
              <li>
                  @task.label
                  @form(routes.Application.deleteTask(task.id)){
                      <input type="submit" value="Delete">
                  }
              </li>
          }
      </ul>
  }

Ebean

Grâces aux briques précédentes, vous pouvez avoir une application parfaitement fonctionnelle. Bien sur, elle risque de ne pas faire grand-chose si vous ne stockez pas de données ! Play utilise par défaut l'ORM Ebean. Cet outil permet de se connecter aux principaux moteurs de données (HsqlDb, Mysql, PostgreSQL ou même Oracle) et de mapper les tables à des objets Java. Ebean s'appuie sur des annotations JPA pour fonctionner (attention, ce n'est pas du pur JPA pour autant !).

Pour ceux qui préfèrent, vous pouvez configurer facilement Play pour utiliser un moteur JPA standard.

Un petit exemple d'entité :

  @Entity
  public class Task extends Model {

        @Id
        public Long id;

        @Required
        public String label;

        public static Finder<Long,Task> find = new Finder(
              Long.class, Task.class
        );

Outils diverses ?

Play fourni également un certain nombre d'outils pour faciliter le développement et l'utilisation de l'application. Notons par exemple la présence d'un gestionnaire de dépendance complet et d'un moteur de build. Tout est également fourni pour permettre une bonne intégration aux principaux IDE tels que Eclipse ou IntelliJ.

Et au final ?

Play est un produit abouti qui dénote beaucoup dans l'écosystème Java. Ces choix d'architecture sont dépaysant mais loin d'être idiots. Si j'avais une chose à reprocher à ce produit, c'est ne pas allez assez loin dans les fonctionnalités offertes par le framework. Ici pas d'administration auto-générées, ni de formulaire complètement généré depuis l'entité JPA. Nous sommes donc encore loin des fonctionnalités offertes par un Django. Toutefois pour les amateurs de "From Scratch", Play offre un très bon compromis étant donné qu'il laisse beaucoup de liberté dans l'implémentation.

Et vous, quels frameworks utilisez-vous pour le web, et pourquoi ?

Les plus !

  • Play fourni tout pour construire l'application de A à Z.
  • L'approche est simple et efficace
  • L'approche "coding" du template qui permet une très bonne capitalisation.
  • Sa modularité

Les moins !

  • Ebean offre quelques fonctionnalités intéressantes, mais est un peu moins puissant qu'un JPA standard.
  • L'éloignement des standards Java.
  • Quelques comportements qu'il faut vite apprendre pour éviter les pertes de temps.