Friday, March 7, 2008

PMD in NetBeans

PMD statically scans Java code looking for potential problems like unused variables, duplicated code, and a lot of other things. PMD is integrated with NetBeans, with the possibility to select the rules to apply and to have an online scan while coding.

PMD also offers an Ant task: we can add the following lines to the build.xml file:

<target depends="init" name="pmd">
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpath="${libs.PMD.classpath}">
<pmd rulesetfiles="${pmd.dir}/my_pmd_rules.xml">
<formatter type="xml" tofile="pmd_report.xml"/>
<fileset dir="src/">
<include name="**/*.java"/>
</fileset>
</pmd>
</taskdef>
</target>

I have shown two different ways of referencing external files or libraries. For the PMD classpath I created a new library in NetBeans and used the variable libs.PMD.classpath that the IDE stored in the .netbeans/6.0/build.properties files in the user's home directory. For the ruleset files I added the pmd.dir property to the private/private.properties file.

Both approaches work, but the second is preferable if you have to build on a CI server, as the first method requires to add a library to the local NetBeans installation (you need to have one, as NetBeans build files use IDE-managed technologies); if you can only access the CI server via ssh this option is... not an option; you can always manually edit the build.properties files but IMHO it's a little awkward.

Modifying the private/private.properties files makes it easy to have different path on the developers' pc and on the CI server.

To define a set of rules you need to create an XML file as follows:

<?xml version="1.0"?>

<ruleset name="my_pmd_rules"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">

<description>
All the rules except the ones we don't like
</description>

<rule ref="rulesets/basic.xml"/>
<rule ref="rulesets/braces.xml"/>
<rule ref="rulesets/clone.xml"/>
<rule ref="rulesets/codesize.xml"/>
<rule ref="rulesets/controversial.xml">
<exclude name="AtLeastOneConstructor" />
</rule>
<rule ref="rulesets/coupling.xml"/>
<rule ref="rulesets/design.xml"/>
<rule ref="rulesets/finalizers.xml"/>
<rule ref="rulesets/imports.xml"/>
<rule ref="rulesets/j2ee.xml"/>
<rule ref="rulesets/junit.xml"/>
<rule ref="rulesets/javabeans.xml"/>
<rule ref="rulesets/naming.xml"/>
<rule ref="rulesets/optimizations.xml">
<exclude name="LocalVariableCouldBeFinal" />
</rule>
<rule ref="rulesets/scratchpad.xml"/>
<rule ref="rulesets/strictexception.xml"/>
<rule ref="rulesets/strings.xml"/>
<rule ref="rulesets/sunsecure.xml"/>
<rule ref="rulesets/typeresolution.xml"/>
<rule ref="rulesets/unusedcode.xml"/>

</ruleset>

In this file I choose all the available rulesets and simply excluded the rules I don't mean to use; the syntax is straightforward.

If we wanted to check the test classes' sources it would suffice to add the following lines

<fileset dir="test/">
<include name="**/*.java"/>
</fileset>

to the pmd task, even if it would be better to define a new Ant target (e.g. pmd-testclasses) as test classes should be validated against different rules.

No comments: