You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@maven.apache.org by "Robert Scholte (JIRA)" <ji...@apache.org> on 2016/07/31 18:17:20 UTC

[jira] [Commented] (MNG-5652) "supplies"/"provides"/"proffers" concept proposal

    [ https://issues.apache.org/jira/browse/MNG-5652?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15401285#comment-15401285 ] 

Robert Scholte commented on MNG-5652:
-------------------------------------

http://cr.openjdk.java.net/~mr/jigsaw/spec/lang-vm.html Jigsaw/Java9 recognizes this as well, they introduce {{provides X.Y with Z1.Z2;}}, but they can do it on a TypeName level.

> "supplies"/"provides"/"proffers" concept proposal
> -------------------------------------------------
>
>                 Key: MNG-5652
>                 URL: https://issues.apache.org/jira/browse/MNG-5652
>             Project: Maven
>          Issue Type: New Feature
>          Components: FDPFC
>            Reporter: Stephen Connolly
>
> The exact name is still undecided. Some candidate names are: "supplies", "provides", and "proffers"
> h2. "supplies" concept proposal
> ===========================
> h3. Introduction
> ------------
> The following is a proposal for Maven in a post-modelVersion-4.0.0 era. The aim of this proposal is to simplify the management of dependency trees in the decentralised era of artifact production that we find ourselves in.
> The core issue is that different organisations can produce artifacts that may overlap. The easiest example is the servlet-api. If we restrict ourselves to version 2.5 of the servlet specification there are quite a few artifacts that all deliver the exact same content:
> * {{jetty:servlet-api:2.5-6.0.2}}
> * {{org.daisy.libs:servlet-api:2.5.0}}
> * {{org.mortbay.jetty:servlet-api-2.5:6.1.14}}
> * {{org.jboss.spec.javax.servlet:jboss-servlet-api_2.5_spec:1.0.1.Final}}
> * etc
> **Note:** this is a generic problem that is not restricted to the servlet-api, the servlet-api just provides the example that will be most familiar to everyone.
> So where these multiple artifacts supplying the equivalent content becomes a problem is when the dependency tree is being calculated. If you have two dependencies each declaring transitive dependencies on different artifacts that supply equivalent content, then you end up with two copies of the same JAR file in your classpath.
> In the case of the servlet-api, the hack most people use is to declare the servlet-api with scope `provided` thus preventing it from being transitive. This is, however, a hack. In a more ideal world it would be better to let the servlet-api be transitive and only when we get to the WAR module would we declare that a specific servlet-api is to be provided in the containers that the WAR is targets for deployment into. 
> We can take a second example that does not have the luxury of a *de facto* hack.
> * {{javax.faces:jsf-api:2.1}}
> * {{org.jboss.spec.javax.faces:jboss-jsf-api_2.1_spec:2.1.28.Final}}
> * {{org.apache.myfaces.core:myfaces-api:2.1.13}}
> Now in the case of the JSF API, you are supposed to bundle the JSF API in your WAR file. So if I use three JSF component libraries, I could very well end up with three different but equivalent JSF API jars in my WAR file.
> Ideally what we want is some way of telling Maven that these artifacts are equivalent.
> Proposal
> --------
> Introduce the concept of "supplies" to the project model. The concept needs three changes to the project model:
> 1. An explicit top level construct for a project to explicitly declare up-front artifacts that it knows - at the time the project is being authored - to contain equivalent content to at least a subset of the project's content. Declarations could include a claim from: `subset`, `superset`, `disjoint` and `equivalent` with the default being `disjoint`.
> 2. An explicit sub-element of the `dependency` construct to allow consumers to *post-facto* declare a specific dependency as supplying equivalent content for other dependencies
> 3. An extension to the `dependency/excludes/exclude` construct to allow consumers to remove claims a dependency makes with respect to supplying equivalent content
> By way of illustration, here are some examples of these constructs mapped to a Model Version 4.0.0 like XML schema. As the post-modelVersion-4.0.0 schema is not yet known, this represents the best way to illustrate how the concept will work, but note that this proposal does not suggest a schema for this concept.
> h3. Example 1
> This illustrates how we would want, say, the `myfaces-api` project model to look.
> {code:xml}
> <project>
>   <groupId>org.apache.myfaces.core</groupId>
>   <artifactId>myfaces-api</artifactId>
>   <version>2.1.3</version>
>   ...
>   <supplyManagement>
>     <supplies>
>       <groupId>javax.faces</groupId>
>       <artifactId>jsf-api</artifactId>
>       <version>[2.1,2.2)</version>
>       <claim>superset</claim>
>     <supplies>
>     <supplies>
>       <groupId>org.jboss.spec.javax.faces</groupId>
>       <artifactId>jboss-jsf-api_2.1_spec</artifactId>
>       <claim>equivalent</claim>
>     <supplies>
>   </supplyManagement>
>   ...
> </project>
> {code}
> This indicates that the {{myfaces-api}} artifact is intended to be useable as a drop-in replacement for either the {{javax.faces:jsf-api}} artifact within a bounded range or for any version of the {{org.jboss.spec.javax.faces:jboss-jsf-api_2.1_spec}} artifact. If you get a supplier conflict in your classpath, then Maven should fail the build.
> For example if somebody forked {{myfaces-api}} but did not list {{myfaces-api}} in the fork's supplyManagement and you end up with both {{myfaces-api}} and {{myfaces-fork-api}} in your classpath. Maven can detect that there are two dependencies that both claim to supply {{javax.faces:jsf-api}} and fail the build, thereby letting the user add either exclusions or additional supplies information to one of the artifacts and preventing duplicate artifacts on the classpath. The build need not be failed if the supplies claims provide a resolution. e.g. if the claim is {{equivalent}} then that implies that there is a 1:1 mapping and hence the artifacts are drop-in replacements. However where the claim is {{superset}} we cannot know that the extra content in our artifact is the same as the extra content in another artifact which has a superset of `javax.faces:jsf-api`.
> h3. Example 2
> This illustrates a JSF component library that is working with the existing JSF APIs
> {code:xml}
> <project>
>   ...
>   <dependencies>
>     <dependency>
>       <groupId>javax.faces</groupId>
>       <artifactId>jsf-api</artifactId>
>       <version>2.1</version>
>       <supplyManagement>
>         <supplies>
>           <groupId>org.jboss.spec.javax.faces</groupId>
>           <artifactId>jboss-jsf-api_2.1_spec</artifactId>
>           <claim>equivalent</claim>
>         <supplies>
>         <supplies>
>           <groupId>org.apache.myfaces.core</groupId>
>           <artifactId>myfaces-api</artifactId>
>           <version>[2.1,2.2)</version>
>           <claim>equivalent</claim>
>         </supplies>
>       </supplyManagement>
>     <dependency>
>   </dependencies>
>   ...
> </project>
> {code}
> In this case we are publishing a transitive dependency with additional supplyManagement injected. Consumers of this project would thus gain the benefit of collapsing their transitive dependencies for any of these three artifacts. As all artifacts are declared with `equivalent` claim, thus the nearest of those three artifacts to the project will win as per the standard dependency resolution rules when dealing with conflicting version requirements in the transitive dependency tree.
> h3. Example 3
> Finally, there is the case where you need to correct an incorrect claim of supply
> {code:xml}
> <project>
>   ...
>   <dependencies>
>     <dependency>
>       <groupId>javax.faces</groupId>
>       <artifactId>jsf-api</artifactId>
>       <version>2.1</version>
>       <exclusions>
>         <exclusion>
>           <groupId>org.jboss.spec.javax.faces</groupId>
>           <artifactId>jboss-jsf-api_2.2_spec</artifactId>
>           <scope>supplies</scope>
>         <exclusion>
>       </exclusions>
>     <dependency>
>   </dependencies>
>   ...
> </project>
> {code}
> This would typically be coupled with adding back in a correct supplies definition, but we need to allow for people to correct the graph after the fact of their dependencies being deployed to the remote repository.
> h3. Claim conflict resolution
> The four classes of claim can be resolved using the following matrix
> |    |                   | A           | A               | A            | A             |
> |    |                   | subset   | equivalent | superset | disjoint   |
> | B | subset       | conflict  | A wins       | A wins     | conflict   |
> | B | equivalent | B wins   | A or B       | A wins     | conflict   |
> | B | superset   | B wins    | B wins      | conflict     | conflict   |
> | B | disjoint      | conflict   | conflict     | conflict     | conflict   |
> The default unspecified claim is {{disjoint}} which indicates that some of the content is reproduced, but not all and there is additional content added. With such a claim there will always be conflict and the build should fail until the Project Model is updated to either remove some of the claims or resolve the dependency clash.
> The ideal claim is {{equivalent}} which indicates that the two artifacts are bi-directionally substitutable. This does not mean that the contents are identical. It does mean that they both deliver on the same contract in an equivalent fashion.
> The {{subset}}` and {{superset}} claims are for aggregation APIs. So for example the Java EE Web Profile API is a superset of the various spec APIs that make up the Java EE Web Profile and a subset of the full Java EE specification. The use of the {{subset}}` claim should be reserved to those cases that are strict subsets. If anything is added that is not in the supplied artifact then the correct claim is {{disjoint}}.
> h3. Validation of supplies claims
> We do not want to introduce Java bias with this feature. As a result the validation of claims and supplies directives will be left to plugins. For the Java case we should probably provide either/both an enforcer rule or a maven hosted plugin to assist in checking JAR projects against the declared supplies declarations, but Maven core should not be concerned with solving the validation problem.
> Similarly, while there may be advantages in a more fine grained API contract negotiation between dependencies, to bind such a concept into the project model would significantly taint the Maven project model with more Java-centric concepts. Given that current software development typically uses at least two core languages: Java and JavaScript, we should be aiming to reduce Java-centric constructs from Maven rather than increase them.
> h3. Backporting
> It will not be possible to fully express this concept in a modelVersion 4.0.0 project model. Thus if generating 4.0.0 compatible project models, the aim should be to fully resolve the dependencies of the project using all available information and express that as the transitive dependencies.
> Thus we will not expose the "supplies" information to modelVersion 4.0.0 parsers but we will expose the end results of that and present the final effective flattened dependency tree.
> modelVersion 4.0.0 consumers will thus be no worse off than they already are and those consumers understanding newer modelVersions can get the enhanced tree resolution that they would not get otherwise. 



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)