Apache Maven for Java: Installation, Builds, Dependencies, CI, and Troubleshooting

· Published · 21 min read

Java source and project configuration move through dependency resolution, compile, test, package, verify, install, and controlled artifact publication with quality gates and repositories

Apache Maven turns a Java project into a repeatable build: it reads one project model, resolves dependencies, compiles source, runs tests, packages an artifact, applies verification gates, and publishes approved releases. This guide starts with installation and a small working project, then builds toward dependency control, multi-module design, private repositories, CI, security, releases, and systematic troubleshooting.

The examples use Maven 3.9.16, the current stable Maven 3 release at the time of writing. Maven 4 is still a release candidate, so use it for compatibility testing unless your team has explicitly approved it. For day-to-day projects, commit the Maven Wrapper and let the repository declare the Maven version that contributors and CI should run.

What Maven does

Maven is a build and project-management tool. Its Project Object Model, stored in pom.xml, identifies the project, declares dependencies, configures plugins, and connects work to a standard lifecycle. A developer can enter an unfamiliar Maven repository and begin with the same small command vocabulary used elsewhere.

ConcernMaven mechanismResult
Project identityGroup, artifact, version, and packaging coordinatesA uniquely addressable build output
Repeatable buildLifecycle phases and versioned pluginsConsistent compile, test, package, and verify steps
LibrariesDependencies, scopes, exclusions, and dependency managementA calculated classpath with traceable origins
Project structureConventional source and resource directoriesLess custom build wiring between repositories
DistributionInstall and deploy phases plus repositoriesArtifacts shared locally or through a repository manager
InformationReports, effective model, dependency tree, and site pluginsInspectable build and project evidence

Maven does not remove the need to understand Java, testing, dependency risk, packaging, repositories, or deployment. It supplies a uniform operating model for those concerns. It is not an extension of Ant, a bag of reusable scripts, or only a documentation generator.

Understand Maven, the JDK, and the artifact repository

ComponentResponsibilityCommon confusion
JDKProvides the Java runtime and tools such as the compilerMaven can run only when a compatible Java runtime is available
MavenBuild orchestration, model processing, plugin execution, and dependency resolutionMaven itself does not compile Java; the compiler plugin invokes the compiler
Local repositoryCaches downloaded artifacts and stores locally installed project artifacts~/.m2/repository is a cache and artifact store, not source control
Remote repositoryServes released or snapshot artifactsMaven Central is one repository, not Maven itself
Repository managerProxies upstreams and hosts organization artifacts with policy and retentionIt should be the controlled dependency boundary, not a public file share

Choose a supported baseline

Decide the runtime Maven uses, the Java release the project targets, and the Maven version the repository requires. Those are separate choices. Maven 3.9.16 can run on JDK 8 or newer, but a modern application may intentionally require JDK 17 or 21. Maven 4 requires Java 17 to run.

java -version
javac -version

# If Maven is already present, show Maven, Java, home, locale and OS details.
mvn --version

Use a supported JDK distribution approved by your organization. Record the required Java and Maven versions in the repository README, enforce them in the build, and use the same major JDK in developer and CI environments unless cross-JDK testing is intentional.

Install Maven on Debian or Ubuntu

The distribution package is the simplest managed installation, but its version follows the operating-system release. Check the candidate before installing if your project requires a newer Maven release.

sudo apt update
apt-cache policy maven
sudo apt install maven

mvn --version

If the packaged version does not meet the project requirement, prefer the project’s Maven Wrapper. Use a manual system installation only when the host policy requires a centrally managed Maven version.

Install Maven on RHEL, Rocky Linux, AlmaLinux, or Fedora

dnf info maven
sudo dnf install maven

mvn --version

Enterprise Linux repositories may prioritize long support over the newest Maven release. Do not replace the distribution package blindly on shared hosts. A wrapper keeps the project version separate from the administrator’s system package.

Install Maven on macOS

brew update
brew install maven

mvn --version

When several JDKs are installed, confirm the Java home reported by mvn --version. Shell Java selection and IDE Java selection can differ.

Install Maven on Windows

Install a JDK first. Download the Maven binary ZIP from the official Apache Maven download page, verify its published checksum, extract it to a stable directory such as C:\Tools\apache-maven-3.9.16, and add its bin directory to the user or system Path.

