MavenSession Object Structure and Usage: The Complete Apache Maven Guide

The MavenSession object is the central immutable state container for every Maven build, encapsulating the execution request, repository system session, project reactor, and thread-local context for multi-threaded builds.

The MavenSession class, located in org.apache.maven.execution within the apache/maven repository, serves as the definitive source of truth throughout the build lifecycle. It ties together user inputs, project models, and repository configurations into a single cohesive object that persists from command-line parsing to build completion. Understanding the MavenSession object structure and usage is essential for writing robust plugins, customizing build logic, and debugging complex multi-module projects.

Core State Components

The MavenSession implementation in impl/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java maintains several critical fields that define the build environment:

  • MavenExecutionRequest – Stores command-line goals, user properties, and repository settings supplied by the CLI
  • MavenExecutionResult – Collects build outcomes, exceptions, and per-project results as execution proceeds
  • RepositorySystemSession – The Aether (Eclipse Aether/Maven Resolver) session handling artifact resolution, mirrors, and proxies
  • Project Reactor StateList<MavenProject> projects, allProjects, and topLevelProject representing the modules being built
  • ProjectDependencyGraph – The calculated dependency relationships between reactor projects
  • Plugin Context MappluginContextsByProjectAndPluginKey storing per-project, per-plugin data that survives across multiple mojo executions
  • Thread-Local Current ProjectThreadLocal<MavenProject> currentProject tracking which module is currently being built on each thread

Session Construction and Initialization

Maven constructs the session through a specific sequence before the reactor builds begin:

  1. Parse Command Line – The CLI builds a DefaultMavenExecutionRequest implementing the MavenExecutionRequest interface
  2. Create Repository SessionMavenSessionBuilderSupplier in impl/maven-impl constructs the Aether RepositorySystemSession
  3. Instantiate Session – The primary constructor stores the request, result, and repository session:
public MavenSession(RepositorySystemSession repositorySystemSession,
                    MavenExecutionRequest request,
                    MavenExecutionResult result)
  1. Populate Projects – After the reactor builds, setProjects(List<MavenProject>) initializes the project list, sets the topLevelProject, and configures the thread-local default
  2. Configure Dependency Graph – Optional mutators set the ProjectDependencyGraph and project map for multi-module navigation

Thread-Local Project Context

The session manages the current project concept through a ThreadLocal variable, enabling parallel builds while maintaining state isolation:

  • getCurrentProject() returns the MavenProject currently being built on the calling thread
  • setCurrentProject(MavenProject project) updates the thread-local for aggregator plugins that switch contexts
  • This mechanism allows thread-safe access to project-specific data without passing project references through every method call

Repository System Integration

The repositorySystemSession field bridges Maven with the underlying Aether resolver:

RepositorySystemSession repoSession = session.getRepositorySession();

This session contains authentication details, mirror configurations, and caching policies. It remains immutable after construction, ensuring consistent artifact resolution throughout the build.

Plugin Context Storage

Plugins persist data across multiple executions using the session's plugin context mechanism:

Map<String, Object> context = session.getPluginContext(pluginDescriptor, project);
context.put("lastExecutionTime", System.currentTimeMillis());

The map is created lazily on first access and stored in pluginContextsByProjectAndPluginKey, surviving for the duration of the build to share state between related mojos.

Code Examples

Injecting MavenSession into a Mojo

Plugins access the session via parameter injection:

@Mojo(name = "report", defaultPhase = LifecyclePhase.VALIDATE)
public class ReportMojo extends AbstractMojo {

    @Parameter(defaultValue = "${session}", readonly = true)
    private MavenSession session;

    public void execute() throws MojoExecutionException {
        MavenProject current = session.getCurrentProject();
        getLog().info("Building: " + current.getArtifactId());
        getLog().info("Goals: " + session.getGoals());
    }
}

Accessing Build Properties

Retrieve user-supplied properties and execution configuration:

Properties userProps = session.getUserProperties();
boolean isOffline = session.isOffline();
File localRepo = session.getLocalRepository().getBasedir();

Storing Cross-Mojo Data

Maintain state between plugin executions:

public void initializeBuild(MavenProject project, PluginDescriptor descriptor) {
    Map<String, Object> ctx = session.getPluginContext(descriptor, project);
    if (!ctx.containsKey("startTime")) {
        ctx.put("startTime", Instant.now());
    }
}

Creating Sessions Programmatically

For standalone tools or testing:

MavenExecutionRequest request = new DefaultMavenExecutionRequest()
    .setGoals(Arrays.asList("clean", "install"))
    .setBaseDirectory(new File("."));

MavenExecutionResult result = new MavenExecutionResult();
MavenSession session = new MavenSession(repoSystemSession, request, result);
session.setProjects(projectList);

Migration to the Maven 4 API

Maven 4 introduces org.apache.maven.api.Session as the modern abstraction. The legacy MavenSession maintains a reference to this new API via the session field, allowing plugins to bridge between legacy code and new API-centric extensions. Core components set this via setSession(Session apiSession) while maintaining backward compatibility with existing plugins that rely on the classic MavenSession API.

Summary

  • MavenSession serves as the immutable container for all build state, located in org.apache.maven.execution
  • Construction follows a strict path: request parsing → repository session creation → constructor invocation → project population
  • Thread-local management enables safe parallel execution by tracking the current project per thread via currentProject
  • Plugin contexts provide durable key-value storage across multiple executions of the same plugin on the same project
  • Repository integration exposes the underlying Aether session through getRepositorySession() for direct artifact manipulation
  • Maven 4 compatibility bridges legacy MavenSession code with the new org.apache.maven.api.Session abstraction

Frequently Asked Questions

How do I access the MavenSession inside a plugin?

Inject the session using the @Parameter annotation with defaultValue="${session}". This provides read-only access to the build state, including the current project, user properties, and repository configuration. Access the current project via session.getCurrentProject() to determine which module is being built.

What is stored in the MavenSession thread-local current project?

The currentProject ThreadLocal stores a reference to the MavenProject actively being built on the specific execution thread. During parallel builds, each thread maintains its own current project reference, allowing thread-safe access to project-specific data without synchronization overhead.

How does MavenSession differ from MavenProject?

MavenSession represents the global build state spanning all modules and the entire execution lifecycle, while MavenProject represents a single module's model (POM, dependencies, build configuration). The session contains the list of all projects and tracks which one is currently being built via the thread-local context.

Can I modify the MavenSession during a build?

Most MavenSession fields are immutable after construction, but the plugin context map and current project thread-local are mutable. Plugins can store data in getPluginContext() and change the current project via setCurrentProject(), but should not attempt to modify the execution request or repository session after initialization.

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 →