Maven Core Components and Their Functions: A Deep Dive into Apache Maven's Architecture

Apache Maven's build engine is orchestrated by a tightly-coupled set of Java components including MavenCli, Maven, MavenSession, MavenProject, ProjectBuilder, and PluginManager that together handle command-line parsing, project resolution, dependency graph construction, and plugin execution.

Understanding Maven core components and their functions is essential for extending Maven, writing custom plugins, or embedding the build engine in other tools. The apache/maven repository organizes these components into a deterministic architecture centered around immutable execution requests and session-based state management.

The Command-Line Interface: MavenCli and MavenExecutionRequest

Parsing CLI Options

The MavenCli class serves as the entry point for the mvn command. Located in compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java, this component parses command-line arguments, configures logging, and prepares the environment for the build. It handles options such as offline mode, custom properties, and specified goals before delegating to the core engine.

Building the Execution Request

MavenExecutionRequest is an immutable-ish holder for all user-supplied configuration. Defined in impl/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java, this interface captures goals, system properties, repository settings, and profile activation. The CLI builds this request object once and passes it to the Maven engine, ensuring reproducible builds through stateless configuration.

The Build Engine: Maven and MavenSession

Maven as the Central Orchestrator

The Maven component (implementation in impl/maven-core) acts as the central orchestrator that coordinates the entire build lifecycle. It receives the MavenExecutionRequest, bootstraps the container, and drives the reactor. According to the source code, this component creates the MavenSession and manages the transition from request to execution.

MavenSession as Runtime Context

MavenSession provides the runtime context for a single Maven build. Found in impl/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java, this class stores the execution request, the result object, the list of projects, the current project, and the repository session. It maintains a ThreadLocal<MavenProject> to safely track the current project during parallel builds, allowing plugins to call session.getCurrentProject() without race conditions.

Project Resolution: MavenProject and ProjectBuilder

From POM XML to MavenProject

MavenProject represents the in-memory model of a single POM. Defined in impl/maven-core/src/main/java/org/apache/maven/project/MavenProject.java, it contains project coordinates, build plugins, dependencies, reporting configuration, and the resolved artifact. This object is the primary data structure that plugins interact with during the build.

Handling Inheritance and Profiles

The ProjectBuilder interface, implemented by DefaultProjectBuilder in impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java, transforms raw POM files into MavenProject instances. This component handles XML parsing, parent POM inheritance, property interpolation, profile activation, and validation. It ensures that multi-module project hierarchies are correctly resolved before the reactor sorts the build order.

Dependency and Repository Management

ProjectDependencyGraph

After project building, Maven constructs a ProjectDependencyGraph (defined in impl/maven-core/src/main/java/org/apache/maven/execution/ProjectDependencyGraph.java). This directed graph represents inter-project dependencies and enables topological sorting of the reactor. It supports incremental builds by determining which projects depend on changed artifacts.

RepositorySystemSession

Maven delegates artifact resolution to Aether through the RepositorySystemSession. Exposed via MavenSession#getRepositorySession(), this session stores repository-wide settings including mirrors, proxies, authentication, and cache configuration. It provides the bridge between Maven's project model and the underlying artifact transport layer.

Plugin Lifecycle Management

InternalPluginManager

The InternalPluginManager (located in impl/maven-core/src/main/java/org/apache/maven/plugin/InternalPluginManager.java) resolves, loads, and executes plugins for each project. It maintains a per-project, per-plugin context map called pluginContextsByProjectAndPluginKey within the MavenSession, ensuring that each plugin instance receives isolated state and preventing cross-contamination in multi-module builds.

EventDispatcher and TransferListener

EventDispatcher publishes Maven-specific events to registered listeners and the build log. Located in impl/maven-core/src/main/java/org/apache/maven/monitor/event/EventDispatcher.java, it handles lifecycle notifications. The TransferListener tracks artifact download/upload progress, with implementations like ConsoleMavenTransferListener and BatchModeMavenTransferListener wired through the CLI to provide user feedback during network operations.

How Maven Core Components Work Together

