Exploring the Source Code of Maven API Modules: Architecture and Implementation

The Maven API modules provide a thin, service-oriented facade centered on the Session interface, delegating implementation details to discoverable SPI services while handling XML parsing, dependency graphs, and artifact resolution through dedicated sub-modules.

The Maven API modules in the apache/maven repository organize Maven’s core functionality into a set of cohesive, programmatic interfaces. These modules expose only API surfaces—such as Session, Project, and Artifact—while the actual implementation logic resides in Maven’s internal runtime, discovered dynamically via Java’s ServiceLoader mechanism.

Maven API Module Architecture Overview

The Maven API is deliberately partitioned into five distinct layers that separate concerns between model definition, extension points, and XML processing.

Core Module

The Core module (maven-api-core) defines the central abstractions including Session, Project, Artifact, and Repository. Located in api/maven-api-core/src/main/java/org/apache/maven/api/Session.java, this layer provides convenience methods for common operations like artifact resolution and dependency collection. The Session interface acts as the primary façade, bundling build state and service access.

SPI Module

The SPI module (maven-api-spi) supplies extension points and service contracts. Defined in api/maven-api-spi/src/main/java/org/apache/maven/api/spi/SpiService.java, this layer specifies interfaces for ArtifactResolver, DependencyResolver, and VersionResolver. Implementations reside in org.apache.maven.internal.* packages and are loaded at runtime via ServiceLoader.

XML Module

The XML module (maven-api-xml) handles Maven-specific XML processing through a streaming-based parser. The XmlService class, found in api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java, provides read(), write(), and merge() operations. It supports Maven’s combine semantics via constants like CHILDREN_COMBINATION_MERGE, combine.children, and combine.self.

Model Module

The Model module provides POJO representations of the Maven model (POMs and settings) alongside XPP3 serialization helpers. The merge logic for these objects is implemented in compat/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java, which handles deep merging of model objects according to Maven’s inheritance rules.

Builder-Support Module

The Builder-Support module contains utilities for handling problems and filesystem artifacts during the build process. The DefaultProblemCollector class in compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblemCollector.java provides centralized error collection and reporting for model building and validation.

The Session Interface: Central Entry Point

The Session interface represents a running Maven build and serves as the primary entry point for all API operations. In api/maven-api-core/src/main/java/org/apache/maven/api/Session.java, the interface exposes:

  • Build environment: getMavenVersion(), getSettings(), getLocalRepository(), getRemoteRepositories()
  • Project state: getProjects() returns the list of projects in the current session
  • Service lookup: getService(Class<T>) retrieves SPI implementations dynamically
  • Convenience shortcuts: createArtifactCoordinates(), resolveArtifact(), collectDependencies()

All higher-level operations delegate to the appropriate service obtained through the session, ensuring plugin code remains decoupled from internal implementations.

Service Provider Interfaces and Extension Points

The SPI layer defines contracts for extending Maven’s behavior without modifying the core. Key service interfaces include:

  • ArtifactResolver: Locates and downloads artifacts from repositories
  • DependencyResolver: Constructs dependency graphs and flattens them for specific scopes
  • VersionResolver: Handles SNAPSHOT versions and version range resolution
  • RepositoryFactory: Creates local and remote repository objects
  • TypeRegistry / LanguageRegistry / PackagingRegistry: Resolve Maven-specific enums from string identifiers

Implementations are discovered through XmlService.getService() or session.getService(), which internally use Java’s ServiceLoader to find providers on the classpath.

XML Processing and Model Merge Semantics

The XmlService provides a streaming-first approach to handling Maven’s XML structures (POMs, settings, toolchains).

Streaming XML Operations

The service supports three primary operations:

  • read(InputStream): Parses XML into an XmlNode tree
  • write(XmlNode, Writer): Serializes nodes back to XML
  • merge(XmlNode dominant, XmlNode recessive): Combines two XML trees according to Maven’s combine rules

Merge Behavior

When merging XML documents (such as overlaying user settings onto defaults), XmlService respects attributes defined in XmlService.java:

  • combine.children with values like CHILDREN_COMBINATION_MERGE
  • combine.self for self-merge behavior
  • combine.id and combine.keys for identifying merge targets

Dependency Resolution Workflow

