How to do a Maven to Gradle migration on a Java Spring Boot project

Maven to Gradle migration

If you’re planning to migrate your Maven project to Gradle, you’ll be looking for the path of least resistance to get the job done as easily as possible. In this article you’ll discover the 8 recommended steps to migrate your Java Spring Boot project from Maven to Gradle.

Why do a migration to Gradle?

If you’re reading this article you probably know why you want to move to Gradle, but some of the reasons probably include:

  • Gradle’s Groovy syntax is less verbose than Maven’s xml
  • easier to configure thanks to it’s graph based task hierarchy
  • features incremental builds to avoid rerunning tasks
  • 2-10 times faster than Maven (according to the Grade docs)

When running your migration, it’s important to run through a pre-determined list of steps and commit your code after each one. With a Spring Boot project, these steps should end up with having the same (or very similar) built jar file, the application running exactly as before, and with the same tests running.

Starting point: a Maven project

We’ll be using an example Maven project which you can download from this GitHub repository. We’ll go back to the initial commit containing the original Maven project:

git clone https://github.com/tkgregory/maven-to-gradle.git
cd maven-to-gradle
git checkout d5fa646871b28a622cca10fb1ca5a52573566fc1

To build and run the Spring Boot application, use the included Maven wrapper script:

./mvnw spring-boot:run

The application includes the following functionality:

  • creates a git.properties file at build time containing the git commit hash
  • exposes this information on http://localhost:8080/actuator/info (Spring Boot does this automatically when git.properties is provided)
  • has a @SpringBootTest test to check that the application starts up
  • depends on Spring Boot web, data-jpa, actuator, and test libraries

Running the migration

You can see each stage of this process in the list of commits of our example project. At each stage described below you’ll also find a link to the commit diff so you can see exactly what’s going on.

1) Generate a build scan

The Gradle build scan can run against a Maven or Gradle project, and publishes useful build information to gradle.com for you to access. We’ll use it to get an insight into the Maven build, and later on to compare to the Gradle version.

Add the file .mvn/extensions.xml with the following contents:

<extensions>
    <extension>
        <groupId>com.gradle</groupId>
        <artifactId>gradle-enterprise-maven-extension</artifactId>
        <version>1.3.6</version>
    </extension>
</extensions>

Then run mvn install and accept the terms of service when prompted.

Click on the link provided, enter your email address, then click the link in the email in your inbox to see the scan results.

Total build time

Select Performance on the left hand menu, and you’ll see our build took 12 seconds in total:

Plugins

The Plugins area will be useful to us later to make sure our Gradle build has all the required plugins:

2) Run an automatic conversion (commit)

Thankfully Gradle can do a share of the heavy lifting and automatically generate the build.gradle file based on the pom.xml. It will even convert dependencies, but unfortunately not plugins.

Multi-module projects: if you have a multi-module Maven project you can follow the same process, as Gradle will automatically create a build.gradle file for each pom.xml in your project, and link them together correctly.

Install or update Gradle

Make sure you’re on the latest version of Gradle. At the time of writing this was 6.1.1.

gradle --version

Run gradle init

Navigate to the project root and run:

gradle init

Type yes when prompted if you want to generate a Gradle build.

The following Gradle resources have now been created:

  • build.gradle file – the pom.xml file equivalent
  • settings.gradle file – sets your project name
  • gradlew and gradlew.bat wrapper scripts – you should use these whenever you run Gradle tasks to ensure you’re using the correct version of Gradle
  • a gradle directory – this contains the wrapper jar file

Edit your .gitignore file and add the following entries for directories that shouldn’t be added to version control:

.gradle/
build/

3) Build your project and work through any errors (commit)

Now we can try to build using Gradle, although we may get errors since the automatic conversion is just a ‘best-effort’ attempt:

./gradlew build

Gradle can’t find the required repositories. In this case we have to declare mavenCentral() in the list of repositories in build.gradle so that Gradle knows where to look.

repositories {
    mavenLocal()
    mavenCentral()
    maven {
        url = 'http://repo.maven.apache.org/maven2'
    }
}

