Chapter 07Lesson 05~185 minutes

Checkpoint Lab — Maven Plugins, Lifecycle Bindings, Plugin Configuration, Executions, and Plugin Management

Prove a production-style plugin policy in a small Maven reactor: centrally manage versions, activate one explicit lifecycle execution, inspect effective configuration, break its phase ordering, repair it, and inventory the active plugin trust boundary.

Checkpoint LabPlugin InventoryExecution EvidencePhase RepairGovernance

Learning objectives

  • Build a Maven project whose reviewed plugin versions are centrally managed and whose custom execution is explicitly activated.
  • Prove which plugin configuration is inherited, which plugins are active, and which execution id ran.
  • Predict and verify artifact changes caused by moving an execution from prepare-package to verify.
  • Inventory active/default-bound plugin identities and repository/wrapper assumptions as build trust evidence.
  • Repair the broken lifecycle ordering and leave a concise verification/cleanup record for Chapter 08.
Current baseline — verified 2026-08-23. Required labs use Maven 3.9.16 via Maven Wrapper 3.3.4, JDK 21 to run Maven, Java 17 as the compiler release target, Compiler Plugin 3.15.0, Surefire 3.5.4, JAR Plugin 3.5.1, Resources Plugin 3.5.0, Help Plugin 3.5.2, and AntRun Plugin 3.2.0. Every lab uses a disposable project directory and -Dmaven.repo.local=<lab>/.lab-m2/repository. Normal user caches/settings are not deleted or rewritten.
Cross-platform note: POSIX examples use ./mvnw, grep, find, and sha256sum. On Windows use mvnw.cmd and PowerShell equivalents such as Select-String, Get-ChildItem, and Get-FileHash. The POM model, plugin execution identity, and lifecycle ordering are platform-independent; shell syntax is not.

1. Checkpoint scenario and acceptance contract

You own a small Java service entering CI. The parent POM is the central plugin policy. The application activates only the custom AntRun execution plus standard build plugins. Your job is not merely to obtain BUILD SUCCESS; it is to prove plugin identity, model inheritance, execution timing, and artifact effect.

CHECKPOINT ACCEPTANCE
[ ] Maven Wrapper / Maven / JDK identities recorded
[ ] isolated local repository recorded
[ ] parent pluginManagement pins reviewed plugin versions
[ ] child active-plugin list is explainable
[ ] effective POM captured
[ ] goal metadata captured for the custom plugin
[ ] write-build-marker execution id observed in logs
[ ] correct build places marker inside JAR
[ ] prediction written before phase is intentionally broken
[ ] broken verify-phase build leaves marker outside already-built JAR
[ ] repair restores prepare-package and artifact contents
[ ] plugin/source/version inventory documented
[ ] cleanup touches only disposable lab state

2. Setup — use the final parent and active child models

Use the parent POM and active child POM from Lesson 2, plus the same tiny Java source/test. The parent centrally owns Resources 3.5.0, Compiler 3.15.0, Surefire 3.5.4, JAR 3.5.1, and AntRun 3.2.0. The child activates them without repeating versions.

<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>dev.academy</groupId>
  <artifactId>plugin-lab-parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <modules>
    <module>app</module>
  </modules>

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

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.5.0</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.15.0</version>
          <configuration>
            <release>${maven.compiler.release}</release>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.5.4</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.5.1</version>
          <configuration>
            <archive>
              <manifestEntries>
                <Build-Policy>academy-plugin-lab</Build-Policy>
              </manifestEntries>
            </archive>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>3.2.0</version>
          <executions>
            <execution>
              <id>write-build-marker</id>
              <phase>prepare-package</phase>
              <goals><goal>run</goal></goals>
              <configuration>
                <target>
                  <echo file="${project.build.outputDirectory}/build-marker.txt">phase=prepare-package</echo>
                </target>
              </configuration>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
<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>
  <parent>
    <groupId>dev.academy</groupId>
    <artifactId>plugin-lab-parent</artifactId>
    <version>1.0.0</version>
  </parent>
  <artifactId>app</artifactId>

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

  <build>
    <plugins>
      <plugin><artifactId>maven-resources-plugin</artifactId></plugin>
      <plugin><artifactId>maven-compiler-plugin</artifactId></plugin>
      <plugin><artifactId>maven-surefire-plugin</artifactId></plugin>
      <plugin><artifactId>maven-jar-plugin</artifactId></plugin>
      <plugin><artifactId>maven-antrun-plugin</artifactId></plugin>
    </plugins>
  </build>
</project>

3. Preflight — record immutable execution assumptions

Write evidence before changing the model. CI should be able to answer which Maven/JDK/wrapper/plugin policy produced the artifact.

