Maven. Practical Tasks
These Maven exercises practice the module «Maven: Build and Dependency Management»: you will create a project from an archetype, build and run it, use the logs to work out lifecycle phases and plugin goals, move your own study project to Maven with a pom.xml and an executable jar, install your own library into the local repository, reproduce and fix a Guava version conflict, add code quality plugins, split a shop into modules and set up dev and prod profiles. All 8 tasks have hints and follow the order of the lessons in the module.
What to Review Before You Start
The tasks on this page need JDK 21 or newer, Maven 3.9 (or the Maven Wrapper) and internet access for the first build: Maven downloads plugins and libraries from Maven Central into the local repository. Each task builds on one or two lessons of the module — if anything in the table feels unclear, open the lesson first.
| Lesson | What you need from it |
|---|---|
| How to Install Maven | mvn -version, JAVA_HOME and PATH |
| How to Create a Maven Project | mvn archetype:generate, the standard directory layout, choosing GAV coordinates |
| Maven Build Lifecycle | phases and plugin goals, -DskipTests vs -Dmaven.test.skip |
| Maven pom.xml Explained | GAV and SNAPSHOT, <properties>, maven.compiler.release, <build> |
| Maven Dependencies | scopes, transitive dependencies, nearest wins, dependency:tree, <exclusions>, <dependencyManagement> |
| Maven Repository Types | the local repository ~/.m2, mvn install, offline mode |
| Maven Plugins | <pluginManagement>, Main-Class in the manifest, Checkstyle, SpotBugs |
| Maven Multi-Module Project | parent POM, <modules>, reactor order, -pl, -am, -amd |
| Maven Profiles | activeByDefault, -P, activation by an environment variable, resource filtering |
| Maven Commands Cheat Sheet | mvn options for tests, dependencies and diagnostics |
Your First Maven Project: a Calculator from the quickstart Archetype
Create a small Maven project from scratch, build it and run it. The exercise checks that Maven is installed and that you know what lives in each directory of the project and what ends up in the jar.
- Check the installation with
mvn -version: the output should show the Maven version and the Java version you are using. If the command is not found or shows a different JDK, go back to Common installation errors. - Generate a project from the
maven-archetype-quickstartarchetype in batch mode (-DinteractiveMode=false):groupId—com.examclouds,artifactId—calculator. Set the archetype version explicitly (1.5 or newer), otherwise you get a project with outdated settings. - Open
pom.xml: themaven.compiler.releaseproperty must not be higher than your JDK version. Draw the directory tree of the project in a README and, forsrc/main/java,src/test/javaandtarget, write one line on what is stored there and whether it belongs in Git. - Replace the
Appclass with aCalculatorclass withadd,subtract,multiplyanddividemethods forint. On division by zero,dividethrows anIllegalArgumentExceptionwith a clear message, andmainprints the results of several operations, including a caught division by zero. - Run
mvn compileand find the compiled class intarget/classes. Then runmvn packageand start the program:java -cp target/calculator-1.0-SNAPSHOT.jar com.examclouds.Calculator. - Open the jar as a zip archive and list what is inside. Is the
AppTestclass, which the archetype created insrc/test/java, there?
Hint
If java -cp prints Could not find or load main class, check that you passed the fully qualified class name (com.examclouds.Calculator) and that you run the command from the project root, where the target directory is. Classes from src/test/java are compiled into target/test-classes and do not go into the jar. Why java -jar does not work for this jar is covered in the plugins exercise.
Phases and Goals: What mvn package Really Runs
Run an experiment on the calculator project from the previous exercise and use the Maven logs to find out which plugin goals each command executes. The log lines you need look like [INFO] --- surefire:3.5.2:test (default-test) @ calculator ---: the plugin prefix, its version, the goal and the execution id (your versions may differ).
- Run
mvn clean,mvn compile,mvn test,mvn packageandmvn verifyone after another. Fill in a table: command → which plugin goals ran (in order) → what appeared intarget. - Run
mvn cleanand right after itmvn surefire:test. How many tests ran, and why? Compare withmvn testand explain how invoking a goal differs from invoking a phase. - Compare
mvn clean package -DskipTestsandmvn clean package -Dmaven.test.skip=true: is there atarget/test-classesdirectory after each command, and what does surefire print in the log? - Add a compilation error to the
AppTesttest (for example, remove a semicolon). Which of the two commands from step 3 still builds the jar, and why? - Run
mvn compiletwice in a row and find the line in the second log saying there is nothing to compile. Change one class and run it again: what changed in the log?
The result is the table and short answers to the questions in a README.
Finding the Goals in the Log Quickly
Filter the output by the three dashes: mvn package | grep -- "--- " on Linux, macOS and Git Bash, or mvn package | findstr /C:"--- " on Windows. You get the list of executed goals without the rest of the noise. A phase runs all previous phases of its lifecycle, while a plugin goal invoked directly runs alone.
Move a Study Project to Maven: pom.xml, Dependencies and an Executable Jar
Online Store · part 11 of 13 · ← part 10 · part 12 →
Take the console Online Store from the course project and move it to Maven so that it builds with a single command on any computer with a JDK and Maven.
- Create a
pom.xmlin the project root. Coordinates:groupId— your reversed domain orcom.plus your last name,artifactId— the project name in lowercase with hyphens (for example,homework),version—1.0-SNAPSHOT,packaging—jar. - Move the files into the standard layout: sources to
src/main/javakeeping the packages, configuration and data files tosrc/main/resources, tests tosrc/test/java. Do not overridesourceDirectoryin<build>: moving the files to the standard is better than bending Maven to the old layout. - In
<properties>, set the Java version with the single propertymaven.compiler.release(21, 25 or your JDK version) and the source encodingproject.build.sourceEncoding=UTF-8. Do not copy the variant from old tutorials —maven-compiler-plugin2.x withsource/target=1.8; whyreleaseis safer is explained in maven-compiler-plugin. - Replace the jar files in the
libdirectory (or libraries added to the IDE by hand) with entries in<dependencies>. Find the coordinates of each library on Maven Central and choose thescope: test libraries —test, a JDBC driver —runtime, everything else —compile. If the project has no libraries, add one that simplifies your code (for example,org.apache.commons:commons-lang3for working with strings) and use it in at least one place. - Configure
maven-jar-pluginto put the main class into the jar manifest, and set<finalName>so that the jar has no version in its name (for example,homework.jar). - Build the project with
mvn clean packageand runjava -jar target/homework.jar. If the program uses third-party libraries and fails withNoClassDefFoundError, explain why, copy the dependencies withmvn dependency:copy-dependencies(they go totarget/dependency) and run the program with them on the classpath:java -cp "target/homework.jar:target/dependency/*" com.example.Main, wherecom.example.Mainis your main class (on Windows the separator is;). - Add a
.gitignorewithtarget/, remove thelibdirectory and the IDE build files from the repository, and reopen the project in IntelliJ IDEA throughpom.xml: the IDE should pick up the dependencies and the Java version on its own.
The result is a project that builds with mvn clean verify right after git clone, with no manual steps.
Where to Start
Do not move everything at once. First create pom.xml and get mvn compile to pass on the code without external libraries, then add the dependencies one at a time: for every package ... does not exist error, find the library on Maven Central that contains that package. mvn dependency:tree shows which versions Maven actually resolved.
Your Own Library in the Local Repository: mvn install and SNAPSHOT
Create two separate Maven projects: a library shop-utils and an application shop-app that uses it. The exercise shows how an artifact gets into the local repository ~/.m2/repository and where another project picks it up from.
- In
shop-utils(groupId—com.examclouds,version—1.0-SNAPSHOT), write aPriceFormatterclass with aformat(BigDecimal price, Locale locale)method that formats a price in the currency of the locale withNumberFormat.getCurrencyInstance(locale). - In
shop-app, declare a dependency onshop-utilsand runmvn compilebefore installing the library. Write down the error: in which repository did Maven look for the artifact? - Run
mvn installinshop-utils. Find the library directory in~/.m2/repositoryand list the files that were put there. Buildshop-appagain and print the price 1299.9 forLocale.USandLocale.GERMANY. - Add a
formatWithDiscount(BigDecimal price, int percent, Locale locale)method toPriceFormatterand, without reinstalling the library, call it fromshop-app. Explain the error and fix it with one command. - Change the library version to
1.0, install it and switchshop-appto it. How does a release version differ from a SNAPSHOT for Maven in later builds? - Build
shop-appoffline:mvn -o package. Why does the build pass without a network? Delete the directorycom/examclouds/shop-utilsfrom~/.m2/repositoryand repeat the command: what changed?
A Quirk to Remember
mvn install puts the artifact only into your own local repository. On another computer or a CI server, shop-app will not build until the library is published to a shared repository with mvn deploy. That is why “it builds on my machine but not on yours” often points to an artifact that exists only in someone’s ~/.m2.
A Guava Version Conflict: Reproduce NoSuchMethodError and Fix It
Reproduce a version conflict between transitive dependencies, find it with mvn dependency:tree and fix it in three ways. You need three small projects with the groupId com.examclouds; install the libraries into the local repository with mvn install, as in the previous exercise.
- The library
report-libdepends oncom.google.guava:guava:19.0and has a method that pads a string with spaces usingStrings.padEnd()from Guava. - The library
http-libdepends oncom.google.guava:guava:33.4.0-jreand has a method that turns a list of strings into an immutable list with theImmutableList.toImmutableList()collector, which appeared in Guava 21. - The application
conflict-appdeclares both libraries —report-libfirst, thenhttp-lib— and calls thehttp-libmethod frommain. Build the application and run it withmvn exec:java -Dexec.mainClass=com.examclouds.Appso that Maven assembles the classpath. The build passes, but the run fails: write down the exception. - Run
mvn dependency:treeandmvn dependency:tree -Dverbose -Dincludes=com.google.guava:guava. By which rule did Maven pick version 19.0? Swap the two libraries in<dependencies>and check whether the result changes. - Restore the original order and fix the conflict in three ways, checking the tree and the run each time: (a) a direct dependency on the right Guava version in
conflict-app; (b)<exclusions>onreport-lib; (c) the Guava version in<dependencyManagement>. Which way would you choose if three more libraries with Guava inside were added to the project tomorrow? - Run
mvn dependency:analyzeon the final variant and explain every warning.
Why the Compiler Did Not Help
The compiler checks only the code of conflict-app itself, while the call to toImmutableList() sits in the already compiled class of http-lib. The JVM detects the incompatible library version only at run time, when it loads the class and looks up the method. If -Dverbose does not show the omitted version, set a recent maven-dependency-plugin version explicitly, as shown in mvn dependency:tree.
Online Store: an Executable Jar, Javadoc and Code Checks with Maven Plugins
Online Store · part 12 of 13 · ← part 11
Continue with the Online Store that you moved to Maven in the exercise «Move a Study Project to Maven». It already has the packages shop.model and shop.service, writes the purchase receipt to a text file with PrintWriter as in the part «Save the Purchase Receipt to a File», and carries the Javadoc comments from the part «Document the Classes with Javadoc». Configure plugins so that the store starts with one command, Maven builds the documentation, and code problems are caught during the build. Set plugin versions explicitly; take the current numbers from Maven Central.
- Set the versions of
maven-compiler-plugin,maven-surefire-pluginandmaven-jar-pluginin<pluginManagement>. Runmvn help:effective-pomand find which other plugins Maven added on its own, and with which versions. - Configure
maven-jar-plugin: the main class of the store (the one whosemainmethod shows the menu, for exampleshop.ShopApp) goes into the manifest, and<finalName>isshop. The commandjava -jar target/shop.jarmust open the store menu. Check the fileMETA-INF/MANIFEST.MFinside the jar, make a purchase from the jar and find which directory the receipt file, written by the store after each purchase, appeared in: a relative path is resolved against the directory the program was started from. - Generate the store documentation with
mvn javadoc:javadocinstead of the manualjavadoc -d docs -sourcepath src ...call from the Javadoc part. Which directory insidetargetdid it go to? Which options of the manual command (-sourcepath,-subpackages,-encoding) are no longer needed, and why? The output must contain no warnings about undocumented public elements. - Add
maven-checkstyle-pluginwith thegoogle_checks.xmlrule set and thecheckgoal in<executions>. Runmvn verify: did the build fail? Make violations of the Google rules break the build, fix what was found in the store classes, and add a suppression to a suppressions file for one check you disagree with. - Add
spotbugs-maven-pluginwith thecheckgoal and build the store. Go through every warning: fix it or explain why it is a false positive. If SpotBugs finds nothing, test it on a deliberate bug: temporarily removehashCode()from the product class while keepingequals(), and make suremvn verifyfails with a SpotBugs message. Why do old tutorials usefindbugs-maven-plugininstead, and why is it no longer used?
If you have not built the Online Store, take any Maven project of yours with several classes and a main method.
If Checkstyle Stays Silent
Every check in google_checks.xml has the warning severity, while the checkstyle:check goal fails the build only on error by default. The violations show up in the log, but the build still ends with BUILD SUCCESS — you need <violationSeverity>warning</violationSeverity>. If SpotBugs does not catch even the removed hashCode(), lower <threshold> to Low and make sure the goal runs after compilation.
A Multi-Module Shop: Parent POM, Modules and the Reactor Build Order
Split a small shop application into three modules and build them with one command from the root. You can take the code from your Online Store or write a minimal one: a product, a cart total and a console entry point.
- Create a root project
shopwith<packaging>pom</packaging>and the modulesshop-model(a recordProductwith a name and a price),shop-service(aCartServiceclass with a list of products and atotal()method) andshop-app(aMainclass that fills the cart and prints the total). - Move the common settings to the root POM:
maven.compiler.release, the encoding, the library versions in<dependencyManagement>and the plugin versions in<pluginManagement>. No library or plugin version number may remain in the module POMs. - Define the dependencies between the modules:
shop-servicedepends onshop-model,shop-apponshop-service. IfMaincreatesProductobjects directly, declare a dependency onshop-modelas well — and explain why this is better than relying on the transitive one. - Run
mvn clean verifyfrom the root. List the modules in<modules>in reverse order and compare with theReactor Build Orderblock in the log: why did the build order not change? - Delete the directory
com/examcloudsfrom~/.m2/repository(if the modules were installed before) and runmvn -pl shop-app packagefrom the root. Why does the build fail? Fix the command with a reactor option, then build everything that depends onshop-modelwith-amd. - Add a dependency on
shop-servicetoshop-model, write down Maven’s message about the cycle and remove the dependency.
Hint
<modules> only decides which projects are built together (aggregation), and <parent> decides where a module takes its settings from (inheritance). Here the root POM does both, but the two mechanisms are independent: a module without <parent> still builds in the reactor, but gets neither the Java version nor the managed dependency versions.
dev and prod Profiles: Environment Settings with Resource Filtering
Make the same application build with development or production settings depending on the Maven profile.
- Create the file
src/main/resources/app.propertieswith the keysapp.env,db.urlandlog.level, whose values are the expressions${app.env},${db.url}and${log.level}, and enable resource filtering in<build>. - Declare the profiles
dev(active by default viaactiveByDefault) andprodwith different values of the three properties. AMainclass readsapp.propertiesfrom the classpath and prints all three values. - Build the project with
mvn clean packageand withmvn clean package -P prod, run the program after each build and compare the output and the contents oftarget/classes/app.properties. - Add a
ciprofile that is activated when the environment variableCIis set (the conditionenv.CIin<activation>) and sets onlylog.level. Set the variable, runmvn help:active-profilesand rebuild the project. Isdevstill active? What ended up inapp.propertiesinstead ofapp.envanddb.url, and why? - Fix the problem from step 4 so that the development values apply whenever
prodis not selected. Suggest two ways. - Explain why the production database password must not be written into the
prodprofile inpom.xml, and where to keep it instead.
Easy to Get Wrong
Filtering replaces ${...} in every file of the resource directory where it is enabled. If that directory holds templates or scripts where ${...} must stay as is, Maven substitutes its own properties there and leaves unknown expressions unchanged. Keep such files in a separate resource directory without filtering.
Where Maven Solutions Usually Break
In Maven exercises the bug is rarely in the Java code: more often the build succeeds but does something other than expected, or it passes on your machine and fails on another one. So judge the result not by the BUILD SUCCESS line but by the contents of target, the program output and the plugin logs.
| Mistake | What happens | How to fix it |
|---|---|---|
A library exists only in your ~/.m2 after mvn install | The build passes for you but fails for a teammate and on CI with Could not find artifact | Check the build with an empty repository: mvn -Dmaven.repo.local=/tmp/m2 verify |
The Java version is set both by maven.compiler.release and by source/target | It is unclear which setting wins; the old source/target do not check the JDK API | Keep the single maven.compiler.release property |
| Plugin versions are missing | Maven uses the defaults of its own version — a teammate with a different Maven gets a different build | Pin the versions in <pluginManagement> |
-DskipTests “just for now” | Nobody sees the broken test, and a jar with untested code moves on | The final check of any solution is mvn clean verify without skip flags |
java -jar on a jar without a manifest or without dependencies | no main manifest attribute, or NoClassDefFoundError on the first call into a library | mainClass in maven-jar-plugin, dependencies on the classpath |
A version conflict “fixed” by copying a jar into the project or editing files in ~/.m2 | The build works only on this machine and breaks with the next update | Put everything that affects the build into pom.xml: an explicit version, <exclusions> or <dependencyManagement> |
Check Your Code
Before you submit a solution, go through the code style recommendations and a short Maven checklist: no target directory and no library jars in the repository, every dependency and plugin version is set explicitly, repeated version numbers are moved to <properties>, and mvn clean verify passes on a fresh clone of the project.
Frequently Asked Questions
In what order should I solve the Maven exercises?
In the order on the page: the exercises follow the lessons of the module. The first two check the installation and the lifecycle on a small project, moving your own project to Maven builds on them, and the version-conflict and multi-module exercises reuse mvn install from the local repository exercise.
Can I solve the exercises in IntelliJ IDEA without installing Maven?
Partly. IntelliJ IDEA ships with its own Maven, so the buttons in the Maven tool window work without an installation. The exercises, however, ask you to run mvn from the command line and read the logs, so you need either an installed Maven or the Maven Wrapper (mvnw) in the project. How to add the wrapper is shown in Creating a Project and the Maven Wrapper.
Where do I find current versions of plugins and libraries for the exercises?
The version numbers in the exercises are examples checked at the time of writing. Look up the latest version on Maven Central (central.sonatype.com) by groupId and artifactId, or run mvn versions:display-plugin-updates and mvn versions:display-dependency-updates in a working project — Maven lists what can be upgraded.
Can I do these exercises with Gradle?
No: they practice the Maven model itself — pom.xml, lifecycle phases, scopes, the version selection rule and profiles. Gradle solves the same problems differently, and half of the questions would no longer make sense. How the tools compare is covered in Maven, Ant or Gradle.
Comments