Você está na página 1de 10

Copyright IBM Corporation 2004 Trademarks

FindBugs, Part 1: Improve the quality of your code Page 1 of 10


FindBugs, Part 1: Improve the quality of your code
Why and how to use FindBugs
Chris Grindstaff (chris@gstaff.org)
Software Engineer
IBM
25 May 2004
Static analysis tools promise to find existing bugs in your code without requiring much effort
on the part of the developer. Of course, if you've been programming for long, you know those
promises don't always pan out. Even so, good static analysis tools are a valuable addition to
your toolbox. In this first of a two-part series, Senior Software Engineer Chris Grindstaff looks at
how FindBugs can help improve the quality of your code and eliminate bugs lying in wait. Don't
miss Part 2 of this series to get the final part of the story.
One of the problems with code quality tools is that they tend to overwhelm developers with
problems that aren't really problems -- that is, false positives. When false positives occur,
developers learn to ignore the output of the tool or abandon it altogether. The creators of
FindBugs, David Hovemeyer and William Pugh, were sensitive to this issue and strove to reduce
the number of false positives they report. Unlike other static analysis tools, FindBugs doesn't focus
on style or formatting; it specifically tries to find real bugs or potential performance problems.
What is FindBugs?
FindBugs is a static analysis tool that examines your class or JAR files looking for potential
problems by matching your bytecodes against a list of bug patterns. With static analysis tools,
you can analyze software without actually running the program. Instead the form or structure of
the class files are analyzed to determine the program's intent, often using the Visitor pattern (see
Resources). Figure 1 shows the results of analyzing an anonymous project (its name has been
withheld in order to protect the horribly guilty):
developerWorks ibm.com/developerWorks/
FindBugs, Part 1: Improve the quality of your code Page 2 of 10
Figure 1. FindBugs UI
Let's take a look at some of the problems that FindBugs can detect.
Examples of problems found
The following list doesn't include all the problems FindBug might find. Instead, I've focused on
some of the more interesting ones.
Detector: Find hash equals mismatch
This detector finds several related problems, all centered around the implementation of equals()
and hashCode(). These two methods are very important because they're called by nearly all of the
Collections-based classes -- List, Maps, Sets, and so on. Generally, this detector finds two different
types of problems -- when a class:
Overrides Object's equals() method, but not its hashCode or vice-versa.
Defines a co-variant version of the equals() or compareTo() method. For example, the Bob
class defines its equals() method as boolean equals(Bob), which overloads the equals()
method defined in Object. Because of the way the Java code resolves overloaded methods
at compile-time, the version of the method defined in Object will almost always be the one
used at runtime, not the one you defined in Bob (unless you explicitly cast the argument to
your equals() method to type Bob). As a result, when one of the instances of this class is put
ibm.com/developerWorks/ developerWorks
FindBugs, Part 1: Improve the quality of your code Page 3 of 10
into any of the collection classes, the Object.equals() version of the method will be used, not
the version defined in Bob. In this case, the Bob class should define an equals() method that
accepts an argument of type Object.
Detector: Return value of method ignored
This detector looks for places in your code where the return value of a method is ignored when it
shouldn't be. One of the more common instances of this scenario is found when invoking String
methods, such as in Listing 1:
Listing 1. Example of ignored return value
1 String aString = "bob";
2 b.replace('b', 'p');
3 if(b.equals("pop"))
This mistake is pretty common. At line 2, the programmer thought he'd replaced all of the b's in the
string with p's. He did, but he forgot that strings are immutable. All of these types of methods return
a new string, never changing the receiver of the message.
Detector: Null pointer dereference and redundant comparisons to null
This detector looks for two types of problems. It looks for cases where a code path will or
could cause a null pointer exception, and it also looks for cases in which there is a redundant
comparison to null. For example, if both of the compared values are definitely null, they're
redundant and may indicate a coding mistake. FindBugs detects a similar problem when it's able to
determine that one of the values is null and the other one isn't, as shown in Listing 2:
Listing 2. Null pointer examples
1 Person person = aMap.get("bob");
2 if (person != null) {
3 person.updateAccessTime();
4 }
5 String name = person.getName();
In this example, if the Map on line 1 does not contain the person named "bob," a null pointer
exception will result on line 5 when the person is asked for his name. Because FindBugs doesn't
know if the map contains "bob" or not, it will flag line 5 as a possible null pointer exception.
Detector: Field read before being initialized
This detector finds fields that are read in constructors before they're initialized. This error is often
caused by mistakenly using a field's name instead of a constructor argument -- although not
always, as Listing 3 shows:
Listing 3. Reading a field in a constructor before it's initialized
1 public class Thing {
2 private List actions;
3 public Thing(String startingActions) {
4 StringTokenizer tokenizer = new StringTokenizer(startingActions);
5 while (tokenizer.hasMoreTokens()) {
6 actions.add(tokenizer.nextToken());
7 }
8 }
9 }
developerWorks ibm.com/developerWorks/
FindBugs, Part 1: Improve the quality of your code Page 4 of 10
In this example, line 6 will cause a null pointer exception because the variable actions has not
been initialized.
These examples are only a small sampling of the types of problems that FindBugs detects (see
Resources for more). At the time of this writing, FindBugs comes with a total of 35 detectors.
Getting started with FindBugs
To run FindBugs, you will need a Java Development Kit (JDK), version 1.4 or higher, although
it can analyze the class files created by older JDKs. The first thing to do is download and install
the latest release of FindBugs -- currently 0.7.1 (see Resources). Fortunately, the download and
installation is pretty straightforward. After downloading the zip or tar, unzip it into a directory of your
choosing. That's it -- the install is finished.
Now that it's installed, let's run it on a sample class. As is often the case with articles, I will speak
to the Windows users and assume that those of the Unix persuasion can deftly translate and follow
along. Open a command prompt and go to the directory in which you installed FindBugs. For me,
that's C:\apps\FindBugs-0.7.3.
In the FindBugs home directory, there are a couple of directories of interest. The documentation is
located in the doc directory, but more important for us, the bin directory contains the batch file to
run FindBugs, which leads me to the next section.
Running FindBugs
Like most tools these days, you can run FindBugs in multiple ways -- from a GUI, from a command
line, using Ant, as an Eclipse plug-in, and using Maven. I'll briefly mention running FindBugs from
the GUI, but I'll primarily focus on running it from Ant and the command line. Partly that's because
the GUI hasn't caught up with all of the command line options. For example, currently you can't
specify filters to include or exclude particular classes in the UI. But the more important reason is
because I think FindBugs is best used as an integrated part of your build, and UIs don't belong in
automated builds.
Using the FindBugs UI
Using the FindBugs UI is straightforward, but a couple of points deserve some elaboration. As
Figure 1 demonstrates, one of the advantages of using the FindBugs UI is the description provided
for each type of detected problem. Figure 1 shows the description for the bug Naked notify in
method. Similar descriptions are provided for each bug pattern, which is extremely useful when
you're first becoming acquainted with the tool. Equally useful is the Source code tab in the lower
pane of the window. If you tell FindBugs where to find your source, it will highlight the offending line
of code when you switch to the appropriate tab.
It's also important to mention that if you choose xml as your output option when running FindBugs
as an Ant task or from the command line, you can load the results of a previous run into the UI.
Doing so is a great way to leverage the advantages of the command-line-based tooling and the UI
tooling at the same time.
ibm.com/developerWorks/ developerWorks
FindBugs, Part 1: Improve the quality of your code Page 5 of 10
Running FindBugs as an Ant task
Let's take a look at how to use FindBugs from an Ant build script. First copy the FindBugs Ant
task to Ant's lib directory so that Ant is made aware of the new task. Copy FIND_BUGS_HOME\lib
\FindBugs-ant.jar to ANT_HOME\lib.
Now take a look at what you need to add to your build script to use the FindBugs task. Because
FindBugs is a custom task, you'll need to use the taskdef task so that Ant knows which classes to
load. Do that by adding the following line to your build file:
<taskdef name="FindBugs" classname="edu.umd.cs.FindBugs.anttask.FindBugsTask"/>
After defining taskdef, you can refer to it by its name, FindBugs. Next you'll add a target to the
build that uses the new task, as shown in Listing 4:
Listing 4. Creating a FindBugs target
1 <target name="FindBugs" depends="compile">
2 <FindBugs home="${FindBugs.home}" output="xml" outputFile="jedit-output.xml">
3 <class location="c:\apps\JEdit4.1\jedit.jar" />
4 <auxClasspath path="${basedir}/lib/Regex.jar" />
5 <sourcePath path="c:\tempcbg\jedit" />
6 </FindBugs>
7 </target>
Let's take a closer look at what's going on in this code.
Line 1: Notice that the target depends on the compile. It's important to remember that FindBugs
works on class files, not source files, so making the target depend on the compile target ensures
that FindBugs will be running across the up-to-date class files. FindBugs is flexible about what it
will accept as input, including a set of class files, JAR files, or a list of directories.
Line 2: You must specify the directory that contains FindBugs, which I did using an Ant property
like this:
<property name="FindBugs.home" value="C:\apps\FindBugs-0.7.3" />
The optional attribute output specifies the output format that FindBugs will use for its results.
The possible values are xml, text, or emacs. If no outputFile is specified, then FindBugs prints
to standard out. As mentioned previously, the XML format has the added advantage of being
viewable within the UI.
Line 3: The class element is used to specify which set of JARs, class files, or directories you want
FindBugs to analyze. To analyze multiple JARs or class files, specify a separate class element
for each. The class element is required unless the projectFile element is included. See the
FindBugs manual for more details.
Line 4: You list your application's dependencies by using the nested element auxClasspath. These
are classes that your application needs, but you don't want FindBugs to analyze. If you don't list
developerWorks ibm.com/developerWorks/
FindBugs, Part 1: Improve the quality of your code Page 6 of 10
your application's dependencies, FindBugs will still analyze your classes as well as it can, but it
will complain when it is unable to find one of the missing classes. as with the class element, you
can specify multiple auxClasspath elements in the FindBugs element. The auxClasspath element
is optional.
Line 5: If the sourcePath element is specified, the path attribute should indicate a directory that
contains your application's source code. Specifying the directory allows FindBugs to highlight the
source code in error when viewing the XML results in the GUI. This element is optional.
That covers the basics. Let's fast forward several weeks.
Filters
You've introduced FindBugs to your team and have been running it as a part of your hourly/nightly
build process. As the team has become more acquainted with the tool, you've decided that some
of the bugs being detected aren't important to your team, for whatever reason. Perhaps you don't
care if some of your classes return objects that could be modified maliciously -- or maybe, like
JEdit, you have a real honest-to-goodness, legitimate reason to invoke System.gc().
You always have the option of "turning off" a particular detector. On a more granular level, you
could exclude certain detectors from finding problems within a specified set of classes or even
methods. FindBugs offers this granular control with exclude and include filters. Exclude and
include filters are currently supported only in the command-line or Ant versions of FindBugs.
As the name implies, you use exclude filters to exclude the reporting of certain bugs. The less
popular, but still useful, include filters can be used to report targeted bugs only. The filters are
defined in an XML file. They may be specified at the command-line with an exclude or include
switch or by using the excludeFilter and includeFilter in your Ant build file. In the examples
below, assume that the exclude switch was used. Also note in the discussion below that I use
"bugcode," "bug," and "detector" somewhat interchangeably.
Filters can be defined in a variety of ways:
Filters that match one of your classes. These filters could be used to ignore all problems
found in a particular class.
Filters that match particular bugcodes in one of your classes. These filters could be used to
ignore some bugs found in a particular class.
Filters that match a set of bugs. These filters could be used to ignore a set of bugs across all
of the analyzed classes.
Filters that match particular methods in one of the analyzed classes. These filters could be
used to ignore all bugs found in a set of methods for a class.
Filters that match some bugs found in methods in one of the analyzed classes. You could use
these filters to ignore some of the bugs found in a particularly buggy set of methods.
That's all there is to getting started. See the FindBugs documentation for more details on additional
ways the FindBugs task can be customized. Now that we know how to set up a build file, let's take
a closer look at integrating FindBugs into your build process.
ibm.com/developerWorks/ developerWorks
FindBugs, Part 1: Improve the quality of your code Page 7 of 10
Integrating FindBugs into your build process
You have several options when it comes to integrating FindBugs into your build process. You can
always execute FindBugs from the command line, but more than likely you're already using Ant for
your build, so using the FindBugs Ant task is the most natural. Because we've covered the basics
of using the FindBugs Ant task earlier, I'll cover some of the reasons you should add FindBugs to
your build process and discuss a few of the issues you may run into.
Why should I integrate FindBugs into my build process?
One of the first questions that's often asked is why would I want to add FindBugs into my build
process? While there are a host of reasons, the most obvious answer is that you want to make
sure problems are detected as soon as your build is run. As your team grows and you inevitably
add more junior developers to the project, FindBugs can act as a safety net, detecting identified
bug patterns. I want to reiterate some of the sentiment expressed in one of the FindBugs papers.
If you put enough developers together, then you're going to have bugs in your code. Tools like
FindBugs certainly won't find all the bugs, but they'll help find some of them. Finding some now is
better than your customers finding them later -- especially when the cost of incorporating FindBugs
into your build process is so low.
Once you've stabilized which filters and classes to include, there's a negligible cost for running
FindBugs, with the additional benefit that it detects new bugs. The benefit is probably even greater
if you've written application-specific detectors.
Generate meaningful results
It's important to recognize that this cost/benefit analysis is only valid so long as you don't generate
a lot of false positives. In other words, the tool's value is diminished if, from build to build, it is no
longer simple to determine whether new bugs have been introduced. The more automated your
analysis can be, the better. If fixing bugs means having to wade through a lot of irrelevant detected
bugs, then you'll likely not use the tool very often, or at least not make good use of it.
Decide which set of problems you don't care about and exclude them from the build. Otherwise,
pick a small set of detectors that you do care about and run just those. Another option would be to
exclude sets of detectors from individual classes, but not others. FindBugs offers a lot of flexibility
with its use of filtering, which should help you generate results that are meaningful to your team,
which leads us to the next section.
Determine what you will do with the results of FindBugs
It may seem obvious, but I've worked with more teams than you might imagine who apparently
add FindBugs-like tools to their builds for the pure joy of it. Let's explore this question in a bit
more detail -- what should you do with your results? It's a difficult question to answer specifically
because it has a lot to do with how your team is organized, how you deal with code ownership
issues, and so on. However, here are some guidelines:
You may want to consider adding the FindBugs results to your source code management
(SCM) system. The general rule of thumb is don't put build artifacts into your SCM system.
developerWorks ibm.com/developerWorks/
FindBugs, Part 1: Improve the quality of your code Page 8 of 10
However, in this particular case, breaking the rule may be the right thing to do because it
allows you to monitor the quality of the code over time.
You may choose to convert the XML results file into an HTML report that you post on your
team's Web site. The conversion can be carried out with an XSL stylesheet or script. Check
the FindBugs Web site or mailing list for examples (see Resources).
Tools like FindBugs can often turn into political weapons used to bludgeon teams or
individuals. Try not to encourage that or let it happen -- remember, it's just a tool that's meant
to help you improve the quality of your code. With that inspirational aside, in next month's
installment I'll show you how to write custom bug detectors.
Summary
I encourage you to try some form of static analysis tool on your code, whether it's FindBugs, PMD,
or something else. They're valuable tools that can find real problems, and FindBugs is one of
the better ones for eliminating false positives. In addition, its pluggable architecture provides an
interesting test bed for writing invaluable application-specific detectors. In Part 2 of this series, I'll
show you how to write custom detectors to find application-specific problems.
ibm.com/developerWorks/ developerWorks
FindBugs, Part 1: Improve the quality of your code Page 9 of 10
Resources
Download the latest version of FindBugs.
The FindBugs site provides a full list of bugs with descriptions.
Read more information about the Visitor pattern.
Here's more information on the Byte Code Engineering Library.
PMD is another powerful open-source static code analysis tool that lets you write custom
rules. It's not quite as powerful as FindBugs because it analyzes the Java files instead of
class files, but well worth checking out.
Two authors have tried to outline a set of best practices for avoiding the type of problems
that FindBugs can detect: Joshua Bloch's Effective Java: Programming Language Guide
(Addison-Wesley, 2001) and Peter Haggar's Practical Java: Programming Language Guide
(Addison-Wesley, 2000) .
In "The future of software development" (developerWorks, June 2003), Eric Allen discusses
some of the current trends in software development and predicts what they may lead to in the
coming years. Check out the rest of Eric's Diagnosing Java code columns for common bug
patterns.
Find hundreds more Java technology resources on the developerWorks Java technology
zone.
Browse for books on these and other technical topics.
developerWorks ibm.com/developerWorks/
FindBugs, Part 1: Improve the quality of your code Page 10 of 10
About the author
Chris Grindstaff
Chris Grindstaff is a Senior Software Engineer at IBM in Research Triangle Park,
North Carolina. Chris wrote his first program at the age of 7, when he convinced his
grade school teacher that "typing" sentences would be just as onerous a punishment
as writing them by hand. Chris is currently interested in a variety of open-source
projects. He has worked extensively with Eclipse and authored several popular
Eclipse plug-ins, which may be found on his Web site. You can contact Chris at
cgrinds@us.ibm.com or chris@gstaff.org.
Copyright IBM Corporation 2004
(www.ibm.com/legal/copytrade.shtml)
Trademarks
(www.ibm.com/developerworks/ibm/trademarks/)

Você também pode gostar