May 15, 2020
Estimated Post Reading Time ~

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.

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:
  1. Known bug detection via SpotBugs and PMD
  2. Java StyleGuide Enforcement via CheckStyle
  3. Code coverage report generation via Clover
The Lineup
SpotBugs
SpotBugs is a program that 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 set up 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 conjunction 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 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 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,autoInstallPackage Read 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



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


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.