The Ultimate Code Quality Setup for Your AEM Project | Perficient Digital

The Ultimate Code Quality Setup for Your AEM Project

If you’re a developer or come from a development background, you’ve seen a lot of bad code in your career. It’s just part of the learning experience… we all wrote bad code at some point in time and learned from it.

XKCD code quality

src: xkcd comics

I can’t cure bad code.

That’s something to be fixed through a code review process. What this post does is show you a few maven plugins you can take advantage of to automate things like:

The Lineup

SpotBugs

SpotBugs is a program which uses static analysis to look for bugs in Java code.
SpotBugs checks for more than 400 bug patterns. Bug descriptions can be found here

We will be using the SpotBugs Maven Plugin.

PMD

PMD is a static source code analyzer. It finds common programming flaws like unused variables, empty catch blocks, unnecessary object creation, and so forth. It’s mainly concerned with Java and Apex, but supports six other languages.

A list of PMD’s java rules can be found here.

We will be using the PMD Maven Plugin.

CheckStyle

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. It automates the process of checking Java code to spare humans of this boring (but important) task. This makes it ideal for projects that want to enforce a coding standard.

We will be using the CheckStyle Maven Plugin and using the Google Java Style Guide with it, you can also configure Sun’s Java Style if you like.

Clover

Clover was an Atlassian offering, you know them as the people behind Jira. In 2017, Atlassian open sourced Clover. We will be using Clover to generate code coverage reports for unit tests. We will also add jUnit5 and aem-mock dependencies for reference.

 

SonarQube and SonarLint

SonarQube is a standalone server that can analyze your project for all types of bugs/code smells. If you do not wish to setup a server, you can use SonarLint which provides multiple IDE plugins that will analyze your code similar to how SonaqQube would. It is not a replacement to a full SonarQube server, but can be used in conjuction or by itself. This is especially important if you are on AMS; since the Adobe AMS Cloud Manager runs SonarQube for code quality.

 

The Setup

I assume you have a working Maven AEM project and know how to add maven dependencies.

Let’s start by adding the dependencies we need. You can add this to your parent POM.

The versions I am using here are the latest as of this post.
Make sure you are using maven-surefire-plugin v2.22.0 or later as it supports jUnit5.

 <build>
    ....
    <pluginManagement>
        <plugins>
            ...
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>3.9.0</version>
            </plugin>
            <plugin>
                <groupId>org.openclover</groupId>
                <artifactId>clover-maven-plugin</artifactId>
                <version>4.3.0</version>
            </plugin>
            <plugin>
                <groupId>com.github.spotbugs</groupId>
                <artifactId>spotbugs-maven-plugin</artifactId>
                <version>3.1.6</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.7.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-checkstyle-plugin</artifactId>
                <version>3.0.0</version>
                <dependencies>
                    <dependency>
                        <groupId>com.puppycrawl.tools</groupId>
                        <artifactId>checkstyle</artifactId>
                        <version>8.12</version>
                    </dependency>
                </dependencies>
            </plugin>
            ...
        </plugins>
    <build>
    ....
<pluginManagement>

 

Then to your <dependencies> add the following:

<!-- TESTING -->
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.wcm</groupId>
    <artifactId>io.wcm.testing.aem-mock</artifactId>
    <version>2.2.16</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.felix</groupId>
    <artifactId>org.apache.felix.http.servlet-api</artifactId>
    <version>1.1.2</version>
    <scope>provided</scope>
</dependency>
<!-- // TESTING -->

We are using jUnit5 and aem-mock. I won’t cover those two, as they are both well documented and easy to work with.

Now that our plugins are defined, we are going to add them to our core or bundle maven submodule, you know the one. We are going to do so by adding a new Maven profile for called codeQuality that is enabled by default:

I have chosen a profile so that it can be easily disabled when needed. This is particularly helpful for when in initial active development and not yet concerned with quality.

