9.3. Les outils d’analyse de qualité du code populaires pour Java et Groovy

Il y a beaucoup d’outils open source qui peuvent aider à identifier les mauvaises pratiques de codage.

Dans le monde Java, trois outils d’analyses statiques ont résisté à l’épreuve du temps, et sont largement utilisés de manière très complémentaire. Checkstyle excelle dans la vérification des conventions et normes de codage, les pratiques de codage, ainsi que d’autres mesures telles que la complexité du code. PMD est un outil d’analyse statique similaire à Checkstyle, plus focalisé sur les pratiques de codage et de conception. Et FindBugs est un outil innovant, issu des travaux de recherche de Bill Pugh et de son équipe de l’université du Maryland, qui se focalise sur l’identification du code dangereux et bogué. Et si vous être en train de travailler avec Groovy ou Grails, vous pouvez utiliser CodeNarc, qui vérifie la norme et les pratiques de codage de Groovy.

Tous ces outils peuvent être facilement intégrés dans votre processus de build. Dans les sections suivantes, nous verrons comment configurer ces outils pour générer des rapports XML que Jenkins peut ensuite utiliser dans ses propres rapports.

9.3.1. Checkstyle

Checkstyle est un outil d’analyse statique pour Java. A l’origine conçu pour faire respecter un ensemble de normes de codage hautement configurable, Checkstyle permet aussi maintenant de vérifier les mauvaises pratiques de codage, ainsi que le code trop complexe ou dupliqué. Checkstyle est un outil polyvalent et flexible qui devrait avoir sa place dans n’importe quelle stratégie d’analyse de code basé sur Java.

Checkstyle supporte un très grand nombre de règles, incluant celles liées aux normes de nommage, annotations, commentaires javadoc, taille de méthode et de classe, mesures de complexité de code, mauvaises pratiques de codage, et beaucoup d’autres.

Le code dupliqué est un autre problème important de la qualité de code — le code dupliqué ou quasi-dupliqué est plus difficile à maintenir et à déboguer. Checkstyle fournit un certain soutien pour la détection de code dupliqué, mais des outils plus spécialisés comme CPD font un meilleur travail dans ce domaine.

Une des choses intéressante au sujet de Checkstyle est la facilité avec laquelle on peut le configurer. Vous pouvez commencer avec les normes de codage de Sun et les adapter selon vos besoins, ou démarrer de zéro. En utilisant le plugin Eclipse (ou même directement en XML), vous pouvez choisir parmi plusieurs centaines de règles, et affiner les options des règles que vous choisissez (voir Figure 9.1, “C’est facile de configurer les règles Checkstyle avec Eclipse”). C’est important, car les organisations, les équipes ou même les projets ont des attentes et des préférences différentes au regard des normes de codage, et c’est mieux d’avoir un ensemble de règles précises qui peuvent être adoptées, plutôt qu'un large éventail de règles qui seront ignorées. C’est encore plus important lorsque de grandes bases de code existant sont impliquées — dans ces cas, il est souvent préférable de commencer avec un ensemble plus limité de règles que d’être submergé par un grand nombre de problèmes de formatage relativement mineurs.

C’est facile de configurer les règles Checkstyle avec Eclipse

Figure 9.1. C’est facile de configurer les règles Checkstyle avec Eclipse


Configurer Checkstyle dans votre build est généralement simple. Si vous utilisez Ant, vous avez besoin de télécharger le fichier JAR de Checkstyle depuis le site web et de le rendre disponible à Ant. Vous pourriez le placer dans votre répertoire lib de Ant, mais cela signifierait de devoir personnaliser l’installation de Ant sur votre serveur de build (et tout les nœuds esclaves), ce n’est donc pas une solution très portable. Une meilleure approche serait de placer le fichier JAR de Checkstyle dans l’un des répertoires de votre projet, ou d’utiliser Ivy ou la librairie Maven Ant Task pour déclarer une dépendance à Checkstyle. Si vous choisissez de garder le fichier JAR de Checkstyle dans les répertoires du projet, vous pourrez déclarer la tâche Checkstyle comme indiqué ici :

          <taskdef resource="checkstyletask.properties"
               classpath="lib/checkstyle-5.3-all.jar"/>

Ensuite, pour générer les rapports Checkstyle dans le format XML exploitable par Jenkins, vous pourrez procéder comme suit :

<target name="checkstyle">
  <checkstyle config="src/main/config/company-checks.xml">
    <fileset dir="src/main/java" includes="**/*.java"/> 
    <formatter type="plain"/> 
    <formatter type="xml"/>
  </checkstyle>
</target>

Maintenant, invoquez juste cette tâche (cf., ant checkstyle) afin de générer les rapports Checkstyle.