java -version
javac -version
mvn --version

# Show which executables Windows resolves.
where.exe java
where.exe mvn

A system-wide M2_HOME variable is not required for current Maven. MAVEN_HOME is necessary only for software that specifically consumes it. The essential requirements are a working Java runtime and Maven’s bin directory on Path.

Install the official binary manually on Linux

Use this method when there is no suitable package and the project cannot yet use the wrapper. Verify the release against Apache’s published SHA-512 value before extracting it.

maven_version='3.9.16'
archive="apache-maven-${maven_version}-bin.tar.gz"
base_url="https://dlcdn.apache.org/maven/maven-3/${maven_version}/binaries"

cd /tmp
curl -fLO "${base_url}/${archive}"
curl -fLO "${base_url}/${archive}.sha512"

# Apache's checksum file contains the expected digest.
expected="$(tr -d '[:space:]' < "${archive}.sha512")"
actual="$(sha512sum "${archive}" | awk '{print $1}')"
test "$actual" = "$expected" || { echo 'Checksum mismatch'; exit 1; }

sudo install -d -m 0755 /opt/apache-maven
sudo tar -xzf "$archive" -C /opt/apache-maven
sudo ln -sfn "/opt/apache-maven/apache-maven-${maven_version}" \
  /opt/apache-maven/current
sudo ln -sfn /opt/apache-maven/current/bin/mvn /usr/local/bin/mvn

mvn --version

Keep the versioned directory and change only the current symlink during a controlled upgrade. This makes rollback precise. Do not pipe an unverified download directly into an extraction command running as root.

Use the Maven Wrapper for each repository

The wrapper commits mvnw, mvnw.cmd, and configuration under .mvn/wrapper/. Contributors invoke the scripts, and the wrapper obtains the configured Maven distribution when it is not already cached.

# Run once in an existing project with an approved Maven installation.
mvn wrapper:wrapper -Dmaven=3.9.16 -Dtype=only-script

# Linux and macOS.
./mvnw --version
./mvnw clean verify

# Windows Command Prompt or PowerShell.
mvnw.cmd --version
mvnw.cmd clean verify

Commit the wrapper scripts and .mvn/wrapper/maven-wrapper.properties. Pin distributionSha256Sum in that properties file so a changed distribution is rejected. Review wrapper upgrades like any other build-system change.

Create a small Java project

mkdir -p hello-maven/src/main/java/com/nitwings/example
mkdir -p hello-maven/src/test/java/com/nitwings/example
cd hello-maven
// src/main/java/com/nitwings/example/Greeting.java
package com.nitwings.example;

public final class Greeting {
    private Greeting() {}

    public static String message(String name) {
        return "Hello, " + name;
    }
}
// src/test/java/com/nitwings/example/GreetingTest.java
package com.nitwings.example;

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

class GreetingTest {
    @Test
    void createsGreeting() {
        assertEquals("Hello, Maven", Greeting.message("Maven"));
    }
}

Write a complete minimal POM

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.nitwings.example</groupId>
  <artifactId>hello-maven</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <junit.version>5.14.0</junit.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.14.1</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.5.4</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Plugin versions above are examples of pinned compatible releases, not a promise that they remain the newest. Review changes using official plugin release notes and update automation. A reproducible build states plugin versions instead of relying on whatever default a Maven installation happens to provide.

Run the first build

./mvnw clean verify

# Inspect the build result.
find target -maxdepth 2 -type f -print
jar tf target/hello-maven-1.0.0-SNAPSHOT.jar

A successful verify means every phase through verification ran. It does not mean the artifact was copied to the local repository or published remotely. Those are separate lifecycle phases.

Know the standard directory layout

PathPurpose
pom.xmlProject Object Model
src/main/javaApplication Java source
src/main/resourcesApplication resources copied to the artifact classpath
src/test/javaTest Java source
src/test/resourcesTest-only resources
targetGenerated classes, reports, and artifacts
.mvnRepository-local Maven configuration and wrapper files

Conventions make projects easier to navigate and reduce configuration. If a repository must use a different layout, configure it explicitly and document why. Do not commit target/.

Understand coordinates and packaging

An artifact is addressed by groupId:artifactId:packaging:classifier:version. The common short form is groupId:artifactId:version. Use a group you control, a stable artifact name, and a version policy understood by consumers.

