Adobe

Static Code Analysis With Open Source Tools For AEM Projects

Two Business Boys Maximize Ideas With Mind Helmet

Implementing static code analysis might seem like a daunting task. In some cases, this may be true depending on logistics, timing, and other factors. There is however a quick and easy way to implement it for AEM projects. Using open-source tools such as CheckStyle, SpotBugs, PMD, and JaCoCo you will pay nothing and reap all the benefits. By leveraging these tools you can:

  • Ensure that everyone is following the same standards to keep your codebase uniform
  • Catch common mistakes and known security vulnerabilities early
  • Speed up code reviews as it acts as an automated reviewer before an actual reviewer gets down to it.
  • Teach because years of using analyzers will turn coders into programmers.

Keep in mind that this is in the context of AEM development. Generally speaking, there are 3 languages. HTL, JavaScript, and Java. We will be focusing on the Java analysis here.

Let us start by creating an archetype project:

mvn -B archetype:generate \
 -D archetypeGroupId=com.adobe.aem \
 -D archetypeArtifactId=aem-project-archetype \
 -D archetypeVersion=25 \
 -D appTitle="My Site" \
 -D appId="mysite" \
 -D groupId="com.mysite" \
 -D aemVersion=6.5.5

Rule Files

Each scanner is going to need a custom set of rules. The easiest way to make them available to your build is by packaging them up into a jar file. In the archetype project you created, add a new folder called static-analysis. Inside of it create this POM file:

<?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.mysite</groupId>
    <artifactId>static-analysis</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

Create a resources folder and inside the following 2 XML files:

  • src/main/resources/my-checkstyle.xml
  • src/main/resources/my-pmd.xml

For CheckStyle 8.41, you can download the default Sun coding conventions from here.

For PMD, you can use the following empty ruleset. We will get into configuring it later.

<ruleset name="Basic"
         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 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
</ruleset>

Make sure to add static-analysis to the list of modules in the root POM. These files will now be available via a dependency to the static-analysis module. If you wish to make it available to other projects, you could publish this module to a Maven repository.

CheckStyle

CheckStyle scans your Java code and looks for style errors. Proper indentation, curly braces, etc. It helps to enforce coding standards. You can find the list of checks for CheckStyle 8.41 here.

On my AEM projects, I use the default Sun checks. The only changes that I make are:

<module name="Checker">
  ...
  <module name="SuppressWarningsFilter"/>
  <module name="LineLength">
    <property name="fileExtensions" value="java"/>
    <property name="max" value="120"/>
  </module>
  ...
</module>
<module name="Checker">
  ...
  <module name="TreeWalker">
    ...
    <module name="JavadocVariable">
      <property name="scope" value="protected"/>
    </module>
    ...
  </module>
  ...
</module>
  • Edit the core/pom.xml and add the following plugin
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>3.1.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>checkstyle</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <skip>false</skip>
        <failsOnError>true</failsOnError>
        <consoleOutput>true</consoleOutput>
        <linkXRef>false</linkXRef>
        <configLocation>my-checkstyle.xml</configLocation>
        <includeResources>false</includeResources>
        <includeTestResources>false</includeTestResources>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>com.mysite</groupId>
            <artifactId>static-analysis</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.puppycrawl.tools</groupId>
            <artifactId>checkstyle</artifactId>
            <version>8.41</version>
        </dependency>
    </dependencies>
</plugin>

You can refer to the documentation for checkstyle:checktyle goal to see what each flag is doing. The important things to call out are:

  • We pull in our static-analysis dependency.
  • The configLocaion matches the config file in our static-analysis dependency.
  • We pull in the desired version of CheckStyle via a dependency. It matches the Sun Checks file link from above for consistency.

Now, you can run mvn clean install on the root of the project and we get:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.1.2:checkstyle (default) on project mysite.core: An error has occurred in Checkstyle report generation.: Failed during checkstyle execution: There are 47 errors reported by Checkstyle 8.41 with my-checkstyle.xml ruleset. -> [Help 1]

CheckStyle is reporting 47 errors and the build has failed. You can attempt to fix them, update the rules file so they are excluded, or set the <failsOnError> plugin flag to false so we can move on to PMD.

To view the report, open the file at target/site/checkstyle.html.

