Series MapLesson 43 / 50
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Final StretchOrdered learning track

Maven Build Engineering

Maven Build, Dependency, Plugin, and Multi-Module Engineering

Mendesain build Maven production-grade untuk JAX-RS enterprise services: POM/effective model, lifecycle, reactor, dependency management, BOM, plugins, toolchains, repositories, testing, packaging, reproducibility, release, supply-chain controls, dan troubleshooting.

35 min read6857 words
PrevNext
Lesson 4350 lesson track42–50 Final Stretch
#maven#pom#dependency-management#bom+11 more

Part 043 — Maven Build, Dependency, Plugin, and Multi-Module Engineering

Maven bukan sekadar perintah mvn clean package. Maven membentuk effective project model dari POM, parent, profiles, settings, repositories, dependency metadata, plugin defaults, dan command-line properties; kemudian menjalankan plugin goals melalui lifecycle dan reactor. Build dapat lulus di laptop namun gagal di CI atau production ketika dependency graph, plugin version, JDK toolchain, repository mirror, annotation processor, test fork, shading, atau artifact publication berbeda. Build engineering bertujuan membuat hasil dapat diprediksi, diaudit, direproduksi, dan dipromosikan sebagai artifact yang sama—bukan dibangun ulang secara diam-diam pada setiap environment.

Daftar Isi

  1. Target kompetensi
  2. Scope dan baseline
  3. Current Maven release reality
  4. Boundary dengan Part 041, 042, and 044
  5. Mental model Maven build
  6. Maven model versus Maven execution
  7. Coordinates and artifact identity
  8. POM as declarative project model
  9. Super POM and default conventions
  10. Effective POM
  11. Parent inheritance
  12. Aggregation versus inheritance
  13. Relative parent resolution
  14. Properties and interpolation
  15. Model interpolation boundaries
  16. Profiles
  17. Profile activation
  18. Profiles are not deployment configuration
  19. Settings and effective settings
  20. Global, user, and project settings
  21. Lifecycle mental model
  22. Built-in lifecycles
  23. Default lifecycle phases
  24. Phase versus goal
  25. Packaging and default lifecycle bindings
  26. Plugin executions
  27. Execution IDs and merge behavior
  28. PluginManagement versus plugins
  29. Plugin versions
  30. Plugin dependencies
  31. Build extensions
  32. Maven core extensions
  33. Reactor mental model
  34. Reactor sort order
  35. Selected-project builds
  36. Resume and failure behavior
  37. Parallel reactor builds
  38. Thread-safe plugins
  39. Multi-module architecture
  40. Parent POM module
  41. BOM module
  42. Library and service modules
  43. Dependency mechanism
  44. Transitive dependencies
  45. Nearest-definition mediation
  46. Dependency scopes
  47. Compile scope
  48. Provided scope
  49. Runtime scope
  50. Test scope
  51. System and import scopes
  52. Optional dependencies
  53. Exclusions
  54. DependencyManagement
  55. Bill of Materials
  56. BOM import order and ownership
  57. Direct dependency declarations
  58. Version ranges and dynamic versions
  59. Snapshots
  60. Dependency convergence
  61. Upper-bound dependency rules
  62. Duplicate classes and split packages
  63. Dependency tree and effective classpath
  64. Plugin classpath versus project classpath
  65. Annotation processors
  66. Generated sources
  67. JPMS and module path
  68. JAX-RS API and implementation dependencies
  69. Provided APIs and runtime ownership
  70. Jersey modules and provider discovery
  71. ServiceLoader resources
  72. Compile plugin
  73. --release versus source and target
  74. Toolchains
  75. Maven runtime JDK versus build toolchain JDK
  76. Multi-release JAR boundary
  77. Resource processing
  78. Resource filtering risks
  79. Encoding and locale
  80. Surefire
  81. Failsafe
  82. Test discovery conventions
  83. Forked JVMs
  84. Test classpath and module path
  85. JaCoCo and instrumentation order
  86. JAR packaging
  87. WAR packaging
  88. Thin JAR versus fat JAR
  89. Shade plugin
  90. Relocation
  91. Resource transformers
  92. Minimization risks
  93. Assembly and dependency-copy patterns
  94. Application server packaging
  95. Manifest and main class
  96. Source and Javadoc artifacts
  97. Repository mental model
  98. Local repository
  99. Remote repositories
  100. Mirrors
  101. Repository managers
  102. Plugin repositories
  103. Repository policy and snapshots
  104. Credentials and servers
  105. Encrypted Maven passwords
  106. Checksums and signatures
  107. Offline builds
  108. Repository poisoning and shadowing
  109. Maven Wrapper
  110. Wrapper distribution modes
  111. Maven Daemon boundary
  112. CI-friendly versions
  113. Release versioning
  114. Release artifact immutability
  115. Deploy versus install
  116. Staging and promotion
  117. Reproducible builds
  118. project.build.outputTimestamp
  119. Sources of nondeterminism
  120. Rebuild verification
  121. Build provenance and SBOM
  122. Dependency vulnerability scanning
  123. License and policy checks
  124. Maven Enforcer
  125. Common Enforcer rules
  126. Ban and allow-list policies
  127. Build caching
  128. Maven build cache boundary
  129. CI workspace and local-repository caching
  130. Cache poisoning and stale metadata
  131. Maven 4 migration boundary
  132. Maven 4 model and build changes
  133. Maven Upgrade Tool
  134. Troubleshooting toolkit
  135. help:effective-pom
  136. help:effective-settings
  137. dependency:tree
  138. dependency:analyze
  139. Debug logging
  140. Build consumer versus producer behavior
  141. Failure-model matrix
  142. Debugging playbook
  143. Architecture patterns
  144. Anti-patterns
  145. PR review checklist
  146. Trade-off yang harus dipahami senior engineer
  147. Internal verification checklist
  148. Latihan verifikasi
  149. Ringkasan
  150. Referensi resmi

