Sunday, September 17, 2017

PMD - A Step towards Writing Clean Code

Today I would throw some questions related to writing clean code, automation, static code analysis and see if you are with me in this exciting journey:
  • Is it practically possible to remember each and every code convention rules , best practices, bad practices, basic design principles while writing codes ? 
  • Is it possible for code reviewers to inspect every lines of code to find code flaws ?
  • Is it smart thing for code reviewers to waste their time, effort and energy to find code flaws related to conventions, bad practices, potential bugs, dead code, unused code and so forth when they could do so much more on the front of finding architecture, design, performance related issues ?
  • Is it good to find small bugs in UAT or QA instead of finding at development phase? 
  • Is it not a smart move to use open source tools to automate the process of reports generation based on customized rule sets?   
If the answer to the above question is No then you are on the right tract otherwise you need to think twice as in this Agile world, it's difficult to survive with this mentality because everyone is moving forward and welcoming DevOps culture, Agile, Automation with open arms.

Today we would discuss about PMD, an open source cross language static code analyzer tool.
In this article, we would discuss below points regarding PMD:
1. Overview
2. Configuring PMD for a Maven Project
3. Rule sets Overview
4. Running PMD
5. Example
5. Conclusion

Let's start our journey to explore PMD in some details!!


1. Overview


PMD is an open source cross language static code analyzer tool to find common programming flaws like dead code, duplicate code,  over complicated expression etc. It's primarily focuses on the bad practices used in the code and reports accordingly as per the defined rule sets. PMD not only has large number of built-in rule sets but it also supports the ability to write custom rules. It supports Java, JavaScript, Salesforce.com Apex and Visualforce, PLSQL, Apache Velocity, XML, XSL.

Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code in Java, C, C++, C#, Groovy, PHP, Ruby, Fortran, JavaScript, PLSQL, Apache Velocity, Scala, Objective C, Matlab, Python, Go, Swift and Salesforce.com Apex and Visualforce.

Note: In this tutorial, we would elaborate on how to use PMD for static code analysis in a Java based project. We would discuss CPD related functionality, usages and it's significance in our next article.


2. Configuring PMD for a Maven Project


We will use Apache Maven PMD Plugin to generate the PMD & CMD reports using the PMD tool. One need to add the maven-pmd-plugin in <reporting> section of the POM and accordingly configure it to use the default rule sets or the customized rule sets. For example:

With default rule sets and configuration:

<project>
  ...
  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
        <version>3.8</version>
      </plugin>
    </plugins>
  </reporting>
  ...
</project>

With customized rule sets and configuration:

<project>
  ...
  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
        <version>3.8</version>
        <configuration>
          <linkXref>true</linkXref>
          <sourceEncoding>utf-8</sourceEncoding>
          <minimumTokens>100</minimumTokens>
          <targetJdk>1.8</targetJdk>
    <rulesets>
             <!-- Relative path, built-in rule sets -->
      <ruleset>/rulesets/java/braces.xml</ruleset>
             <ruleset>/rulesets/java/naming.xml</ruleset>
             <!-- Absolute path, custom local file system rule set -->
             <ruleset>D:\rulesets\ruleset.xml</ruleset>
             <!-- URL, could be custom or built-in rule sets -->
             <ruleset>http://localhost/design.xml</ruleset>
           </rulesets>
          <excludes>
            <exclude>**/*Bean.java</exclude>
            <exclude>**/generated/*.java</exclude>
          </excludes>
          <excludeRoots>
            <excludeRoot>target/generated-sources/stubs</excludeRoot>
          </excludeRoots>
        </configuration>
      </plugin>
    </plugins>
  </reporting>
  ...
</project>

Some Pointer:

  • linkXref: if enabled, report will link directly to the cross-referenced source, provided one also have entry for JXR plugin in their POM
  • sourceEncoding: tell Maven which encoding to use when reading the java source
  • exclude: exclude source which one wants to ignore
  • targetJDK: specify which java version to use
  • ruleset: specify the built-in rule sets or your own customized rule sets, supports relative address, an absolute address and even a URL
  • One can also explicitly execute the PMD plugin and generate the same report by setting the plugin in the <build> section of your POM.

3. Rule Sets Overview


The PMD plugin uses five default rule sets:
  • basic.xml
  • empty.xml
  • imports.xml
  • unnecessary.xml 
  • unusedcode.xml
But there are also other built-in rule sets that one can configure in the plugin along with their own created rule sets. The rule sets may reside in the classpath, filesystem or at a URL.

Let's create our own customized rule sets


It's very simple to create our own rule sets. Just follow the below steps: 
  • Create a new ruleset.xml file (using the below template)
    Don't forget to change name & description accordingly.
    <?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 is customized according to my needs and checks my code flaws
      </description>
    </ruleset>
    
  • Pick the rules that are right for you
    <!-- used the entire 'strings' ruleset -->
      <rule ref="rulesets/java/strings.xml"/>
    
That's it. Nothing else. But when one will starts writing custom rule sets then they will come across below scenarios like:
  • Sometime wants to use the entire rule sets 
  • Sometimes wants to use only specific rules from a rule sets
  • Sometimes wants to customize the rule a bit, change the message and raise the priority
  • Sometimes wants to customize a rule's property value
  • Sometimes wants to use the entire rule sets except one or two 
  • Sometimes wants to exclude certain files from processing by a rule sets

Now what to do ? Don't worry!! We got you covered here. Just follow the below examples:
<?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>
     Start your Journet to write your custom rule sets. This examples shows 
various scenarios on how to add various rule sets.  
  </description>
  
   <!-- Sometimes wants to exclude certain files from processing by a rule sets -->
  <exclude-pattern>.*/some/package/.*</exclude-pattern>
  <exclude-pattern>.*/some/other/package/FunkyClassNamePrefix.*</exclude-pattern>
  <include-pattern>.*/some/package/ButNotThisClass.*</include-pattern>

  <!-- Sometime wants to use the entire rule sets  -->
  <rule ref="rulesets/java/strings.xml"/>

  <!-- Sometimes wants to use only specific rules from a rule sets -->
  <rule ref="rulesets/java/unusedcode.xml/UnusedLocalVariable"/>
  <rule ref="rulesets/java/imports.xml/DuplicateImports"/>

  <!-- Sometimes wants to customize the rule a bit, 
       change the message and raise the priority  -->
  <rule
   ref="rulesets/java/basic.xml/EmptyCatchBlock"
   message="Must handle exceptions">
    <priority>2</priority>
  </rule>

  <!-- Sometimes wants to customize a rule's property value -->
  <rule ref="rulesets/java/codesize.xml/CyclomaticComplexity">
    <properties>
        <property name="reportLevel" value="5"/>
    </properties>
  </rule>

  <!-- Sometimes wants to use the entire rule sets except one or two -->
  <rule ref="rulesets/java/braces.xml">
    <exclude name="WhileLoopsMustUseBraces"/>
  </rule>
</ruleset>

4. Running PMD


There are mainly two ways to run PMD:
  1. Don't add an entry in a ‘project reports’ section with the PMD report
    mvn pmd:pmd
    
  2. Add an entry in the ‘project reports’ section with the PMD report
    mvn site
    
The generated report is called pmd.html and is located in the target/site folder.

Note: If there are no violations, then by default no reports will be created and the entire PMD or CPD section is not rendered in the site. If one wants to change this behavior then set the skipEmptyReport for PMD or skipEmptyReport for CPD to false.

5. Example


Without an example this journey can't be completed. Therefore here it is:
import java.lang.String;
import java.lang.*;
public class App {

 private String name; 
 
 {}
 
 public void bar() {
   try {
    
   } catch (Exception e) { 
     e.printStackTrace();
    }
 } 
...

}

Corresponding PMD report when we use command mvn pmd:pmd
PMD Results

The following document contains the results of PMD 5.6.1.

Files

com/anshulsblog/pmd/App.java

Violation                                             Line

Avoid importing anything from the package java.lang   3 
Avoid importing anything from the package java.lang   4 
Avoid unused private fields such as 'name'.           11 
Empty initializer was found                           13 
Avoid empty try blocks                                18–19 

Corresponding PMD report when we use command mvn site



5. Conclusion


PMD is a flexible and highly configurable static analyzer tool. One should use this "code review automation tool" for static code analysis so that bugs can fixed at development time only.

Thank you for reading this article. Feel free to connect with me for any queries and suggestion and don't forget to share your comments. Happy Learning!!

2 comments:

  1. Completely agree... I found very important and useful information on code reviews and static code analyzers. Thanks for sharing the importance of clean codes.

    ReplyDelete