lundi 2 août 2010

Accélérer le développement d'applications Vaadin avec Maven et Jetty

Cet article applique les principes décrits dans l'article "Accélérer le développement d'applications web Java avec Maven" aux applications web utilisant le framework Vaadin.


Présentation

Vaadin est un framework permettant de réaliser facilement des applications web riches. Il est construit sur le framework GWT.

Le principal avantage de Vaadin réside dans la génération des pages (ou écrans) de l'application. Ces derniers sont réalisés uniquement via du code Java, à la manière d'une interface Swing. Vaadin est packagé avec un ensemble de thèmes satisfaisant la plupart des besoins.


Objectif

On se propose d'accélérer le développement des applications utilisant Vaadin en mettant en place un rechargement dynamique des classes Java.

Comme dans le précédent article, ce tutoriel se focalisera sur l'utilisation de l'outil de build Maven conjointement au serveur d'application Jetty.


Etape 1 - Création du module Maven

Une fois de plus, la commande mvn archetype:generate se révèle très pratique pour mettre en route rapidement un projet Maven utilisant Vaadin.

$ mvn archetype:generate

Dans la liste des modèles de projet proposés, on note la présence du modèle "vaadin-archetype-clean". C'est ce dernier qui servira à générer un module de test Dans mon cas, il porte le numéro 7.

Choose a number : 7
Choose version : 1.3.0
Define value for property 'groupId': fr.pingtimeout
Define value for property 'artifactId': maven-vaadin
Define value for property 'version': 1.0-SNAPSHOT
Define value for property 'package': fr.pingtimeout

Le projet "maven-vaadin" est maintenant créé. Il contient une application basique utilisant Vaadin et affichant un bouton "Click Me".

On peut vérifier que le module contient bien les fichiers attendus.