Resolving dependencies through the Maven API modules follows a consistent pattern:

  1. Obtain a Session instance from the Maven launcher or test harness
  2. Retrieve the resolver via session.getService(DependencyResolver.class)
  3. Collect the graph using session.collectDependencies(artifact, PathScope.compile()), which returns a Node tree
  4. Flatten or resolve paths with session.resolveDependencies(root, PathScope.compile()) to obtain a list of filesystem paths
  5. Optional version resolution for ranges and SNAPSHOTs via session.resolveVersion(coords)

The Node class represents a lightweight tree node storing an Artifact and its children, produced by DependencyResolver.collect() and traversed for flattening.

Practical Code Examples

Resolving a SNAPSHOT Artifact

Session session = ...; // obtained from Maven launcher
ArtifactCoordinates coords = session.createArtifactCoordinates(
        "org.apache.maven", "maven-core", "3.9.0-SNAPSHOT", "jar");
DownloadedArtifact artifact = session.resolveArtifact(coords);
System.out.println("Resolved to: " + artifact.getPath());

Source: Session.resolveArtifact in api/maven-api-core/src/main/java/org/apache/maven/api/Session.java

Building a Project Programmatically

Session session = ...;
Project project = session.getService(ProjectFactory.class)
        .create("com.example", "demo", "1.0.0", session.getLocalRepository());
project.addDependency(
        session.createArtifactCoordinates("org.apache.commons", "commons-lang3", "3.14.0", "jar"));
session.registerListener(event -> System.out.println("Event: " + event.type()));

Source: ProjectFactory SPI and Project API in api/maven-api-core/src/main/java/org/apache/maven/api/Project.java

Merging Maven Settings Files

XmlService xml = XmlService.getService();
XmlNode defaultSettings = xml.read(new FileInputStream("settings.xml"), null);
XmlNode userSettings = xml.read(new FileInputStream("user-settings.xml"), null);
XmlNode merged = xml.merge(userSettings, defaultSettings); // user overrides defaults
xml.write(merged, new FileWriter("merged-settings.xml"));

Source: XmlService.merge in api/maven-api-xml/src/main/java/org/apache/maven/api/xml/XmlService.java

Summary

  • The Maven API modules are organized into five layers: Core, SPI, XML, Model, and Builder-Support, each with distinct responsibilities
  • Session in api/maven-api-core/src/main/java/org/apache/maven/api/Session.java serves as the central façade, providing access to settings, repositories, and generic service lookup
  • Service discovery occurs via Java’s ServiceLoader, allowing the API to remain thin while implementations reside in org.apache.maven.internal.* packages
  • XML processing uses XmlService for streaming parse, write, and merge operations following Maven’s combine semantics (e.g., CHILDREN_COMBINATION_MERGE)
  • Dependency resolution leverages DependencyResolver and Node objects to construct and flatten dependency graphs without binding to specific implementation classes

Frequently Asked Questions

What is the primary entry point class in the Maven API modules?

The Session interface, located in api/maven-api-core/src/main/java/org/apache/maven/api/Session.java, serves as the primary entry point. It provides access to the Maven version, settings, local and remote repositories, and acts as a factory for retrieving SPI implementations via the getService(Class<T>) method.

How does Maven discover implementations for the SPI interfaces?

Maven uses Java’s ServiceLoader mechanism to discover implementations at runtime. While the API modules define interfaces like ArtifactResolver and DependencyResolver in the SPI package, the concrete implementations reside in org.apache.maven.internal.* packages and are loaded dynamically when requested through the Session or XmlService.getService().

What are the merge semantics used by XmlService when combining XML documents?

The XmlService.merge() method follows Maven’s specific combine rules, using attributes like combine.children, combine.self, combine.id, and combine.keys defined as constants in XmlService.java (e.g., CHILDREN_COMBINATION_MERGE). These rules determine how dominant and recessive XML trees combine, with user settings typically overriding defaults while preserving structural integrity.

Which module handles POJO representation of the POM structure?

The Model module provides the POJO representation of the Maven model, including POMs and settings files. While the API defines the interfaces, the concrete implementation and merge logic reside in compat/maven-model/src/main/java/org/apache/maven/model/merge/ModelMerger.java, which handles the deep merging of model objects according to Maven’s inheritance rules.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →