Skip to content

Measure the impact of porting Maven components and plugins to the Maven 4 API #13302

Description

@slachiewicz

Description

Tracks porting the Apache Maven components and plugins from the Maven 3 API (MavenProject, org.apache.maven.artifact, maven-compat, Sisu and Plexus components) to the Maven 4 API at 4.0.0-rc-7. This is an impact study, not a migration: it measures what ports cleanly, where the Maven 4 API has no equivalent, and which components are better kept on Maven 3, or retired, than ported.

The work is organised as follows:

  • Each component gets a branch agent/mvn4-api: one commit switches CI to Maven 4 only (maven4-build: true, version from the maven-gh-actions-shared@v5 default), the next ports the code. Where a PR is open, it is a draft labelled not-for-merge; the tables below link it.
  • Findings are kept in comments below, edited in place: one per shared component and Doxia component, and five grouped comments with a collapsed section per component for the rest.
  • A component whose PR is labelled wontfix has its PR closed and is marked "completed: migration not needed".
  • Gaps feed issues against apache/maven and fix PRs in the estate, listed under "Filed from this work".
  • On 2026-10-06 every agent/mvn4-api branch was rebased onto its default branch. A component's findings give its measurements from before the rebase, unless they include a rebase note.

Status

Needs migration Ported Partial Blocked In progress No migration needed Already on the Maven 4 API
36 16 18 2 0 16 12

Closed as wontfix, migration to the Maven 4 API not needed: maven-build-cache-extension, maven-common-artifact-filters, maven-dependency-analyzer, maven-dependency-tree, maven-doxia, maven-doxia-converter, maven-doxia-sitetools, maven-file-management, maven-fluido-skin, maven-invoker, maven-jarsigner, maven-mapping, maven-reporting-api, maven-reporting-exec, maven-reporting-impl, maven-script-interpreter, maven-shared-jar, maven-shared-resources, maven-shared-utils.

Gaps found so far (rc-7)

Each row names the components where the gap was hit; details are in their findings comments below.

In the Maven 4 API: needs apache/maven