PMD

Like CheckStyle, PMD will scan your Java code. Instead of checking on the code styling, it dives in to see what your program is doing. My favorite rule checks for cyclomatic complexity. On top of that, it also does CPD (copy-paste-detection). You can find the list of rules for PMD 6.32.0 here.

Earlier we created an empty my-pmd.xml ruleset file. Now, we will build it by referencing OOTB ruleset files that can be imported. Then, we selectively exclude those rules that you don’t want.

<ruleset name="Basic"
         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 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">

    <rule ref="category/java/bestpractices.xml">
        <!-- deprecated -->
        <exclude name="PositionLiteralsFirstInCaseInsensitiveComparisons"/>
        <exclude name="PositionLiteralsFirstInComparisons"/>
    </rule>
    <rule ref="category/java/codestyle.xml">
        <!-- deprecated -->
        <exclude name="AbstractNaming"/>
        <exclude name="AvoidFinalLocalVariable"/>
        <exclude name="AvoidPrefixingMethodParameters"/>
        <exclude name="ForLoopsMustUseBraces"/>
        <exclude name="IfElseStmtsMustUseBraces"/>
        <exclude name="IfStmtsMustUseBraces"/>
        <exclude name="MIsLeadingVariableName"/>
        <exclude name="SuspiciousConstantFieldName"/>
        <exclude name="VariableNamingConventions"/>
        <exclude name="WhileLoopsMustUseBraces"/>
        <!-- impractical -->
        <exclude name="AtLeastOneConstructor"/>
    </rule>
    <rule ref="category/java/design.xml">
        <!-- deprecated -->
        <exclude name="ModifiedCyclomaticComplexity"/>
        <exclude name="NcssConstructorCount"/>
        <exclude name="NcssMethodCount"/>
        <exclude name="NcssTypeCount"/>
        <exclude name="StdCyclomaticComplexity"/>
        <!-- impractical -->
        <exclude name="DataClass"/>
        <exclude name="LawOfDemeter"/>
        <exclude name="LoosePackageCoupling"/>
    </rule>
    <rule ref="category/java/documentation.xml"/>
    <rule ref="category/java/errorprone.xml">
        <!-- deprecated -->
        <exclude name="DataflowAnomalyAnalysis"/>
        <exclude name="DoNotCallSystemExit"/>
        <exclude name="InvalidSlf4jMessageFormat"/>
        <exclude name="LoggerIsNotStaticFinal"/>
        <!-- impractical -->
        <exclude name="BeanMembersShouldSerialize"/>
    </rule>
    <rule ref="category/java/multithreading.xml">
        <!-- deprecated -->
        <exclude name="UnsynchronizedStaticDateFormatter"/>
    </rule>
    <rule ref="category/java/performance.xml">
        <!-- deprecated -->
        <exclude name="AvoidUsingShortType"/>
        <exclude name="SimplifyStartsWith"/>
        <!-- impractical -->
        <exclude name="AvoidInstantiatingObjectsInLoops"/>
    </rule>
    <rule ref="category/java/security.xml"/>

</ruleset>

Most of the excluded rules are deprecated. We exclude them to silence the warnings. Otherwise, there are only really 6 that we exclude because they are impractical (or so I have found) in AEM projects. Otherwise, I run my projects with all the default rules OOTB.

Edit the core/pom.xml and add the following plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <version>3.13.0</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>pmd</goal>
                <goal>cpd</goal>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <skip>false</skip>
        <failOnViolation>true</failOnViolation>
        <printFailingErrors>true</printFailingErrors>
        <linkXRef>false</linkXRef>
        <rulesets>
            <ruleset>my-pmd.xml</ruleset>
        </rulesets>
        <targetJdk>11</targetJdk>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>com.mysite</groupId>
            <artifactId>static-analysis</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.pmd</groupId>
            <artifactId>pmd-core</artifactId>
            <version>6.32.0</version>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.pmd</groupId>
            <artifactId>pmd-java</artifactId>
            <version>6.32.0</version>
        </dependency>
    </dependencies>
</plugin>

Now, you can run mvn clean install on the root of the project and we get:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-pmd-plugin:3.13.0:check (default) on project mysite.core: You have 32 PMD violations. For more details see: /mysite/core/target/pmd.xml -> [Help 1]