Target kompetensi

Setelah menyelesaikan part ini, Anda harus mampu:

  • menjelaskan bagaimana Maven membentuk effective model dari POM, parent, profiles, settings, dan CLI;
  • membedakan lifecycle phase, plugin goal, execution, packaging binding, dan reactor;
  • mendesain multi-module structure yang memisahkan parent, BOM, libraries, service, and distribution modules;
  • memahami transitive dependency mediation, scopes, optional dependencies, exclusions, and dependency management;
  • mengelola BOM import without losing ownership of versions;
  • mendeteksi dependency convergence, duplicate classes, split packages, and runtime classpath drift;
  • membedakan project dependency classpath, plugin classpath, annotation-processor path, and module path;
  • menggunakan toolchains so Maven runtime JDK and compilation/test JDK are explicit;
  • mengkonfigurasi Surefire, Failsafe, JaCoCo, compiler, resources, JAR/WAR, and Shade deliberately;
  • memilih thin JAR, fat JAR, WAR, or distribution layout based on runtime architecture;
  • mengoperasikan local/remote repositories, mirrors, repository managers, credentials, and snapshot policies;
  • menggunakan Maven Wrapper and CI-friendly versions;
  • membuat immutable release and promote the same artifact;
  • menghasilkan reproducible JAR/WAR artifacts and verify rebuilds;
  • menambahkan Enforcer, dependency policy, SBOM, signature, and vulnerability gates;
  • mendiagnosis effective POM, dependency tree, plugin execution, and CI/local differences;
  • memahami bahwa Maven 4 remains a preview boundary until officially declared production-safe.

Scope dan baseline

Baseline:

  • Java 17+ source baseline;
  • Maven 3.9.x as current stable family;
  • Maven 4 concepts are discussed as migration/preview boundary;
  • multi-module JAX-RS/Jersey backend;
  • JUnit/Surefire/Failsafe from Part 041;
  • Docker image assembly follows in Part 044;
  • internal artifact repository manager may exist;
  • CI/CD and release pipelines are external to Maven but invoke it.

Part ini tidak mengasumsikan:

  • Maven 4 is production-approved;
  • exact stable Maven patch version in internal agents;
  • one parent/BOM architecture;
  • one artifact repository vendor;
  • Maven Central direct access is allowed;
  • WAR or fat JAR packaging;
  • Spring Boot plugin;
  • Shade plugin is required;
  • internal shared parent POM;
  • build cache is enabled;
  • release plugin is used;
  • dependency lockfile exists;
  • current CSG repository topology.

Current Maven release reality

As of July 2026, the official Maven download page lists:

  • Maven 3.9.16 as current stable/recommended release;
  • Maven 4.0.0-rc-5 as preview;
  • Maven 4 requiring JDK 17 to execute;
  • official warning that Maven 4 preview is not yet safe for production use.

Do not hard-code these exact versions into long-lived architecture policy. Instead maintain a verified supported-version matrix.


Boundary dengan Part 041, 042, and 044

PartFokus
Part 041Test strategy and evidence
Part 042Performance measurement and capacity
Part 043Build model, dependencies, plugins, artifacts, repositories, release
Part 044Container image construction, runtime filesystem/process, and supply chain

Maven should produce a deterministic application artifact. Docker should consume that artifact or execute a controlled Maven build stage without changing application semantics.


Mental model Maven build

flowchart LR SRC[Source tree] --> MODEL[POM + parent + profiles] SETTINGS[settings.xml + mirrors + credentials] --> MODEL MODEL --> EFFECTIVE[Effective model] EFFECTIVE --> GRAPH[Dependency + plugin graphs] GRAPH --> REACTOR[Reactor order] REACTOR --> LIFECYCLE[Lifecycle phases] LIFECYCLE --> GOALS[Plugin goals] GOALS --> OUTPUT[Classes/tests/JAR/WAR] OUTPUT --> VERIFY[Quality and reproducibility gates] VERIFY --> REPO[Immutable artifact repository]

Build correctness requires both the model and execution environment.


Maven model versus Maven execution

Model answers:

  • project identity;
  • dependencies;
  • plugins;
  • modules;
  • repositories;
  • properties;
  • profiles.

Execution answers:

  • requested phase/goal;
  • reactor selection;
  • Maven/JDK versions;
  • settings and credentials;
  • available repositories;
  • environment and CLI properties;
  • parallelism;
  • filesystem and locale.

The same POM can behave differently under different execution context.


Coordinates and artifact identity

Canonical coordinates:

groupId:artifactId:packaging:classifier:version

Examples:

com.example.quote:quote-domain:jar:1.4.2
com.example.quote:quote-service:jar:tests:1.4.2

Coordinates must identify immutable release content.


POM as declarative project model

POM declares what a project is and how plugins participate.

Avoid turning POM into an imperative shell-script replacement through excessive Ant executions or OS-specific command chains.


Super POM and default conventions

Every POM inherits defaults from Maven's Super POM and packaging lifecycle mappings.

Defaults may change across major Maven/plugin versions. Explicitly pin production-relevant plugin versions and inspect effective POM.


Effective POM

Effective POM results from merging:

  • Super POM;
  • parent hierarchy;
  • current POM;
  • active profiles;
  • interpolation;
  • model rules.

Use it to answer “what Maven actually sees.”


Parent inheritance

Inherited elements include many properties, dependency/plugin management, build configuration, and metadata.

Not every element inherits identically. Module aggregation is separate.

Parent should define organization-wide defaults and policies, not application-specific dependencies that every child accidentally receives.


Aggregation versus inheritance

Aggregator:

<packaging>pom</packaging>
<modules>
  <module>quote-domain</module>
  <module>quote-service</module>
</modules>

Parent inheritance uses <parent>.

A project can be aggregator, parent, both, or neither.