ElementExampleMeaning
groupIdcom.nitwings.platformOrganization or product namespace
artifactIdmail-eventsModule or distributable name
version2.4.0Immutable release identity
packagingjar, war, or pomArtifact type and default lifecycle bindings
classifiersourcesAdditional artifact from the same project version

Understand the three lifecycles

Maven has default, clean, and site lifecycles. A lifecycle contains ordered phases. Calling a phase runs it and every preceding phase in that lifecycle.

Default phasePurpose
validateCheck that the model and required project information are valid
compileCompile main source
testRun unit tests
packageCreate the JAR, WAR, or other package
verifyRun integration and quality checks bound before verification
installPlace the artifact and POM in the local repository
deployPublish the artifact to a configured remote repository
./mvnw validate
./mvnw test
./mvnw package
./mvnw verify
./mvnw install

Prefer phases over calling implementation goals directly. verify gives CI a clear quality boundary. A goal such as dependency:tree invokes one plugin capability and does not imply the project lifecycle ran.

Use clean deliberately

./mvnw clean verify

clean removes the project’s build output before the default lifecycle runs. It is useful for release and CI evidence, but it also removes incremental output and makes every build slower. During development, ./mvnw test or ./mvnw verify may be sufficient. CI should begin from an isolated checkout and may still use clean verify for clarity.

Understand plugins, goals, and executions

Plugins perform Maven’s work. A goal is a plugin operation. An execution binds one or more goals to a lifecycle phase. Put plugins that must run in this project under build/plugins. Use pluginManagement in a parent to define versions and shared defaults without activating every plugin in every child.

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-enforcer-plugin</artifactId>
      <version>3.6.2</version>
      <executions>
        <execution>
          <id>enforce-build-runtime</id>
          <goals><goal>enforce</goal></goals>
          <configuration>
            <rules>
              <requireMavenVersion><version>[3.9.0,4.0.0)</version></requireMavenVersion>
              <requireJavaVersion><version>[17,18)</version></requireJavaVersion>
            </rules>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Use dependency scopes correctly

ScopeCompile classpathTest classpathRuntime classpathTypical use
compileYesYesYesLibraries required by main code
providedYesYesNoAPI supplied by the runtime container
runtimeNoYesYesRuntime implementation such as a database driver
testNoYesNoTest frameworks and test utilities
systemYesYesNoAvoid for ordinary libraries; it hard-codes a local file
importNot a classpath scopeNot a classpath scopeNot a classpath scopeImports dependency management from a BOM POM

Declare every library the source uses directly, even if it currently arrives transitively. This documents the contract and prevents a downstream dependency change from silently removing a required API.

Inspect and resolve dependency conflicts

./mvnw dependency:tree
./mvnw dependency:tree -Dverbose
./mvnw dependency:tree -Dincludes=com.fasterxml.jackson.core
./mvnw dependency:analyze

Maven normally chooses the nearest dependency definition when multiple paths provide different versions. Do not “fix” a conflict by adding random exclusions until the tree looks short. Identify the direct consumers, consult compatibility information, select an intentional version in dependency management, exclude only the unwanted path, and run relevant integration tests.

<dependency>
  <groupId>com.example</groupId>
  <artifactId>service-client</artifactId>
  <version>4.2.0</version>
  <exclusions>
    <exclusion>
      <groupId>org.example</groupId>
      <artifactId>conflicting-library</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Centralize versions with dependency management and BOMs

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.example.platform</groupId>
      <artifactId>platform-bom</artifactId>
      <version>8.1.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Dependency management selects versions; it does not automatically add ordinary dependencies. Child modules still declare what they use. A Bill of Materials is valuable when a tested library family must move together. Keep imports few, understand precedence, and inspect the effective POM when two BOMs manage the same artifact.

Build a multi-module project

mail-platform/
├── pom.xml
├── mail-domain/
│   └── pom.xml
├── mail-storage/
│   └── pom.xml
└── mail-service/
    └── pom.xml
<!-- mail-platform/pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.nitwings.mail</groupId>
  <artifactId>mail-platform</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <modules>
    <module>mail-domain</module>
    <module>mail-storage</module>
    <module>mail-service</module>
  </modules>
