Maven ·
‹ Previous
⏱ 5 min read Modified: 2026-11-08

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.

  1. 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.
  2. Generate a project from the maven-archetype-quickstart archetype 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.
  3. Open pom.xml: the maven.compiler.release property must not be higher than your JDK version. Draw the directory tree of the project in a README and, for src/main/java, src/test/java and target, write one line on what is stored there and whether it belongs in Git.
  4. Replace the App class with a Calculator class with add, subtract, multiply and divide methods for int. On division by zero, divide throws an IllegalArgumentException with a clear message, and main prints the results of several operations, including a caught division by zero.
  5. Run mvn compile and find the compiled class in target/classes. Then run mvn package and start the program: java -cp target/calculator-1.0-SNAPSHOT.jar com.examclouds.Calculator.
  6. Open the jar as a zip archive and list what is inside. Is the AppTest class, which the archetype created in src/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).

  1. Run mvn clean, mvn compile, mvn test, mvn package and mvn verify one after another. Fill in a table: command → which plugin goals ran (in order) → what appeared in target.
  2. Run mvn clean and right after it mvn surefire:test. How many tests ran, and why? Compare with mvn test and explain how invoking a goal differs from invoking a phase.
  3. Compare mvn clean package -DskipTests and mvn clean package -Dmaven.test.skip=true: is there a target/test-classes directory after each command, and what does surefire print in the log?
  4. Add a compilation error to the AppTest test (for example, remove a semicolon). Which of the two commands from step 3 still builds the jar, and why?
  5. Run mvn compile twice 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.

  1. Create a pom.xml in the project root. Coordinates: groupId — your reversed domain or com. plus your last name, artifactId — the project name in lowercase with hyphens (for example, homework), version — 1.0-SNAPSHOT, packaging — jar.
  2. Move the files into the standard layout: sources to src/main/java keeping the packages, configuration and data files to src/main/resources, tests to src/test/java. Do not override sourceDirectory in <build>: moving the files to the standard is better than bending Maven to the old layout.
  3. In <properties>, set the Java version with the single property maven.compiler.release (21, 25 or your JDK version) and the source encoding project.build.sourceEncoding = UTF-8. Do not copy the variant from old tutorials — maven-compiler-plugin 2.x with source/target = 1.8; why release is safer is explained in maven-compiler-plugin.
  4. Replace the jar files in the lib directory (or libraries added to the IDE by hand) with entries in <dependencies>. Find the coordinates of each library on Maven Central and choose the scope: 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-lang3 for working with strings) and use it in at least one place.
  5. Configure maven-jar-plugin to 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).
  6. Build the project with mvn clean package and run java -jar target/homework.jar. If the program uses third-party libraries and fails with NoClassDefFoundError, explain why, copy the dependencies with mvn dependency:copy-dependencies (they go to target/dependency) and run the program with them on the classpath: java -cp "target/homework.jar:target/dependency/*" com.example.Main, where com.example.Main is your main class (on Windows the separator is ;).
  7. Add a .gitignore with target/, remove the lib directory and the IDE build files from the repository, and reopen the project in IntelliJ IDEA through pom.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.

  1. In shop-utils (groupId — com.examclouds, version — 1.0-SNAPSHOT), write a PriceFormatter class with a format(BigDecimal price, Locale locale) method that formats a price in the currency of the locale with NumberFormat.getCurrencyInstance(locale).
  2. In shop-app, declare a dependency on shop-utils and run mvn compile before installing the library. Write down the error: in which repository did Maven look for the artifact?
  3. Run mvn install in shop-utils. Find the library directory in ~/.m2/repository and list the files that were put there. Build shop-app again and print the price 1299.9 for Locale.US and Locale.GERMANY.
  4. Add a formatWithDiscount(BigDecimal price, int percent, Locale locale) method to PriceFormatter and, without reinstalling the library, call it from shop-app. Explain the error and fix it with one command.
  5. Change the library version to 1.0, install it and switch shop-app to it. How does a release version differ from a SNAPSHOT for Maven in later builds?
  6. Build shop-app offline: mvn -o package. Why does the build pass without a network? Delete the directory com/examclouds/shop-utils from ~/.m2/repository and 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.

  1. The library report-lib depends on com.google.guava:guava:19.0 and has a method that pads a string with spaces using Strings.padEnd() from Guava.
  2. The library http-lib depends on com.google.guava:guava:33.4.0-jre and has a method that turns a list of strings into an immutable list with the ImmutableList.toImmutableList() collector, which appeared in Guava 21.
  3. The application conflict-app declares both libraries — report-lib first, then http-lib — and calls the http-lib method from main. Build the application and run it with mvn exec:java -Dexec.mainClass=com.examclouds.App so that Maven assembles the classpath. The build passes, but the run fails: write down the exception.
  4. Run mvn dependency:tree and mvn 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.
  5. 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> on report-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?
  6. Run mvn dependency:analyze on 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.

  1. Set the versions of maven-compiler-plugin, maven-surefire-plugin and maven-jar-plugin in <pluginManagement>. Run mvn help:effective-pom and find which other plugins Maven added on its own, and with which versions.
  2. Configure maven-jar-plugin: the main class of the store (the one whose main method shows the menu, for example shop.ShopApp) goes into the manifest, and <finalName> is shop. The command java -jar target/shop.jar must open the store menu. Check the file META-INF/MANIFEST.MF inside 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.
  3. Generate the store documentation with mvn javadoc:javadoc instead of the manual javadoc -d docs -sourcepath src ... call from the Javadoc part. Which directory inside target did 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.
  4. Add maven-checkstyle-plugin with the google_checks.xml rule set and the check goal in <executions>. Run mvn 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.
  5. Add spotbugs-maven-plugin with the check goal 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 remove hashCode() from the product class while keeping equals(), and make sure mvn verify fails with a SpotBugs message. Why do old tutorials use findbugs-maven-plugin instead, 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.

  1. Create a root project shop with <packaging>pom</packaging> and the modules shop-model (a record Product with a name and a price), shop-service (a CartService class with a list of products and a total() method) and shop-app (a Main class that fills the cart and prints the total).
  2. 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.
  3. Define the dependencies between the modules: shop-service depends on shop-model, shop-app on shop-service. If Main creates Product objects directly, declare a dependency on shop-model as well — and explain why this is better than relying on the transitive one.
  4. Run mvn clean verify from the root. List the modules in <modules> in reverse order and compare with the Reactor Build Order block in the log: why did the build order not change?
  5. Delete the directory com/examclouds from ~/.m2/repository (if the modules were installed before) and run mvn -pl shop-app package from the root. Why does the build fail? Fix the command with a reactor option, then build everything that depends on shop-model with -amd.
  6. Add a dependency on shop-service to shop-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.

  1. Create the file src/main/resources/app.properties with the keys app.env, db.url and log.level, whose values are the expressions ${app.env}, ${db.url} and ${log.level}, and enable resource filtering in <build>.
  2. Declare the profiles dev (active by default via activeByDefault) and prod with different values of the three properties. A Main class reads app.properties from the classpath and prints all three values.
  3. Build the project with mvn clean package and with mvn clean package -P prod, run the program after each build and compare the output and the contents of target/classes/app.properties.
  4. Add a ci profile that is activated when the environment variable CI is set (the condition env.CI in <activation>) and sets only log.level. Set the variable, run mvn help:active-profiles and rebuild the project. Is dev still active? What ended up in app.properties instead of app.env and db.url, and why?
  5. Fix the problem from step 4 so that the development values apply whenever prod is not selected. Suggest two ways.
  6. Explain why the production database password must not be written into the prod profile in pom.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

Please log in or register to have a possibility to add comment.