Keep distinction explicit because reactor and model inheritance solve different problems.


Relative parent resolution

Maven may resolve parent from relative path before repository, depending configuration.

For released reusable parent, consider explicit empty <relativePath/> to avoid accidentally using a local sibling POM.

Verify internal standard.


Properties and interpolation

Use properties for governed versions and common settings:

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

Avoid creating a second programming language in property indirection.


Model interpolation boundaries

Interpolation timing differs between model fields and plugin runtime parameters.

Environment and system properties can make model non-reproducible.

Do not embed secrets in POM properties.


Profiles

Profiles modify the effective model.

Use for build-environment concerns such as:

  • optional integration tests;
  • JDK-specific compilation;
  • artifact-signing in release;
  • platform-native modules.

Avoid using profiles for DEV/UAT/PROD application configuration.


Profile activation

Activation sources include:

  • explicit -P;
  • property;
  • JDK;
  • OS;
  • file existence;
  • active profiles in settings.

Implicit activation can surprise CI. Report active profiles in build diagnostics.


Profiles are not deployment configuration

Bad:

<profile id="prod">
  <properties>
    <database.password>...</database.password>
  </properties>
</profile>

The build artifact should remain environment-neutral. Runtime config belongs to deployment/configuration systems.


Settings and effective settings

settings.xml configures execution-user/environment concerns:

  • local repository;
  • mirrors;
  • proxies;
  • servers/credentials;
  • profiles;
  • plugin groups.

Do not commit personal credentials.


Global, user, and project settings

Maven supports global and user settings; newer Maven tooling may support project-scoped settings patterns depending version.

CI should supply a controlled settings file and disclose its hash/source without exposing secrets.


Lifecycle mental model

Lifecycle is ordered phases. Plugins bind goals to phases.

Invoking a later phase executes earlier phases in that lifecycle.


Built-in lifecycles

Maven defines three built-in lifecycles:

  • clean;
  • default;
  • site.

clean package invokes phases from two separate lifecycles sequentially.


Default lifecycle phases

Important phases include:

validate
compile
test
package
verify
install
deploy

There are additional integration-test setup/teardown phases.

Prefer invoking verify for full local/CI correctness rather than package if integration/quality gates bind later.


Phase versus goal

Phase:

mvn verify

Goal:

mvn dependency:tree
mvn help:effective-pom

Direct goal invocation may bypass lifecycle assumptions.


Packaging and default lifecycle bindings

Packaging such as jar, war, and pom determines default plugin bindings.

A custom packaging type can require extensions and adds maintenance risk.


Plugin executions

A plugin can have multiple executions with:

  • ID;
  • goals;
  • phase;
  • configuration.

Example:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>${maven.failsafe.version}</version>
  <executions>
    <execution>
      <id>integration-tests</id>
      <goals>
        <goal>integration-test</goal>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Execution IDs and merge behavior

Parent and child executions with same ID can merge/override according to model rules.

Use stable, meaningful IDs and inspect effective POM after parent changes.


PluginManagement versus plugins

pluginManagement defines versions/default configuration for child opt-in.

plugins activates/configures plugin in current project and can be inherited.

Do not assume a plugin in pluginManagement runs.


Plugin versions

Pin plugin versions.

Unpinned plugins make builds dependent on Maven defaults or repository metadata and weaken reproducibility.

Govern compiler, resources, Surefire, Failsafe, JAR/WAR, Shade, Enforcer, source, Javadoc, deploy, and release-related plugins.


Plugin dependencies

Plugins have their own dependency graph.

Overriding plugin dependencies can fix compatibility but may break plugin internals. Document why and remove when upstream resolves it.


Build extensions

Build extensions can participate in artifact handlers, transports, lifecycle, and model resolution.

They execute with powerful access to the build. Pin, review, and minimize them.


Maven core extensions

.mvn/extensions.xml can load core extensions.

Core extensions affect the entire build and supply-chain trust boundary.


Reactor mental model

The reactor collects selected projects, sorts them by inter-project relationships, and builds them in order.

flowchart LR P[parent] --> D[domain] P --> API[api] D --> APP[application] API --> APP APP --> SVC[service] SVC --> DIST[distribution]

Reactor sort order

Sort order considers actual project references such as dependencies and plugin relationships, not simply <modules> order.

Dependency management alone generally does not create build ordering without an actual reference.


Selected-project builds

Common options:

-pl module
-am
-amd
-rf

Selected builds can skip required quality gates or produce misleading local repository state. CI should define supported commands.


Resume and failure behavior

Maven can fail fast, fail at end, or resume depending flags/version.

A resumed build may reuse artifacts in local repository from prior partial execution. Clean workspace or verify provenance when correctness matters.


Parallel reactor builds

-T can build modules concurrently.

Risks:

  • non-thread-safe plugins;
  • shared generated files;
  • fixed ports;
  • integration test resource collisions;
  • repository-manager pressure;
  • nondeterministic logs.

Measure and verify.


Thread-safe plugins

Plugin metadata may declare thread safety, but custom scripts/extensions and external resources may still conflict.

Parallel-safe build is an end-to-end property.


Multi-module architecture

Example:

quote-order-parent/
  pom.xml
  quote-order-bom/
  quote-domain/
  quote-application/
  quote-adapter-jaxrs/
  quote-adapter-postgres/
  quote-service/
  quote-distribution/

Avoid one module per package or a single module containing unrelated deployables.


Parent POM module

Parent owns:

  • plugin versions/configuration;
  • Java/toolchain policy;
  • encoding;
  • quality gates;
  • organization metadata;
  • common properties.

Parent should not force runtime dependencies into every child.


BOM module

BOM owns dependency versions and compatibility set.

It should not contain build plugin configuration as its primary purpose.

Separate parent and BOM when consumers need version alignment without inheriting build policy.


Library and service modules