</project>

The reactor sorts modules by their declared relationships and builds them in order. Aggregation answers which modules build together. Inheritance answers which model values a child receives. One POM can do both, but the concepts are independent.

# Build the complete reactor.
./mvnw clean verify

# Build one module and the reactor projects it requires.
./mvnw -pl mail-service -am verify

# Also build projects that depend on the selected module.
./mvnw -pl mail-domain -amd test

# Resume after a corrected module.
./mvnw -rf :mail-storage verify

Use properties and profiles carefully

Properties remove repetition from a model. Profiles modify the model for an explicit build context. Do not use profiles to conceal fundamental differences in application behavior, and do not create one artifact whose contents vary invisibly by developer workstation.

<profiles>
  <profile>
    <id>quality</id>
    <build>
      <plugins>
        <!-- Add approved analysis plugins here. -->
      </plugins>
    </build>
  </profile>
</profiles>
./mvnw help:active-profiles
./mvnw -Pquality verify

Environment names and secrets usually belong in runtime configuration, not in compile-time Maven profiles. Build one tested artifact and promote that exact artifact between environments.

Separate project configuration from user settings

LocationStore hereDo not store here
pom.xmlProject dependencies, plugins, build policy, and public repository identifiersPasswords, personal paths, or workstation-only assumptions
.mvn/Repository-local Maven and wrapper configurationUnencrypted credentials
~/.m2/settings.xmlUser or CI mirrors, server IDs, proxies, and profile activationContent that every clone needs to understand the project
CI secret storeShort-lived repository credentials and signing materialOrdinary build configuration

Use a repository manager as the organization boundary

A repository manager can proxy Maven Central, host private releases and snapshots, retain metadata, and apply access or quarantine policy. Configure a mirror in settings.xml so CI and developers resolve through the controlled endpoint.

<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
  <mirrors>
    <mirror>
      <id>company-repository</id>
      <name>Approved artifact repository</name>
      <url>https://repo.example.com/repository/maven-public/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>

Use TLS, restrict publishing accounts, separate snapshot and release policy, retain audit evidence, and define what happens when an upstream artifact changes or becomes unavailable. Avoid adding arbitrary public repositories to individual POMs.

Configure publishing without putting secrets in the POM

<!-- pom.xml -->
<distributionManagement>
  <repository>
    <id>company-releases</id>
    <url>https://repo.example.com/repository/maven-releases/</url>
  </repository>
  <snapshotRepository>
    <id>company-snapshots</id>
    <url>https://repo.example.com/repository/maven-snapshots/</url>
  </snapshotRepository>
</distributionManagement>
<!-- ~/.m2/settings.xml, generated securely by the CI job -->
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0">
  <servers>
    <server>
      <id>company-releases</id>
      <username>${env.MAVEN_REPO_USER}</username>
      <password>${env.MAVEN_REPO_TOKEN}</password>
    </server>
  </servers>
</settings>

The server id must match the repository id. Prefer a short-lived, least-privilege token injected by CI. Mask it from logs, restrict the settings file permissions, and remove the temporary file after the job.

Protect credentials and diagnostic output

Unsafe: mvn -X, effective settings, environment dumps, transfer debug output, and CI shell tracing can expose repository URLs, usernames, tokens, proxy credentials, and private project details. Capture only what the investigation needs, redact before sharing, and protect logs under the incident-retention policy.

# Safer first-line diagnostics without debug output.
./mvnw --version
./mvnw help:active-profiles
./mvnw help:effective-pom -Doutput=target/effective-pom.xml

Maven password encryption reduces casual disclosure in a settings file but does not turn a reusable credential on the same machine into a secret against that user. CI secret stores and short-lived tokens provide the stronger boundary.

Use toolchains when Maven and compilation need different JDKs

Toolchains let plugins select an installed JDK independently from the Java runtime that launched Maven. This helps test and compile against approved JDKs without changing the shell for every command.

<!-- ~/.m2/toolchains.xml -->
<toolchains>
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>17</version>
      <vendor>any</vendor>
    </provides>
    <configuration>
      <jdkHome>/opt/jdks/jdk-17</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

Do not commit machine-specific JDK paths. Commit the project’s toolchain requirements and provision the corresponding paths through workstation and CI configuration.

