Monthly Archives: February 2015

Configure FindBugs in Multi-Module Maven Builds

FindBugs is similarly configured for multi-module Maven builds as the Checkstyle and PMD plugins. As in the Checkstyle and PMD example, the configuration example below shows how to set up a “developer build” as the default configuration, allowing a developer to execute FindBugs with the “mvn clean install” command and create data for reports instead of failing the build. As usual, we will use a Maven profile for release builds, so that developers can create local reports, and the CI/CD system can fail the build in case of violations.

The benefits of using a centralized FindBugs filter configuration are in my opinion somewhat limited, but it is still possible to create and use such a configuration artifact (jar resource file) in the Maven FindBugs plugin configuration. The “dependency” section pulls in the configuration artifact, and the “excludeFilterFile” parameter references the FindBugs rules configuration in the resource configuration artifact. This filename must match the actual rules filename used in the dependency. The configuration settings are parameterized with Maven build variables, but this example also works with hardcoded settings.

<build>
  ...
  <plugins>
  ...
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>findbugs-maven-plugin</artifactId>
      <version>${findbugsMavenPluginVersion}</version>
      <configuration>
        <xmlOutput>true</xmlOutput>
        <effort>Max</effort>
        <threshold>Low</threshold>
        <excludeFilterFile>findbugs-exclude.xml</excludeFilterFile>
        <failOnError>false</failOnError>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>${buildToolsGroupId}</groupId>
          <artifactId>${buildToolsArtifactId}</artifactId>
          <version>${buildToolsVersion}</version>
        </dependency>
      </dependencies>
      <executions>
        <execution>
        <phase>validate</phase>
        <goals>
          <goal>check</goal>
        </goals>
        </execution>
      </executions>
    </plugin>
  ...
  </plugins>
  ...
</build>

The automated continuous integration (CI) build uses the same configuration with the “failOnError” parameter set to “true”:

<profiles>
  ...
  <profile>
    <id>release</id>
    ...
    <build>
      <plugins>
      ...
        <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>findbugs-maven-plugin</artifactId>
          <version>${findbugsMavenPluginVersion}</version>
          <configuration>
            <xmlOutput>true</xmlOutput>
            <effort>Max</effort>
            <threshold>Low</threshold>
            <excludeFilterFile>findbugs-exclude.xml</excludeFilterFile>
            <failOnError>true</failOnError>
          </configuration>
          <dependencies>
            <dependency>
              <groupId>${buildToolsGroupId}</groupId>
              <artifactId>${buildToolsArtifactId}</artifactId>
              <version>${buildToolsVersion}</version>
            </dependency>
          </dependencies>
          <executions>
            <execution>
              <phase>validate</phase>
              <goals>
                <goal>check</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        ...
      </plugins>
      ...
    </build>
    ...
  </profile>
  ...
</profiles>

Reporting with the “mvn site” command is enabled by including the plugin configuration once more in the POM file’s “reporting” section:

<reporting>
  ...
  <plugins>
  ...
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>findbugs-maven-plugin</artifactId>
      <version>${findbugsMavenPluginVersion}</version>
      <configuration>
        <xmlOutput>true</xmlOutput>
        <effort>Max</effort>
        <threshold>Low</threshold>
        <excludeFilterFile>findbugs-exclude.xml</excludeFilterFile>
      </configuration>
    </plugin>
  ...
  </plugins>
  ...
</reporting>

See https://github.com/mbeiter/util for an example on how to configure the Maven FindBugs Plugin as described in this post for a multi-module build.

Configure the Codehaus License Maven Plugin in Multi-Module Builds

There are two major options to automatically add license headers to source files:

  • Mojo License Maven plugin from codehaus
  • Mycila Maven License plugin from mycila

I think that the “mycila” plugin is easier to use than the codehaus plugin. However, it seems as if the codehaus plugin is the go-forward tool, despite its problems and quirks. I found it very difficult to make the mojo Maven license plugin work in a way so that I could use my own license file in a centralized configuration artifact (i.e. a dedicated jar file holding the custom license file).

The good thing about the mojo Maven license plugin is that, in addition to the source code license headers, it can also create (although unfortunately not reliably update) the project license file.

As a first step, add a “license” section on the project level to document the license choice. It is commonly recommended to use a compatible name and identifier for the license. For open source projects, most licenses already have an SPDX name identifier assigned.