Library module publishes reusable API/implementation.

Service module produces deployable artifact and should own runtime closure.

Do not let internal module boundaries become accidental public APIs.


Dependency mechanism

Maven resolves dependency graph from direct POM declarations and transitive metadata.

Resolution is not simply “highest version wins.”


Transitive dependencies

A direct dependency can bring many artifacts.

Treat each transitive artifact as supply-chain and classpath input.


Nearest-definition mediation

When multiple versions appear, Maven typically chooses the nearest definition in dependency tree; ties can depend on declaration order.

Declare important versions directly or in dependency management.


Dependency scopes

Scope controls availability across compile, runtime, test, and transitivity.

Mis-scoping often causes production ClassNotFoundException or artifact bloat.


Compile scope

Default scope. Available in compile/runtime/test and generally transitive.

Use only when consumers or runtime truly require the dependency.


Provided scope

Compile/test available, expected from runtime container/platform.

Typical for Jakarta APIs when deploying to a compliant application server.

Incorrect use in standalone runtime causes missing classes.


Runtime scope

Not needed to compile application code but needed at runtime, such as some drivers/implementations.

Ensure packaging/container includes it.


Test scope

Only test compilation/execution.

Test utility APIs accidentally referenced from main code expose scope mistakes.


System and import scopes

system uses local filesystem path and is non-portable; avoid.

import is for POM dependencies inside dependencyManagement, typically BOM imports.


Optional dependencies

Optional means downstream consumers do not inherit it transitively.

It does not mean optional at runtime for the declaring module.

Use for feature integrations where consumer must opt in.


Exclusions

Exclusion removes one transitive edge.

Every exclusion needs a reason and compatibility test. It can create runtime gaps.


DependencyManagement

Dependency management supplies versions and other defaults when dependency is declared.

It does not add the dependency to classpath by itself.


Bill of Materials

BOM is a POM imported in dependencyManagement:

<dependency>
  <groupId>com.example.platform</groupId>
  <artifactId>enterprise-bom</artifactId>
  <version>${platform.version}</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>

Use a BOM as tested compatibility set.


BOM import order and ownership

Multiple BOMs can manage the same artifact. Effective result can depend on management order and explicit overrides.

Define ownership:

  • Jakarta platform;
  • Jersey;
  • observability;
  • cloud SDK;
  • testing.

Avoid uncontrolled “BOM soup.”


Direct dependency declarations

Declare libraries your code directly uses even if currently transitive.

This documents API dependency and protects against parent dependency graph changes.


Version ranges and dynamic versions

Avoid ranges, LATEST, RELEASE, and floating versions for production builds.

They make rebuild content time-dependent.


Snapshots

Snapshots are mutable development versions.

Repository metadata resolves timestamped snapshot builds.

Do not release production from unresolved mutable snapshots unless policy explicitly accepts it.


Dependency convergence

Convergence means one selected version per coordinate path as required by policy.

Even when Maven mediates one version, callers compiled against another can be incompatible.

Use Enforcer and integration tests.


Upper-bound dependency rules

“Require upper bound” policies try to ensure resolved version is not lower than versions requested transitively.

Higher is not automatically compatible. Use it as signal, not semantic proof.


Duplicate classes and split packages

Different artifacts can contain same class/package.

Symptoms:

  • classpath-order behavior;
  • linkage errors;
  • service discovery confusion;
  • JPMS split-package failure.

Scan packaged artifact and dependency graph.


Dependency tree and effective classpath

Use filtered verbose trees and classpath output to understand:

  • omitted versions;
  • scopes;
  • optional/excluded paths;
  • conflict winner;
  • duplicate sources.

Do not debug only from top-level dependency list.


Plugin classpath versus project classpath

Plugin code runs in plugin realm with its own dependencies.

Adding dependency to project does not necessarily change plugin behavior, and vice versa.


Annotation processors

Processors execute during compilation and can generate code/resources.

Pin versions and isolate processor path where supported.

They are build-time executable code with supply-chain risk.


Generated sources

Generated sources must be:

  • deterministic;
  • added to compile roots;
  • cleaned;
  • not manually edited;
  • version-compatible;
  • included/excluded from source control by policy.

JPMS and module path

Maven/compiler/Surefire can use classpath or module path.

Automatic module names, split packages, reflection, and test patching can differ.

Decide explicitly; do not accidentally enter module-path mode after adding module-info.java.


JAX-RS API and implementation dependencies

Separate:

  • Jakarta REST API;
  • Jersey server/client modules;
  • servlet/container integration;
  • JSON/XML providers;
  • HK2/CDI integration;
  • Grizzly/Jetty/other runtime.

Do not depend on implementation transitively through unrelated module.


Provided APIs and runtime ownership

Application server deployment:

Jakarta APIs → provided
server implementation → platform-owned

Standalone deployment:

JAX-RS implementation and server connector → runtime dependencies/package

Test both actual packaging modes.


Jersey modules and provider discovery

Jersey features/providers may require explicit modules and registration.

A successful compile does not prove runtime provider discovery.


ServiceLoader resources