Separate unit and integration tests

Surefire normally runs unit tests in the test phase. Failsafe normally runs integration tests using integration-test and verify. Use Failsafe’s verify goal so teardown and result checking complete even when an integration test fails.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>3.5.4</version>
  <executions>
    <execution>
      <goals>
        <goal>integration-test</goal>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
</plugin>
./mvnw test
./mvnw verify

# Run one unit test during diagnosis.
./mvnw -Dtest=GreetingTest test

Unsafe: publishing with -DskipTests or -Dmaven.test.skip=true removes evidence from the release path. Keep these flags for an authorized diagnosis where the missing assurance is explicit. Do not make them the normal CI or release command.

Create reproducible build evidence

  • Pin the Maven Wrapper distribution and its checksum.
  • Pin build-plugin versions and manage dependency versions centrally.
  • Use maven.compiler.release instead of an accidental workstation JDK API surface.
  • Set project.build.outputTimestamp for plugins that support reproducible archives.
  • Build in an isolated checkout with a recorded JDK, Maven, OS image, and command.
  • Generate dependency, test, quality, SBOM, signature, and provenance evidence as policy requires.
  • Publish the verified artifact once and promote that exact digest.
<properties>
  <project.build.outputTimestamp>2026-08-24T00:00:00Z</project.build.outputTimestamp>
</properties>

For releases, set the timestamp from a controlled release process rather than changing it on every build. Reproducibility is verified by comparing independently produced artifacts, not claimed because one property exists.

Use a disciplined CI pipeline

set -eu

./mvnw --version
./mvnw --batch-mode --no-transfer-progress clean verify

artifact='target/hello-maven-1.0.0.jar'
test -f "$artifact"
sha256sum "$artifact"

Cache downloaded artifacts using a key that includes the operating system, JDK, wrapper configuration, and relevant POM files. Treat the cache as expendable and untrusted. A cache hit must not bypass tests or verification. Archive test reports even when the build fails.

Publish snapshots and releases deliberately

TypeIdentityPolicy
SnapshotVersion ends in -SNAPSHOTMutable development line with explicit retention
ReleaseFixed version such as 2.4.0Immutable; never overwrite an existing coordinate
Promoted artifactExact verified digest selected from a controlled buildMove metadata or copy within repository policy, do not rebuild

Unsafe: ./mvnw deploy writes to a shared remote repository when credentials and distribution management permit it. Run it only from an authorized release job after verify, version checks, branch or tag policy, signing, and approval gates. A developer laptop should not be the routine release boundary.

Useful commands for daily work

CommandUse
./mvnw testCompile and run unit tests
./mvnw verifyRun all configured checks through verification
./mvnw help:effective-pomSee the fully inherited and profile-adjusted model
./mvnw help:active-profilesSee active build profiles
./mvnw dependency:treeTrace dependency versions and paths
./mvnw dependency:analyzeFind some used-undeclared and unused-declared dependencies
./mvnw -pl module -am verifyBuild one module with required reactor dependencies
./mvnw -o verifyTest whether the build works with the existing local cache only
./mvnw -U verifyForce update checks for snapshots and missing release metadata
./mvnw -B -ntp verifyBatch CI output without transfer progress

Troubleshoot “mvn not found” and wrong Java

command -v java
command -v javac
command -v mvn
java -version
mvn --version
printf '%s\n' "$PATH"

If the shell and Maven report different Java installations, inspect JAVA_HOME, shell startup files, SDK managers, alternatives, symlinks, and the CI image. On Windows, use where.exe. Fix one selection mechanism rather than appending more paths until the desired binary happens to win.

Troubleshoot dependency resolution

SymptomLikely boundaryEvidence
Artifact not foundWrong coordinates, repository policy, version, or publicationDependency tree, repository UI, effective settings, exact URL and status
UnauthorizedServer ID mismatch, expired token, or missing permissionDistribution/repository ID, credential owner, repository audit log
PKIX path failureUntrusted TLS issuer, intercepted TLS, or wrong endpoint certificateCertificate chain, Java truststore, repository hostname and proxy path
Checksum failureCorrupt cache, changed upstream artifact, proxy issue, or attackRepository digest, local file digest, manager audit and upstream state
Works on one laptopUncommitted settings, cached unpublished artifact, profile, or JDK differenceClean isolated build, effective POM/settings, local repository origin
Wrong transitive versionNearest-definition selection or imported management precedenceVerbose dependency tree and effective POM
./mvnw help:effective-settings -Doutput=target/effective-settings.xml
./mvnw help:effective-pom -Doutput=target/effective-pom.xml
./mvnw dependency:tree -Dverbose -DoutputFile=target/dependency-tree.txt