$ cd maven-vaadin
$ tree.
|-- pom.xml
`-- src
    `-- main
        |-- java
        |   `-- fr
        |       `-- pingtimeout
        |           `-- MyVaadinApplication.java
        `-- webapp
            |-- META-INF
            |   |-- MANIFEST.MF
            |   `-- context.xml
            `-- WEB-INF
                `-- web.xml

Etape 2 - Activation du rechargement automatique

Cette fois, le plugin Jetty est déjà présent dans le fichier pom.xml. Toutefois, il doit être sensiblement modifié.

Dans le pom du module, le paramètre scanIntervalSeconds a pour valeur '0'. Un commentaire dans le fichier indique d'ailleurs que cette valeur désactive le rechargement automatique des classes.
Il faut donc changer la valeur du paramètre scanIntervalSeconds (par exemple, en spécifiant un scan toutes les secondes).

<scanIntervalSeconds>1</scanIntervalSeconds>

Etape 3 - Définition des ressources

Le chemin vers les ressources du projet sont définies par le paramètre code>resourcesAsCSV. Ce paramètre spécifie qu'elles sont situées dans le dossier /src/main/webapp et dans le dossier ${project.build.directory}/${project.build.finalName}. Dans mon cas, ceci équivaut au dossier /target/maven-vaadin-1.0-SNAPSHOT/.

Ce paramétrage pose un problème pour le rechargement automatique :

  • les classes recompilées sont situées dans le dossier ${project.build.outputDirectory} - dossier (1)
  • les classes initialement construites avec le war sont situées dans le dossier ${project.build.directory}/${project.build.finalName}/WEB-INF/classes/ - dossier (2)

En cas de modification d'un fichier .java, Jetty relance la compilation dans le dossier (1) mais n'en tient pas compte car les anciennes classes sont toujours dans le dossier (2).

Pour résoudre ce problème, il suffit de donner une nouvelle valeur au paramètre resourcesAsCSV :

<resourcesAsCSV>src/main/webapp,${project.build.directory}/${project.build.finalName}/WEB-INF/lib</resourcesAsCSV>

Seules les librairies de l'application seront désormais accédées en tant que ressources.

Etape 4 - Tests

Le serveur peut désormais être lancé via la commande :

$ mvn jetty:run

Premier lancement, premier échec. L'erreur suivante est générée par Jetty :

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to configure plugin parameters for: org.mortbay.jetty:maven-jetty-plugin:6.1.25

Cause: Setter org.mortbay.resource.ResourceCollection.setResourcesAsCSV( java.lang.Class ) threw exception when called with parameter 'src/main/webapp,/home/pingtimeout/Projects/maven-vaadin/target/maven-vaadin-1.0-SNAPSHOT/WEB-INF/lib': java.lang.IllegalArgumentException: file://home/pingtimeout/Projects/maven-vaadin/target/maven-vaadin-1.0-SNAPSHOT/WEB-INF/lib is not an existing directory.

Contrairement au précédent tutoriel, il est obligatoire de spécifier la cible "package", faute de quoi Jetty ne peut pas trouver le dossier ${project.build.directory}/${project.build.finalName}/WEB-INF/lib, car ce dernier n'existe pas.


Lancer le serveur avec la commande :

$ mvn clean package jetty:run

Le serveur est correctement démarré, il est possible de consulter l'application à l'adresse :

http://localhost:8080/maven-vaadin/
.

[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 1 seconds.

Aucune opération supplémentaire n'est requise pour que Jetty prenne directement en compte les modifications effectuées aux fichiers Java.

Modifier le fichier MyVaadinApplication.java pour y ajouter un bouton.

public void init() {
        window = new Window("My Vaadin Application");
        setMainWindow(window);
        window.addComponent(new Button("Click Me"));
        window.addComponent(new Button("Click Pingtimeout"));
}

Relancer une simple compilation des classes Java du projet

$ mvn compile

Des que le fichier est recompilé, Jetty recharge automatiquement les classes de l'application web.

[INFO] Restart completed at Mon Aug 02 14:04:51 CEST 2010

Rafraîchir le navigateur internet pour constater que la modification a été prise en compte.

Epilogue

Le plugin Jetty pour Maven se révèle d'une grande efficacité. Moyennant quelques paramètres, il est possible de lui faire recharger dynamiquement des classes Java dans des applications complexe (war avec de nombreuses librairies externes).

Des problèmes à la mise en place du plugin ? Des améliorations à apporter à ce tutoriel ? Voyons tout ça dans les commentaires !

dimanche 1 août 2010

Accélérer le développement d'applications web Java avec Maven

Lorsqu'une application web (war) est développée avec Maven, elle doit être re-déployée sur un serveur d'application pour être testée, même quand elle ne contient que des modifications de forme. En utilisant le plugin Jetty pour Maven, il est possible d'effectuer un rechargement dynamique des JSP de manière transparente.

Présentation

Le développement d'une application J2EE est généralement découpé en plusieurs couches (application n-tiers) :
  • La couche de présentation ne contient que les écrans de l'application et ne gère aucune logique métier
  • Les couches sous-jacentes contiennent la logique métier, le modèle de données de l'application ainsi que la liaison avec un éventuel SGBD
L'outil de build Maven permet de séparer proprement ces différentes couches en un projet dit multi-modules. Dans ce cas, le module contenant les écrans est packagé sous la forme d'une application web java (war) et contient tous les modules du projet.
Note : le cas des ear n'est pas pris en compte dans cet article.
Un problème fréquent consiste à effectuer des modifications de forme (par exemple, la mise en page de l'application) et à vouloir s'assurer que ces modifications sont conformes au résultat attendu. Il faut alors reconstruire toute l'application web et la redéployer sur un serveur d'application. On embarque alors inutilement toutes les couches sous-jacentes.

Le plugin Jetty pour Maven

Parmi les plugins disponibles pour Maven, on note la présence d'un serveur d'application à part entière : Jetty. Ce plugin permet de lancer un serveur J2EE sur l'application web générée. Il permet également de prendre directement en compte les modifications faites sur les fichiers du module.

Etape 1 - Création d'un projet Maven

Commençons par créer un projet d'application web avec Maven. Exécuter la commande suivante : $ mvn archetype:generate
Dans la liste des modèles de projet proposés, on note la présence du modèle "maven-archetype-webapp (An archetype which contains a sample Maven Webapp project.)". Dans mon cas, il porte le numéro 66.
Choose a number : 66
Choose version : 1.0
Define value for property 'groupId': fr.pingtimeout
Define value for property 'artifactId': maven-webapp
Define value for property 'version': 1.0-SNAPSHOT
Define value for property 'package': fr.pingtimeout
Le projet "maven-webapp" est maintenant créé. Il contient un seul fichier JSP et affiche le très célèbre "Hello World".
On peut vérifier que le module contient bien les fichiers attendus.

$ cd maven-webapp
$ tree
.
|-- pom.xml
`-- src
    `-- main
        |-- resources
        `-- webapp
            |-- WEB-INF
            |   `-- web.xml
            `-- index.jsp

Etape 2 - Ajout du plugin Jetty au module

Le plugin Jetty n'est pas encore utilisable à ce stade. Modifier le fichier pom.xml en ajoutant, dans la section "build", les lignes suivantes :
<plugins>
    <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
    </plugin>
</plugins>
La section "build" doit donc être la suivante :
<build>
    <finalName>maven-webapp</finalName>
    <plugins>
        <plugin>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>maven-jetty-plugin</artifactId>
        </plugin>
    </plugins>
</build>
Jetty peut maintenant être utilisé dans le module avec la commande : $ mvn jetty:run
Note : si le projet n'est pas compilé, la cible "jetty:run" passera automatiquement les phases de compilation et de test.

Etape 3 - Premier et dernier test

Après avoir lancé le serveur, il est possible de consulter le résultat de la JSP à l'adresse : http://localhost:8080/maven-webapp/
Aucune opération supplémentaire n'est requise pour que Jetty prenne directement en compte les modifications effectuées aux fichiers JSP.
Modifier le fichier index.jsp afin d'y ajouter un message, par exemple :
<html>
  <body>
    <h2>Hello World!</h2>
    <hr/>
    <h3>
      <% out.println(";-)"); %>
    </h3>
  </body>
</html>
Rafraîchir le navigateur internet pour constater que, même sans recompilation, la modification a été prise en compte.

Et les frameworks dans tout ça ?

Jetty peut également redéployer des applications plus lourdes, notamment celles qui utilisent des frameworks comme Struts ou Vaadin. Ce point fera l'objet d'un prochain article.


Un moyen encore plus simple de faire du rechargement dynamique ? Des remarques sur ce tutoriel ? N'hésitez pas à vous exprimer dans les commentaires.