I have added a custom PMD rule using official documentation (http://pmd.sourceforge.net/pmd-5.1.0/howtowritearule.html)
Rule:
package com.comp.www.lty.awards.service.pmd;
import net.sourceforge.pmd.lang.ast.*;
import net.sourceforge.pmd.lang.java.ast.*;
import net.sourceforge.pmd.lang.java.rule.*;
public class WhileLoopsMustUseBracesRule extends AbstractJavaRule {
public Object visit(ASTWhileStatement node, Object data) {
Node firstStmt = node.jjtGetChild(1);
if (!hasBlockAsFirstChild(firstStmt)) {
addViolation(data, node);
}
return super.visit(node,data);
}
private boolean hasBlockAsFirstChild(Node node) {
return (node.jjtGetNumChildren() != 0 && (node.jjtGetChild(0) instanceof ASTBlock));
}
}
This is my ruleset xml file:
<?xml version="1.0"?>
<ruleset name="My custom rules"
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>While loop desc</description>
<rule name="WhileLoopsMustUseBracesRule"
message="Avoid using 'while' statements without curly braces"
class="com.comp.www.lty.awards.service.pmd.WhileLoopsMustUseBracesRule">
<description>
Avoid using 'while' statements without using curly braces
</description>
<priority>3</priority>
<example>
<![CDATA[
public void doSomething() {
while (true)
x++;
}
]]>
</example>
</rule>
</ruleset>
I am using mvn to run these pmd rules. Works fine when I try to use existing rules from pmd jar, but whenever I try to use my above custom rule, I am getting ClassNotFoundException.
Stacktrace:
java.lang.ClassNotFoundException: com.comp.www.lty.awards.service.pmd.WhileLoopsMustUseBracesRule
at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:244)
at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:230)
at net.sourceforge.pmd.RuleSetFactory.parseSingleRuleNode(RuleSetFactory.java:377)
at net.sourceforge.pmd.RuleSetFactory.parseRuleNode(RuleSetFactory.java:291)
at net.sourceforge.pmd.RuleSetFactory.parseRuleSetNode(RuleSetFactory.java:242)
at net.sourceforge.pmd.RuleSetFactory.createRuleSet(RuleSetFactory.java:176)
at net.sourceforge.pmd.RuleSetFactory.createRuleSet(RuleSetFactory.java:171)
at net.sourceforge.pmd.RuleSetFactory.parseRuleSetReferenceNode(RuleSetFactory.java:331)
at net.sourceforge.pmd.RuleSetFactory.parseRuleNode(RuleSetFactory.java:289)
at net.sourceforge.pmd.RuleSetFactory.parseRuleSetNode(RuleSetFactory.java:242)
at net.sourceforge.pmd.RuleSetFactory.createRuleSet(RuleSetFactory.java:176)
at net.sourceforge.pmd.RuleSetFactory.createRuleSet(RuleSetFactory.java:171)
at net.sourceforge.pmd.RuleSetFactory.createRuleSets(RuleSetFactory.java:135)
at net.sourceforge.pmd.RuleSetFactory.createRuleSets(RuleSetFactory.java:119)
at net.sourceforge.pmd.RulesetsFactoryUtils.getRuleSets(RulesetsFactoryUtils.java:31)
at net.sourceforge.pmd.processor.AbstractPMDProcessor.createRuleSets(AbstractPMDProcessor.java:54)
at net.sourceforge.pmd.processor.MultiThreadProcessor.processFiles(MultiThreadProcessor.java:38)
at net.sourceforge.pmd.PMD.processFiles(PMD.java:352)
at org.apache.maven.plugin.pmd.PmdReport.executePmd(PmdReport.java:377)
at org.apache.maven.plugin.pmd.PmdReport.executePmdWithClassloader(PmdReport.java:280)
at org.apache.maven.plugin.pmd.PmdReport.canGenerateReport(PmdReport.java:254)
at org.apache.maven.reporting.AbstractMavenReport.execute(AbstractMavenReport.java:119)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions(MojoExecutor.java:365)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:199)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Although not an ideal solution, but even I spent time to include this custom rule class file in the pmd jar (Custom Java PMD rule: Can't find the class CustomRule), but that didn't help either.
pom.xml: http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot spring-boot-starter-parent 1.4.0.RELEASE lty-awards-service com.comp.www war lty-awards-service Spring Boot Service Sample 1.0-SNAPSHOT http://maven.apache.org 1.8
<findbugs-maven-plugin.version>3.0.3</findbugs-maven-plugin.version>
<findbugs.include.filter.location>buildtools/findbugs/include.xml</findbugs.include.filter.location>
<findbugs.exclude.filter.location>buildtools/findbugs/exclude.xml</findbugs.exclude.filter.location>
<maven-pmd-plugin.version>3.5</maven-pmd-plugin.version>
<pmd.version>5.3.2</pmd.version>
<pmdRule.version>5.1.0</pmdRule.version>
<pmd.ruleset.location>buildtools/pmd-rules/ltyawardsservicepmd.xml</pmd.ruleset.location>
<pmd.skip>false</pmd.skip>
<pmd.typeResolution>true</pmd.typeResolution>
<!-- spring boot -->
<debug.port>5000</debug.port>
<run.jvmArguments>-Dspring.profiles.active=dev -Dapplication.home=. -Dapplication.name=${project.name}
-Dapplication.environment=dev -Dproject.name=${project.name} -Xdebug
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debug.port}
</run.jvmArguments>
<error-inspector.version>0.1.9</error-inspector.version>
<platform-diagnostics.version>0.0.43</platform-diagnostics.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId>
<version>${pmdRule.version}</version>
<scope>system</scope>
<systemPath>/Users/babu/.m2/repository/net/sourceforge/pmd/pmd/5.1.0/test.jar</systemPath>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.comp.prime.errorcatalog</groupId>
<artifactId>error-inspector</artifactId>
<version>${error-inspector.version}</version>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/error-inspect</outputDirectory>
<destFileName>inspect.jar</destFileName>
</artifactItem>
<artifactItem>
<groupId>com.comp.www.platform</groupId>
<artifactId>platform-diagnostics-main</artifactId>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/error-inspect/lib</outputDirectory>
<destFileName>platform-diagnostics-main.jar</destFileName>
</artifactItem>
</artifactItems>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<message>.*** ERROR: Detected Maven version ${maven.version}, We need Maven 3.0.3 or
higher ***.
</message>
<version>[3.0.3,)</version>
</requireMavenVersion>
<requireJavaVersion>
<message>.*** ERROR: Detected JDK version ${java.version}, We need JDK
${project.jdk.version} or higher ***.
</message>
<version>${project.jdk.version}</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
<execution>
<id>enforce-dont-exist</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireFilesDontExist>
<files>
<file>${project.basedir}/src/main/scala</file>
</files>
</requireFilesDontExist>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${project.jdk.version}</source>
<target>${project.jdk.version}</target>
<encoding>UTF-8</encoding>
</configuration>
<version>3.3</version>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin.version}</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaCompatVersion>${scala.major.minor.version}</scalaCompatVersion>
<jvmArgs>
<jvmArg>-Xms512m</jvmArg>
<jvmArg>-Xmx2048m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>${maven-pmd-plugin.version}</version>
<dependencies>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-core</artifactId>
<version>${pmd.version}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-java</artifactId>
<version>${pmd.version}</version>
</dependency>
<dependency>
<groupId>WhileLoopsMustUseBracesRule</groupId>
<artifactId>pmd.ruleset</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>system</scope>
<systemPath>/Users/babu/Downloads/cache.jar</systemPath>
</dependency>
<dependency>
<groupId>com.comp.www.platform</groupId>
<artifactId>platform-build-tools</artifactId>
<version>${platform-build-tools.version}</version>
</dependency>
</dependencies>
<configuration>
<targetJdk>${project.jdk.version}</targetJdk>
<includeTests>true</includeTests>
<skip>${pmd.skip}</skip>
<sourceEncoding>${project.sourceEncoding}</sourceEncoding>
<rulesets>
<ruleset>${pmd.ruleset.location}</ruleset>
</rulesets>
</configuration>
<executions>
<execution>
<id>cpd-report</id>
<phase>test-compile</phase>
<goals>
<goal>check</goal>
<goal>cpd-check</goal>
<goal>pmd</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.3</version>
</plugin>
</plugins>
</reporting>-->
Here test.jar is my jar bundled (i.e. pmd jar unbundled, added class file & ruleset and again bundled it as test.jar). Although this is not how I want to use the rule. But if there is a better way to use pmd jar as it is & add a mvn dependency for new rule that is the ideal solution I'm looking for. Thanks!
You need to add your custom rule jar to the classpath when PMD runs. You should bundle your custom PMD rule in its own Maven dependency, then you can include it in the PMD plugin classpath by adding a dependency to your plugin configuration like so:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.7</version>
<dependencies>
<dependency>
<groupId>com.foo.bar</groupId>
<artifactId>my-custom-pmd-rule</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
...
</plugin>
See this page for more information about plugin classloading. See how it says:
Please note that the plugin classloader does neither contain the dependencies of the current project nor its build output
Try changing your pom.xml file to the following. Notice I moved your test.jar dependency to the plugin dependencies so that it'll be loaded on the plugin's classpath. Note that you don't have to bundle your rules with PMD. You just need to package your rules in its own jar and include it in the plugin dependencies section.
<findbugs-maven-plugin.version>3.0.3</findbugs-maven-plugin.version>
<findbugs.include.filter.location>buildtools/findbugs/include.xml</findbugs.include.filter.location>
<findbugs.exclude.filter.location>buildtools/findbugs/exclude.xml</findbugs.exclude.filter.location>
<maven-pmd-plugin.version>3.5</maven-pmd-plugin.version>
<pmd.version>5.3.2</pmd.version>
<pmdRule.version>5.1.0</pmdRule.version>
<pmd.ruleset.location>buildtools/pmd-rules/ltyawardsservicepmd.xml</pmd.ruleset.location>
<pmd.skip>false</pmd.skip>
<pmd.typeResolution>true</pmd.typeResolution>
<!-- spring boot -->
<debug.port>5000</debug.port>
<run.jvmArguments>-Dspring.profiles.active=dev -Dapplication.home=. -Dapplication.name=${project.name}
-Dapplication.environment=dev -Dproject.name=${project.name} -Xdebug
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${debug.port}
</run.jvmArguments>
<error-inspector.version>0.1.9</error-inspector.version>
<platform-diagnostics.version>0.0.43</platform-diagnostics.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.comp.prime.errorcatalog</groupId>
<artifactId>error-inspector</artifactId>
<version>${error-inspector.version}</version>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/error-inspect</outputDirectory>
<destFileName>inspect.jar</destFileName>
</artifactItem>
<artifactItem>
<groupId>com.comp.www.platform</groupId>
<artifactId>platform-diagnostics-main</artifactId>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/error-inspect/lib</outputDirectory>
<destFileName>platform-diagnostics-main.jar</destFileName>
</artifactItem>
</artifactItems>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<message>.*** ERROR: Detected Maven version ${maven.version}, We need Maven 3.0.3 or
higher ***.
</message>
<version>[3.0.3,)</version>
</requireMavenVersion>
<requireJavaVersion>
<message>.*** ERROR: Detected JDK version ${java.version}, We need JDK
${project.jdk.version} or higher ***.
</message>
<version>${project.jdk.version}</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
<execution>
<id>enforce-dont-exist</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireFilesDontExist>
<files>
<file>${project.basedir}/src/main/scala</file>
</files>
</requireFilesDontExist>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${project.jdk.version}</source>
<target>${project.jdk.version}</target>
<encoding>UTF-8</encoding>
</configuration>
<version>3.3</version>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin.version}</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaCompatVersion>${scala.major.minor.version}</scalaCompatVersion>
<jvmArgs>
<jvmArg>-Xms512m</jvmArg>
<jvmArg>-Xmx2048m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>${maven-pmd-plugin.version}</version>
<dependencies>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-core</artifactId>
<version>${pmd.version}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-java</artifactId>
<version>${pmd.version}</version>
</dependency>
<dependency>
<groupId>WhileLoopsMustUseBracesRule</groupId>
<artifactId>pmd.ruleset</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>system</scope><systemPath>/Users/babu/Downloads/cache.jar</systemPath>
</dependency>
<dependency>
<groupId>com.comp.www.platform</groupId>
<artifactId>platform-build-tools</artifactId>
<version>${platform-build-tools.version}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId>
<version>${pmdRule.version}</version>
<scope>system</scope>
<systemPath>/Users/babu/.m2/repository/net/sourceforge/pmd/pmd/5.1.0/test.jar</systemPath>
</dependency>
</dependencies>
<configuration>
<targetJdk>${project.jdk.version}</targetJdk>
<includeTests>true</includeTests>
<skip>${pmd.skip}</skip>
<sourceEncoding>${project.sourceEncoding}</sourceEncoding>
<rulesets>
<ruleset>${pmd.ruleset.location}</ruleset>
</rulesets>
</configuration>
<executions>
<execution>
<id>cpd-report</id>
<phase>test-compile</phase>
<goals>
<goal>check</goal>
<goal>cpd-check</goal>
<goal>pmd</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.3</version>
</plugin>
</plugins>
</reporting>-->