samedi 27 septembre 2014

Maven Avancé : offline, debug, tree, list, Reactor

Maven : Quelques Commandes Utiles

Forcing updates

Working offline is sometimes interesting, but forcing updates can be useful to retrieve the latest version of a snapshot artifacts developed by one of your teammates. The --update-snapshots options (or -U) forces maven to check all snapshot dependencies and retrieve latest versions:

mvn --update-snapshots clean install or mvn -U clean install

Thanks to this option, you’re sure to build the project against the latest available version of all your snapshot dependencies. If not used, the dependencies are checked once a day (default behavior).

Specific local repository

The maven.repo.local variable allows configuring the local repository location of Maven:

mvn clean install –Dmaven.repo.local=~/tmp/tmp-repo

This creates a complete new local repository at the specified location.

Offline Mode

Maven often takes a lot of time to check the potential updates of dependencies and plugins. But sometimes, you want to skip this process, either because you don’t have an Internet connection or because you know that you can safely ignore them.

Before being able to run maven in offline mode, be sure you have compiled everything at least one time online. Maven retrieves all the dependencies in your local repository. Then, the offline mode just reuses the latest retrieved artifacts. Enabling the offline mode is quite simple:

mvn –-offline clean install or mvn  -o clean install
Debug maven
mvn -X

This command is used to start the maven goals in debug mode and gives logging information. This command gives more information like what artifact is failing for what reason. This command can be used when you are not getting any clue for your maven project execution failure.

Maven Dependencies information
mvn help:effective-pom
Shows the logical contents of a pom.xml, including contents inherited from the parent pom.xml, up to and including the Maven super POM.
mvn dependency:tree
Shows all dependencies (including transitive dependencies) of your project. This is very helpful for debugging dependency version issues.
mvn dependency:list
There is another Maven dependency plugin goal: dependency:list.  It displays the same project’s dependencies in a flat, alphabetically-sorted list

mvn dependency:sources

Downloads all project sources separate from IDE project creation.  Execute from root of parent project, then have your IDE synch up sources.

Maven Reactor

Dans un projet multi-module, Maven offre des options permettant de contrôler le build de modules avec leurs dépendances. En effet, il existe des options qui vous permettent de manipuler la façon dont Maven va construire vos projets multi-module. C'est grâce au plugin maven-reactor-plugin qui a été redescendu au coeur de Maven.  Pour illustrer mes propos, je prends l’exemple d’un projet multi-module (Root) contenant :
  1.    Le module common partagé par tous les autres modules. Ce dernier ne dépend d’aucun autre module.
  2.   Le module core contenant 2 sous modules : dea et domain qui dépendent eux aussi du module common
  3.   Le module service dépend du module dea.

Si on résume :


-          Module service è dea
-          Module dea è common
-          Module domain è common
-          Module common indépendant

Pour connaitre l’ordre de construction de module et le graphe de dépendance, il suffit de taper la commande : mvn install


Il faut remarquer ici qu’avant la construction d’un module, toutes ses dépendances sont préalablement construites : construction du module common, ensuite core/dea puis core/domain et enfin service

Resuming the Build with --resume-from

Qui n’a pas fait un commit (par erreur ou pas !) d’une classe qui ne compile pas ou qui fait échouer les tests ? Cela fait automatiquement échouer la construction du module concerné. Une option très utile pour commencer la construction du projet à partir d’un module (donc ignorer le module fautif). Imaginons juste qu’une erreur survient dans le module core/domain (erreur de compilation ou de test qui ne passe plus, etc.). Lorsque vous lancez la commande mvn install vous aurez :



La construction du module core/domain a échoué. Vous remarquez aussi que la construction des autres modules est ignorée (SKIPPED). En effet, lorsqu’une erreur survient dans un module (domain ici), la construction s’arrête et les modules restants sont ignorés (dea et service). 

Solution 1 : option  --resume-from ou –rf

Nous corrigeons le module domain sans changer common ; nous savons que common est bon et que donc il n'est pas nécessaire de le reconstruire ou de le tester. Nous pouvons alors utiliser l'argument resume-from (-rf) ainsi :


mvn install –rf [–resume-from] core/domain

Le module passé en paramètre de l’option -rf est identifié à partir de son chemin relatif ou de son artifactId.


Ainsi, common ne sera pas reconstruit et le build reprendra là où nous l'avions laissé dans domianSi domain est construit avec succès, Maven poursuivra et construira les autres modules.

Solution 2 : --fail-at-end (--fae)

Cette solution consiste à ignorer la construction du module. La construction du projet continue malgré l’erreur survenue dans le module domain.



Tous les modules sont construits excepté domain.

Maven pl(--projects)

Une option qui vous permet de choisir spécifiquement les modules du projet à construire. La liste de modules est séparée par des virgules. Pour identifier chaque module, vous pouvez soit utiliser son chemin relatif par rapport à la racine du projet soit son artifactId.


Seuls les deux modules (domain et service) sont construits, ce qui nous évitera d'avoir à exécuter Maven séparément dans chaque répertoire.

Maven --also-make

Une option qui s’utilise conjointement avec l’option -pl. En plus de construire les modules demandés par l’option -pl, Maven va également construire toutes les dépendances de chaque module. Par exemple, mvn install –pl dea –am va d’abord construire le module common puis celui de core/dea comme illustré sur cette image.


Lorsque nous utilisons --also-make, Maven va examiner la liste de modules passés en paramètre (ici, uniquement core/deaet va descendre dans l'arbre des dépendances à la recherche des modules qu'il va devoir construire. Dans ce cas, il va automatiquement construire common (car core/dea dépend du module common), mais sans construire core/domain ni service.

Avec –pl service, common est construit (une dépendance de dea) puis core/dea (une dépendance de service) et on termine par la construction du module service.


Si vous avez bien compris, le module common ne dépend d’aucun module :


Maven --also-make-dependents

Comme pour (-am), cette option s’utilise conjointement à l’option -pl. En plus de construire les modules demandés par l’option -pl, Maven va construire tous les modules qui dépendent de ces derniers. Si vous exécutez la commande mvn install –pl core/dea –amd, Maven construit d’abord le module core/dea puis service (qui dépend du module dea). En plus la construction du module core/dea, Maven a construit tous les modules qui dépendent du module core/dea (dans notre exemple, il y a que le module service qui dépend du core/dea)


La commande mvn clean install –pl common –amd permet de construire tous les modules : on commence par la  construction du module common

  • Quels sont les modules qui dépendent du common ? core/domain et core/dea, il faudra donc les construire. 
  • Ensuite, quels sont les modules qui dépendent des modules core/dea ou core/domain ? service dépend du module core/dea donc il faut le construire….












jeudi 25 septembre 2014

Introduction Java 8 : Stream, Expression lambda, Interface fonctionnelle

Parmi les nouveaux concepts introduits dans Java 8, nous pouvons particulièrement souligner les expressions lambda qui ont connu un développement considérable pour ses rapports étroits avec les langages de programmation fonctionnelle. Son intérêt principal provient de la simplicité de sa syntaxe.
L’introduction des expressions lambda n’aurait pas été utile si l’API Collection n’avait été revue. Il faut se rappeler que L’API Collection est la plus utilisée du JDK. Conçue il y a un peu plus de 15 ans, elle est encore aujourd’hui au cœur de toutes les applications Java. Avec l’introduction de la genericité en 2004, elle a subi sa toute première mise à jour. Cette mise à jour, bien qu’importante, n’a cependant pas modifié ses patterns d’utilisation basés essentiellement sur les Iterator. Avec l’introduction des expressions lambda, l’API Collection a été entièrement révisée en introduisant les Stream, qui, ajoutée à la notion de Collector, rend obsolète le pattern Iterator.
Les expressions lambda : Quel est le besoin ?
Nous allons implémenter une classe permettant d’effectuer des opérations arithmétiques sur des nombres entiers (addition, soustraction, multiplication). Pour de raison de clarté et simplicité, les opérations se portent sur deux  nombres.

Commençons par créer une interface Operation contenant une méthode calculer prenant 2 nombres entiers en paramètre :

 Ensuite nous implémentons les classes pour chaque opération (addition, soustraction, multiplication) :

Imaginons maintenant une méthode statique prenant en paramètre : deux entiers et une opération à calculer :

Notre classe pour effectuer les calculs :


Que peut-on remarquer ? Nous avons défini une interface et une classe pour chaque opération. L’ajout d’une nouvelle opération (par exemple la division) nécessite la création d’une nouvelle classe pour l’implémenter.
Grace aux expressions lambda, nous allons simplifier le code de notre calculatrice. L’utilisation des expressions lambda est une technique pour écrire des fonctions anonyme au sens où son prototype n’a pas de nom et directement définies là où elles sont appelées. Cela permet une écriture plus simple pour les fonctions créées à la volée, pour un besoin spécifique.

Expressions lambda et interface fonctionnelle de Java

Definition de la JSR 335

A functional interface is an interface that has just one abstract method, and thus represents a single function contract. (In some cases, this "single" method may take the form of multiple abstract methods with override-equivalent signatures (8.4.2) inherited from superinterfaces; in this case, the inherited methods logically represent a single method.)

Pour implémenter les expressions lambda, le langage s’appuie sur les interfaces fonctionnelles également connues sous le nom de Single Abstract Method interfaces (SAM Interfaces). C’est toute interface qui possède une seule méthode d’instance abstraite. Il est aussi possible d’annoter une interface par @FunctionalInterface afin de demander au  compilateur de vérifier que l'interface possède  bien une seule méthode abstraite. C’est un peu le même principe qu’avec l’annotation @Override. Notre interface Operation est un bel exemple. Quelques exemples supplémentaires d’interfaces fonctionnelles du jdk : ActionListener, Runnable, Callable, Comparator
Une expression lambda peut être affectée dans une variable d’une interface fonctionnelle à condition que les signatures de la méthode abstraite (de l’interface fonctionnelle) correspondent à celle de l’expression lambda. Vous pouvez imaginer l’expression lambda comme une implémentation de la méthode abstraite de l’interface.
Une expression Lambda se compose de paramètres, d’un symbole flèche et d’un corps (exécution). La syntaxe utilisée est la suivante :

S’il y a une seule instruction, les accolades peuvent être omises ou même les types des paramètres qui seront déterminés par l’inférence de type du compilateur :

Que peut-on remarquer : La création d’une lambda expression ne nécessite pas l’instanciation d’un objet (utilisation du mot clé new). En effet, nous n'avons pas besoin de demander à la JVM de créer un objet qui sera ensuite nettoyé par le garbage ce qui permet de faire un gain de performance. En utilisant les expressions lambda, seule l’interface fonctionnelle (Operation) est nécessaire :



Vous pouvez même remarquer la définition à la volée d’une nouvelle opération (la division)

Collections et lambda expression :

Une collection de données est un conteneur d’éléments de même type (ou d’un type dérivé) qui possède un protocole particulier pour l’ajout, le retrait et la recherche d’éléments. Depuis le JDK 5.0, les collections sont manipulées par le biais de classes génériques implémentant l’interface Collection<E>, E représentant le type des éléments de la collection. Cette interface fournit tout ce qui est nécessaire au type de rangement des données (listes, tables associatives ou map en anglais, piles, files, ordonnées, etc…) et à leur manipulation simple (ajout, suppression, parcours, …).  Cependant le traitement plus complexe (filtrage, transformation, …), nécessite une API externes notamment Google Guava, Commons Collection, etc.

L’objectif ici est de pouvoir appliquer une lambda expression aux éléments d’une collection par exemple pour filtrer les éléments en ne sélectionnant que ceux qui remplissent une condition particulière (cela revient à appliquer une lambda expression à chaque élément ((E e) -> true/false, si true, l’élément est sélectionné) ou transformer un élément E en un autre élément X ((E e) -> X). Ce type de traitement renvoie une autre collection (donc duplication de collection en mémoire) contenant les éléments sélectionnés ou les nouveaux éléments transformés. Pour éviter ce type de duplication, Oracle a introduit un nouveau concept : Stream. L'utilisation d'un Stream permet d'éviter la duplication de collections en mémoire lorsque le traitement que l'on veut effectuer nécessite l'utilisation de collections intermédiaires (la nouvelle collection contenant les éléments filtrés ou transformés).

Un Stream est composé d’une source (List, tableau, etc.) et d’un ensemble d’opérations qu’on peut appliquer sur les éléments de la source.



On a deux types d’opérations : opération intermédiaire qui transforme le Stream  en un autre (après avoir appliqué une expression lambda sur les éléments de la source) et opération terminale qui produit un résultat. Un exemple pour illustrer nos propos : à partir d’un ensemble de personnes (une Collection), nous voulons le nom de personnes de plus de 18 ans. Comment faire ?

  1.        Il faut tout d’abord créer un Stream à partir d’une collection de personnes (la source du Stream est une collection de personnes)
  2.        Ensuite appliquer une opération intermédiaire qui filtre en appliquant une expression lambda : (Personne p) -> p.getAge() > 18. Il faudra imaginer ici une méthode (nommons Stream.filter (lambdaExpression)) fournie par la classe Stream, prenant en paramètre une expression lambda dont le type de retour est boolean (le filtre appliqué à une personne retourne true si la personne a plus de 18 ans sinon false) et qui (l’opération filter de la classe Stream) retourne un autre Stream dont la source ne contient que les personnes de plus 18 ans. 
  3.        Il faudra ensuite appliquer sur le Stream résultant, une  opération (méthode) de transformation. Il faudra aussi imaginer ici une méthode (nommons Stream.map ((Personne p) -> p.getName()) fournie par la classe Stream, prenant en paramètre une expression lambda dont le type de retour est String (l’expression lambda appliquée à une personne retourne le nom de la personne) et qui retourne un autre Stream dont la source ne contient que les noms de personnes.  
  4.        Une opération terminale pour finir qui permet de collecter les éléments de la source du Stream dans une liste. Ce type d’opération est aussi fourni par la classe Stream (Stream.collect). Ici pas besoin d’une expression lambda, il suffit juste de passer le paramètre Stream.collect(Collectors.toList()) fourni par la classe Collectors pour transformer les éléments de la source du Stream en une liste de nom de personnes (classe List<String>)
Nous pouvons remarquer que la stratégie d’itération est encapsulée dans la classe Stream. En effet un Stream dispose de sa propre stratégie de parcours. Il n'est donc PAS nécessaire d’indiquer explicitement COMMENT faire ce parcours en utilisant par exemple une boucle comme pour les collections (for, while, etc.).


Un autre exemple sur une liste de nombres. Nous voulons le carré de tous les nombres impairs de la liste :

Comme vous le constatez, la classe collection dispose d’une nouvelle méthode : stream. Cette évolution survenue en Java 8 aurait engendré la problématique de modification de toutes les classes qui implémentent la classe Collection :



Pour pallier à ce problème les méthodes par défaut sont introduites. L’objectif est de fournir une solution au manque d’évolution des interfaces. Les méthodes par défaut permettent de déclarer une méthode dans une interface et proposer une implémentation par défaut qui sera exécutée si elle n'est pas surchargée.


Avec Java 8 vous pouvez désormais créer de méthodes comportant du code (sans oublier d’ajouter le mot clé default) ainsi que la déclaration de méthodes statiques. Une méthode par défaut n'exige pas d'être implémentée puisqu'elle l'est déjà mais il reste tout à fait possible de le faire.


 A suivre ...