Archive

Posts Tagged ‘maven’

Tomcat, Maven, Angular, Grunt et Yeoman sont dans un bateau

Depuis quelques mois, je développe (avec mon pote Jérémy ) une application web basée sur AngularJsangular

Au fil des itérations, nous avons :

  • Ajouté pas pal de dépendances Javascript, (d3.js, angularstrap, ..) il faut alors gérer les dépendances communes, les mises à jour, les conflits, …
  • Utilisé Twitter bootstrap en mode LESS, ce qui nous permet de définir le thème de notre application de manière centrale et facilite aussi la création de « composants » utilisables partout dans l’application
  • Utilisé CoffeeScript …. parce que la syntaxe nous plait bien. Une sorte de JavaScript un peu amélioré.
  • Concaténé les fichiers javascripts pour limiter le nombre de requêtes HTTP et la latence qui va avec.

Fans de CodeStory, nous avons adapté le mécanisme qu’il utilisent : gérer ces taches au runtime, coté serveur. Cependant au fur et à mesure que le projet avançait le code source grossit et les temps de compilation LESS et CoffeeScript devenaient désagréables et pénalisants pour le développement. Plus de 20 secondes d’attente pour tester la modification d’une feuille de style, …. ca laisse trop de temps pour twitter ! :).

Devoxx France  2012 est passé par la et pas mal de présentations autour des outils d’assemblage JavaScript : Yeoman, Grunt et Bower.

  • Bower est un outil de gestion de dépendances web (Javascript, Css, fonts, ..)
  • Grunt est le Ant de Javascript : enchainer des taches pour former une chaine d’assemblage ou de tests. Il peut servir de lanceur pour un serveur web de développement, qui surveille les changements dans les sources et recompile tout instantanément
  • Yeoman est un générateur de projets, qui permet de démarrer les projets rapidement. Il se base sur Bower et Grunt

Yeoman workflow

Enervé par une journée passée à trop souvent attendre que les transformations LESS ou Coffee se terminent, je passe une partie de mon WeekEnd à migrer nos sources vers une organisation « Yeoman », le bilan est extrêmement positif, la compilation est instantanée, la page se rafraichit immédiatement.

C’est clair, le moteur JavaScript utilisé sur JVM (Rhino.js) est beaucoup plus lent que le moteur V8 qui propulse node.js.

Maintenant, plus question de reculer, mais il va falloir résoudre plusieurs soucis :

  • Notre application doit être déployée sous forme d’un unique fichier War (je n’ose pas vous donner le nom du serveur d’app), embarquant à la fois le serveur (ressources REST) et le client (fichiers HTML, JS, CSS, …).

Au run, le client et le serveur sont donc déployés sous la même URL racine, alors qu’en développement, les fichiers « client » sont mis à disposition par un « serveur Grunt » et les ressources REST par un Tomcat local.

Pour résoudre ce soucis, nous avons paramètré le serveur web de Grunt pour que les appels REST soient délégués à Tomcat.

Pour cela : installer le module grunt-connect-proxy :

npm install grunt-connect-proxy --save-dev

Puis le déclarer dans le fichier Gruntfile.js

  • Tout le cycle de vie de l’application et de son système sont gérés par maven. Nous voulions donc intégrer le build Yeoman (Npm, Grunt et Bower en fait) dans le build maven. Pour faire rapide, le plugin antrun a fait le boulot dans un premier temps. Puis j’ai intégré tout ça dans un plugin maven, pour rendre le pom plus léger et partager ça avec une autre équipe.

L’organisation par défaut du projet est la suivante :

pom.xml
 - src
   - main
     - java
     - webapp
     - …
   - test
     - ..
 - yo
   package.json
   component.json
   Gruntfile.js
   - app
     index.html
     ...
   - test
     ...
   - dist
     ...

Et voici les modifications à apporter au pom.xml

Le bilan est très positif est nous sommes super satisfaits de cette plateforme de développement.

Le plugin yeoman-maven-plugin est disponible sur github et sur le repo maven central, j’ai également publié un projet exemple qui intègre tout ceci avec une application REST/JAXRS.

Maven2 au ChtiJUG

Ce soir j’ai participé à la deuxième réunion du ChtiJUG, le club des Javaistes du Nord.

Thème de la soirée : Maven2, la présentation était effectuée par Arnaud Héritier, commiter et membre du comité du projet.

Cela fait maintenant 6 ans que je pratique Maven et je venais surtout pour profiter de l’expérience d’Arnaud que l’on imagine avoir participé à de multiples mises en places.

