Configure PMD in Multi-Module Maven Builds

Similar to Checkstyle, the execution of PMD can be both automated in developer builds and in the CI/CD environment, and at the same time use a centralized configuration artifact for the PMD rules setup.

The “Maven PMD Plugin” can be used to integrate PMD into Maven builds. The plugin actually performs two tasks: First the PMD analysis and second a “copy and paste” detection, where the plugin tries to detect reuse of code by copy and paste rather than language specific code reuse mechanisms. The plugin can execute multiple goals for these checks (“pmd” and “cpd”) and there are variants of these goals that fail the build in case of a rule violation (using “check” instead of “pmd” and “cpd-check” instead of “cpd”). By setting up a Maven profile for release builds, developers can create local rule violation reports in their build environment, and the CI/CD system can fail the build in case of rule violations.

The configuration below sets up a default configuration of the PMD plugin which allows a developer to execute the PMD checks with the “mvn clean install” command. In case of rule violations, the developer build is configured to not fail, but instead create data for reports. Some of the configuration settings are parameterized with Maven build variables, but the configuration works the same way when these settings are being hardcoded.

In this example, we use a configuration artifact (jar resource file) for the PMD rules configuration. The “dependency” section pulls in the configuration artifact, and the “ruleset” parameter points to the PMD rules configuration in the resource configuration artifact. This filename must match the actual rules filename used in the dependency. Note that PMD performs a JDK specific analysis, and thus requires to specify the JDK version. This should match the JDK version configured in the Maven Compiler Plugin.

<build>
  ...
  <plugins>
  ...
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-pmd-plugin</artifactId>
      <version>${mavenPmdPluginVersion}</version>
      <configuration>
        <linkXref>true</linkXref>
        <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
        <minimumTokens>100</minimumTokens>
        <targetJdk>${javaVersion}</targetJdk>
        <rulesets>
          <ruleset>pmd-rules.xml</ruleset>
        </rulesets>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>${buildToolsGroupId}</groupId>
          <artifactId>${buildToolsArtifactId}</artifactId>
          <version>${buildToolsVersion}</version>
        </dependency>
      </dependencies>
      <executions>
        <execution>
          <phase>validate</phase>
          <goals>
            <goal>pmd</goal>
            <goal>cpd</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  ...
  </plugins>
  ...
</build>

For the automated continuous integration (CI) build, we use a very similar variant of the configuration in a profile. For the CI build, the plugin is using the two alternate goals to fail the build in case of a rules violation:

<profiles>
  ...
  <profile>
    <id>release</id>
    ...
    <build>
      <plugins>
      ...
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-pmd-plugin</artifactId>
          <version>${mavenPmdPluginVersion}</version>
          <configuration>
            <linkXref>true</linkXref>
            <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
            <minimumTokens>100</minimumTokens>
            <targetJdk>${javaVersion}</targetJdk>
            <rulesets>
              <ruleset>pmd-rules.xml</ruleset>
            </rulesets>
          </configuration>
          <dependencies>
            <dependency>
              <groupId>${buildToolsGroupId}</groupId>
              <artifactId>${buildToolsArtifactId}</artifactId>
              <version>${buildToolsVersion}</version>
            </dependency>
          </dependencies>
          <executions>
            <execution>
              <phase>validate</phase>
              <goals>
                <goal>check</goal>
                <goal>cpd-check</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        ...
      </plugins>
      ...
    </build>
    ...
  </profile>
  ...
</profiles>

To create a graphical PMD and CPD report with the “mvn site” command, the Maven PMD plugin can be configured to render the results from the anlysis phase into an html report. To enable this functionality and automatically include the reports in the project’s site report, add the following code to the “reporting” section in the POM file (in addition to the plugin configuration in the “build” section):

<reporting>
  ...
  <plugins>
  ...
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-pmd-plugin</artifactId>
      <version>${mavenPmdPluginVersion}</version>
      <configuration>
        <linkXref>true</linkXref>
        <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
        <minimumTokens>100</minimumTokens>
        <skipEmptyReport>false</skipEmptyReport>
        <targetJdk>${javaVersion}</targetJdk>
        <rulesets>
          <ruleset>pmd-rules.xml</ruleset>
        </rulesets>
      </configuration>
    </plugin>
  ...
  </plugins>
  ...
</reporting>

To create the graphical report, the plugin uses the analysis results that are already present in the “target” directory of an artifact, and then transforms them into a graphical representation. This is why the configuration does not have a dependency on the PMD configuration artifact.

Note that this plugin needs the JXR plugin to be executed beforehand if it should provide cross-references to the source code in its reports.

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