...
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
The provides element indicates that this artifact embeds equivalent content to the named dependency
<provides groupId<project modelVersion="..." groupIdartifactId="..." artifactId [platformId="..."] [platformIdversion="..."] versionrange="..."> type="..." [classifier="...]/>
The following are mandatory elements
groupId
attribute - the groupId of the embedded dependencyartifactId
attribute - the artifactId of the embedded dependencyrange
attribute - the version range of the embedded dependency - this will either be a hard range, e.g.[1.0]
where the exact dependency has been explicitly embedded or a compatibility range, e.g.[1.0,2.0)
where there is an "aliasing" or equivalence within an agreed API contracttype
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
platformId
attribute - the platformId of the embedded dependencyclassifier
attribute - the classifier of the embedded dependencyversion
attribute - the version of the embedded dependency. When present, therange
attribute should probably be a hard range for this version only. When absent, this indicates that there has been an "aliasing" and as such therange
attribute should reflect the compatibility constraints of the alias implementation.
Some examples may assist in the relative use-cases of the range
and version
attributes.
org.slf4j:log4j-over-slf4j
provides an alternative set of implementations of thelog4j:log4j::[1.0,2.0)
APIs. It does not actually include any content from any of the log4j jars. Rather the exact same public API contract has been re-implemented. Thus theorg.slf4j:log4j-over-slf4j
artifact might well state:<provides groupId="log4j" artifactId="log4j" range="[1.0,2.0)" type="jar"/>
there is noversion
attribute because the content has not been replicatedch.qos.logback:logback-classic
is an SPI implementation fororg.slf4j:slf4j-api
. Asslf4j-api
requires that there is at most one SPI implementation on the classpath, it may be useful for all SPI implementations to declare<provides groupId="org.slf4j" artifactId="slf4j-spi-impl" range="[1.7.0,1.8.0)" type="jar"/>
(this would also need slf4j-api to have a<supports groupId="org.slf4j" artifactId="slf4j-spi-impl" range="[1.7.0,1.8.0)" type="jar"/>
tag to trigger conflict resolution. If slf4j-api did not have an internal fallback implementation then it would use a<requires>
tag instead of a<supports>
tag.- überjars which basically aggregate multiple jar files would specify the
version
tag. Thusorg.hamcrest:hamcrest-all::1.3:jar
would have the<provides groupId="org.hamcrest" artifactId="hamcrest-core" version="1.3" range="[1.3]" type="jar"/>
because it literally duplicates the exact content oforg.hamcrest:hamcrest-core::1.3
. The range needs to be a hard range as it has been embedded directly and is an intrinsic part of the artifact.
<requires>
element
The requires element indicates a mandatory transitive dependency.
<requires groupId="..." artifactId="..." [platformId="..."] version="..." range="..." type="..." [classifier="...] [modelVersion="..."]>
<component .../>
<license .../>
<provides .../>
<requires .../>
<supports .../>
</requires>
The following are mandatory elements:
groupId
attribute - the groupId of the dependencyartifactId
attribute - the artifactId of the dependencyversion
attribute - the version of the dependency that was resolved at build time and is the recommended default version of the dependency to use. The child elements of thisrequires
element represent the information for the specified version of the dependencyrange
attribute - the version range of the dependency. This may be themodelVersion 4.0.0
style "unspecified here is a hint" style version, e.g.1.0
. Preference would be that it uses theversion
attribute as a lower bound, but any valid Maven version range is acceptable.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:
platformId
attribute - the platformId of the dependencyclassifier
attribute - the classifier of the dependencymodelVersion
attribute - if the dependency's Project Dependency Tree uses amodelVersion
less than or equal to themodelVersion
of the rootproject
tag in this Project Dependency Tree then this attribute must be omitted, otherwise the tag will contain themodelVersion
of the dependency's Project Dependency Tree. The presence of this attribute is an indicator to the consumer that the contained elements were the result of an XSLT transformation of the dependency's tree and thus, if the consumer understands this newermodelVersion
then a more correct view of the dependency tree could be obtained by fetching and parsing the dependency's Project Dependency Tree directly and substituting the parsed contents.license
elements - there can be any number of these, they reflect the license terms of the dependency as detailed from the dependency's tree.component
elements - there can be any number of these. If present they are a hint to the consumer abouttype
specific components that are present within the dependency 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 thecomponent
elements if they choose.provides
elements - there can be any number of these. If present they indicate that this dependency 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 dependency has a mandatory transitive dependency.supports
element - there can be any number of these. If present they indicate that the dependency has an optional transitive dependency.
<supports>
element
The supports element indicates an optional transitive dependency.
<supports groupId="..." artifactId="..." [platformId="..."] [version="..."] range="..." type="..." [classifier="..."]/>
The following are mandatory elements
groupId
attribute - the groupId of the dependencyartifactId
attribute - the artifactId of the dependencyrange
attribute - the version range of the dependency. This may be themodelVersion 4.0.0
style "unspecified here is a hint" style version, e.g.1.0
. Preference would be that it uses theversion
attribute as a lower bound, but any valid Maven version range is acceptable.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 attributes
platformId
attribute - the platformId of the dependencyversion
attribute - the version of the dependency that was resolved at build time and is the recommended default version of the dependency to use.classifier
attribute - the classifier of the dependency
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 spdx="..."/>
<license spdx="..."/>
...
<license spdx="..."/>
<artifacts [platformId="..."]>
<artifact type="..." [classifier="..."]>
<information>
<!-- optional element if need to override root level information for specific artifacts -->
<information>
<!-- container for descriptive information -->
[<name>...</name>]
[<description>...</description>]
...
</information>
<license spdx="..."/>
<license spdx="..."/>
...
<license spdx="..."/>
<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="..."/>
<license spdx="..."/>
...
<license spdx="..."/>
<!--
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="..."] type="..." [classifier="..."]/>..." [classifier="..."]/>
</artifact>
<artifact ...>
...
</artifact>
...
<artifact ...>
...
</artifact>
<supports groupId </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="...">
artifactId="..." [platformId="
</artifacts>
..."] version
<artifacts platformId="...">
[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>...
</artifacts>
</project>
Constructing a Project Dependency Trees model
The following process will be used to construct a Project Dependency Trees model:
- For each artifact, construct the list of direct mandatory dependencies
- For each artifact, construct the list of direct optional dependencies
- For each artifact, construct the list of embedded dependencies
- For each artifact, construct the list of licenses
- For each artifact, construct the list of components
- Construct the Project Dependency Trees of the embedded dependencies
- Process the embedded dependency trees.
- Any
provides
elements should be appended to the list of embedded dependencies. - Any
requires
elements should be appended to the list of direct mandatory dependencies. - Any
supports
elements should be appended to the list of direct optional dependencies. - Any
component
elements should be appended to the list of components - Licensing will be assumed to have been correctly defined by the project when the decision was made to embed dependencies
- Any
- Process the list of direct optional dependencies, removing any duplicates with the list of embedded dependencies (i.e. if in promoting an embedded dependency's supports tags we support it and have also embedded it, then we just have embedded it)
- Process the list of direct mandatory dependencies, removing any duplicates with the list of embedded dependencies (i.e. if in promoting an embedded dependency's provides tags we provide it and have also required it, then we just have provided it)
- Process the list of direct optional dependencies, removing any duplicates with the list of mandatory dependencies (i.e. if in promoting an embedded dependency's supports tags we support it and have also required it, then we just have required it)
- Construct the Project Dependency Trees of the mandatory dependencies
The principle is that
provides
tags effectively ensure that the dependency is "promoted up".requires
tags retain the tree nestingsupports
tags do not detail any transitive dependencies as this only comes into play if the consumer already has arequires
dependency on the same coordinates and needs to perform conflict resolution.
If a dependency is missing a Project Dependency Trees model, then the same process can be used to construct that model from the modelVersion 4.0.0
POM
TODO define the process for constructing the effective modelVersion 4.0.0
POM
TODO decide if we should include some "well known" conventions, e.g. that:
- the
javadoc
andsource
jar files probably do not have any dependencies - the
test-jar
probably has the test scope dependencies - that
war
artifacts probably have<provides>
tags for alljar
dependencies - etc
The above would probably be generally useful but would complicate the specification of the process for other consumers
Test data set
TODO: We need a cohort of Project Dependency Tree documents for implementations to validate their parsers against.
TODO: We need a cohort of modelVersion 4.0.0
POMs for implementations to validate their generation of effective Project Dependency Trees in the absence of a deployed Project Dependency Trees document along with the expected effective Project Dependency Tree documents
TODO: We need a cohort of sample transformations of trees to ensure that implementations can correctly aggregate Project Dependency Trees when building new projects that depend on other projects.
Schema
Here is a draft XML schema:
...