Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The aim of the Project Dependency Trees model is to resolve these issues.

Model evolution

One of the top level elements of the Maven POM is the modelVersion element that specifies the model version for the POM. To date there have been two model versions 3.0.0 and 4.0.0. In both cases, a critical issue for changing the model version is that older clients cannot parse the newer model. This required the forking of Central (which is why central is repo.maven.org/maven2 because 4.0.0 was introduced with Maven 2 and Maven 1 clients could not parse the new model version)

...

  • Legacy clients are clients that are not aware of the Project Dependency Trees model
  • Modern clients are clients that are aware of the Project Dependency Trees model, but need not necessarily be aware of the latest modelVersions deployed by the newest version of Maven.

Legacy clients

Legacy clients cannot be aware of the Project Dependency Trees model. For this reason, any project that deploys a Project Dependency Trees model will also deploy a modelVersion 4.0.0 POM which is the best-effort translation of the Project Dependency Trees model for the primary artifact of the project.

Modern clients

As part of the process of evolving a Project Dependency Trees schema, each new version of the model will be accompanied by an XSLT transformation(s) that will be published into Central at a defined set of coordinates. This will allow a modern client to convert a schema - that is newer than the highest modelVersion it was built against - into the newest modelVersion that it supports. In general the XSLT transformation will essentially strip out elements that are not understood by older clients, though it is possible that more adventurous transformations may be included.

The rationale for choosing XSLT as the transformation... and consequently forcing the Project Dependency Trees model to be expressed in XML is that XSLT is currently the only cross-platform transformation engine available across the JVM, Ruby, .NET, C/C++ native code and JavaScript runtimes.

Artifact Dependency differentiation

The current POM provides a single scoped dependency tree that is then universally applied to all artifacts produced by the project. This does not align correctly with what the artifacts produced by a single project actually require.

...

NOTE the Project Dependency Trees will have no concept of scope. This may cause issues for artifact types where for example a different dependency is transitive during compilation compared with execution. The current thinking is that such situations will be exceedingly rare if they ever occur, and that as such, for least surprise, the consumer will have to configure their build tool to address this issue if it ever arises.

Non-atomic deployment

The primary driver of non-atomic deployment is the production of platform specific artifacts for the project. In this regard, the Project Dependency Trees model assumes that deployments will be at least atomic per platform. "At least atomic per platform" means that the initial deployment may include multiple platforms and subsequent additional deployments will be atomic per platform. For example:

...

NOTE: while different projects can follow different conventions for what the different platformIds are used for, as the dependency's platformId is part of the dependency tree, the project can perform the appropriate mapping of its transitive dependencies platform identifiers into its own convention, so deviations in conventions between projects should not prove fatal.

Conflict resolution

The modelVersion 4.0.0 POM mixes build time dependency specification with consumption time dependency specification. This has the effect of significantly complicating the dependency model within the POM:

...

Conflict resolution is also related to the next issue.

Version ranges and reproducible builds

One of the main issues with version ranges in the modelVersion 4.0.0 POM is that they produce an irreproducible build, as the consumer will re-resolve the version range every time it builds the dependency tree, and as such may resolve a different version.

...

The Project Dependency Trees model enabled consumers to make this choice by providing not only the version range but also the resolved version of each dependency. The version range can then be used to guide conflict resolution and the resolved version information can be used as hints to pre-select the exact version to use if the consumer wants a reproducible build.

Build time information

The Project Dependency Trees model removes all the build time information that was previously exposed from the modelVersion 4.0.0 POM, thus there is no <build><profiles> or <reporting> sections. 

...

