Status
Version 
Issue(s) 
Sources 
Developer(s)Stephen Connolly

Status

This RFC is currently in the DRAFT state. Nothing in this RFC has been agreed or confirmed.

Contents

Introduction

The next generation Project Object Model to be used by Maven 5.0+

Background

Maven uses the Project Object Model as a descriptor for the declarative build requirements of a project.

Due to the way Maven has been implemented, the current release versions will consider any modelVersion other than the one that they target as invalid and will fail to parse the model.

For build time concerns, this is not that major a concern, and in fact may be desirable behaviour, e.g. I should not be able to build a Maven 2.x / 3.x project with Maven 1.x.

Where the modelVersion becomes a constraint, however, is when it comes to transitive dependency resolution. The Maven Central repository has grown in popularity, and now the consumers of the information in central are no longer only Apache Maven. There are other build tools that parse the POM to extract dependency information, e.g. Apache BuildrGradleApache Ivysbt, etc. As these build tools are not under the control of the Apache Maven project, we risk breaking their ability to parse the POM as a unit of dependency expression if we modify the pom schema or model version.

While we could change the schema if we "forked" the central repository, the experience from the previous reposotory fork (for the Maven 1.x / Model Version 3.0.0 to Maven 2.x / Model Version 4.0.0 transition) was traumatic and a repeat is generally considered to be a Bad Plan™.

The result of all this is that the Apache Maven project has been unable to evolve our POM to reflect the new needs.

The current plan for a Path Forward™ uses three legs:

  1. We keep deploying modelVersion 4.0.0 poms to the repository as a best effort expression of the dependency information of artifacts such that legacy clients can continue to consume artifacts deployed with non-legacy clients.
  2. We deploy a dependency-only model using a defined contract for forwards compatibility (to allow for future evolution) using a different file extension (see Project Dependency Trees schema)
  3. The POM then becomes a build-time only concern and does not need to be deployed to the repository - except for those cases where the pom may be used as either a parent or a mix-in

This page will represent (TODO replace "will represent" with "represents" when near finalised) the specification for the next modelVersion of the POM to be used by Maven.

Classification of change requests

There are currently  flagged as either waiting for a major version bump in Maven because they are a behavioural change or waiting for a modelVersion bump because they change the POM schema. This section aims to classify and summarise the changes requested by users / developers in order to better understand the rationale for the proposed new POM schema.

New content to include in the POM

There are six general sub-themes around content to include in the POM.

The following issues look to add content for documentational purposes. This content would be consumed both by developers reading the POM "by hand" as well as by more automated tooling such as the Maven Site generation

The following issues look to add content to assist using maven on a specific project

The following issues concern configuration that needs to be shared between plugins

The following issues concern providing explanations of dependencies within the POM

The following issue concerns property evaluation

The following issues repeat / revert changes that previous experience has deemed to be a mistake. As such the current opinion is that these issues should not be fixed.

Supports / provides style concepts

The following issues are all essentially the same theme, namely look to add additional classes of dependency information to the dependency graph.

Versioning related issues

There is no specific set of themes here:

Lifecycle related changes

Two main themes around lifecycle changes

The following issues relate to trying to solve dependency issues within a multi-module reactor where one module consumes artifacts at one point in the lifecycle which are produced at a different point in the lifecycle by a different module.

The following issues relate to specification of the lifecycle itself.

Scope related changes

There is no specific set of themes here:

Profile activation

The following issues are all focused on gaps in profile activation:

POM format

The following issues look to address deficiencies (perceved or otherwise) in the modelVersion 4.0.0 POM format:

Mix-ins

The following issues look for mix-ins that allow content for the POM to be included from other sources:

Existing model

The existing 4.0.0 model POM has the following high-level structure:

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <!-- The Basics -->
  <groupId>...</groupId>
  <artifactId>...</artifactId>
  <version>...</version>
  <packaging>...</packaging>
  <dependencies>...</dependencies>
  <parent>...</parent>
  <dependencyManagement>...</dependencyManagement>
  <modules>...</modules>
  <properties>...</properties>
 
  <!-- Build Settings -->
  <build>...</build>
  <reporting>...</reporting>
 
  <!-- More Project Information -->
  <name>...</name>
  <description>...</description>
  <url>...</url>
  <inceptionYear>...</inceptionYear>
  <licenses>...</licenses>
  <organization>...</organization>
  <developers>...</developers>
  <contributors>...</contributors>
 
  <!-- Environment Settings -->
  <issueManagement>...</issueManagement>
  <ciManagement>...</ciManagement>
  <mailingLists>...</mailingLists>
  <scm>...</scm>
  <prerequisites>...</prerequisites>
  <repositories>...</repositories>
  <pluginRepositories>...</pluginRepositories>
  <distributionManagement>...</distributionManagement>
  <profiles>...</profiles>
