Updating FindBugs beyond 3.0.1 for Gradle
04 Jun 2017

The popular Java build tool Gradle comes with built-in support for FindBugs, a static code analysis tool for finding bugs in Java code. Recently FindBugs just stops working on my current project with a StackOverflowError. A short search reveals that this bug has been fixed already some time ago, however there is no official bug release available, so what do to?

One of the problems with this bug is that it doesn't give any hint about which part of my code caused the exception. Running gradle findbugsMain --stacktrace only shows

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':findbugsMain'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:98)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:68)
        ...
Caused by: java.lang.StackOverflowError
        at edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.walkCFG(ValueRangeAnalysisFactory.java:900)
        at edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.walkCFG(ValueRangeAnalysisFactory.java:914)
        at edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.walkCFG(ValueRangeAnalysisFactory.java:914)
        at edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.walkCFG(ValueRangeAnalysisFactory.java:914)
        at edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.walkCFG(ValueRangeAnalysisFactory.java:914)
        at edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.walkCFG(ValueRangeAnalysisFactory.java:914)
        ...

So it's not only that FindBugs rejects one class or method (which might be solved by some refactoring or exclusion trick), it stopped working for the entire project.

Looking at the project history shows that the latest version of FindBugs (3.0.1) has been released in March 2015, more than two years ago. The project has since then moved to GitHub and has released some previews (the last one in June 2016).

The offical way for using a new version of FindBugs in Gradle is by declaring the required version in the FindBugs configuration options of your build.gradle like this:

findbugs {
    toolVersion = '...'
}

This requires that the declared version is available in one of the configured repositories. Unfortunately none of the FindBugs prereleases made it to the typically used Maven Central repository, so just updating the version doesn't help.

The common solution for these kind of problems is to mavenize the required library, i.e. to provide it in a custom repository, for example in a project specific directory.

To do this we create the directory gradle/repository within our project.If you use the Gradle Wrapper you will already have a gradle subdirectory. Just create a new repositories directory within this existing directory. This is the root directory for our custom project repository.

From FindBug's prereleases we use the latest 3.0.2 preview and download one of the full build zip files, for example findbugs-3.0.2-dev-20160306-90c514b.zip. In the extracted archive we find the file lib/findbugs.jar, this is the jar we need to make available for Gradle.

The Maven repository format requires the jars to reside in a directory that represents the group ID (com.google.code.findbugs), artifact ID (findbugs), and version of the artifact. So in our case the directory is com/google/code/findbugs/findbugs/version/.See group ID and artifact ID of the released 3.0.1 version. Let's use 3.0.2-preview2 as the version, so we have to copy the the extracted lib/findbugs.jar to com/google/code/findbugs/findbugs/3.0.2-preview2/findbugs-3.0.2-preview2.jar in gradle/repository. Furthermore we have to provide a POM file for the new library so that Gradle knows of all required library dependencies. I tried with the 3.0.1 POM and it worked without problems. The only things we have to do is to save this file with the new name findbugs-3.0.2-preview2.pom in the same directory as the jar file and change the version within the file (line 12) from 3.0.1 to 3.0.2-preview2 as well.

We are nearly done. Gradle doesn't know automatically about this new repository, so we have to declare it as follows in our build.gradle:

repositories {
    maven {
        name 'Custom Project Repository'
        url "${rootProject.projectDir}/gradle/repository"
    }
    // other repositories, e.g. mavenCentral
}

Note: this belongs to the project repositories configuration, not to the buildscript repositories.

Finally we can declare the new version and FindBugs should work again as expected:

findbugs {
    toolVersion = '3.0.2-preview2'
}

The demonstrated solution can be adapted to other libraries as well. Whenever you need something as a Gradle (or Maven) dependency that is not available in a public repository, you can create your own local dependency either from the source code or from pre-built versions. Just take (or build) a jar, create a matching pom file, and put both into your repository.

However, using a local directory as repository is probably only suitable for smaller or open source projects. In an enterprise environment you typically want to upload both jar and corresponding pom into your enterprise repository, for example Nexus or Artifactory. This will provide the mavenized library version to all of your in-house projects.

I'm interested in your feedback. Send me a mail or ping me on twitter.