Once the license is defined and chosen, configure it for the project as follows:

  1. Create a centralized configuration artifact, with a “resources” directory containing a “licenses” parent folder, and within the “licenses” folder, a folder named with the license identifier (e.g. the SPDX identifier). Only use lowercase letters for this directory name, and do not use spaces. This directory contains two files: One for the license header that goes into each file (“header.txt.ftl”), the other one for the license that goes into each Maven module’s root folder (“license.txt.ftl”).
    The license header file contains the body to be used in a generic template that comes with the plugin, while the license file that will be placed into the Maven root folder actually is a template that allows substitution. The content from the header file will later on be completed with a package description line and a copyright line, while the Maven root folder license file will not go through this transformation. For some reason, the header file cannot contain blank lines, as blank lines in this file will lead to a misleading error message.
  2. Create a “licenses.properties” file in the license parent folder in the resources folder created in step 1. This file must contain a mapping of the license folder name (aka the license name or SPDX identifier) to a human readable name. This makes the license available to the plugin (the name is displayed, referencing the identifier, which by convention identifies the folder that holds the license files).
  3. Build and release the configuration artifact
  4. Configure the license maven plugin. Reference the folder name in the resource folder of the configuration artifact from step 1 as the <licenseName>. The <licenseResolver> tag must contain the path to the folder from step 1. As an example, if the license is stored in ./src/main/resources/license/mylicense, then “mylicense” is the licenseName and “license” is the licenseResolver. Include the dependency to the configuration artifact as a dependency of the License Maven plugin, and complete the configuration of the plugin as needed (e.g. if it should fail the build in case of missing / incorrect licenses, if the plugin is allowed to modify the license section in files, which files to ignore, etc).
  5. Add the following required project meta information:
    1. <inceptionYear>
      This is used in conjunction with the current year as the value for “${copyright.years}” in the Maven module folder license file, and it will automatically be inserted in the file headers before the body of the header license notice
    2. <licenses> with <license> block containing name, URL, and distribution (set to “repo” in this example)
    3. Name and URL of the organization holding the copyright
      This is used as the value for ${copyright.holder} in the Maven module folder license file, and it will automatically be inserted in the file headers before the body of the header license notice after the “${copyright.years}” value
  6. For each Maven module, add a ./src/license directory, with a “licenseDescription.ftl” file. This file should contain a single-line module specific description, which will be prefixed to all headers in the respective module. The content of this file is added into the file header right before the copyright years, copyright holder, and header license body.
    This file must not contain any blank lines. Some IDEs add a newline at the end of this file when editing / saving it. The license plugin removes this newline when it updates the license headers (good!), but it is not smart enough to also do this during the subsequent checks when it tries to find out if a file header needs to be changed. Consequently, every subsequent build will fail with an error of “file headers need to be updated”, but an update of the headers will not fix the issue. The solution is to remove the newline in this template file.

This is how the overall configuration in the parent POM should look like. Note that the name in the organization contains an html entities encoded representation of the email address to make it work with the License Maven plugin. Some of the configuration values are parameterized, but the example works equally well for hardcoded values:

<project>
  ...
  <inceptionYear>2014</inceptionYear>
  ...
  <licenses>
    <license>
      <name>BSD 3-clause Revised License</name>
      <url>LICENSE.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  ...
  <organization>
    <name>Your Name &lt;your@email.com&gt;</name>
    <url>http://www.your.website.com</url>
  </organization>
  ...
  <build>
    ...
    <plugins>
      ...
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>license-maven-plugin</artifactId>
        <version>${licenseMavenPluginVersion}</version>
        <configuration>
          <licenseName>3-bsd-mb</licenseName>
          <licenseResolver>classpath://license</licenseResolver>
          <descriptionTemplate>${basedir}/src/license/licenseDescription.ftl</descriptionTemplate>
          <canUpdateCopyright>true</canUpdateCopyright>
          <canUpdateDescription>true</canUpdateDescription>
          <addJavaLicenseAfterPackage>false</addJavaLicenseAfterPackage>
          <failOnMissingHeader>true</failOnMissingHeader>
          <failOnNotUptodateHeader>true</failOnNotUptodateHeader>
          <excludes>
            <exclude>**/licenseDescription.ftl</exclude>
            <exclude>**/package-info.java</exclude>
            <exclude>**/*.txt</exclude>
            <exclude>**/*.properties</exclude>
          </excludes>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>${buildToolsGroupId}</groupId>
            <artifactId>${buildToolsArtifactId}</artifactId>
            <version>${buildToolsVersion}</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <phase>validate</phase>
            <goals>
              <goal>check-file-header</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
    ...
  </build>
</project>

The build tools project at https://github.com/mbeiter/util/tree/master/build-tools contains an example for a centralized configuration artifact holding a custom 3 clause BSD license configuration that can be used with this plugin. See https://github.com/mbeiter/util for an example on how to configure the License Maven plugin as described in this post for a multi-module build, using the license from the configuration artifact.

Usage:

  • To check all available licenses (note that custom licenses do not show up here, even if they are available!):
    # mvn license:license-list
  • To check if all files have a valid license header (this function can also be used to check which files would be modified when a header is added automatically):
    # mvn license:check-file-header
  • To add the license header to all files:
    # mvn license:update-file-header
  • To create the project license file (for some reason, an update to existing files does not work):
    # mvn license:update-project-license