Code documentation
Development Tools
Code Structure
Techniques and Standards
Help and Web Site
How To
Functional Info
Background Info

JMRI Help:

Contents Index
Glossary FAQ

Donate to JMRI.org

JMRI Code: Static Analysis with SpotBugs

SpotBugs is a tool that can examine code to find possible problems. It does a "static analysis", looking through the code to find certain "known bad ideas": Things that are likely to cause occasional/intermittent problems, poor performance, etc. Because those kinds of problems are hard to find with tests, finding them by inspection is often the only realistic approach. Having a tool that can do those inspections automatically, so that they can be done every time something is changed, keeps the code from slowly getting worse and worse without anybody noticing until it's too late. See the spotbugs history page.

For more information on SpotBugs, see the SpotBugs home page.

We routinely run SpotBugs as part of our continuous integration process. You can see the detailed results of the most recent (daily) run along with recent progress trend online.

If SpotBugs is finding a false positive, a bug that's not really a bug, you can turn it off with an annotation such as:

import edu.umd.cs.findbugs.annotations;
@SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY", "Even tiny differences should trigger update")
Although Java itself considers it optional, we require the second "justification" argument. Explaining why you've added this annotation to suppress a message will help whoever comes after you and is trying to understand the code. It will also help make sure you properly understand the cause of the underlying bug report: Sometimes what seems a false positive really isn't. Annotations without a justification clause will periodically be removed. Note that the @SuppressFBWarnings contents in this form should be all on one line so that automated scanners can more reliably see it.

For clarity, this annotation also supports a form that lets you be more verbose:

@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY",
        justification = "OK to compare floats, as even tiny differences should trigger update")
This can make it easier to see what is what when quickly scanning through the code.

If you need to put more than one message type in an annotation, use array syntax:

@edu.umd.cs.findbugs.annotations.SuppressFBWarnings("{type1},{type2}","why both are needed")

There are also Java and SpotBugs annotations that can help it better understand your code. Sometimes they'll give it enough understanding of e.g. when a variable can be null, that it'll no longer make false-positive mistakes. For more on this, see the Java annotations and SpotBugs annotation pages.

The basics of annotations are covered in a Java annotations tutorial.

It can be useful to mark code with one of the following annotations so that SpotBugs does a good job of reasoning about it:

For more information about these annotations, please see links above and

We do not use these annotations:

Running SpotBugs

SpotBugs is automatically run by Travis CI as part of the Pull Request process, and by Jenkins as part of the daily builds. If new errors appear during CI, the job will (usually) fail. You can look at the Jenkins output to see where there are existing errors to work on. There's a very nice browser that lets you select items by type, by file, by package, etc, available by clicking on the "SpotBugs Warnings" button on the left of the Jenkins SpotBugs job.

To run it locally:

The ant spotbugs-ci target is similar, except it creates the output as a spotbugs.xml XML file that Jenkins uses to track which items have been fixed, etc.

The .spotbugs.xml file specifies which items in SpotBugs reports are suppressed for the JMRI project's usual reporting via Jenkins. The .spotbugs-check.xml file is a super-set which identifies which are significant enough to fail CI. (This is specified two places in the pom.xml, which is used by the scripts/travis.sh to control Maven running, which in turn is used by the .travis.yml file as the Travis run-time script)

Background

Simon White added FindBugs support to our Ant-based build chain during the development of JMRI 2.5.5.

Suppressed Warnings

We have turned off the routine SpotBugs checking of certain kinds of conditions:
RI_REDUNDANT_INTERFACES
This flags cases where a class implements an interface, and also inherits from a superclass that already implements that interface. This is redundant and untidy, but it can't cause the code to malfunction. We have enough of them that we've turned off the warning, and will come back to it some later time.
SIC_INNER_SHOULD_BE_STATIC_ANON, SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS
Static, as opposed to non-static, inner classes (classes defined within another class) take less space because they don't maintain references to the containing object. This warning suggests moving an anonymous (defined in-line to the code) inner class to a regular (defined not in-line) class, so it can be made static. Although probably a small improvement, it's a bit of work for a small improvement. We have enough of them that we've turned off the warning, and we'll come back to it later.
DM_CONVERT_CASE, DM_DEFAULT_ENCODING,
These cover aspects of handling characters in non-Western languages. Most of our String handling doesn't have anything to do with this, so it would be a huge number of annotations for only a small amount of I18N gain. Perhaps we'll revisit these later.
Malicious Code
This is a class of warnings centered around the idea that data and code methods shouldn't be too visible, especially when static. This is true, but JMRI isn't a hardened library that's being released into a world of people trying to break it, as these changes are a lower priority.
Se,SvVI
This is a large class of warnings associated with Java serialization. JMRI doesn't use serialization, and is unlikely to do so in the future, so we suppress these to raise the average quality of the issued warnings.
SLF4J_LOGGER_SHOULD_BE_NON_STATIC
This warning indicates that loggers should be non-static variables, but the JMRI standard is for the logger to be a static final variable, so every logger would be flagged if this were enabled.

Status and Counts

We've been making slow but continuous progress on removing these, although new Bug patterns are constantly being added to Spotbugs:

Main JMRI Codebase

Category JMRI 2.5.4 JMRI 4.13.3 JMRI 4.17.3 JMRI 5.5.3
Bad practice Warnings 164 13 0 0
Correctness Warnings 77 25 25
Experimental Warnings 7
Malicious code vulnerability Warnings 221 (disabled)
Multithreaded correctness Warnings 90 175 165 198
Performance Warnings 459 15 1 0
Style / Dodgy Code Warnings 304 406 127 295
Total 1322 636 293 518
Medium Priority 199 79 427
Low Priority 437 214 128
@SuppressFBWarnings
Lines
868 873

JMRI Test Code

JMRI 5.1.1
SB 4.7.0
JMRI 5.5.3
SB 4.7.3
High Priority Total 251 4
Medium Priority Total 4692 328
Low Priority Total 988 371
Total Warnings 5931 703
High Priority Density 0.95 0.01
Medium Priority Density 17.77 1.19
Low Priority Density 3.74 1.35
Total Density 22.46 2.55

Density - Defects per Thousand lines of non-commenting source statements.

A lot of work has gone into JMRI cycle to bring the bug count down. The Continuous Integration server runs SpotBugs twice a day, which helps developers see the results of their coding without having to set up to run SpotBugs themselves.

If you are checking code in to the JMRI repository, please check the CI server and make sure that your changes do not introduce new errors.