Dans Maven 2, vous pouvez ajouter quelque chose comme la section <reporting> qui suit :

<reporting>
  <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <version>2.4</version>
        <configuration>
          <configLocation>
            src/main/config/company-checks.xml
          </configLocation>
        </configuration>
      </plugin>
  </plugins>
</reporting>

Pour un projet Maven 3, vous avez besoin d’ajouter un plugin sur l’élément <reportPlugins> de la section <configuration> du maven-site-plugin :

<project>
  <properties>
    <sonar.url>http://buildserver.acme.org:9000</sonar.url>
  </properties>
  <build>
    ...
     <plugins>
       ...
       <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.0-beta-2</version>
        <configuration>
         <reportPlugins>
          <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-checkstyle-plugin</artifactId>
           <version>2.4</version>
           <configuration>
            <configLocation>
              ${sonar.url}/rules_configuration/export/java/My_Rules/checkstyle.xml
            </configLocation>
           </configuration>
          </plugin>
         </reportPlugins>
        </configuration>
       </plugin>
      </plugins>
    </build>
</project>

Maintenant, l’exécution de mvn checkstyle:checkstyle ou mvn site analysera votre code source et génèrera des rapports XML que Jenkins peut utiliser.

A noter que dans le dernier exemple, nous utilisons un ensemble de règles de Checkstyle que nous avons transféré sur un serveur Sonar (définie par la propriété ${sonar.url}). Cette stratégie rend facile l’utilisation du même ensemble des règles Checkstyle pour Eclipse, Maven, Jenkins, et Sonar.

Les versions récentes de Gradle offrent aussi une certaine prise en charge intégrée de Checkstyle. Vous pouvez le configurer pour vos builds comme il suit :

apply plugin: 'code-quality'

Cela utilisera par défaut l’ensemble de règles de Checkstyle définies dans config/checkstyle/checkstyle.xml. Vous pouvez redéfinir cela avec la propriété checkstyleConfigFileName : au moment de l’écriture de ce livre, néanmoins, il n'est pas possible de télécharger le plugin de qualité de code pour Gradle afin d'obtenir les règles Checkstyle depuis une URL.

Vous pouvez générer les rapports Checkstyle ici en exécutant gradle checkstyleMain or gradle check.

9.3.2. PMD/CPD

PMD est un autre outil populaire d’analyse statique. Il se focalise sur les problèmes potentiels de codage comme le code non utilisé ou sous-optimisé, la taille et la complexité du code, et les bonnes pratiques de codage. Certaines règles typiques intègrent « Empty If Statement », « Broken Null Check », « Avoid Deeply Nested If Statements, « Switch Statements Should Have Default», et « Logger Is Not Static Final ». Il y a une bonne quantité de ressemblances avec certaines règles de Checkstyle, bien que PMD ait quelques règles plus techniques, et d’autres plus spécialisées tels que les règles relatives à JSF et Android.

PMD est aussi livré avec CPD, un détecteur open source robuste de code dupliqué ou quasi-dupliqué.

PMD est un peu moins flexible que Checkstyle, bien que vous puissiez choisir les règles que vous voulez utiliser dans Eclipse, et les exporter ensuite dans un fichier XML (voir Figure 9.2, “Configurer les règles PMD dans Eclipse”). Vous pouvez alors importer ces règles dans les autres projets Eclipse, dans Sonar, ou les utiliser dans vos builds Ant ou Maven.

Configurer les règles PMD dans Eclipse

Figure 9.2. Configurer les règles PMD dans Eclipse


PMD est livré avec une tâche Ant que vous pouvez utiliser pour générer les rapports PMD et CPD. Tout d’abord, toutefois, vous devez définir ces tâches, comme le montre l’exemple suivant :

<path id="pmd.classpath">
    <pathelement location="org.apache.maven.model.Build@1460c81d"/>
    <fileset dir="lib/pmd">
        <include name="*.jar"/>
    </fileset>
</path>

<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" 
         classpathref="pmd.classpath"/>

 <taskdef name="cpd" classname="net.sourceforge.pmd.cpd.CPDTask" 
          classpathref="pmd.classpath"/>

Ensuite, vous pouvez générer le rapport PMD XML en invoquant la tâche PMD comme illustré ici :

<target name="pmd">
 <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" 
          classpathref="pmd.classpath"/>

 <pmd rulesetfiles="basic" shortFilenames="true">
  <formatter type="xml" toFile="target/pmd.xml" />
  <fileset dir="src/main/java" includes="**/*.java"/> 
 </pmd>
</target>

Et, pour générer le rapport CPD XML, vous pouvez faire quelque chose comme ça :

<target name="cpd">
  <cpd minimumTokenCount="100" format="xml" outputFile="/target/cpd.xml">
    <fileset dir="src/main/java" includes="**/*.java"/> 
  </cpd>
