Vérifier votre code Java avec PMD

GNU/Linux Magazine n° 105 | mai 2008 | Romain pelisse
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Produire un code de qualité est garant de facilité de maintenance et d'évolution pour n'importe quel projet. Néanmoins, les bonnes pratiques en matière de programmation sont nombreuses, et il n'est pas aisé de s'assurer que tous les développeurs les connaissent, les comprennent et surtout les appliquent. Sans compter les délais de réalisation d'un projet qui ne permettent jamais de faire toutes ces améliorations du code de dernière minute, pourtant si nécessaires...

1. L'enjeu de la qualité de code

Contrairement à une idée répandue, la qualité d'un code ne se limite pas à sa simple lisibilité ou à sa capacité à répondre aux attentes fonctionnelles de ses utilisateurs. En effet, avec plus de 40% du budget des maintenances logicielles consacrés à la réécriture, on peut comprendre aujourd'hui qu'on attend aussi d'un code de qualité le respect d'un ensemble de bonnes pratiques, mais aussi de conventions. Obtenir un code conforme à ces règles améliore notablement la capacité de maintenance du code, et favorise l'avortement de bugs dès la conception du logiciel. Malheureusement, qu'il s'agisse d'un projet open source ou d'un projet interne, il semble impossible de s'assurer que tout intervenant respecte les règles de présentation, et de bonnes pratiques en vigueur. Pourquoi ? Entre autres, pour les raisons suivantes :

- Les développeurs changent tout au long du projet et varient largement en expérience, du débutant au programmeur chevronné.

- Certains intervenants ou experts n'interviennent que très peu de temps, il n'est donc pas possible de les former sur l'ensemble des règles du projet.

- Les règles et bonnes pratiques de programmation ne sont pas absolues, surtout entre développeurs de différentes générations et « écoles ».

Si vous prenez une dizaine de programmeurs et que vous leur demandez quels sont leurs « 10 commandements » en termes de programmation, aucun ne vous dressera la même liste ! Pire, certaines des règles proposées entreront en contradiction les unes avec les autres. L'exemple de la présentation du code source (accolades à la Berkeley ou non) vient immédiatement à l'esprit, mais ce n'est pas le seul. Les conventions de nommage (préfixer les champs par '_' ou usage systématique du mot clé 'this') ou encore de bonnes pratiques de programmation (« Toute classe doit fournir un constructeur, même privé », « Utiliser StringBuffer plutôt String ») ne sont pas toujours les mêmes selon le parcours du développeur (Un exemple encore évident est le programmeur temps réel et le programmeur de gestion). Bref, toutes les règles ou bonnes pratiques ne sont pas systématiquement applicables. Certains projets laisseront de côté telle pratique au profit de telle autre, et certaines architectures autoriseront ou non telle ou telle pratique.

Un exemple concret est l'utilisation de JNI, qui est l'API qui permet d'exécuter un code C, natif, depuis une classe Java. Sur un projet de développement d'applications embarquées ou un développement très lié au système, l'usage de JNI est, a minima, légitime, au plus, incontournable. Sur un projet J2EE, qui doit donc respecter scrupuleusement la spécification, et assurer un maximum de portabilité, l'usage de JNI est, au minimum, assujetti à la validation de l'architecture technique, au pire simplement interdit.

Alors, comment s'assurer que tout le monde respecte les « mêmes règles » du jeu ? Comment détecter les « écarts de conduite » ? Et, pour aller encore plus loin, comment définir ses propres règles et implémenter un contrôle automatique de leur application ?

2. Les solutions libres

Bien évidemment, il existe plusieurs solutions libres et/ou open source qui permettent de contrôler la qualité d'un code Java. Toutes ces solutions n'ont pas le même domaine d'application et ne fournissent pas les mêmes contrôles. Certaines se recouvrent, d'autres se complètent assez bien. Parmi ces outils, citons juste les suivants, avec leurs domaines d'application :

- PMD, Lint4j, Findbugs contrôlent le respect de bonnes pratiques et détectent d'éventuels bugs (ex : vérification de la fermeture des sessions de connexion à la base de données).

- Checkstyle, Jalopy, Eclipse Formatter assurent le respect des conventions de présentation du code (ex : accolade à la Berkeley).

- JDepend, Classcycle, Depfind recherchent les dépendances cycliques.

- Ckjm, Javancss fournissent un grand nombre de métriques (taille du code, couplage afférent/efférent, nombre de méthodes) généralement très révélatrices.

Dans le cadre de cet article, nous n'étudierons que PMD, car il présente plusieurs qualités qui en font un bon outil pour débuter :

- Il est reconnu et déjà utilisé par beaucoup de projets Java de manière presque systématique.

- Il est simple d'utilisation et s'intègre aussi bien dans Eclipse que dans Ant ou Maven.

- Il couvre un large spectre, en termes de contrôle (de la convention de nommage au calcul de complexité cyclomatique).

- Il est très facilement personnalisable.

- Il fournit, en standard, près de 300 règles « prêtes à l'emploi ».

- Le projet est pérenne et stable depuis maintenant longtemps, et possède une grande quantité de contributeurs qui lui assurent indépendance et longévité.

- Enfin, il offre un mécanisme simple, élégant et facilement déployable pour ajouter de nouveaux contrôles.

Nous verrons bien évidemment aussi les limites de PMD, ainsi que son architecture interne. En effet, cette dernière nous permettra de bien comprendre qu'il s'agit d'un outil d'analyse de code statique, puissant, mais tout de même rudimentaire sur certains aspects.

3. Les règles et les jeux de règles (« rules » and « rulesets »)

3.1 Mettre en place la vérification d'une règle PMD

Fidèle à la tradition du monde Java, PMD se configure à l'aide d'un fichier XML. Le seul pré-requis est donc que ce fichier se trouve dans le CLASSPATH (différents emplacements où la machine virtuelle Java recherche les classes des objets qu'elle doit instancier) pour que PMD le charge et tienne compte de la configuration qui y est décrite. Ce fichier XML est un ruleset, c'est-à-dire, un ensemble de règles... Mais qu'est-ce qu'une règle ?

Il s'agit de l'implémentation du contrôle d'une convention (« Tous les paquetages de notre projet, doivent commencer par net.sourceforge. ») ou d'une bonne pratique (« Toute classe utilitaire, ne contenant donc que des méthodes statiques, ne doit pas présenter de construire public ») ou encore de la programmation en elle-même (« Cette instruction est tautologique, elle ne sert donc à rien »). Toutes ces choses peuvent être détectées par PMD. À chaque règle, on trouve associé un ensemble de données :

- name, le nom de la règle (UseSingleton) ;

- class, la classe Java qui implémente le contrôle de la règle (net.sourceforge.pmd.rules.design.UseSingleton) ;

- message, le message à associer à une infraction à la règle. (« Toutes les méthodes sont statiques, envisager de transformer cette classe en un singleton ou une classe abstraite. ») ;

- une description détaillée de la règle, plus complète que le simple message ;

- une priorité allant de 1 à 5 pour qualifier l'importance de l'infraction ;

- un à plusieurs exemples d'infractions ou de bonnes pratiques associées à la règle.

  <rule name="UseSingleton"

    message="All methods are static. Consider using Singleton instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning."

    class="net.sourceforge.pmd.rules.design.UseSingleton"

    externalInfoUrl="http://pmd.sourceforge.net/rules/design.html#UseSingleton">

    <description>

      < ![CDATA[

  If you have a class that has nothing but static methods, consider making it a Singleton.

  Note that this doesn't apply to abstract classes, since their subclasses may

  well include non-static methods. Also, if you want this class to be a Singleton,

  remember to add a private constructor to prevent instantiation.

      ]]>

    </description>

    <priority>3</priority>

    <example>

      < ![CDATA[

public class MaybeASingleton {

  public static void foo() {}

  public static void bar() {}

}

      ]]>

    </example>

  </rule>

Tout ça, juste pour configurer une règle ? Ca semble un rien rébarbatif, surtout si on veut 200 des règles de PMD ! Heureusement, il existe un champ supplémentaire 'ref' qui permet de définir un élément 'rule' comme outillé par un autre élément 'rule'. Ainsi, pour utiliser une règle fournie par PMD, il suffit de la déclarer dans votre fichier de configuration ainsi :

<rule ref="rulesets/controversial.xml/UnusedModifier"/>

Plutôt simple et très puissante, car cette notation permet de rendre complètement indépendante votre configuration PMD de l'implémentation des règles. Ainsi, si vous utilisez la règle UnusedModifier avec la version 3.8 de PMD et que, dans une version plus récente de l'outil, la classe qui implémente cette règle change, cette modification n'aura aucun impact sur votre fichier de 'rulesets' quand vous déciderez d'utiliser la version 4.1 !

3.2 Personnaliser les règles PMD

Autre avantage de la structuration de PMD, la personnalisation. En effet, si vous définissez une règle avec le champ 'ref', vous pouvez aussi redéfinir la valeur de ses autres attributs. Supposez que votre projet dispose d'un ensemble défini de règles qui suivent une nomenclature précise. Soit une règle identifiée par « METHODES-5 : Toute méthode ne doit disposer que d'une seule instruction 'return' ». Il est totalement possible de redéfinir l'identifiant comme le message de la règle PMD, ainsi même que sa description :

    <rule name="METHODES-5"

message="Toute méthode ne doit disposer que d'une seule instruction 'return'"

           ref="controversial.xml/OnlyOneReturnRule"

           externalInfoUrl="http://serveur.interne/avec/mes.propres.regles.en.ligne">

<description>

  < ![CDATA[

    Ma propre description détaillée de la règle, issue des documents d'architecture interne.

  ]]>

  </description>

       <priority>1</priority><!-- je peux aussi redéfinir la priorité -->

       <example>

< ![CDATA[

  Mes propres exemples et contre exemples !

]]>

       </example>

     </rule>

Notez aussi qu'il est possible que certaines règles de PMD requièrent une configuration spécifique. Ce paramétrage par défaut peut aussi être surchargé, si besoin est :

  <!--   Ici on configure simplement le degré de complexité cyclomatique à 7.

    On garde le reste de la configuration par défaut de la règle ( message,

    description, etc...)

  -->

  <rule ref="rulesets/codesize.xml/CyclomaticComplexity">

    <properties>

      <property name="reportLevel" value="7"/>

</properties>

  </rule>

3.3 Structurer ses règles en plusieurs fichiers

Si le nombre de règles devient conséquent, il est pertinent de les diviser en plusieurs fichiers, un par thème par exemple. La syntaxe XML de PMD étant assez astucieuse, elle permet de le faire ainsi :

< ?xml version="1.0" encoding="UTF-8"?>

<ruleset name="Normes de programmation Java et J2EE">

  <description>

    Ce fichier contient le paramétrage de nos règles de programmation,

    spécifiques à notre projet.

  </description>

  <rule ref="section/encapsulation.xml" />

  <rule ref="section/perfs.xml" />

  <rule ref="section/j2ee.xml" />

...

</ruleset>

En inspectant ce fichier, PMD ira naturellement chercher dans le CLASSPATH un répertoire 'section' contenant les différents fichiers ('encapsulation.xml','perfs.xml', etc.) et chargera leur contenu. Ces fichiers sont eux aussi de simples fichiers de règles comme décrit précédemment.

Ce fonctionnement permet d'éviter de regrouper trop de règles au sein d'un même fichier, ce qui rendrait le fichier difficile à lire et à maintenir (gardez à l'esprit que vous pouvez vous retrouvez avec plusieurs centaines de règles). De plus, il est aussi possible et aisé de désactiver un ensemble de règles au besoin (il suffit de commenter la balise 'rule' associée au fichier que l'on souhaite retirer).

Note

Générer un seul fichier pour PMD à partir de plusieurs fichiers

Généralement, il est bien commode de ne disposer que d'un seul fichier pour configurer PMD. Dans certains cas d'utilisation, comme au sein d'Eclipse, il n'est pas possible d'utiliser plusieurs fichiers (Il faut les importer un à un, ce qui est prohibitif). Néanmoins, il est plus pratique et structurant de disposer de plusieurs fichiers pour maintenir aisément ses règles.

Pour gérer cette situation, le plus simple est de conserver les règles dans différents fichiers, mais de générer un fichier unique, les regroupant toutes, à l'aide d'une simple transformation XSLT. Pour effectuer cette transformation, on s'appuie juste sur un fichier XML contenant la liste des fichiers de règles :

< ?xml version="1.0" encoding="UTF-8"?>

<rulesets>

    <ruleset filename="../section/encapsulation.xml"/>

    <ruleset filename="../section/performance.xml"/>

....

</rulesets></pre>

<span class="code_par">Notez bien que la syntaxe XML ci-dessus ressemble à celle de PMD, par soucis de clarté, mais qu'elle ne respecte pas la DTD de PMD.</span>

<p class="normal">On réalise ensuite la transformation XSLT:

<pre>

        <xsl :element name="ruleset">

</xsl><xsl :attribute name="name">toutes-mes-regles</xsl>

            <!-- note the following <xsl:text> tag are here to provide Carriage Return

                    and the generated file to ensure a "nicer" XML presentation. -->

<xsl :text>

</xsl>

            <xsl :comment>

                THIS FILE HAS BEEN AUTOMATICLY GENERATED.

            </xsl>

            <xsl :for-each select="ruleset">

                <!-- Opening the appropriate file -->

                <xsl :variable name="filename" select="@filename"/>

                <xsl :variable name="rulesets" select="document($filename)"/>

                <!-- Adding to our tree all the nodes present there -->

                </xsl><xsl :for-each select="$rulesets/ruleset/rule[not(contains(@ref,'chapitres'))]">

</xsl><xsl :text>

</xsl>

                        <xsl :comment>***************************</xsl>

                                         <xsl :text>

</xsl>

                        <xsl :copy-of select="."/>

<xsl :text>

</xsl>

La transformation XSLT ci-dessus opère ainsi : elle ouvre chaque fichier indiqué par les attributs 'filename' des balises 'ruleset' et recopie son contenu dans le fichier de résultat. Pour appeler cette transformation, on peut utiliser un simple script Ant :

<xslt processor="trax"

 style="src/main/xsl/rulesets-merge.xsl"

        in="src/main/xml/rulesets-list.xml"

        out="target/all-rules-in-one-file.xml">

</xslt>

Ou simplement en ligne de commande, sous Linux :

xsltproc src/main/xsl/rulesets-merge.xsl src/main/xml/rulesets-list.xml > target/all-rules-in-one-file.xml

...et le tour est joué.

Cerise sur le gâteau, toujours sous un système *nix, il est aisé d'écrire un script générant automatiquement le fichier de référence contenant la liste des 'rulesets' :

#!/bin/sh

#

# This shell automaticly build the 'toutes-regles.template.xml' file

#

TARGET="./src/main/artifacts"

TEMPLATE="${TARGET}/toutes-regles.template.xml"

RULESETS_DIR="./src/main/resources/chapitres/"

# writing the header

echo '<?xml version="1.0" encoding="UTF-8"?>' > ${TEMPLATE}

# adding root element

echo '<rulesets>' >> ${TEMPLATE}

# adding some warning !

echo '<![CDATA[' >> ${TEMPLATE}

echo '*******************************************************************' >> ${TEMPLATE}

echo 'This file has been automaticly generated by the build-template.sh ' >> ${TEMPLATE}

echo ' script. DO NOT EDIT IT, ANY MODIFICATIONS WILL BE OVERWRITTEN      ' >> ${TEMPLATE}

echo ' DURING RELEASE PROCESS ! ! !                                      ' >> ${TEMPLATE}

echo '*******************************************************************' >> ${TEMPLATE}

echo ']]>' >> ${TEMPLATE}

# adding childrens...

ls -1 ${RULESETS_DIR}/[0-9]*.xml | \

while

        read FILE

do

        # adapting path to xslt engine

        FILENAME=` echo ${FILE} | sed -e 's/\.\/src\/main/\.\./g' -e 's/\/\//\//g'`

        echo " <ruleset filename=\"${FILENAME}\"/>" >> ${TEMPLATE}

done

echo '</rulesets>' >> ${TEMPLATE}

Et voilà, le tour est joué, la génération du fichier peut s'intégrer à un processus de construction automatisé, assurant qu'à chaque nouvelle version du référentiel de règle, le fichier monolithique sera régénéré automatiquement.

4. Architecture interne de PMD

On dispose désormais d'un ou plusieurs fichiers de règles, mais comment PMD s'en sert-il ? Bien qu'il ne soit pas nécessaire de comprendre l'architecture interne de PMD pour l'utiliser, voici quelques explications sur son fonctionnement qui nous permettront de mieux comprendre son mécanisme d'extension comme ses limites sous jacentes.

4.1 L'arbre syntaxique abstrait (AST)

Quand PMD analyse une classe, il commence par construire un arbre syntaxique abstrait (Abstract Syntax Tree) simplement en fournissant le code source au précompilateur Java, qui le construit naturellement pour vérifier que le code source est « syntaxiquement juste » (Qu'il ne manque pas de point virgule à la fin des lignes, que l'instruction soit bien formée, ce genre de choses). Cet arbre syntaxique est au cœur du fonctionnement interne de PMD :

Figure 1 : Arbre syntaxique abstrait et le code source associé

La figure 1 présente, à gauche, l'arbre syntaxique associé à l'extrait de code présenté à droite. On constate donc que l'arbre présente simplement la structure interne de la classe sous forme d'une simple arborescence. À partir de cette arborescence, PMD recherche des infractions à l'aide de cette représentation. Nous étudierons plus en détail ce processus quand nous verrons les mécanismes d'extension.

4.2 Le pattern du visiteur

Pour chaque code source, PMD va appliquer le pattern du Visiteur. En effet, il va transmettre la classe à chacune des règles définies dans le ruleset. Chaque règle va ainsi pouvoir parcourir l'arbre à la recherche d'une infraction. Si une règle trouve une infraction, elle ajoute cette dernière au rapport. Une fois le processus achevé, le rapport est enregistré dans un fichier, au format de votre choix.

Figure 2 : Architecture interne de PMD

4.3 Limite architecturale

L'objectif de cette rapide présentation de l'architecture interne de PMD est de faciliter la réalisation d'extensions et de comprendre les limites de l'outil. Tout d'abord, PMD a besoin d'une classe Java syntaxiquement valide pour obtenir un AST. Ce n'est pas une grande limitation en soit, car la plupart des IDE garantissent que le code livré, au moins, compile.

PMD ne dispose pas d'une vue d'ensemble du projet. Il s'appuie sur le code source essentiellement et il ne construit pas de « méta modèle » de l'application. Il est donc impossible pour lui d'implémenter une règle qui vérifierait qu'il n'y a pas d'opération de transtypage inutile. En effet, pour détecter qu'un transtypage est inutile, PMD devrait disposer de l'ensemble des bibliothèques utilisées par le projet pour, à coup sûr, pouvoir retrouver la signature de la méthode invoquée. D'autres outils, comme Eclipse, assurent ce genre de contrôle, mais il est important de bien comprendre cette limitation lorsque l'on se demande si une règle peut être implémenter.

Dans la pratique, cette limitation se révèle être d'assez peu d'importance ; de plus, le projet étudie la possibilité de contourner cette difficulté (notamment en étudiant le fonctionnement de l'un de ses « concurrents », Findbugs :) ).

De même, l'analyse de PMD se repose uniquement sur le code source, il ne lui est pas possible de faire une analyse dynamique, c'est-à-dire au cours de l'exécution de ce dernier. Bref, PMD n'est pas un outil de profiling ! En soi, c'est évident, mais il vaut mieux le préciser.

5. Utilisation

Maintenant que nous savons comment PMD opère et que nous disposons de notre fichier, il ne reste plus qu'à l'essayer. Il est aussi possible de l'utiliser en ligne de commande, mais l'utilisation au sein de l'IDE ou d'un outil de build est de loin la meilleure solution.

5.1 Eclipse et les autres IDE

L'usage le plus pratique de PMD est bien évidemment au sein de son environnement de développement. Le plus utilisé dans le monde Java est Eclipse, qui a aussi la qualité d'être open source. Eclipse dispose d'une architecture permettant d'étendre ses fonctionnalités par le biais de plugins (greffons). Il existe donc un plugin PMD pour Eclipse. Ce dernier s'installe comme n'importe quel plugin Eclipse par le biais d'une URL à ajouter à la liste connue d'Eclipse pour installer et mettre à jour ses greffons. PMD s'intègre aussi dans les IDE suivants :

- pmd-bluej

- pmd-eclipse-plugin

- pmd-emacs

- pmd-gel

- pmd-jbuilder

- pmd-jdeveloper

- pmd-jedit

- pmd-netbeans

 

Fig. 3 : Exemple d'utilisation de PMD au sein d'Eclipse

5.2 Ant

Si Eclipse permet au développeur de vérifier son code, il ne permet pas un contrôle automatique du projet. De plus, lorsque l'on veut analyser une grosse base de code, il n'est pas simple d'utiliser Eclipse. Heureusement, il est possible d'intégrer PMD à un script Ant, et de générer un rapport détaillé des infractions présentes dans le code.

<target name="pmd" description="Contrôle de la qualité du code">

  <!-- On définit un classpath dans lequel on place les dépendances

    de PMD. -->

  <path id="pmd.classpath">

    <pathelement location="lib/pmd-3.9.jar" />

    <pathelement location="lib/asm-3.0.jar" />

    <pathelement location="lib/backport-util-concurrent-3.0.jar"/>

    <pathelement location="lib/jaxen-1.1-1.jar" />

    </path>

  <!-- On indique à Ant l'existence d'une nouvelle tâche : 'pmd'-->

  <taskdef name="pmd"

      classname="net.sourceforge.pmd.ant.PMDTask">

    <classpath refid="pmd.classpath" />

</taskdef>

  <!-- Désormais, on peut utiliser pmd : -->

  <pmd>

    <!-- On précise l'emplacement du fichier de règles-->

    <ruleset>les-regles.xml</ruleset>

<!-- On indique format du rapport -->

    <formatter type="xml"

        toFile="pmd-report.xml"/>

<!-- on indique à pmd quels fichiers sources analysés -->

    <fileset dir="src">

<include name="**/*.java" />

    </fileset>

  </pmd>

</target>

5.3 Maven 2

Avec Maven 2, le processus est encore plus simple. On ajoute dans la section « reports », le plugin PMD :

<reporting>

  <plugins>

    <plugin>

      <groupid>org.apache.maven.plugins</groupid>

      <artifactid>maven-pmd-plugin</artifactid>

      <configuration>

        <rulesets>

          <ruleset>les-regles.xml</ruleset>

        </rulesets>

</configuration>

    </plugin>

  </plugins>

</reporting>

Il suffit ensuite d'exécuter la commande suivante pour obtenir un rapport PMD dans le répertoire target :

$ mvn pmd:pmd

6. Mécanisme d'extension

Faisons un rapide bilan : nous savons configurer PMD pour vérifier les règles de notre choix. Nous connaissons son mécanisme interne, et nous savons le faire fonctionner dans notre IDE comme dans notre build. Reste maintenant à faire peut-être le plus important, ajouter ses propres vérifications et contrôles. PMD propose pour cela 2 mécanismes d'extension qui permettent d'ajouter des règles « maison ».

6.1 Règle « Java »

Le premier mécanisme est élémentaire. Il s'agit de créer une classe Java qui héritera de la classe AbstractRule et qui surchargera les méthodes qu'il désire « visiter » pour effectuer son traitement. En soi, c'est simple, mais ça pose des problèmes de déploiement. En effet, dans le cadre d'Eclipse, pour que l'IDE intègre vos nouvelles règles Java, il faut développer un fragment de plugin (hors scope de cet article), et, d'une manière générale, il faut gérer l'ensemble du code supplémentaire comme un mini projet Java, ce qui peut se révéler assez lourd.

6.2 Règle XPath

Si je vous décourage tout de suite d'utiliser le premier mécanisme d'extension, c'est tout simplement parce que le deuxième est beaucoup plus pratique. Pour bien le comprendre, revenons, encore une fois, à l'architecture de PMD. PMD analyse non pas le code source, mais l'AST, qui est une vision arborescente et hiérarchique du contenu de la classe. Hors, il existe une technologie standard pour parcourir une telle arborescence, nommée XPath. XPath est, en effet, un langage de requête permettant, un peu comme le SQL permet de sélectionner des informations dans une base, d'interroger l'AST sur sa structure. Des requêtes telle que « Si tu implémentes l'interface Clone, as-tu une implémentation de la méthode clone() qui remonte des exceptions de type CloneNotSupported ? » deviennent donc réalisables entièrement en XPath:

  <rule name="CloneThrowsCloneNotSupportedException"

     message="clone() method should throw CloneNotSupportedException"

     class="net.sourceforge.pmd.rules.XPathRule">

     <properties>

       <property name="xpath">

         <value>

           <![CDATA[

//MethodDeclaration

[

MethodDeclarator/@Image = 'clone'

and count(MethodDeclarator/FormalParameters/*) = 0

and count(NameList/Name[contains

(@Image,'CloneNotSupportedException')]) = 0

]

[

../../../../ClassOrInterfaceDeclaration[@Final = 'false']

]

           ]]>

         </value>

       </property>

     </properties>

</rule>

Le principe derrière est simple. Si un élément correspond à la requête, une infraction est ajoutée au rapport. Certes, au premier abord, XPath n'est pas un langage de requête évident, mais PMD propose à ses utilisateurs un outil graphique très pratique, nommé le « designer », pour faciliter la conception de requêtes Xpath :

Figure 4 : L'outil « designer » de PMD

La figure 3 présente l'interface du designer, qui se divise en 4 zones :

- La zone A permet de saisir le code Java qui sera inspecté ;

- La zone B présente l'AST généré à partir du code source de la zone A ;

- La zone C permet de saisir la requête XPAth ;

- La zone D indique la ou les infraction(s) relevée(s) par la requête XPath.

L'atout majeur de ce mécanisme est sa facilité de déploiement. Les nouvelles règles ne sont que des requêtes XPath externalisées dans le fichier de configuration de PMD. Elles se déploient donc sans aucune difficulté à travers les différents environnements et ne nécessitent pas d'ajout de bibliothèques (jar).

En conclusion

Voilà une présentation de PMD synthétique qui, je l'espère, vous aura convaincu d'utiliser cet outil puissant et élégant. Quelques remarques importantes avant de vous laisser. Tout d'abord, cet article n'aborde pas tout ce que peut faire PMD (notamment la détection de copier/coller pour Java et d'autres langages, l'analyse de flux de données et, pour la prochaine version, un détecteur de code mort !). L'objectif étant de rester concis, on ne pouvait donc pas tout aborder. De plus, il est important de noter que si aujourd'hui PMD ne permet d'analyser que du code Java, une partie des contributeurs a pour objectif d'intégrer de nouveaux langages (Javascript et Ruby entre autres). Si vous trouvez ce point intéressant, n'hésitez pas à vous manifester auprès du projet, pour convaincre les quelques réticents ! :)

Autre point important : ne vous laissez pas piéger par la relative facilité de tout ceci. Installer et configurer PMD dans Eclipse ne prend pas beaucoup de temps, mais définir, structurer et outiller un référentiel complet et pertinent de règles de qualité pour un ou plusieurs projets demande plus de temps et une certaine expertise.

Liens

- La qualité logicielle (fr) : http://lil.univ-littoral.fr/~oumoumsack/qualite/

- Principles and Patterns (en) : http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf

- PMD, le site du projet : http://pmd.sourceforge.net/

- Liste exhaustive des outils existants : http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis

- XRadar, un puissant outil de « reporting » de qualité : http://xradar.sourceforge.net/

- Sonar, un jeune projet similaire et très prometteur : http://sonar.hortis.ch/

- Définition de la complexité cyclomatique : http://en.wikipedia.org/wiki/Cyclomatic_complexity

Merci à Hugues Frachon pour sa relecture de candide et à François Le Droff (http://www.droff.com/) pour m'avoir tant appris sur la qualité de code et finalement donner l'opportunité de contribuer à PMD.