Try ./gradlew build again and you’ll get a success:

4) Compare the built artifacts (commit)

Add the following script to compareArtifacts.sh. It will compare the size of the built artifacts in Maven’s target and Gradle’s build directories.

#!/bin/sh
MAVEN=target/maven-to-gradle-0.0.1-SNAPSHOT.jar
GRADLE=build/libs/maven-to-gradle-0.0.1-SNAPSHOT.jar

ls -l --block-size=M $MAVEN  | awk '{print $9, $5}'
ls -l --block-size=M $GRADLE  | awk '{print $9, $5}' 

Now run it:

./compareArtifacts.sh

We’re missing 40MB of code here. Could be important? ⚠️

What’s happened is that Gradle isn’t building the full executable ‘fat’ Spring Boot jar since we don’t have the correct plugin. Add the Spring Boot plugins (commit) to the plugins section:

    id 'org.springframework.boot' version '2.2.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'

Now build again with ./gradlew build, then run ./compareArtifacts.sh again:

Much better! Of course this verification method is by no means bulletproof, but it gives us a good indication that we’re on the right track.

Complex builds: if you have a complex build, I recommend extracting the two jar files (jar -xvf <jar-file-name>) and comparing the contents using a diff tool.

5) Run a build scan on the Gradle build to see which plugins are missing

Now let’s run a scan on our Gradle build using ./gradlew build --scan and compare with the Maven build scan:

Maven
Gradle

Both Maven and Gradle add a lot of plugins by default. Ignoring all the org.apache.maven.plugins plugins, we can see that we should include:

  • spring-boot-maven-plugin – we already added this one
  • git-commit-id-plugin – we don’t have this one yet, meaning our http://localhost:8080/actuator/info endpoint won’t work. You can see this by running ./gradlew bootRun:
Missing git.properties file

6) Convert plugins (commit)

At this point we need to convert any remaining plugins to Gradle. Usually there’s a direct equivalent or very similar plugin.

For generating the git.properties file we’ll add this plugin to plugins block:

id "com.gorylenko.gradle-git-properties" version "2.2.1"

And add it’s configuration:

gitProperties {
    gitPropertiesName = "git.properties"
    gitPropertiesResourceDir = "$buildDir/classes"
    keys = ['git.commit.id']
}

Let’s run ./gradlew build and we can see the git.properties file has been generated in build/classes:

git.commit.id=eea1a128...'

7) Ensure tests are running (commit)

Run ./gradlew test and you’ll see from the test report in build/reports/tests/test/index.html that our test is not running:

Enable JUnit 5 tests by adding the following test configuration to the end of build.gradle:

test {
    useJUnitPlatform()
}

Run ./gradlew test again and the test report should now look much better:

8) Manually test the application (if applicable)

If you have a full automated test suite then this step might not be required, but in our case we’re going to manually start the application and verify it has the expected behaviour.

Start the application in Gradle:

./gradlew bootRun

Does http://localhost:8080/actuator/info return us what we expect?

Looks good! ✅ How about http://localhost:8080/vegetables?

Yup, two of your five-a-day. 🍆

Conclusion

Converting your project from Maven to Gradle doesn’t have to be difficult if you follow these steps carefully, ensuring you commit at each stage in case you need to go back to a previous point.

Once your conversion is complete, if you’re working in a team I recommend keeping the Maven build files for a few weeks until your teammates have moved across to Gradle. Don’t forget to also update your CI server.

Then it’s time for the fun part, deleting the pom.xml files and enjoying your more powerful Gradle build.

Resources

CODE
See the code for this example in this GitHub repository

DOCUMENTATION
Checkout out Gradle’s own useful guide to migrations

VIDEO
If you prefer to learn in video format, check out this accompanying video to this post on the Tom Gregory Tech YouTube channel.

How to do a Maven to Gradle migration on a Java Spring Boot project

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top

To keep up to date with all things to do with scaling developer productivity, subscribe to my monthly newsletter!