Today we are going to look at some small tricks that will make you more effective when using Maven! Some of this will be simple, and some of them you might not have heard about. I've done some presentations at work and similar earlier about this, and there is almost always something new to people. The topics range from basic usage and flags, to ways of handling your dependencies. One or two might be a bit enterprise specific, but most can be useful no matter if you program inside a company or not. (no secret that I love those of you who, like myself, LOVE computers with a burning passion! <3 )
If you don't know, Maven is a build automation tool (dependency management, building, running tests etc.) used for languages on the JVM (Java Virtual Machine). I use it when programming in Kotlin and Java, with the latter becoming less and less frequent (Kotlin rules!!). Several people will probably ask why I'm not using Gradle instead, but that is a discussion for another time.
There is probably much more that could be covered, but I think this is the most important once you know the basics. If you are new to Maven, suggest starting with an introduction like this. The guide covers creating projects as well, but many modern frameworks have easier and better generators. Spring Boot have its own initializer website, and I've made a CLI version. Quarkus also have a nice web UI you can use to create new projects.
My last self-promotion before we begin… General command line skills can be very useful when working with almost any command line tool, including Maven. If you need a refresher, I recently wrote a guide to getting started with the command line (important topics: piping, searching with grep, less).
Basic usage and flags
Check Maven version
I want to show this for one reason, it's useful when checking for possible errors. Simply type
mvn -v and you will get information about the Maven and Java versions currently being used:
(remember that the Java version can be changed by changing where the environment variable JAVAHOME points to!)
package vs install
You may have noticed that you have a .m2-folder in your home directory. This can contain a settings.xml file (if you have specific local settings like proxies), as well as your local Maven repository (where Maven downloads and stores dependencies and artifacts locally). Why is this important? package builds your project (compiles, run tests etc.), and install does the same in addition to installing it (copying it) to your local Maven repository (.m2/repository).
NB! As package does not install to .m2 directory you can get some weird issues in multi-module Maven projects with dependencies to each other. Maven reads the dependencies from this local repository, and if it doesn't find it there, it will try the to download it.
using -f option to run a specific pom file
Just like the title says. If you do something like
mvn -f mymodule/pom.xml install, Maven will build based upon the pom.xml file in the directory mymodule instead of the directory you are currently in.
using -X option to get debug information
mvn -X goals (replace goals with the actual Maven goals!) will show you looooots of information you can use when debugging possible issues. Just be aware that this is a lot of information! Ranging from all packages imported, what settings files are used (local for your user as well as global settings for the computer), complete configurations for things like Surefire (testing) and so on.
Now a little story time! I recently helped someone I know with some issues they were having with Maven. All dependencies (within an enterprise) timed out when downloading, and there was a note of a proxy we couldn't find in his local .m2/settings.xml file. How did we find the issue you may ask? Was it some global settings in a parent pom? Well, actually no! Within the first 100 lines of running
mvn -X install we got our answer. A global settings file was put into Mavens install directory (probably by mistake and forgotten). By using the -X option we found the problem, without scratching our heads for days :)
Run single test(class)
You can run only tests with
mvn test, but did you know you can run just a single test class by using
mvn test -Dtest=MyClassTest? If you have several test classes with that name, use the fully qualified name (including package, example: com.example.MyClassTest).
Want to run just a single test method within that test class? Just as simple!
mvn test -Dtest=MyClassTest#myMethod will run the test myMethod within your test class.
Don't trim stack trace
Sometimes you get unexpected nullpointers or other exceptions within the method you are testing. Some times you will se a very minimal stack trace with just your test method, and the rest cut off (aka trimmed). If you want the complete stack trace with causes, line numbers etc., then you can use the definition
-DtrimStackTrace=false! (works with test, package install, other goals, etc.). Helps me a lot, especially since I prefer to use the command line instead of big bloaty IDEs.
Want to skip all tests when building?
mvn install -DskipTests will run install like usual, but not run any tests (but they will still be compiled!).
mvn dependency:tree is one of the most useful commands for checking the dependencies in your project, including transitive dependencies (dependencies of your dependencies and so on). This include all versions of dependencies being used. These reports can be quite long for bigger projects, so it can be useful to pipe the result to less or search it using grep (if you have a suspicion of deprecated code and similar causing weird behavior).
Have a suspicion that you use conflicting versions of packages? Or have unused dependencies in your project?
mvn dependency:analyze can help you here! It will print all info like this. (also useful to browse by piping the output to less!).
NB! Some dependencies in big frameworks like Spring might not be used directly at compile time, so there might be some false positives in the unused packages report.
Many enterprises (and maybe open source projects) have hierarchies of parent pom files to handle global settings, approved dependency versions and so on. Sometimes you may want to see what version of parent poms you are currently using (maybe to debug if you are using the correct one, because these things can be updated!). Then
mvn dependency:display-ancestors is your friend! Running it with a new Spring Boot project will show you the version of the Spring Boot starter poms it's using:
Maven have a versions plugin that can be used for several operations relating to versions. The only command here I find useful is
mvn versions:set -DnewVersion=1.0.0. This will set the new versions of your project (including submodules) to 1.0.0. No need to edit the files manually! Neat!
Bonus: ZSH completion
Oh-My-ZSH has some really useful completions so you don't have to memorize any Maven commands. Just add mvn to your plugins list, open a new tab in your terminal (or new session) and you get nice completion! (you probably need Maven installed first, just to mention it…). Mine looks like this
plugins=(git z gh mvn thefuck) on my personal machine (kubectl is also super useful if you use Kubernetes!).
Let's do an example. Let's say I type
mvn dependency: and press tab. Then ZSH will suggest the possible completions for me:
(you can off course complete anything, including goals like package, deploy etc. I just wanted to show something that didn't cover my entire window ;) )
One thing worth mentioning is that this plugin completes the Spring Boot goals like
mvn spring-boot:run and Quarkus goals like
quarkus:dev as well! How cool is that?!?