</target>

Vous pouvez placer l’ensemble de ces règles XML dans le classpath de votre projet (par exemple, dans src/main/resources pour un projet Maven), ou dans un module séparé (si vous voulez partager la configuration entre les projets). Un exemple sur la façon de configurer Maven 2 pour générer des rapports PMD et CPD en utilisant un ensemble de règles XML exporté est indiqué ici :

<reporting>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-pmd-plugin</artifactId>
      <version>2.5</version>
      <configuration>
        <!-- PMD options -->
        <targetJdk>1.6</targetJdk>
        <aggregate>true</aggregate>
        <format>xml</format>
        <rulesets>
          <ruleset>/pmd-rules.xml</ruleset>
        </rulesets>

        <!-- CPD options -->
        <minimumTokens>20</minimumTokens>
        <ignoreIdentifiers>true</ignoreIdentifiers>
      </configuration>
    </plugin>
  </plugins>
</reporting>

Si vous utilisez Maven 3, vous devez placer la définition du plugin dans la section de configuration <maven-site-plugin>. Cet exemple montre aussi comment utiliser un ensemble de règles dans une autre dépendance (dans ce cas, le fichier pmd-rules.jar):

<project>
  ...
  <build>
    ...
     <plugins>
       ...
       <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.0-beta-2</version>
          <configuration>
            <reportPlugins>
              <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                  <!-- PMD options -->
                  <targetJdk>1.6</targetJdk>
                  <aggregate>true</aggregate>
                  <format>xml</format>
                  <rulesets>
                    <ruleset>/pmd-rules.xml</ruleset>
                  </rulesets>

                  <!-- CPD options -->
                  <minimumTokens>50</minimumTokens>
                  <ignoreIdentifiers>true</ignoreIdentifiers>
                </configuration>
              </plugin>
            </reportPlugins>
          </configuration>
          <dependencies>
            <dependency>
              <groupId>com.wakaleo.code-quality</groupId>
              <artifactId>pmd-rules</artifactId>
              <version>1.0.1</version>
            </dependency>
          </dependencies>
        </plugin>
      </plugins>
    </build>
</project>

Maintenant, vous pouvez exécuter soit mvn site soit mvn pmd:pmd pmd:cpd pour générer les rapports PMD et CPD.

Malheureusement, il n’existe actuellement aucune prise en charge de Gradle pour PMD ou CPD, vous devez donc vous contenter d'appeler directement le plugin PMD pour Ant, comme montré ici :

configurations {
    pmdConf
}

dependencies {
    pmdConf 'pmd:pmd:4.2.5'
}

task pmd << {
    println 'Running PMD static code analysis'
    ant {
        taskdef(name:'pmd', classname:'net.sourceforge.pmd.ant.PMDTask', 
                classpath: configurations.pmdConf.asPath)

        taskdef(name:'cpd', classname:'net.sourceforge.pmd.cpd.CPDTask', 
                        classpath: configurations.pmdConf.asPath)

        pmd(shortFilenames:'true', failonruleviolation:'false', 
            rulesetfiles:'conf/pmd-rules.xml') {
            formatter(type:'xml', toFile:'build/pmd.xml')
            fileset(dir: "src/main/java") {
                include(name: '**/*.java')
            }
            fileset(dir: "src/test/java") {
                include(name: '**/*.java')
            }
        }

        cpd(minimumTokenCount:'50', format: 'xml',
            ignoreIdentifiers: 'true',
            outputFile:'build/cpd.xml') {
            fileset(dir: "src/main/java") {
                include(name: '**/*.java')
            }
            fileset(dir: "src/test/java") {
                include(name: '**/*.java')
            }        
        }
    }
}

Cette configuration utilisera la règle PMD configuré dans le répertoire src/config, et génèrera un rapport PMD XML appelé pmd.xml dans le répertoire build. Il lancera aussi CPD et générera un rapport CPD XML appelé cpd.xml dans le répertoire build.

9.3.3. FindBugs

FindBugsest un puissant outil d’analyse de code qui vérifie le byte code de votre application afin de trouver des bogues potentiels, des problèmes de performances, ou des mauvaises habitudes de codage. FindBugs est le résultat d’une recherche menée par Bill Pugh à l’université du Maryland, et qui étudie les modèles de byte code venant de bogues dans de réels grands projets, comme les JDKs, Eclipse, ou le code source d’applications Google. FindBugs peut détecter des problèmes assez importants tels que des exceptions de pointeurs nuls, des boucles infinies, et un accès non intentionnel de l’état interne d’un objet. Contrairement à beaucoup d’autres outils d’analyse statique, FindBugs tend à trouver un plus petit nombre de problèmes, mais de ces problèmes, une grande partie sera importante.