Fat JAR assembly must merge META-INF/services/* when required.

Overwriting instead of merging can remove providers, logging bindings, drivers, or annotation metadata.


Compile plugin

Pin compiler plugin and configure release/annotation processors/warnings deliberately.

Enable warnings policy without making generated code unusable.


--release versus source and target

--release constrains language, bytecode, and documented JDK API surface for target release.

source + target alone can still compile against APIs unavailable on target runtime.


Toolchains

Toolchains let plugins select JDK tools independently of the JDK running Maven.

Use to standardize compile/test/Javadoc JDKs across developer and CI.


Maven runtime JDK versus build toolchain JDK

Example:

Maven 3.9 runs on JDK 21
compiler toolchain builds --release 17
integration tests run on JDK 17 and 21 matrix

Record both in build provenance.


Multi-release JAR boundary

Multi-release JAR can contain version-specific classes under META-INF/versions.

It adds testing and packaging complexity. Use only for library needs with multi-JDK matrix.


Resource processing

Resources plugin copies/filter resources.

Separate filtered configuration templates from binary/static resources.


Resource filtering risks

Filtering can corrupt binary files or replace ${...} intended for runtime/framework.

Use explicit includes and delimiters.

Never filter secrets into artifact.


Encoding and locale

Set source/resource/report encodings and test locale/timezone where behavior matters.

Build should not depend on workstation default encoding.


Surefire

Surefire runs unit-style tests during test.

Pin version and configure JUnit Platform, forks, module path, system properties, and reports.


Failsafe

Failsafe runs integration tests across integration-test and verify.

Failure is finalized in verify, allowing post-integration cleanup phases.

Invoke verify, not direct integration-test, in CI.


Test discovery conventions

File naming controls default discovery.

Use explicit conventions such as:

*Test.java
*Tests.java
*IT.java

Align Surefire/Failsafe to avoid double execution or missing tests.


Forked JVMs

Forks isolate tests but consume memory/processes.

Configure:

  • fork count;
  • reuse;
  • heap/native limits;
  • argLine;
  • environment;
  • parallelism.

CI OOM can originate from test forks plus containers.


Test classpath and module path

Test execution may differ from production classpath due to test-scope libraries and module-path patching.

Add packaged-artifact smoke test.


JaCoCo and instrumentation order

JaCoCo agent commonly injects argLine for test forks.

Other plugins/profiles overriding argLine can remove it.

Use late property evaluation patterns supported by plugins and inspect command line.


JAR packaging

JAR includes compiled classes/resources and manifest.

A normal JAR does not automatically contain dependencies.


WAR packaging

WAR can package web resources and libraries under standard layout.

Use when Servlet/application server owns runtime.

Review duplicate platform libraries and provided scopes.


Thin JAR versus fat JAR

Thin JAR:

  • dependencies external;
  • smaller app artifact;
  • runtime classpath assembly needed.

Fat JAR:

  • single distributable;
  • resource/class collisions;
  • larger rebuild/layer churn;
  • license/metadata concerns.

Shade plugin

Shade builds uber JAR and can relocate packages/transform resources.

Test runtime discovery and signatures.


Relocation

Relocation changes package names to isolate dependency conflicts.

It can break:

  • reflection;
  • serialized names;
  • service descriptors;
  • config strings;
  • JPMS metadata.

Use only for deliberate library isolation.


Resource transformers

Transformers may merge:

  • services;
  • manifests;
  • properties;
  • license/notice files.

Review all collision behavior.


Minimization risks

Shade minimization attempts remove apparently unused classes.

Reflection, CDI, JAX-RS provider discovery, JAXB, ServiceLoader, and serialization may look unused statically.

Avoid unless extensive runtime tests prove correctness.


Assembly and dependency-copy patterns

Alternative distribution:

app.jar
lib/*.jar
conf/

This improves Docker layer caching and classpath visibility but requires launcher/classpath management.


Application server packaging

For external container, package only app-owned libraries and APIs according to server contract.

Test against exact server version.


Manifest and main class

Define:

  • Main-Class if executable;
  • implementation/build metadata;
  • automatic module name if needed;
  • classpath only if deployment strategy uses it.

Avoid timestamps or environment-specific metadata in reproducible artifact unless controlled.


Source and Javadoc artifacts

Libraries/public artifacts often publish source and Javadoc JARs.

Pin plugins and use same release toolchain.


Repository mental model

flowchart LR BUILD[Maven resolver] --> LOCAL[(Local repository)] LOCAL --> MIRROR[Mirror/repository manager] MIRROR --> HOSTED[Hosted releases/snapshots] MIRROR --> PROXY[Proxied external repositories] BUILD --> PLUGIN[Plugin repositories]

Repository resolution is part of the software supply chain.


Local repository

Local repository is cache plus installed artifacts/metadata.

It can be corrupted or polluted by locally installed unreleased versions.

CI should use controlled cache/workspace and avoid relying on developer-local installs.


Remote repositories

Prefer centrally managed repositories.

Do not add arbitrary third-party repositories to application POM because they affect every consumer and widen trust.


Mirrors

Settings mirrors redirect repository requests to approved repository manager.

Mirror patterns and ordering require testing.

A mirror is not merely a faster cache; it is a policy boundary.


Repository managers

Repository manager can provide:

  • proxy/cache;
  • hosted releases/snapshots;
  • quarantine;
  • policy/scanning;
  • retention;
  • access control;
  • audit.

Separate immutable releases from mutable snapshots.


Plugin repositories

Plugins resolve separately from project dependencies.

Avoid untrusted plugin repositories. Plugins execute code during build.


Repository policy and snapshots

Configure release/snapshot enablement, update policy, and checksum policy.

CI release build should not silently resolve newer snapshot metadata mid-run.


Credentials and servers

Credentials belong in settings <servers> referenced by server ID.

Use CI secret injection and least privilege.

Never place plaintext credentials in POM or command logs.


Encrypted Maven passwords

Maven supports password encryption mechanisms, but encryption key handling and threat model must be understood.

CI secret managers are usually stronger than committed encrypted values.


Checksums and signatures

Verify Maven distribution signatures/checksums and artifact repository integrity according to policy.

A checksum detects corruption, not publisher trust by itself.


Offline builds

-o proves dependencies/plugins are locally available but does not guarantee clean-room reproducibility.

A controlled repository snapshot or dependency preload is needed.


Repository poisoning and shadowing

Risks:

  • malicious coordinate in public repository;
  • internal/public namespace collision;
  • mutable snapshot;
  • compromised repository credential;
  • plugin execution.

Reserve internal group IDs and route through policy-controlled manager.


Maven Wrapper

Wrapper provides project-pinned Maven distribution invocation:

./mvnw verify
mvnw.cmd verify

Commit wrapper scripts/config according to security policy.


Wrapper distribution modes

Wrapper can use different download/distribution modes depending wrapper version.

Pin URL/version/checksum and avoid opaque unverified downloads.


Maven Daemon boundary

mvnd can improve local/repeated build startup and parallelism.

Daemon state can retain JDK/environment/classloader effects. CI adoption requires reproducibility and isolation review.


CI-friendly versions

Maven supports placeholders such as:

${revision}
${sha1}
${changelist}

Use only supported patterns and flatten/publish consumer POM correctly when required.


Release versioning

Version should identify source and artifact immutably.

Do not reuse a released version.

Choose semantic/calendar/release-train convention and document compatibility meaning.


Release artifact immutability

Build once, test once, sign once, then promote the same bytes across environments.

Do not rebuild JAR/WAR for UAT and PROD with different profile properties.


Deploy versus install

install writes artifact into local repository.

deploy publishes to remote repository.

CI stages should prevent accidental snapshot/release destination confusion.


Staging and promotion

Preferred flow:

build candidate
  → verify
  → publish immutable candidate
  → policy/signature approval
  → promote repository metadata/tag

Promotion should not alter bytes.


Reproducible builds

A reproducible build produces byte-for-byte identical output from identical declared inputs and controlled environment.

It improves audit and tamper detection.


project.build.outputTimestamp

Set a controlled timestamp for ZIP/JAR entries:

<project.build.outputTimestamp>
  ${env.SOURCE_DATE_EPOCH}
</project.build.outputTimestamp>

or a fixed/CI-provided value according to Maven/plugin support.


Sources of nondeterminism

Examples:

  • archive timestamps;
  • file order;
  • generated metadata;
  • locale/timezone;
  • random IDs;
  • absolute paths;
  • non-pinned dependencies/plugins;
  • generated source order;
  • manifest build host/user;
  • resource line endings;
  • JDK version.

Rebuild verification

Process:

  1. clean environment;
  2. fetch declared source/dependencies;
  3. build with pinned toolchain;
  4. compare hashes;
  5. inspect differences with archive diff tools;
  6. retain evidence.

Build provenance and SBOM

Generate component inventory and provenance at JAR/image layers.

Maven dependency SBOM and container SBOM answer overlapping but different questions.

Link source commit, Maven artifact hash, image digest, and attestations.


Dependency vulnerability scanning

Scan direct and transitive dependencies.

Policy needs:

  • severity;
  • exploitability/context;
  • suppression owner/expiry;
  • fix availability;
  • runtime reachability limitations;
  • snapshot freshness.

Do not blindly fail on every database entry without triage.


License and policy checks

Track licenses, notices, prohibited groups/artifacts, and cryptography/export concerns where applicable.

Generated fat JAR/image must retain required notices.


Maven Enforcer

Enforcer runs policy rules during build.

Use early validate phase for fast feedback.


Common Enforcer rules

Examples:

  • require Maven/JDK versions;
  • dependency convergence;
  • require upper-bound dependencies;
  • banned dependencies;
  • require release dependencies;
  • duplicate classes via additional rules;
  • enforce bytecode version.

Rule availability depends on plugin/additional-rule version.


Ban and allow-list policies

Ban known-problematic artifacts/groups and require approved APIs.

Every exception must have rationale, owner, and expiry.


Build caching

Caching can reuse outputs when inputs match.

Correct cache key must include:

  • source/resources;
  • POM/effective config;
  • JDK/toolchain;
  • plugin/dependency versions;
  • environment inputs;
  • generated-code inputs.

Maven build cache boundary

Maven build-cache capabilities vary by extension/version and may still require organizational evaluation.

Do not assume experimental cache produces release-safe evidence.


CI workspace and local-repository caching

Caching .m2/repository speeds downloads but can preserve:

  • corrupted artifacts;
  • stale metadata;
  • locally installed modules;
  • snapshots;
  • partial downloads.

Use immutable cache keys and periodic clean rebuild.


Cache poisoning and stale metadata

Treat CI caches as untrusted acceleration, not authoritative repository.

Verify checksums and repository source; do not cache secrets/settings.


Maven 4 migration boundary

Maven 4 introduces model/execution improvements and stricter behavior, but as of July 2026 official download guidance still treats release candidates as preview and not production-safe.

Run compatibility CI without replacing stable release until approved.


Maven 4 model and build changes

Potential migration areas include:

  • stricter model validation;
  • new POM/model capabilities;
  • root project semantics;
  • reactor behavior;
  • plugin compatibility;
  • consumer/build POM separation;
  • JDK 17 runtime requirement.

Use official migration documentation for selected RC/final version.


Maven Upgrade Tool

Official Maven site exposes an Upgrade Tool for migration assistance.

Generated changes still require review and full build/release verification.


Troubleshooting toolkit

Always capture:

mvn -version
wrapper configuration
active profiles
effective POM/settings
dependency tree
plugin versions
JDK/toolchain
repository/mirror IDs
full error with -e/-X when needed

Redact credentials.


help:effective-pom

Use to inspect inherited and profile-activated model.

Optionally include verbose origin comments where supported.


help:effective-settings

Use to confirm mirrors, profiles, proxies, and server IDs without exposing secret values.


dependency:tree

Use filters and verbose output to locate mediation path.

Example:

mvn dependency:tree -Dincludes=com.fasterxml.jackson.core

dependency:analyze

Find used-undeclared and unused-declared dependencies.

Reflection, ServiceLoader, annotation processors, and runtime-only use can cause false positives; review rather than auto-delete.


Debug logging

-X reveals model/resolver/plugin details but can expose environment, URLs, or headers.

Use temporarily and protect logs.


Build consumer versus producer behavior

A project build can consume rich parent/profile model, while published consumers should receive a clean POM with resolvable versions and no local-only placeholders.

Validate published POM, not just JAR.


Failure-model matrix

FailureImpactDetectionResponse
Plugin version unpinnedbuild changes over timeeffective POM diffpin in parent
Parent adds runtime dependency globallyclasspath bloat/couplingdependency treeuse management, not dependencies
BOM conflict hidden by orderwrong library familyeffective managementexplicit owner/override tests
Transitive API used undeclaredbreak after graph changedependency analyze/compiledirect declaration
provided in standalone appruntime missing classpackaged smoke testruntime scope/package
Platform API packaged in WARclassloader conflictserver integrationprovided scope
Duplicate classesunpredictable class loadingscan/enforcerconverge/exclude
Shade overwrites servicesmissing providerruntime testservice transformer
Shade minimization removes reflective classproduction failureintegration/native pathdisable/keep rules
Annotation processor floatsgenerated-code driftbuild diffpin processor path
source/target uses newer APIruntime linkage errortarget-JDK test--release/toolchain
Profile embeds environment configdifferent artifacts per envhash comparisonruntime config
Integration tests bound but CI runs packagemissing verificationpipeline auditrun verify
integration-test called directlycleanup/verification skippedCI commandinvoke verify
Local installed artifact masks missing repoCI-only failureclean repo buildisolated local repo
Mutable snapshot in releaseunreproducible binarydependency policyrelease-only dependencies
Direct public repositoriessupply-chain bypassPOM scanmirror/repository manager
CI .m2 cache poisonedwrong/corrupt inputclean rebuildverify/purge cache
Release version redeployedartifact ambiguityrepository policyimmutable repository
Rebuild per environmentuntraceable driftdigest mismatchpromote same artifact
Maven 4 RC used as stable silentlyplugin/build riskversion gateapproved stable matrix

Debugging playbook

Works locally, fails in CI

Compare:

  • Maven and JDK versions;
  • wrapper usage;
  • toolchains;
  • settings/mirrors;
  • active profiles;
  • local repository pollution;
  • filesystem case/line endings;
  • locale/timezone;
  • reactor selection;
  • CI secrets/proxies.

ClassNotFoundException in packaged service

Check:

  • dependency scope;
  • fat/thin packaging;
  • Shade transformers;
  • runtime dependency copy;
  • provided APIs;
  • classloader/container;
  • duplicate versions;
  • module path.

NoSuchMethodError or LinkageError

Inspect selected runtime version and all paths in dependency tree.

Check plugin-generated/shaded copies and application-server libraries.

Maven downloads from unexpected repository

Inspect effective settings, mirrors, POM repositories, transitive POM repositories according to resolver behavior, and plugin repositories.

Tests missing

Check naming patterns, Surefire/Failsafe versions, JUnit engine, tags, profiles, module selection, and direct phase invocation.

JaCoCo report empty

Check agent argLine, fork settings, plugin execution order, property override, excluded tests, and report phase.

Reactor module not rebuilt

Check -pl, -am, actual dependency edge, module path, installed local artifact, and resume flags.

Reproducible hash differs

Unpack and compare archive entries, timestamps, ordering, manifest, generated files, toolchain, line endings, and dependency/plugin versions.


Architecture patterns

Parent plus BOM separation

Parent defines build policy; BOM defines dependency compatibility.

Layered module graph

Domain → application → adapters → service/distribution, with no reverse framework leakage.

Build once, promote

Immutable Maven artifact becomes the input to image build/promotion.

Enforced dependency platform

BOM + Enforcer + dependency tree report + integration tests.

Clean-room release verification

Release pipeline uses wrapper, pinned JDK/toolchains, isolated repository, reproducible settings, and hash comparison.

Published-POM verification

Consumer sample resolves released artifact from repository and compiles/runs.


Anti-patterns

  • mvn clean package as universal release command;
  • unpinned plugins;
  • dependencies declared in parent for convenience;
  • dozens of overlapping BOMs;
  • arbitrary repositories in POM;
  • using system scope;
  • dynamic/range versions;
  • release with snapshots;
  • environment-specific Maven profiles;
  • secrets in POM/filtering;
  • local JAR committed to repository;
  • direct use of transitive dependencies;
  • blanket exclusions without tests;
  • dependency convergence ignored;
  • fat JAR without service/resource merge;
  • Shade minimization in reflection-heavy app without tests;
  • Maven runtime JDK assumed equal target JDK;
  • CI uses system Maven instead of wrapper without policy;
  • calling Failsafe integration-test without verify;
  • same release version overwritten;
  • rebuilding artifact for each environment;
  • trusting .m2 cache as source of truth;
  • adopting Maven 4 preview silently.

PR review checklist

Model and modules

  • Coordinates/version/packaging correct?
  • Aggregation and inheritance distinguished?
  • Parent contains policy, not accidental runtime deps?
  • BOM ownership clear?
  • Module graph respects architecture?
  • Effective POM reviewed after parent/profile changes?

Dependencies

  • Direct dependencies declared?
  • Scopes match runtime?
  • Optional/exclusions justified?
  • No snapshot/range for release?
  • Convergence/upper-bound checks?
  • Duplicate-class/split-package risk?
  • API/implementation boundary correct?
  • Annotation processors pinned?

Plugins and lifecycle

  • Plugin versions pinned?
  • Execution phase/ID deliberate?
  • pluginManagement versus plugins correct?
  • Thread-safe in parallel build?
  • Surefire/Failsafe discovery correct?
  • verify is CI gate?
  • JaCoCo argLine preserved?
  • Toolchain and compiler release explicit?

Packaging

  • JAR/WAR/thin/fat choice justified?
  • ServiceLoader/resources merged?
  • Provided libraries align with runtime?
  • Packaged artifact smoke tested?
  • Manifest/build metadata deterministic?
  • Published POM usable?

Repositories and supply chain

  • Wrapper/version pinned?
  • Mirror/repository manager used?
  • No arbitrary plugin repositories?
  • Credentials only in settings/CI secrets?
  • Release repository immutable?
  • SBOM/vulnerability/license policy?
  • Reproducible-build controls?
  • CI cache treated as acceleration only?

Release

  • Build once/promote same bytes?
  • Source commit ↔ artifact hash traceable?
  • No mutable snapshot inputs?
  • Signature/checksum/provenance retained?
  • Maven 4 compatibility tested separately?

Trade-off yang harus dipahami senior engineer

DecisionBenefitCost/risk
Shared parentconsistent policyinheritance coupling
Separate BOMreusable version alignmentmore release coordination
Direct versions per moduleexplicit local controldrift
Imported platform BOMtested ecosystemoverride complexity
Thin JARlayer/classpath clarityruntime assembly
Fat JARsingle artifactcollisions/layer churn
WARcontainer-managed platformserver coupling
Provided APIno duplicate platformmissing standalone runtime
Shade relocationconflict isolationreflection/metadata breakage
Reactor parallelismbuild speedplugin/resource races
More test forksisolation/parallelismmemory/process cost
Wrapperconsistent Mavenwrapper supply-chain maintenance
mvndlocal speeddaemon-state differences
Snapshot dependencyfast integrationmutable/non-reproducible
Immutable releaseauditabilitydisciplined promotion
Repository mirrorpolicy/controlcentral dependency
CI local-repo cachefaster downloadsstale/poison risk
Strict Enforcerearly consistencyexception governance
Reproducible buildtamper verificationplugin/environment work
Maven 4 early adoptionfuture capabilitiespreview/plugin risk

Internal verification checklist

Maven and JDK

  • Maven stable version in developers/CI.
  • Maven Wrapper version/distribution/checksum.
  • Maven 4 evaluation status.
  • Maven runtime JDK.
  • compiler/test/Javadoc toolchains.
  • locale/timezone/encoding.
  • mvnd usage.

Project model

  • Root aggregator.
  • Parent POMs.
  • BOMs and import order.
  • Module graph.
  • Profile inventory and activation.
  • CI-friendly version variables.
  • effective-POM review process.

Dependencies

  • Jakarta API/platform versions.
  • Jersey and server modules.
  • dependency-convergence rules.
  • banned dependencies/groups.
  • optional/exclusion policy.
  • duplicate-class scanning.
  • annotation processors.
  • vulnerability/license tooling.

Plugins and tests

  • compiler/resources.
  • Surefire/Failsafe.
  • JaCoCo.
  • JAR/WAR/Shade/Assembly.
  • source/Javadoc.
  • Enforcer.
  • SBOM/signing.
  • plugin version policy.
  • reactor parallelism.

Repositories

  • repository manager vendor/topology.
  • mirrors and allowed remotes.
  • releases/snapshots repositories.
  • plugin repositories.
  • checksum/signature policy.
  • credentials/server IDs.
  • local-repository/cache policy.
  • retention/quarantine/audit.

Packaging and release

  • thin JAR/fat JAR/WAR strategy.
  • runtime dependency ownership.
  • manifest/build info.
  • service-resource transformations.
  • release versioning.
  • artifact immutability.
  • staging/promotion.
  • reproducibility verification.
  • source/artifact/image traceability.

Latihan verifikasi

  1. Generate effective POM and explain the origin of every production plugin execution.
  2. Remove a direct dependency currently supplied transitively and observe dependency-analysis/runtime impact.
  3. Introduce two incompatible transitive versions, then resolve using BOM/management and compatibility tests.
  4. Build the same artifact with two clean workspaces and compare byte hashes/archive differences.
  5. Run compile with Maven on one JDK and toolchain on another; prove target-runtime compatibility.
  6. Package a Jersey fat JAR and validate all META-INF/services providers after shading.
  7. Execute package, integration-test, and verify; document differences and cleanup behavior.
  8. Build selected reactor module with and without -am, then identify local-repository masking.
  9. Simulate repository-manager outage and verify offline/cache behavior without accepting stale snapshot silently.
  10. Run a Maven 4 preview compatibility build in a non-release lane and catalog every warning/plugin issue.

Ringkasan

  • Maven builds an effective model before executing lifecycle/plugin goals.
  • Aggregation, inheritance, lifecycle, plugin execution, and reactor are distinct concepts.
  • Pin Maven through wrapper policy and pin production-relevant plugin versions.
  • dependencyManagement and BOMs manage versions but do not add dependencies.
  • Maven dependency mediation is nearest-definition based, not simply highest version.
  • Directly used APIs should be direct dependencies.
  • Scope must reflect who supplies runtime classes.
  • Jakarta/JAX-RS API and Jersey/runtime implementation ownership must remain explicit.
  • Plugin classpath, project classpath, annotation processors, and module path are different.
  • Use compiler --release and Maven toolchains for target-JDK correctness.
  • Surefire runs unit tests; Failsafe completes integration verification through verify.
  • Fat JAR shading requires careful resource merging and reflection-aware testing.
  • Repository mirrors/managers are supply-chain policy boundaries.
  • Snapshots and dynamic versions weaken release reproducibility.
  • Build once and promote the exact same immutable artifact.
  • Reproducibility requires controlled timestamps, ordering, generated metadata, toolchains, and dependencies.
  • Enforcer, SBOM, vulnerability, license, and signature gates turn build policy into executable evidence.
  • CI caches accelerate resolution but are not authoritative inputs.
  • As of July 2026, Maven 3.9.16 is stable while Maven 4.0.0-rc-5 remains preview/not production-safe according to official guidance.
  • Exact internal versions, parent/BOM structure, repository topology, and release process remain Internal verification checklist.

Referensi resmi

Lesson Recap

You just completed lesson 43 in final stretch. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.