Gap Hit in
No public Session outside a Maven runtime: the only route is ApiRunner in maven-impl (internal), with no transporter and no way to supply a RepositorySystem or ModelResolver resolver-ant-tasks, test fixtures everywhere
No SPI to skip or replace a mojo execution, and no lifecycle participant; Listener only observes build-cache-extension
No plugin manager, forked-lifecycle API or mojo-parameter expression evaluator: nothing resolves, configures or runs another plugin's mojo, so a Maven 4 site plugin cannot run reports (confirmed from inside a plugin realm) build-cache-extension, plugin-tools, site
Mojo-descriptor model lacks threadSafe, instantiationStrategy, executionStrategy, requiresReports and component requirements, so it cannot carry a Maven 3 plugin.xml plugin-tools, jdeps
PackagingProvider not honoured: packaging lookup still goes through the Sisu LifecycleMapping (#13306) acr, rar, jmod
No MavenExecutionRequest data on Session: -e, -ff/-fae/-fn, requested (-P) profile ids, settings and toolchains file locations (-s, -gs, -t, -gt) surefire, enforcer, javadoc
No original (pre-interpolation) model on Project, and no version resolution for plugins, report plugins and extensions (RELEASE/LATEST) release, enforcer
No settings encryption or decryption API: a plugin cannot decrypt a {...} value it reads itself, nor re-encrypt passwords it writes into a settings.xml for a forked build release, gpg
Resolution: no metadata resolution; RemoteRepository has no policies; no pre-managed version, declared version constraint, conflict or trail data and no parent link on Node; no way to configure conflict resolution or scope selection for a verbose collection; Project has no getArtifacts() or project references archetype, resolver-ant-tasks, dependency-tree, dependency, enforcer, plugin-tools, surefire
No factory for Dependency, Type or Version outside a Session, and no public test fixtures dependency-tree, mapping, plugin-tools, surefire
Toolchains: no service turns a ToolchainModel into a Toolchain inside a plugin, and a ToolchainFactory contributed by an extension is found only through the Sisu index toolchains
JavaPathType classifies each dependency alone: no main-module-aware module-path/class-path split (addOutputDirectory is documented as not called) jmod
A plugin cannot replace the project's POM file: Project has no setter, and the consumer POM is generated from the model shade
DependencyCoordinatesFactory.create(Session, model.Dependency) drops scope, optional and exclusions shade
Local repository: Session.withLocalRepository inherits the build's resolver configuration and cannot override the split layout; Project.getParent() returns reactor parents only; no settings merge; no view of the plugin's own artifacts invoker
Session.resolveArtifact no longer fetches the POM or follows relocation; resolved dependencies come level by level rather than in tree order dependency
Repository access: ProjectBuilder cannot build a project from coordinates (a Source request returns empty with no problem reported); Transport has no existence check; no remote-layout service doap
Resolution ignores the includesDependencies of war, ear and rar types, and lists an artifact reached by several paths only under its nearest parent; no timestamped snapshot version on a resolved artifact; no session start time ear
No ArtifactHandler equivalent mapping (file-name tokens), surefire
Expressions not evaluated: ${plugin.artifactMap}, ${project.artifactMap}, ${plugin}, ${basedir}, ${session.parallel}, ${project.name}-style defaults surefire, scm-publish, changelog
No path from Session/Project to MavenProject or RepositorySystemSession for code that still needs them reporting-exec, reporting-impl, changelog
Mojo configuration: an XmlNode parameter drops XML attributes, also hit in checkstyle and ear (#13305); PlexusConfiguration parameters are not supported at all; MojoExecution.getPlugin().getDependencies() throws when the plugin realm comes from the cache (#13307) antrun
A model without <name> gives a null name where Maven 3 fell back to the artifactId site (fix belongs in the doxia-sitetools port)
Immutable model: configuration cannot populate v4 model types; builders do not clear a value set to null; the strict reader rejects <reports> and plugin <goals> surefire, archetype
Maven 4 DI is invisible to Sisu, cannot inject an empty List, requires a value on @Named (a bare @Named is the empty key, not default), and cannot inject Sisu or Plexus components (#13326) every port; scm-publish, changelog (ScmManager), plugin-tools (ArchiverManager, VelocityComponent), surefire (LocationManager)

In estate tooling and libraries: fixable by PR

Gap Hit in Fix
maven-plugin-tools writes no <resolutions>, so @Resolution fields stay null (a port that relies on it silently resolves nothing); mojo constructor injection unsupported (apache/maven-plugin-tools#1229); dependencyResolutionPathScopes not extracted; @Mojo(dependencyCollection = true) and @Execute(phase = "none") crash the generator jdeps, acr, ejb, rar, dependency, javadoc plugin-tools
ScmManager obtainable only through Sisu scm-publish, changelog, release none once #13326 lands: the port injects it through javax.inject; apache/maven-scm#1418 closed
Archivers from plexus-archiver's ServiceLoaderArchiverManager throw a NullPointerException when unpacking, because AbstractArchiver gets its ArchiverManager only by injection; plexus-archiver 4.x has no Sisu-free ArchiverManager at all assembly, dependency codehaus-plexus/plexus-archiver#500, merged, not yet released (issue codehaus-plexus/plexus-archiver#499)
Legacy BuildContext (org.sonatype.plexus:plexus-build-api 0.0.7) forces org.eclipse.sisu.plexus at compile scope resources (master), acr, ejb apache/maven-filtering#363, waiting on #12576
Dependency-reduced POM rejected by Maven 4 doxia-converter, any shaded build apache/maven-shade-plugin#813
<proc>none</proc> in the maven-shared-components parent: the Maven 4 DI index is written by hand every shared-component port parent POM
Report mojos: maven-site-plugin 3.x runs only Maven 3 mojos changelog, reporting-impl reporting chain, see below
plugin-tools writes <requirements> from @Component fields inherited from AbstractMavenReport into a 2.0.0 plugin.xml, which rc-7 rejects for every goal of the plugin, so report and non-report goals cannot share an artifact pmd plugin-tools, reporting-impl
maven-shared-utils JavaTool/findTool calls the toolchain reflectively; the v4 toolchain class is not public, so it warns and silently runs the Maven JDK jarsigner (fixed with an adapter); likely jmod, jlink, jdeps, jdeprscan maven-shared-utils

Findings so far

  • A port is rarely a type swap. The immutable Maven 4 model forced redesigns (maven-release edits the POM as a DOM; maven-shade-plugin can no longer publish its dependency-reduced POM), and three enforcer rules were removed rather than silently weakened.
  • Reporting must move as a chain: doxia-sitetools → maven-site-plugin → reporting-impl → reporting-exec. Site plugin 3.x runs only Maven 3 mojos as reports, so every report goal is blocked; the non-report goals of the same plugins port.
  • Maven 3 and 4 cannot share one line for ported components: Maven 4 DI (org.apache.maven.api.di) is invisible to Sisu, so every ported component needs a 3.x line while its consumers run on Maven 3.
  • Not every component should adopt Maven 4 DI. indexer-core is used outside Maven (IDEs, repository managers); dropping its Maven dependency serves it better than a port.
  • Green tests do not prove a port. A @Resolution field that stayed null passed every maven-rar-plugin test until an IT with real dependencies was added, and maven-shade-plugin stays green while publishing the original POM instead of the reduced one.

Filed from this work

Components

Counts are src/main files importing the Maven 3 API on the default branch, excluding a plugin's own org.apache.maven.plugins.* packages; for the shared components the count is imports. For repositories that hold both a library and a plugin (surefire, release, enforcer, plugin-tools, archetype, scm, jxr, wrapper), only the library modules are counted and ported; the plugin modules are consumers.

Migration needed

Component Maven 3 API use PR Status
maven-acr-plugin 1 ported, green locally (4 ITs); app-client packaging still registered through Plexus components.xml: rc-7 ignores PackagingProvider
maven-antrun-plugin 7 ported, green locally (4 unit, 29 ITs); Ant reference maven.project is now the v4 Project, maven.project.helper removed
maven-archetype archetype-common 11 partial: archetype-common ported; archetype plugin excluded, does not compile; remote catalog loses cache and update policy
maven-artifact-plugin 11 partial: 25 unit tests; ITs 13 → 12 passed (buildinfo-dir: a pom project has no main artifact); check-buildplan approximated with no execution-plan service; reproducible-central report blocked by the reporting chain
maven-assembly-plugin 21 ported, green locally (271 unit, 156 ITs; the one failing IT is excluded from run-its by the POM); needs an unreleased plexus-archiver: its archivers cannot unpack without a container
maven-build-cache-extension 31 apache/maven-build-cache-extension#544 ✅ completed: migration not needed; PR closed as wontfix
maven-changelog-plugin 3 blocked: compiles, but maven-site-plugin 3.x cannot run a v4 report mojo (Cannot cast ChangeLogReportFactory to org.apache.maven.plugin.Mojo); ITs 4 of 4 → 0 of 4
maven-changes-plugin 28 partial: the four non-report goals ported (54 unit tests, 16 ITs incl. 11 new); report goals excluded
maven-checkstyle-plugin 8 partial: check ported (16 unit, 30 ITs; 4 ITs no longer run the report first); report goals blocked, 19 report ITs excluded; plexus-resources built without Sisu
maven-common-artifact-filters 16 apache/maven-common-artifact-filters#130 ✅ completed: migration not needed; PR closed as wontfix
maven-dependency-analyzer 14 apache/maven-dependency-analyzer#305 ✅ completed: migration not needed; PR closed as wontfix
maven-dependency-plugin 75 partial: 25 of 29 goals ported (174 unit tests; about 265 Maven 3 harness tests excluded, not deleted); add, remove, list-repositories, analyze-report have no route; tree -Dverbose loses conflict data
maven-dependency-tree 12 apache/maven-dependency-tree#149 ✅ completed: migration not needed; PR closed as wontfix
maven-doap-plugin 3 ported, green locally (15 unit, 4 ITs); five behavioural approximations: repository policies, metadata, existence checks
maven-doxia-sitetools 5 (integration-tools, site-renderer) apache/maven-doxia-sitetools#701 ✅ completed: migration not needed; PR closed as wontfix
maven-ear-plugin 20 partial: 49 unit tests, 103 EarMojoIT cases and 17 of 18 ITs pass; skinny-wars-timestamp fails (no timestamped snapshot version); includesDependencies ignored by the resolver
maven-ejb-plugin 1 ported, green locally (36 unit, 9 ITs); needs sisu.plexus at compile scope for the legacy BuildContext, as maven-resources-plugin does
maven-enforcer enforcer-rules 38, enforcer-api 2, enforcer-extension 1 partial: api and rules ported (347 tests, 0 failures); 3 rules removed for lack of API (requireUpperBoundDeps, banDynamicVersions, requireProfileIdsExist); plugin and extension excluded
maven-gpg-plugin 9 partial: green locally (13 ITs + 4 Bouncy Castle ITs; gpg signer ITs not runnable here); encrypted {...} passphrases have no API route and now fail explicitly
maven-help-plugin 12 ported, green locally (61 unit, 36 ITs); describe prefix lookup and evaluate rebuilt without a plugin manager or expression evaluator; effective-pom properties now sorted
maven-indexer indexer-core 3 partial: indexer-core ported (model only); indexer-cli and search-backend-indexer still wired through Sisu and fail at runtime
maven-invoker-plugin 18 partial: 67 unit tests; 70 of 71 ITs run pass (22 not run); MINVOKER-377 fails because an alternate local repository cannot override the split layout; report goal blocked by the reporting chain
maven-jarsigner-plugin 3 ported, green locally (52 unit, 9 ITs); settings decryption rebuilt from plexus-sec-dispatcher internals
maven-javadoc-plugin 24 partial: every goal runs directly (75 unit tests; ITs 79 → 75 passed, the 4 new failures are mvn site runs); as a site report blocked by the reporting chain
maven-jdeprscan-plugin 5 ported, green locally (6 unit, 6 ITs)
maven-jdeps-plugin 3 ported, green locally (11 unit, 7 ITs); @Resolution fields stay null because plugin-tools writes no <resolutions>
maven-jlink-plugin 5 ported, green locally (17 unit, 28 ITs); module path now from DependencyResolver, LocationManager dropped
maven-jmod-plugin 5 ported, green locally (18 ITs); main-module-aware module path still from plexus-java LocationManager
maven-mapping 1 apache/maven-mapping#77 ✅ completed: migration not needed; PR closed as wontfix
maven-plugin-tools tools-api 13, annotations 4, generators 3 partial: all but the descriptor model ported; maven-plugin-plugin and plugin-report excluded, do not compile
maven-pmd-plugin 26 partial: check goals ported (20 unit, 5 new ITs); report goals blocked; check depended on the report only through @Execute, now removed
maven-project-info-reports-plugin 26 blocked: every goal is a report; ProjectBuilder cannot build a project from a repository artifact, and conflict data exists only as text
maven-rar-plugin 4 ported, green locally (4 ITs, one new covering dependencies); rar packaging still registered through Maven 3 components
maven-release release-manager 59, release-api 7, oddeven-policy 1 partial: libraries ported (791 tests, 0 failures); immutable model forced a DOM-editing redesign; maven-release-plugin excluded, does not compile
maven-remote-resources-plugin 8 ported, green locally (11 unit, 9 + 7 ITs); supplemental-model merging reduced; a project resource can no longer exclude a bundle resource
maven-reporting-exec 21 apache/maven-reporting-exec#167 ✅ completed: migration not needed; PR closed as wontfix
maven-reporting-impl 8 apache/maven-reporting-impl#254 ✅ completed: migration not needed; PR closed as wontfix
maven-resolver-ant-tasks 10 partial: model and settings ported; relies on internal ApiRunner for a Session outside Maven
maven-scm-publish-plugin 2 ported, green locally (5 ITs); ScmManager built from the Sisu index by hand, maven-scm has no non-Sisu factory
maven-scripting-plugin 4 ported, green locally (5 ITs); script binding project is now the v4 Project
maven-shade-plugin 10 partial: green locally (72 unit, 85 ITs), but the dependency-reduced POM can no longer become the published POM (the original is installed); transformer SPI unchanged, Shader SPI changes
maven-shared-jar 4 apache/maven-shared-jar#182 ✅ completed: migration not needed; PR closed as wontfix
maven-site-plugin 24 partial: 10 goals run (deploy, stage, effective-site, attach-descriptor fully; site, jar, run without reports); 40 of 62 ITs, the other 22 lose report pages; reports need a plugin-manager API
maven-surefire maven-surefire-common 8; the forked-JVM modules use none partial: maven-surefire-common ported; it holds the mojo base class, so surefire and failsafe plugins cannot move separately
maven-toolchains-plugin 9 ported, green locally (8 ITs); an extension's toolchain factory is found only through the Sisu index

✅ Migration not needed: no Maven API used

16 components

These use no import from Maven core, model, plugin or artifact packages and no org.apache.maven:* dependency (Doxia is not Maven API). They work on Maven 4 as they are; the branch only switches CI to Maven 4.

Component PR Status
maven-archetypes green locally, 18 archetype builds; only archetype templates use the Maven API
maven-doxia apache/maven-doxia#1109 ✅ completed: migration not needed; PR closed as wontfix
maven-doxia-converter apache/maven-doxia-converter#165 ✅ completed: migration not needed; PR closed as wontfix
maven-executor green locally, 62 tests
maven-file-management apache/maven-file-management#119 ✅ completed: migration not needed; PR closed as wontfix
maven-fluido-skin apache/maven-fluido-skin#351 ✅ completed: migration not needed; PR closed as wontfix
maven-invoker apache/maven-invoker#189 ✅ completed: migration not needed; PR closed as wontfix
maven-jarsigner apache/maven-jarsigner#117 ✅ completed: migration not needed; PR closed as wontfix
maven-jxr ✅ completed: migration not needed; only maven-jxr-plugin uses the Maven API (34 tests green on Maven 4)
maven-reporting-api apache/maven-reporting-api#88 ✅ completed: migration not needed; PR closed as wontfix
maven-scm green locally, 340 tests; only maven-scm-plugin uses the Maven API
maven-script-interpreter apache/maven-script-interpreter#223 ✅ completed: migration not needed; PR closed as wontfix
maven-shared-resources apache/maven-shared-resources#89 ✅ completed: migration not needed; PR closed as wontfix
maven-shared-utils apache/maven-shared-utils#442 ✅ completed: migration not needed; PR closed as wontfix
maven-wagon green locally, 1035 tests
maven-wrapper green locally, 34 tests; only maven-wrapper-plugin uses the Maven API

✅ Already on the Maven 4 API

5 entries

The 2022 mvn4 prototype branches, written against an earlier API, were checked against the agent/mvn4-api ports and deleted (61 branches).

Out of scope: maven (core), maven-integration-testing and maven-resolver (core or below it); maven-verifier (superseded by maven-executor); maven-shared-incremental (retirement vote open) and maven-shared-io (retired); maven-doxia-linkcheck and maven-default-skin (archived); maven-plugin-testing (master is empty); parent POMs, maven-apache-resources, maven-gh-actions-shared, maven-site, maven-sources and maven-dist-tool (no library code).

Related: #12709 (minimum Maven baseline).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions