Comparative FindBugs (CFB): Introduction
Findbugs is a wonderful tool that performs a static analysis of Java code,
flagging lots of bugs, and with astoundingly few false alarms. If you haven't
encountered it before, I'll let David Hovemeyer and William Pugh (the folks who created
FindBugs)
explain it to you.
Go ahead and read
Using FindBugs in Anger
now. This page will be waiting for you when you're done.
A note about the picture: this shows Canadian Forces Base (a different kind of CFB) Uplands in 1952.
This location still serves air traffic today, but is better known as Ottawa International Airport (YOW).
I remember seeing the picture on display at the airport a few years ago, and found it online
here
with the following note:
“
Darrell Larose Lost Ottawa RCAF Station Uplands in 1952, aerial view shows 57 RCAF CF-86 Sabre jet fighters. The ground view shows a line of 21 CF-86's This was a staging for Operation Leap Frog to RCAF Station Grostenquin. The airbase at Uplands/Ottawa Airport was decommissioned as a base in 1996. ”
I believe that this image is in the Canadian public domain, given that
copyright has now expired
on any photographs from 1952 that were either subject to Crown Copyright or whose copyright was owned by a
corporation, and I find it hard to imagine a private individual having permission to photograph a military base.
If you are the copyright holder and want this picture removed, please contact me (user cejaekl site yahoo.com).
In an ideal world, we would all run FindBugs on every Java project from its inception, and keep
the count of FindBugs diagnostics at zero. (In theory, there is no difference between theory and
practice. In practice, there is.)
I don't know about you folks, but I tend to end up being responsible for code bases that have been
developed by other people, to other standards.
I put together CFB after I ran FindBugs on some code that I had
“inherited” in this way, and came up with 1025 “bugs” crying out for attention.
(No, I will not tell you which code… to protect the guilty.)
There are a few reasons why I couldn't bash those all into oblivion right away:
- The code had been in production for a while, and functioned acceptably.
It's hard to justify spending several weeks fixing bugs that have not affected anyone in production.
- Changing that much code at once would introduce significant churn, and the risk of introducing
one or more new bugs would have been unacceptably high. (The risk was higher than it might have been,
because the same code base had almost no unit tests… but that's a topic for another day.)
So, in this imperfect world, I opted for the imperfect solution of attempting gradual improvement
over the long term, instead of a “big bang” bug bashing party.
Trendy business speakers might call this approach “continuous improvement” or
改善, but I just call it pragmatic.
In short, I was resigned to having a large number of FindBugs messages in this code base for the
foreseeable future. This left me with a problem, though: I wanted to leverage FindBugs to make sure
that, as I introduced new code (and bug fixes), I didn't make things worse by adding code that FindBugs
complained about. (The Romans had a saying for this: primum non nocere—first, do no harm.)
I needed some way to identify any FindBugs diagnostic messages that were generated by code that had changed.
This is not as hard as it might seem. By running FindBugs on the code both before and after a commit
to the source code repository, and then computing the difference between the two FindBugs reports,
we get a list of bugs in three categories:
- new bugs that were not present before the commit
- fixed bugs that were present before the commit, but are no longer present after it
- old bugs that were present both before and after the commit
CFB is a tool to do just that: run FindBugs whenever commits are added to the source code repository,
generate reports on the differences between runs, and (optionally) send a notification email when
new “bugs” are introduced or old “bugs“ are fixed.
Installing and Using CFB
The source code for CFB is available under the GNU General Public Licence, version 3 or later.
The head-of-stream should, in general, be safe to use—I make a point of keeping it that way.
You can confirm the current status by checking my Jenkins server.
The code coverage report
(showing which bits of the code base should really have better unit tests)
is also available there, and you can even see the current
result of running CFB on itself.
I don't (currently) offer precompiled packages, but building it yourself is not hard.
-
Prerequisites:
- Install Java SDK 7 or later
- Install PostgreSQL, and create a user and database (tablespace) to hold CFB's analyses
-
Have the following Java libraries (.jar files) available on your system:
- Apache Commons CLI (commons-cli.jar)
- JUnit 4 (junit4.jar)
- PostgreSQL JDBC driver (postgresql.jar)
These can be installed on a Debian system by installing the following packages:
libcommons-cli-java junit4 libpostgresql-jdbc-java
-
Fetch the latest version of FindBugs from here.
You'll need a version of the code that includes the messages.xml file describing how to interpret the
XML reports that FindBugs produces, because CFB relies on this to produce its reports. Currently, this means
you'll want to download
findbugs-3.0.1.zip and
findbugs-3.0.1-source.zip,
extracting both archives to the same path (and allowing the second to overwrite files from the first).
-
Configure paths:
-
Edit setcp.sh, and verify that the absolute paths for commons-cli.jar, junit4.jar and postgresql.jar
point to the correct locations on your system. (I use Debian 8 “Jessie” and the paths given are correct for that system).
-
Set the environment variable FINDBUGS_HOME to point to the path where you've extracted the FindBugs code
(for example, /data/jenkins/findbugs-3.0.1).
-
(Optional) If you want to send email notifications when things get worse (or better), then edit config.properties.
Note that you'll need to have an old-style (non-authenticating) SMTP server available to accept the outgoing
mail messages.
-
(Optional) Test-compile the code, and run the unit tests (includes calculating code coverage, hence the name of the script):
$ ./cov.sh
-
Create a FindBugs Project (.fbp) file. This specifies the following things about your project:
- The path(s) to search for code that should be analyzed. These can be directories or .jar files.
- The path(s) to search for code that is referenced by the code being analyzed. For example, third-party libraries
that you use, but do not control.
- An .fbp file can also list path(s) to search for the source code of the .jar and .class files that are
being analyzed. This is useful if you run an analysis in the FindBugs GUI, but is not relevant when running
FindBugs via CFB.
You can create your .fbp file by launching the FindBugs GUI
(${FINDBUGS_HOME}/bin/findbugs), creating a new project, and then selecting
Save As... and specifying FindBugs project file as your output filetype.
But you may find it easier to create it by yourself. Here's a simple example that should be self-explanatory:
<Project projectName="CFB">
<Jar>/home/chris/prog/cfb/bin<Jar>
<AuxClasspathEntry>/usr/share/java/commons-cli.jar<AuxClasspathEntry>
<AuxClasspathEntry>/usr/share/java/junit4.jar<AuxClasspathEntry>
<AuxClasspathEntry>/usr/share/java/postgresql.jar<AuxClasspathEntry>
<SrcDir>/home/chris/prog/cfb/prod<SrcDir>
<Project
-
Run CFB to do an initial analysis:
$ ./go.sh -u dbUser -p dbPassword -d databaseName -f /path/to/your.fbp -o /where/to/write/report.html
If you are worried about the security implications of having your database password visible in the command-line, you can specify it in your
config.properties file (db.pass=dbPassword) instead.
See net.jaekl.CFB.Config for a full list of configuration settings.
To see a full list of command-line options, use:
$ ./go.sh --help
-
Continue developing your code.
-
Re-run CFB, to generate a comparison (delta) report on the differences since you last ran it on your project:
$ ./go.sh -u dbUser -p dbPassword -d databaseName -f /path/to/your.fbp -o /where/to/write/report.html