Blog Arolla

Arrête de jouer, deviens un ninja !

Derrière ce titre a priori sans aucun sens se cache une présentation de mon dernier coup de cœur: un framework web full stack pour java.

Là je vous entends dire: tiens tiens...il veut nous parler de Play! celui là ?

Et bien non, je veux vous parler de ....

Ninja web framework

Projet open-source initié en 2012, ce framework prendrait son origine chez des utilisateurs de Play 1 qui auraient trouvé Play 2 trop "Scala". En résumé cela donne un framework s'inspirant de Rails et Play mais avec une stack 100% Java.

Il est depuis peu en version 3.0.0


Leur site web en a profité pour se faire une beauté, et compléter sa documentation.

Composition

Tout d'abord, votre projet ninja est un projet Maven de facto, vous ne devriez donc pas être perdus.

La création du projet se fait effectivement via un archétype maven et si on regarde la liste des fonctionnalités on retrouve des noms bien connus du monde java:

  • Html templating/rendering avec freemarker
  • Migration de base de donnée avec FlyWay
  • Injection de dépendance avec Guice
  • Validation avec la JSR 303 (Hibernate-validation)
  • Logs avec logback et slf4j
  • Sérialisation/dé-sérialisation (Json,Xml) avec Jackson
  • Mockito et FluentLenium

On profite de surcroît de tous les avantages de Maven (gestion de dépendance, build, etc.).

A savoir qu'il ne s'agit pas seulement d'un archétype, mais bien d'une stack complète.

En effet la couche Ninja apporte elle-même de nombreuses fonctionnalités sympathiques (i18n, scheduler, Filtres) mais surtout de quoi tester dans tous les sens:

  • NinjaRouterTest, pour tester ses routes
  • NinjaTest, pour tester sur une application déployée
  • NinjaDocTester, pour tester et documenter vos Api
  • Pour le reste vous avez à disposition Mockito

Modules

A l'instar de Play, on peut agrémenter sa stack ninja de modules. Pour l'instant la liste est courte, ce qui s'explique par la jeunesse du framework.

Plus concrètement

Différents archétypes sont à votre disposition:

  • ninja-core-demo-archetype, un showroom du framework: beaucoup de nettoyage à faire si vous l'utilisez comme base
  • ninja-jpa-demo-archetype, un projet d'exemple plus simple, comprenant juste un écran avec lien vers base de données (dao,migration): c'est celui que j'utilise pour créer mes projet
  • ninja-appengine-blog-archetype, un moteur de blog basique fonctionnant sur app-engine

Il existe d'autres archétypes (plusieurs variantes à chaque fois: blog,demo,core-demo) mais tous ne sont pas à jours avec la dernière version de ninja, il faudra donc quelques fois se référer à la page de mise à jour de version.

Nous allons intégrer un projet Ninja dans un projet Maven existant composé d'un pom parent et d'un module contenant les dtos (potentiellement partagé avec une application cliente: android ou autre). Le modèle est composé d'une simple classe TaskDto (une tâche, afin de gérer une todolist).

Allons dans le root de ce projet Maven de type pom (packaging), et exécutons la commande suivante

mvn archetype:generate -DarchetypeGroupId=org.ninjaframework -DarchetypeArtifactId=ninja-servlet-jpa-blog-archetype -DarchetypeVersion=2.5.1

Note: la version 3 des archétypes n'est pas encore sortie, mais la montée de version se fait aisément dans le pom.

La création étant interactive, nous renseignons les informations suivantes:

groupId: fr.arolla.ninja
artifactId: ninja-todo-backend
version: 1.0-SNAPSHOT
package: fr.arolla.ninja

Nous appelons ce sous-projet ninja-todo-backend, et vérifions qu'il est bien ajouté à la liste des modules du projet parent.

Un simple mvn clean install permet de vérifier que le sous-projet ninja a été correctement intégré au projet parent.

Nous pouvons aussi vérifier que le projet ninja se lance:

cd ninja-todo-backend

mvn ninja:run

L'application est alors accessible à l'adresse http://localhost:8080/

Ouvrons le module ninja-todo-backend et découvrons différents répertoires:

/src/main/java:

folder assets : répertoire contenant les ressources statiques telles que les images, les fichiers css, etc.

folder conf : les fichiers de propriétés, l'internationalisation, les routes, etc.

folder controllers : c'est le code java sur lesquels sont mappés vos routes définies dans le fichier conf/Routes.java

folder db/migration : les fichiers de migration sql, un pour chaque version

folder models : rien de bien original, ce sont les classes du modèle

folder views : les vues au format freemarker, un sous répertoire par Controller comprenant un template par fonction

Une approche MVC

Commençons par ajouter notre module de modèle au projet ninja-todo-backend (dans le fichier pom.xml), puis ajoutons un écran affichant une liste de tâches à accomplir.