cd maven-plugin-lab
REPO="$PWD/.lab-m2/repository"
mkdir -p checkpoint-evidence "$REPO"
set -o pipefail

./mvnw --version | tee checkpoint-evidence/maven-version.txt
java -version 2> checkpoint-evidence/java-version.txt
cp .mvn/wrapper/maven-wrapper.properties checkpoint-evidence/wrapper.properties
sha256sum pom.xml app/pom.xml   > checkpoint-evidence/pom-input-sha256.txt
printf 'maven.repo.local=%s
' "$REPO"   > checkpoint-evidence/repository.txt

4. Prediction 1 — managed policy versus active execution

Before building, write two predictions:

Prediction 1A:
  The effective child POM will show versions/configuration inherited from
  parent pluginManagement for the plugins the child references.

Prediction 1B:
  The log will contain exactly one project-defined AntRun execution id
  write-build-marker at prepare-package, and the resulting JAR will contain
  build-marker.txt because that file is generated before package.

Do not treat the effective POM as execution proof; it is only half of the evidence chain.

5. Capture effective plugin configuration and goal metadata

Generate a model artifact and goal descriptor before the main build.

./mvnw -Dmaven.repo.local="$REPO"   org.apache.maven.plugins:maven-help-plugin:3.5.2:effective-pom   -pl app -Doutput=checkpoint-evidence/effective-app.xml

./mvnw -Dmaven.repo.local="$REPO"   org.apache.maven.plugins:maven-antrun-plugin:3.2.0:help   -Ddetail=true -Dgoal=run   | tee checkpoint-evidence/antrun-run-help.txt

grep -n -A28 -B5 'write-build-marker'   checkpoint-evidence/effective-app.xml

Verify that the effective execution id, phase, goal, plugin version, and output path match the prediction. If not, stop and diagnose inheritance/profile state before running the build.

6. Execute the good build and prove artifact causality

Use clean verify so prior marker files cannot produce a false positive.

./mvnw -Dmaven.repo.local="$REPO" clean verify   | tee checkpoint-evidence/good-build.log

jar tf app/target/app-1.0.0.jar   | tee checkpoint-evidence/jar-entries-good.txt
unzip -p app/target/app-1.0.0.jar build-marker.txt   | tee checkpoint-evidence/marker-good.txt
unzip -p app/target/app-1.0.0.jar META-INF/MANIFEST.MF   | tee checkpoint-evidence/manifest-good.txt
sha256sum app/target/app-1.0.0.jar   > checkpoint-evidence/jar-good.sha256

grep 'maven-antrun-plugin:3.2.0:run'   checkpoint-evidence/good-build.log   > checkpoint-evidence/antrun-execution-good.txt

Expected: one write-build-marker execution is visible; marker text is phase=prepare-package; the marker is a JAR entry; and the manifest contains the centrally managed Build-Policy entry.

7. Build an explicit plugin inventory

An effective POM can be large, so the handoff needs a concise reviewed inventory. Record at minimum plugin coordinate/version, activation source, execution ids, lifecycle position, and repository policy. Include Maven core default bindings as a distinct source from POM-declared plugins.

Plugin Chosen / observed version Activation source What to prove
Resources 3.5.0 Child reference + parent management Resource goals/version in effective POM/log.
Compiler 3.15.0 Child reference + parent management release=17; compile/testCompile execution.
Surefire 3.5.4 Child reference + parent management Test count/report + goal version.
JAR 3.5.1 Child reference + parent management Manifest policy + JAR goal version.
AntRun 3.2.0 Child explicit activation + managed execution Execution id/phase/marker artifact.
Maven 3.9.16 built-in default map Includes JAR 3.5.0 / Resources 3.4.0 defaults Maven runtime packaging mapping Document that project pins intentionally override reviewed versions where active.

8. Prediction 2 — deliberately move the execution too late

Edit only the parent execution phase from prepare-package to verify. Before running, predict what changes:

Prediction 2A:
  AntRun will still execute and build-marker.txt will exist in target/classes
  by the end of verify.

Prediction 2B:
  The already-created JAR will NOT contain build-marker.txt because package
  occurs before verify.

Prediction 2C:
  The JAR checksum will differ from or relate to the good build only according
  to actual packaged inputs; the late marker cannot be assumed to affect it.

The key is to predict the execution graph and artifact boundary independently.

9. Run the broken build and preserve the cause

Change the phase, then run another clean verify. Do not run package again afterward before inspecting, because that would hide the ordering failure.

./mvnw -Dmaven.repo.local="$REPO" clean verify   | tee checkpoint-evidence/broken-phase.log

