# How Maven's Compatibility Layer Bridges Maven 2 and Maven 3 APIs

> Understand how Maven's compatibility layer in `maven-compat` makes Maven 2 APIs work with Maven 3+ architecture without code duplication. Learn about seamless migration strategies.

- Repository: [The Apache Software Foundation/maven](https://github.com/apache/maven)
- Tags: internals
- Published: 2026-07-07

---

**Apache Maven's compatibility layer, contained in the `maven-compat` module, preserves deprecated Maven 2 APIs by adapting them to modern Maven 3+ component-based architecture without duplicating core logic.**

The Apache Maven project maintains strict backward compatibility for plugins and extensions written against legacy APIs. While Maven 3+ introduced a cleaner, component-based model centered around `ProjectBuilder` and `RepositorySystem`, thousands of existing plugins still rely on the historic `MavenProjectBuilder` interface. This article examines how the compatibility layer in the `apache/maven` repository translates these legacy calls to the modern engine.

## What Is Maven's Compatibility Layer?

Maven's compatibility layer is a dedicated module located at `compat/maven-compat/` that houses deprecated interfaces and adapter implementations. Rather than re-implementing build logic, this layer acts as a **translation bridge**: it accepts calls using Maven 2-style signatures, transforms the request objects into modern equivalents, delegates to the current core components, and returns results wrapped in legacy types.

The module is declared in [`compat/maven-compat/pom.xml`](https://github.com/apache/maven/blob/main/compat/maven-compat/pom.xml) and explicitly marked as providing deprecated Maven 2 classes. This approach ensures that bug fixes and performance improvements in the modern builder automatically benefit legacy callers.

## How the Compatibility Layer Works: The Adapter Pattern

The core mechanism follows the adapter pattern, implemented primarily in `DefaultMavenProjectBuilder`. When legacy code invokes the deprecated `MavenProjectBuilder` interface, the following translation occurs:

### Step 1: Legacy Interface Calls

Legacy plugins invoke methods on the `MavenProjectBuilder` interface, located at [`compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java`](https://github.com/apache/maven/blob/main/compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java). This interface remains available for backward compatibility but delegates all work to adapter implementations.

### Step 2: Adapter Creation and Injection

Maven constructs a `DefaultMavenProjectBuilder` instance (annotated with `@Named @Singleton`) via dependency injection. This adapter is defined at [`compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java`](https://github.com/apache/maven/blob/main/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java) and receives injections of the modern `ProjectBuilder`, `RepositorySystem`, and `LegacySupport` components.

### Step 3: Request Building and Session Injection

The adapter converts the legacy `ProjectBuilderConfiguration` into a modern `ProjectBuildingRequest` using the `toRequest()` method. It then calls `injectSession()` to enrich the request with data from the current `MavenSession` (retrieved via `LegacySupport`), including repository sessions, system properties, and remote repositories. This mirrors the behavior of a CLI run.

### Step 4: Repository Normalization

Old APIs sometimes pass `org.apache.maven.model.Repository` objects. The adapter converts these to `ArtifactRepository` instances using `RepositorySystem.buildArtifactRepository()`, then applies mirrors, proxies, and authentication configurations via the `normalizeToArtifactRepositories()` method.

### Step 5: Delegation to Modern APIs

After preparing the request, the adapter delegates to the modern `ProjectBuilder.build()` method defined in [`maven-builder-support/src/main/java/org/apache/maven/building/ProjectBuilder.java`](https://github.com/apache/maven/blob/main/maven-builder-support/src/main/java/org/apache/maven/building/ProjectBuilder.java). The resulting `ProjectBuildingResult` is unwrapped to extract the `MavenProject` for return to the legacy caller.

### Step 6: Error Translation

If `ProjectBuilder` throws a `ProjectBuildingException` caused by a `ModelBuildingException`, the adapter's `transformError()` method wraps it in the historic `InvalidProjectModelException`. This preserves the old exception hierarchy expected by legacy error handling code.

## Key Components and Source Code Locations

| Component | Location | Purpose |
|-----------|----------|---------|
| **Legacy API** | [`compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java`](https://github.com/apache/maven/blob/main/compat/maven-compat/src/main/java/org/apache/maven/project/MavenProjectBuilder.java) | Deprecated interface for building Maven projects |
| **Adapter Implementation** | [`compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java`](https://github.com/apache/maven/blob/main/compat/maven-compat/src/main/java/org/apache/maven/project/DefaultMavenProjectBuilder.java) | Translates legacy calls to modern `ProjectBuilder` |
| **Module Descriptor** | [`compat/maven-compat/pom.xml`](https://github.com/apache/maven/blob/main/compat/maven-compat/pom.xml) | Declares the deprecated nature of the module |
| **Modern Builder** | [`maven-builder-support/src/main/java/org/apache/maven/building/ProjectBuilder.java`](https://github.com/apache/maven/blob/main/maven-builder-support/src/main/java/org/apache/maven/building/ProjectBuilder.java) | Core API for model building and validation |
| **Session Provider** | [`maven-embedder/src/main/java/org/apache/maven/cli/LegacySupport.java`](https://github.com/apache/maven/blob/main/maven-embedder/src/main/java/org/apache/maven/cli/LegacySupport.java) | Provides `MavenSession` access for adapters |

## Practical Example: Calling the Legacy API

This example demonstrates how legacy code consumes the deprecated API while the compatibility layer handles the translation internally:

```java
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilderConfiguration;
import org.apache.maven.project.DefaultProjectBuilderConfiguration;
import java.io.File;

public class LegacyBuilderDemo {
    public static void main(String[] args) throws Exception {
        // Plexus container resolves the @Named bean to DefaultMavenProjectBuilder
        MavenProjectBuilder builder = container.lookup(MavenProjectBuilder.class);
        
        ProjectBuilderConfiguration cfg = new DefaultProjectBuilderConfiguration();
        cfg.setLocalRepository(localRepo);  // old ArtifactRepository
        cfg.setUserProperties(System.getProperties());
        
        // This call routes through DefaultMavenProjectBuilder to the modern ProjectBuilder
        MavenProject project = builder.build(new File("pom.xml"), cfg);
        
        System.out.println("Project artifactId: " + project.getArtifactId());
    }
}

```

## Inside the Adapter: How Translation Works

The `DefaultMavenProjectBuilder` performs the actual translation using these key operations:

```java
// Convert legacy configuration to modern request
ProjectBuildingRequest req = toRequest(configuration);

// Inject current session data (repositories, properties)
req = injectSession(req);

// Normalize repository definitions for the new API
req.setRemoteRepositories(normalizeToArtifactRepositories(remoteRepos));

// Delegate to modern builder and unwrap result
MavenProject project = projectBuilder.build(pomFile, req).getProject();

```

This snippet illustrates how the adapter preserves the old method signatures while internally utilizing the streamlined Maven 3+ building engine.

## Why Maven Maintains This Layer

Maven's compatibility layer exists for three critical reasons:

- **Plugin Ecosystem Preservation**: Thousands of third-party plugins written against Maven 2 APIs continue to function without modification.
- **Internal Dependencies**: Some internal Maven components, such as the site plugin, still reference the deprecated classes.
- **Single Source of Truth**: By delegating to the modern `ProjectBuilder`, the layer avoids code duplication and ensures that performance improvements and bug fixes in the core automatically propagate to legacy callers.

## Summary

- **Maven's compatibility layer** resides in `compat/maven-compat/` and preserves deprecated Maven 2 APIs through adapter classes.
- The **adapter pattern** translates legacy `MavenProjectBuilder` calls to modern `ProjectBuilder` invocations without re-implementing build logic.
- Key translation steps include **request building** (`toRequest()`), **session injection** (`injectSession()`), and **repository normalization** (`normalizeToArtifactRepositories()`).
- **Error translation** maintains backward compatibility by wrapping modern exceptions in legacy types like `InvalidProjectModelException`.
- This approach ensures the **entire plugin ecosystem** remains functional while Maven core evolves.

## Frequently Asked Questions

### Where is Maven's compatibility layer located in the source code?

The compatibility layer is located in the `compat/maven-compat/` directory within the Apache Maven repository. The module contains deprecated interfaces and adapter implementations that bridge Maven 2-style APIs to Maven 3+ components.

### How does the compatibility layer handle repository configurations differently between Maven 2 and Maven 3?

The layer converts `org.apache.maven.model.Repository` objects to `ArtifactRepository` instances using `RepositorySystem.buildArtifactRepository()`. The `normalizeToArtifactRepositories()` method in `DefaultMavenProjectBuilder` applies mirrors, proxies, and authentication settings to ensure the modern repository system receives properly configured requests.

### Why doesn't Maven simply remove the deprecated MavenProjectBuilder interface?

Removing the interface would break binary compatibility for existing plugins. Many third-party plugins and some internal Maven components still depend on these APIs. The adapter approach allows Maven to maintain backward compatibility while consolidating all building logic in the modern `ProjectBuilder` implementation.

### What happens if the modern ProjectBuilder throws an exception?

The adapter catches `ProjectBuildingException` and examines its cause. If the root cause is a `ModelBuildingException`, the `transformError()` method wraps it in an `InvalidProjectModelException`. This preserves the exception hierarchy expected by legacy code while still conveying the underlying error details.