J’ai trouvé la présentation intéressante, Arnaud ne manie pas la langue de bois et n’est pas avar en anecdotes et conseils.

Voici les points que j’ai retenu de sa présentation :

  • Maven ne convient pas à tous les projets, notamment quand il n’y a pas de besoin de standardisation ou de réutilisation
  • Dans un premier temps, se contenter d’une mise en place simpliste, en évitant de sur utiliser les capacités avancées de l’outil : héritages à n niveaux, trop de modules, profils, …
  • De concentrer ensuite sur la sécurisation du build : utiliser un proxy maven pour pallier à une défaillance des serveurs centraux, préciser la version de tous les plugins.
  • Enfin : industrialiser le build : mettre en place une intégration continue, mettre en place les outils et rapports qualité, automatiser la release.

Arnaud a également pas mal partagé sur sa vision des outils de l’écosystème Maven et se montre très enthousiaste quand il parle de Nexus (proxy et référentiel maven)  ou de Hudson (intégration continue)

Autre remarque d’Arnaud : configurez Maven pour qu’il s’adapte à votre IDE et pas l’inverse : il faut passer le moins de temps possible dans la console.

Je pense que la fin de la présentation ainsi que certaines questions ont du plonger une bonne partie de la salle dans de profonds doutes quand au coté simple de l’outil, le niveau était assez relevé 🙂

Coup de chapeau à mon ami et collègue Jérémy qui a brillamment gagné une licence IntelliJ IDEA en répondant à une question difficile : de quel projet est issu Maven : vous le saviez vous ?

Étiquettes : ,

Ca bouge du coté des plugins maven pour GWT

Charlie Collins annonce qu’il va rejoindre le projet Mojo afin de fusionner le plugin GWT-Maven qu’il a réalisé avec celui du projet Mojo : gwt-maven-plugin.

Je pense que c’est une très bonne nouvelle pour l’industrialisation des projets GWT, tous les efforts d’outillage autour de maven porteront sur le même produit.

Étiquettes : ,

Créer un packaging maven2

Maven 2 propose d’origine les types de packaging des différents composants java / j2ee : jar, war, ejb et ear.

Cependant, il faut parfois utiliser des types de packaging moins conventionnels qui nécessitent la création d’un packaging maven dédié.