OPEN QUESTION: do we deploy the newer modelVersion POM as the groupId:artifactId::version::pom or as groupId:artifactId::version:build:pom? The first form ensures that the POM cannot be used as a parent by modelVersion 4.0.0 projects as they will blow up immediately, however there has been an established practice of using <packaging>pom</packaging> for projects that produce non-standard artifacts and want to opt-out of the standard lifecycle binding, and thus we would break consumption of those "side" artifacts by legacy clients. Perhaps the solution is to follow the second form (i.e. it gets deployed with <classifier>build</classifier> and either put a Maven enforcer execution into the modelVersion 4.0.0 POM or use the <prerequisites> tag to try and at least alert that the parent is invalid.

Project Dependency Trees

<project> element

The project dependency trees model consists of a top level <project> tag and three types of immediate children elements:

<project modelVersion="..." groupId="..." artifactId="..." [platformId="..."] version="...">
  <information .../>
  <license .../>
  <artifacts .../>
</project>

The following are mandatory elements:

  • modelVersion attribute - containing the model version of the project dependency trees, which can be used by consumers to select an XSLT tranformation to apply against the model if they need to translate a newer modelVersion to one that the consumer can parse.
  • groupId attribute - containing the groupId of the project
  • artifactId attribute - containing the artifactId of the project
  • version attribute - containing the version of the project
  • artifacts element - at least one element must be present, there are uniqueness constraints on this element relating to platformId attributes

The following are optional elements:

  • platformId attribute - this is only present for additional atomic deployments of platform specific artifacts taking place after the initial deployment. When present there must be exactly one artifacts element and it must have the matching platformId as specified on the project element.
  • information element - there can be at most one of these, it contains additional information about the project and its artifacts
  • license elements - there can be any number of these, each element represents a set of licensing terms under which the project's artifacts are made available.

    In the vast majority of cases, projects are covered by a single set of license terms. Those cases will have a single <license> element that provides the SPDX expression for the license terms, e.g. <license spdx="(LGPL-2.0 AND GPL-2.0)"> which would indicate that portions of the code are LGPL and other portions are GPL.

    In other cases, projects are dual or multi-licensed. Those cases will have multiple <license> elements where the consumer is free to select any one of those expressions as the license terms that they will be complying with, thus <license spdx="GPL-2.0"/><license spdx="(BSD-2-Clause AND Apache-2.0)"/> would indicate that the consumer either has to comply with the terms of the GPL or both BSD and Apache licenses. While SPDX syntax would allow for <license spdx="(GPL-2.0 OR BSD-2-Clause AND Apache-2.0)"/> as an equivalent expression, dual licensing seems important enough that it should be separated out explicitly in separate elements as the priority rules of SPDX expression syntax - while clear and unambiguous - can be confusing to the uninitiated with regard to OR being lower priority than AND

<information> element

The information element consists of optional information about the project and its artifacts.

TODO: decide what, if any, additional content can go in here, SCM, Issue trackers, URLs, Mailing Lists, etc.

<information>
 <name .../>
 <description .../>
</information>

There are no mandatory elements

The following are optional elements:

  • name element - containing the name of the project (or when the <information> tag is scoped to a specific <artifact> overriding the project name for that specific artifact)
  • description element - containing the description of the project (or when the <information> tag is scoped to a specific <artifact> the description of that specific artifact)

<license> element

The license element consists of information about one set of licensing terms that the project and its artifacts is made available under.

<license spdx="..."/>

There is one mandatory element

  • spdx attribute - containing a SPDX expression for a single set of terms that the project and its artifacts (or when the <license> tag is scoped to a specific <artifact> overriding the project licenses for that specific artifact) are made available under. The SPDX expression may not contain OR operators. Instead the SPDX expression must be normalized to remove OR operators and instead present each disjoint set of licenses as a separate <license> tag.

There are no optional elements

<artifacts> element

The artifacts element consists of details of all the artifacts produced by the project. The artifacts are partitioned by platformId.

<artifacts [platformId="..."]>
 <artifact .../>
</artifacts>

There is one mandatory element:

  • artifact element - there must be at least one artifact element within an artifacts element. There is no upper limit. Each artifact element corresponds to an artifact that has been deployed with the Project Dependency Trees

There is one optional element:

  • platformId attribute - the presence of this attribute indicates that all the contained artifact elements are platform specific artifacts for the specified platformId. The absence of this attribute indicates that all the contained artifact elements are non-platform specific artifacts.

    NOTE: if an artifact supports a subset of all the platforms, we currently will envision that as non-platform specific as it targets more than one platform. An alternative solution may be to come up with a generic platform identifier that covers the multiple platforms or define a concatenation rule for platformIds, but addressing this concern otherwise is currently seen as premature optimization.

Uniqueness constraints apply to the artifacts element.

  • If the enclosing <project> element has the platformId attribute specified then there must be one and only one <artifacts> element and that element must have the platformId attribute specified and the platformId must match exactly that of the <project> elements.
  • If the enclosing <project> element has no platformId attribute then there may be at most one <artifacts> element without a platformId attribute and for any additional <artifacts> elements the platformId element must be unique.

<artifact> element

The artifact element consists of the details of a specific artifact produced by the project.

<artifact type="..." [classifier="..."]>
<information .../>
<license .../>
<component .../>
<provides .../>
<requires .../>
<supports .../>
</artifact>

There is one mandatory element:

  • type attribute - this is the file type of the artifact. NOTE: this is the actual file extension that the artifact is deployed with, not the "packaging" type which would require consumers to have awareness of all "packaging" to file type mappings.

The following are optional elements:

  • classifier attribute - this is used to indicate side artifacts such as sources or javadoc, etc.
  • information element - there can be at most one of these, this is used to override the project level information for this specific artifact. This tag effectively inherits from the project level tag.
  • license elements - there can be any number of these. If there are no license elements then the license terms of this specific artifact are the license terms expressed in the project level tag. If there are any license elements in an artifact then only those license elements apply to the artifact.
  • component elements - there can be any number of these. If present they are a hint to the consumer about type specific components that are present within the artifact and that may need to be considered during conflict resolution. For example, with a JAR artifact, the components may be Java 9+ module identifiers. Consumers may ignore the component elements if they choose.
  • provides elements - there can be any number of these. If present they indicate that this artifact embeds equivalent content to the named dependency. The exact meaning of "embeds" is dependent on the type of artifact and the type of dependency.
  • requires elements - there can be any number of these. If present they indicate that the consumer has a mandatory transitive dependency.
  • supports element - there can be any number of these. If present they indicate that the consumer has an optional transitive dependency.  

<component> element

The component element consists of hints to the consumer of type specific components that are present within the artifact for consideration during conflict resolution.

<component id="..."/>

There is one mandatory element:

  • id attribute - this is an identifier, the conventions of how this identifier is used will be established by the tooling around the specific artifact types.

There are no optional elements:

One anticipated usage of the component element is for JAR artifacts that the id would correspond to the Java 9+ module identifiers as in Java 9+ the module identifier must be unique on the module path and hence conflict resolution will be required to process the dependency tree into a flattened modulepath with validation of uniqueness of component identifiers enforced.

<provides> element

 

<requires> element

 

<supports> element

 

Example

The following is a pseudo-example of a Project Dependency Tree

<project modelVersion="..." groupId="..." artifactId="..." [platformId="..."] version="...">
    <information>
        <!-- container for descriptive information -->
        [<name>...</name>]
        [<description>...</description>]
        ...
    </information>
<license spdxIdspdx="..."/>
<license spdxIdspdx="..."/>
...
<license spdxIdspdx="..."/>
    <artifacts [platformId="..."]>
        <artifact type="..." [classifier="..."]>
            <information>
                <!-- optional element if need to override root level information for specific artifacts -->
            </information>
            <!-- 
              components are internal packaging constructs used by the packaging type but requiring more general validation
              e.g. for Java 9+ the ids could be the module ids if we wanted to validate that the module ids were unique in the
              effective tree.
            -->
            <component id="..."/>
            <component id="..."/>
            ...
            <component id="..."/>
            <!--
If the artifact has a different set of licenses from those defined at the project level, we define the licenses
of this artifact here. Otherwise we defer to the licenses defined at the top level of the project.
              licensing is a top level concern, and legitimately can vary per artifact. Let's not solve license compatibility, 
              rather leverage https://spdx.org/
            -->
            <license spdx:id="..."/>
            <license spdx:id="..."/>
            ...
            <license spdx:id="..."/>
            <!--
              provides is a marker that we have duplication of content. This could be because we are much like the many servlet-api jar
              files where there are many GAV's of the same javax.servlet:servlet-api:3.0 thus we could have the case where
 
              org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:jar:1.0.2.Final PROVIDES javax.servlet:servlet-api:3.0
              org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:jar:1.0.1.Final PROVIDES javax.servlet:servlet-api:3.0
              org.jboss.spec.javax.servlet:jboss-servlet-api_3.0_spec:jar:1.0.0.Final PROVIDES javax.servlet:servlet-api:3.0
              org.mortbay.jetty:servlet-api-3.0:jar:7.0.0pre2 PROVIDES javax.servlet:servlet-api:3.0
 
              similarly
 
              org.slf4j:log4j-over-slf4j:jar:1.7.21 PROVIDES log4j:log4j:[1.0,2)
 
              The consumer of the tree can then decide if/when/how to collapse redundant nodes as they see fit.
 
              TODO: decide optionality of version and range attributes
            -->
            <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]>
                <!-- no elements here as we have "rebundled" hence implicitly promoted up one level-->
            </provides>
            <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            ...
            <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <!--
              requires are the mandatory dependencies. This is effectively a recursive artifact where the GAV is not inherited and
              where we have discarded the information section. If you want those details, fetch that project's dependencies trees.
            -->
            <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                <component id="..."/>
                <license spdx:id="..."/>
                <provides groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
                <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                    ...
                </requires>
                <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            </requires>
            <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                ...
            </requires>
            ...
            <requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="..."]>
                ...
            </requires>
            <!--
              supports are the optional dependencies. We list them here to aid in conflict resolution. We do not include a nested tree
              as a consumer would only pull them in if the consumer already has its own a requires for them, so we really only
              need to validate the range. 
 
              TODO: decide optionality of range attribute
              TODO: decide if we want a version attribute 
            -->
            <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
            <supports groupId="..." artifactId="..." [platformId="..."] version="..." [range="..."] type="..." [classifier="..."]/>
        </artifact>
        <artifact ...>
            ...
        </artifact>
        ...
        <artifact ...>
            ...
        </artifact>
    </artifacts>
<!-- if the project does not specify a platformId then we can include additional platform details that were part of the atomic deployment -->
<artifacts platformId="...">
...
</artifacts>
...
<artifacts platformId="...">
...
</artifacts>
</project>

Schema

Here

...

is

...

a

...

draft

...

XML

...

schema:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" >

...

<xs:element name="license" xmlns:spdx="http://spdx.org/rdf/terms#">
<xs:annotation>
<xs:documentation source="version">5.0.0+</xs:documentation>
<xs:documentation source="description">
The <code>&lt;license&gt;</code> element defines one of the licenses
under which the artifacts are made available. Where a license is
attached to the <code>&lt;project&gt;</code> element this defines the
default licenses for all artifacts in the project. Where a license is
attached to an <code>&lt;artifact&gt;</code> element this signifies
that the specific artifact is covered by the
<code>&lt;license&gt;</code> elements defined within that
<code>&lt;artifact&gt;</code> element. Licenses are identified using
the <a href="http://spdx.org">SPDX</a> identifiers</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="spdxIdspdx" type="xs:string"/>
</xs:complexType>
</xs:element>

...