</project>

The major critiques of the existing model are:

The other issue with the existing model is that it is being used for two distinct purposes and as such finds it difficult to be a master of both:

The vision of the 4.0.0 POM was that all projects would be cut from a series of standard templates (a.k.a. packaging):

In this vision, almost all 4.0.0 POMs should basically consist of the following structure:

<project>
  <modelVersion>4.0.0</modelVersion>
  <parent> <!-- most projects should inherit from a parent pom of some sort --> 
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <relativePath/>
  </parent>
  <artifactId>...</artifactId> <!-- most projects should inherit the parent's groupId -->
  <version>...</version>
  <packaging><!-- THIS IS THE IMPORTANT BIT--></packaging>
  <dependencies>
    ... <!-- Here is the project dependencies -->
  </dependencies>
  <build> <!-- no custom plugin configuration or bindings -->
    <extensions> <!-- this is only needed if not inherited from the parent -->
      <extension>
        <groupId>...</groupId>
        <artifactId>...</artifactId>
        <version>...</version>
      </extension>
    </extensions>
  </build>
</project>

In other words, when using the 4.0.0 POM in accordance with its initial vision, there should be at most 25 lines of boilerplate above the specification of the project dependencies and in the ideal case that boilerplate can be reduced to ~14 lines which specify:

When we inspect real world POMs however, we see that this pattern is almost never followed. Instead of producing custom templates/packaging most projects instead just fight with a standard template/packaging. The end result of this kind of fighting is POMs that run into the 10,000+ LOC levels with many plugin bindings and overloading of an existing lifecycle binding and profiles used to enable additional side-build processes. The reasons cited for these long POMs include:

Changes

This section details the rationale for all the changes to the POM format.

Dual usage

The most important change for the 5.0.0 POM is to split the dual usage:

DECISION: The POM is for Building, the Project Dependency Trees is for consumption of artifacts

AFFECTS: 

XML vs custom DSL

The project dependency trees schema will be XML because that is designed to be a machine generated document that is for consumption primarily by machines but needs to remain easily parsable by humans. The choice of XML is dictated by the requirement to enable multiple tools to have a level of forward compatibility and, at this time, the only cross-technology tool that can deliver a mapping is XSLT. For this reason the Project Dependency Trees schema will be an XML format.

As the 5.0.0 POM will only be used by Maven, and as the 5.0.0 POM will require Maven 5.0+ to build, there is no longer a strict requirement to retain the XML format for the 5.0.0 POM.

There are, however, a number of advantages to continuing with the XML based format at least for the 5.x release train of Maven:

The single biggest reason for retaining XML, however, is that we expect the build model will need to evolve. With the Project Dependency Trees schema, we need to provide for backwards compatibility (i.e. newer clients need to be able to parse older schemas) and limited forward compatibility (i.e. older clients need to be able to parse newer schemas). With the POM, we only need to provide for limited backwards compatibility (i.e. newer versions of Maven need to be able to parse a defined range of older schemas) without forwards compatibility (i.e. older versions of Maven will not be able to build newer POM modelVersions). Ideally we want the range of backwards compatibility to reach back as far as the 4.0.0 POM. Retaining XML as a POM format allows for technology such as XSLT to be used to map say a 5.0.0 POM into a 5.3.0 POM which would thus reduce the number of parsers that would be required to be included within Maven (we will still need a custom parser for the 4.0.0 POM, and it is likely that Maven 6.0+ would need custom two parsers one for the 4.0.0 POM and one for the 5.x.y POMs). A custom DSL would force tool vendors to produce syntax parsers for each and every model version.

DECISION: The 5.0.0 POM will be XML

AFFECTS:  

Elements vs Attributes

There seems to be universal agreement to use attributes where possible. The reason for choosing elements in the 4.0.0 was purely a technical limitation of the Modello toolchain at the time.

DECISION: The 5.0.0 POM will use XML attributes for data that cannot have child data. At a minimum the groupId/artifactId/platformId/version/classifier/type information of project/parent/dependencies/plugins/extensions will be defined using attributes.

AFFECTS:  

Customizing build behavior / One-off projects

The term packaging in the 4.0.0 POM is used for two distinct purposes: defining the type of the primary artifact and defining the base template of the build process. The Project Dependency Trees schema removes the concept of a primary artifact by providing the dependency trees of all attached artifacts, thus a single Maven project that produces a .jar.war and even say a secondary "skinny" .war will have the appropriate dependency trees for each artifact declared in the PDT. This contrasts with the 4.0.0 POM which only defined the dependencies of the primary artifact and relied on build tooling convention to infer what contextual transitive dependencies should be extracted by consumers from the POM.

Thus, in the 5.0.0 POM we only really want to specify the template for the lifecycles and default bindings. Given that the use case for this data is purely as a template, it makes sense to change the name to template.

DECISION: The 5.0.0 POM will use the term template rather than packaging.

AFFECTS: 

One of the requirements that a lot of projects have is cross-cutting inheritance. There is general agreement that mix-ins are the way to achieve this.

DECISION: The 5.0.0 POM will allow for mix-ins

AFFECTS:  

The evidence of the use of Maven shows that there seems to be a significant number of projects that believe themselves to be "one-off" projects that will not benefit from expressing the build logic in a reusable template. We need to provide a way to allow projects to easily change their effective lifecycle and plugin bindings

DECISION: The 5.0.0 POM will provide the ability to define custom lifecycles directly within the POM

AFFECTS: 

DECISION: The 5.0.0 POM will provide the ability to override and completely clear the plugin bindings against individual lifecycles

AFFECTS: 

Custom scopes

One of the blockers for custom scopes has been the requirement that the 4.0.0 POM be used for both the declarative build description and the consumer's dependency graph construction. Any custom scopes introduced into the POM would either break or confuse clients that relied on the assumed 5 scopes defined in the 4.0.0 POM. The Project Dependency Trees schema removes use case of consumption of the POM by consumers of the artifacts produced by the project. This has the effect of completely removing the limits on scopes. The 4.0.0 scopes will likely remain the conventions as interoperability with older plugins as well as conventions in the default configurations of plugins will simplify their use, but in those cases where a project needs to define and consume its own scopes it should be possible to permit it.

DECISION: The 5.0.0 POM will allow the definition and consumption of custom scopes directly within the POM, parent POM, mixins, or templates

AFFECTS: 

The system scope was a Java-centric special scope experiment that hit issues with consumption of dependencies across multiple platforms.

DECISION: The 5.0.0 POM will not provide any special case behaviour for a scope named system

AFFECTS: 

Build vs Reporting

If we look at the two use cases of building with Maven 2/3 there are actually two distinct use-cases:

This was shaped by having two configuration sections in the 4.0.0 POM, build and reporting. One of the issues with these two sections is that they did not have parity of configurability. Specifically, the reporting section did not have a pluginManagement. The solution of having the pluginManagement from the build section apply to the reporting section feels incorrect as now the child element of one is affecting another.

DECISION: The 5.0.0 POM will treat global plugin configuration defaults as a top level concern and have a tag equivalent to pluginManagement at the top level of the POM.

AFFECTS: 

If we seek to find a generic solution to the split between the build and reporting sections in the POM, it becomes apparent that these are all really just ways of defining bindings of plugins to the phases of various lifecycles. The build section defines the bindings against the default lifecycle, while the reporting section defines the bindings against the site lifecycle (Note: this is a simplification as the reporting plugins are actually partly invoked by the site plugin and thus are not actually specifically bound to the lifecycle, rather the site:site goal is bound to the site phase of the site lifecycle and that goal is responsible for invoking the reporting plugin goals)

As a side-effect of making it easier to produce custom lifecycles, we probably need to be able to make it easier to manage the bindings of plugins for the custom lifecycles.

DECISION: The 5.0.0 POM will remove the distinction between build and reporting relying rather on lifecycle specific binding declarations

AFFECTS: 

 

Project Object Model

<project> element

The Project Object Model consists of a top level <project> tag with child elements

<project modelVersion="5.0.0" [groupId="..."] artifactId="..." [version="..."] template="...">
  [<parent [groupId="..."] [artifactId="..."] [version="..."] [relativePath="..."]/>]
  [<mixin [groupId="..."] [artifactId="..."] [version="..."] [relativePath="..."]/>]
  [<extensions [mode="override|inherit"]>
    ...
  </extensions>]
  [<lifecycle id="..." mode="override|inherit">
    ...
  </lifectcle>]
  ...
</project>

The following are mandatory elements:

The following are optional elements:

<parent> element

The parent element identifies the parent project from which conventions will be inherited.

<parent [groupId="..."] [artifactId="..."] [version="..."] [relativePath="..."]/>

Technically from a schema perspective all attributes are optional, however there are two minimum valid sets of attributes:

The following are the attributes:

<mixin> element

The mixin element identifies additional projects from which conventions will be inherited.

<mixin [groupId="..."] [artifactId="..."] [version="..."] [relativePath="..."]/>

Technically from a schema perspective all attributes are optional, however there are two minimum valid sets of attributes:

The following are the attributes:

<extensions> element

The extensions element identifies extensions to be enabled for this project.

<extensions [mode="override|inherit"]>
  [<extension [groupId="..."] [artifactId="..."] [version="..."] [relativePath="..."]/>]
  ...