FIndBugs est moins configurable que les autres outils que nous avons vu, mais en pratique vous n’avez généralement pas besoin d’affiner autant les règles qu'avec les autres outils dont nous avons discuté. Vous pouvez lister les règles individuelles que vous voulez appliquer, mais vous ne pouvez pas configurer un fichier XML partagé entre vos builds Maven et votre IDE, par exemple.

FindBugs est livrée empaqueté avec une tâche Ant. Vous pouvez définir la tâche FindBugs dans Ant comme montré en dessous. FindBugs a besoin de référencer le répertoire home de FindBugs, qui est où la distribution binaire a été décompressée. Pour rendre le build plus portable, nous stockons l’installation de FIndBugs dans la structure de répertoire de notre projet, dans le répertoire tools/findbugs :

<property name="findbugs.home" value="tools/findbugs" />

<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" > 
  <classpath>
    <fileset dir="${findbugs.home}/lib" includes="**/*.jar"/>
  </classpath>
</taskdef>

Ensuite, pour exécuter FindBugs, vous pourrez configurer une cible ‘findbugs’ comme montré dans l’exemple suivant. A noter que FindBugs s'exécute sur le byte code de votre application, et non sur le code source, donc vous devez compiler votre code source en premier :

<target name="findbugs" depends="compile"> 
  <findbugs home="${findbugs.home}" output="xml" outputFile="target/findbugs.xml">
    <class location="${classes.dir}" /> 
    <auxClasspath refId="dependency.classpath" /> 
    <sourcePath path="src/main/java" />
  </findbugs>
</target>

Si vous utilisez Maven 2, vous n’avez pas besoin de garder une copie locale de l’installation de FindBugs. Vous avez juste besoin de configurer FindBugs dans la section reporting comme montré ici :

<reporting>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>findbugs-maven-plugin</artifactId>
      <version>2.3.1</version>
      <configuration>
        <effort>Max</effort>
        <xmlOutput>true</xmlOutput>
      </configuration>
    </plugin>
  </plugins>
</reporting>

Ou pour un projet Maven 3 :

<project>
  ...
  <build>
    ...
     <plugins>
       ...
       <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.0-beta-2</version>
          <configuration>
            <reportPlugins>
                <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>findbugs-maven-plugin</artifactId>
                  <version>2.3.1</version>
                  <configuration>
                    <effort>Max</effort>
                    <xmlOutput>true</xmlOutput>
                  </configuration>
                </plugin>
            </reportPlugins>
          </configuration>
        </plugin>
      </plugins>
    </build>
</project>

Dans les deux cas, vous pouvez générer vos rapports XML en lançant soit mvn site soit mvn findbugs:findbugs. Les rapports XML seront placés dans le répertoire target.

Au moment de la rédaction, il n’y a pas de prise en charge de FindBugs dans Gradle, donc vous devez invoquer le plugin Ant de FindBugs.

9.3.4. CodeNarc

CodeNarc est un outil d’analyse statique de code Groovy, similaire à PMD pour Java. Il vérifie le code source Groovy afin de trouver des défauts potentiels, des mauvais styles et pratiques de codage, du code trop complexe, et ainsi de suite. Des règles typiques incluent « Constant If Expression », « Empty Else Block », « GString As Map Key », et « Grails Stateless Service ».

Pour des projets basés sur Maven ou Ant, le plus simple est d'utiliser le plugin Ant de CodeNarc (un plugin Maven est en cours de développement au moment de l'écriture du livre). Une configuration typique de Ant pour l’utiliser avec Jenkins ressemblerait à ceci :

<taskdef name="codenarc" classname="org.codenarc.ant.CodeNarcTask"/>
<target name="runCodeNarc">
    <codenarc ruleSetFiles="rulesets/basic.xml,rulesets/imports.xml"
              maxPriority1Violations="0">

        <report type="xml">
            <option name="outputFile" value="reports/CodeNarc.xml" />
        </report>

        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </codenarc>
</target>

Vous pouvez intégrer CodeNarc dans un projet Grails simplement en installant le plugin CodeNarc :

$ grails install-plugin codenarc

Cela configurera CodeNarc pour analyser les fichiers Groovy dans le code de votre application Grails, aussi bien que dans les répertoires src/groovy et test.

Gradle 0.8 fournit aussi une prise en charge de CodeNarc dans le plugin de qualité de code, que vous pouvez le configurer dans vos builds comme montré ici :

apply plugin: 'code-quality'

Cela utilisera par défaut le fichier de configuration de CodeNarc suivant config/codenarc/codenarc.xml. Vous pouvez redéfinir cela avec la propriété codeNarcConfigFileName.

Vous pouvez générer les rapports CodeNarc en exécutant gradle codenarcMain ou, plus simplement, gradle check.