3 juin 2015

Maven & Yeoman


Nous allons voir comment mixer un projet Java Maven classique avec un projet frontal Yeoman à l'aide du plugin yeoman-maven-plugin de Thomas Recloux, qui est aussi utilisé dans JHipster.

Le frontal Web écrit en Javascript va utiliser les services REST du back écrit en Java. Le tout dans un seul et même projet et packagé au sein d'un même Jar ou War.

Arborescence du projet

pom.xml     => Fichier Maven
src         => Source du projet
   main
      java
      resources
      webapp
   test
      java
      resources
yo          => Projet Yeoman
   package.json
   Gruntfile.js
   bower.json
   app/
Le répertoire "yo" contient les sources du projet Yeoman qui ne sont pas inclus directement dans les sources du projet. Ceci permet de conserver un projet Yeoman indépendamment du reste du projet qui lui respecte la structure d'un projet Maven.

Maven

La construction du projet "Yeoman" s'effectuera à l'aide de la commande "grunt build" lancée par Maven via le plugin yeoman-maven-plugin.

Ce build nécessite l'appel aux outils "npm", "bower" et "grunt".

Ainsi le plugin va lancer les commandes suivantes par défaut :
  • npm install
  • bower install --no-color
  • grunt test --no-color
  • grunt build --no-color
Ainsi, Maven lance npm, bower puis grunt lors de chaque build de l'application.

Application classique Java EE

Dans le cas d'une application Java classique (non SpringBoot) packagée sous forme de War, nous devons indiquer à Maven d'inclure les fichiers produits par Grunt à la racine du War.

Pour cela, nous utilisons le plugin maven-war-plugin pour inclure les fichiers du répertoire yo/dist/public produit par Grunt : Nous obtenons le profil suivant dans le pom.xml : Lancer la commande Maven suivante pour packager l'application sous forme d'un War :
mvn package
Les commandes npm install, bower install, grunt test et grunt build sont alors lancés par Maven via le plugin maven-yeoman-plugin afin de compiler le projet yeoman, ensuite Maven va packager les fichiers produits par grunt dans le fichier War.

Nous obtenons alors le packaging suivant :
  • myapp.war :
à la racine :
  les fichiers du répertoire "yo/dist/public" produit par Grunt : index.html, etc.
  les fichiers du répertoire "src/main/resources"
META-INF/
    MANIFEST.MF
    maven/
        pom.xml
WEB-INF/
    classes/
        ... les classes Java du projet
    lib/
        ... les librairies jars
org/
    springframework/
        ... les classes de spring

SpringBoot

Dans le cas d'une application Spring Boot, nous allons voir comment configurer Maven pour intégrer les fichiers du projet Yeoman dans le packaging Jar ou War d'une application Spring Boot.

Nous allons voir les packagings suivants :
  • packaging final pour la mise en production
  • packaging lors du développement

Profil Production : Packaging final

Spring Boot utilise par défaut les répertoires de fichiers de ressources dans le classpath de l'application :
  • /static
  • /resources
  • /public
  • /META-INF/resources
=> voir la documentation officielle de Spring Boot : 26.1.4 Static Content

Dans le cas d'un projet yeoman basé sur le générateur angular-fullstack, l'outil Grunt va produire les fichiers dans le répertoire dist/public.

Or public correspond au nom d'un des répertoires de ressources gérés en standard par Spring Boot. Nous n'avons plus qu'à l'inclure dans le classpath de l'application lors de la phase de packaging via Maven.

Pour cela, nous pouvons indiquons à Maven que le contenu du répertoire yo/dist fera parti du classpath du Jar ou du War généré :
Nous obtenons donc la configuration suivante pour le profil du packaging "production" : Lancer la commande Maven suivante pour packager l'application :
mvn package -Pprod
Nous aurons ainsi le packaging suivant dans le cas du packaging de l'application sous forme de Jar :
  • myapp.jar :
