New API with immutable model
Related PRs:
- https://github.com/apache/maven-plugin-tools/pull/117
- https://github.com/apache/maven-plugin-testing/pull/24
- https://github.com/apache/maven-install-plugin/pull/35
- https://github.com/apache/maven-deploy-plugin/pull/27
- https://github.com/apache/maven-clean-plugin/pull/20
- https://github.com/apache/maven-compiler-plugin/pull/147
- https://github.com/apache/maven-filtering/pull/46
- https://github.com/apache/maven-resources-plugin/pull/35
If we want to use immutable objects, we should use Java Records. We still do not have Valhalla ready.
The developers often do not understand Java Memory Model and there is no guarantee that they would not make a mistake when they update any immutable object. The developers usually would not understand why ConcurrentLinkedQueue is preferable over ArrayList and List.copyOf() is ideal https://docs.oracle.com/javase/10/docs/api/java/util/List.html#copyOf(java.util.Collection), and so we should use List.copyOf() which means that we should switch the project to Java 10 - bad luck! Nobody would accept Java 10, I guess.
The immutable object should be fetched with data via a builder in constructor.
MavenProject and MavenProjectBuilder composition
public record MavenProject(MavenProjectBuilder builder) {
MavenProject {
dependencies = List.copyOf(builder.getDependencies());
...
...
...
}
}
If we want to use immutable objects, we should use Java Records. We still do not have Valhalla ready. The developers often do not understand Java Memory Model and there is no guarantee that they would not make a mistake when they update any immutable object. The developers usually would not understand why ConcurrentLinkedQueue is preferable over ArrayList and
List.copyOf()is ideal https://docs.oracle.com/javase/10/docs/api/java/util/List.html#copyOf(java.util.Collection), and so we should useList.copyOf()which means that we should switch the project to Java 10 - bad luck! Nobody would accept Java 10, I guess.The immutable object should be fetched with data via a builder in constructor.
MavenProject and MavenProjectBuilder compositionpublic record MavenProject(MavenProjectBuilder builder) { MavenProject { dependencies = List.copyOf(builder.getDependencies()); ... ... ... } }
Have you had a look at the generated code or are those just general comments about java records / immutable objects ?
Yes I saw the MDO.
Yes I saw the MDO.
So the objects from the model are immutable, they absolutely can not be modified.
So unless we raise the requirements to JDK 17, I don't see how we could actually use records / List.of(). That said, I'm not sure there's actually much difference at the end. Of course, using records would be less verbose, but given we don't have to maintain that code manually (because it's generated), I'm not sure it really matters. For the List.of(x) point, the model already uses immutable lists, though those use Collections.unmodifiableList(xxx) to be JDK 8 compatible.
Don;t use Collections.unmodifiableList(xxx). It is vert old style - before year 2004 where the constructors where not thread safe, a typical findings with String and final fields - therefore Java changed impl for JMM of field semantics, used memory bariers at the end of constructor to make fields coherent with main mem, see JSR 133 https://download.oracle.com/otn-pub/jcp/memory_model-1.0-pfd-spec-oth-JSpec/memory_model-1_0-pfd-spec.pdf?AuthParam=1648805410_6b63e95379a8c30dbb165b2da54c9d0d
Don;t use
Collections.unmodifiableList(xxx). It is vert old style - before year 2004 where the constructors where not thread safe, a typical findings with String and final fields - therefore Java changed impl for JMM of field semantics, used memory bariers at the end of constructor to make fields coherent with main mem, see JSR 133 https://download.oracle.com/otn-pub/jcp/memory_model-1.0-pfd-spec-oth-JSpec/memory_model-1_0-pfd-spec.pdf?AuthParam=1648805410_6b63e95379a8c30dbb165b2da54c9d0d
Sorry, I don't get the point of immutable collections vs memory semantics. I agree we should use List.of(xxx) if we were to raise to JDK 17, but I'm not sure there's a consensus there. What do you propose I use instead for immutable collections ? Also, in case you missed the point, I'm always using
list != null ? Collections.unmodifiableList( new ArrayList<>( list ) ) : Collections.emptyList()
in order to make sure the collections are actually safe and immutable. I can re-implement a custom unmodifiable collection to avoid the double creation, but that's more an optimisation to me rather than an actual problem.
First question... Do we want to have a cycles between packages and interfaces?
eg:
org.apache.maven.api. Session needs org.apache.maven.api.services.ArtifactDeployer
and ArtifactDeployer needs Session.
First question... Do we want to have a cycles between packages and interfaces? eg:
org.apache.maven.api. Sessionneedsorg.apache.maven.api.services.ArtifactDeployerandArtifactDeployerneedsSession.
I can remove all the methods from the session. Those are shortcuts to improve the user-friendliness of the api. My thinking was that simple usages would just use the Session rather than having to go through the services, for example creating an Artifact, but maybe simple usages are not really so frequent...
Or we could remove the default implementations delegating to the various services from the Session interface. The Service interface could be moved up one package (along the Session). That should allow getting rid of this cycle I think.
First question... Do we want to have a cycles between packages and interfaces? eg:
org.apache.maven.api. Sessionneedsorg.apache.maven.api.services.ArtifactDeployerandArtifactDeployerneedsSession.
The cycle has been removed.
My wish is that for a future Maven release we have maven-xx-api and maven-xx-spi modules (I'm not sure if we need a single huge api module, or multiple small ones) , so plugins and extensions don't need to compile with maven-core. With this we can give them a module name, so developers that want to use a module descriptor can do so. However, that implies no split packages! Keep that in mind when choosing the package names.
IMHO also the API from https://maven.apache.org/shared/maven-shared-utils/apidocs/org/apache/maven/shared/utils/logging/package-summary.html should be at least partially contained in the API package. Otherwise you always depend on the huge maven-shared-utils library if you want to style the output of a mojo.
What about https://github.com/apache/maven/blob/2926f033cb934e7bcef51362639663bc2a39cf9a/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java and https://github.com/apache/maven/blob/896c707d324330d7d4ad92674187923945efcda9/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/DefaultLifecycleMapping.java and/or https://github.com/apache/maven/blob/master/maven-core/src/main/java/org/apache/maven/lifecycle/providers/packaging/AbstractLifecycleMappingProvider.java. IMHO those are the recommended (and only?) way to override the default lifecycles for certain types (https://books.sonatype.com/mvnref-book/reference/writing-plugins-sect-plugins-lifecycle.html). Are those supposed to be implementation details or should those be moved to API as well (compare with https://issues.apache.org/jira/browse/MNG-5697).
Is this change planned for Maven 4 only or will be also soon backported to 3.9 line?
Is this change planned for Maven 4 only or will be also soon backported to 3.9 line?
No, that's definitely a longer term goal, so maven 4 only. It would still be experimental for a couple of months / releases, the time to make sure the API is complete and stable. A transition would also be needed to switch maven-core internal to this API and reverse the current code, i.e. have the old api implemented on top of the new one, so that we can drop it completely after a period.
@gnodet Is there already a branch for any of the core plugins, to see if this API is complete? (i.e. allows to drop all other Maven dependencies)? Which plugin would be a good candidate for it?
@gnodet Is there already a branch for any of the core plugins, to see if this API is complete? (i.e. allows to drop all other Maven dependencies)? Which plugin would be a good candidate for it?
Yes, I created a mvn4 branch for each maven project, but not everything has been migrated yet.
https://github.com/apache/maven-plugin-tools/commits/mvn4
https://github.com/apache/maven-install-plugin/commits/mvn4
https://github.com/apache/maven-deploy-plugin/commits/mvn4
...
What about https://github.com/apache/maven/blob/2926f033cb934e7bcef51362639663bc2a39cf9a/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java and https://github.com/apache/maven/blob/896c707d324330d7d4ad92674187923945efcda9/maven-core/src/main/java/org/apache/maven/lifecycle/mapping/DefaultLifecycleMapping.java and/or https://github.com/apache/maven/blob/master/maven-core/src/main/java/org/apache/maven/lifecycle/providers/packaging/AbstractLifecycleMappingProvider.java. IMHO those are the recommended (and only?) way to override the default lifecycles for certain types (https://books.sonatype.com/mvnref-book/reference/writing-plugins-sect-plugins-lifecycle.html). Are those supposed to be implementation details or should those be moved to API as well (compare with https://issues.apache.org/jira/browse/MNG-5697).
The extensibility part has not been really well defined yet. I think the idea is to be really opened including whatever changes are needed for some time, but the reason I'd like to merge this PR is to be able to actually get started on other PRs, because as long as the API is not available, we can't create any PR on other projects to leverage it.
On the ArtifactHandler part, I don't think the ArtifactHandler should be visible in the API. It has to be part of the SPI which is not really defined yet.
On a related note, I think we should think whether it would make sense to split the Artifact interface into Coordinates, AttachedArtifact and ResolvedArtifact or whatever makes sense, because the Artifact interface conveys different semantics depending where it is used.
All tests are passing. I'd like to go on and merge this PR (and release an alpha version if possible). This would allow further work on other components and plugins (see the various PRs in this PR description) which are blocked because the API is not available in any public release or snapshot version.
👍 for merge and for release alpha
:+1: as well, will take a deeper look tonight... but that should not block anything
Consider using Nonnull by default, so the only place when you need to document nullability becomes @Nullable.
For instance, jspecify did not include @NonNull for a long time, and now they say @NonNull is rarely needed: https://github.com/jspecify/jspecify/blob/fa7b1d8654e5ec0696b5a8a8c49e275a2121aafd/src/main/java/org/jspecify/nullness/NonNull.java