Commençons par ajouter notre table dans le fichier db/migration/V1__.sql, qui est le script initial joué par flyway.

create table Tasks (
id bigint generated by default as identity,
title varchar(255),
description varchar(255),
doneAt timestamp,
dueDate timestamp,
primary key (id));

Attelons nous ensuite à la partie C (le controller) en créant un simple controller avec une méthode retrieveAllTasks(), dans le répertoire controller:

@Singleton
public class TaskListController {

    @Inject
    Provider<EntityManager> entitiyManagerProvider;
    @Inject
    private static final Logger LOGGER= LoggerFactory.getLogger(TaskListController.class);

    private Function<Task,TaskDto> TO_DTO = new Function<Task, TaskDto>() {
        @Override
        public TaskDto apply(Task task) {
            TaskDto dto = new TaskDto(task.getDueDate(), task.getDescription(), task.getTitle());
            if (task.getDoneAt() != null) {
                dto.done();
            }
            return dto;
        }
    };

    @Transactional
    public Result retrieveAllTasks() {
            List<TaskDto> tasks= retrieveTasks();
            return Results
                .html()
                .render("tasks", tasks);
    }

    private List<TaskDto> retrieveTasks() {
        EntityManager entityManager = entitiyManagerProvider.get();

        TypedQuery<Task> query= entityManager.createQuery("SELECT x FROM Task ", Task.class);
        List<Task> tasks = query.getResultList();

        return Lists.transform(tasks, TO_DTO);
    }

Il nous manque à router ce controller, en mappant une route sur cette fonction dans le fichier conf/Routes.java:

router.GET().route("/tasks").with(TaskListController.class, "retrieveAllTasks");

Le controller fait appel à la fonction 'render' pour générer le rendu au format html, qui prend en paramètre une liste nommée 'tasks'. Nous allons donc devoir créer la vue correspondante dans le répertoire views/{NomController}/{nomFonction}.ftl.html (un template freemarker), ce qui dans notre cas donnera le fichier views/TaskListController/retrieveAllTasks.ftl.html:

<#import "../layout/defaultLayout.ftl.html" as layout>
<@layout.myLayout "Home page">
<h1>Waiting tasks</h1>
<#if !tasks?has_content>
    <p>No Tasks entries yet</p>
<#else>
    <h1>Tasks</h1>
    <#list tasks as task>
        <div class="jumbotron">
            <h2>${task.title}</h2>
            <p>${task.description}</p>
        </div>
    </#list>
</#if>
</@layout.myLayout>

Accédez à votre page dans votre navigateur (http://localhost:8080/tasks). Il n'y a aucune tâche pour l'instant.

Ninja framework propose un tips assez utile. En effet nous trouvons dans le package conf une classe StartupActions qui contient la fonction suivante

    @Start(order=100) //ordre de priorité basse
    public void generateDummyDataWhenInTest() {
       if (!ninjaProperties.isProd()) {
            setupDao.setup();
        }
    }

Nous allons donc pouvoir remplir avec quelques tâches notre base au démarrage (et uniquement en phase de développement) en rajoutant des données dans la classe SetupDao:

entityManager.persist(new Task("Article Blog Ninja", "faire un court article pour parler du framework ninja";));
entityManager.persist(new Task("Jam de code","s'inscrire à la prochaine Jam de code Arolla"));

Relancez le server, un liste de taches apparaît maintenant.

Exposez facilement des api REST

Effectivement exposer les mêmes données en json est simple, il vous suffit d'ajouter une route:

router.GET().route("/tasks/json").with(TaskListController.class, "retrieveAllTasksJson");

Ainsi qu'une fonction dans le controller retournant du json:

    @Transactional
    public Result retrieveAllTasksJson() {
        List<TaskDto> tasks= retrieveTasks();
        return Results
                .json()
                .render(tasks);
    }

Et nous pouvons retrouver nos tâches au format json.

La documentation est très bien faite, il est donc facile de monter en compétence sur les différentes facettes du framework.
Je n'en montrerai donc pas plus car il serait assez difficile de faire mieux que le site officiel.

Conclusion

Le framework ninja est un bon framework, bien pensé avec lequel on est vraiment productif.
L'objectif est donc bien atteint.
Son intégration directement dans Maven est je pense un gros atout.
Il est juste dommage qu'il ne soit pas arrivé quelques années plus tôt, il aurait surement fait une entrée plus fracassante.
Dans un contexte où Play est vraiment implanté sur ce créneau, et à l'heure où certaines personnes se détournent de Java et de Maven, il répond cependant à un certain besoin et à mon avis il trouvera quand même son public.

Les sources sont disponibles ici.

Plus de publications

Software gardener au quotidien, je suis adepte de TDD, d'XP et de tout ce qui peux me permettre de mener à bien mes missions

Comments are closed.