</extensions> 

There is one attribute:

There can be 0-N extension elements.

<extension> element

The extension element identifies additional projects containing extensions to enable for the project.

<extension [groupId="..."] [artifactId="..."] [version="..."] [relativePath="..."]/>

Technically from a schema perspective all attributes are optional, however there are two minimum valid sets of attributes:

The following are the attributes:

 

 

 

 

TODO resolve the inheritance problem

We have multiple places where things are coming from...

we need a model of inheritance that is easy for people to understand.

First stab:

Second stab:

I think we need to build the tree of mixins, resolve the closest version (with tree pruning) and then apply them...

Then after that, we can start to build the tree of extensions...

This is a pain!

 

 

TODO write this up... I'm just dumping stuff I have done on the mail thread here to make it easier to collaborate:

<project modelVersion="5.0.0" [groupId="..."] artifactId="..." [version="..."] template="...">
  [<parent groupId="..." artifactId="..." [version="..."] [relativePath="...']/>]

  [<mixin groupId="..." artifactId="..." [version="..."]/>]
  [<mixin groupId="..." artifactId="..." [version="..."]/>]
  ...
  [<mixin groupId="..." artifactId="..." [version="..."]/>]

  [<lifecycle id="..." mode="override|inherit">
    <phase id="..." [after="..." | before="..."]/>
    <phase id="..." [after="..." | before="..."]/>
    ...
    <phase id="..." [after="..." | before="..."]/>

  </lifecycle>]
  [<lifecycle id="...">
    ...
  </lifecycle>]
  ...
  [<lifecycle id="...">
    ...
  </lifecycle>]

  [<scope id="compile" [mode="override|inherit"]>
    <dependency groupId="..." artifactId="..." [platformId="..."] version="..." [classifier="..."] type="..."/> <!-- type is mandatory-->
    <dependency groupId="..." artifactId="..." [platformId="..."] version="..." [classifier="..."] type="..."/>
    ...
    <dependency groupId="..." artifactId="..." [platformId="..."] version="..." [classifier="..."] type="..."/>
  </scope>]
  [<scope id="...">
    ...
  </scope>]
  ...
  [<scope id="...">
    ...
  </scope>]

  [<extensions [mode="override|inherit"]>
    <extension groupId="..." artifactId="..." version="..."/>
    ...
  </extensions>]
  [<plugins [mode="override|inherit"]>
    <!-- this is what pluginManagement was -->
    <plugin groupId="..." artifactId="..." version="...">
      ...
    </plugin>
    ...
  </plugins>]

  [<bindings [mode="override|inherit"]>
    <!-- this is what plugins was, we make explicit here that this is the binding of executions into the lifecycles -->
  </bindings>]

  [<platform id="..." [mode="override|inherit"]>
    <activation>
      <!-- define how we determine that this platform can be built in the current environment -->
    </activation>
    <!-- allow platform specific mixins -->
    [<mixin groupId="..." artifactId="..." [version="..."]/>]
    <!-- allow platform specific lifecycles -->
    [<lifecycle id="...">
      ...
    </lifecycle>]

    <!-- allow platform specific dependencies -->
    [<scope>
      ...
    </scope>]

    <!-- allow platform specific bindings... but plugin management is from the root only -->
    [<bindings>
      ...
    </bindings>]

    <!-- allow most of the other root tags except platform and packaging and deployment config -->
  </platform>]
  [<platform id="...">
    ...
  </platform>]
  ...
  [<platform id="...">
    ...
  </platform>]

  <!-- template is only allowed in poms with an id of "parent" or "mixin". It allows a parent/mixin to be used by different template ids and define specialized defaults -->
  [<template id="...">
    [<mixin groupId="..." artifactId="..." [version="..."]/>]
    <!-- allow platform specific lifecycles -->
    [<lifecycle id="...">
      ...
    </lifecycle>]

    <!-- allow platform specific dependencies -->
    [<scope>
      ...
    </scope>]

    <!-- allow platform specific bindings... but plugin management is from the root only -->
    [<bindings>
      ...
    </bindings>]

    <!-- allow most of the other root tags except platform and packaging and deployment config -->
  </template>]
  [<template id="...">
    ...
  </template>]
  ...
  [<template id="...">
    ...
  </template>]

  <!-- unsure if we still need profiles -->
  <!-- perhaps we still need properties -->
  <!-- TBD deployment config, repositories, etc -->

</project>
 

 

Some things that came to mind, in no particular order:

 

Building the effective build time model would be:

 

 

Appendix 1 - Issues flagged for consideration post-modelVersion 4.0.0

This is not a perfect query as it includes issues that target behavioural changes outside the scope of modelVersion changes, but all the relevant issues should be a subset of this list