Chapter 04Lesson 05~150 minutes

Checkpoint Lab — Apache Maven Installation, Wrapper, Settings, Local Repository, and Project Bootstrap

Checkpoint lab: prove a wrapper-first Maven bootstrap with verified distribution integrity, explicit settings, isolated local repository, failure injection, recovery, and cleanup.

CheckpointWrapper IntegrityClean RoomRecoveryVerification

Learning objectives

  • Bootstrap a disposable Maven project whose normal build entry point is the project wrapper.
  • Record and verify wrapper distribution URL/checksum, Maven version, JDK runtime, settings path, and isolated local-repository location.
  • Predict and observe how wrapper state, repository state, and project output change during first and repeat builds.
  • Inject a broken Central mirror, diagnose the repository-policy failure, and restore the clean settings without changing project source.
  • Produce a concise verification record and clean up only disposable Chapter 04 state.
Version baseline — verified 2026-08-23. The required path uses JDK 21 and Apache Maven 3.9.16. Maven 3.9.16 is the current recommended GA and requires JDK 8+ to execute; Maven 3.10.0-rc-1 and Maven 4.0.0-rc-6 are previews and are not required. Maven Wrapper 3.3.4 is the current stable wrapper. Wrapper examples use the only-script distribution type and pin the Maven distribution with distributionSha256Sum after the downloaded archive has first been verified against Apache's published release checksum/signature. Re-check these versions before applying the examples to production.

1. Checkpoint scenario and acceptance contract

You are preparing a tiny JVM service for a team that uses ephemeral CI agents. The repository must carry its Maven launch contract, no credentials may be committed, CI must be able to use a fresh local repository, and a mirror outage must be diagnosable without deleting user caches.

Required evidence
[ ] project contains mvnw, mvnw.cmd, and .mvn/wrapper/maven-wrapper.properties
[ ] wrapper selects Maven 3.9.16
[ ] Maven distribution is pinned with distributionSha256Sum after Apache release verification
[ ] ./mvnw -v (or mvnw.cmd -v) records Maven and Java runtime identity
[ ] lab uses explicit -s settings path and isolated maven.repo.local
[ ] first build populates only disposable wrapper/repository state
[ ] project JAR exists under target/
[ ] deliberate broken mirror fails for the predicted repository reason
[ ] clean settings restore succeeds with controlled state
[ ] cleanup removes only the disposable checkpoint directory

2. Setup and preflight

Create a brand-new directory. If it already exists or contains valuable data, stop. Record the installed Java and system Maven used only for wrapper bootstrap.

test ! -e maven-bootstrap-checkpoint
mkdir -p maven-bootstrap-checkpoint/src/main/java/com/example
cd maven-bootstrap-checkpoint
java -version
mvn -v
Destructive boundary: cleanup later removes the entire maven-bootstrap-checkpoint directory. Never repurpose that command for an existing repository.

3. Create the project model and source

<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.example</groupId>
  <artifactId>maven-bootstrap-checkpoint</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
</project>
package com.example;
public final class App {
  public static void main(String[] args) {
    System.out.println("maven bootstrap checkpoint");
  }
}

Prediction 1: before wrapper generation there should be no mvnw, .mvn/wrapper, isolated wrapper home, local repository, or target/.

4. Generate and review the wrapper

mvn org.apache.maven.plugins:maven-wrapper-plugin:3.3.4:wrapper \
  -Dmaven=3.9.16 \
  -Dtype=only-script

git diff --no-index /dev/null .mvn/wrapper/maven-wrapper.properties || true
sed -n '1,160p' .mvn/wrapper/maven-wrapper.properties

From this point forward, treat direct mvn as outside the checkpoint contract. A reviewer should be able to see the Maven version/distribution URL change in Git when the project upgrades Maven.

5. Verify the release and pin distribution SHA-256

Repeat the official release verification workflow from Lesson 2: download the Maven 3.9.16 binary archive from Apache, download the published SHA-512 checksum (or verify the Apache signature/KEYS), require the SHA-512 match, compute SHA-256 of the verified archive, then write that exact SHA-256 as distributionSha256Sum in the wrapper properties.

mkdir -p .lab/verify
curl -fL -o .lab/verify/apache-maven-3.9.16-bin.zip \
  https://dlcdn.apache.org/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.zip