The interaction between these components follows a strict lifecycle:

  1. CLI → RequestMavenCli parses arguments and builds a MavenExecutionRequest.
  2. Engine Start – The Maven component receives the request and creates a new MavenSession.
  3. Project BuildingMaven invokes ProjectBuilder to read the top-level POM and modules, producing MavenProject objects.
  4. Reactor Sorting – Projects are topologically sorted into a ProjectDependencyGraph and stored in the session.
  5. Plugin ExecutionInternalPluginManager resolves plugins and executes mojos according to the lifecycle phase.
  6. Result CollectionMavenExecutionResult aggregates exceptions and the final project list.
  7. Shutdown – The session closes, resources release, and the CLI exits with the appropriate status code.

Key Design Patterns in Maven Core Architecture

  • Stateless Request PatternMavenExecutionRequest is built once and never mutated during the build, ensuring deterministic behavior.
  • Thread-Local Current ProjectMavenSession uses ThreadLocal<MavenProject> to support parallel builds while allowing plugins to safely query the current project.
  • Plugin Context Isolation – The pluginContextsByProjectAndPluginKey map guarantees isolated state for each plugin instance per project.
  • Extensibility via SPI – Core services are looked up from the Plexus container, allowing custom implementations of ProjectBuilder and other components.

Programmatic Usage Example

Below is a practical example demonstrating how to interact with Maven core components programmatically:

// 1️⃣ Create an execution request (normally done by MavenCli)
MavenExecutionRequest request = new DefaultMavenExecutionRequest()
        .setBaseDirectory(new File("."))
        .setGoals(Collections.singletonList("install"))
        .setUserProperties(System.getProperties())
        .setOffline(false);

// 2️⃣ Build a Maven session
MavenSession session = new MavenSession(
        plexusContainer,
        request,
        new DefaultMavenExecutionResult(),
        repositorySystemSession);

// 3️⃣ Access the list of projects after building (requires ProjectBuilder)
ProjectBuilder builder = plexusContainer.lookup(ProjectBuilder.class);
ProjectBuildingResult buildResult = builder.build(
        new File("pom.xml"),
        request.getProjectBuildingRequest());

List<MavenProject> projects = buildResult.getProjects();
session.setProjects(projects);

// 4️⃣ Retrieve the current project (used inside a Mojo)
MavenProject current = session.getCurrentProject();
System.out.println("Building " + current.getArtifactId());

// 5️⃣ Resolve a plugin and execute a mojo manually
PluginManager pluginManager = plexusContainer.lookup(PluginManager.class);
PluginDescriptor descriptor = pluginManager.getPluginDescriptor(
        "org.apache.maven.plugins:maven-compiler-plugin", session);
Mojo mojo = pluginManager.getMojo(descriptor, "compile");
mojo.execute();   // In real code you would also set the MojoExecutionContext

Summary

  • MavenCli in compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java serves as the entry point, parsing command-line arguments into a MavenExecutionRequest.
  • Maven and MavenSession (in impl/maven-core) orchestrate the build and maintain runtime state, including thread-local current project tracking.
  • ProjectBuilder and MavenProject handle POM parsing, inheritance resolution, and in-memory project representation.
  • ProjectDependencyGraph enables topological sorting of multi-module builds, while RepositorySystemSession manages artifact resolution.
  • InternalPluginManager executes plugins with isolated contexts via pluginContextsByProjectAndPluginKey in the session.
  • The architecture emphasizes immutable requests, thread safety, and extensibility through the Plexus container.

Frequently Asked Questions

What is the difference between MavenSession and MavenExecutionRequest?

MavenExecutionRequest is an immutable configuration object created at startup that holds user-supplied settings like goals, properties, and repository configurations. MavenSession is the runtime context that lives for the duration of the build, storing mutable state such as the current project, plugin contexts, and the execution result. While the request defines what to build, the session tracks how the build progresses.

How does Maven handle multi-module project builds?

Maven uses the ProjectBuilder to read all module POMs, then constructs a ProjectDependencyGraph to topologically sort projects based on inter-module dependencies. This graph, stored in MavenSession, determines the reactor build order and enables parallel execution while respecting dependency constraints.

Where does Maven store plugin-specific state during a build?

Plugin state is stored in the pluginContextsByProjectAndPluginKey map within MavenSession. This map ensures that each plugin receives an isolated context per project, preventing state leakage between different modules or plugin instances during multi-module builds.

Can Maven be embedded in other Java applications?

Yes, Maven is designed for embedding. By using the MavenCli or directly instantiating MavenExecutionRequest and MavenSession through the Plexus container, developers can trigger builds programmatically. The compat/maven-embedder module provides the necessary infrastructure to bootstrap the Maven engine within other applications.

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 →