public => le répertoire "public" produit par Grunt
... les autres fichiers du projet
META-INF/
    MANIFEST.MF
    maven/
        pom.xml
Nous aurons le packaging suivant dans le cas du packaging de l'application sous forme de War :
  • myapp.war :
META-INF/
    MANIFEST.MF
    maven/
        pom.xml
WEB-INF/
    classes/
        public => le répertoire "public" produit par Grunt
        ... les autres fichiers du projet
    lib/
        ... les librairies jars
org/
    springframework/
        ... les classes de spring

Profil Développement : utilisation de "grunt serve"

Nous n'incluons pas la partie statique dans le packaging de l'application car celle-ci sera servie par le serveur lancé par la commande "grunt serve".

C'est pourquoi nous n'avons pas besoin de lancer grunt depuis Maven, ni du plugin "maven-yeoman-plugin" pour le profil de développement.


Voici le profil "dev" :

Démarrer l'application Spring Boot via la commande :
mvn spring-boot:run
Démarrer le serveur des ressources statiques :
cd yo

grunt serve


Nous pouvons accéder à l'application via l'URL suivante :
Les modifications apportées dans le projet yeoman seront reportées directement dans le navigateur Web lancé sur le port 9000 via le livereload ou browersync.

Nous devons maintenant définir un proxy sur le serveur lancé par Grunt :

Grunt : Définir un proxy sur le serveur de Grunt pour rediriger les requêtes REST vers le serveur Tomcat

Les requêtes lancées par AngularJS vers les services Rest sont par défaut dirigées vers le serveur lancé par Grunt qui est sur le port 9000.

C'est pourquoi nous avons besoin de définir un proxy sur le serveur lancé par Grunt afin qu'il redirige les requêtes vers le serveur Tomcat sur le port 8080.

De plus, nous avons besoin d'ajouter un filtre CORS pour que le serveur accepte les requêtes des pages provenant du serveur de Grunt.

Filtre Java CORS

Nous ajoutons le filtre suivant dans l'application Java qui ne doit être actif que durant le développement, c'est à dire lorsque le profil Spring est "dev" :

Ajout du proxy dans Grunt

Nous avons des configurations différentes à définir dans le fichier Gruntfile.js suivant si le plugin "connect" ou "browser-sync" est utilisé :

Dans le cas du plugin "connect"

Dans le cas où Grunt lance son serveur de fichiers statiques via le plugin "grunt-contrib-connect", nous pouvons suivre l'article suivant pour effectuer cette configuration : http://www.hierax.org/2014/01/grunt-proxy-setup-for-yeoman.html

  • Installer le plugin "grunt-connect-proxy" :
  • npm install grunt-connect-proxy --save-dev
    
  • Dans le fichier Gruntfile.js> :
    • Ajouter la déclaration du plugin au début du fichier :
    • grunt.loadNpmTasks('grunt-connect-proxy');
      
    • Modifier la configuration du plugin "connect" en ajoutant le roxy pour rediriger les requêtes vers le serveur Java :
    •     connect: {
            options: {
              ...
            },
            proxies: [
              {
                context: '/',
                host: 'localhost',
                port: 8080
              }
            ],
            livereload: {
              options: {
                open: true,
                middleware: function (connect, options) {
                  return [
                    ...
                    
                    // Setup the proxy
                    require('grunt-connect-proxy/lib/utils').proxyRequest
                  ];
                }
              }
            },
            ...
          }
      

Dans le cas du plugin "browser-sync"

Dans le cas où Grunt lance son serveur de fichiers statiques via "browser-sync", nous avons à définir le proxy de la manière suivante :
        browserSync: {
            dev: {
                ...
            },
            options: {
                watchTask: true,
                proxy: "localhost:8080"
            }
        },

Conclusion

Nous venons de voir comment packagée une application Spring Boot ou classique avec Maven.

L'utilisation d'un proxy sur le serveur démarré par Grunt permet de ne pas avoir à modifier directement dans le code les URL vers les services REST situés sur le serveur Java.