curl -fL -o .lab/verify/apache-maven-3.9.16-bin.zip.sha512 \
  https://downloads.apache.org/maven/maven-3/3.9.16/binaries/apache-maven-3.9.16-bin.zip.sha512

EXPECTED_SHA512="$(awk '{print $1}' .lab/verify/apache-maven-3.9.16-bin.zip.sha512)"
ACTUAL_SHA512="$(sha512sum .lab/verify/apache-maven-3.9.16-bin.zip | awk '{print $1}')"
test "$EXPECTED_SHA512" = "$ACTUAL_SHA512"

DIST_SHA256="$(sha256sum .lab/verify/apache-maven-3.9.16-bin.zip | awk '{print $1}')"
printf 'Verified distribution SHA-256: %s\n' "$DIST_SHA256"

python - "$DIST_SHA256" <<'PY2'
from pathlib import Path
import sys
p=Path('.mvn/wrapper/maven-wrapper.properties')
lines=[x for x in p.read_text().splitlines() if not x.startswith('distributionSha256Sum=')]
lines.append('distributionSha256Sum='+sys.argv[1])
p.write_text('\n'.join(lines)+'\n')
PY2
grep -E '^(distributionUrl|distributionSha256Sum)=' .mvn/wrapper/maven-wrapper.properties \
  | tee .lab-wrapper-contract.tmp
# Move this record into .lab/ after the lab directory is created below.

Prediction 2: changing only the checksum to an incorrect value should be treated as an integrity failure, not as permission to disable checksum enforcement.

6. Create isolated wrapper, settings, and repository state

<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <interactiveMode>false</interactiveMode>
  <offline>false</offline>
</settings>
mkdir -p .lab/wrapper-home .lab/m2repo
mv .lab-wrapper-contract.tmp .lab/wrapper-contract.txt
export MAVEN_USER_HOME="$PWD/.lab/wrapper-home"
printf '%s\n' "$PWD/.lab/m2repo" > .lab/local-repository.txt
printf '%s\n' "$PWD/.lab/settings.xml" > .lab/settings-path.txt

./mvnw -s .lab/settings.xml \
  -Dmaven.repo.local="$PWD/.lab/m2repo" \
  -v | tee .lab/maven-version.txt
java -version 2> .lab/java-version.txt

Prediction 3: the wrapper home and local repository start empty. Wrapper execution populates wrapper-managed Maven state; Maven execution populates plugin/dependency repository state. These are different directories and evidence streams.

7. Capture effective settings without secrets

./mvnw -s .lab/settings.xml \
  -Dmaven.repo.local="$PWD/.lab/m2repo" \
  org.apache.maven.plugins:maven-help-plugin:3.5.2:effective-settings \
  -Doutput=.lab/effective-settings.xml

grep -n "localRepository\|offline\|mirror\|proxy" .lab/effective-settings.xml | head -60

Do not add showPasswords=true. In this lab there are no credentials anyway, but the operational habit should remain safe when the same command is used elsewhere.

8. First build — observe cold state

./mvnw -s .lab/settings.xml \
  -Dmaven.repo.local="$PWD/.lab/m2repo" \
  -B -ntp package | tee .lab/build-first.log

jar tf target/maven-bootstrap-checkpoint-1.0.0-SNAPSHOT.jar \
  | tee .lab/jar-contents.txt
find .lab/m2repo -type f | sort > .lab/repository-files.txt
sha256sum target/maven-bootstrap-checkpoint-1.0.0-SNAPSHOT.jar \
  | tee .lab/artifact.sha256

Verify that repository files appeared under .lab/m2repo and project output appeared under target/. No step should require deleting or writing to your normal ~/.m2/repository.

9. Repeat build — distinguish cache warmth from output identity

./mvnw -s .lab/settings.xml \
  -Dmaven.repo.local="$PWD/.lab/m2repo" \
  -B -ntp package | tee .lab/build-repeat.log
sha256sum target/maven-bootstrap-checkpoint-1.0.0-SNAPSHOT.jar \
  | tee .lab/artifact-repeat.sha256

