Fixing code style errors with Spotless and Maven

Table of Contents

In previous post related to coding style I’ve described how to validate the code and generate report with Checkstyle. This time I would like to focus on Spotless which not only offers the same functionality as Checkstyle, but also can fix code style errors automatically.

Autoformatting example

Before configuring the Maven and running Spotless I would like to illustrate the functionality by presenting two code snippets. Both have the same code, but have different formatting applied. The first one is before running Spotless and the second one after applying it.

Before

/**
 * Messy
 *          code
 *          makes
 *           programmers
 * life
 *  harder.</p>
 */
public class App {

    final static public String myConst = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ";

    public static void main(String[] args) {
        System.out
                .println(myConst);
        System
                .out
                .println("Please fix me :)");
    }
}

After

/** Messy code makes programmers life harder. */
public class App {

    public static final String myConst =
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ";

    public static void main(String[] args) {
        System.out.println(myConst);
        System.out.println("Please fix me :)");
    }
}

Example project

In this post I will use code base from the project on GitHub. The project looks like this:

鈹溾攢鈹 pom.xml (Maven configuration)
鈹斺攢鈹 src
    鈹斺攢鈹 main
        鈹斺攢鈹 java
            鈹斺攢鈹 codes
                鈹斺攢鈹 hubertwo
                    鈹斺攢鈹 maven
                        鈹斺攢鈹 spotless
                            鈹斺攢鈹 App.java (Messy source code)

In real life, projects have much more files but for the demonstration purposes I keep it as simple as possible. We have Maven configuration pom.xml and App.java which contains messy code (presented at the beginning of the post).

Adding plugin to pom.xml

First things first, we need to add Spotless plugin to Maven configuration pom.xml.

<plugin>
    <groupId>com.diffplug.spotless</groupId>
    <artifactId>spotless-maven-plugin</artifactId>
    <version>2.9.0</version>
</plugin>

Since Spotless supports many languages like Java, Groovy or Kotlin (you can also format Markdown files), we need to add little more configuration. If we don’t do it Spotless won’t pick up our Java files and as a result won’t check and fix code style.

<configuration>
    <java>
        <includes>
            <include>src/main/java/**/*.java</include> <!-- Check application code -->
            <include>src/test/java/**/*.java</include> <!-- Check application tests code -->
        </includes>
        <googleJavaFormat>
            <version>1.15.0</version>
            <style>GOOGLE</style>
        </googleJavaFormat>
    </java>
</configuration>

This is minimal and simple configuration, and in real life project you will add more configuration. For sake of simplicity, I decided to configure default Google style. To find additional configuration parameters check links section.

Checking the code style

Ok now we should be ready to go. Let’s first check the code style, without applying any changes. To do so we will execute spotless:check goal.

> mvn spotless:check
[INFO] Scanning for projects...
...
[INFO] --- spotless-maven-plugin:2.9.0:check (default-cli) @ maven-spotless ---
[INFO] BUILD FAILURE-
...
[ERROR] Failed to execute goal com.diffplug.spotless:spotless-maven-plugin:2.9.0:check (default-cli) on project maven-spotless: The following files had format violations:
[ERROR]     src/main/java/codes/hubert/maven/spotless/App.java
[ERROR]         @@ -1,22 +1,13 @@
[ERROR]          package路codes.hubert.maven.spotless;
[ERROR]          
[ERROR]         -/**
[ERROR]         -路*路Messy
[ERROR]         -路*路路路路路路路路路路code
[ERROR]         -路*路路路路路路路路路路makes
[ERROR]         -路*路路路路路路路路路路路programmers
[ERROR]         -路*路life
[ERROR]         -路*路路harder.</p>
[ERROR]         -路*/
[ERROR]         +/**路Messy路code路makes路programmers路life路harder.路*/
[ERROR]          public路class路App路{
[ERROR]          
[ERROR]         -路路final路static路public路String路myConst路="Lorem路ipsum路dolor路sit路amet,路consectetur路adipiscing路elit,路sed路do路eiusmod路tempor路incididunt路ut路labore路";
[ERROR]         +路路public路static路final路String路myConst路=
[ERROR]         +路路路路路路"Lorem路ipsum路dolor路sit路amet,路consectetur路adipiscing路elit,路sed路do路eiusmod路tempor路incididunt路ut路labore路";
[ERROR]          
[ERROR]          路路public路static路void路main(String[]路args){
[ERROR]         -路路路路System.out
[ERROR]         -路路路路路路路路路路路路.println(myConst);
[ERROR]         -路路路路System
[ERROR]         -路路路路路路路路路路路路.out
[ERROR]         -路路路路路路路路路路路路.println("Please路fix路me路:)");
[ERROR]         +路路路路System.out.println(myConst);
[ERROR]         +路路路路System.out.println("Please路fix路me路:)");
[ERROR]          路路}
[ERROR]         -}
[ERROR]         +}