<!-- ====================================================================== -->
<!-- C O D E  Q U A L I T Y P R O F I LE -->
<!-- ====================================================================== -->
<profiles>
    <profile>
        <id>codeQuality</id>
        <!-- 
            Active by default. use `-P-codeQuality` to deactivate it.
            Note the prefix `-` which disables the `codeQuality` profile
            Combine it with other profiles using CVS. Example: `-P-codeQuality,autoInstallPackage`
        -->
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <!-- 
                log in color, if terminal supports ANSI escape sequences
                see: https://confluence.atlassian.com/clover/using-test-optimization-in-maven-170492714.html
              -->
            <ansi.color>true</ansi.color>
        </properties>
        <!-- Code Quality Reporting -->
        <reporting>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-pmd-plugin</artifactId>
                    <configuration>
                        <skipEmptyReport>false</skipEmptyReport>
                        <printFailingErrors>true</printFailingErrors>
                        <!-- Dont use cache here, the lifesycle for `site` goal wont support it-->
                        <analysisCache>false</analysisCache>
                        <targetJdk>1.8</targetJdk>
                    </configuration>
                    <reportSets>
                        <reportSet>
                            <reports>
                                <report>pmd</report>
                            </reports>
                        </reportSet>
                    </reportSets>
                </plugin>
                <plugin>
                    <groupId>com.github.spotbugs</groupId>
                    <artifactId>spotbugs-maven-plugin</artifactId>
                    <configuration>
                        <xmlOutput>true</xmlOutput>
                        <trace>true</trace>
                    </configuration>
                    <reportSets>
                        <reportSet>
                            <reports>
                                <report>spotbugs</report>
                            </reports>
                        </reportSet>
                    </reportSets>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-checkstyle-plugin</artifactId>
                    <configuration>
                        <encoding>UTF-8</encoding>
                        <failsOnError>true</failsOnError>
                        <failOnViolation>true</failOnViolation>
                        <violationSeverity>warning</violationSeverity>
                        <includes>src/**\/*.java</includes>
                        <configLocation>google_checks.xml</configLocation>
                    </configuration>
                    <reportSets>
                        <reportSet>
                            <reports>
                                <report>checkstyle</report>
                            </reports>
                        </reportSet>
                    </reportSets>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jxr-plugin</artifactId>
                    <version>2.5</version>
                </plugin>
                <plugin>
                    <groupId>org.openclover</groupId>
                    <artifactId>clover-maven-plugin</artifactId>
                    <reportSets>
                        <reportSet>
                            <reports>
                                <report>clover</report>
                            </reports>
                        </reportSet>
                    </reportSets>
                </plugin>
            </plugins>
        </reporting>
        <!-- Code Quality Execution -->
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-checkstyle-plugin</artifactId>
                    <configuration>
                        <encoding>UTF-8</encoding>
                        <failsOnError>true</failsOnError>
                        <failOnViolation>true</failOnViolation>
                        <violationSeverity>warning</violationSeverity>
                        <includes>src/**\/*.java</includes>
                        <configLocation>google_checks.xml</configLocation>
                    </configuration>
                    <executions>
                        <execution>
                            <id>checkstyle</id>
                            <goals>
                                <goal>check</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>com.github.spotbugs</groupId>
                    <artifactId>spotbugs-maven-plugin</artifactId>
                    <configuration>
                        <xmlOutput>true</xmlOutput>
                        <trace>true</trace>
                    </configuration>
                    <executions>
                        <execution>
                            <id>spotbugs</id>
                            <goals>
                                <goal>check</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-pmd-plugin</artifactId>
                    <configuration>
                        <printFailingErrors>true</printFailingErrors>
                        <analysisCache>true</analysisCache>
                        <targetJdk>1.8</targetJdk>
                    </configuration>
                    <executions>
                        <execution>
                            <!-- run inverfy phase to allow analysis cache to work properly -->
                            <goals>
                                <goal>check</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <!--
                    Clover configs to run Optimize, Report, Log and Check
                    see: https://confluence.atlassian.com/clover/best-practices-for-maven-171180506.html 
                -->
                <plugin>
                    <groupId>org.openclover</groupId>
                    <artifactId>clover-maven-plugin</artifactId>
                    <configuration>
                        <targetPercentage>90%</targetPercentage>
                        <debug>true</debug>
                    </configuration>
                    <executions>
                        <execution>
                            <id>clover.inst</id>
                            <phase>validate</phase>
                            <goals>
                                <!--
                                    Instrumentation changes source code, we need to use instrument-test
                                    because it forks a custom build lifecycle, runs only to the test phase
                                    If we use a goal that does not fork, it will affect other plugins like PMD and findbugs
                                    see: http://openclover.org/doc/maven/4.2.0/plugin-info.html
                                -->
                                <goal>instrument-test</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>clover.check</id>
                            <!--
                                    In the verify phase, we can generate the report, log results
                                    and check coverage against configured targetPercentage.
                                    see: http://openclover.org/doc/maven/4.2.0/plugin-info.html
                              -->
                            <phase>verify</phase>
                            <goals>
                                <goal>clover</goal>
                                <goal>log</goal>
                                <goal>check</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

For the most part, the options for each plugin are self explanatory. I added comments next to the ones that needed explaining.

And yeah, it is long, but that’s maven’s fault ¯_(ツ)_/¯  I can hear you screaming…

Let’s run it!

Now onto the good part! run it! mvn clean install will run the build with codeQuality profile enabled by default, if you need to disable it run mvn clean install -P-codeQuality

If you need to disable code quality and apply another profile, like autoInstallPackage you can do so with mvn clean install -P-codeQuality,autoInstallPackageRead more about deactivating profiles in the “Deactivating a profile” section in the maven docs

By default when code quality is enabled, any bug or style error will cause the build to fail, this is by default so that your CI fails when a violation is committed into source code.

Generating reports

To generate your reports, run mvn site, this will generate reports for all our plugins, you can view the report by opening: target/site/project-reports.html

code-quality-report

Gotchas

The Maven Build Lifecycle 

When working with maven plugins, you have to beware of the maven build lifecycle since some plugins may alter the generated source, like in the case of Clover where instrumentation is necessary.

In the case of Clover, we used the instrument-test goal, which will form a new build lifecycle; so that the instrumented source does not get deployed to your AEM instance. As a side effect, unit tests will run twice. You can fix this by moving Clover to its own profile and activating that Clover profile separately from your main build.

Balancing those plugins can be tough, but a good understanding of the maven build lifecycle is key.

Reports

Some plugins may not generate any reports if you do not have anything in your src/main/java (no java code). Also, if a plugin is not in the <reporting> section, a report will not be generated with mvn site

Leave a Reply