Pour exemple, je vais créer un packaging nommé « custom », qui va créer des archives avec l’extension .cust et qui contient les classes et ressources du projet dans un répertoire « classes » et les dépendances du projet dans un répertoire lib.

  • Première étape : créer le plugin maven2 à l’aide de l’archetype plugin :
    mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-plugin -DgroupId=org.tartachuc.m2 -DartifactId=my-custom-packaging
  • Ajouter dans le descripteur de projet pom.xml le projet maven-archiver :
    
    <dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-archiver</artifactId>
    <version>2.2</version>
    </dependency>
    
  • Modifier le MOJO généré par l’archetype pour qu’il prenne en charge le packaging.
    • Renommer la classe MyMojo en CustomPackageMojo
    • Changer le commentaire en entête de la classe pour préciser le goal : custom et la phase : package
      
      /**
      * Goal which creates a custom package
      * @goal custom
      * @phase package
      */
      
    • Ajouter au Mojo les attributs et leur documentation qui vont permettre de récupérer des informations sur le projet qui utilise le plugin :
      /**
      * The maven project.
      * @parameter expression="${project}"
      * @required
      * @readonly
      */
      private MavenProject project;
      
      /**
      * The directory containing generated classes.
      * @parameter expression="${project.build.outputDirectory}"
      * @required
      * @readonly
      */
      private File classesDirectory;
      
      /**
      * Build directory.
      * @parameter expression="${project.build.directory}"
      * @required
      */
      private File buildDirectory;
    • Ajouter au Mojo les attributs qui vont permettre de créer l’archive :
      /**
       * The Jar archiver needed for archiving.
       * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}"
       * @required
       */
      private JarArchiver jarArchiver;
      
      /**
       * The maven archive configuration to use
       * @parameter
       */
      protected MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
    • Ce packaging maison nécessite d’archiver les dépendances du projet, dans un premier temps les dépendances vont être copiées dans un répertoire de travail. Pour pouvoir configurer ce répertoire de travail, ajouter un attribut au Mojo
      /**
       * Lib directory
       *
       * @parameter expression="${project.build.directory}/custom/libs"
       * @required
       */
      private File libDirectory;
    • Il faut maintenant implémenter la méthode « execute » qui est le point d’entrée du Mojo.
      /** {@inheritDoc} */
      public void execute() throws MojoExecutionException {
          // Compute archive name
          String archiveName = project.getBuild().getFinalName() + ".cust";
          File custFile = new File(buildDirectory, archiveName);
          // Configure archiver
          MavenArchiver archiver = new MavenArchiver();
          archiver.setArchiver(jarArchiver);
          archiver.setOutputFile(custFile);
      
          // copy runtime libs
          copyLibs();
          try {
              // archive classes
              archiver.getArchiver().addDirectory(classesDirectory, "classes/");
              // archive libs
              archiver.getArchiver().addDirectory(libDirectory, "lib/");
              // create archive
              archiver.createArchive(project, archive);
              // set archive as artifact
              project.getArtifact().setFile(custFile);
          } catch (ArchiverException e) {
              throw new MojoExecutionException("Exception while packaging", e);
          } catch (ManifestException e) {
              throw new MojoExecutionException("Exception while packaging", e);
          } catch (IOException e) {
              throw new MojoExecutionException("Exception while packaging", e);
          } catch (DependencyResolutionRequiredException e) {
              throw new MojoExecutionException("Exception while packaging", e);
          }
      }
      
      /**
       * Copy runtime libs
       * @throws MojoExecutionException
       */
      protected void copyLibs() throws MojoExecutionException {
          try {
              Set artifacts = project.getArtifacts();
              for (Iterator iter = artifacts.iterator(); iter.hasNext();) {
                  Artifact artifact = (Artifact) iter.next();
                  ScopeArtifactFilter filter = new ScopeArtifactFilter(
                          Artifact.SCOPE_RUNTIME);
                  if (!artifact.isOptional() && filter.include(artifact)
                          && "jar".equals(artifact.getType())) {
                      FileUtils.copyFileToDirectory(artifact.getFile(),
                              libDirectory);
                  }
              }
          } catch (IOException e) {
              throw new MojoExecutionException("Error copying libs", e);
          }
      }    
  • Il faut maintenant indiquer à maven quel MOJO exécuter lors du cycle de vie de notre nouveau packaging. Pour cela, le plugin doit inclure un descripteur plexus nommé components.xml à placer dans répertoire src/main/resources/META-INF/plexus.
    <?xml version="1.0" encoding="UTF-8"?>
    <component-set>
      <components>
        <component>
          <role>org.apache.maven.artifact.handler.ArtifactHandler</role>
          <role-hint>cust</role-hint>
          <implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
          <configuration>
            <type>cust</type>
            <extension>cust</extension>
            <language>java</language>
          </configuration>
        </component>
        <component>
          <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
          <role-hint>cust</role-hint>
          <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
          <configuration>
            <phases>
              <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
              <compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>
              <process-test-resources>org.apache.maven.plugins:maven-resources-plugin:testResources</process-test-resources>
              <test-compile>org.apache.maven.plugins:maven-compiler-plugin:testCompile</test-compile>
              <test>org.apache.maven.plugins:maven-surefire-plugin:test</test>
              <package>org.tartachuc.m2:my-custom-packaging:custom</package>
              <install>org.apache.maven.plugins:maven-install-plugin:install</install>
              <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
            </phases>
          </configuration>
        </component>
      </components>
    </component-set>

    Nous avons inséré notre MOJO (org.tartachuc.m2:my-custom-packaging:custom) lors de la phase de packaging. les autres MOJO sont repris des plugins standards de maven.

  • Pour installer le plugin dans votre référentiel local, exécuter la commande
    mvn install
  • Pour utiliser le packaging nouvellement créé, il faut créer un nouveau projet et déclarer le packaging cust et le plugin créé ci-dessus comme extension du build.
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>org.tartachuc.m2</groupId>
      <artifactId>my-custom-project</artifactId>
      <packaging>cust</packaging>
      <version>1.0-SNAPSHOT</version>
      <name>my-custom-project uses a custom packaging</name>
      <dependencies>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-orm</artifactId>
          <version>2.5.3</version>
        </dependency>
      </dependencies>
      <build>
        <extensions>
          <extension>
            <groupId>org.tartachuc.m2</groupId>
            <artifactId>my-custom-packaging</artifactId>
            <version>1.0-SNAPSHOT</version>
          </extension>
        </extensions>
      </build>
    </project>
  • Here it is 🙂
Étiquettes :