PMD is reporting 32 violations and the build has failed. You can attempt to fix them, update the ruleset file so they are excluded, or set the <failOnViolation> plugin flag to false so we can move on to SpotBugs.

To view the report, open the file at target/site/pmd.html.

SpotBugs

CheckStyle and PMD operate on source code files. SpotBugs on the other hand operates on the compiled byte code. As the name implies, it does a better job at detecting bugs than the other two. There are about 400 standard bug patterns.

Just like CheckStyle and PMD you can build custom filter files that can be used to exclude checks. In practice, I have found that the defaults work well enough and reasonably apply to AEM projects. We will just configure the Maven plugin.

Edit the core/pom.xml and add the following plugin:

<plugin>
    <groupId>com.github.spotbugs</groupId>
    <artifactId>spotbugs-maven-plugin</artifactId>
    <version>4.2.0</version>
    <configuration>
        <skip>false</skip>
        <failOnError>true</failOnError>
        <xmlOutput>true</xmlOutput>
        <xmlOutputDirectory>target/site</xmlOutputDirectory>
    </configuration>
    <executions>
        <execution>
            <id>spotbugs</id>
            <goals>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
    <dependencies>
        <dependency>
          <groupId>com.github.spotbugs</groupId>
          <artifactId>spotbugs</artifactId>
          <version>4.2.2</version>
        </dependency>
    </dependencies>
</plugin>

Now run mvn clean install on the root of the project and we get a build SUCCESS. No bugs spotted. Go take a look at the bug descriptions. Try to create a bug within a core bundle to see if SpotBugs picks it up.

To view the report, open the file at target/site/spotbugs.xml.

JaCoCo

JaCoCo is the reporting tool that measures code coverage and generates visual reports. It relies on unit tests to execute your lines of code, collecting metrics along the way.

If used properly, it is a good tool to write unit tests. It will help you craft unit tests that explore all possible aspects of your code.
 
When used improperly, it provides a meaningless measure of quality. I’ve heard dev leads or managers brag about 90% coverage. Then I find out that a lot if not all the tests hit the lines of code, with no actual testing. Go figure.

Edit the core/pom.xml and add the following plugin

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.6</version>
    <executions>
        <execution>
            <id>default-prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>default-report</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>default-check</id>
            <goals>
                <goal>check</goal>
            </goals>
            <configuration>
                <rules>
                    <rule>
                        <element>CLASS</element>
                        <limits>
                            <limit>
                                <counter>LINE</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.90</minimum>
                            </limit>
                            <limit>
                                <counter>BRANCH</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.90</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

In the configuration, I have set the LINE and BRANCH coverage to at least 90%. Check out the other available counters here and the rule configuration here.

Now, you can run mvn clean install on the root of the project and we get a build SUCCESS. The archetype’s coverage is pretty high. To cause the rule to fail delete one of the unit tests.

To view the report, open the file at target/site/jacoco/index.html.

Conclusion

If you are dealing with an existing codebase, setting up code analysis may be impossible. You may have to set it to report only instead of failing the build as I have done here. You will have better success at the start of a project. Get your static code analysis set up early before major development gets underway. My suggestion would be to go with as many defaults as possible. If you look into the documentation of each rule, they are reasonable. Of course, things get subjective.

There are proprietary tools such as SonarQube, Fortify, or CodeClimate. They offer the same analysis with added management and reporting features. If you are still in the procurement process there is no need to put off your static analysis setup. I have found that projects set up with the tools mentioned here passed with ease when integrated with the proprietary one.

After years of using these code analyzers, I have gotten pretty good at predicting them. In fact, my Java has improved. And as a code reviewer, I find this gets rid of 90% of the chaff and allows me to focus on the implemented feature.

Should you find yourself in a position to implement the analysis in a report-only fashion, leverage the Maven Site Plugin. Check out our blog where this (and Clover instead of JaCoCo) is discussed here.

 

About the Author

Juan Ayala is a Lead Developer in the Adobe practice at Perficient, Inc., focused on the Adobe Experience platform and the things revolving around it.

More from this Author

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to the Weekly Blog Digest:

Sign Up