Adding Spotbugs support to a NetBeans Platform Application

In the past week, I have been integrating FindBugs in my company NetBeans based Rich Client Application. FindBugs is a famous tool for static code analysis; it looks at your jars to find bugs, bad practices, unclosed streams … FindBugs did its job, and spotted many problems in our code, later I discovered this it was forked into a new project called SpotBugs that seems well maintained.

We wanted a complete report for all our modules, so I had to run FindBugs on them all, merge the results and convert them to HTML. All these tasks were done in ANT scripts because ANT is our build system and it was automated inside Jenkins.

I made a small sample project on GitHub to show what I did.

The first step was to create a separated ANT build.xml file called spotbugs.xml, that would be imported by every module because in this way I reduce duplicated code.

<?xml version="1.0" encoding="UTF-8"?>
<project name="changeme" default="all" basedir=".">

 <!-- where SpotBugs is located -->
 <property name="spotbugs.home" value="${basedir}/../../libs/spotbugs" />

 <path id="spotbugs.classpath">
 <fileset dir="${spotbugs.home}/lib">
 <include name="**/*.jar" />
 </fileset>
 </path>

 <taskdef name="spotbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask">
 <classpath refid="spotbugs.classpath" />
 </taskdef>

 <target name="spotbugs" depends="jar">
 <echo message="${spotbugs.home}" />

 <property name="output.folder" value="${basedir}/../../output" />

 <!-- I launch SpotBugs -->
 <spotbugs home="${spotbugs.home}"
 output="xml"
 outputFile="${output.folder}/${code.name.base}_spotbugs.xml"
 >
 <sourcePath path="${basedir}/src/java" />
 <auxClasspath path="${module.classpath}" />
 <class location="${suite.dir}/build/cluster/modules/${module.jar.basename}" />
 </spotbugs>
 </target>

</project> 

Writing the task was easy because SpotBugs/FindBugs’ ANT task is well documented, and I had just to plug the right variables that were generated by the NetBeans build system thanks to the dependence to the jar task.

The output format must be XML so I can merge the results later and it is better to pass the path used to build the module (auxClasspath). The class tag points to the generated jar file.

Inside each module’s build.xml I added this line to import the file:

<import file="${basedir}/../../spotbugs.xml" />

The next step was to launch the ANT target on each project, collect the results and convert them into HTML. I modified the module suite’s build.xml to perform this.

<property name="spotbugs.home" value="${basedir}/../libs/spotbugs" />

 <path id="spotbugs.classpath">
 <fileset dir="${spotbugs.home}/lib">
 <include name="**/*.jar" />
 </fileset>
 </path>

 <taskdef name="unionBugs" classname="edu.umd.cs.findbugs.anttask.UnionBugs">
 <classpath refid="spotbugs.classpath" />
 </taskdef>

 <taskdef name="convertXmlToText" classname="edu.umd.cs.findbugs.anttask.ConvertXmlToTextTask">
 <classpath refid="spotbugs.classpath" />
 </taskdef>


 <target name="spotbugs" depends="-init">
 <!-- I define some variable -->
 <property name="output.folder" value="${basedir}/../output" />
 <property name="xml.output" value="${output.folder}/spotbugs.xml" />
 <property name="html.output" value="${output.folder}/spotbugs.html" />

 <!-- I remove the previous files-->
 <delete failonerror="false">
 <fileset dir="${output.folder}" includes="**/*"/>
 </delete>

 <mkdir dir="${output.folder}" />

 <subant buildpath="${modules.sorted}" target="spotbugs" inheritall="false" inheritrefs="fakse" failonerror="false" />

 <unionBugs to="${xml.output}" >
 <fileset dir="${output.folder}">
 <include name="**/*_spotbugs.xml" />
 </fileset>
 </unionBugs>

 <convertXmlToText
 classpathref="spotbugs.classpath"
 input="${xml.output}"
 outputfile="${html.output}"
 format="html:fancy-hist.xsl"
 />

 </target>

In the script above, first I define some useful variables, then I delete the old output, finally, there is the most important part: the subant task that invokes SpotBugs on all modules. The modules.sorted variable has the list of all suite modules and it is very convenient in this case.

The unionBugs task merges all the .xml files generated by the previous subant task into a single XML file that is converted to HTML by the convertXmlToText task.

The convertXmlToText task lets specify how the HTML file will be generated with the format attribute; if we take a look at the documentation we can use specify 4 type of formatting (plain.xsl, default.xsl, fancy.xsl, fancy-hist.xsl) or use our own.

Running the spotbugs target in the suite will generate a spotbugs.html file inside the output folder at the same level of the suite.

if you open it will look like this:

 

A small note about SpotBugs: SpotBugs is a fork of FindBugs, so if you point to the FindBugs home instead of the SpotBugs one everything should work the same way, the class packages are also the same. However, I had some problems building SpotBugs, I could clone the git repository and launch Gradle to generate its jars, but I had to manually rename them and put them in a proper directory structure to make them work. I have included them in the GitHub repository to make your life easier.

 

 

 

 

 

Leave a Comment

Your email address will not be published. Required fields are marked *