Protect effective settings as sensitive. It may expose infrastructure and account information even when passwords are masked.

Repair a single corrupt local artifact safely

Identify the exact coordinate from the error and dependency tree. Move only that coordinate’s directory out of the local repository, then resolve again through the approved repository manager.

artifact_path="$HOME/.m2/repository/com/example/problem-library/2.3.1"
quarantine="$HOME/.m2/quarantine/problem-library-2.3.1-$(date +%Y%m%d%H%M%S)"

test -d "$artifact_path"
mkdir -p "$(dirname "$quarantine")"
mv "$artifact_path" "$quarantine"

./mvnw -U verify

Unsafe: deleting the entire ~/.m2/repository removes every cached and locally installed artifact, destroys useful incident evidence, creates a large uncontrolled redownload, and may break projects that depend on unpublished local builds. Quarantine the exact coordinate first. If a full isolated test is needed, use a temporary repository without touching the working cache:

temporary_repo="$(mktemp -d)"
./mvnw -Dmaven.repo.local="$temporary_repo" clean verify

Troubleshoot tests and plugin execution

# Show the failing test report.
find target/surefire-reports target/failsafe-reports -type f -maxdepth 2 -print 2>/dev/null

# Re-run one unit test.
./mvnw -Dtest=GreetingTest test

# Resume a reactor after the failed module is fixed.
./mvnw -rf :mail-storage verify

Read the first meaningful failure, not only the final reactor summary. Separate test assertion, forked-JVM crash, timeout, missing test resource, plugin incompatibility, and application startup failure. Preserve reports and dump files before cleaning the workspace.

Diagnose an effective-model surprise

A value may come from the current POM, a parent, an imported BOM, a profile, user settings, global settings, or a command-line property. Generate the effective POM and active profiles, then trace the source before editing.

./mvnw help:active-profiles
./mvnw help:effective-pom -Dverbose -Doutput=target/effective-pom.xml
./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout

Do not commit the generated effective POM as the project POM. It expands inherited defaults and is diagnostic output, not the maintainable source model.

Evaluate Maven 4 without surprising the main build

Maven 4 is not yet a general-availability release. Apache recommends preparing on the latest Maven 3, upgrading plugins, running the build in parallel on a current Maven 4 release candidate, and only then adopting Maven 4-specific model features. Maven 4 requires Java 17 to run.

  • Keep the production wrapper on the approved Maven 3.9 release.
  • Add a non-blocking or explicitly experimental CI job using the current Maven 4 release candidate.
  • Compare lifecycle behavior, plugins, warnings, output artifacts, and integration tests.
  • Remove duplicate plugin declarations and invalid model practices reported by validation.
  • Do not adopt a Maven 4-only POM model until every required build environment is ready.

Production readiness checklist

  • The repository contains a reviewed Maven Wrapper with a pinned distribution checksum.
  • Maven and JDK requirements are documented and enforced.
  • Direct dependencies are declared directly, versions are managed, and conflicts are explained.
  • Build-plugin versions are pinned and updated through review.
  • Unit tests, integration tests, quality gates, and packaging run through verify.
  • No credentials, personal paths, or environment secrets exist in the POM.
  • Dependencies resolve through an approved TLS repository manager.
  • Release publishing uses a least-privilege CI identity and immutable version policy.
  • The artifact digest, test reports, dependency evidence, JDK, Maven version, source revision, and build command are retained.
  • The verified artifact is promoted rather than rebuilt for each environment.
  • A clean isolated build succeeds without unpublished workstation artifacts.
  • Rollback selects a previously verified immutable artifact; it does not overwrite a release coordinate.

Primary references

Related technical notes

Technical review

Need this checked against your own sending system?

Share the domain, headers, bounces, provider warning, logs, or infrastructure symptom and NitWings will identify the practical next step.

Schedule a Technical Review
Advertisement