Compare transfer activity and the two artifact hashes. If hashes differ, investigate output reproducibility; if they match, that is useful evidence for this tiny project but not a universal guarantee. More rigorous reproducible-build controls appear later in the course.

10. Inject a broken Central mirror

<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
  <mirrors>
    <mirror>
      <id>broken-central</id>
      <name>Intentional Chapter 04 failure</name>
      <url>https://repo.example.invalid/maven2</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
</settings>
rm -rf .lab/broken-repo
mkdir -p .lab/broken-repo
set +e
./mvnw -s .lab/broken-settings.xml \
  -Dmaven.repo.local="$PWD/.lab/broken-repo" \
  -B -ntp \
  org.apache.maven.plugins:maven-help-plugin:3.5.2:effective-pom \
  > .lab/broken-mirror.log 2>&1
STATUS=$?
set -e
printf 'exit=%s\n' "$STATUS"
grep -Ei 'broken-central|repo\.example\.invalid|transfer|resolve|plugin' .lab/broken-mirror.log | head -40

test "$STATUS" -ne 0

Expected: the build fails before Java compilation because plugin resolution is routed to the synthetic invalid mirror. Record that causal classification before repairing anything.

11. Restore clean settings and prove recovery

rm -rf .lab/recovery-repo
mkdir -p .lab/recovery-repo
./mvnw -s .lab/settings.xml \
  -Dmaven.repo.local="$PWD/.lab/recovery-repo" \
  -B -ntp \
  org.apache.maven.plugins:maven-help-plugin:3.5.2:effective-pom \
  -Doutput=.lab/recovered-effective-pom.xml

test -s .lab/recovered-effective-pom.xml

The repair changed only settings/repository routing; project source and wrapper configuration remained unchanged. That is stronger diagnostic evidence than “I changed several things and it works now.”

12. Verification checklist

test -x mvnw
test -f mvnw.cmd
test -f .mvn/wrapper/maven-wrapper.properties
grep -q '^distributionSha256Sum=' .mvn/wrapper/maven-wrapper.properties
grep -q 'Apache Maven 3.9.16' .lab/maven-version.txt
test -s .lab/effective-settings.xml
test -s .lab/repository-files.txt
test -f target/maven-bootstrap-checkpoint-1.0.0-SNAPSHOT.jar
test -s .lab/broken-mirror.log
test -s .lab/recovered-effective-pom.xml
printf 'CHECKPOINT_OK\n' 

A passing checklist proves the checkpoint conditions only. It does not prove the public repository is always available, all Maven plugins are pinned, all dependencies are verified, or later release publishing is secure.

13. Cleanup and rollback

Review the absolute path and preserve any synthetic evidence you want for notes. Then leave the directory and remove only the disposable checkpoint.

pwd
cd ..
ls -ld maven-bootstrap-checkpoint
# rm -rf maven-bootstrap-checkpoint
Never replace that lab path with ~/.m2, an existing repository, or a shared CI cache. The course intentionally avoids destructive cleanup of normal Maven state.

Knowledge check

After wrapper generation, CI invokes mvn package. Which checkpoint invariant did it violate?

The first build populates .lab/m2repo. Is that directory a release repository?

The broken mirror test fails with an invalid-host transfer error before compilation. What layer should be repaired?

Why use a fresh .lab/broken-repo for the mirror failure?

The Maven distribution URL is unchanged but distributionSha256Sum changes in a pull request. Is that significant?

Why record Maven version and Java runtime separately?

Summary and bridge to Chapter 05

The checkpoint established a production-minded Maven bootstrap contract: project-owned wrapper, independently verified distribution pin, explicit Maven/JDK evidence, secret-free alternate settings, isolated resolver state, controlled failure injection, causal recovery, and safe cleanup. Chapter 05 can now assume the Maven executable/environment is understood and focus on the POM, packaging, build lifecycle, phases, and goals.

Next chapter

Maven POM, lifecycle, phases, and goals

With execution identity stabilized, Chapter 05 moves inside Maven's project model: how pom.xml, packaging, lifecycle phases, and plugin goals determine what the build actually does.

Official references and version notes

Checkpoint baseline verified against current Apache Maven documentation on 2026-08-23. The mandatory path is local/free and does not require a hosted repository, paid CI, signing service, or production credential.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.