test -f app/target/classes/build-marker.txt
jar tf app/target/app-1.0.0.jar   | tee checkpoint-evidence/jar-entries-broken.txt

if grep -q 'build-marker.txt' checkpoint-evidence/jar-entries-broken.txt; then
  echo 'Unexpected: investigate stale/custom packaging behavior'
  exit 1
else
  echo 'Expected: marker generated after JAR packaging'
fi

grep 'maven-antrun-plugin:3.2.0:run'   checkpoint-evidence/broken-phase.log   > checkpoint-evidence/antrun-execution-broken.txt

This is a controlled lifecycle-order failure, not a plugin-resolution failure. The original evidence remains visible: plugin ran, output exists, artifact lacks it.

10. Repair only the ordering control

Restore prepare-package. Do not add a second JAR execution, manually modify the JAR, or move the marker with a post-build script. Rebuild from clean state and prove the original contract again.

./mvnw -Dmaven.repo.local="$REPO" clean verify   | tee checkpoint-evidence/repaired.log

jar tf app/target/app-1.0.0.jar | grep 'build-marker.txt'
unzip -p app/target/app-1.0.0.jar build-marker.txt
sha256sum app/target/app-1.0.0.jar   > checkpoint-evidence/jar-repaired.sha256

11. Plugin source and prefix policy check

Record that the required build uses Apache Maven plugin coordinates under org.apache.maven.plugins and the organization's approved mirror/repository policy. Inspect effective settings if needed, but do not copy real server credentials into evidence. For security-sensitive automation or diagnosis, prefer explicit plugin coordinates/versions rather than relying on an unexplained prefix mapping.

CI rule: build plugins execute with CI-agent privileges. Fork/untrusted-code jobs should not receive publishing/signing credentials merely because the same Maven command is used on trusted branches.

12. Final verification checklist

Check Evidence / pass condition
Wrapper/runtime Maven 3.9.16 and JDK identity captured.
Project model Effective POM contains intended managed versions/configuration.
Activation Only intended child plugins are active; custom AntRun is explicitly referenced.
Execution One write-build-marker execution at prepare-package.
Tests Surefire reports show the expected test ran and passed.
Artifact JAR contains marker and expected manifest entry.
Phase failure Broken run proves verify is too late for marker inclusion.
Repository/cache Only isolated .lab-m2 is used for lab state.
Trust inventory Coordinates, versions, activation source, execution ids, repository policy documented.
Secrets No real repository/signing/CI credentials in POM, logs, or evidence bundle.

13. Cleanup / rollback

Restore the good parent POM before archiving evidence. Remove only the disposable lab tree or its target/ and .lab-m2 directories. If you are preserving checkpoint evidence, copy it outside the disposable directory first. No remote repository, CI system, or shared Maven cache was modified by the mandatory path.

Knowledge check

The custom plugin is centrally managed. What single fact proves it is also active in the application module?

Why does moving the marker execution to verify create a JAR-content failure even though Maven reports the goal ran?

Why inventory Maven core default bindings separately from project pins?

A short prefix resolves to an unexpected plugin group. What is the safe diagnostic move?

What is the production value of stable execution ids?

14. What Chapter 07 adds to the production build-engineering model

  • Executable-input inventory: plugins are reviewed/pinned build dependencies, not invisible Maven features.
  • Activation clarity: packaging defaults, active plugin declarations, managed policy, and direct goals are distinguishable.
  • Lifecycle evidence: execution ids and phases explain ordering and artifact effects.
  • Inheritance governance: parent policy is verified through effective child models, not assumed.
  • Supply-chain boundary: plugin source, prefix metadata, mirrors, local repository state, and CI privileges are explicit concerns.
  • Failure discipline: the team repairs the narrow model cause instead of deleting caches, duplicating plugins, or changing repositories blindly.

Chapter 08 builds on this plugin model to cover Maven properties, profiles, settings.xml, mirrors, proxies, servers, and environment-specific build state—where activation and machine-specific configuration become the next source of reproducibility/security risk.

Official references and version notes

Version-sensitive statements in this lesson were checked against Apache Maven primary documentation on 2026-08-23. The required path uses Maven 3.9.16 through Maven Wrapper 3.3.4, JDK 21 to run Maven, Java 17 as the project release target, Maven Compiler Plugin 3.15.0, Surefire 3.5.4, Maven JAR Plugin 3.5.1, Maven Resources Plugin 3.5.0, Maven Help Plugin 3.5.2, and Maven AntRun Plugin 3.2.0. Maven 4 preview-only behavior is not required in this chapter.

The checkpoint intentionally contrasts Maven 3.9.16 built-in lifecycle plugin versions with centrally selected project pins. Current standalone plugin versions should always be re-checked before future upgrades.

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.