You are using PMD in your project. After a while, you start customizing your ruleset as described in best practices and how to make a ruleset. But the project grows and grows and it soon consists of several sub modules or even separate projects maybe even living in separate source repositories. The how can you share the ruleset, so that it can be applied to each project?

“Build-tools pattern”

Now it’s time for the “build-tools pattern”. It’s described as the recommended way for sharing checkstyle configurations in a multi-module maven build: Multimodule Configuration. The same solution can be applied for PMD.

The basic idea is to create a separate artifact that is the home of the common configuration for build-tools such as checkstyle and PMD. This artifact is called “build-tools”. It can be released independently of the other projects, that use it. This allows for updating the ruleset, but not enforcing every project to upgrade at once - each project depends on a specific version of the build-tools artifact and so can decide on its own when the best time is to upgrade.

The build-tools artifact

The first step is to create a new project or subproject which will contain your PMD ruleset. If you use git as source control system, you’ll probably use a separate repository for the build-tools, which means, it is a separate simple maven project on its own:

build-tools
├── pom.xml
└── src
    └── main
        └── resources
            └── com
                └── mycompany
                    └── project
                        └── pmd-ruleset.xml

It will be a maven project, that produces a jar file, which contains the pmd-ruleset.xml file. Please note, that the file is not directly inside the resources folder, but within subfolders (in my example “com/mycompany/project”) to avoid name-clashes with other projects/dependencies.

The minimal pom.xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany.project</groupId>
    <artifactId>build-tools</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</project>

And a sample pmd-ruleset.xml could look like this:

<?xml version="1.0"?>
<ruleset name="Custom ruleset"
    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">

    <description>
        This ruleset defines the PMD rules for project "project" in "mycompany".
    </description>

    <rule ref="rulesets/java/basic.xml"/>
    <rule ref="rulesets/java/imports.xml"/>
    <rule ref="rulesets/java/unnecessary.xml"/>
</ruleset>

As you can see, this ruleset defines the default rules, that would be active by default when using the maven-pmd-plugin.

A simple mvn install should create the jar file and install it into your local repository.

Using the build-tools artifact

Now in the other projects, the maven-pmd-plugin is used to run PMD during the build as a verification step. You’ll need to configure the build-tools artifact as an additional dependency in the build section, as shown:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany.project</groupId>
    <artifactId>project</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>3.6</version>
                <configuration>
                    <rulesets>
                        <ruleset>com/mycompany/project/pmd-ruleset.xml</ruleset>
                    </rulesets>
                    <printFailingErrors>true</printFailingErrors>
                </configuration>
                <executions>
                   <execution>
                       <goals>
                           <goal>check</goal>
                       </goals>
                   </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.mycompany.project</groupId>
                        <artifactId>build-tools</artifactId>
                        <version>0.0.1-SNAPSHOT</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

A mvn verify will now execute PMD on the project using the ruleset from the build-tools artifact. If it find any violations, it will fail the build and print the errors.

What else can be in “build-tools”

This build-tools artifact looks like it could collect various pieces:

  • Checkstyle configuration
  • PMD configuration
  • FindBugs / Spotbugs configuration
  • Code-Formatting configuration for IDEs
  • Other shared resources that are used during the build like ant-script-snippets, shell scripts, …

Update (2021-11-22)