As we expected the build failed due to code style violations. From output above we can observe that Spotless is very verbose. It shows the diff between current code and code after auto-formatting. Note that, no code changes were applied.

Fixing code style errors automatically

Now when we know that we need to fix the code style, we can do it manually or use Spotless to fix all formatting issues for us! The decision is yours, but I would go with Spotless. What we need to do is to run spotless:apply goal, wait few seconds and voil脿!

> mvn spotless:apply
[INFO] Scanning for projects...
[INFO] -------------< codes.hubert.maven.spotless:maven-spotless >-------------
[INFO] Building maven-spotless 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] --- spotless-maven-plugin:2.9.0:apply (default-cli) @ maven-spotless ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.102 s

In contrast to check goal the apply goal is not verbose at all. It just simply tells us if formatting was done or not. The main change we can observe are the changes in source code. Our example App.java file should now be modified and formatting rules should be applied. You can see the differences in your IDE or compare code mentioned earlier.

To show that Spotless applied changes I will use Git here:

> git diff .
diff --git a/src/main/java/codes/hubert/maven/spotless/App.java b/src/main/java/codes/hubert/maven/spotless/App.java
index 175311b..b382cc7 100644
--- a/src/main/java/codes/hubert/maven/spotless/App.java
+++ b/src/main/java/codes/hubert/maven/spotless/App.java
@@ -1,22 +1,13 @@
 package codes.hubert.maven.spotless;
 
-/**
- * Messy
- *          code
- *          makes
- *           programmers
- * life
- *  harder.</p>
- */
+/** Messy code makes programmers life harder. */
...

Now once the code is formatted and follows coding style you can commit it.

Troubleshooting

Support for Java 17

At them moment of writing this post Spotless won’t work with Java 16, Java 17+ out of the box. If you try to use Spotless you will probably get Maven build error:

[ERROR] Failed to execute goal com.diffplug.spotless:spotless-maven-plugin:2.9.0:apply (default-cli) on project maven-spotless: Execution default-cli of goal com.diffplug.spotless:spotless-maven-plugin:2.9.0:apply failed: java.lang.reflect.InvocationTargetException: class com.google.googlejavaformat.java.JavaInput (in unnamed module @0x6cf31447) cannot access class com.sun.tools.javac.parser.Tokens$TokenKind (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.parser to unnamed module @0x6cf31447 -> [Help 1]

Fortunately there’s workaround for this! To make Spotless work simply create new file in you project directory jvm.config and put params listed below in it.

 --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

You can check if problems with Java 16+ were solved here.

TL;DR - Maven configuration

You can find the link to repository with example code at the bottom of this page. However, if you are just looking for ready to use solution, you can find full configuration below.

pom.xml

<plugin>
    <groupId>com.diffplug.spotless</groupId>
    <artifactId>spotless-maven-plugin</artifactId>
    <version>2.9.0</version>
    <configuration>
        <java>
            <includes>
                <include>src/main/java/**/*.java</include>
                <include>src/test/java/**/*.java</include>
            </includes>
            <googleJavaFormat>
                <version>1.15.0</version>
                <style>GOOGLE</style>
            </googleJavaFormat>
        </java>
    </configuration>
</plugin>

For Java 16, Java 17, Java 18:
.mvn/jvm.config

 --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED 
 --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

Summary

In few steps we configured the Maven Spotless plugin, checked for code violations, applied auto formatting, compared the differences and made Spotless to run with Java 17.

Source code

All code samples and Maven project described in this post is available on GitHub Fixing code style errors with Spotless and Maven.
If you found this post useful do not forget to leave a 猸愶笍 on GitHub :) Thanks!

Read more

  1. Spotless on GitHub
  2. Spotless Java/Maven configuration
  3. Google Java Style Guide