From 2d224623a32770401e0a5a8aecf1333797f89b8e Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Sat, 28 Sep 2024 11:03:24 +0200 Subject: [PATCH] [MNG-8120] Refactor ModelBuilder and ProjectBuilder (#1700) With the introduction of the build pom and raw -> build pom transformation, the construction of the effective poms in two steps become very problematic. Over the time, multiple caches have been added to the ProjectBuilder and ModelBuilder related classes which are often redundant. This PR thus changes things and move the recursive construction of the models fully into the ModelBuilder in a single call. When building build poms, a first step is done by parsing the file models from the root, then building all needed effective models from those. All the inference can be cleanly done because the builder has all the file models ready. The result will be used by the ProjectBuilder to build the projects. --- .../java/org/apache/maven/api/Artifact.java | 13 +- .../apache/maven/api/ArtifactCoordinates.java | 6 +- .../java/org/apache/maven/api/Constants.java | 2 +- .../java/org/apache/maven/api/Dependency.java | 5 +- .../maven/api/DependencyCoordinates.java | 7 +- .../java/org/apache/maven/api/PathType.java | 2 +- .../java/org/apache/maven/api/Project.java | 7 + .../java/org/apache/maven/api/plugin/Log.java | 8 +- .../services/DependencyResolverResult.java | 2 +- .../maven/api/services/ModelBuilder.java | 7 +- .../api/services/ModelBuilderException.java | 11 +- .../api/services/ModelBuilderRequest.java | 268 +- .../api/services/ModelBuilderResult.java | 52 +- .../maven/api/services/ModelProblem.java | 5 +- .../api/services/ModelProblemCollector.java | 13 + .../api/services/ModelRepositoryHolder.java | 33 - .../maven/api/services/ModelTransformer.java | 23 +- .../api/services/ModelTransformerContext.java | 66 - .../ModelTransformerContextBuilder.java | 43 - .../services/ModelTransformerException.java | 38 - .../maven/api/spi/ModelTransformer.java | 1 - .../services/model/ModelBuildingEvent.java | 55 - .../services/model/ModelBuildingListener.java | 33 - .../api/services/model}/ModelResolver.java | 3 +- .../model}/ModelResolverException.java | 4 +- .../api/services/model/ModelValidator.java | 39 +- .../maven/api/services/model/RootLocator.java | 3 +- .../model/WorkspaceModelResolver.java | 31 - .../maven/internal/impl/AbstractSession.java | 4 +- .../impl/AetherDependencyWrapper.java | 14 +- .../internal/impl/PathModularization.java | 7 +- .../org/apache/maven/internal/impl/Utils.java | 3 +- .../impl/model/BuildModelTransformer.java | 188 -- .../model/DefaultInheritanceAssembler.java | 29 +- .../impl/model/DefaultModelBuilder.java | 2645 +++++++++-------- .../impl/model/DefaultModelBuilderResult.java | 187 +- .../impl/model/DefaultModelBuildingEvent.java | 33 - .../impl/model/DefaultModelInterpolator.java | 16 +- .../impl/model/DefaultModelProblem.java | 2 +- .../model/DefaultModelProblemCollector.java | 198 -- .../model/DefaultModelTransformerContext.java | 141 - ...DefaultModelTransformerContextBuilder.java | 247 -- .../impl/model/DefaultModelValidator.java | 204 +- .../impl/model/DefaultRootLocator.java | 12 + .../maven/internal/impl/model/ModelData.java | 1 + .../DefaultArtifactDescriptorReader.java | 25 +- .../DefaultModelRepositoryHolder.java | 112 - .../impl/resolver/DefaultModelResolver.java | 88 +- .../internal/impl/util}/PhasingExecutor.java | 18 +- .../resolver/DefaultModelResolverTest.java | 12 +- .../internal/impl/standalone/ApiRunner.java | 2 +- .../standalone/RepositorySystemSupplier.java | 9 +- .../impl/standalone/TestApiStandalone.java | 12 +- .../impl/util}/PhasingExecutorTest.java | 10 +- .../AbstractCoreMavenComponentTestCase.java | 79 +- .../project/AbstractMavenProjectTestCase.java | 28 +- .../maven/project/TestProjectBuilder.java | 16 +- .../maven/internal/impl/DefaultEvent.java | 2 +- .../maven/internal/impl/DefaultProject.java | 2 +- .../internal/impl/DefaultProjectBuilder.java | 2 +- .../maven/internal/impl/DefaultSession.java | 4 +- .../internal/impl/InternalMavenSession.java | 9 + .../impl/DefaultConsumerPomBuilder.java | 150 +- .../concurrent/BuildPlanExecutor.java | 4 +- .../project/DefaultModelBuildingListener.java | 126 - .../maven/project/DefaultProjectBuilder.java | 809 +---- .../maven/project/ReactorModelPool.java | 4 +- .../AbstractCoreMavenComponentTestCase.java | 49 +- .../org/apache/maven/MavenTestHelper.java | 21 +- .../impl/ConsumerPomBuilderTest.java | 104 +- .../apache/maven/model/ModelBuilderTest.java | 2 + .../project/AbstractMavenProjectTestCase.java | 19 +- .../DefaultMavenProjectBuilderTest.java | 12 + .../maven/project/PomConstructionTest.java | 22 +- .../maven/project/ProjectBuilderTest.java | 14 +- .../PomConstructionWithSettingsTest.java | 6 +- .../project-builder/MNG-6723/.mvn/.gitkeep | 0 .../complete-model/w-parent/pom.xml | 3 + .../complete-model/w-parent/sub/sub/pom.xml | 11 + .../complete-model/wo-parent/sub/pom.xml | 11 + .../plugin-exec-inheritance/pom.xml | 4 +- .../pom-inheritance/{sub => child-1}/pom.xml | 0 .../pom-inheritance/child-2/pom.xml | 36 + .../maven-inherit-plugin/pom.xml | 12 + .../maven-pax-plugin/pom.xml | 12 + .../profile-module-inheritance/sub/pom.xml | 2 +- .../profile-module/module-1/pom.xml | 9 + .../profile-module/module-2/pom.xml | 9 + .../profile-module/module-3/pom.xml | 9 + .../profile-module/module-4/pom.xml | 9 + .../profile-module/module-5/pom.xml | 9 + .../reporting-plugin-config/pom.xml | 2 +- .../unc-path/pom.xml | 2 +- .../test/resources/consumer/trivial/pom.xml | 2 +- .../resources/projects/tree/consumer/pom.xml | 2 +- .../test/resources/projects/tree/dep/pom.xml | 2 +- .../src/test/resources/projects/tree/pom.xml | 10 +- .../DefaultTransformerContextBuilder.java | 5 +- .../maven/model/building/ModelProblem.java | 3 - .../org/apache/maven/model/BaseObject.java | 6 +- .../repository/metadata/BaseObject.java | 6 +- .../MavenRepositorySystemSupplier.java | 8 +- .../org/apache/maven/settings/BaseObject.java | 6 +- .../maven/toolchain/model/BaseObject.java | 6 +- src/site/markdown/configuration.md | 12 +- 105 files changed, 2768 insertions(+), 3923 deletions(-) delete mode 100644 api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelRepositoryHolder.java delete mode 100644 api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContext.java delete mode 100644 api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContextBuilder.java delete mode 100644 api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerException.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingEvent.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingListener.java rename {api/maven-api-core/src/main/java/org/apache/maven/api/services => maven-api-impl/src/main/java/org/apache/maven/api/services/model}/ModelResolver.java (97%) rename {api/maven-api-core/src/main/java/org/apache/maven/api/services => maven-api-impl/src/main/java/org/apache/maven/api/services/model}/ModelResolverException.java (97%) delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/api/services/model/WorkspaceModelResolver.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/BuildModelTransformer.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuildingEvent.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblemCollector.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContext.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContextBuilder.java delete mode 100644 maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelRepositoryHolder.java rename {maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent => maven-api-impl/src/main/java/org/apache/maven/internal/impl/util}/PhasingExecutor.java (69%) rename {maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent => maven-api-impl/src/test/java/org/apache/maven/internal/impl/util}/PhasingExecutorTest.java (84%) delete mode 100644 maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java create mode 100644 maven-core/src/test/projects/project-builder/MNG-6723/.mvn/.gitkeep create mode 100644 maven-core/src/test/resources-project-builder/complete-model/w-parent/sub/sub/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/complete-model/wo-parent/sub/pom.xml rename maven-core/src/test/resources-project-builder/pom-inheritance/{sub => child-1}/pom.xml (100%) create mode 100644 maven-core/src/test/resources-project-builder/pom-inheritance/child-2/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-inherit-plugin/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-pax-plugin/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module/module-1/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module/module-2/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module/module-3/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module/module-4/pom.xml create mode 100644 maven-core/src/test/resources-project-builder/profile-module/module-5/pom.xml diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java index 0967ba92b2a8..7303e8e61c58 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java @@ -39,7 +39,7 @@ @Immutable public interface Artifact { /** - * {@return a unique identifier for this artifact}. + * {@return a unique identifier for this artifact} * The identifier is composed of groupId, artifactId, extension, classifier, and version. * * @see ArtifactCoordinates#getId() @@ -58,7 +58,7 @@ default String key() { } /** - * {@return the group identifier of the artifact}. + * {@return the group identifier of the artifact} * * @see ArtifactCoordinates#getGroupId() */ @@ -66,7 +66,7 @@ default String key() { String getGroupId(); /** - * {@return the identifier of the artifact}. + * {@return the identifier of the artifact} * * @see ArtifactCoordinates#getArtifactId() */ @@ -74,7 +74,8 @@ default String key() { String getArtifactId(); /** - * {@return the version of the artifact}. Contrarily to {@link ArtifactCoordinates}, + * {@return the version of the artifact} + * Contrarily to {@link ArtifactCoordinates}, * each {@code Artifact} is associated to a specific version instead of a range of versions. * If the {@linkplain #getBaseVersion() base version} contains a meta-version such as {@code SNAPSHOT}, * those keywords are replaced by, for example, the actual timestamp. @@ -85,7 +86,7 @@ default String key() { Version getVersion(); /** - * {@return the version or meta-version of the artifact}. + * {@return the version or meta-version of the artifact} * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. * Meta-versions are represented in a base version by their symbols (e.g., {@code SNAPSHOT}), * while they are replaced by, for example, the actual timestamp in the {@linkplain #getVersion() version}. @@ -121,7 +122,7 @@ default String key() { boolean isSnapshot(); /** - * {@return coordinates with the same identifiers as this artifact}. + * {@return coordinates with the same identifiers as this artifact} * This is a shortcut for {@code session.createArtifactCoordinates(artifact)}. * * @see org.apache.maven.api.Session#createArtifactCoordinates(Artifact) diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java index de9796796413..dab32ee48c35 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java @@ -33,13 +33,13 @@ @Immutable public interface ArtifactCoordinates { /** - * {@return the group identifier of the artifact}. + * {@return the group identifier of the artifact} */ @Nonnull String getGroupId(); /** - * {@return the identifier of the artifact}. + * {@return the identifier of the artifact} */ @Nonnull String getArtifactId(); @@ -53,7 +53,7 @@ public interface ArtifactCoordinates { String getClassifier(); /** - * {@return the specific version, range of versions or meta-version of the artifact}. + * {@return the specific version, range of versions or meta-version of the artifact} * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. */ @Nonnull diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java index 3006de198ab8..00997bd44556 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java @@ -271,7 +271,7 @@ public final class Constants { * @since 4.0.0 */ @Config(type = "java.lang.Integer", defaultValue = "cores/2 + 1") - public static final String MAVEN_PROJECT_BUILDER_PARALLELISM = "maven.projectBuilder.parallelism"; + public static final String MAVEN_MODEL_BUILDER_PARALLELISM = "maven.modelBuilder.parallelism"; /** * User property for enabling/disabling the consumer POM feature. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java index 5436a3e14391..50fbb6035327 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java @@ -36,7 +36,8 @@ @Immutable public interface Dependency extends Artifact { /** - * {@return the type of the dependency}. A dependency can be a JAR file, + * {@return the type of the dependency} + * A dependency can be a JAR file, * a modular-JAR if it is intended to be placed on the module-path, * a JAR containing test classes, etc. * @@ -46,7 +47,7 @@ public interface Dependency extends Artifact { Type getType(); /** - * {@return the time at which the dependency will be used}. + * {@return the time at which the dependency will be used} * If may be, for example, at compile time only, at run time or at test time. * * @see DependencyCoordinates#getScope() diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java index 13999eaedf73..a7330bcbb7e8 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinates.java @@ -39,7 +39,8 @@ @Immutable public interface DependencyCoordinates extends ArtifactCoordinates { /** - * {@return the type of the dependency}. A dependency can be a JAR file, + * {@return the type of the dependency} + * A dependency can be a JAR file, * a modular-JAR if it is intended to be placed on the module-path, * a JAR containing test classes, etc. */ @@ -47,7 +48,7 @@ public interface DependencyCoordinates extends ArtifactCoordinates { Type getType(); /** - * {@return the time at which the dependency will be used}. + * {@return the time at which the dependency will be used} * If may be, for example, at compile time only, at run time or at test time. */ @Nonnull @@ -62,7 +63,7 @@ public interface DependencyCoordinates extends ArtifactCoordinates { Boolean getOptional(); /** - * {@return transitive dependencies to exclude}. + * {@return transitive dependencies to exclude} */ @Nonnull Collection getExclusions(); diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java index 4672f6b49f32..368d14443cba 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java @@ -123,7 +123,7 @@ public String[] option(Iterable paths) { String name(); /** - * {@return a string representation for this extensible enum describing a path type}. + * {@return a string representation for this extensible enum describing a path type} * For example {@code "PathType[PATCH_MODULE:foo.bar]"}. */ @Nonnull diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java index 47342ce8a7ec..219e21f2469b 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java @@ -224,6 +224,13 @@ default String getId() { /** * Returns project parent project, if any. + *

+ * Note that the model may have a parent defined, but an empty parent + * project may be returned if the parent comes from a remote repository, + * as a {@code Project} must refer to a buildable project. + * + * @return an optional containing the parent project + * @see Model#getParent() */ @Nonnull Optional getParent(); diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java index 16a55a805527..f2968295e456 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java @@ -37,7 +37,7 @@ @Provider public interface Log { /** - * {@return true if the debug error level is enabled}. + * {@return true if the debug error level is enabled} */ boolean isDebugEnabled(); @@ -70,7 +70,7 @@ public interface Log { void debug(Supplier content, Throwable error); /** - * {@return true if the info error level is enabled}. + * {@return true if the info error level is enabled} */ boolean isInfoEnabled(); @@ -103,7 +103,7 @@ public interface Log { void info(Supplier content, Throwable error); /** - * {@return true if the warn error level is enabled}. + * {@return true if the warn error level is enabled} */ boolean isWarnEnabled(); @@ -136,7 +136,7 @@ public interface Log { void warn(Supplier content, Throwable error); /** - * {@return true if the error error level is enabled}. + * {@return true if the error error level is enabled} */ boolean isErrorEnabled(); diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java index c8f125eb7e6f..e2ab1c2a9e4e 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java @@ -97,7 +97,7 @@ public interface DependencyResolverResult { Map> getDispatchedPaths(); /** - * {@return all dependencies associated to their paths}. + * {@return all dependencies associated to their paths} * Some dependencies may be associated to a null value if there is no path available. */ @Nonnull diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java index dc502aeeb97b..4362353185e0 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilder.java @@ -31,9 +31,12 @@ public interface ModelBuilder extends Service { List VALID_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0); - ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException; + ModelBuilderSession newSession(); - ModelTransformerContextBuilder newTransformerContextBuilder(); + interface ModelBuilderSession { + + ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException; + } Model buildRawModel(ModelBuilderRequest request); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java index 0448904665c2..13d5176609ed 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderException.java @@ -60,10 +60,17 @@ public ModelBuilderResult getResult() { * @return The identifier of the POM or an empty string if not known, never {@code null}. */ public String getModelId() { - if (result == null || result.getModelIds().isEmpty()) { + if (result == null) { + return ""; + } else if (result.getEffectiveModel() != null) { + return result.getEffectiveModel().getId(); + } else if (result.getRawModel() != null) { + return result.getRawModel().getId(); + } else if (result.getFileModel() != null) { + return result.getFileModel().getId(); + } else { return ""; } - return result.getModelIds().get(0); } /** diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java index a72dd730498c..ccd39248b137 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java @@ -38,9 +38,6 @@ * Request used to build a {@link org.apache.maven.api.Project} using * the {@link ProjectBuilder} service. * - * TODO: replace ModelRepositoryHolder with just the enum for the strategy - * TODO: replace validation level with an enum (though, we usually need just a boolean) - * * @since 4.0.0 */ @Experimental @@ -48,36 +45,38 @@ public interface ModelBuilderRequest { /** - * Denotes minimal validation of POMs. This validation level is meant for processing of POMs from repositories - * during metadata retrieval. + * The possible request types for building a model. */ - int VALIDATION_LEVEL_MINIMAL = 0; - - /** - * Denotes validation as performed by Maven 2.0. This validation level is meant as a compatibility mode to allow - * users to migrate their projects. - */ - int VALIDATION_LEVEL_MAVEN_2_0 = 20; - - /** - * Denotes validation as performed by Maven 3.0. This validation level is meant for existing projects. - */ - int VALIDATION_LEVEL_MAVEN_3_0 = 30; + enum RequestType { + /** + * The request is for building a model from a POM file in a project on the filesystem. + */ + BUILD_POM, + /** + * The request is for building a model from a parent POM file from a downloaded artifact. + */ + PARENT_POM, + /** + * The request is for building a model from a dependency POM file from a downloaded artifact. + */ + DEPENDENCY + } /** - * Denotes validation as performed by Maven 3.1. This validation level is meant for existing projects. + * The possible merge modes for combining remote repositories. */ - int VALIDATION_LEVEL_MAVEN_3_1 = 31; + enum RepositoryMerging { - /** - * Denotes validation as performed by Maven 4.0. This validation level is meant for new projects. - */ - int VALIDATION_LEVEL_MAVEN_4_0 = 40; + /** + * The repositories declared in the POM have precedence over the repositories specified in the request. + */ + POM_DOMINANT, - /** - * Denotes strict validation as recommended by the current Maven version. - */ - int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_4_0; + /** + * The repositories specified in the request have precedence over the repositories declared in the POM. + */ + REQUEST_DOMINANT, + } @Nonnull Session getSession(); @@ -85,28 +84,12 @@ public interface ModelBuilderRequest { @Nonnull ModelSource getSource(); - int getValidationLevel(); - - boolean isTwoPhaseBuilding(); + @Nonnull + RequestType getRequestType(); boolean isLocationTracking(); - /** - * Indicates if the model to be built is a local project or a dependency. - * In case the project is loaded externally from a remote repository (as a dependency or - * even as an external parent), the POM will be parsed in a lenient way. Local POMs - * are parsed more strictly. - */ - boolean isProjectBuild(); - - /** - * Specifies whether plugin processing should take place for the built model. - * This involves merging plugins specified by the {@link org.apache.maven.api.Packaging}, - * configuration expansion (merging configuration defined globally for a given plugin - * using {@link org.apache.maven.api.model.Plugin#getConfiguration()} - * into the configuration for each {@link org.apache.maven.api.model.PluginExecution}. - */ - boolean isProcessPlugins(); + boolean isRecursive(); /** * Defines external profiles that may be activated for the given model. @@ -141,22 +124,13 @@ public interface ModelBuilderRequest { Map getUserProperties(); @Nonnull - ModelResolver getModelResolver(); - - @Nonnull - ModelRepositoryHolder getModelRepositoryHolder(); + RepositoryMerging getRepositoryMerging(); @Nullable - Object getListener(); - - @Nullable - ModelBuilderResult getInterimResult(); - - @Nullable - ModelTransformerContextBuilder getTransformerContextBuilder(); + List getRepositories(); @Nullable - List getRepositories(); + ModelTransformer getLifecycleBindingsInjector(); @Nonnull static ModelBuilderRequest build(@Nonnull ModelBuilderRequest request, @Nonnull ModelSource source) { @@ -194,45 +168,35 @@ static ModelBuilderRequestBuilder builder(ModelBuilderRequest request) { @NotThreadSafe class ModelBuilderRequestBuilder { Session session; - int validationLevel; + RequestType requestType; boolean locationTracking; - boolean twoPhaseBuilding; + boolean recursive; ModelSource source; - boolean projectBuild; - boolean processPlugins = true; Collection profiles; List activeProfileIds; List inactiveProfileIds; Map systemProperties; Map userProperties; - ModelResolver modelResolver; - ModelRepositoryHolder modelRepositoryHolder; - Object listener; - ModelBuilderResult interimResult; - ModelTransformerContextBuilder transformerContextBuilder; + RepositoryMerging repositoryMerging; List repositories; + ModelTransformer lifecycleBindingsInjector; ModelBuilderRequestBuilder() {} ModelBuilderRequestBuilder(ModelBuilderRequest request) { this.session = request.getSession(); - this.validationLevel = request.getValidationLevel(); + this.requestType = request.getRequestType(); this.locationTracking = request.isLocationTracking(); - this.twoPhaseBuilding = request.isTwoPhaseBuilding(); + this.recursive = request.isRecursive(); this.source = request.getSource(); - this.projectBuild = request.isProjectBuild(); - this.processPlugins = request.isProcessPlugins(); this.profiles = request.getProfiles(); this.activeProfileIds = request.getActiveProfileIds(); this.inactiveProfileIds = request.getInactiveProfileIds(); this.systemProperties = request.getSystemProperties(); this.userProperties = request.getUserProperties(); - this.modelResolver = request.getModelResolver(); - this.modelRepositoryHolder = request.getModelRepositoryHolder(); - this.listener = request.getListener(); - this.interimResult = request.getInterimResult(); - this.transformerContextBuilder = request.getTransformerContextBuilder(); + this.repositoryMerging = request.getRepositoryMerging(); this.repositories = request.getRepositories(); + this.lifecycleBindingsInjector = request.getLifecycleBindingsInjector(); } public ModelBuilderRequestBuilder session(Session session) { @@ -240,13 +204,8 @@ public ModelBuilderRequestBuilder session(Session session) { return this; } - public ModelBuilderRequestBuilder validationLevel(int validationLevel) { - this.validationLevel = validationLevel; - return this; - } - - public ModelBuilderRequestBuilder twoPhaseBuilding(boolean twoPhaseBuilding) { - this.twoPhaseBuilding = twoPhaseBuilding; + public ModelBuilderRequestBuilder requestType(RequestType requestType) { + this.requestType = requestType; return this; } @@ -255,18 +214,13 @@ public ModelBuilderRequestBuilder locationTracking(boolean locationTracking) { return this; } - public ModelBuilderRequestBuilder source(ModelSource source) { - this.source = source; - return this; - } - - public ModelBuilderRequestBuilder projectBuild(boolean projectBuild) { - this.projectBuild = projectBuild; + public ModelBuilderRequestBuilder recursive(boolean recursive) { + this.recursive = recursive; return this; } - public ModelBuilderRequestBuilder processPlugins(boolean processPlugins) { - this.processPlugins = processPlugins; + public ModelBuilderRequestBuilder source(ModelSource source) { + this.source = source; return this; } @@ -295,132 +249,96 @@ public ModelBuilderRequestBuilder userProperties(Map userPropert return this; } - public ModelBuilderRequestBuilder modelResolver(ModelResolver modelResolver) { - this.modelResolver = modelResolver; - return this; - } - - public ModelBuilderRequestBuilder modelRepositoryHolder(ModelRepositoryHolder modelRepositoryHolder) { - this.modelRepositoryHolder = modelRepositoryHolder; - return this; - } - - public ModelBuilderRequestBuilder listener(Object listener) { - this.listener = listener; - return this; - } - - public ModelBuilderRequestBuilder interimResult(ModelBuilderResult interimResult) { - this.interimResult = interimResult; + public ModelBuilderRequestBuilder repositoryMerging(RepositoryMerging repositoryMerging) { + this.repositoryMerging = repositoryMerging; return this; } - public ModelBuilderRequestBuilder transformerContextBuilder( - ModelTransformerContextBuilder transformerContextBuilder) { - this.transformerContextBuilder = transformerContextBuilder; + public ModelBuilderRequestBuilder repositories(List repositories) { + this.repositories = repositories; return this; } - public ModelBuilderRequestBuilder repositories(List repositories) { - this.repositories = repositories; + public ModelBuilderRequestBuilder lifecycleBindingsInjector(ModelTransformer lifecycleBindingsInjector) { + this.lifecycleBindingsInjector = lifecycleBindingsInjector; return this; } public ModelBuilderRequest build() { return new DefaultModelBuilderRequest( session, - validationLevel, + requestType, locationTracking, - twoPhaseBuilding, + recursive, source, - projectBuild, - processPlugins, profiles, activeProfileIds, inactiveProfileIds, systemProperties, userProperties, - modelResolver, - modelRepositoryHolder, - listener, - interimResult, - transformerContextBuilder, - repositories); + repositoryMerging, + repositories, + lifecycleBindingsInjector); } private static class DefaultModelBuilderRequest extends BaseRequest implements ModelBuilderRequest { - private final int validationLevel; + private final RequestType requestType; private final boolean locationTracking; - private final boolean twoPhaseBuilding; + private final boolean recursive; private final ModelSource source; - private final boolean projectBuild; - private final boolean processPlugins; private final Collection profiles; private final List activeProfileIds; private final List inactiveProfileIds; private final Map systemProperties; private final Map userProperties; - private final ModelResolver modelResolver; - private final ModelRepositoryHolder modelRepositoryHolder; - private final Object listener; - private final ModelBuilderResult interimResult; - private final ModelTransformerContextBuilder transformerContextBuilder; + private final RepositoryMerging repositoryMerging; private final List repositories; + private final ModelTransformer lifecycleBindingsInjector; @SuppressWarnings("checkstyle:ParameterNumber") DefaultModelBuilderRequest( @Nonnull Session session, - int validationLevel, + @Nonnull RequestType requestType, boolean locationTracking, - boolean twoPhaseBuilding, + boolean recursive, @Nonnull ModelSource source, - boolean projectBuild, - boolean processPlugins, Collection profiles, List activeProfileIds, List inactiveProfileIds, Map systemProperties, Map userProperties, - ModelResolver modelResolver, - ModelRepositoryHolder modelRepositoryHolder, - Object listener, - ModelBuilderResult interimResult, - ModelTransformerContextBuilder transformerContextBuilder, - List repositories) { + RepositoryMerging repositoryMerging, + List repositories, + ModelTransformer lifecycleBindingsInjector) { super(session); - this.validationLevel = validationLevel; + this.requestType = nonNull(requestType, "requestType cannot be null"); this.locationTracking = locationTracking; - this.twoPhaseBuilding = twoPhaseBuilding; + this.recursive = recursive; this.source = source; - this.projectBuild = projectBuild; - this.processPlugins = processPlugins; this.profiles = profiles != null ? List.copyOf(profiles) : List.of(); this.activeProfileIds = activeProfileIds != null ? List.copyOf(activeProfileIds) : List.of(); this.inactiveProfileIds = inactiveProfileIds != null ? List.copyOf(inactiveProfileIds) : List.of(); this.systemProperties = systemProperties != null ? Map.copyOf(systemProperties) : session.getSystemProperties(); this.userProperties = userProperties != null ? Map.copyOf(userProperties) : session.getUserProperties(); - this.modelResolver = modelResolver; - this.modelRepositoryHolder = modelRepositoryHolder; - this.listener = listener; - this.interimResult = interimResult; - this.transformerContextBuilder = transformerContextBuilder; + this.repositoryMerging = repositoryMerging; this.repositories = repositories != null ? List.copyOf(repositories) : null; + this.lifecycleBindingsInjector = lifecycleBindingsInjector; } @Override - public int getValidationLevel() { - return validationLevel; + public RequestType getRequestType() { + return requestType; } @Override - public boolean isTwoPhaseBuilding() { - return twoPhaseBuilding; + public boolean isLocationTracking() { + return locationTracking; } @Override - public boolean isLocationTracking() { - return locationTracking; + public boolean isRecursive() { + return recursive; } @Nonnull @@ -429,15 +347,6 @@ public ModelSource getSource() { return source; } - public boolean isProjectBuild() { - return projectBuild; - } - - @Override - public boolean isProcessPlugins() { - return processPlugins; - } - @Override public Collection getProfiles() { return profiles; @@ -464,31 +373,18 @@ public Map getUserProperties() { } @Override - public ModelResolver getModelResolver() { - return modelResolver; + public RepositoryMerging getRepositoryMerging() { + return repositoryMerging; } @Override - public ModelRepositoryHolder getModelRepositoryHolder() { - return modelRepositoryHolder; - } - - public Object getListener() { - return listener; - } - - @Override - public ModelBuilderResult getInterimResult() { - return interimResult; - } - - public ModelTransformerContextBuilder getTransformerContextBuilder() { - return transformerContextBuilder; + public List getRepositories() { + return repositories; } @Override - public List getRepositories() { - return repositories; + public ModelTransformer getLifecycleBindingsInjector() { + return lifecycleBindingsInjector; } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java index 4033aa237169..bf98e933911b 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java @@ -19,7 +19,6 @@ package org.apache.maven.api.services; import java.util.List; -import java.util.Optional; import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Nonnull; @@ -35,15 +34,12 @@ public interface ModelBuilderResult { /** - * Gets the sequence of model identifiers that denote the lineage of models from which the effective model was - * constructed. Model identifiers should be handled as "opaque strings" and this method should be used as source - * if navigating the linage. The first identifier from the list denotes the model on which the model builder - * was originally invoked. The last identifier will always be the super POM. + * Gets the source from which the model was read. * - * @return The model identifiers from the lineage of models, never {@code null}. + * @return The source from which the model was read, never {@code null}. */ @Nonnull - List getModelIds(); + ModelSource getSource(); /** * Gets the file model. @@ -53,14 +49,6 @@ public interface ModelBuilderResult { @Nonnull Model getFileModel(); - /** - * Returns the file model + profile injection. - * - * @return the activated file model, never {@code null}. - */ - @Nonnull - Model getActivatedFileModel(); - /** * Gets the file model + build pom transformation, without inheritance nor interpolation. * @@ -70,36 +58,28 @@ public interface ModelBuilderResult { Model getRawModel(); /** - * Gets the assembled model with inheritance, interpolation and profile injection. + * Gets the effective model of the parent POM. * - * @return The assembled model, never {@code null}. + * @return the effective model of the parent POM, never {@code null} */ @Nonnull - Model getEffectiveModel(); + Model getParentModel(); /** - * Gets the specified raw model as it was read from a model source. Apart from basic validation, a raw model has not - * undergone any updates by the model builder, e.g. reflects neither inheritance nor interpolation. The model - * identifier should be from the collection obtained by {@link #getModelIds()}. + * Gets the assembled model with inheritance, interpolation and profile injection. * - * @see #getModelIds() - * @param modelId The identifier of the desired raw model, must not be {@code null}. - * @return The raw model or {@code null} if the specified model id does not refer to a known model. + * @return The assembled model, never {@code null}. */ @Nonnull - Optional getRawModel(@Nonnull String modelId); + Model getEffectiveModel(); /** - * Gets the profiles from the specified model that were active during model building. The model identifier should be - * from the collection obtained by {@link #getModelIds()}. + * Gets the profiles that were active during model building. * - * @see #getModelIds() - * @param modelId The identifier of the model whose active profiles should be retrieved, must not be {@code null}. - * @return The active profiles of the model or an empty list if the specified model id does - * not refer to a known model or has no active profiles. + * @return The active profiles of the model or an empty list if the model has no active profiles. */ @Nonnull - List getActivePomProfiles(@Nonnull String modelId); + List getActivePomProfiles(); /** * Gets the external profiles that were active during model building. External profiles are those that were @@ -118,6 +98,14 @@ public interface ModelBuilderResult { @Nonnull List getProblems(); + /** + * Gets the children of this result. + * + * @return the children of this result, can be empty but never {@code null} + */ + @Nonnull + List getChildren(); + /** * Creates a human-readable representation of these errors. */ diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java index eda871cb9cec..f0741828813e 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblem.java @@ -18,6 +18,8 @@ */ package org.apache.maven.api.services; +import org.apache.maven.api.annotations.Nonnull; + /** * Describes a problem that was encountered during model building. A problem can either be an exception that was thrown * or a simple string message. In addition, a problem carries a hint about its source, e.g. the POM file that exhibits @@ -44,15 +46,16 @@ enum Version { * information that is available at the point the problem occurs and as such merely serves as best effort * to provide information to the user to track the problem back to its origin. * - * @see ModelBuilderResult#getModelIds() * @return The identifier of the model from which the problem originated or an empty string if unknown, never * {@code null}. */ + @Nonnull String getModelId(); /** * Gets the applicable maven version/validation level of this problem * @return The version, never {@code null}. */ + @Nonnull Version getVersion(); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java index 06d68b09d28d..9ef9aa861ed3 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelProblemCollector.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.maven.api.model.InputLocation; +import org.apache.maven.api.model.Model; /** * Collects problems that are encountered during model building. The primary purpose of this component is to account for @@ -64,4 +65,16 @@ void add( Exception exception); void add(ModelProblem problem); + + ModelBuilderException newModelBuilderException(); + + void setSource(String location); + + void setSource(Model model); + + String getSource(); + + void setRootModel(Model model); + + Model getRootModel(); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelRepositoryHolder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelRepositoryHolder.java deleted file mode 100644 index 3be93256bebc..000000000000 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelRepositoryHolder.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services; - -import java.util.List; - -import org.apache.maven.api.RemoteRepository; -import org.apache.maven.api.model.Repository; - -public interface ModelRepositoryHolder { - - void merge(List repos, boolean replace); - - List getRepositories(); - - ModelRepositoryHolder copy(); -} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java index 0c17ae41bc3a..5d2503ec31bc 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformer.java @@ -18,27 +18,26 @@ */ package org.apache.maven.api.services; -import java.nio.file.Path; - +import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.model.Model; /** - * The ModelTransformer is a way to transform the local pom while streaming the input. - * - * The {@link #transform(ModelTransformerContext, Model, Path)} method uses a Path on purpose, to ensure the - * local pom is the original source. + * A model transformer. * * @since 4.0.0 */ +@Experimental public interface ModelTransformer { + /** - * @param context the context, cannot be null - * @param model the model to transform - * @param path the pom file, cannot be null - * @throws ModelTransformerException if the transformation fails + * Apply a transformation on the file model. + * + * @param model the input model + * @param problems the problem collector to report any issues encountered during transformation + * @return the transformed model, or the input model if no transformation is needed */ @Nonnull - Model transform(@Nonnull ModelTransformerContext context, @Nonnull Model model, @Nonnull Path path) - throws ModelTransformerException; + Model transform( + @Nonnull Model model, @Nonnull ModelBuilderRequest request, @Nonnull ModelProblemCollector problems); } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContext.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContext.java deleted file mode 100644 index a6e31720fae6..000000000000 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContext.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services; - -import java.nio.file.Path; - -import org.apache.maven.api.model.Model; - -/** - * Context used to transform a pom file. - * - * @since 4.0.0 - */ -public interface ModelTransformerContext { - - /** - * Key to get the TransformerContext from the SessionData - */ - Object KEY = ModelTransformerContext.class; - - /** - * Get the value of the Maven user property. - */ - String getUserProperty(String key); - - /** - * Get the model based on the path when resolving the parent based on relativePath. - * - * @param from the requiring model - * @param pomFile the path to the pomFile - * @return the model, otherwise {@code null} - */ - Model getRawModel(Path from, Path pomFile); - - /** - * Get the model from the reactor based on the groupId and artifactId when resolving reactor dependencies. - * - * @param from the requiring model - * @param groupId the groupId - * @param artifactId the artifactId - * @return the model, otherwise {@code null} - * @throws IllegalStateException if multiple versions of the same GA are part of the reactor - */ - Model getRawModel(Path from, String groupId, String artifactId); - - /** - * Locate the POM file inside the given directory. - */ - Path locate(Path path); -} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContextBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContextBuilder.java deleted file mode 100644 index 9efb6e63eeb8..000000000000 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerContextBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services; - -/** - * The transformerContextBuilder is responsible for initializing the TransformerContext. - * In case rawModels are missing, it could do new buildingRequests on the ModelBuilder. - * - * @since 4.0.0 - */ -public interface ModelTransformerContextBuilder { - /** - * This method is used to initialize the TransformerContext - * - * @param request the modelBuildingRequest - * @param problems the problemCollector - * @return the mutable transformerContext - */ - ModelTransformerContext initialize(ModelBuilderRequest request, ModelProblemCollector problems); - - /** - * The immutable transformerContext, can be used after the buildplan is finished. - * - * @return the immutable transformerContext - */ - ModelTransformerContext build(); -} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerException.java deleted file mode 100644 index c68456beb433..000000000000 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelTransformerException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services; - -import org.apache.maven.api.annotations.Experimental; - -/** - * Exception thrown when a {@link ModelTransformer} fails. - * - * @since 4.0.0 - */ -@Experimental -public class ModelTransformerException extends MavenException { - - public ModelTransformerException(Exception e) { - super(e); - } - - public ModelTransformerException(String message, Throwable exception) { - super(message, exception); - } -} diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java index d37982993f2a..525b353c6a5c 100644 --- a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ModelTransformer.java @@ -23,7 +23,6 @@ import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.di.Named; import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.ModelTransformerException; /** * Marker interface for model transformers. diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingEvent.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingEvent.java deleted file mode 100644 index f2d8bf861984..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingEvent.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services.model; - -import java.util.function.Consumer; - -import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.ModelBuilderRequest; -import org.apache.maven.api.services.ModelProblemCollector; - -/** - * Holds data relevant for a model building event. - * - */ -public interface ModelBuildingEvent { - - /** - * Gets the model being built. The precise state of this model depends on the event being fired. - * - * @return The model being built, never {@code null}. - */ - Model model(); - - Consumer update(); - - /** - * Gets the model building request being processed. - * - * @return The model building request being processed, never {@code null}. - */ - ModelBuilderRequest request(); - - /** - * Gets the container used to collect problems that were encountered while processing the event. - * - * @return The container used to collect problems that were encountered, never {@code null}. - */ - ModelProblemCollector problems(); -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingListener.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingListener.java deleted file mode 100644 index a0c2bfb4a768..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelBuildingListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services.model; - -/** - * Defines events that the model builder fires during construction of the effective model. When a listener encounters - * errors while processing the event, it can report these problems via {@link ModelBuildingEvent#problems()}. - */ -public interface ModelBuildingListener { - - /** - * Notifies the listener that the model has been constructed to the extent where build extensions can be processed. - * - * @param event The details about the event. - */ - default void buildExtensionsAssembled(ModelBuildingEvent event) {} -} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelResolver.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java similarity index 97% rename from api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelResolver.java rename to maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java index 9bec73491766..b3a0e2332edc 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelResolver.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.api.services; +package org.apache.maven.api.services.model; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -29,6 +29,7 @@ import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.model.Dependency; import org.apache.maven.api.model.Parent; +import org.apache.maven.api.services.ModelSource; /** * Resolves a POM from its coordinates. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelResolverException.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelResolverException.java similarity index 97% rename from api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelResolverException.java rename to maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelResolverException.java index 167e67070a9f..0c9f829210fb 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelResolverException.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelResolverException.java @@ -16,7 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.api.services; +package org.apache.maven.api.services.model; + +import org.apache.maven.api.services.MavenException; /** * Signals an error when resolving the path to an external model. diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java index de45f9d5a5ba..40a8e813de64 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java @@ -27,15 +27,44 @@ * */ public interface ModelValidator { + /** + * Denotes minimal validation of POMs. This validation level is meant for processing of POMs from repositories + * during metadata retrieval. + */ + int VALIDATION_LEVEL_MINIMAL = 0; + /** + * Denotes validation as performed by Maven 2.0. This validation level is meant as a compatibility mode to allow + * users to migrate their projects. + */ + int VALIDATION_LEVEL_MAVEN_2_0 = 20; + /** + * Denotes validation as performed by Maven 3.0. This validation level is meant for existing projects. + */ + int VALIDATION_LEVEL_MAVEN_3_0 = 30; + /** + * Denotes validation as performed by Maven 3.1. This validation level is meant for existing projects. + */ + int VALIDATION_LEVEL_MAVEN_3_1 = 31; + /** + * Denotes validation as performed by Maven 4.0. This validation level is meant for new projects. + */ + int VALIDATION_LEVEL_MAVEN_4_0 = 40; + /** + * Denotes strict validation as recommended by the current Maven version. + */ + int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_4_0; + /** * Checks the specified file model for missing or invalid values. This model is directly created from the POM * file and has not been subjected to inheritance, interpolation or profile/default injection. * * @param model The model to validate, must not be {@code null}. + * @param validationLevel The validation level. * @param request The model building request that holds further settings, must not be {@code null}. * @param problems The container used to collect problems that were encountered, must not be {@code null}. */ - default void validateFileModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems) { + default void validateFileModel( + Model model, int validationLevel, ModelBuilderRequest request, ModelProblemCollector problems) { // do nothing } @@ -44,18 +73,22 @@ default void validateFileModel(Model model, ModelBuilderRequest request, ModelPr * transformation and has not been subjected to inheritance, interpolation or profile/default injection. * * @param model The model to validate, must not be {@code null}. + * @param validationLevel The validation level. * @param request The model building request that holds further settings, must not be {@code null}. * @param problems The container used to collect problems that were encountered, must not be {@code null}. */ - void validateRawModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems); + void validateRawModel( + Model model, int validationLevel, ModelBuilderRequest request, ModelProblemCollector problems); /** * Checks the specified (effective) model for missing or invalid values. The effective model is fully assembled and * has undergone inheritance, interpolation and other model operations. * * @param model The model to validate, must not be {@code null}. + * @param validationLevel The validation level. * @param request The model building request that holds further settings, must not be {@code null}. * @param problems The container used to collect problems that were encountered, must not be {@code null}. */ - void validateEffectiveModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems); + void validateEffectiveModel( + Model model, int validationLevel, ModelBuilderRequest request, ModelProblemCollector problems); } diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java index 505dbbe12a19..7f7d177a805a 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java @@ -20,6 +20,7 @@ import java.nio.file.Path; +import org.apache.maven.api.Service; import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.Nullable; @@ -34,7 +35,7 @@ * The default implementation will look for a {@code .mvn} child directory * or a {@code pom.xml} containing the {@code root="true"} attribute. */ -public interface RootLocator { +public interface RootLocator extends Service { String UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE = "Unable to find the root directory. " + "Create a .mvn directory in the root directory or add the root=\"true\"" diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/WorkspaceModelResolver.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/WorkspaceModelResolver.java deleted file mode 100644 index 4dd18968d567..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/WorkspaceModelResolver.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.api.services.model; - -import org.apache.maven.api.model.Model; - -/** - * WorkspaceModelResolver - */ -public interface WorkspaceModelResolver { - - Model resolveRawModel(String groupId, String artifactId, String versionConstraint); - - Model resolveEffectiveModel(String groupId, String artifactId, String versionConstraint); -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java index ece39133d3af..b92ec7b8ee08 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java @@ -297,9 +297,7 @@ private Service lookup(Class c) { try { return lookup.lookup(c); } catch (LookupException e) { - NoSuchElementException nsee = new NoSuchElementException(c.getName()); - e.initCause(e); - throw nsee; + throw new NoSuchElementException(c.getName(), e); } } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AetherDependencyWrapper.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AetherDependencyWrapper.java index e6d130de42f9..39bf82517431 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AetherDependencyWrapper.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/AetherDependencyWrapper.java @@ -58,7 +58,7 @@ abstract class AetherDependencyWrapper { } /** - * {@return the group identifier of the wrapped dependency}. + * {@return the group identifier of the wrapped dependency} * The default implementation delegates to the Eclipse Aether artifact. */ public String getGroupId() { @@ -66,7 +66,7 @@ public String getGroupId() { } /** - * {@return the artifact identifier of the wrapped dependency}. + * {@return the artifact identifier of the wrapped dependency} * The default implementation delegates to the Eclipse Aether artifact. */ public String getArtifactId() { @@ -74,7 +74,7 @@ public String getArtifactId() { } /** - * {@return the file extension of the wrapped dependency}. + * {@return the file extension of the wrapped dependency} * The default implementation delegates to the Eclipse Aether artifact. */ public String getExtension() { @@ -82,7 +82,7 @@ public String getExtension() { } /** - * {@return the type of the wrapped dependency}. + * {@return the type of the wrapped dependency} * The default implementation infers the type from the properties associated to the Eclipse Aether artifact. */ public Type getType() { @@ -91,7 +91,7 @@ public Type getType() { } /** - * {@return the classifier ("jar", "test-jar", …) of the wrapped dependency}. + * {@return the classifier ("jar", "test-jar", …) of the wrapped dependency} * The default implementation first delegates to the Eclipse Aether artifact. * If the latter does not provide a non-empty classifier, * then the default value is determined by {@linkplain #getType() type}. @@ -109,7 +109,7 @@ public String getClassifier() { } /** - * {@return the scope (compile, test, …) of this dependency}. + * {@return the scope (compile, test, …) of this dependency} */ @Nonnull public DependencyScope getScope() { @@ -117,7 +117,7 @@ public DependencyScope getScope() { } /** - * {@return a string representation of this dependency}. + * {@return a string representation of this dependency} * This is for debugging purposes only and may change in any future version. */ @Override diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/PathModularization.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/PathModularization.java index d8e9e941ddcf..1b3cf9b0071b 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/PathModularization.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/PathModularization.java @@ -222,7 +222,8 @@ private PathModularization() { } /** - * {@return the type of path detected}. The return value is {@link JavaPathType#MODULES} + * {@return the type of path detected} + * The return value is {@link JavaPathType#MODULES} * if the dependency is a modular JAR file or a directory containing module descriptor(s), * or {@link JavaPathType#CLASSES} otherwise. A JAR file without module descriptor but with * an "Automatic-Module-Name" manifest attribute is considered modular. @@ -246,14 +247,14 @@ public void addIfFilenameBasedAutomodules(Collection automodulesDetected } /** - * {@return whether the dependency contains a module of the given name}. + * {@return whether the dependency contains a module of the given name} */ public boolean containsModule(String name) { return descriptors.containsValue(name); } /** - * {@return a string representation of this object for debugging purposes}. + * {@return a string representation of this object for debugging purposes} * This string representation may change in any future version. */ @Override diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/Utils.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/Utils.java index 0d0d43c32f93..2fb4ad3d338c 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/Utils.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/Utils.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; @@ -49,6 +50,6 @@ static T cast(Class clazz, Object o, String name) { } static List map(Collection list, Function mapper) { - return list.stream().map(mapper).collect(Collectors.toList()); + return list.stream().map(mapper).filter(Objects::nonNull).collect(Collectors.toList()); } } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/BuildModelTransformer.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/BuildModelTransformer.java deleted file mode 100644 index b879649dfeb7..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/BuildModelTransformer.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.internal.impl.model; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import org.apache.maven.api.di.Named; -import org.apache.maven.api.di.Singleton; -import org.apache.maven.api.model.Dependency; -import org.apache.maven.api.model.InputLocation; -import org.apache.maven.api.model.Model; -import org.apache.maven.api.model.Parent; -import org.apache.maven.api.services.ModelTransformer; -import org.apache.maven.api.services.ModelTransformerContext; - -/** - * ModelSourceTransformer for the build pom - * - * @since 4.0.0 - */ -@Named -@Singleton -public class BuildModelTransformer implements ModelTransformer { - - @Override - public Model transform(ModelTransformerContext context, Model model, Path path) { - Model.Builder builder = Model.newBuilder(model); - handleParent(context, model, path, builder); - handleReactorDependencies(context, model, path, builder); - handleCiFriendlyVersion(context, model, path, builder); - return builder.build(); - } - - // - // Infer parent information - // - void handleParent(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) { - Parent parent = model.getParent(); - if (parent != null) { - String version = parent.getVersion(); - - // CI Friendly version for parent - String modVersion = replaceCiFriendlyVersion(context, version); - - // Update parent - builder.parent(parent.with().version(modVersion).build()); - } - } - - // - // CI friendly versions - // - void handleCiFriendlyVersion(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) { - String version = model.getVersion(); - String modVersion = replaceCiFriendlyVersion(context, version); - builder.version(modVersion); - } - - // - // Infer inner reactor dependencies version - // - void handleReactorDependencies(ModelTransformerContext context, Model model, Path pomFile, Model.Builder builder) { - List newDeps = new ArrayList<>(); - boolean modified = false; - for (Dependency dep : model.getDependencies()) { - Dependency.Builder depBuilder = null; - if (dep.getVersion() == null) { - Model depModel = context.getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId()); - if (depModel != null) { - String version = depModel.getVersion(); - InputLocation versionLocation = depModel.getLocation("version"); - if (version == null && depModel.getParent() != null) { - version = depModel.getParent().getVersion(); - versionLocation = depModel.getParent().getLocation("version"); - } - depBuilder = Dependency.newBuilder(dep); - depBuilder.version(version).location("version", versionLocation); - if (dep.getGroupId() == null) { - String depGroupId = depModel.getGroupId(); - InputLocation groupIdLocation = depModel.getLocation("groupId"); - if (depGroupId == null && depModel.getParent() != null) { - depGroupId = depModel.getParent().getGroupId(); - groupIdLocation = depModel.getParent().getLocation("groupId"); - } - depBuilder.groupId(depGroupId).location("groupId", groupIdLocation); - } - } - } - if (depBuilder != null) { - newDeps.add(depBuilder.build()); - modified = true; - } else { - newDeps.add(dep); - } - } - if (modified) { - builder.dependencies(newDeps); - } - } - - protected String replaceCiFriendlyVersion(ModelTransformerContext context, String version) { - if (version != null) { - for (String key : Arrays.asList("changelist", "revision", "sha1")) { - String val = context.getUserProperty(key); - if (val != null) { - version = version.replace("${" + key + "}", val); - } - } - } - return version; - } - - protected Optional resolveRelativePath( - Path pomFile, ModelTransformerContext context, Path relativePath, String groupId, String artifactId) { - Path pomPath = pomFile.resolveSibling(relativePath).normalize(); - if (Files.isDirectory(pomPath)) { - pomPath = context.locate(pomPath); - } - - if (pomPath == null || !Files.isRegularFile(pomPath)) { - return Optional.empty(); - } - - return Optional.ofNullable(context.getRawModel(pomFile, pomPath.normalize())) - .map(BuildModelTransformer::toRelativeProject); - } - - private static RelativeProject toRelativeProject(final Model m) { - String groupId = m.getGroupId(); - if (groupId == null && m.getParent() != null) { - groupId = m.getParent().getGroupId(); - } - - String version = m.getVersion(); - if (version == null && m.getParent() != null) { - version = m.getParent().getVersion(); - } - - return new RelativeProject(groupId, m.getArtifactId(), version); - } - - protected static class RelativeProject { - private final String groupId; - - private final String artifactId; - - private final String version; - - protected RelativeProject(String groupId, String artifactId, String version) { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - } - - public String getGroupId() { - return groupId; - } - - public String getArtifactId() { - return artifactId; - } - - public String getVersion() { - return version; - } - } -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultInheritanceAssembler.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultInheritanceAssembler.java index 43d75e184bee..5c4040aa4e53 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultInheritanceAssembler.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultInheritanceAssembler.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; +import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Singleton; import org.apache.maven.api.model.InputLocation; @@ -36,6 +37,7 @@ import org.apache.maven.api.services.ModelBuilderRequest; import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.model.InheritanceAssembler; +import org.apache.maven.model.v4.MavenMerger; /** * Handles inheritance of model values. @@ -50,7 +52,16 @@ public class DefaultInheritanceAssembler implements InheritanceAssembler { private static final String CHILD_DIRECTORY_PROPERTY = "project.directory"; - private final InheritanceModelMerger merger = new InheritanceModelMerger(); + private final MavenMerger merger; + + @Inject + public DefaultInheritanceAssembler() { + this(new InheritanceModelMerger()); + } + + public DefaultInheritanceAssembler(MavenMerger merger) { + this.merger = merger; + } @Override public Model assembleModelInheritance( @@ -134,17 +145,11 @@ protected String extrapolateChildUrl(String parentUrl, boolean appendPath, Map transformers; + private final List transformers; private final ModelCacheFactory modelCacheFactory; + private final ModelResolver modelResolver; @SuppressWarnings("checkstyle:ParameterNumber") @Inject @@ -165,13 +170,12 @@ public DefaultModelBuilder( PluginManagementInjector pluginManagementInjector, DependencyManagementInjector dependencyManagementInjector, DependencyManagementImporter dependencyManagementImporter, - @Nullable LifecycleBindingsInjector lifecycleBindingsInjector, PluginConfigurationExpander pluginConfigurationExpander, ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator, - ModelTransformer transformer, ModelVersionParser versionParser, - List transformers, - ModelCacheFactory modelCacheFactory) { + List transformers, + ModelCacheFactory modelCacheFactory, + ModelResolver modelResolver) { this.modelProcessor = modelProcessor; this.modelValidator = modelValidator; this.modelNormalizer = modelNormalizer; @@ -185,274 +189,1510 @@ public DefaultModelBuilder( this.pluginManagementInjector = pluginManagementInjector; this.dependencyManagementInjector = dependencyManagementInjector; this.dependencyManagementImporter = dependencyManagementImporter; - this.lifecycleBindingsInjector = lifecycleBindingsInjector; this.pluginConfigurationExpander = pluginConfigurationExpander; this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; - this.transformer = transformer; this.versionParser = versionParser; this.transformers = transformers; this.modelCacheFactory = modelCacheFactory; + this.modelResolver = modelResolver; } - @Override - public ModelTransformerContextBuilder newTransformerContextBuilder() { - return new DefaultModelTransformerContextBuilder(this); + public ModelBuilderSession newSession() { + return new ModelBuilderSession() { + DefaultModelBuilderSession mainSession; + + /** + * Builds a model based on the provided ModelBuilderRequest. + * + * @param request The request containing the parameters for building the model. + * @return The result of the model building process. + * @throws ModelBuilderException If an error occurs during model building. + */ + @Override + public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException { + // Create or derive a session based on the request + DefaultModelBuilderSession session; + if (mainSession == null) { + mainSession = new DefaultModelBuilderSession(request); + session = mainSession; + } else { + session = mainSession.derive(request, new DefaultModelBuilderResult()); + } + // Build the request + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { + // build the build poms + session.buildBuildPom(); + } else { + // simply build the effective model + session.buildEffectiveModel(new LinkedHashSet<>()); + } + return session.result; + } + }; } - @Override - public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException { - request = fillRequestDefaults(request); - if (request.getInterimResult() != null) { - return build(request, request.getInterimResult(), new LinkedHashSet<>()); - } else { - return build(request, new LinkedHashSet<>()); - } - } + protected class DefaultModelBuilderSession implements ModelProblemCollector { + final Session session; + final ModelBuilderRequest request; + final DefaultModelBuilderResult result; + final ModelCache cache; + final Graph dag; + final Map> mappedSources; + + String source; + Model sourceModel; + Model rootModel; + + List pomRepositories; + List externalRepositories; + List repositories; - private static ModelBuilderRequest fillRequestDefaults(ModelBuilderRequest request) { - ModelBuilderRequest.ModelBuilderRequestBuilder builder = ModelBuilderRequest.builder(request); - if (request.getModelRepositoryHolder() == null) { - builder.modelRepositoryHolder(new DefaultModelRepositoryHolder( + DefaultModelBuilderSession(ModelBuilderRequest request) { + this( request.getSession(), - DefaultModelRepositoryHolder.RepositoryMerging.POM_DOMINANT, - request.getSession().getRemoteRepositories())); + request, + new DefaultModelBuilderResult(), + request.getSession() + .getData() + .computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance), + new Graph(), + new ConcurrentHashMap<>(64), + List.of(), + repos(request), + repos(request)); } - if (request.getModelResolver() == null) { - builder.modelResolver(new DefaultModelResolver()); + + static List repos(ModelBuilderRequest request) { + return List.copyOf( + request.getRepositories() != null + ? request.getRepositories() + : request.getSession().getRemoteRepositories()); } - return builder.build(); - } - protected ModelBuilderResult build(ModelBuilderRequest request, Collection importIds) - throws ModelBuilderException { - // phase 1 - DefaultModelBuilderResult result = new DefaultModelBuilderResult(); + @SuppressWarnings("checkstyle:ParameterNumber") + private DefaultModelBuilderSession( + Session session, + ModelBuilderRequest request, + DefaultModelBuilderResult result, + ModelCache cache, + Graph dag, + Map> mappedSources, + List pomRepositories, + List externalRepositories, + List repositories) { + this.session = session; + this.request = request; + this.result = result; + this.cache = cache; + this.dag = dag; + this.mappedSources = mappedSources; + this.pomRepositories = pomRepositories; + this.externalRepositories = externalRepositories; + this.repositories = repositories; + this.result.setSource(this.request.getSource()); + } + + DefaultModelBuilderSession derive(ModelSource source) { + return derive(source, new DefaultModelBuilderResult(result)); + } + + DefaultModelBuilderSession derive(ModelSource source, DefaultModelBuilderResult result) { + return derive(ModelBuilderRequest.build(request, source), result); + } + + /** + * Creates a new session, sharing cached datas and propagating errors. + */ + DefaultModelBuilderSession derive(ModelBuilderRequest request) { + return derive(request, new DefaultModelBuilderResult(result)); + } + + DefaultModelBuilderSession derive(ModelBuilderRequest request, DefaultModelBuilderResult result) { + if (session != request.getSession()) { + throw new IllegalArgumentException("Session mismatch"); + } + return new DefaultModelBuilderSession( + session, + request, + result, + cache, + dag, + mappedSources, + pomRepositories, + externalRepositories, + repositories); + } + + @Override + public String toString() { + return "ModelBuilderSession[" + "session=" + + session + ", " + "request=" + + request + ", " + "result=" + + result + ", " + "cache=" + + cache + ']'; + } + + PhasingExecutor createExecutor() { + return new PhasingExecutor(Executors.newFixedThreadPool(getParallelism())); + } + + private int getParallelism() { + int parallelism = Runtime.getRuntime().availableProcessors() / 2 + 1; + try { + String str = request.getUserProperties().get(Constants.MAVEN_MODEL_BUILDER_PARALLELISM); + if (str != null) { + parallelism = Integer.parseInt(str); + } + } catch (Exception e) { + // ignore + } + return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors())); + } + + public Model getRawModel(Path from, String groupId, String artifactId) { + ModelSource source = getSource(groupId, artifactId); + if (source != null) { + if (addEdge(from, source.getPath())) { + return null; + } + try { + return derive(source).readRawModel(); + } catch (ModelBuilderException e) { + // gathered with problem collector + } + } + return null; + } + + public Model getRawModel(Path from, Path path) { + if (!Files.isRegularFile(path)) { + throw new IllegalArgumentException("Not a regular file: " + path); + } + if (addEdge(from, path)) { + return null; + } + try { + return derive(ModelSource.fromPath(path)).readRawModel(); + } catch (ModelBuilderException e) { + // gathered with problem collector + } + return null; + } + + /** + * Returns false if the edge was added, true if it caused a cycle. + */ + private boolean addEdge(Path from, Path p) { + try { + dag.addEdge(from.toString(), p.toString()); + return false; + } catch (Graph.CycleDetectedException e) { + add( + Severity.FATAL, + ModelProblem.Version.BASE, + "Cycle detected between models at " + from + " and " + p, + null, + e); + return true; + } + } + + public ModelSource getSource(String groupId, String artifactId) { + Set sources = mappedSources.get(new GAKey(groupId, artifactId)); + if (sources != null) { + return sources.stream() + .reduce((a, b) -> { + throw new IllegalStateException(String.format( + "No unique Source for %s:%s: %s and %s", + groupId, artifactId, a.getLocation(), b.getLocation())); + }) + .orElse(null); + } + return null; + } + + public void putSource(String groupId, String artifactId, ModelSource source) { + mappedSources + .computeIfAbsent(new GAKey(groupId, artifactId), k -> new HashSet<>()) + .add(source); + // Also register the source under the null groupId + if (groupId != null) { + putSource(null, artifactId, source); + } + } + + public boolean hasFatalErrors() { + return result.getProblems().stream().anyMatch(p -> p.getSeverity() == ModelProblem.Severity.FATAL); + } + + public boolean hasErrors() { + return result.getProblems().stream() + .anyMatch(p -> p.getSeverity() == ModelProblem.Severity.FATAL + || p.getSeverity() == ModelProblem.Severity.ERROR); + } + + @Override + public List getProblems() { + return result.getProblems(); + } + + public void setSource(String source) { + this.source = source; + this.sourceModel = null; + } + + public void setSource(Model source) { + this.sourceModel = source; + this.source = null; + + if (rootModel == null) { + rootModel = source; + } + } + + public String getSource() { + if (source == null && sourceModel != null) { + source = ModelProblemUtils.toPath(sourceModel); + } + return source; + } + + private String getModelId() { + return ModelProblemUtils.toId(sourceModel); + } + + public void setRootModel(Model rootModel) { + this.rootModel = rootModel; + } + + public Model getRootModel() { + return rootModel; + } + + @Override + public void add(ModelProblem problem) { + result.addProblem(problem); + } + + @Override + public void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) { + add(severity, version, message, null, null); + } + + @Override + public void add( + BuilderProblem.Severity severity, + ModelProblem.Version version, + String message, + InputLocation location) { + add(severity, version, message, location, null); + } + + @Override + public void add( + BuilderProblem.Severity severity, ModelProblem.Version version, String message, Exception exception) { + add(severity, version, message, null, exception); + } + + public void add( + BuilderProblem.Severity severity, + ModelProblem.Version version, + String message, + InputLocation location, + Exception exception) { + int line = -1; + int column = -1; + String source = null; + String modelId = null; + + if (location != null) { + line = location.getLineNumber(); + column = location.getColumnNumber(); + if (location.getSource() != null) { + modelId = location.getSource().getModelId(); + source = location.getSource().getLocation(); + } + } + + if (modelId == null) { + modelId = getModelId(); + source = getSource(); + } + + if (line <= 0 && column <= 0 && exception instanceof ModelParserException e) { + line = e.getLineNumber(); + column = e.getColumnNumber(); + } + + ModelProblem problem = + new DefaultModelProblem(message, severity, version, source, line, column, modelId, exception); + + add(problem); + } + + public ModelBuilderException newModelBuilderException() { + return new ModelBuilderException(result); + } + + public void mergeRepositories(List toAdd, boolean replace) { + List repos = + toAdd.stream().map(session::createRemoteRepository).toList(); + if (replace) { + Set ids = repos.stream().map(RemoteRepository::getId).collect(Collectors.toSet()); + repositories = repositories.stream() + .filter(r -> !ids.contains(r.getId())) + .toList(); + pomRepositories = pomRepositories.stream() + .filter(r -> !ids.contains(r.getId())) + .toList(); + } else { + Set ids = + pomRepositories.stream().map(RemoteRepository::getId).collect(Collectors.toSet()); + repos = repos.stream().filter(r -> !ids.contains(r.getId())).toList(); + } + + RepositoryFactory repositoryFactory = session.getService(RepositoryFactory.class); + if (request.getRepositoryMerging() == ModelBuilderRequest.RepositoryMerging.REQUEST_DOMINANT) { + repositories = repositoryFactory.aggregate(session, repositories, repos, true); + pomRepositories = repositories; + } else { + pomRepositories = repositoryFactory.aggregate(session, pomRepositories, repos, true); + repositories = repositoryFactory.aggregate(session, pomRepositories, externalRepositories, false); + } + } + + // + // Transform raw model to build pom + // + Model transformFileToRaw(Model model) { + Model.Builder builder = Model.newBuilder(model); + builder = handleParent(model, builder); + builder = handleReactorDependencies(model, builder); + builder = handleCiFriendlyVersion(model, builder); + return builder.build(); + } + + // + // Infer parent information + // + Model.Builder handleParent(Model model, Model.Builder builder) { + Parent parent = model.getParent(); + if (parent != null) { + String version = parent.getVersion(); + String modVersion = replaceCiFriendlyVersion(version); + if (!Objects.equals(version, modVersion)) { + if (builder == null) { + builder = Model.newBuilder(model); + } + builder.parent(parent.withVersion(modVersion)); + } + } + return builder; + } + + // + // CI friendly versions + // + Model.Builder handleCiFriendlyVersion(Model model, Model.Builder builder) { + String version = model.getVersion(); + String modVersion = replaceCiFriendlyVersion(version); + if (!Objects.equals(version, modVersion)) { + if (builder == null) { + builder = Model.newBuilder(model); + } + builder.version(modVersion); + } + return builder; + } + + // + // Infer inner reactor dependencies version + // + Model.Builder handleReactorDependencies(Model model, Model.Builder builder) { + List newDeps = new ArrayList<>(); + boolean modified = false; + for (Dependency dep : model.getDependencies()) { + Dependency.Builder depBuilder = null; + if (dep.getVersion() == null) { + Model depModel = getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId()); + if (depModel != null) { + String version = depModel.getVersion(); + InputLocation versionLocation = depModel.getLocation("version"); + if (version == null && depModel.getParent() != null) { + version = depModel.getParent().getVersion(); + versionLocation = depModel.getParent().getLocation("version"); + } + depBuilder = Dependency.newBuilder(dep); + depBuilder.version(version).location("version", versionLocation); + if (dep.getGroupId() == null) { + String depGroupId = depModel.getGroupId(); + InputLocation groupIdLocation = depModel.getLocation("groupId"); + if (depGroupId == null && depModel.getParent() != null) { + depGroupId = depModel.getParent().getGroupId(); + groupIdLocation = depModel.getParent().getLocation("groupId"); + } + depBuilder.groupId(depGroupId).location("groupId", groupIdLocation); + } + } + } + if (depBuilder != null) { + newDeps.add(depBuilder.build()); + modified = true; + } else { + newDeps.add(dep); + } + } + if (modified) { + if (builder == null) { + builder = Model.newBuilder(model); + } + builder.dependencies(newDeps); + } + return builder; + } + + String replaceCiFriendlyVersion(String version) { + if (version != null) { + for (String key : Arrays.asList("changelist", "revision", "sha1")) { + String val = request.getUserProperties().get(key); + if (val != null) { + version = version.replace("${" + key + "}", val); + } + } + } + return version; + } + + private void buildBuildPom() throws ModelBuilderException { + // Retrieve and normalize the source path, ensuring it's non-null and in absolute form + Path top = request.getSource().getPath(); + if (top == null) { + throw new IllegalStateException("Recursive build requested but source has no path"); + } + top = top.toAbsolutePath().normalize(); + + // Obtain the root directory, resolving it if necessary + Path rootDirectory; + try { + rootDirectory = session.getRootDirectory(); + } catch (IllegalStateException e) { + rootDirectory = session.getService(RootLocator.class).findRoot(top); + } + + // Locate and normalize the root POM if it exists, fallback to top otherwise + Path root = modelProcessor.locateExistingPom(rootDirectory); + if (root != null) { + root = root.toAbsolutePath().normalize(); + } else { + root = top; + } + + // Load all models starting from the root + loadFromRoot(root, top); + + // Check for errors after loading models + if (hasErrors()) { + throw newModelBuilderException(); + } + + // For the top model and all its children, build the effective model. + // This is done through the phased executor + var allResults = results(result).toList(); + List exceptions = new CopyOnWriteArrayList<>(); + try (PhasingExecutor executor = createExecutor()) { + for (DefaultModelBuilderResult r : allResults) { + executor.execute(() -> { + DefaultModelBuilderSession mbs = derive(r.getSource(), r); + try { + mbs.buildEffectiveModel(new LinkedHashSet<>()); + } catch (ModelBuilderException e) { + // gathered with problem collector + } catch (RuntimeException t) { + exceptions.add(t); + } + }); + } + } + + // Check for errors again after execution + if (exceptions.size() == 1) { + throw exceptions.get(0); + } else if (exceptions.size() > 1) { + MavenException fatalException = new MavenException("Multiple fatal exceptions occurred"); + exceptions.forEach(fatalException::addSuppressed); + throw fatalException; + } else if (hasErrors()) { + throw newModelBuilderException(); + } + } + + /** + * Generates a stream of DefaultModelBuilderResult objects, starting with the provided + * result and recursively including all its child results. + * + * @param r The initial DefaultModelBuilderResult object from which to generate the stream. + * @return A Stream of DefaultModelBuilderResult objects, starting with the provided result + * and including all its child results. + */ + Stream results(DefaultModelBuilderResult r) { + return Stream.concat(Stream.of(r), r.getChildren().stream().flatMap(this::results)); + } + + @SuppressWarnings("checkstyle:MethodLength") + private void loadFromRoot(Path root, Path top) { + try (PhasingExecutor executor = createExecutor()) { + DefaultModelBuilderResult r = Objects.equals(top, root) ? result : new DefaultModelBuilderResult(); + loadFilePom(executor, top, root, Set.of(), r); + } + if (result.getFileModel() == null && !Objects.equals(top, root)) { + logger.warn( + "The top project ({}) cannot be found in the reactor from root project ({}). " + + "Make sure the root directory is correct (a missing '.mvn' directory in the root " + + "project is the most common cause) and the project is correctly included " + + "in the reactor (missing activated profiles, command line options, etc.). For this " + + "build, the top project will be used as the root project.", + top, + root); + cache.clear(); + mappedSources.clear(); + loadFromRoot(top, top); + } + } + + private void loadFilePom( + Executor executor, Path top, Path pom, Set parents, DefaultModelBuilderResult r) { + try { + Path pomDirectory = Files.isDirectory(pom) ? pom : pom.getParent(); + ModelSource src = ModelSource.fromPath(pom); + Model model = derive(src, r).readFileModel(); + // keep all loaded file models in memory, those will be needed + // during the raw to build transformation + putSource(getGroupId(model), model.getArtifactId(), src); + Model activated = activateFileModel(model); + for (String subproject : getSubprojects(activated)) { + if (subproject == null || subproject.isEmpty()) { + continue; + } + + subproject = subproject.replace('\\', File.separatorChar).replace('/', File.separatorChar); + + Path rawSubprojectFile = modelProcessor.locateExistingPom(pomDirectory.resolve(subproject)); + + if (rawSubprojectFile == null) { + ModelProblem problem = new DefaultModelProblem( + "Child subproject " + subproject + " of " + pomDirectory + " does not exist", + Severity.ERROR, + ModelProblem.Version.BASE, + model, + -1, + -1, + null); + r.addProblem(problem); + continue; + } + + Path subprojectFile = rawSubprojectFile.toAbsolutePath().normalize(); + + if (parents.contains(subprojectFile)) { + StringBuilder buffer = new StringBuilder(256); + for (Path aggregatorFile : parents) { + buffer.append(aggregatorFile).append(" -> "); + } + buffer.append(subprojectFile); + + ModelProblem problem = new DefaultModelProblem( + "Child subproject " + subprojectFile + " of " + pom + " forms aggregation cycle " + + buffer, + Severity.ERROR, + ModelProblem.Version.BASE, + model, + -1, + -1, + null); + r.addProblem(problem); + continue; + } + + DefaultModelBuilderResult cr = + Objects.equals(top, subprojectFile) ? result : new DefaultModelBuilderResult(r); + if (request.isRecursive()) { + r.getChildren().add(cr); + } + + executor.execute(() -> loadFilePom(executor, top, subprojectFile, concat(parents, pom), cr)); + } + } catch (ModelBuilderException e) { + // gathered with problem collector + add(Severity.ERROR, ModelProblem.Version.V40, "Failed to load project " + pom, e); + } + if (r != result) { + r.getProblems().forEach(result::addProblem); + } + } + + static Set concat(Set a, T b) { + Set result = new HashSet<>(a); + result.add(b); + return Set.copyOf(result); + } + + void buildEffectiveModel(Collection importIds) throws ModelBuilderException { + Model resultModel = readEffectiveModel(); + setSource(resultModel); + setRootModel(resultModel); + + // model path translation + resultModel = + modelPathTranslator.alignToBaseDirectory(resultModel, resultModel.getProjectDirectory(), request); + + // plugin management injection + resultModel = pluginManagementInjector.injectManagement(resultModel, request, this); + + // lifecycle bindings injection + if (request.getRequestType() != ModelBuilderRequest.RequestType.DEPENDENCY) { + org.apache.maven.api.services.ModelTransformer lifecycleBindingsInjector = + request.getLifecycleBindingsInjector(); + if (lifecycleBindingsInjector != null) { + resultModel = lifecycleBindingsInjector.transform(resultModel, request, this); + } + } + + // dependency management import + resultModel = importDependencyManagement(resultModel, importIds); + + // dependency management injection + resultModel = dependencyManagementInjector.injectManagement(resultModel, request, this); + + resultModel = modelNormalizer.injectDefaultValues(resultModel, request, this); + + if (request.getRequestType() != ModelBuilderRequest.RequestType.DEPENDENCY) { + // plugins configuration + resultModel = pluginConfigurationExpander.expandPluginConfiguration(resultModel, request, this); + } + + for (var transformer : transformers) { + resultModel = transformer.transformEffectiveModel(resultModel); + } + + result.setEffectiveModel(resultModel); + + // effective model validation + modelValidator.validateEffectiveModel( + resultModel, + request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM + ? ModelValidator.VALIDATION_LEVEL_STRICT + : ModelValidator.VALIDATION_LEVEL_MINIMAL, + request, + this); + + if (hasErrors()) { + throw newModelBuilderException(); + } + } + + Model readParent(Model childModel) throws ModelBuilderException { + Model parentModel; + + Parent parent = childModel.getParent(); + if (parent != null) { + parentModel = readParentLocally(childModel); + if (parentModel == null) { + parentModel = resolveAndReadParentExternally(childModel); + } + + if (!"pom".equals(parentModel.getPackaging())) { + add( + Severity.ERROR, + ModelProblem.Version.BASE, + "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel) + + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"", + parentModel.getLocation("packaging")); + } + result.setParentModel(parentModel); + } else { + String superModelVersion = childModel.getModelVersion(); + if (superModelVersion == null || !VALID_MODEL_VERSIONS.contains(superModelVersion)) { + // Maven 3.x is always using 4.0.0 version to load the supermodel, so + // do the same when loading a dependency. The model validator will also + // check that field later. + superModelVersion = MODEL_VERSION_4_0_0; + } + parentModel = getSuperModel(superModelVersion); + } + + return parentModel; + } + + private Model readParentLocally(Model childModel) throws ModelBuilderException { + ModelSource candidateSource = getParentPomFile(childModel, request.getSource()); + if (candidateSource == null) { + return null; + } + + Model candidateModel = derive(candidateSource).readAsParentModel(); + + // + // TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we + // have a model that is suitable, yet more checks are done here and the one for the version is problematic + // before because with parents as ranges it will never work in this scenario. + // + + String groupId = getGroupId(candidateModel); + String artifactId = candidateModel.getArtifactId(); + + Parent parent = childModel.getParent(); + if (groupId == null + || !groupId.equals(parent.getGroupId()) + || artifactId == null + || !artifactId.equals(parent.getArtifactId())) { + StringBuilder buffer = new StringBuilder(256); + buffer.append("'parent.relativePath'"); + if (childModel != getRootModel()) { + buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel)); + } + buffer.append(" points at ").append(groupId).append(':').append(artifactId); + buffer.append(" instead of ").append(parent.getGroupId()).append(':'); + buffer.append(parent.getArtifactId()).append(", please verify your project structure"); + + setSource(childModel); + add(Severity.WARNING, ModelProblem.Version.BASE, buffer.toString(), parent.getLocation("")); + return null; + } + + String version = getVersion(candidateModel); + if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) { + try { + VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion()); + if (!parentRange.contains(versionParser.parseVersion(version))) { + // version skew drop back to resolution from the repository + return null; + } + + // Validate versions aren't inherited when using parent ranges the same way as when read externally. + String rawChildModelVersion = childModel.getVersion(); + + if (rawChildModelVersion == null) { + // Message below is checked for in the MNG-2199 core IT. + add( + Severity.FATAL, + ModelProblem.Version.V31, + "Version must be a constant", + childModel.getLocation("")); + + } else { + if (rawChildVersionReferencesParent(rawChildModelVersion)) { + // Message below is checked for in the MNG-2199 core IT. + add( + Severity.FATAL, + ModelProblem.Version.V31, + "Version must be a constant", + childModel.getLocation("version")); + } + } + + // MNG-2199: What else to check here ? + } catch (VersionParserException e) { + // invalid version range, so drop back to resolution from the repository + return null; + } + } + + // + // Here we just need to know that a version is fine to use but this validation we can do in our workspace + // resolver. + // + + return candidateModel; + } + + Model resolveAndReadParentExternally(Model childModel) throws ModelBuilderException { + ModelBuilderRequest request = this.request; + setSource(childModel); + + Parent parent = childModel.getParent(); + + String groupId = parent.getGroupId(); + String artifactId = parent.getArtifactId(); + String version = parent.getVersion(); + + // add repositories specified by the current model so that we can resolve the parent + if (!childModel.getRepositories().isEmpty()) { + var previousRepositories = repositories; + mergeRepositories(childModel.getRepositories(), false); + if (!Objects.equals(previousRepositories, repositories)) { + if (logger.isDebugEnabled()) { + logger.debug("Merging repositories from " + childModel.getId() + "\n" + + repositories.stream() + .map(Object::toString) + .collect(Collectors.joining("\n", " ", ""))); + } + } + } + + ModelSource modelSource; + try { + modelSource = resolveReactorModel(groupId, artifactId, version); + if (modelSource == null) { + AtomicReference modified = new AtomicReference<>(); + modelSource = modelResolver.resolveModel(request.getSession(), repositories, parent, modified); + if (modified.get() != null) { + parent = modified.get(); + } + } + } catch (ModelResolverException e) { + // Message below is checked for in the MNG-2199 core IT. + StringBuilder buffer = new StringBuilder(256); + buffer.append("Non-resolvable parent POM"); + if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) { + buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version)); + } + if (childModel != getRootModel()) { + buffer.append(" for ").append(ModelProblemUtils.toId(childModel)); + } + buffer.append(": ").append(e.getMessage()); + if (childModel.getProjectDirectory() != null) { + if (parent.getRelativePath() == null + || parent.getRelativePath().isEmpty()) { + buffer.append(" and 'parent.relativePath' points at no local POM"); + } else { + buffer.append(" and 'parent.relativePath' points at wrong local POM"); + } + } + + add(Severity.FATAL, ModelProblem.Version.BASE, buffer.toString(), parent.getLocation(""), e); + throw newModelBuilderException(); + } + + ModelBuilderRequest lenientRequest = ModelBuilderRequest.builder(request) + .requestType(ModelBuilderRequest.RequestType.PARENT_POM) + .source(modelSource) + .build(); + + Model parentModel = derive(lenientRequest).readAsParentModel(); + + if (!parent.getVersion().equals(version)) { + String rawChildModelVersion = childModel.getVersion(); + + if (rawChildModelVersion == null) { + // Message below is checked for in the MNG-2199 core IT. + add( + Severity.FATAL, + ModelProblem.Version.V31, + "Version must be a constant", + childModel.getLocation("")); + } else { + if (rawChildVersionReferencesParent(rawChildModelVersion)) { + // Message below is checked for in the MNG-2199 core IT. + add( + Severity.FATAL, + ModelProblem.Version.V31, + "Version must be a constant", + childModel.getLocation("version")); + } + } + + // MNG-2199: What else to check here ? + } + + return parentModel; + } + + Model activateFileModel(Model inputModel) throws ModelBuilderException { + setRootModel(inputModel); + + // profile activation + DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); + + setSource("(external profiles)"); + List activeExternalProfiles = + profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, this); + + result.setActiveExternalProfiles(activeExternalProfiles); + + if (!activeExternalProfiles.isEmpty()) { + Properties profileProps = new Properties(); + for (Profile profile : activeExternalProfiles) { + profileProps.putAll(profile.getProperties()); + } + profileProps.putAll(profileActivationContext.getUserProperties()); + profileActivationContext.setUserProperties(profileProps); + } + + profileActivationContext.setProjectProperties(inputModel.getProperties()); + setSource(inputModel); + List activePomProfiles = + profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, this); + + // model normalization + setSource(inputModel); + inputModel = modelNormalizer.mergeDuplicates(inputModel, request, this); + + Map interpolatedActivations = getProfileActivations(inputModel); + inputModel = injectProfileActivations(inputModel, interpolatedActivations); - DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result); + // profile injection + inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, this); + inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, this); + + return inputModel; + } + + @SuppressWarnings("checkstyle:methodlength") + private Model readEffectiveModel() throws ModelBuilderException { + Model inputModel = readRawModel(); + if (hasFatalErrors()) { + throw newModelBuilderException(); + } + + inputModel = activateFileModel(inputModel); + + setRootModel(inputModel); + + // profile activation + DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); + + List activeExternalProfiles = result.getActiveExternalProfiles(); + + if (!activeExternalProfiles.isEmpty()) { + Properties profileProps = new Properties(); + for (Profile profile : activeExternalProfiles) { + profileProps.putAll(profile.getProperties()); + } + profileProps.putAll(profileActivationContext.getUserProperties()); + profileActivationContext.setUserProperties(profileProps); + } + + Model parentModel = readParent(inputModel); + + List parentInterpolatedProfiles = + interpolateActivations(parentModel.getProfiles(), profileActivationContext, this); + // profile injection + List parentActivePomProfiles = + profileSelector.getActiveProfiles(parentInterpolatedProfiles, profileActivationContext, this); + Model injectedParentModel = profileInjector + .injectProfiles(parentModel, parentActivePomProfiles, request, this) + .withProfiles(List.of()); + + Model model = inheritanceAssembler.assembleModelInheritance(inputModel, injectedParentModel, request, this); + + // model normalization + model = modelNormalizer.mergeDuplicates(model, request, this); + + // profile activation + profileActivationContext.setProjectProperties(model.getProperties()); + + List interpolatedProfiles = + interpolateActivations(model.getProfiles(), profileActivationContext, this); + + // profile injection + List activePomProfiles = + profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this); + result.setActivePomProfiles(activePomProfiles); + model = profileInjector.injectProfiles(model, activePomProfiles, request, this); + model = profileInjector.injectProfiles(model, activeExternalProfiles, request, this); + + // model interpolation + Model resultModel = model; + resultModel = interpolateModel(resultModel, request, this); + + // url normalization + resultModel = modelUrlNormalizer.normalize(resultModel, request); + + // Now the fully interpolated model is available: reconfigure the resolver + if (!resultModel.getRepositories().isEmpty()) { + List oldRepos = + repositories.stream().map(Object::toString).toList(); + mergeRepositories(resultModel.getRepositories(), true); + List newRepos = + repositories.stream().map(Object::toString).toList(); + if (!Objects.equals(oldRepos, newRepos)) { + logger.debug("Replacing repositories from " + resultModel.getId() + "\n" + + newRepos.stream().map(s -> " " + s).collect(Collectors.joining("\n"))); + } + } + + return resultModel; + } + + Model readFileModel() throws ModelBuilderException { + Model model = cache(request.getSource(), FILE, this::doReadFileModel); + // set the file model in the result outside the cache + result.setFileModel(model); + return model; + } + + @SuppressWarnings("checkstyle:methodlength") + Model doReadFileModel() throws ModelBuilderException { + ModelSource modelSource = request.getSource(); + Model model; + setSource(modelSource.getLocation()); + logger.debug("Reading file model from " + modelSource.getLocation()); + try { + boolean strict = request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM; + // TODO: we do cache, but what if strict does not have the same value? + Path rootDirectory; + try { + rootDirectory = request.getSession().getRootDirectory(); + } catch (IllegalStateException ignore) { + rootDirectory = modelSource.getPath(); + } + try (InputStream is = modelSource.openStream()) { + model = modelProcessor.read(XmlReaderRequest.builder() + .strict(strict) + .location(modelSource.getLocation()) + .path(modelSource.getPath()) + .rootDirectory(rootDirectory) + .inputStream(is) + .build()); + } catch (XmlReaderException e) { + if (!strict) { + throw e; + } + try (InputStream is = modelSource.openStream()) { + model = modelProcessor.read(XmlReaderRequest.builder() + .strict(false) + .location(modelSource.getLocation()) + .path(modelSource.getPath()) + .rootDirectory(rootDirectory) + .inputStream(is) + .build()); + } catch (XmlReaderException ne) { + // still unreadable even in non-strict mode, rethrow original error + throw e; + } - // read and validate raw model - Model fileModel = readFileModel(request, problems); - result.setFileModel(fileModel); + add( + Severity.ERROR, + ModelProblem.Version.V20, + "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(), + e); + } - Model activatedFileModel = activateFileModel(fileModel, request, result, problems); - result.setActivatedFileModel(activatedFileModel); + InputLocation loc = model.getLocation(""); + InputSource v4src = loc != null ? loc.getSource() : null; + if (v4src != null) { + try { + Field field = InputSource.class.getDeclaredField("modelId"); + field.setAccessible(true); + field.set(v4src, ModelProblemUtils.toId(model)); + } catch (Throwable t) { + // TODO: use a lazy source ? + throw new IllegalStateException("Unable to set modelId on InputSource", t); + } + } + } catch (XmlReaderException e) { + add( + Severity.FATAL, + ModelProblem.Version.BASE, + "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(), + e); + throw newModelBuilderException(); + } catch (IOException e) { + String msg = e.getMessage(); + if (msg == null || msg.isEmpty()) { + // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException + if (e.getClass().getName().endsWith("MalformedInputException")) { + msg = "Some input bytes do not match the file encoding."; + } else { + msg = e.getClass().getSimpleName(); + } + } + add( + Severity.FATAL, + ModelProblem.Version.BASE, + "Non-readable POM " + modelSource.getLocation() + ": " + msg, + e); + throw newModelBuilderException(); + } - if (!request.isTwoPhaseBuilding()) { - return build(request, result, importIds); - } else if (hasModelErrors(problems)) { - throw problems.newModelBuilderException(); - } + if (model.getModelVersion() == null) { + String namespace = model.getNamespaceUri(); + if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) { + model = model.withModelVersion(namespace.substring(NAMESPACE_PREFIX.length())); + } + } - return result; - } + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { + model = model.withPomFile(modelSource.getPath()); + + Parent parent = model.getParent(); + if (parent != null) { + String groupId = parent.getGroupId(); + String artifactId = parent.getArtifactId(); + String version = parent.getVersion(); + String path = Optional.ofNullable(parent.getRelativePath()).orElse(".."); + if (version == null && !path.isEmpty()) { + Path pomFile = model.getPomFile(); + Path relativePath = Paths.get(path); + Path pomPath = pomFile.resolveSibling(relativePath).normalize(); + if (Files.isDirectory(pomPath)) { + pomPath = modelProcessor.locateExistingPom(pomPath); + } + if (pomPath != null && Files.isRegularFile(pomPath)) { + Model parentModel = + derive(ModelSource.fromPath(pomPath)).readFileModel(); + if (parentModel != null) { + String parentGroupId = getGroupId(parentModel); + String parentArtifactId = parentModel.getArtifactId(); + String parentVersion = getVersion(parentModel); + if ((groupId == null || groupId.equals(parentGroupId)) + && (artifactId == null || artifactId.equals(parentArtifactId))) { + model = model.withParent(parent.with() + .groupId(parentGroupId) + .artifactId(parentArtifactId) + .version(parentVersion) + .build()); + } + } + } + } + } - private Model activateFileModel( - Model inputModel, - ModelBuilderRequest request, - DefaultModelBuilderResult result, - DefaultModelProblemCollector problems) - throws ModelBuilderException { - problems.setRootModel(inputModel); + // subprojects discovery + if (getSubprojects(model).isEmpty() + // only discover subprojects if POM > 4.0.0 + && !MODEL_VERSION_4_0_0.equals(model.getModelVersion()) + // and if packaging is POM (we check type, but the session is not yet available, + // we would require the project realm if we want to support extensions + && Type.POM.equals(model.getPackaging())) { + List subprojects = new ArrayList<>(); + try (Stream files = Files.list(model.getProjectDirectory())) { + for (Path f : files.toList()) { + if (Files.isDirectory(f)) { + Path subproject = modelProcessor.locateExistingPom(f); + if (subproject != null) { + subprojects.add(f.getFileName().toString()); + } + } + } + if (!subprojects.isEmpty()) { + model = model.withSubprojects(subprojects); + } + } catch (IOException e) { + add(Severity.FATAL, ModelProblem.Version.V41, "Error discovering subprojects", e); + } + } + } - // profile activation - DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); + for (var transformer : transformers) { + model = transformer.transformFileModel(model); + } - problems.setSource("(external profiles)"); - List activeExternalProfiles = - profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, problems); + setSource(model); + modelValidator.validateFileModel( + model, + request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM + ? ModelValidator.VALIDATION_LEVEL_STRICT + : ModelValidator.VALIDATION_LEVEL_MINIMAL, + request, + this); + if (hasFatalErrors()) { + throw newModelBuilderException(); + } - result.setActiveExternalProfiles(activeExternalProfiles); + return model; + } - if (!activeExternalProfiles.isEmpty()) { - Properties profileProps = new Properties(); - for (Profile profile : activeExternalProfiles) { - profileProps.putAll(profile.getProperties()); - } - profileProps.putAll(profileActivationContext.getUserProperties()); - profileActivationContext.setUserProperties(profileProps); + Model readRawModel() throws ModelBuilderException { + // ensure file model is available + readFileModel(); + Model model = cache(request.getSource(), RAW, this::doReadRawModel); + // set the raw model in the result outside the cache + result.setRawModel(model); + return model; } - profileActivationContext.setProjectProperties(inputModel.getProperties()); - problems.setSource(inputModel); - List activePomProfiles = - profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, problems); + private Model doReadRawModel() throws ModelBuilderException { + ModelBuilderRequest request = this.request; + Model rawModel = readFileModel(); - // model normalization - problems.setSource(inputModel); - inputModel = modelNormalizer.mergeDuplicates(inputModel, request, problems); + if (!MODEL_VERSION_4_0_0.equals(rawModel.getModelVersion()) + && request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { + rawModel = transformFileToRaw(rawModel); + } - Map interpolatedActivations = getProfileActivations(inputModel); - inputModel = injectProfileActivations(inputModel, interpolatedActivations); + for (var transformer : transformers) { + rawModel = transformer.transformRawModel(rawModel); + } - // profile injection - inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, problems); - inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, problems); + modelValidator.validateRawModel( + rawModel, + request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM + ? ModelValidator.VALIDATION_LEVEL_STRICT + : ModelValidator.VALIDATION_LEVEL_MINIMAL, + request, + this); - return inputModel; - } + if (hasFatalErrors()) { + throw newModelBuilderException(); + } - @SuppressWarnings("checkstyle:methodlength") - private Model readEffectiveModel( - final ModelBuilderRequest request, - final DefaultModelBuilderResult result, - DefaultModelProblemCollector problems) - throws ModelBuilderException { - Model inputModel = readRawModel(request, problems); - if (problems.hasFatalErrors()) { - throw problems.newModelBuilderException(); + return rawModel; } - inputModel = activateFileModel(inputModel, request, result, problems); + /** + * Reads the request source's parent. + */ + Model readAsParentModel() { + return cache(request.getSource(), PARENT, this::doReadAsParentModel); + } - problems.setRootModel(inputModel); + private Model doReadAsParentModel() throws ModelBuilderException { + Model raw = readRawModel(); + Model parentData = readParent(raw); + Model parent = new DefaultInheritanceAssembler(new DefaultInheritanceAssembler.InheritanceModelMerger() { + @Override + protected void mergeModel_Modules( + Model.Builder builder, + Model target, + Model source, + boolean sourceDominant, + Map context) {} + + @Override + protected void mergeModel_Subprojects( + Model.Builder builder, + Model target, + Model source, + boolean sourceDominant, + Map context) {} + + @SuppressWarnings("deprecation") + @Override + protected void mergeModel_Profiles( + Model.Builder builder, + Model target, + Model source, + boolean sourceDominant, + Map context) { + builder.profiles(Stream.concat(source.getProfiles().stream(), target.getProfiles().stream()) + .map(p -> p.withModules(null).withSubprojects(null)) + .toList()); + } + }) + .assembleModelInheritance(raw, parentData, request, this); - ModelData resultData = new ModelData(request.getSource(), inputModel); - String superModelVersion = - inputModel.getModelVersion() != null ? inputModel.getModelVersion() : MODEL_VERSION_4_0_0; - if (!VALID_MODEL_VERSIONS.contains(superModelVersion)) { - // Maven 3.x is always using 4.0.0 version to load the supermodel, so - // do the same when loading a dependency. The model validator will also - // check that field later. - superModelVersion = MODEL_VERSION_4_0_0; + return parent.withParent(null); } - ModelData superData = new ModelData(null, getSuperModel(superModelVersion)); - - // profile activation - DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); - List activeExternalProfiles = result.getActiveExternalProfiles(); + private Model importDependencyManagement(Model model, Collection importIds) { + DependencyManagement depMgmt = model.getDependencyManagement(); - if (!activeExternalProfiles.isEmpty()) { - Properties profileProps = new Properties(); - for (Profile profile : activeExternalProfiles) { - profileProps.putAll(profile.getProperties()); + if (depMgmt == null) { + return model; } - profileProps.putAll(profileActivationContext.getUserProperties()); - profileActivationContext.setUserProperties(profileProps); - } - Collection parentIds = new LinkedHashSet<>(); + String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion(); - List lineage = new ArrayList<>(); + importIds.add(importing); - for (ModelData currentData = resultData; ; ) { - String modelId = currentData.id(); - result.addModelId(modelId); + List importMgmts = null; - Model model = currentData.model(); - result.setRawModel(modelId, model); - problems.setSource(model); + List deps = new ArrayList<>(depMgmt.getDependencies()); + for (Iterator it = deps.iterator(); it.hasNext(); ) { + Dependency dependency = it.next(); - // model normalization - model = modelNormalizer.mergeDuplicates(model, request, problems); + if (!("pom".equals(dependency.getType()) && "import".equals(dependency.getScope())) + || "bom".equals(dependency.getType())) { + continue; + } - // profile activation - profileActivationContext.setProjectProperties(model.getProperties()); + it.remove(); - List interpolatedProfiles = - interpolateActivations(model.getProfiles(), profileActivationContext, problems); + DependencyManagement importMgmt = loadDependencyManagement(dependency, importIds); - // profile injection - List activePomProfiles = - profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, problems); - result.setActivePomProfiles(modelId, activePomProfiles); - model = profileInjector.injectProfiles(model, activePomProfiles, request, problems); - if (currentData == resultData) { - model = profileInjector.injectProfiles(model, activeExternalProfiles, request, problems); + if (importMgmt != null) { + if (importMgmts == null) { + importMgmts = new ArrayList<>(); + } + + importMgmts.add(importMgmt); + } } - lineage.add(model); + importIds.remove(importing); - if (currentData == superData) { - break; - } + model = model.withDependencyManagement( + model.getDependencyManagement().withDependencies(deps)); - // add repositories specified by the current model so that we can resolve the parent - if (!model.getRepositories().isEmpty()) { - List oldRepos = request.getModelRepositoryHolder().getRepositories().stream() - .map(Object::toString) - .toList(); - request.getModelRepositoryHolder().merge(model.getRepositories(), false); - List newRepos = request.getModelRepositoryHolder().getRepositories().stream() - .map(Object::toString) - .toList(); - if (!Objects.equals(oldRepos, newRepos)) { - logger.debug("Merging repositories from " + model.getId() + "\n" - + newRepos.stream().map(s -> " " + s).collect(Collectors.joining("\n"))); - } + return dependencyManagementImporter.importManagement(model, importMgmts, request, this); + } + + private DependencyManagement loadDependencyManagement(Dependency dependency, Collection importIds) { + String groupId = dependency.getGroupId(); + String artifactId = dependency.getArtifactId(); + String version = dependency.getVersion(); + + if (groupId == null || groupId.isEmpty()) { + add( + Severity.ERROR, + ModelProblem.Version.BASE, + "'dependencyManagement.dependencies.dependency.groupId' for " + dependency.getManagementKey() + + " is missing.", + dependency.getLocation("")); + return null; + } + if (artifactId == null || artifactId.isEmpty()) { + add( + Severity.ERROR, + ModelProblem.Version.BASE, + "'dependencyManagement.dependencies.dependency.artifactId' for " + dependency.getManagementKey() + + " is missing.", + dependency.getLocation("")); + return null; + } + if (version == null || version.isEmpty()) { + add( + Severity.ERROR, + ModelProblem.Version.BASE, + "'dependencyManagement.dependencies.dependency.version' for " + dependency.getManagementKey() + + " is missing.", + dependency.getLocation("")); + return null; } - // we pass a cloned model, so that resolving the parent version does not affect the returned model - ModelData parentData = readParent(model, currentData.source(), request, problems); + String imported = groupId + ':' + artifactId + ':' + version; - if (parentData == null) { - currentData = superData; - } else if (!parentIds.add(parentData.id())) { - StringBuilder message = new StringBuilder("The parents form a cycle: "); - for (String parentId : parentIds) { - message.append(parentId).append(" -> "); + if (importIds.contains(imported)) { + StringBuilder message = + new StringBuilder("The dependencies of type=pom and with scope=import form a cycle: "); + for (String modelId : importIds) { + message.append(modelId).append(" -> "); } - message.append(parentData.id()); - - problems.add(Severity.FATAL, ModelProblem.Version.BASE, message.toString()); + message.append(imported); + add(Severity.ERROR, ModelProblem.Version.BASE, message.toString()); + return null; + } - throw problems.newModelBuilderException(); - } else { - currentData = parentData; + Model importModel = cache( + groupId, + artifactId, + version, + IMPORT, + () -> doLoadDependencyManagement(dependency, groupId, artifactId, version, importIds)); + DependencyManagement importMgmt = importModel != null ? importModel.getDependencyManagement() : null; + if (importMgmt == null) { + importMgmt = DependencyManagement.newInstance(); } - } - Model tmpModel = lineage.get(0); + // [MNG-5600] Dependency management import should support exclusions. + List exclusions = dependency.getExclusions(); + if (importMgmt != null && !exclusions.isEmpty()) { + // Dependency excluded from import. + List dependencies = importMgmt.getDependencies().stream() + .filter(candidate -> exclusions.stream().noneMatch(exclusion -> match(exclusion, candidate))) + .map(candidate -> addExclusions(candidate, exclusions)) + .collect(Collectors.toList()); + importMgmt = importMgmt.withDependencies(dependencies); + } - // inject interpolated activations - List interpolated = interpolateActivations(tmpModel.getProfiles(), profileActivationContext, problems); - if (interpolated != tmpModel.getProfiles()) { - tmpModel = tmpModel.withProfiles(interpolated); + return importMgmt; } - // inject external profile into current model - tmpModel = profileInjector.injectProfiles(tmpModel, activeExternalProfiles, request, problems); + @SuppressWarnings("checkstyle:parameternumber") + private Model doLoadDependencyManagement( + Dependency dependency, + String groupId, + String artifactId, + String version, + Collection importIds) { + Model importModel; + ModelSource importSource; + try { + importSource = resolveReactorModel(groupId, artifactId, version); + if (importSource == null) { + importSource = modelResolver.resolveModel( + request.getSession(), repositories, dependency, new AtomicReference<>()); + } + } catch (ModelBuilderException e) { + StringBuilder buffer = new StringBuilder(256); + buffer.append("Non-resolvable import POM"); + if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) { + buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version)); + } + buffer.append(": ").append(e.getMessage()); - lineage.set(0, tmpModel); + add(Severity.ERROR, ModelProblem.Version.BASE, buffer.toString(), dependency.getLocation(""), e); + return null; + } - checkPluginVersions(lineage, request, problems); + Path rootDirectory; + try { + rootDirectory = request.getSession().getRootDirectory(); + } catch (IllegalStateException e) { + rootDirectory = null; + } + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM && rootDirectory != null) { + Path sourcePath = importSource.getPath(); + if (sourcePath != null && sourcePath.startsWith(rootDirectory)) { + add( + Severity.WARNING, + ModelProblem.Version.BASE, + "BOM imports from within reactor should be avoided", + dependency.getLocation("")); + } + } - // inheritance assembly - Model resultModel = assembleInheritance(lineage, request, problems); + final ModelBuilderResult importResult; + try { + ModelBuilderRequest importRequest = ModelBuilderRequest.builder() + .session(request.getSession()) + .requestType(ModelBuilderRequest.RequestType.DEPENDENCY) + .systemProperties(request.getSystemProperties()) + .userProperties(request.getUserProperties()) + .source(importSource) + .repositories(repositories) + .build(); + DefaultModelBuilderSession modelBuilderSession = new DefaultModelBuilderSession(importRequest); + // build the effective model + modelBuilderSession.buildEffectiveModel(importIds); + importResult = modelBuilderSession.result; + } catch (ModelBuilderException e) { + e.getResult().getProblems().forEach(this::add); + return null; + } - // consider caching inherited model + importResult.getProblems().forEach(this::add); - problems.setSource(resultModel); - problems.setRootModel(resultModel); + importModel = importResult.getEffectiveModel(); - // model interpolation - resultModel = interpolateModel(resultModel, request, problems); + return importModel; + } - // url normalization - resultModel = modelUrlNormalizer.normalize(resultModel, request); + ModelSource resolveReactorModel(String groupId, String artifactId, String version) { + Set sources = mappedSources.get(new GAKey(groupId, artifactId)); + if (sources != null) { + for (ModelSource source : sources) { + Model model = derive(source).readRawModel(); + if (Objects.equals(model.getVersion(), version)) { + return source; + } + } + // TODO: log a warning ? + } + return null; + } - result.setEffectiveModel(resultModel); + private T cache(String groupId, String artifactId, String version, String tag, Supplier supplier) { + return cache.computeIfAbsent(groupId, artifactId, version, tag, supplier); + } - // Now the fully interpolated model is available: reconfigure the resolver - if (!resultModel.getRepositories().isEmpty()) { - List oldRepos = request.getModelRepositoryHolder().getRepositories().stream() - .map(Object::toString) - .toList(); - request.getModelRepositoryHolder().merge(resultModel.getRepositories(), true); - List newRepos = request.getModelRepositoryHolder().getRepositories().stream() - .map(Object::toString) - .toList(); - if (!Objects.equals(oldRepos, newRepos)) { - logger.debug("Replacing repositories from " + resultModel.getId() + "\n" - + newRepos.stream().map(s -> " " + s).collect(Collectors.joining("\n"))); - } + private T cache(Source source, String tag, Supplier supplier) { + return cache.computeIfAbsent(source, tag, supplier); } + } - return resultModel; + @SuppressWarnings("deprecation") + private static List getSubprojects(Model activated) { + List subprojects = activated.getSubprojects(); + if (subprojects.isEmpty()) { + subprojects = activated.getModules(); + } + return subprojects; } private List interpolateActivations( - List profiles, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) { + List profiles, DefaultProfileActivationContext context, ModelProblemCollector problems) { if (profiles.stream() .map(org.apache.maven.api.model.Profile::getActivation) .noneMatch(Objects::nonNull)) { @@ -510,7 +1750,12 @@ private String transformPath(String path, ActivationFile target, String location try { return profileActivationFilePathInterpolator.interpolate(path, context); } catch (InterpolationException e) { - addInterpolationProblem(problems, target, path, e, locationKey); + problems.add( + Severity.ERROR, + ModelProblem.Version.BASE, + "Failed to interpolate file location " + path + ": " + e.getMessage(), + target.getLocation(locationKey), + e); } } return path; @@ -519,355 +1764,17 @@ private String transformPath(String path, ActivationFile target, String location return profiles.stream().map(new ProfileInterpolator()).toList(); } - private static void addInterpolationProblem( - DefaultModelProblemCollector problems, - InputLocationTracker target, - String path, - InterpolationException e, - String locationKey) { - problems.add( - Severity.ERROR, - ModelProblem.Version.BASE, - "Failed to interpolate file location " + path + ": " + e.getMessage(), - target.getLocation(locationKey), - e); - } - private static boolean isNotEmpty(String string) { - return string != null && !string.isEmpty(); - } - - public ModelBuilderResult build(final ModelBuilderRequest request, final ModelBuilderResult result) - throws ModelBuilderException { - return build(request, result, new LinkedHashSet<>()); - } - - public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException { - request = fillRequestDefaults(request); - DefaultModelProblemCollector problems = new DefaultModelProblemCollector(new DefaultModelBuilderResult()); - Model model = readRawModel(request, problems); - if (hasModelErrors(problems)) { - throw problems.newModelBuilderException(); - } - return model; - } - - private ModelBuilderResult build( - ModelBuilderRequest request, final ModelBuilderResult phaseOneResult, Collection importIds) - throws ModelBuilderException { - DefaultModelBuilderResult result = asDefaultModelBuilderResult(phaseOneResult); - - DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result); - - // phase 2 - Model resultModel = readEffectiveModel(request, result, problems); - problems.setSource(resultModel); - problems.setRootModel(resultModel); - - // model path translation - resultModel = modelPathTranslator.alignToBaseDirectory(resultModel, resultModel.getProjectDirectory(), request); - - // plugin management injection - resultModel = pluginManagementInjector.injectManagement(resultModel, request, problems); - - resultModel = fireEvent(resultModel, request, problems, ModelBuildingListener::buildExtensionsAssembled); - - if (request.isProcessPlugins()) { - if (lifecycleBindingsInjector == null) { - throw new IllegalStateException("lifecycle bindings injector is missing"); - } - - // lifecycle bindings injection - resultModel = lifecycleBindingsInjector.injectLifecycleBindings(resultModel, request, problems); - } - - // dependency management import - resultModel = importDependencyManagement(resultModel, request, problems, importIds); - - // dependency management injection - resultModel = dependencyManagementInjector.injectManagement(resultModel, request, problems); - - resultModel = modelNormalizer.injectDefaultValues(resultModel, request, problems); - - if (request.isProcessPlugins()) { - // plugins configuration - resultModel = pluginConfigurationExpander.expandPluginConfiguration(resultModel, request, problems); - } - - for (var transformer : transformers) { - resultModel = transformer.transformEffectiveModel(resultModel); - } - - result.setEffectiveModel(resultModel); - - // effective model validation - modelValidator.validateEffectiveModel(resultModel, request, problems); - - if (hasModelErrors(problems)) { - throw problems.newModelBuilderException(); - } - - return result; - } - - private DefaultModelBuilderResult asDefaultModelBuilderResult(ModelBuilderResult phaseOneResult) { - if (phaseOneResult instanceof DefaultModelBuilderResult) { - return (DefaultModelBuilderResult) phaseOneResult; - } else { - return new DefaultModelBuilderResult(phaseOneResult); - } - } - - public Result buildRawModel(Path pomFile, int validationLevel, boolean locationTracking) { - return buildRawModel(pomFile, validationLevel, locationTracking, null); - } - - public Result buildRawModel( - Path pomFile, int validationLevel, boolean locationTracking, ModelTransformerContext context) { - final ModelBuilderRequest request = ModelBuilderRequest.builder() - .validationLevel(validationLevel) - .locationTracking(locationTracking) - .source(ModelSource.fromPath(pomFile)) - .build(); - DefaultModelProblemCollector problems = new DefaultModelProblemCollector(new DefaultModelBuilderResult()); - try { - Model model = readFileModel(request, problems); - - try { - if (transformer != null && context != null) { - transformer.transform(context, model, pomFile); - } - } catch (ModelBuilderException e) { - problems.add(Severity.FATAL, ModelProblem.Version.V40, null, e); - } - - return Result.newResult(model, problems.getProblems()); - } catch (ModelBuilderException e) { - return Result.error(problems.getProblems()); - } - } - - Model readFileModel(ModelBuilderRequest request, DefaultModelProblemCollector problems) - throws ModelBuilderException { - ModelSource modelSource = request.getSource(); - Model model = - cache(getModelCache(request), modelSource, FILE, () -> doReadFileModel(modelSource, request, problems)); - - if (modelSource.getPath() != null) { - if (getTransformerContextBuilder(request) instanceof DefaultModelTransformerContextBuilder contextBuilder) { - contextBuilder.putSource(getGroupId(model), model.getArtifactId(), modelSource); - } - } - - return model; - } - - @SuppressWarnings("checkstyle:methodlength") - private Model doReadFileModel( - ModelSource modelSource, ModelBuilderRequest request, DefaultModelProblemCollector problems) - throws ModelBuilderException { - Model model; - problems.setSource(modelSource.getLocation()); - try { - boolean strict = request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0; - - Path rootDirectory; - try { - rootDirectory = request.getSession().getRootDirectory(); - } catch (IllegalStateException ignore) { - rootDirectory = modelSource.getPath(); - } - try (InputStream is = modelSource.openStream()) { - model = modelProcessor.read(XmlReaderRequest.builder() - .strict(strict) - .location(modelSource.getLocation()) - .path(modelSource.getPath()) - .rootDirectory(rootDirectory) - .inputStream(is) - .build()); - } catch (XmlReaderException e) { - if (!strict) { - throw e; - } - try (InputStream is = modelSource.openStream()) { - model = modelProcessor.read(XmlReaderRequest.builder() - .strict(false) - .location(modelSource.getLocation()) - .path(modelSource.getPath()) - .rootDirectory(rootDirectory) - .inputStream(is) - .build()); - } catch (XmlReaderException ne) { - // still unreadable even in non-strict mode, rethrow original error - throw e; - } - - Severity severity = request.isProjectBuild() ? Severity.ERROR : Severity.WARNING; - problems.add( - severity, - ModelProblem.Version.V20, - "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(), - e); - } - - InputLocation loc = model.getLocation(""); - InputSource v4src = loc != null ? loc.getSource() : null; - if (v4src != null) { - try { - Field field = InputSource.class.getDeclaredField("modelId"); - field.setAccessible(true); - field.set(v4src, ModelProblemUtils.toId(model)); - } catch (Throwable t) { - // TODO: use a lazy source ? - throw new IllegalStateException("Unable to set modelId on InputSource", t); - } - } - } catch (XmlReaderException e) { - problems.add( - Severity.FATAL, - ModelProblem.Version.BASE, - "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(), - e); - throw problems.newModelBuilderException(); - } catch (IOException e) { - String msg = e.getMessage(); - if (msg == null || msg.isEmpty()) { - // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException - if (e.getClass().getName().endsWith("MalformedInputException")) { - msg = "Some input bytes do not match the file encoding."; - } else { - msg = e.getClass().getSimpleName(); - } - } - problems.add( - Severity.FATAL, - ModelProblem.Version.BASE, - "Non-readable POM " + modelSource.getLocation() + ": " + msg, - e); - throw problems.newModelBuilderException(); - } - - if (modelSource.getPath() != null) { - model = model.withPomFile(modelSource.getPath()); - - Parent parent = model.getParent(); - if (parent != null) { - String groupId = parent.getGroupId(); - String artifactId = parent.getArtifactId(); - String version = parent.getVersion(); - String path = Optional.ofNullable(parent.getRelativePath()).orElse(".."); - if (version == null && !path.isEmpty()) { - Path pomFile = model.getPomFile(); - Path relativePath = Paths.get(path); - Path pomPath = pomFile.resolveSibling(relativePath).normalize(); - if (Files.isDirectory(pomPath)) { - pomPath = getModelProcessor().locateExistingPom(pomPath); - } - if (pomPath != null && Files.isRegularFile(pomPath)) { - ModelBuilderRequest parentRequest = - ModelBuilderRequest.build(request, ModelSource.fromPath(pomPath)); - Model parentModel = readFileModel(parentRequest, problems); - if (parentModel != null) { - String parentGroupId = getGroupId(parentModel); - String parentArtifactId = parentModel.getArtifactId(); - String parentVersion = getVersion(parentModel); - if ((groupId == null || groupId.equals(parentGroupId)) - && (artifactId == null || artifactId.equals(parentArtifactId))) { - model = model.withParent(parent.with() - .groupId(parentGroupId) - .artifactId(parentArtifactId) - .version(parentVersion) - .build()); - } - } - } - } - } - - // subprojects discovery - if (model.getSubprojects().isEmpty() - && model.getModules().isEmpty() - // only discover subprojects if POM > 4.0.0 - && !MODEL_VERSION_4_0_0.equals(model.getModelVersion()) - // and if packaging is POM (we check type, but the session is not yet available, - // we would require the project realm if we want to support extensions - && Type.POM.equals(model.getPackaging())) { - List subprojects = new ArrayList<>(); - try (Stream files = Files.list(model.getProjectDirectory())) { - for (Path f : files.toList()) { - if (Files.isDirectory(f)) { - Path subproject = modelProcessor.locateExistingPom(f); - if (subproject != null) { - subprojects.add(f.getFileName().toString()); - } - } - } - if (!subprojects.isEmpty()) { - model = model.withSubprojects(subprojects); - } - } catch (IOException e) { - problems.add(Severity.FATAL, ModelProblem.Version.V41, "Error discovering subprojects", e); - } - } - } - - for (var transformer : transformers) { - model = transformer.transformFileModel(model); - } - - problems.setSource(model); - modelValidator.validateFileModel(model, request, problems); - if (hasFatalErrors(problems)) { - throw problems.newModelBuilderException(); - } - - return model; - } - - Model readRawModel(ModelBuilderRequest request, DefaultModelProblemCollector problems) - throws ModelBuilderException { - ModelSource modelSource = request.getSource(); - - ModelData modelData = - cache(getModelCache(request), modelSource, RAW, () -> doReadRawModel(modelSource, request, problems)); - - return modelData.model(); - } - - private ModelData doReadRawModel( - ModelSource modelSource, ModelBuilderRequest request, DefaultModelProblemCollector problems) - throws ModelBuilderException { - Model rawModel = readFileModel(request, problems); - if (!MODEL_VERSION_4_0_0.equals(rawModel.getModelVersion()) && modelSource.getPath() != null) { - Path pomFile = modelSource.getPath(); - - try { - ModelTransformerContextBuilder transformerContextBuilder = getTransformerContextBuilder(request); - if (transformerContextBuilder != null) { - ModelTransformerContext context = transformerContextBuilder.initialize(request, problems); - rawModel = this.transformer.transform(context, rawModel, pomFile); - } - } catch (ModelTransformerException e) { - problems.add(Severity.FATAL, ModelProblem.Version.V40, null, e); - } - } - - String namespace = rawModel.getNamespaceUri(); - if (rawModel.getModelVersion() == null && namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) { - rawModel = rawModel.withModelVersion(namespace.substring(NAMESPACE_PREFIX.length())); - } - - for (var transformer : transformers) { - rawModel = transformer.transformRawModel(rawModel); - } - - modelValidator.validateRawModel(rawModel, request, problems); + return string != null && !string.isEmpty(); + } - if (hasFatalErrors(problems)) { - throw problems.newModelBuilderException(); + public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException { + DefaultModelBuilderSession build = new DefaultModelBuilderSession(request); + Model model = build.readRawModel(); + if (((ModelProblemCollector) build).hasErrors()) { + throw build.newModelBuilderException(); } - - return new ModelData(modelSource, rawModel); + return model; } static String getGroupId(Model model) { @@ -878,7 +1785,7 @@ static String getGroupId(Model model) { return groupId; } - private String getVersion(Model model) { + static String getVersion(Model model) { String version = model.getVersion(); if (version == null && model.getParent() != null) { version = model.getParent().getVersion(); @@ -903,58 +1810,6 @@ private DefaultProfileActivationContext getProfileActivationContext(ModelBuilder return context; } - private void checkPluginVersions(List lineage, ModelBuilderRequest request, ModelProblemCollector problems) { - if (request.getValidationLevel() < ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { - return; - } - - Map plugins = new HashMap<>(); - Map versions = new HashMap<>(); - Map managedVersions = new HashMap<>(); - - for (int i = lineage.size() - 1; i >= 0; i--) { - Model model = lineage.get(i); - Build build = model.getBuild(); - if (build != null) { - for (Plugin plugin : build.getPlugins()) { - String key = plugin.getKey(); - if (versions.get(key) == null) { - versions.put(key, plugin.getVersion()); - plugins.put(key, plugin); - } - } - PluginManagement mgmt = build.getPluginManagement(); - if (mgmt != null) { - for (Plugin plugin : mgmt.getPlugins()) { - String key = plugin.getKey(); - managedVersions.computeIfAbsent(key, k -> plugin.getVersion()); - } - } - } - } - - for (String key : versions.keySet()) { - if (versions.get(key) == null && managedVersions.get(key) == null) { - InputLocation location = plugins.get(key).getLocation(""); - problems.add( - Severity.WARNING, - ModelProblem.Version.V20, - "'build.plugins.plugin.version' for " + key + " is missing.", - location); - } - } - } - - private Model assembleInheritance( - List lineage, ModelBuilderRequest request, ModelProblemCollector problems) { - Model parent = lineage.get(lineage.size() - 1); - for (int i = lineage.size() - 2; i >= 0; i--) { - Model child = lineage.get(i); - parent = inheritanceAssembler.assembleModelInheritance(child, parent, request, problems); - } - return parent; - } - private Map getProfileActivations(Model model) { return model.getProfiles().stream() .filter(p -> p.getActivation() != null) @@ -1003,146 +1858,6 @@ private Model interpolateModel(Model model, ModelBuilderRequest request, ModelPr return interpolatedModel; } - private ModelData readParent( - Model childModel, - ModelSource childSource, - ModelBuilderRequest request, - DefaultModelProblemCollector problems) - throws ModelBuilderException { - ModelData parentData = null; - - Parent parent = childModel.getParent(); - if (parent != null) { - parentData = readParentLocally(childModel, childSource, request, problems); - if (parentData == null) { - parentData = readParentExternally(childModel, request, problems); - } - - Model parentModel = parentData.model(); - if (!"pom".equals(parentModel.getPackaging())) { - problems.add( - Severity.ERROR, - ModelProblem.Version.BASE, - "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel) - + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"", - parentModel.getLocation("packaging")); - } - } - - return parentData; - } - - private ModelData readParentLocally( - Model childModel, - ModelSource childSource, - ModelBuilderRequest request, - DefaultModelProblemCollector problems) - throws ModelBuilderException { - final Parent parent = childModel.getParent(); - final ModelSource candidateSource; - final Model candidateModel; - final WorkspaceModelResolver resolver = getWorkspaceModelResolver(request); - if (resolver == null) { - candidateSource = getParentPomFile(childModel, childSource); - - if (candidateSource == null) { - return null; - } - - ModelBuilderRequest candidateBuildRequest = ModelBuilderRequest.build(request, candidateSource); - - candidateModel = readRawModel(candidateBuildRequest, problems); - } else { - try { - candidateModel = - resolver.resolveRawModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); - } catch (ModelBuilderException e) { - problems.add(Severity.FATAL, ModelProblem.Version.BASE, e.getMessage(), parent.getLocation(""), e); - throw problems.newModelBuilderException(); - } - if (candidateModel == null) { - return null; - } - candidateSource = ModelSource.fromPath(candidateModel.getPomFile()); - } - - // - // TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we - // have a model that is suitable, yet more checks are done here and the one for the version is problematic - // before because with parents as ranges it will never work in this scenario. - // - - String groupId = getGroupId(candidateModel); - String artifactId = candidateModel.getArtifactId(); - - if (groupId == null - || !groupId.equals(parent.getGroupId()) - || artifactId == null - || !artifactId.equals(parent.getArtifactId())) { - StringBuilder buffer = new StringBuilder(256); - buffer.append("'parent.relativePath'"); - if (childModel != problems.getRootModel()) { - buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel)); - } - buffer.append(" points at ").append(groupId).append(':').append(artifactId); - buffer.append(" instead of ").append(parent.getGroupId()).append(':'); - buffer.append(parent.getArtifactId()).append(", please verify your project structure"); - - problems.setSource(childModel); - problems.add(Severity.WARNING, ModelProblem.Version.BASE, buffer.toString(), parent.getLocation("")); - return null; - } - - String version = getVersion(candidateModel); - if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) { - try { - VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion()); - if (!parentRange.contains(versionParser.parseVersion(version))) { - // version skew drop back to resolution from the repository - return null; - } - - // Validate versions aren't inherited when using parent ranges the same way as when read externally. - String rawChildModelVersion = childModel.getVersion(); - - if (rawChildModelVersion == null) { - // Message below is checked for in the MNG-2199 core IT. - problems.add( - Severity.FATAL, - ModelProblem.Version.V31, - "Version must be a constant", - childModel.getLocation("")); - - } else { - if (rawChildVersionReferencesParent(rawChildModelVersion)) { - // Message below is checked for in the MNG-2199 core IT. - problems.add( - Severity.FATAL, - ModelProblem.Version.V31, - "Version must be a constant", - childModel.getLocation("version")); - } - } - - // MNG-2199: What else to check here ? - } catch (VersionParserException e) { - // invalid version range, so drop back to resolution from the repository - return null; - } - } - - // - // Here we just need to know that a version is fine to use but this validation we can do in our workspace - // resolver. - // - - /* - * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; } - */ - - return new ModelData(candidateSource, candidateModel); - } - private boolean rawChildVersionReferencesParent(String rawChildModelVersion) { return rawChildModelVersion.equals("${pom.version}") || rawChildModelVersion.equals("${project.version}") @@ -1159,249 +1874,10 @@ private ModelSource getParentPomFile(Model childModel, ModelSource source) { } } - private ModelData readParentExternally( - Model childModel, ModelBuilderRequest request, DefaultModelProblemCollector problems) - throws ModelBuilderException { - problems.setSource(childModel); - - Parent parent = childModel.getParent(); - - String groupId = parent.getGroupId(); - String artifactId = parent.getArtifactId(); - String version = parent.getVersion(); - - ModelResolver modelResolver = getModelResolver(request); - Objects.requireNonNull( - modelResolver, - String.format( - "request.modelResolver cannot be null (parent POM %s and POM %s)", - ModelProblemUtils.toId(groupId, artifactId, version), - ModelProblemUtils.toSourceHint(childModel))); - - ModelSource modelSource; - try { - AtomicReference modified = new AtomicReference<>(); - modelSource = modelResolver.resolveModel( - request.getSession(), request.getModelRepositoryHolder().getRepositories(), parent, modified); - if (modified.get() != null) { - parent = modified.get(); - } - } catch (ModelResolverException e) { - // Message below is checked for in the MNG-2199 core IT. - StringBuilder buffer = new StringBuilder(256); - buffer.append("Non-resolvable parent POM"); - if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) { - buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version)); - } - if (childModel != problems.getRootModel()) { - buffer.append(" for ").append(ModelProblemUtils.toId(childModel)); - } - buffer.append(": ").append(e.getMessage()); - if (childModel.getProjectDirectory() != null) { - if (parent.getRelativePath() == null || parent.getRelativePath().isEmpty()) { - buffer.append(" and 'parent.relativePath' points at no local POM"); - } else { - buffer.append(" and 'parent.relativePath' points at wrong local POM"); - } - } - - problems.add(Severity.FATAL, ModelProblem.Version.BASE, buffer.toString(), parent.getLocation(""), e); - throw problems.newModelBuilderException(); - } - - int validationLevel = Math.min(request.getValidationLevel(), ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0); - ModelBuilderRequest lenientRequest = ModelBuilderRequest.builder(request) - .validationLevel(validationLevel) - .projectBuild(false) - .source(modelSource) - .build(); - - Model parentModel = readParentModel(lenientRequest, problems); - - if (!parent.getVersion().equals(version)) { - String rawChildModelVersion = childModel.getVersion(); - - if (rawChildModelVersion == null) { - // Message below is checked for in the MNG-2199 core IT. - problems.add( - Severity.FATAL, - ModelProblem.Version.V31, - "Version must be a constant", - childModel.getLocation("")); - - } else { - if (rawChildVersionReferencesParent(rawChildModelVersion)) { - // Message below is checked for in the MNG-2199 core IT. - problems.add( - Severity.FATAL, - ModelProblem.Version.V31, - "Version must be a constant", - childModel.getLocation("version")); - } - } - - // MNG-2199: What else to check here ? - } - - return new ModelData(modelSource, parentModel); - } - - Model readParentModel(ModelBuilderRequest request, DefaultModelProblemCollector problems) { - ModelSource modelSource = request.getSource(); - Model model = cache(getModelCache(request), modelSource, PARENT, () -> doReadParentModel(request, problems)); - return model; - } - - private Model doReadParentModel(ModelBuilderRequest request, DefaultModelProblemCollector problems) { - Model raw = readRawModel(request, problems); - - ModelData parentData; - if (raw.getParent() != null) { - parentData = readParentExternally(raw, request, problems); - } else { - String superModelVersion = raw.getModelVersion() != null ? raw.getModelVersion() : "4.0.0"; - if (!VALID_MODEL_VERSIONS.contains(superModelVersion)) { - // Maven 3.x is always using 4.0.0 version to load the supermodel, so - // do the same when loading a dependency. The model validator will also - // check that field later. - superModelVersion = MODEL_VERSION_4_0_0; - } - parentData = new ModelData(null, getSuperModel(superModelVersion)); - } - - Model parent = inheritanceAssembler.assembleModelInheritance(raw, parentData.model(), request, problems); - return parent.withParent(null); - } - private Model getSuperModel(String modelVersion) { return superPomProvider.getSuperPom(modelVersion); } - private Model importDependencyManagement( - Model model, - ModelBuilderRequest request, - DefaultModelProblemCollector problems, - Collection importIds) { - DependencyManagement depMgmt = model.getDependencyManagement(); - - if (depMgmt == null) { - return model; - } - - String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion(); - - importIds.add(importing); - - List importMgmts = null; - - List deps = new ArrayList<>(depMgmt.getDependencies()); - for (Iterator it = deps.iterator(); it.hasNext(); ) { - Dependency dependency = it.next(); - - if (!("pom".equals(dependency.getType()) && "import".equals(dependency.getScope())) - || "bom".equals(dependency.getType())) { - continue; - } - - it.remove(); - - DependencyManagement importMgmt = loadDependencyManagement(model, request, problems, dependency, importIds); - - if (importMgmt != null) { - if (importMgmts == null) { - importMgmts = new ArrayList<>(); - } - - importMgmts.add(importMgmt); - } - } - - importIds.remove(importing); - - model = model.withDependencyManagement(model.getDependencyManagement().withDependencies(deps)); - - return dependencyManagementImporter.importManagement(model, importMgmts, request, problems); - } - - private DependencyManagement loadDependencyManagement( - Model model, - ModelBuilderRequest request, - DefaultModelProblemCollector problems, - Dependency dependency, - Collection importIds) { - String groupId = dependency.getGroupId(); - String artifactId = dependency.getArtifactId(); - String version = dependency.getVersion(); - - if (groupId == null || groupId.isEmpty()) { - problems.add( - Severity.ERROR, - ModelProblem.Version.BASE, - "'dependencyManagement.dependencies.dependency.groupId' for " + dependency.getManagementKey() - + " is missing.", - dependency.getLocation("")); - return null; - } - if (artifactId == null || artifactId.isEmpty()) { - problems.add( - Severity.ERROR, - ModelProblem.Version.BASE, - "'dependencyManagement.dependencies.dependency.artifactId' for " + dependency.getManagementKey() - + " is missing.", - dependency.getLocation("")); - return null; - } - if (version == null || version.isEmpty()) { - problems.add( - Severity.ERROR, - ModelProblem.Version.BASE, - "'dependencyManagement.dependencies.dependency.version' for " + dependency.getManagementKey() - + " is missing.", - dependency.getLocation("")); - return null; - } - - String imported = groupId + ':' + artifactId + ':' + version; - - if (importIds.contains(imported)) { - StringBuilder message = - new StringBuilder("The dependencies of type=pom and with scope=import form a cycle: "); - for (String modelId : importIds) { - message.append(modelId).append(" -> "); - } - message.append(imported); - problems.add(Severity.ERROR, ModelProblem.Version.BASE, message.toString()); - - return null; - } - - Model importModel = cache( - getModelCache(request), - groupId, - artifactId, - version, - IMPORT, - () -> doLoadDependencyManagement( - model, request, problems, dependency, groupId, artifactId, version, importIds)); - DependencyManagement importMgmt = importModel != null ? importModel.getDependencyManagement() : null; - if (importMgmt == null) { - importMgmt = DependencyManagement.newInstance(); - } - - // [MNG-5600] Dependency management import should support exclusions. - List exclusions = dependency.getExclusions(); - if (importMgmt != null && !exclusions.isEmpty()) { - // Dependency excluded from import. - List dependencies = importMgmt.getDependencies().stream() - .filter(candidate -> exclusions.stream().noneMatch(exclusion -> match(exclusion, candidate))) - .map(candidate -> addExclusions(candidate, exclusions)) - .collect(Collectors.toList()); - importMgmt = importMgmt.withDependencies(dependencies); - } - - return importMgmt; - } - private static org.apache.maven.api.model.Dependency addExclusions( org.apache.maven.api.model.Dependency candidate, List exclusions) { return candidate.withExclusions(Stream.concat(candidate.getExclusions().stream(), exclusions.stream()) @@ -1417,145 +1893,6 @@ private boolean match(String match, String text) { return match.equals("*") || match.equals(text); } - @SuppressWarnings("checkstyle:parameternumber") - private Model doLoadDependencyManagement( - Model model, - ModelBuilderRequest request, - DefaultModelProblemCollector problems, - Dependency dependency, - String groupId, - String artifactId, - String version, - Collection importIds) { - final WorkspaceModelResolver workspaceResolver = getWorkspaceModelResolver(request); - final ModelResolver modelResolver = getModelResolver(request); - if (workspaceResolver == null && modelResolver == null) { - throw new NullPointerException(String.format( - "request.workspaceModelResolver and request.modelResolver cannot be null (parent POM %s and POM %s)", - ModelProblemUtils.toId(groupId, artifactId, version), ModelProblemUtils.toSourceHint(model))); - } - - Model importModel = null; - if (workspaceResolver != null) { - try { - importModel = workspaceResolver.resolveEffectiveModel(groupId, artifactId, version); - } catch (ModelBuilderException e) { - problems.add(Severity.FATAL, ModelProblem.Version.BASE, null, e); - return null; - } - } - - // no workspace resolver or workspace resolver returned null (i.e. model not in workspace) - if (importModel == null) { - final ModelSource importSource; - try { - importSource = modelResolver.resolveModel( - request.getSession(), - request.getModelRepositoryHolder().getRepositories(), - dependency, - new AtomicReference<>()); - } catch (ModelBuilderException e) { - StringBuilder buffer = new StringBuilder(256); - buffer.append("Non-resolvable import POM"); - if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) { - buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version)); - } - buffer.append(": ").append(e.getMessage()); - - problems.add( - Severity.ERROR, ModelProblem.Version.BASE, buffer.toString(), dependency.getLocation(""), e); - return null; - } - - Path rootDirectory; - try { - rootDirectory = request.getSession().getRootDirectory(); - } catch (IllegalStateException e) { - rootDirectory = null; - } - if (importSource.getPath() != null && rootDirectory != null) { - Path sourcePath = importSource.getPath(); - if (sourcePath.startsWith(rootDirectory)) { - problems.add( - Severity.WARNING, - ModelProblem.Version.BASE, - "BOM imports from within reactor should be avoided", - dependency.getLocation("")); - } - } - - final ModelBuilderResult importResult; - try { - ModelBuilderRequest importRequest = ModelBuilderRequest.builder() - .session(request.getSession()) - .repositories(request.getModelRepositoryHolder().getRepositories()) - .validationLevel(ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL) - .systemProperties(request.getSystemProperties()) - .userProperties(request.getUserProperties()) - .source(importSource) - .modelResolver(modelResolver) - .modelRepositoryHolder( - request.getModelRepositoryHolder().copy()) - .twoPhaseBuilding(false) - .build(); - importResult = build(importRequest, importIds); - } catch (ModelBuilderException e) { - e.getResult().getProblems().forEach(problems::add); - return null; - } - - importResult.getProblems().forEach(problems::add); - - importModel = importResult.getEffectiveModel(); - } - - return importModel; - } - - private static T cache( - ModelCache cache, String groupId, String artifactId, String version, String tag, Callable supplier) { - return cache.computeIfAbsent(groupId, artifactId, version, tag, asSupplier(supplier)); - } - - private static T cache(ModelCache cache, Source source, String tag, Callable supplier) { - return cache.computeIfAbsent(source, tag, asSupplier(supplier)); - } - - private static Supplier asSupplier(Callable supplier) { - return () -> { - try { - return supplier.call(); - } catch (Exception e) { - uncheckedThrow(e); - return null; - } - }; - } - - static void uncheckedThrow(Throwable t) throws T { - throw (T) t; // rely on vacuous cast - } - - private Model fireEvent( - Model model, - ModelBuilderRequest request, - ModelProblemCollector problems, - BiConsumer catapult) { - ModelBuildingListener listener = getModelBuildingListener(request); - - if (listener != null) { - AtomicReference m = new AtomicReference<>(model); - - ModelBuildingEvent event = new DefaultModelBuildingEvent(model, m::set, request, problems); - - catapult.accept(listener, event); - - return m.get(); - } - - return model; - } - private boolean containsCoordinates(String message, String groupId, String artifactId, String version) { return message != null && (groupId == null || message.contains(groupId)) @@ -1563,37 +1900,5 @@ private boolean containsCoordinates(String message, String groupId, String artif && (version == null || message.contains(version)); } - protected boolean hasModelErrors(ModelProblemCollector problems) { - return problems.hasErrors(); - } - - protected boolean hasFatalErrors(ModelProblemCollector problems) { - return problems.hasFatalErrors(); - } - - ModelProcessor getModelProcessor() { - return modelProcessor; - } - - private ModelCache getModelCache(ModelBuilderRequest request) { - return request.getSession() - .getData() - .computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance); - } - - private static ModelBuildingListener getModelBuildingListener(ModelBuilderRequest request) { - return (ModelBuildingListener) request.getListener(); - } - - private static WorkspaceModelResolver getWorkspaceModelResolver(ModelBuilderRequest request) { - return null; // request.getWorkspaceModelResolver(); - } - - private static ModelResolver getModelResolver(ModelBuilderRequest request) { - return request.getModelResolver(); - } - - private static ModelTransformerContextBuilder getTransformerContextBuilder(ModelBuilderRequest request) { - return request.getTransformerContextBuilder(); - } + record GAKey(String groupId, String artifactId) {} } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java index e10a7e864289..f834577a596c 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilderResult.java @@ -19,11 +19,9 @@ package org.apache.maven.internal.impl.model; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,128 +29,83 @@ import org.apache.maven.api.model.Profile; import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelProblem; +import org.apache.maven.api.services.ModelSource; /** * Collects the output of the model builder. - * */ class DefaultModelBuilderResult implements ModelBuilderResult { + private ModelSource source; private Model fileModel; - private Model activatedFileModel; - + private Model rawModel; + private Model parentModel; private Model effectiveModel; - - private List modelIds; - - private Map rawModels; - - private Map> activePomProfiles; - + private List activePomProfiles; private List activeExternalProfiles; + private final List problems = new CopyOnWriteArrayList<>(); + private final DefaultModelBuilderResult problemHolder; - private List problems; + private final List children = new ArrayList<>(); DefaultModelBuilderResult() { - modelIds = new ArrayList<>(); - rawModels = new HashMap<>(); - activePomProfiles = new HashMap<>(); - activeExternalProfiles = new ArrayList<>(); - problems = new ArrayList<>(); - } - - DefaultModelBuilderResult(ModelBuilderResult result) { - this(); - this.activeExternalProfiles.addAll(result.getActiveExternalProfiles()); - this.effectiveModel = result.getEffectiveModel(); - this.fileModel = result.getFileModel(); - this.problems.addAll(result.getProblems()); - - for (String modelId : result.getModelIds()) { - this.modelIds.add(modelId); - this.rawModels.put(modelId, result.getRawModel(modelId).orElseThrow()); - this.activePomProfiles.put(modelId, result.getActivePomProfiles(modelId)); - } + this(null); } - @Override - public Model getFileModel() { - return fileModel; + DefaultModelBuilderResult(DefaultModelBuilderResult problemHolder) { + this.problemHolder = problemHolder; } - public DefaultModelBuilderResult setFileModel(Model fileModel) { - this.fileModel = fileModel; - return this; + public ModelSource getSource() { + return source; } - public Model getActivatedFileModel() { - return activatedFileModel; - } - - public DefaultModelBuilderResult setActivatedFileModel(Model activatedFileModel) { - this.activatedFileModel = activatedFileModel; - return this; + public void setSource(ModelSource source) { + this.source = source; } @Override - public Model getEffectiveModel() { - return effectiveModel; + public Model getFileModel() { + return fileModel; } - public DefaultModelBuilderResult setEffectiveModel(Model model) { - this.effectiveModel = model; - return this; + public void setFileModel(Model fileModel) { + this.fileModel = fileModel; } @Override - public List getModelIds() { - return modelIds; + public Model getRawModel() { + return rawModel; } - public DefaultModelBuilderResult addModelId(String modelId) { - // Intentionally notNull because Super POM may not contain a modelId - Objects.requireNonNull(modelId, "modelId cannot be null"); - - modelIds.add(modelId); - - return this; + public void setRawModel(Model rawModel) { + this.rawModel = rawModel; } @Override - public Model getRawModel() { - return rawModels.get(modelIds.get(0)); + public Model getParentModel() { + return parentModel; } - @Override - public Optional getRawModel(String modelId) { - return Optional.ofNullable(rawModels.get(modelId)); + public void setParentModel(Model parentModel) { + this.parentModel = parentModel; } - public DefaultModelBuilderResult setRawModel(String modelId, Model rawModel) { - // Intentionally notNull because Super POM may not contain a modelId - Objects.requireNonNull(modelId, "modelId cannot be null"); - - rawModels.put(modelId, rawModel); + @Override + public Model getEffectiveModel() { + return effectiveModel; + } - return this; + public void setEffectiveModel(Model model) { + this.effectiveModel = model; } @Override - public List getActivePomProfiles(String modelId) { - List profiles = activePomProfiles.get(modelId); - return profiles != null ? profiles : List.of(); + public List getActivePomProfiles() { + return activePomProfiles; } - public DefaultModelBuilderResult setActivePomProfiles(String modelId, List activeProfiles) { - // Intentionally notNull because Super POM may not contain a modelId - Objects.requireNonNull(modelId, "modelId cannot be null"); - - if (activeProfiles != null) { - this.activePomProfiles.put(modelId, new ArrayList<>(activeProfiles)); - } else { - this.activePomProfiles.remove(modelId); - } - - return this; + public void setActivePomProfiles(List activeProfiles) { + this.activePomProfiles = activeProfiles; } @Override @@ -160,34 +113,50 @@ public List getActiveExternalProfiles() { return activeExternalProfiles; } - public DefaultModelBuilderResult setActiveExternalProfiles(List activeProfiles) { - if (activeProfiles != null) { - this.activeExternalProfiles = new ArrayList<>(activeProfiles); - } else { - this.activeExternalProfiles.clear(); - } - - return this; + public void setActiveExternalProfiles(List activeProfiles) { + this.activeExternalProfiles = activeProfiles; } + /** + * Returns an unmodifiable list of problems encountered during the model building process. + * + * @return a list of ModelProblem instances representing the encountered problems, + * guaranteed to be non-null but possibly empty. + */ @Override public List getProblems() { - return problems; - } - - public DefaultModelBuilderResult setProblems(List problems) { - if (problems != null) { - this.problems = new ArrayList<>(problems); - } else { - this.problems.clear(); + return Collections.unmodifiableList(problems); + } + + /** + * Adds a given problem to the list of problems and propagates it to the parent result if present. + * + * @param problem The problem to be added. It must be an instance of ModelProblem. + */ + public void addProblem(ModelProblem problem) { + problems.add(problem); + if (problemHolder != null) { + problemHolder.addProblem(problem); } + } - return this; + @Override + public List getChildren() { + return children; } public String toString() { - if (!modelIds.isEmpty()) { - String modelId = modelIds.get(0); + String modelId; + if (effectiveModel != null) { + modelId = effectiveModel.getId(); + } else if (rawModel != null) { + modelId = rawModel.getId(); + } else if (fileModel != null) { + modelId = fileModel.getId(); + } else { + modelId = null; + } + if (!problems.isEmpty()) { StringBuilder sb = new StringBuilder(); sb.append(problems.size()) .append( @@ -203,7 +172,11 @@ public String toString() { sb.append(" - ["); sb.append(problem.getSeverity()); sb.append("] "); - sb.append(problem.getMessage()); + if (problem.getMessage() != null && !problem.getMessage().isEmpty()) { + sb.append(problem.getMessage()); + } else if (problem.getException() != null) { + sb.append(problem.getException().toString()); + } String loc = Stream.of( problem.getModelId().equals(modelId) ? problem.getModelId() : "", problem.getModelId().equals(modelId) ? problem.getSource() : "", @@ -217,6 +190,6 @@ public String toString() { } return sb.toString(); } - return null; + return modelId; } } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuildingEvent.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuildingEvent.java deleted file mode 100644 index db76a0686675..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuildingEvent.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.internal.impl.model; - -import java.util.function.Consumer; - -import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.ModelBuilderRequest; -import org.apache.maven.api.services.ModelProblemCollector; -import org.apache.maven.api.services.model.ModelBuildingEvent; - -/** - * Holds data relevant for a model building event. - */ -record DefaultModelBuildingEvent( - Model model, Consumer update, ModelBuilderRequest request, ModelProblemCollector problems) - implements ModelBuildingEvent {} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelInterpolator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelInterpolator.java index 53262e4b8a0b..ea2eee3ead76 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelInterpolator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelInterpolator.java @@ -148,7 +148,7 @@ private InnerInterpolator createInterpolator( } protected List getProjectPrefixes(ModelBuilderRequest request) { - return request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_4_0 + return request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM ? PROJECT_PREFIXES_4_0 : PROJECT_PREFIXES_3_1; } @@ -159,21 +159,17 @@ protected List createValueSources( ValueSource projectPrefixValueSource; ValueSource prefixlessObjectBasedValueSource; - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_4_0) { + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_4_0, model, false); prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model); } else { projectPrefixValueSource = new PrefixedObjectValueSource(PROJECT_PREFIXES_3_1, model, false); - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { - projectPrefixValueSource = - new ProblemDetectingValueSource(projectPrefixValueSource, PREFIX_POM, PREFIX_PROJECT, problems); - } + projectPrefixValueSource = + new ProblemDetectingValueSource(projectPrefixValueSource, PREFIX_POM, PREFIX_PROJECT, problems); prefixlessObjectBasedValueSource = new ObjectBasedValueSource(model); - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { - prefixlessObjectBasedValueSource = - new ProblemDetectingValueSource(prefixlessObjectBasedValueSource, "", PREFIX_PROJECT, problems); - } + prefixlessObjectBasedValueSource = + new ProblemDetectingValueSource(prefixlessObjectBasedValueSource, "", PREFIX_PROJECT, problems); } // NOTE: Order counts here! diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblem.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblem.java index f30bffddf22a..0787b0eec118 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblem.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblem.java @@ -107,7 +107,7 @@ public DefaultModelProblem( this.columnNumber = columnNumber; this.modelId = (modelId != null) ? modelId : ""; this.exception = exception; - this.version = version; + this.version = version != null ? version : Version.BASE; } @Override diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblemCollector.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblemCollector.java deleted file mode 100644 index 8df01905227a..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelProblemCollector.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.internal.impl.model; - -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; - -import org.apache.maven.api.model.InputLocation; -import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.BuilderProblem; -import org.apache.maven.api.services.ModelBuilderException; -import org.apache.maven.api.services.ModelBuilderResult; -import org.apache.maven.api.services.ModelProblem; -import org.apache.maven.api.services.ModelProblemCollector; -import org.apache.maven.api.spi.ModelParserException; - -/** - * Collects problems that are encountered during model building. The primary purpose of this component is to account for - * the fact that the problem reporter has/should not have information about the calling context and hence cannot provide - * an expressive source hint for the model problem. Instead, the source hint is configured by the model builder before - * it delegates to other components that potentially encounter problems. Then, the problem reporter can focus on - * providing a simple error message, leaving the donkey work of creating a nice model problem to this component. - * - */ -class DefaultModelProblemCollector implements ModelProblemCollector { - - private final ModelBuilderResult result; - - private List problems; - - private String source; - - private Model sourceModel; - - private Model rootModel; - - private Set severities = EnumSet.noneOf(ModelProblem.Severity.class); - - DefaultModelProblemCollector(ModelBuilderResult result) { - this.result = result; - this.problems = result.getProblems(); - - for (ModelProblem problem : this.problems) { - severities.add(problem.getSeverity()); - } - } - - public boolean hasFatalErrors() { - return severities.contains(ModelProblem.Severity.FATAL); - } - - public boolean hasErrors() { - return severities.contains(ModelProblem.Severity.ERROR) || severities.contains(ModelProblem.Severity.FATAL); - } - - @Override - public List getProblems() { - return problems; - } - - public void setSource(String source) { - this.source = source; - this.sourceModel = null; - } - - public void setSource(Model source) { - this.sourceModel = source; - this.source = null; - - if (rootModel == null) { - rootModel = source; - } - } - - private String getSource() { - if (source == null && sourceModel != null) { - source = ModelProblemUtils.toPath(sourceModel); - } - return source; - } - - private String getModelId() { - return ModelProblemUtils.toId(sourceModel); - } - - public void setRootModel(Model rootModel) { - this.rootModel = rootModel; - } - - public Model getRootModel() { - return rootModel; - } - - public String getRootModelId() { - return ModelProblemUtils.toId(rootModel); - } - - @Override - public void add(ModelProblem problem) { - problems.add(problem); - - severities.add(problem.getSeverity()); - } - - public void addAll(Collection problems) { - this.problems.addAll(problems); - - for (ModelProblem problem : problems) { - severities.add(problem.getSeverity()); - } - } - - @Override - public void add(BuilderProblem.Severity severity, ModelProblem.Version version, String message) { - add(severity, version, message, null, null); - } - - @Override - public void add( - BuilderProblem.Severity severity, ModelProblem.Version version, String message, InputLocation location) { - add(severity, version, message, location, null); - } - - @Override - public void add( - BuilderProblem.Severity severity, ModelProblem.Version version, String message, Exception exception) { - add(severity, version, message, null, exception); - } - - public void add( - BuilderProblem.Severity severity, - ModelProblem.Version version, - String message, - InputLocation location, - Exception exception) { - int line = -1; - int column = -1; - String source = null; - String modelId = null; - - if (location != null) { - line = location.getLineNumber(); - column = location.getColumnNumber(); - if (location.getSource() != null) { - modelId = location.getSource().getModelId(); - source = location.getSource().getLocation(); - } - } - - if (modelId == null) { - modelId = getModelId(); - source = getSource(); - } - - if (line <= 0 && column <= 0 && exception instanceof ModelParserException e) { - line = e.getLineNumber(); - column = e.getColumnNumber(); - } - - ModelProblem problem = - new DefaultModelProblem(message, severity, version, source, line, column, modelId, exception); - - add(problem); - } - - public ModelBuilderException newModelBuilderException() { - ModelBuilderResult result = this.result; - if (result.getModelIds().isEmpty()) { - DefaultModelBuilderResult tmp = new DefaultModelBuilderResult(); - tmp.setEffectiveModel(result.getEffectiveModel()); - tmp.setProblems(getProblems()); - tmp.setActiveExternalProfiles(result.getActiveExternalProfiles()); - String id = getRootModelId(); - tmp.addModelId(id); - tmp.setRawModel(id, getRootModel()); - result = tmp; - } - return new ModelBuilderException(result); - } -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContext.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContext.java deleted file mode 100644 index 28c61d962a08..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContext.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.internal.impl.model; - -import java.nio.file.Path; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; - -import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.ModelTransformerContext; -import org.apache.maven.api.services.model.ModelProcessor; - -/** - * - * @since 4.0.0 - */ -class DefaultModelTransformerContext implements ModelTransformerContext { - final ModelProcessor modelLocator; - - final Map userProperties = new ConcurrentHashMap<>(); - - final Map modelByPath = new ConcurrentHashMap<>(); - - final Map modelByGA = new ConcurrentHashMap<>(); - - public static class Holder { - private volatile boolean set; - private volatile Model model; - - Holder() {} - - Holder(Model model) { - this.model = Objects.requireNonNull(model); - this.set = true; - } - - public static Model deref(Holder holder) { - return holder != null ? holder.get() : null; - } - - public Model get() { - if (!set) { - synchronized (this) { - if (!set) { - try { - this.wait(); - } catch (InterruptedException e) { - // Ignore - } - } - } - } - return model; - } - - public Model computeIfAbsent(Supplier supplier) { - if (!set) { - synchronized (this) { - if (!set) { - this.set = true; - this.model = supplier.get(); - this.notifyAll(); - } - } - } - return model; - } - } - - DefaultModelTransformerContext(ModelProcessor modelLocator) { - this.modelLocator = modelLocator; - } - - @Override - public String getUserProperty(String key) { - return userProperties.get(key); - } - - @Override - public Model getRawModel(Path from, Path p) { - return Holder.deref(modelByPath.get(p)); - } - - @Override - public Model getRawModel(Path from, String groupId, String artifactId) { - return Holder.deref(modelByGA.get(new GAKey(groupId, artifactId))); - } - - @Override - public Path locate(Path path) { - return modelLocator.locateExistingPom(path); - } - - static class GAKey { - private final String groupId; - private final String artifactId; - private final int hashCode; - - GAKey(String groupId, String artifactId) { - this.groupId = groupId; - this.artifactId = artifactId; - this.hashCode = Objects.hash(groupId, artifactId); - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof GAKey)) { - return false; - } - - GAKey other = (GAKey) obj; - return Objects.equals(artifactId, other.artifactId) && Objects.equals(groupId, other.groupId); - } - } -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContextBuilder.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContextBuilder.java deleted file mode 100644 index 76d94a6d3130..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelTransformerContextBuilder.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.internal.impl.model; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.ModelBuilderException; -import org.apache.maven.api.services.ModelBuilderRequest; -import org.apache.maven.api.services.ModelProblem; -import org.apache.maven.api.services.ModelProblemCollector; -import org.apache.maven.api.services.ModelSource; -import org.apache.maven.api.services.ModelTransformerContext; -import org.apache.maven.api.services.ModelTransformerContextBuilder; -import org.apache.maven.internal.impl.model.DefaultModelTransformerContext.GAKey; -import org.apache.maven.internal.impl.model.DefaultModelTransformerContext.Holder; - -/** - * Builds up the transformer context. - * After the buildplan is ready, the build()-method returns the immutable context useful during distribution. - * This is an inner class, as it must be able to call readRawModel() - * - * @since 4.0.0 - */ -class DefaultModelTransformerContextBuilder implements ModelTransformerContextBuilder { - private final Graph dag = new Graph(); - private final DefaultModelBuilder defaultModelBuilder; - private final DefaultModelTransformerContext context; - - private final Map> mappedSources = new ConcurrentHashMap<>(64); - - private volatile boolean fullReactorLoaded; - - DefaultModelTransformerContextBuilder(DefaultModelBuilder defaultModelBuilder) { - this.defaultModelBuilder = defaultModelBuilder; - this.context = new DefaultModelTransformerContext(defaultModelBuilder.getModelProcessor()); - } - - /** - * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt - */ - @Override - public ModelTransformerContext initialize(ModelBuilderRequest request, ModelProblemCollector collector) { - // We must assume the TransformerContext was created using this.newTransformerContextBuilder() - DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector; - return new ModelTransformerContext() { - - @Override - public Path locate(Path path) { - return context.locate(path); - } - - @Override - public String getUserProperty(String key) { - return context.userProperties.computeIfAbsent( - key, k -> request.getUserProperties().get(key)); - } - - @Override - public Model getRawModel(Path from, String gId, String aId) { - Model model = findRawModel(from, gId, aId); - if (model != null) { - String groupId = DefaultModelBuilder.getGroupId(model); - context.modelByGA.put(new GAKey(groupId, model.getArtifactId()), new Holder(model)); - context.modelByPath.put(model.getPomFile(), new Holder(model)); - } - return model; - } - - @Override - public Model getRawModel(Path from, Path path) { - Model model = findRawModel(from, path); - if (model != null) { - String groupId = DefaultModelBuilder.getGroupId(model); - context.modelByGA.put( - new DefaultModelTransformerContext.GAKey(groupId, model.getArtifactId()), - new Holder(model)); - context.modelByPath.put(path, new Holder(model)); - } - return model; - } - - private Model findRawModel(Path from, String groupId, String artifactId) { - ModelSource source = getSource(groupId, artifactId); - if (source == null) { - // we need to check the whole reactor in case it's a dependency - loadFullReactor(); - source = getSource(groupId, artifactId); - } - if (source != null) { - if (!addEdge(from, source.getPath(), problems)) { - return null; - } - try { - ModelBuilderRequest gaBuildingRequest = ModelBuilderRequest.build(request, source); - return defaultModelBuilder.readRawModel(gaBuildingRequest, problems); - } catch (ModelBuilderException e) { - // gathered with problem collector - } - } - return null; - } - - private void loadFullReactor() { - if (!fullReactorLoaded) { - synchronized (DefaultModelTransformerContextBuilder.this) { - if (!fullReactorLoaded) { - doLoadFullReactor(); - fullReactorLoaded = true; - } - } - } - } - - private void doLoadFullReactor() { - Path rootDirectory; - try { - rootDirectory = request.getSession().getRootDirectory(); - } catch (IllegalStateException e) { - // if no root directory, bail out - return; - } - List toLoad = new ArrayList<>(); - Path root = defaultModelBuilder.getModelProcessor().locateExistingPom(rootDirectory); - toLoad.add(root); - while (!toLoad.isEmpty()) { - Path pom = toLoad.remove(0); - try { - ModelBuilderRequest gaBuildingRequest = - ModelBuilderRequest.build(request, ModelSource.fromPath(pom)); - Model rawModel = defaultModelBuilder.readFileModel(gaBuildingRequest, problems); - List subprojects = rawModel.getSubprojects(); - if (subprojects.isEmpty()) { - subprojects = rawModel.getModules(); - } - for (String subproject : subprojects) { - Path subprojectFile = defaultModelBuilder - .getModelProcessor() - .locateExistingPom(pom.getParent().resolve(subproject)); - if (subprojectFile != null) { - toLoad.add(subprojectFile); - } - } - } catch (ModelBuilderException e) { - // gathered with problem collector - } - } - } - - private Model findRawModel(Path from, Path p) { - if (!Files.isRegularFile(p)) { - throw new IllegalArgumentException("Not a regular file: " + p); - } - - if (!addEdge(from, p, problems)) { - return null; - } - - ModelBuilderRequest req = ModelBuilderRequest.build(request, ModelSource.fromPath(p)); - - try { - return defaultModelBuilder.readRawModel(req, problems); - } catch (ModelBuilderException e) { - // gathered with problem collector - } - return null; - } - }; - } - - private boolean addEdge(Path from, Path p, DefaultModelProblemCollector problems) { - try { - dag.addEdge(from.toString(), p.toString()); - return true; - } catch (Graph.CycleDetectedException e) { - problems.add(new DefaultModelProblem( - "Cycle detected between models at " + from + " and " + p, - ModelProblem.Severity.FATAL, - null, - null, - 0, - 0, - null, - e)); - return false; - } - } - - @Override - public ModelTransformerContext build() { - return context; - } - - public ModelSource getSource(String groupId, String artifactId) { - Set sources; - if (groupId != null) { - sources = mappedSources.get(groupId + ":" + artifactId); - if (sources == null) { - return null; - } - } else if (artifactId != null) { - sources = mappedSources.get(artifactId); - if (sources == null) { - return null; - } - } else { - return null; - } - return sources.stream() - .reduce((a, b) -> { - throw new IllegalStateException(String.format( - "No unique Source for %s:%s: %s and %s", - groupId, artifactId, a.getLocation(), b.getLocation())); - }) - .orElse(null); - } - - public void putSource(String groupId, String artifactId, ModelSource source) { - mappedSources - .computeIfAbsent(groupId + ":" + artifactId, k -> new HashSet<>()) - .add(source); - mappedSources.computeIfAbsent(artifactId, k -> new HashSet<>()).add(source); - } -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java index 07c26da3c969..65545d1b8eef 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java @@ -66,6 +66,7 @@ import org.apache.maven.api.services.BuilderProblem.Severity; import org.apache.maven.api.services.ModelBuilder; import org.apache.maven.api.services.ModelBuilderRequest; +import org.apache.maven.api.services.ModelProblem; import org.apache.maven.api.services.ModelProblem.Version; import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.model.ModelValidator; @@ -298,10 +299,42 @@ public DefaultModelValidator(ModelVersionProcessor versionProcessor) { @Override @SuppressWarnings("checkstyle:MethodLength") - public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblemCollector problems) { + public void validateFileModel( + Model m, int validationLevel, ModelBuilderRequest request, ModelProblemCollector problems) { Parent parent = m.getParent(); - if (request.getValidationLevel() == ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL) { + if (parent != null) { + validateStringNotEmpty( + "parent.groupId", problems, Severity.FATAL, Version.BASE, parent.getGroupId(), parent); + + validateStringNotEmpty( + "parent.artifactId", problems, Severity.FATAL, Version.BASE, parent.getArtifactId(), parent); + + if (equals(parent.getGroupId(), m.getGroupId()) && equals(parent.getArtifactId(), m.getArtifactId())) { + addViolation( + problems, + Severity.FATAL, + Version.BASE, + "parent.artifactId", + null, + "must be changed" + + ", the parent element cannot have the same groupId:artifactId as the project.", + parent); + } + + if (equals("LATEST", parent.getVersion()) || equals("RELEASE", parent.getVersion())) { + addViolation( + problems, + Severity.WARNING, + Version.BASE, + "parent.version", + null, + "is either LATEST or RELEASE (both of them are being deprecated)", + parent); + } + } + + if (validationLevel == ModelValidator.VALIDATION_LEVEL_MINIMAL) { // profiles: they are essential for proper model building (may contribute profiles, dependencies...) HashSet minProfileIds = new HashSet<>(); for (Profile profile : m.getProfiles()) { @@ -316,7 +349,7 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem profile); } } - } else if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { + } else if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { Set modules = new HashSet<>(); for (int i = 0, n = m.getModules().size(); i < n; i++) { String module = m.getModules().get(i); @@ -387,7 +420,7 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem } } - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); // The file pom may not contain the modelVersion yet, as it may be set later by the // ModelVersionXMLFilter. @@ -408,7 +441,8 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m); } - validate20RawDependencies(problems, m.getDependencies(), "dependencies.dependency.", EMPTY, request); + validate20RawDependencies( + problems, m.getDependencies(), "dependencies.dependency.", EMPTY, validationLevel, request); validate20RawDependenciesSelfReferencing( problems, m, m.getDependencies(), "dependencies.dependency", request); @@ -419,22 +453,35 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem m.getDependencyManagement().getDependencies(), "dependencyManagement.dependencies.dependency.", EMPTY, + validationLevel, request); } - validateRawRepositories(problems, m.getRepositories(), "repositories.repository.", EMPTY, request); + validateRawRepositories( + problems, m.getRepositories(), "repositories.repository.", EMPTY, validationLevel, request); validateRawRepositories( - problems, m.getPluginRepositories(), "pluginRepositories.pluginRepository.", EMPTY, request); + problems, + m.getPluginRepositories(), + "pluginRepositories.pluginRepository.", + EMPTY, + validationLevel, + request); Build build = m.getBuild(); if (build != null) { - validate20RawPlugins(problems, build.getPlugins(), "build.plugins.plugin.", EMPTY, request); + validate20RawPlugins( + problems, build.getPlugins(), "build.plugins.plugin.", EMPTY, validationLevel, request); PluginManagement mgmt = build.getPluginManagement(); if (mgmt != null) { validate20RawPlugins( - problems, mgmt.getPlugins(), "build.pluginManagement.plugins.plugin.", EMPTY, request); + problems, + mgmt.getPlugins(), + "build.pluginManagement.plugins.plugin.", + EMPTY, + validationLevel, + request); } } @@ -459,7 +506,12 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem validate30RawProfileActivation(problems, profile.getActivation(), prefix); validate20RawDependencies( - problems, profile.getDependencies(), prefix, "dependencies.dependency.", request); + problems, + profile.getDependencies(), + prefix, + "dependencies.dependency.", + validationLevel, + request); if (profile.getDependencyManagement() != null) { validate20RawDependencies( @@ -467,27 +519,40 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem profile.getDependencyManagement().getDependencies(), prefix, "dependencyManagement.dependencies.dependency.", + validationLevel, request); } validateRawRepositories( - problems, profile.getRepositories(), prefix, "repositories.repository.", request); + problems, + profile.getRepositories(), + prefix, + "repositories.repository.", + validationLevel, + request); validateRawRepositories( problems, profile.getPluginRepositories(), prefix, "pluginRepositories.pluginRepository.", + validationLevel, request); BuildBase buildBase = profile.getBuild(); if (buildBase != null) { - validate20RawPlugins(problems, buildBase.getPlugins(), prefix, "plugins.plugin.", request); + validate20RawPlugins( + problems, buildBase.getPlugins(), prefix, "plugins.plugin.", validationLevel, request); PluginManagement mgmt = buildBase.getPluginManagement(); if (mgmt != null) { validate20RawPlugins( - problems, mgmt.getPlugins(), prefix, "pluginManagement.plugins.plugin.", request); + problems, + mgmt.getPlugins(), + prefix, + "pluginManagement.plugins.plugin.", + validationLevel, + request); } } } @@ -495,7 +560,8 @@ public void validateFileModel(Model m, ModelBuilderRequest request, ModelProblem } @Override - public void validateRawModel(Model m, ModelBuilderRequest request, ModelProblemCollector problems) { + public void validateRawModel( + Model m, int validationLevel, ModelBuilderRequest request, ModelProblemCollector problems) { // [MNG-6074] Maven should produce an error if no model version has been set in a POM file used to build an // effective model. // @@ -610,8 +676,9 @@ private void validate20RawPlugins( List plugins, String prefix, String prefix2, + int validationLevel, ModelBuilderRequest request) { - Severity errOn31 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_1); + Severity errOn31 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_1); Map index = new HashMap<>(); @@ -690,7 +757,8 @@ private void validate20RawPlugins( @Override @SuppressWarnings("checkstyle:MethodLength") - public void validateEffectiveModel(Model m, ModelBuilderRequest request, ModelProblemCollector problems) { + public void validateEffectiveModel( + Model m, int validationLevel, ModelBuilderRequest request, ModelProblemCollector problems) { validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.BASE, m.getModelVersion(), m); validateCoordinatesId("groupId", problems, m.getGroupId(), m); @@ -739,17 +807,17 @@ public void validateEffectiveModel(Model m, ModelBuilderRequest request, ModelPr validateStringNotEmpty("version", problems, Severity.ERROR, Version.BASE, m.getVersion(), m); - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); - validateEffectiveDependencies(problems, m, m.getDependencies(), false, request); + validateEffectiveDependencies(problems, m, m.getDependencies(), false, validationLevel, request); DependencyManagement mgmt = m.getDependencyManagement(); if (mgmt != null) { - validateEffectiveDependencies(problems, m, mgmt.getDependencies(), true, request); + validateEffectiveDependencies(problems, m, mgmt.getDependencies(), true, validationLevel, request); } - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { - Severity errOn31 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_1); + if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { + Severity errOn31 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_1); validateBannedCharacters( EMPTY, "version", problems, errOn31, Version.V20, m.getVersion(), null, m, ILLEGAL_VERSION_CHARS); @@ -786,7 +854,13 @@ public void validateEffectiveModel(Model m, ModelBuilderRequest request, ModelPr "build.plugins.plugin.groupId", problems, Severity.ERROR, Version.V20, p.getGroupId(), p); validate20PluginVersion( - "build.plugins.plugin.version", problems, p.getVersion(), p.getKey(), p, request); + "build.plugins.plugin.version", + problems, + p.getVersion(), + p.getKey(), + p, + validationLevel, + request); validateBoolean( "build.plugins.plugin.inherited", @@ -808,13 +882,18 @@ public void validateEffectiveModel(Model m, ModelBuilderRequest request, ModelPr p.getKey(), p); - validate20EffectivePluginDependencies(problems, p, request); + validate20EffectivePluginDependencies(problems, p, validationLevel, request); } - validate20RawResources(problems, build.getResources(), "build.resources.resource.", request); + validate20RawResources( + problems, build.getResources(), "build.resources.resource.", validationLevel, request); validate20RawResources( - problems, build.getTestResources(), "build.testResources.testResource.", request); + problems, + build.getTestResources(), + "build.testResources.testResource.", + validationLevel, + request); } Reporting reporting = m.getReporting(); @@ -839,11 +918,13 @@ public void validateEffectiveModel(Model m, ModelBuilderRequest request, ModelPr } for (Repository repository : m.getRepositories()) { - validate20EffectiveRepository(problems, repository, "repositories.repository.", request); + validate20EffectiveRepository( + problems, repository, "repositories.repository.", validationLevel, request); } for (Repository repository : m.getPluginRepositories()) { - validate20EffectiveRepository(problems, repository, "pluginRepositories.pluginRepository.", request); + validate20EffectiveRepository( + problems, repository, "pluginRepositories.pluginRepository.", validationLevel, request); } DistributionManagement distMgmt = m.getDistributionManagement(); @@ -860,11 +941,16 @@ public void validateEffectiveModel(Model m, ModelBuilderRequest request, ModelPr } validate20EffectiveRepository( - problems, distMgmt.getRepository(), "distributionManagement.repository.", request); + problems, + distMgmt.getRepository(), + "distributionManagement.repository.", + validationLevel, + request); validate20EffectiveRepository( problems, distMgmt.getSnapshotRepository(), "distributionManagement.snapshotRepository.", + validationLevel, request); } } @@ -875,9 +961,10 @@ private void validate20RawDependencies( List dependencies, String prefix, String prefix2, + int validationLevel, ModelBuilderRequest request) { - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); - Severity errOn31 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_1); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn31 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_1); Map index = new HashMap<>(); @@ -907,7 +994,7 @@ private void validate20RawDependencies( } } else if ("system".equals(dependency.getScope())) { - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_1) { + if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_3_1) { addViolation( problems, Severity.WARNING, @@ -1015,15 +1102,16 @@ private void validateEffectiveDependencies( Model m, List dependencies, boolean management, + int validationLevel, ModelBuilderRequest request) { - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); String prefix = management ? "dependencyManagement.dependencies.dependency." : "dependencies.dependency."; for (Dependency d : dependencies) { - validateEffectiveDependency(problems, d, management, prefix, request); + validateEffectiveDependency(problems, d, management, prefix, validationLevel, request); - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { + if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { validateBoolean( prefix, "optional", problems, errOn30, Version.V20, d.getOptional(), d.getManagementKey(), d); @@ -1087,16 +1175,16 @@ private void validateEffectiveModelAgainstDependency( } private void validate20EffectivePluginDependencies( - ModelProblemCollector problems, Plugin plugin, ModelBuilderRequest request) { + ModelProblemCollector problems, Plugin plugin, int validationLevel, ModelBuilderRequest request) { List dependencies = plugin.getDependencies(); if (!dependencies.isEmpty()) { String prefix = "build.plugins.plugin[" + plugin.getKey() + "].dependencies.dependency."; - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); for (Dependency d : dependencies) { - validateEffectiveDependency(problems, d, false, prefix, request); + validateEffectiveDependency(problems, d, false, prefix, validationLevel, request); validateVersion( prefix, "version", problems, errOn30, Version.BASE, d.getVersion(), d.getManagementKey(), d); @@ -1122,6 +1210,7 @@ private void validateEffectiveDependency( Dependency d, boolean management, String prefix, + int validationLevel, ModelBuilderRequest request) { validateCoordinatesId( prefix, @@ -1194,9 +1283,9 @@ private void validateEffectiveDependency( d); } - if (request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) { + if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { for (Exclusion exclusion : d.getExclusions()) { - if (request.getValidationLevel() < ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0) { + if (validationLevel < ModelValidator.VALIDATION_LEVEL_MAVEN_3_0) { validateCoordinatesId( prefix, "exclusions.exclusion.groupId", @@ -1254,6 +1343,7 @@ private void validateRawRepositories( List repositories, String prefix, String prefix2, + int validationLevel, ModelBuilderRequest request) { Map index = new HashMap<>(); @@ -1292,7 +1382,7 @@ private void validateRawRepositories( Repository existing = index.get(key); if (existing != null) { - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); addViolation( problems, @@ -1310,9 +1400,13 @@ private void validateRawRepositories( } private void validate20EffectiveRepository( - ModelProblemCollector problems, Repository repository, String prefix, ModelBuilderRequest request) { + ModelProblemCollector problems, + Repository repository, + String prefix, + int validationLevel, + ModelBuilderRequest request) { if (repository != null) { - Severity errOn31 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_1); + Severity errOn31 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_1); validateBannedCharacters( prefix, @@ -1351,8 +1445,12 @@ private void validate20EffectiveRepository( } private void validate20RawResources( - ModelProblemCollector problems, List resources, String prefix, ModelBuilderRequest request) { - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + ModelProblemCollector problems, + List resources, + String prefix, + int validationLevel, + ModelBuilderRequest request) { + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); for (Resource resource : resources) { validateStringNotEmpty( @@ -1940,13 +2038,21 @@ private boolean validate20PluginVersion( String string, String sourceHint, InputLocationTracker tracker, + int validationLevel, ModelBuilderRequest request) { if (string == null) { - // NOTE: The check for missing plugin versions is handled directly by the model builder - return true; + addViolation( + problems, + Severity.WARNING, + ModelProblem.Version.V20, + fieldName, + sourceHint, + " is missing.", + tracker); + return false; } - Severity errOn30 = getSeverity(request, ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_3_0); + Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); if (!validateVersion(EMPTY, fieldName, problems, errOn30, Version.V20, string, sourceHint, tracker)) { return false; @@ -2026,10 +2132,6 @@ private static boolean equals(String s1, String s2) { return c1.equals(c2); } - private static Severity getSeverity(ModelBuilderRequest request, int errorThreshold) { - return getSeverity(request.getValidationLevel(), errorThreshold); - } - private static Severity getSeverity(int validationLevel, int errorThreshold) { if (validationLevel < errorThreshold) { return Severity.WARNING; diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultRootLocator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultRootLocator.java index e50db13f24ca..a5a36490189a 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultRootLocator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultRootLocator.java @@ -27,12 +27,24 @@ import java.nio.file.Files; import java.nio.file.Path; +import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.di.Named; import org.apache.maven.api.services.model.RootLocator; @Named public class DefaultRootLocator implements RootLocator { + @Override + @Nullable + public Path findRoot(Path basedir) { + Path rootDirectory = basedir; + while (rootDirectory != null && !isRootDirectory(rootDirectory)) { + rootDirectory = rootDirectory.getParent(); + } + return rootDirectory; + } + + @Override public boolean isRootDirectory(Path dir) { if (Files.isDirectory(dir.resolve(".mvn"))) { return true; diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/ModelData.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/ModelData.java index ffa8c10a93ec..ef0bf75f3465 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/ModelData.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/ModelData.java @@ -33,6 +33,7 @@ record ModelData(ModelSource source, Model model) { * @return The effective identifier of the model, never {@code null}. */ public String id() { + // TODO: this should be model.getId() but it fails for some reason // if source is null, it is the super model, which can be accessed via empty string return source != null ? source.getLocation() : ""; } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java index 3c16120b20b6..82bae431cfae 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultArtifactDescriptorReader.java @@ -34,10 +34,8 @@ import org.apache.maven.api.services.ModelBuilderRequest; import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelProblem; -import org.apache.maven.api.services.ModelRepositoryHolder; -import org.apache.maven.api.services.ModelResolver; -import org.apache.maven.api.services.ModelResolverException; import org.apache.maven.api.services.ModelSource; +import org.apache.maven.api.services.model.ModelResolverException; import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.impl.model.ModelProblemUtils; import org.eclipse.aether.RepositoryEvent; @@ -48,9 +46,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.impl.ArtifactDescriptorReader; import org.eclipse.aether.impl.ArtifactResolver; -import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryEventDispatcher; -import org.eclipse.aether.impl.VersionRangeResolver; import org.eclipse.aether.impl.VersionResolver; import org.eclipse.aether.repository.WorkspaceReader; import org.eclipse.aether.resolution.ArtifactDescriptorException; @@ -74,9 +70,7 @@ @Named @Singleton public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader { - private final RemoteRepositoryManager remoteRepositoryManager; private final VersionResolver versionResolver; - private final VersionRangeResolver versionRangeResolver; private final ArtifactResolver artifactResolver; private final RepositoryEventDispatcher repositoryEventDispatcher; private final ModelBuilder modelBuilder; @@ -86,17 +80,12 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader @Inject public DefaultArtifactDescriptorReader( - RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver, - VersionRangeResolver versionRangeResolver, ArtifactResolver artifactResolver, ModelBuilder modelBuilder, RepositoryEventDispatcher repositoryEventDispatcher, Map artifactRelocationSources) { - this.remoteRepositoryManager = - Objects.requireNonNull(remoteRepositoryManager, "remoteRepositoryManager cannot be null"); this.versionResolver = Objects.requireNonNull(versionResolver, "versionResolver cannot be null"); - this.versionRangeResolver = Objects.requireNonNull(versionRangeResolver, "versionRangeResolver cannot be null"); this.artifactResolver = Objects.requireNonNull(artifactResolver, "artifactResolver cannot be null"); this.modelBuilder = Objects.requireNonNull(modelBuilder, "modelBuilder cannot be null"); this.repositoryEventDispatcher = @@ -203,25 +192,19 @@ private Model loadPom( .toList(); String gav = pomArtifact.getGroupId() + ":" + pomArtifact.getArtifactId() + ":" + pomArtifact.getVersion(); - ModelResolver modelResolver = new DefaultModelResolver(); - ModelRepositoryHolder modelRepositoryHolder = new DefaultModelRepositoryHolder( - iSession, DefaultModelRepositoryHolder.RepositoryMerging.REQUEST_DOMINANT, repositories); ModelBuilderRequest modelRequest = ModelBuilderRequest.builder() .session(iSession) - .projectBuild(false) - .processPlugins(false) - .twoPhaseBuilding(false) + .requestType(ModelBuilderRequest.RequestType.DEPENDENCY) .source(ModelSource.fromPath(pomArtifact.getPath(), gav)) // This merge is on purpose because otherwise user properties would override model // properties in dependencies the user does not know. See MNG-7563 for details. .systemProperties(toProperties(session.getUserProperties(), session.getSystemProperties())) .userProperties(Map.of()) - .modelResolver(modelResolver) - .modelRepositoryHolder(modelRepositoryHolder) + .repositoryMerging(ModelBuilderRequest.RepositoryMerging.REQUEST_DOMINANT) .repositories(repositories) .build(); - ModelBuilderResult modelResult = modelBuilder.build(modelRequest); + ModelBuilderResult modelResult = modelBuilder.newSession().build(modelRequest); // ModelBuildingEx is thrown only on FATAL and ERROR severities, but we still can have WARNs // that may lead to unexpected build failure, log them if (!modelResult.getProblems().isEmpty()) { diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelRepositoryHolder.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelRepositoryHolder.java deleted file mode 100644 index 155825408bcd..000000000000 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelRepositoryHolder.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.internal.impl.resolver; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apache.maven.api.RemoteRepository; -import org.apache.maven.api.Session; -import org.apache.maven.api.model.Repository; -import org.apache.maven.api.services.ModelRepositoryHolder; -import org.apache.maven.api.services.RepositoryFactory; - -public class DefaultModelRepositoryHolder implements ModelRepositoryHolder { - - /** - * The possible merge modes for combining remote repositories. - */ - public enum RepositoryMerging { - - /** - * The repositories declared in the POM have precedence over the repositories specified in the request. - */ - POM_DOMINANT, - - /** - * The repositories specified in the request have precedence over the repositories declared in the POM. - */ - REQUEST_DOMINANT, - } - - final Session session; - final RepositoryMerging repositoryMerging; - - List pomRepositories; - List repositories; - List externalRepositories; - Set ids; - - public DefaultModelRepositoryHolder( - Session session, RepositoryMerging repositoryMerging, List externalRepositories) { - this.session = session; - this.repositoryMerging = repositoryMerging; - this.pomRepositories = List.of(); - this.externalRepositories = List.copyOf(externalRepositories); - this.repositories = List.copyOf(externalRepositories); - this.ids = new HashSet<>(); - } - - protected DefaultModelRepositoryHolder(DefaultModelRepositoryHolder holder) { - this.session = holder.session; - this.repositoryMerging = holder.repositoryMerging; - this.pomRepositories = List.copyOf(holder.pomRepositories); - this.externalRepositories = List.copyOf(holder.externalRepositories); - this.repositories = List.copyOf(holder.repositories); - } - - @Override - public void merge(List toAdd, boolean replace) { - List repos = - toAdd.stream().map(session::createRemoteRepository).toList(); - if (replace) { - Set ids = repos.stream().map(RemoteRepository::getId).collect(Collectors.toSet()); - repositories = - repositories.stream().filter(r -> !ids.contains(r.getId())).toList(); - pomRepositories = pomRepositories.stream() - .filter(r -> !ids.contains(r.getId())) - .toList(); - } else { - Set ids = - pomRepositories.stream().map(RemoteRepository::getId).collect(Collectors.toSet()); - repos = repos.stream().filter(r -> !ids.contains(r.getId())).toList(); - } - - RepositoryFactory repositoryFactory = session.getService(RepositoryFactory.class); - if (repositoryMerging == RepositoryMerging.REQUEST_DOMINANT) { - repositories = repositoryFactory.aggregate(session, repositories, repos, true); - pomRepositories = repositories; - } else { - pomRepositories = repositoryFactory.aggregate(session, pomRepositories, repos, true); - repositories = repositoryFactory.aggregate(session, pomRepositories, externalRepositories, false); - } - } - - @Override - public List getRepositories() { - return List.copyOf(repositories); - } - - @Override - public ModelRepositoryHolder copy() { - return new DefaultModelRepositoryHolder(this); - } -} diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelResolver.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelResolver.java index 4b66104f3dc4..1eaf9f2c8f18 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelResolver.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/resolver/DefaultModelResolver.java @@ -24,6 +24,7 @@ import java.nio.file.Path; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -32,14 +33,19 @@ import org.apache.maven.api.RemoteRepository; import org.apache.maven.api.Session; import org.apache.maven.api.Version; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Singleton; +import org.apache.maven.api.model.Dependency; +import org.apache.maven.api.model.InputLocation; +import org.apache.maven.api.model.Parent; import org.apache.maven.api.services.ArtifactResolverException; -import org.apache.maven.api.services.ModelResolver; -import org.apache.maven.api.services.ModelResolverException; import org.apache.maven.api.services.ModelSource; import org.apache.maven.api.services.Source; import org.apache.maven.api.services.VersionRangeResolverException; +import org.apache.maven.api.services.model.ModelResolver; +import org.apache.maven.api.services.model.ModelResolverException; /** * A model resolver to assist building of dependency POMs. @@ -50,13 +56,64 @@ @Singleton public class DefaultModelResolver implements ModelResolver { + @Nonnull @Override + public ModelSource resolveModel( + @Nonnull Session session, + @Nullable List repositories, + @Nonnull Parent parent, + @Nonnull AtomicReference modified) + throws ModelResolverException { + return resolveModel( + session, + repositories, + parent.getGroupId(), + parent.getArtifactId(), + parent.getVersion(), + "parent", + parent.getLocation("version"), + version -> modified.set(parent.withVersion(version))); + } + + @Nonnull + public ModelSource resolveModel( + @Nonnull Session session, + @Nullable List repositories, + @Nonnull Dependency dependency, + @Nonnull AtomicReference modified) + throws ModelResolverException { + return resolveModel( + session, + repositories, + dependency.getGroupId(), + dependency.getArtifactId(), + dependency.getVersion(), + "dependency", + dependency.getLocation("version"), + version -> modified.set(dependency.withVersion(version))); + } + + @Override + public ModelSource resolveModel( + @Nonnull Session session, + @Nullable List repositories, + @Nonnull String groupId, + @Nonnull String artifactId, + @Nonnull String version, + @Nonnull Consumer resolvedVersion) + throws ModelResolverException { + return resolveModel(session, repositories, groupId, artifactId, version, null, null, resolvedVersion); + } + + @SuppressWarnings("checkstyle:ParameterNumber") public ModelSource resolveModel( Session session, List repositories, String groupId, String artifactId, String version, + String type, + InputLocation location, Consumer resolvedVersion) throws ModelResolverException { try { @@ -65,7 +122,9 @@ public ModelSource resolveModel( && coords.getVersionConstraint().getVersionRange().getUpperBoundary() == null) { // Message below is checked for in the MNG-2199 core IT. throw new ModelResolverException( - String.format("The requested version range '%s' does not specify an upper bound", version), + "The requested " + (type != null ? type + " " : "") + "version range '" + version + "'" + + (location != null ? " (at " + location + ")" : "") + + " does not specify an upper bound", groupId, artifactId, version); @@ -73,7 +132,8 @@ public ModelSource resolveModel( List versions = session.resolveVersionRange(coords, repositories); if (versions.isEmpty()) { throw new ModelResolverException( - String.format("No versions matched the requested version range '%s'", version), + "No versions matched the requested " + (type != null ? type + " " : "") + "version range '" + + version + "'", groupId, artifactId, version); @@ -83,11 +143,8 @@ public ModelSource resolveModel( resolvedVersion.accept(newVersion); } - DownloadedArtifact resolved = session.resolveArtifact( - session.createArtifactCoordinates(groupId, artifactId, newVersion, "pom"), repositories); - Path path = resolved.getPath(); - String location = groupId + ":" + artifactId + ":" + newVersion; - return new ResolverModelSource(path, location); + Path path = getPath(session, repositories, groupId, artifactId, newVersion); + return new ResolverModelSource(path, groupId + ":" + artifactId + ":" + newVersion); } catch (VersionRangeResolverException | ArtifactResolverException e) { throw new ModelResolverException( e.getMessage() + " (remote repositories: " @@ -101,7 +158,18 @@ public ModelSource resolveModel( } } - private static class ResolverModelSource implements ModelSource { + protected Path getPath( + Session session, + List repositories, + String groupId, + String artifactId, + String newVersion) { + DownloadedArtifact resolved = session.resolveArtifact( + session.createArtifactCoordinates(groupId, artifactId, newVersion, "pom"), repositories); + return resolved.getPath(); + } + + protected static class ResolverModelSource implements ModelSource { private final Path path; private final String location; diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/PhasingExecutor.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/util/PhasingExecutor.java similarity index 69% rename from maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/PhasingExecutor.java rename to maven-api-impl/src/main/java/org/apache/maven/internal/impl/util/PhasingExecutor.java index c27f5ce5b3f5..85d4ed597695 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/PhasingExecutor.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/util/PhasingExecutor.java @@ -16,12 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.lifecycle.internal.concurrent; +package org.apache.maven.internal.impl.util; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Phaser; +/** + * The phasing executor is a simple executor that allows to execute tasks in parallel + * and wait for all tasks to be executed before closing the executor. The tasks that are + * currently being executed are allowed to submit new tasks while the executor is closed. + * The executor implements {@link AutoCloseable} to allow using the executor with + * a try-with-resources statement. + * + * The {@link #phase()} method can be used to submit tasks and wait for them to be executed + * without closing the executor. + */ public class PhasingExecutor implements Executor, AutoCloseable { private final ExecutorService executor; private final Phaser phaser = new Phaser(); @@ -43,12 +53,14 @@ public void execute(Runnable command) { }); } - public void await() { - phaser.arriveAndAwaitAdvance(); + public AutoCloseable phase() { + phaser.register(); + return () -> phaser.awaitAdvance(phaser.arriveAndDeregister()); } @Override public void close() { + phaser.arriveAndAwaitAdvance(); executor.shutdownNow(); } } diff --git a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/resolver/DefaultModelResolverTest.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/resolver/DefaultModelResolverTest.java index 51daf7079af0..42f3686382d9 100644 --- a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/resolver/DefaultModelResolverTest.java +++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/resolver/DefaultModelResolverTest.java @@ -28,8 +28,8 @@ import org.apache.maven.api.Session; import org.apache.maven.api.model.Dependency; import org.apache.maven.api.model.Parent; -import org.apache.maven.api.services.ModelResolver; -import org.apache.maven.api.services.ModelResolverException; +import org.apache.maven.api.services.model.ModelResolver; +import org.apache.maven.api.services.model.ModelResolverException; import org.apache.maven.internal.impl.standalone.ApiRunner; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -87,7 +87,7 @@ void testResolveParentThrowsModelResolverExceptionWhenNoMatchingVersionFound() t ModelResolverException.class, () -> newModelResolver().resolveModel(session, null, parent, new AtomicReference<>()), "Expected 'ModelResolverException' not thrown."); - assertEquals("No versions matched the requested version range '[2.0,2.1)'", e.getMessage()); + assertEquals("No versions matched the requested parent version range '[2.0,2.1)'", e.getMessage()); } @Test @@ -102,7 +102,7 @@ void testResolveParentThrowsModelResolverExceptionWhenUsingRangesWithoutUpperBou ModelResolverException.class, () -> newModelResolver().resolveModel(session, null, parent, new AtomicReference<>()), "Expected 'ModelResolverException' not thrown."); - assertEquals("The requested version range '[1,)' does not specify an upper bound", e.getMessage()); + assertEquals("The requested parent version range '[1,)' does not specify an upper bound", e.getMessage()); } @Test @@ -158,7 +158,7 @@ void testResolveDependencyThrowsModelResolverExceptionWhenNoMatchingVersionFound ModelResolverException.class, () -> newModelResolver().resolveModel(session, null, dependency, new AtomicReference<>()), "Expected 'ModelResolverException' not thrown."); - assertEquals("No versions matched the requested version range '[2.0,2.1)'", e.getMessage()); + assertEquals("No versions matched the requested dependency version range '[2.0,2.1)'", e.getMessage()); } @Test @@ -173,7 +173,7 @@ void testResolveDependencyThrowsModelResolverExceptionWhenUsingRangesWithoutUppe ModelResolverException.class, () -> newModelResolver().resolveModel(session, null, dependency, new AtomicReference<>()), "Expected 'ModelResolverException' not thrown."); - assertEquals("The requested version range '[1,)' does not specify an upper bound", e.getMessage()); + assertEquals("The requested dependency version range '[1,)' does not specify an upper bound", e.getMessage()); } @Test diff --git a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java index 15a605ea20ef..05a3cfaa187b 100644 --- a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java +++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java @@ -158,7 +158,7 @@ public Path getTopDirectory() { @Override public Path getRootDirectory() { - return null; + throw new IllegalStateException(); } @Override diff --git a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java index 558dea7f7271..09eb857e8597 100644 --- a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java +++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java @@ -32,7 +32,6 @@ import org.apache.maven.internal.impl.DefaultPluginConfigurationExpander; import org.apache.maven.internal.impl.DefaultSuperPomProvider; import org.apache.maven.internal.impl.DefaultUrlNormalizer; -import org.apache.maven.internal.impl.model.BuildModelTransformer; import org.apache.maven.internal.impl.model.DefaultDependencyManagementImporter; import org.apache.maven.internal.impl.model.DefaultDependencyManagementInjector; import org.apache.maven.internal.impl.model.DefaultInheritanceAssembler; @@ -51,6 +50,7 @@ import org.apache.maven.internal.impl.model.DefaultRootLocator; import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator; import org.apache.maven.internal.impl.resolver.DefaultArtifactDescriptorReader; +import org.apache.maven.internal.impl.resolver.DefaultModelResolver; import org.apache.maven.internal.impl.resolver.DefaultVersionRangeResolver; import org.apache.maven.internal.impl.resolver.DefaultVersionResolver; import org.apache.maven.internal.impl.resolver.MavenArtifactRelocationSource; @@ -987,9 +987,7 @@ public final ArtifactDescriptorReader getArtifactDescriptorReader() { protected ArtifactDescriptorReader createArtifactDescriptorReader() { // from maven-resolver-provider return new DefaultArtifactDescriptorReader( - getRemoteRepositoryManager(), getVersionResolver(), - getVersionRangeResolver(), getArtifactResolver(), getModelBuilder(), getRepositoryEventDispatcher(), @@ -1056,13 +1054,12 @@ protected ModelBuilder createModelBuilder() { new DefaultPluginManagementInjector(), new DefaultDependencyManagementInjector(), new DefaultDependencyManagementImporter(), - (m, r, b) -> m, new DefaultPluginConfigurationExpander(), new ProfileActivationFilePathInterpolator(new DefaultPathTranslator(), new DefaultRootLocator()), - new BuildModelTransformer(), new DefaultModelVersionParser(getVersionScheme()), List.of(), - new DefaultModelCacheFactory()); + new DefaultModelCacheFactory(), + new DefaultModelResolver()); } private RepositorySystem repositorySystem; diff --git a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/TestApiStandalone.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/TestApiStandalone.java index 5eec24147c4b..aa8a3db3e8ed 100644 --- a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/TestApiStandalone.java +++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/TestApiStandalone.java @@ -42,11 +42,13 @@ void testStandalone() { Session session = ApiRunner.createSession(); ModelBuilder builder = session.getService(ModelBuilder.class); - ModelBuilderResult result = builder.build(ModelBuilderRequest.builder() - .session(session) - .source(ModelSource.fromPath(Paths.get("pom.xml").toAbsolutePath())) - .projectBuild(true) - .build()); + ModelBuilderResult result = builder.newSession() + .build(ModelBuilderRequest.builder() + .session(session) + .source(ModelSource.fromPath(Paths.get("pom.xml").toAbsolutePath())) + .requestType(ModelBuilderRequest.RequestType.BUILD_POM) + .recursive(true) + .build()); assertNotNull(result.getEffectiveModel()); ArtifactCoordinates coords = diff --git a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/PhasingExecutorTest.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/util/PhasingExecutorTest.java similarity index 84% rename from maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/PhasingExecutorTest.java rename to maven-api-impl/src/test/java/org/apache/maven/internal/impl/util/PhasingExecutorTest.java index 5c038cd072e2..623c24a3f45e 100644 --- a/maven-core/src/test/java/org/apache/maven/lifecycle/internal/concurrent/PhasingExecutorTest.java +++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/util/PhasingExecutorTest.java @@ -16,20 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.lifecycle.internal.concurrent; +package org.apache.maven.internal.impl.util; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.junit.jupiter.api.Test; -public class PhasingExecutorTest { +class PhasingExecutorTest { @Test void testPhaser() { - PhasingExecutor p = new PhasingExecutor(Executors.newFixedThreadPool(4)); - p.execute(() -> waitSomeTime(p, 2)); - p.await(); + try (PhasingExecutor p = new PhasingExecutor(Executors.newFixedThreadPool(4))) { + p.execute(() -> waitSomeTime(p, 2)); + } } private void waitSomeTime(Executor executor, int nb) { diff --git a/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java b/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java index 36c9110f9ec7..0201b2fd0a62 100644 --- a/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java +++ b/maven-compat/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Properties; +import org.apache.maven.api.Session; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.InvalidRepositoryException; import org.apache.maven.artifact.repository.ArtifactRepository; @@ -34,8 +35,9 @@ import org.apache.maven.execution.DefaultMavenExecutionResult; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; -import org.apache.maven.internal.impl.DefaultRepositoryFactory; -import org.apache.maven.internal.impl.DefaultSession; +import org.apache.maven.internal.impl.DefaultLookup; +import org.apache.maven.internal.impl.DefaultSessionFactory; +import org.apache.maven.internal.impl.InternalMavenSession; import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.model.Build; import org.apache.maven.model.Dependency; @@ -48,19 +50,15 @@ import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.repository.RepositorySystem; -import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.apache.maven.repository.internal.MavenSessionBuilderSupplier; +import org.apache.maven.session.scope.internal.SessionScope; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.testing.PlexusTest; import org.codehaus.plexus.util.FileUtils; -import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider; -import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager; -import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer; -import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; +import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.LocalRepository; import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; -import static org.mockito.Mockito.mock; @PlexusTest @Deprecated @@ -70,7 +68,10 @@ public abstract class AbstractCoreMavenComponentTestCase { protected PlexusContainer container; @Inject - protected RepositorySystem repositorySystem; + protected org.eclipse.aether.RepositorySystem repositorySystem; + + @Inject + protected RepositorySystem mavenRepositorySystem; @Inject protected org.apache.maven.project.ProjectBuilder projectBuilder; @@ -128,20 +129,7 @@ protected MavenSession createMavenSession(File pom, Properties executionProperti .setSystemProperties(executionProperties) .setUserProperties(new Properties()); - initRepoSession(configuration); - - MavenSession session = new MavenSession( - getContainer(), configuration.getRepositorySession(), request, new DefaultMavenExecutionResult()); - DefaultSession iSession = new DefaultSession( - session, - mock(org.eclipse.aether.RepositorySystem.class), - null, - null, - new SimpleLookup(List.of(new DefaultRepositoryFactory(new DefaultRemoteRepositoryManager( - new DefaultUpdatePolicyAnalyzer(), new DefaultChecksumPolicyProvider())))), - null); - InternalSession.associate(session.getRepositorySession(), iSession); - session.setSession(iSession); + initRepoSession(request, configuration); List projects = new ArrayList<>(); @@ -165,18 +153,45 @@ protected MavenSession createMavenSession(File pom, Properties executionProperti projects.add(project); } + InternalSession iSession = InternalSession.from(configuration.getRepositorySession()); + InternalMavenSession mSession = InternalMavenSession.from(iSession); + MavenSession session = mSession.getMavenSession(); + session.setProjects(projects); session.setAllProjects(session.getProjects()); return session; } - protected void initRepoSession(ProjectBuildingRequest request) throws Exception { - File localRepoDir = new File(request.getLocalRepository().getBasedir()); - LocalRepository localRepo = new LocalRepository(localRepoDir); - DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); - session.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory().newInstance(session, localRepo)); - request.setRepositorySession(session); + protected void initRepoSession( + MavenExecutionRequest mavenExecutionRequest, ProjectBuildingRequest projectBuildingRequest) + throws Exception { + File localRepoDir = new File(projectBuildingRequest.getLocalRepository().getBasedir()); + LocalRepository localRepo = new LocalRepository(localRepoDir, "simple"); + + RepositorySystemSession session = new MavenSessionBuilderSupplier(repositorySystem) + .get() + .withLocalRepositories(localRepo) + .build(); + projectBuildingRequest.setRepositorySession(session); + + DefaultSessionFactory defaultSessionFactory = + new DefaultSessionFactory(repositorySystem, null, new DefaultLookup(container), null); + + MavenSession mSession = new MavenSession( + container, + projectBuildingRequest.getRepositorySession(), + mavenExecutionRequest, + new DefaultMavenExecutionResult()); + + InternalSession iSession = defaultSessionFactory.newSession(mSession); + mSession.setSession(iSession); + + SessionScope sessionScope = getContainer().lookup(SessionScope.class); + sessionScope.enter(); + sessionScope.seed(MavenSession.class, mSession); + sessionScope.seed(Session.class, iSession); + sessionScope.seed(InternalMavenSession.class, InternalMavenSession.from(iSession)); } protected MavenProject createStubMavenProject() { @@ -201,7 +216,7 @@ protected List getRemoteRepositories() throws InvalidReposit repository.setReleases(policy); repository.setSnapshots(policy); - return Arrays.asList(repositorySystem.buildArtifactRepository(repository)); + return Arrays.asList(mavenRepositorySystem.buildArtifactRepository(repository)); } protected List getPluginArtifactRepositories() throws InvalidRepositoryException { @@ -211,7 +226,7 @@ protected List getPluginArtifactRepositories() throws Invali protected ArtifactRepository getLocalRepository() throws InvalidRepositoryException { File repoDir = new File(getBasedir(), "target/local-repo").getAbsoluteFile(); - return repositorySystem.createLocalRepository(repoDir); + return mavenRepositorySystem.createLocalRepository(repoDir); } protected class ProjectBuilder { diff --git a/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java b/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java index 68d6827b7a7f..fffdbb85c655 100644 --- a/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java +++ b/maven-compat/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java @@ -26,25 +26,28 @@ import java.net.URL; import java.util.Arrays; +import org.apache.maven.api.Session; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; +import org.apache.maven.bridge.MavenRepositorySystem; import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.DefaultMavenExecutionResult; import org.apache.maven.execution.MavenSession; import org.apache.maven.internal.impl.DefaultLookup; import org.apache.maven.internal.impl.DefaultSession; +import org.apache.maven.internal.impl.InternalMavenSession; import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.model.building.ModelBuildingException; import org.apache.maven.model.building.ModelProblem; import org.apache.maven.repository.RepositorySystem; import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.apache.maven.session.scope.internal.SessionScope; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.testing.PlexusTest; import org.eclipse.aether.DefaultRepositorySystemSession; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; /** */ @@ -56,6 +59,12 @@ public abstract class AbstractMavenProjectTestCase { @Inject protected RepositorySystem repositorySystem; + @Inject + protected org.eclipse.aether.RepositorySystem resolverRepositorySystem; + + @Inject + protected MavenRepositorySystem mavenRepositorySystem; + @Inject protected PlexusContainer container; @@ -117,7 +126,7 @@ protected MavenProject getProjectWithDependencies(File pom) throws Exception { ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); configuration.setLocalRepository(getLocalRepository()); configuration.setRemoteRepositories(Arrays.asList(new ArtifactRepository[] {})); - configuration.setProcessPlugins(false); + configuration.setProcessPlugins(true); configuration.setResolveDependencies(true); initRepoSession(configuration); @@ -146,7 +155,7 @@ protected MavenProject getProject(File pom) throws Exception { return projectBuilder.build(pom, configuration).getProject(); } - protected void initRepoSession(ProjectBuildingRequest request) { + protected void initRepoSession(ProjectBuildingRequest request) throws Exception { File localRepo = new File(request.getLocalRepository().getBasedir()); DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); session.setLocalRepositoryManager(new LegacyLocalRepositoryManager(localRepo)); @@ -156,12 +165,13 @@ protected void initRepoSession(ProjectBuildingRequest request) { MavenSession msession = new MavenSession(getContainer(), session, mavenExecutionRequest, new DefaultMavenExecutionResult()); DefaultSession iSession = new DefaultSession( - msession, - mock(org.eclipse.aether.RepositorySystem.class), - null, - null, - new DefaultLookup(container), - null); + msession, resolverRepositorySystem, null, mavenRepositorySystem, new DefaultLookup(container), null); InternalSession.associate(session, iSession); + + SessionScope sessionScope = container.lookup(SessionScope.class); + sessionScope.enter(); + sessionScope.seed(MavenSession.class, msession); + sessionScope.seed(InternalMavenSession.class, iSession); + sessionScope.seed(Session.class, iSession); } } diff --git a/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java b/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java index cfda6e1caccf..0fc0ba33cc09 100644 --- a/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java +++ b/maven-compat/src/test/java/org/apache/maven/project/TestProjectBuilder.java @@ -26,12 +26,10 @@ import java.util.Collections; import org.apache.maven.api.services.ModelBuilder; -import org.apache.maven.api.services.model.ModelProcessor; -import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.api.services.model.LifecycleBindingsInjector; import org.apache.maven.bridge.MavenRepositorySystem; import org.apache.maven.model.root.RootLocator; import org.eclipse.aether.RepositorySystem; -import org.eclipse.aether.impl.RemoteRepositoryManager; @Named("classpath") @Singleton @@ -40,22 +38,20 @@ public class TestProjectBuilder extends DefaultProjectBuilder { @Inject public TestProjectBuilder( ModelBuilder modelBuilder, - ModelProcessor modelProcessor, ProjectBuildingHelper projectBuildingHelper, MavenRepositorySystem repositorySystem, RepositorySystem repoSystem, - RemoteRepositoryManager repositoryManager, ProjectDependenciesResolver dependencyResolver, - RootLocator rootLocator) { + RootLocator rootLocator, + LifecycleBindingsInjector lifecycleBindingsInjector) { super( modelBuilder, - modelProcessor, projectBuildingHelper, repositorySystem, repoSystem, - repositoryManager, dependencyResolver, - rootLocator); + rootLocator, + lifecycleBindingsInjector); } @Override @@ -63,7 +59,7 @@ public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest configur throws ProjectBuildingException { ProjectBuildingResult result = super.build(pomFile, configuration); - result.getProject().setRemoteArtifactRepositories(Collections.emptyList()); + result.getProject().setRemoteArtifactRepositories(Collections.emptyList()); return result; } diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultEvent.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultEvent.java index 2f4cf648c832..2f03e6654333 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultEvent.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultEvent.java @@ -48,7 +48,7 @@ public Session getSession() { @Override public Optional getProject() { - return Optional.ofNullable(delegate.getProject()).map(session::getProject); + return Optional.ofNullable(session.getProject(delegate.getProject())); } @Override diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java index ba1c6d708fc8..359f84094a12 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java @@ -159,7 +159,7 @@ public Path getRootDirectory() { @Override public Optional getParent() { MavenProject parent = project.getParent(); - return parent != null ? Optional.of(session.getProject(parent)) : Optional.empty(); + return Optional.ofNullable(session.getProject(parent)); } @Nonnull diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java index a446f63b26ee..efc6d3e7855a 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java @@ -100,7 +100,7 @@ public Optional getPomFile() { @Nonnull @Override public Optional getProject() { - return Optional.ofNullable(res.getProject()).map(session::getProject); + return Optional.ofNullable(session.getProject(res.getProject())); } @Nonnull diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java index cd83f7c44921..a931b9874ba7 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java @@ -93,7 +93,9 @@ public List getProjects(List projects) { @Override public Project getProject(MavenProject project) { - return allProjects.computeIfAbsent(project.getId(), id -> new DefaultProject(this, project)); + return project != null && project.getBasedir() != null + ? allProjects.computeIfAbsent(project.getId(), id -> new DefaultProject(this, project)) + : null; } @Override diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/InternalMavenSession.java b/maven-core/src/main/java/org/apache/maven/internal/impl/InternalMavenSession.java index 1ee399c6c040..e165dfd8e6c5 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/impl/InternalMavenSession.java +++ b/maven-core/src/main/java/org/apache/maven/internal/impl/InternalMavenSession.java @@ -23,6 +23,7 @@ import org.apache.maven.api.Project; import org.apache.maven.api.RemoteRepository; import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Nullable; import org.apache.maven.execution.MavenSession; import static org.apache.maven.internal.impl.Utils.cast; @@ -33,8 +34,16 @@ static InternalMavenSession from(Session session) { return cast(InternalMavenSession.class, session, "session should be an " + InternalMavenSession.class); } + static InternalMavenSession from(org.eclipse.aether.RepositorySystemSession session) { + return cast(InternalMavenSession.class, session.getData().get(InternalSession.class), "session"); + } + List getProjects(List projects); + /** + * May return null if the input project is null or is not part of the reactor. + */ + @Nullable Project getProject(org.apache.maven.project.MavenProject project); List toArtifactRepositories( diff --git a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java index ab2463adc6d5..2ad05360cf3f 100644 --- a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java @@ -20,7 +20,6 @@ import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Provider; import java.nio.file.Path; import java.util.ArrayList; @@ -36,13 +35,12 @@ import org.apache.maven.api.model.ModelBase; import org.apache.maven.api.model.Profile; import org.apache.maven.api.model.Repository; +import org.apache.maven.api.services.ModelBuilder; import org.apache.maven.api.services.ModelBuilderException; import org.apache.maven.api.services.ModelBuilderRequest; import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelProblemCollector; -import org.apache.maven.api.services.ModelResolver; import org.apache.maven.api.services.ModelSource; -import org.apache.maven.api.services.ModelTransformer; import org.apache.maven.api.services.SuperPomProvider; import org.apache.maven.api.services.model.DependencyManagementImporter; import org.apache.maven.api.services.model.DependencyManagementInjector; @@ -53,6 +51,7 @@ import org.apache.maven.api.services.model.ModelNormalizer; import org.apache.maven.api.services.model.ModelPathTranslator; import org.apache.maven.api.services.model.ModelProcessor; +import org.apache.maven.api.services.model.ModelResolver; import org.apache.maven.api.services.model.ModelUrlNormalizer; import org.apache.maven.api.services.model.ModelValidator; import org.apache.maven.api.services.model.ModelVersionParser; @@ -61,15 +60,14 @@ import org.apache.maven.api.services.model.ProfileActivationContext; import org.apache.maven.api.services.model.ProfileInjector; import org.apache.maven.api.services.model.ProfileSelector; +import org.apache.maven.api.spi.ModelTransformer; import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.impl.model.DefaultModelBuilder; import org.apache.maven.internal.impl.model.DefaultProfileSelector; import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator; import org.apache.maven.model.v4.MavenModelVersion; import org.apache.maven.project.MavenProject; -import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.RemoteRepositoryManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,71 +77,70 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder { public static final String POM_PACKAGING = "pom"; - @Inject - private ProfileInjector profileInjector; - - @Inject - private InheritanceAssembler inheritanceAssembler; - - @Inject - private DependencyManagementImporter dependencyManagementImporter; - - @Inject - private DependencyManagementInjector dependencyManagementInjector; - - @Inject - private LifecycleBindingsInjector lifecycleBindingsInjector; - - @Inject - private ModelInterpolator modelInterpolator; - - @Inject - private ModelNormalizer modelNormalizer; - - @Inject - private ModelPathTranslator modelPathTranslator; - - @Inject - private ModelProcessor modelProcessor; - - @Inject - private ModelUrlNormalizer modelUrlNormalizer; - - @Inject - private ModelValidator modelValidator; + private final ProfileInjector profileInjector; + private final InheritanceAssembler inheritanceAssembler; + private final DependencyManagementImporter dependencyManagementImporter; + private final DependencyManagementInjector dependencyManagementInjector; + private final LifecycleBindingsInjector lifecycleBindingsInjector; + private final ModelInterpolator modelInterpolator; + private final ModelNormalizer modelNormalizer; + private final ModelPathTranslator modelPathTranslator; + private final ModelProcessor modelProcessor; + private final ModelUrlNormalizer modelUrlNormalizer; + private final ModelValidator modelValidator; + private final PluginConfigurationExpander pluginConfigurationExpander; + private final PluginManagementInjector pluginManagementInjector; + private final SuperPomProvider superPomProvider; + private final ModelVersionParser versionParser; + private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; + private final List transformers; + private final ModelCacheFactory modelCacheFactory; + private final ModelResolver modelResolver; @Inject - private PluginConfigurationExpander pluginConfigurationExpander; - - @Inject - private PluginManagementInjector pluginManagementInjector; - - @Inject - private SuperPomProvider superPomProvider; - - @Inject - private ModelVersionParser versionParser; - - @Inject - private ModelTransformer modelTransformer; - - // To break circular dependency - @Inject - private Provider repositorySystem; - - @Inject - private RemoteRepositoryManager remoteRepositoryManager; - - @Inject - private ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; - - @Inject - private List transformers; - - @Inject - private ModelCacheFactory modelCacheFactory; + @SuppressWarnings("checkstyle:ParameterNumber") + DefaultConsumerPomBuilder( + ProfileInjector profileInjector, + InheritanceAssembler inheritanceAssembler, + DependencyManagementImporter dependencyManagementImporter, + DependencyManagementInjector dependencyManagementInjector, + LifecycleBindingsInjector lifecycleBindingsInjector, + ModelInterpolator modelInterpolator, + ModelNormalizer modelNormalizer, + ModelPathTranslator modelPathTranslator, + ModelProcessor modelProcessor, + ModelUrlNormalizer modelUrlNormalizer, + ModelValidator modelValidator, + PluginConfigurationExpander pluginConfigurationExpander, + PluginManagementInjector pluginManagementInjector, + SuperPomProvider superPomProvider, + ModelVersionParser versionParser, + ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator, + List transformers, + ModelCacheFactory modelCacheFactory, + ModelResolver modelResolver) { + this.profileInjector = profileInjector; + this.inheritanceAssembler = inheritanceAssembler; + this.dependencyManagementImporter = dependencyManagementImporter; + this.dependencyManagementInjector = dependencyManagementInjector; + this.lifecycleBindingsInjector = lifecycleBindingsInjector; + this.modelInterpolator = modelInterpolator; + this.modelNormalizer = modelNormalizer; + this.modelPathTranslator = modelPathTranslator; + this.modelProcessor = modelProcessor; + this.modelUrlNormalizer = modelUrlNormalizer; + this.modelValidator = modelValidator; + this.pluginConfigurationExpander = pluginConfigurationExpander; + this.pluginManagementInjector = pluginManagementInjector; + this.superPomProvider = superPomProvider; + this.versionParser = versionParser; + this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator; + this.transformers = transformers; + this.modelCacheFactory = modelCacheFactory; + this.modelResolver = modelResolver; + } - Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public Model build(RepositorySystemSession session, MavenProject project, Path src) throws ModelBuilderException { @@ -180,6 +177,7 @@ public List getActiveProfiles( return new ArrayList<>(); } }; + // TODO: the custom selector should be used as a flag on the request DefaultModelBuilder modelBuilder = new DefaultModelBuilder( modelProcessor, modelValidator, @@ -194,25 +192,27 @@ public List getActiveProfiles( pluginManagementInjector, dependencyManagementInjector, dependencyManagementImporter, - lifecycleBindingsInjector, pluginConfigurationExpander, profileActivationFilePathInterpolator, - modelTransformer, versionParser, transformers, - modelCacheFactory); + modelCacheFactory, + modelResolver); InternalSession iSession = InternalSession.from(session); ModelBuilderRequest.ModelBuilderRequestBuilder request = ModelBuilderRequest.builder(); - request.projectBuild(true); + request.requestType(ModelBuilderRequest.RequestType.BUILD_POM); request.session(iSession); request.source(ModelSource.fromPath(src)); - request.validationLevel(ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL); request.locationTracking(false); - request.modelResolver(iSession.getData().get(SessionData.key(ModelResolver.class))); - request.transformerContextBuilder(modelBuilder.newTransformerContextBuilder()); request.systemProperties(session.getSystemProperties()); request.userProperties(session.getUserProperties()); - return modelBuilder.build(request.build()); + request.lifecycleBindingsInjector(lifecycleBindingsInjector::injectLifecycleBindings); + ModelBuilder.ModelBuilderSession mbSession = + iSession.getData().get(SessionData.key(ModelBuilder.ModelBuilderSession.class)); + if (mbSession == null) { + mbSession = modelBuilder.newSession(); + } + return mbSession.build(request.build()); } static Model transform(Model model, MavenProject project) { diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java index 6f77b927815b..d40e2e5ac47e 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java @@ -53,6 +53,7 @@ import org.apache.maven.execution.ProjectExecutionEvent; import org.apache.maven.execution.ProjectExecutionListener; import org.apache.maven.internal.MultilineMessageHelper; +import org.apache.maven.internal.impl.util.PhasingExecutor; import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer; import org.apache.maven.internal.xml.XmlNodeImpl; import org.apache.maven.lifecycle.LifecycleExecutionException; @@ -301,10 +302,9 @@ following plugin(s) that have goals not built with Maven 4 to support concurrent } void execute() { - try { + try (var phase = executor.phase()) { plan(); executePlan(); - executor.await(); } catch (Exception e) { session.getResult().addException(e); } diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java b/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java deleted file mode 100644 index eef005b51749..000000000000 --- a/maven-core/src/main/java/org/apache/maven/project/DefaultModelBuildingListener.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.maven.project; - -import java.util.List; -import java.util.Objects; - -import org.apache.maven.api.services.BuilderProblem; -import org.apache.maven.api.services.ModelProblem; -import org.apache.maven.api.services.model.ModelBuildingEvent; -import org.apache.maven.api.services.model.ModelBuildingListener; -import org.apache.maven.artifact.repository.ArtifactRepository; -import org.apache.maven.model.Model; -import org.apache.maven.plugin.PluginManagerException; -import org.apache.maven.plugin.PluginResolutionException; -import org.apache.maven.plugin.version.PluginVersionResolutionException; - -/** - * Processes events from the model builder while building the effective model for a {@link MavenProject} instance. - * - */ -public class DefaultModelBuildingListener implements ModelBuildingListener { - - private final MavenProject project; - - private final ProjectBuildingHelper projectBuildingHelper; - - private final ProjectBuildingRequest projectBuildingRequest; - - private List remoteRepositories; - - private List pluginRepositories; - - public DefaultModelBuildingListener( - MavenProject project, - ProjectBuildingHelper projectBuildingHelper, - ProjectBuildingRequest projectBuildingRequest) { - this.project = Objects.requireNonNull(project, "project cannot be null"); - this.projectBuildingHelper = - Objects.requireNonNull(projectBuildingHelper, "projectBuildingHelper cannot be null"); - this.projectBuildingRequest = - Objects.requireNonNull(projectBuildingRequest, "projectBuildingRequest cannot be null"); - this.remoteRepositories = projectBuildingRequest.getRemoteRepositories(); - this.pluginRepositories = projectBuildingRequest.getPluginArtifactRepositories(); - } - - /** - * Gets the project whose model is being built. - * - * @return The project, never {@code null}. - */ - public MavenProject getProject() { - return project; - } - - @Override - public void buildExtensionsAssembled(ModelBuildingEvent event) { - Model model = new Model(event.model()); - - try { - pluginRepositories = projectBuildingHelper.createArtifactRepositories( - model.getPluginRepositories(), pluginRepositories, projectBuildingRequest); - } catch (Exception e) { - event.problems() - .add( - BuilderProblem.Severity.ERROR, - ModelProblem.Version.BASE, - "Invalid plugin repository: " + e.getMessage(), - e); - } - project.setPluginArtifactRepositories(pluginRepositories); - - if (event.request().isProcessPlugins()) { - try { - ProjectRealmCache.CacheRecord record = - projectBuildingHelper.createProjectRealm(project, model, projectBuildingRequest); - - project.setClassRealm(record.getRealm()); - project.setExtensionDependencyFilter(record.getExtensionArtifactFilter()); - } catch (PluginResolutionException | PluginManagerException | PluginVersionResolutionException e) { - event.problems() - .add( - BuilderProblem.Severity.ERROR, - ModelProblem.Version.BASE, - "Unresolvable build extension: " + e.getMessage(), - e); - } - - projectBuildingHelper.selectProjectRealm(project); - } - - // build the regular repos after extensions are loaded to allow for custom layouts - try { - remoteRepositories = projectBuildingHelper.createArtifactRepositories( - model.getRepositories(), remoteRepositories, projectBuildingRequest); - } catch (Exception e) { - event.problems() - .add( - BuilderProblem.Severity.ERROR, - ModelProblem.Version.BASE, - "Invalid artifact repository: " + e.getMessage(), - e); - } - project.setRemoteArtifactRepositories(remoteRepositories); - - if (model.getDelegate() != event.model()) { - event.update().accept(model.getDelegate()); - } - } -} diff --git a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java index d90f09a5a126..09ab3fe9eb54 100644 --- a/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java +++ b/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java @@ -30,7 +30,6 @@ import java.nio.file.Path; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -40,26 +39,13 @@ import java.util.Objects; import java.util.Properties; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.maven.ProjectCycleException; import org.apache.maven.RepositoryUtils; -import org.apache.maven.api.Constants; -import org.apache.maven.api.Session; import org.apache.maven.api.SessionData; import org.apache.maven.api.model.Build; import org.apache.maven.api.model.Dependency; @@ -67,48 +53,38 @@ import org.apache.maven.api.model.DeploymentRepository; import org.apache.maven.api.model.Extension; import org.apache.maven.api.model.Model; -import org.apache.maven.api.model.Parent; import org.apache.maven.api.model.Plugin; import org.apache.maven.api.model.Profile; import org.apache.maven.api.model.ReportPlugin; -import org.apache.maven.api.services.MavenException; +import org.apache.maven.api.services.BuilderProblem.Severity; import org.apache.maven.api.services.ModelBuilder; import org.apache.maven.api.services.ModelBuilderException; import org.apache.maven.api.services.ModelBuilderRequest; import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelProblem; -import org.apache.maven.api.services.ModelResolver; -import org.apache.maven.api.services.ModelResolverException; +import org.apache.maven.api.services.ModelProblem.Version; +import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.ModelSource; -import org.apache.maven.api.services.ModelTransformerContext; -import org.apache.maven.api.services.ModelTransformerContextBuilder; +import org.apache.maven.api.services.ModelTransformer; import org.apache.maven.api.services.Source; -import org.apache.maven.api.services.model.ModelBuildingListener; -import org.apache.maven.api.services.model.ModelProcessor; +import org.apache.maven.api.services.model.LifecycleBindingsInjector; import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.InvalidArtifactRTException; import org.apache.maven.artifact.InvalidRepositoryException; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.bridge.MavenRepositorySystem; import org.apache.maven.internal.impl.InternalSession; -import org.apache.maven.internal.impl.resolver.DefaultModelRepositoryHolder; -import org.apache.maven.model.building.DefaultModelProblem; import org.apache.maven.model.building.FileModelSource; +import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.model.building.ModelSource2; import org.apache.maven.model.building.ModelSource3; -import org.apache.maven.model.resolution.UnresolvableModelException; import org.apache.maven.model.root.RootLocator; +import org.apache.maven.plugin.PluginManagerException; +import org.apache.maven.plugin.PluginResolutionException; +import org.apache.maven.plugin.version.PluginVersionResolutionException; import org.apache.maven.repository.internal.ArtifactDescriptorUtils; -import org.apache.maven.utils.Os; -import org.codehaus.plexus.interpolation.AbstractValueSource; -import org.codehaus.plexus.interpolation.InterpolationException; -import org.codehaus.plexus.interpolation.StringSearchInterpolator; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.RequestTrace; -import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.repository.LocalRepositoryManager; -import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.WorkspaceRepository; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResult; @@ -122,38 +98,32 @@ @Singleton public class DefaultProjectBuilder implements ProjectBuilder { - public static final int DEFAULT_BUILDER_PARALLELISM = Runtime.getRuntime().availableProcessors() / 2 + 1; - private final Logger logger = LoggerFactory.getLogger(getClass()); private final ModelBuilder modelBuilder; - private final ModelProcessor modelProcessor; private final ProjectBuildingHelper projectBuildingHelper; private final MavenRepositorySystem repositorySystem; private final org.eclipse.aether.RepositorySystem repoSystem; - private final RemoteRepositoryManager repositoryManager; private final ProjectDependenciesResolver dependencyResolver; - private final RootLocator rootLocator; + private final LifecycleBindingsInjector lifecycleBindingsInjector; @SuppressWarnings("checkstyle:ParameterNumber") @Inject public DefaultProjectBuilder( ModelBuilder modelBuilder, - ModelProcessor modelProcessor, ProjectBuildingHelper projectBuildingHelper, MavenRepositorySystem repositorySystem, RepositorySystem repoSystem, - RemoteRepositoryManager repositoryManager, ProjectDependenciesResolver dependencyResolver, - RootLocator rootLocator) { + RootLocator rootLocator, + LifecycleBindingsInjector lifecycleBindingsInjector) { this.modelBuilder = modelBuilder; - this.modelProcessor = modelProcessor; this.projectBuildingHelper = projectBuildingHelper; this.repositorySystem = repositorySystem; this.repoSystem = repoSystem; - this.repositoryManager = repositoryManager; this.dependencyResolver = dependencyResolver; this.rootLocator = rootLocator; + this.lifecycleBindingsInjector = lifecycleBindingsInjector; } // ---------------------------------------------------------------------- // MavenProjectBuilder Implementation @@ -161,7 +131,7 @@ public DefaultProjectBuilder( @Override public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException { - try (BuildSession bs = new BuildSession(request, false)) { + try (BuildSession bs = new BuildSession(request)) { Path path = pomFile.toPath(); return bs.build(path, ModelSource.fromPath(path)); } @@ -187,7 +157,7 @@ static ModelSource toSource(org.apache.maven.model.building.ModelSource modelSou @Override public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request) throws ProjectBuildingException { - try (BuildSession bs = new BuildSession(request, false)) { + try (BuildSession bs = new BuildSession(request)) { return bs.build(null, modelSource); } } @@ -201,7 +171,7 @@ public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest req @Override public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request) throws ProjectBuildingException { - try (BuildSession bs = new BuildSession(request, false)) { + try (BuildSession bs = new BuildSession(request)) { return bs.build(artifact, allowStubModel); } } @@ -209,48 +179,11 @@ public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, Pr @Override public List build(List pomFiles, boolean recursive, ProjectBuildingRequest request) throws ProjectBuildingException { - try (BuildSession bs = new BuildSession(request, true)) { + try (BuildSession bs = new BuildSession(request)) { return bs.build(pomFiles, recursive); } } - static class InterimResult { - - File pomFile; - - ModelBuilderRequest request; - - ModelBuilderResult result; - - MavenProject project; - - boolean root; - - List subprojects = Collections.emptyList(); - - ProjectBuildingResult projectBuildingResult; - - InterimResult( - File pomFile, - ModelBuilderRequest request, - ModelBuilderResult result, - MavenProject project, - boolean root) { - this.pomFile = pomFile; - this.request = request; - this.result = result; - this.project = project; - this.root = root; - } - - InterimResult(ModelBuilderRequest request, ProjectBuildingResult projectBuildingResult) { - this.request = request; - this.projectBuildingResult = projectBuildingResult; - this.pomFile = projectBuildingResult.getPomFile(); - this.project = projectBuildingResult.getProject(); - } - } - private static class StubModelSource implements ModelSource { private final String xml; private final Artifact artifact; @@ -380,96 +313,20 @@ public int hashCode() { class BuildSession implements AutoCloseable { private final ProjectBuildingRequest request; private final RepositorySystemSession session; - private final List repositories; - private final ReactorModelPool modelPool; - private final ConcurrentMap parentCache; - private final ModelTransformerContextBuilder transformerContextBuilder; - private final ExecutorService executor; - private final ModelResolver modelResolver; - private final Map ciFriendlyVersions = new ConcurrentHashMap<>(); - - BuildSession(ProjectBuildingRequest request, boolean localProjects) { + private final ModelBuilder.ModelBuilderSession modelBuilderSession; + private final Map projectIndex = new ConcurrentHashMap<>(256); + + BuildSession(ProjectBuildingRequest request) { this.request = request; this.session = RepositoryUtils.overlay(request.getLocalRepository(), request.getRepositorySession(), repoSystem); - InternalSession.from(session); - this.repositories = RepositoryUtils.toRepos(request.getRemoteRepositories()); - this.executor = createExecutor(getParallelism(request)); - if (localProjects) { - this.modelPool = new ReactorModelPool(); - this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder(); - } else { - this.modelPool = null; - this.transformerContextBuilder = null; - } - this.parentCache = new ConcurrentHashMap<>(); - this.modelResolver = new ModelResolverWrapper() { - @Override - protected org.apache.maven.model.resolution.ModelResolver getResolver( - List repositories) { - return new ProjectModelResolver( - session, - RequestTrace.newChild(null, request), - repoSystem, - repositoryManager, - repositories, - request.getRepositoryMerging(), - modelPool, - parentCache); - } - }; - } - - ExecutorService createExecutor(int parallelism) { - // - // We need an executor that will not block. - // We can't use work stealing, as we are building a graph - // and this could lead to cycles where a thread waits for - // a task to finish, then execute another one which waits - // for the initial task... - // In order to work around that problem, we override the - // invokeAll method, so that whenever the method is called, - // the pool core size will be incremented before submitting - // all the tasks, then the thread will block waiting for all - // those subtasks to finish. - // This ensures the number of running workers is no more than - // the defined parallism, while making sure the pool will not - // be exhausted - // - return new ThreadPoolExecutor( - parallelism, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()) { - final AtomicInteger parked = new AtomicInteger(); - - @Override - public List> invokeAll(Collection> tasks) - throws InterruptedException { - setCorePoolSize(parallelism + parked.incrementAndGet()); - try { - return super.invokeAll(tasks); - } finally { - setCorePoolSize(parallelism + parked.decrementAndGet()); - } - } - }; + InternalSession iSession = InternalSession.from(session); + this.modelBuilderSession = modelBuilder.newSession(); + iSession.getData().set(SessionData.key(ModelBuilder.ModelBuilderSession.class), modelBuilderSession); } @Override - public void close() { - this.executor.shutdownNow(); - } - - private int getParallelism(ProjectBuildingRequest request) { - int parallelism = DEFAULT_BUILDER_PARALLELISM; - try { - String str = request.getUserProperties().getProperty(Constants.MAVEN_PROJECT_BUILDER_PARALLELISM); - if (str != null) { - parallelism = Integer.parseInt(str); - } - } catch (Exception e) { - // ignore - } - return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors())); - } + public void close() {} ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws ProjectBuildingException { ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); @@ -484,15 +341,18 @@ ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws Projec project = new MavenProject(); project.setFile(pomFile != null ? pomFile.toFile() : null); - ModelBuildingListener listener = - new DefaultModelBuildingListener(project, projectBuildingHelper, this.request); - ModelBuilderRequest.ModelBuilderRequestBuilder builder = getModelBuildingRequest(); - ModelBuilderRequest request = builder.projectBuild(modelPool != null) - .source(modelSource) - .projectBuild(true) + ModelBuilderRequest.RequestType type = pomFile != null + && this.request.isProcessPlugins() + && this.request.getValidationLevel() == ModelBuildingRequest.VALIDATION_LEVEL_STRICT + ? ModelBuilderRequest.RequestType.BUILD_POM + : ModelBuilderRequest.RequestType.PARENT_POM; + MavenProject theProject = project; + ModelBuilderRequest request = builder.source(modelSource) + .requestType(type) .locationTracking(true) - .listener(listener) + .lifecycleBindingsInjector( + (m, r, p) -> injectLifecycleBindings(m, r, p, theProject, this.request)) .build(); if (pomFile != null) { @@ -501,7 +361,7 @@ ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws Projec ModelBuilderResult result; try { - result = modelBuilder.build(request); + result = modelBuilderSession.build(request); } catch (ModelBuilderException e) { result = e.getResult(); if (result == null || result.getEffectiveModel() == null) { @@ -514,14 +374,13 @@ ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws Projec modelProblems = result.getProblems(); - initProject(project, Collections.emptyMap(), result); - } else if (request.isResolveDependencies()) { - projectBuildingHelper.selectProjectRealm(project); + initProject(project, result); } DependencyResolutionResult resolutionResult = null; if (request.isResolveDependencies()) { + projectBuildingHelper.selectProjectRealm(project); resolutionResult = resolveDependencies(project); } @@ -603,339 +462,66 @@ List build(List pomFiles, boolean recursive) throws } List doBuild(List pomFiles, boolean recursive) { - Map projectIndex = new ConcurrentHashMap<>(256); - - // phase 1: get file Models from the reactor. - List interimResults = build(projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive); - ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); - try { - // Phase 2: get effective models from the reactor - List results = build(projectIndex, interimResults); - - request.getRepositorySession() - .getData() - .set(ModelTransformerContext.KEY, transformerContextBuilder.build()); - - return results; + return pomFiles.stream() + .map(pomFile -> build(pomFile, recursive)) + .flatMap(List::stream) + .collect(Collectors.toList()); } finally { Thread.currentThread().setContextClassLoader(oldContextClassLoader); } } @SuppressWarnings("checkstyle:parameternumber") - private List build( - Map projectIndex, - List pomFiles, - Set aggregatorFiles, - boolean root, - boolean recursive) { - List> tasks = pomFiles.stream() - .map(pomFile -> ((Callable) - () -> build(projectIndex, pomFile, concat(aggregatorFiles, pomFile), root, recursive))) - .collect(Collectors.toList()); - try { - List> futures = executor.invokeAll(tasks); - List list = new ArrayList<>(); - for (Future future : futures) { - InterimResult interimResult = future.get(); - list.add(interimResult); - } - return list; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private Set concat(Set set, T elem) { - Set newSet = new HashSet<>(set); - newSet.add(elem); - return newSet; - } - - @SuppressWarnings("checkstyle:parameternumber") - private InterimResult build( - Map projectIndex, - File pomFile, - Set aggregatorFiles, - boolean isRoot, - boolean recursive) { - MavenProject project = new MavenProject(); - project.setFile(pomFile); - - project.setRootDirectory( - rootLocator.findRoot(pomFile.getParentFile().toPath())); - - DefaultModelBuildingListener listener = - new DefaultModelBuildingListener(project, projectBuildingHelper, request); - - ModelBuilderRequest modelBuildingRequest = getModelBuildingRequest() - .source(ModelSource.fromPath(pomFile.toPath())) - .projectBuild(true) - .twoPhaseBuilding(true) - .locationTracking(true) - .listener(listener) - .build(); - + private List build(File pomFile, boolean recursive) { ModelBuilderResult result; try { - result = modelBuilder.build(modelBuildingRequest); + ModelTransformer injector = (m, r, p) -> { + MavenProject project = projectIndex.computeIfAbsent(m.getId(), f -> new MavenProject()); + return injectLifecycleBindings(m, r, p, project, request); + }; + ModelBuilderRequest modelBuildingRequest = getModelBuildingRequest() + .source(ModelSource.fromPath(pomFile.toPath())) + .requestType(ModelBuilderRequest.RequestType.BUILD_POM) + .locationTracking(true) + .recursive(recursive) + .lifecycleBindingsInjector(injector) + .build(); + result = modelBuilderSession.build(modelBuildingRequest); } catch (ModelBuilderException e) { result = e.getResult(); - if (result == null || result.getFileModel() == null) { - return new InterimResult( - modelBuildingRequest, - new DefaultProjectBuildingResult(e.getModelId(), pomFile, convert(e.getProblems()))); - } - // validation error, continue project building and delay failing to help IDEs - // result.getProblems().addAll(e.getProblems()) ? - } - - Model model = result.getActivatedFileModel(); - - // In case the model is using CI friendly versions, at this point, it will contain uninterpolated version - // such as ${revision}${changelist}, so we need to take care of it now - Model modelWithVersion = getModelWithInterpolatedVersion(model, result.getProblems()::add); - modelPool.put(model.getPomFile(), modelWithVersion); - - InterimResult interimResult = new InterimResult(pomFile, modelBuildingRequest, result, project, isRoot); - - if (recursive) { - File basedir = pomFile.getParentFile(); - List subprojects = model.getSubprojects(); - if (subprojects.isEmpty()) { - subprojects = model.getModules(); - } - List subprojectFiles = new ArrayList<>(); - for (String subproject : subprojects) { - if (subproject == null || subproject.isEmpty()) { - continue; - } - - subproject = subproject.replace('\\', File.separatorChar).replace('/', File.separatorChar); - - Path subprojectPath = modelProcessor.locateExistingPom(new File(basedir, subproject).toPath()); - File subprojectFile = subprojectPath != null ? subprojectPath.toFile() : null; - - if (subprojectFile == null) { - ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem( - "Child subproject " + subprojectFile + " of " + pomFile + " does not exist", - ModelProblem.Severity.ERROR, - ModelProblem.Version.BASE, - model, - -1, - -1, - null); - result.getProblems().add(problem); - continue; - } - - if (Os.IS_WINDOWS) { - // we don't canonicalize on unix to avoid interfering with symlinks - try { - subprojectFile = subprojectFile.getCanonicalFile(); - } catch (IOException e) { - subprojectFile = subprojectFile.getAbsoluteFile(); - } - } else { - subprojectFile = new File(subprojectFile.toURI().normalize()); - } - - if (aggregatorFiles.contains(subprojectFile)) { - StringBuilder buffer = new StringBuilder(256); - for (File aggregatorFile : aggregatorFiles) { - buffer.append(aggregatorFile).append(" -> "); - } - buffer.append(subprojectFile); - - ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem( - "Child subproject " + subprojectFile + " of " + pomFile + " forms aggregation cycle " - + buffer, - ModelProblem.Severity.ERROR, - ModelProblem.Version.BASE, - model, - -1, - -1, - null); - result.getProblems().add(problem); - - continue; - } - - subprojectFiles.add(subprojectFile); - } - - if (!subprojectFiles.isEmpty()) { - interimResult.subprojects = build(projectIndex, subprojectFiles, aggregatorFiles, false, recursive); + if (result == null || result.getEffectiveModel() == null) { + return List.of(new DefaultProjectBuildingResult(e.getModelId(), pomFile, convert(e.getProblems()))); } } - projectIndex.put(pomFile, project); - - return interimResult; - } - - private Model getModelWithInterpolatedVersion( - Model model, Consumer problems) { - String version = model.getVersion(); - if (version == null && model.getParent() != null) { - version = model.getParent().getVersion(); - } - if (version != null && version.contains("${")) { - try { - StringSearchInterpolator interpolator = new StringSearchInterpolator(); - interpolator.addValueSource(new AbstractValueSource(false) { - @Override - public String getValue(String key) { - String val = request.getUserProperties().getProperty(key); - if (val == null) { - val = model.getProperties().get(key); - if (val == null) { - val = request.getSystemProperties().getProperty(key); - if (val == null) { - val = ciFriendlyVersions.get(key); - } - } - } - if (val != null) { - String oldVal = ciFriendlyVersions.put(key, val); - if (oldVal != null && !val.equals(oldVal)) { - throw new MavenException("Non unique property value detected for key '" + key - + "' which is bound to '" + oldVal + "' and '" + val + "'"); - } - } - return val; - } - }); - version = interpolator.interpolate(version); - } catch (InterpolationException | MavenException e) { - ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem( - "Unable to interpolate ", - ModelProblem.Severity.ERROR, - ModelProblem.Version.BASE, - model, - -1, - -1, - null); - problems.accept(problem); - } - if (model.getVersion() != null) { - return model.withVersion(version); - } else { - return model.withParent(model.getParent().withVersion(version)); - } - } - return model; - } - - private List build( - Map projectIndex, List interimResults) { - // The transformation may need to access dependencies raw models, - // which may cause some re-entrance in the build() method and can - // actually cause deadlocks. In order to workaround the problem, - // we do a first pass by reading all rawModels in order. List results = new ArrayList<>(); - boolean failure = false; - for (InterimResult r : interimResults) { - DefaultProjectBuildingResult res; - try { - Model model = modelBuilder.buildRawModel(r.request); - res = new DefaultProjectBuildingResult( - model.getId(), - model.getPomFile() != null ? model.getPomFile().toFile() : null, - null); - } catch (ModelBuilderException e) { - failure = true; - res = new DefaultProjectBuildingResult( - e.getModelId(), - r.request.getSource().getPath() != null - ? r.request.getSource().getPath().toFile() - : null, - convert(e.getProblems())); - } - results.add(res); - } - if (failure) { - return results; - } + List allModels = results(result).toList(); + for (ModelBuilderResult r : allModels) { + File pom = r.getSource().getPath().toFile(); + MavenProject project = projectIndex.get(r.getEffectiveModel().getId()); + Path rootDirectory = rootLocator.findRoot(pom.getParentFile().toPath()); + project.setRootDirectory(rootDirectory); + project.setFile(pom); + project.setExecutionRoot(pom.equals(pomFile)); + initProject(project, r); + project.setCollectedProjects(results(r) + .filter(cr -> cr != r) + .map(cr -> projectIndex.get(cr.getEffectiveModel().getId())) + .collect(Collectors.toList())); - List>> callables = interimResults.stream() - .map(interimResult -> - (Callable>) () -> doBuild(projectIndex, interimResult)) - .collect(Collectors.toList()); - - try { - List>> futures = executor.invokeAll(callables); - return futures.stream() - .map(listFuture -> { - try { - return listFuture.get(); - } catch (InterruptedException e) { - uncheckedThrow(e); - return null; - } catch (ExecutionException e) { - uncheckedThrow(e.getCause()); - return null; - } - }) - .flatMap(List::stream) - .collect(Collectors.toList()); - } catch (InterruptedException e) { - uncheckedThrow(e); - return null; - } - } - - private List doBuild(Map projectIndex, InterimResult interimResult) { - if (interimResult.projectBuildingResult != null) { - return Collections.singletonList(interimResult.projectBuildingResult); - } - MavenProject project = interimResult.project; - try { - ModelBuilderResult result = modelBuilder.build(ModelBuilderRequest.builder(interimResult.request) - .interimResult(interimResult.result) - .build()); - - // 2nd pass of initialization: resolve and build parent if necessary - List problems = convert(result.getProblems()); - try { - initProject(project, projectIndex, result); - } catch (InvalidArtifactRTException iarte) { - problems.add(new DefaultModelProblem( - null, - org.apache.maven.model.building.ModelProblem.Severity.ERROR, - null, - new org.apache.maven.model.Model(result.getEffectiveModel()), - -1, - -1, - iarte)); - } - - List results = build(projectIndex, interimResult.subprojects); - - project.setExecutionRoot(interimResult.root); - project.setCollectedProjects( - results.stream().map(ProjectBuildingResult::getProject).collect(Collectors.toList())); DependencyResolutionResult resolutionResult = null; if (request.isResolveDependencies()) { resolutionResult = resolveDependencies(project); } - - results.add(new DefaultProjectBuildingResult(project, problems, resolutionResult)); - - return results; - } catch (ModelBuilderException e) { - DefaultProjectBuildingResult result; - if (project == null || interimResult.result.getEffectiveModel() == null) { - result = new DefaultProjectBuildingResult( - e.getModelId(), interimResult.pomFile, convert(e.getProblems())); - } else { - project.setModel(new org.apache.maven.model.Model(interimResult.result.getEffectiveModel())); - result = new DefaultProjectBuildingResult(project, convert(e.getProblems()), null); - } - return Collections.singletonList(result); + results.add(new DefaultProjectBuildingResult(project, convert(result.getProblems()), resolutionResult)); } + return results; + } + + private Stream results(ModelBuilderResult result) { + return Stream.concat(result.getChildren().stream().flatMap(this::results), Stream.of(result)); } private List convert(List problems) { @@ -943,26 +529,27 @@ private List convert(List (org.apache.maven.model.building.ModelProblem) new DefaultModelProblem( - p.getMessage(), - org.apache.maven.model.building.ModelProblem.Severity.valueOf( - p.getSeverity().name()), - org.apache.maven.model.building.ModelProblem.Version.valueOf( - p.getVersion().name()), - p.getSource(), - p.getLineNumber(), - p.getColumnNumber(), - p.getModelId(), - p.getException())) + .map(p -> (org.apache.maven.model.building.ModelProblem) + new org.apache.maven.model.building.DefaultModelProblem( + p.getMessage(), + org.apache.maven.model.building.ModelProblem.Severity.valueOf( + p.getSeverity().name()), + org.apache.maven.model.building.ModelProblem.Version.valueOf( + p.getVersion().name()), + p.getSource(), + p.getLineNumber(), + p.getColumnNumber(), + p.getModelId(), + p.getException())) .toList(); } @SuppressWarnings({"checkstyle:methodlength", "deprecation"}) - private void initProject(MavenProject project, Map projects, ModelBuilderResult result) { + private void initProject(MavenProject project, ModelBuilderResult result) { project.setModel(new org.apache.maven.model.Model(result.getEffectiveModel())); project.setOriginalModel(new org.apache.maven.model.Model(result.getFileModel())); - initParent(project, projects, result); + initParent(project, result); Artifact projectArtifact = repositorySystem.createArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging()); @@ -976,16 +563,14 @@ private void initProject(MavenProject project, Map projects, project.addTestCompileSourceRoot(build.getTestSourceDirectory()); } - project.setActiveProfiles(Stream.concat( - result.getActivePomProfiles(result.getModelIds().get(0)).stream(), - result.getActiveExternalProfiles().stream()) - .map(org.apache.maven.model.Profile::new) - .toList()); + project.setActiveProfiles( + Stream.concat(result.getActivePomProfiles().stream(), result.getActiveExternalProfiles().stream()) + .map(org.apache.maven.model.Profile::new) + .toList()); project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles())); - for (String modelId : result.getModelIds()) { - project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId))); - } + project.setInjectedProfileIds( + result.getEffectiveModel().getId(), getProfileIds(result.getActivePomProfiles())); // // All the parts that were taken out of MavenProject for Maven 4.0.0 @@ -1115,24 +700,17 @@ private void initProject(MavenProject project, Map projects, } } - private void initParent(MavenProject project, Map projects, ModelBuilderResult result) { - Model parentModel = result.getModelIds().size() > 1 - && !result.getModelIds().get(1).isEmpty() - ? result.getRawModel(result.getModelIds().get(1)).orElse(null) - : null; + private void initParent(MavenProject project, ModelBuilderResult result) { + Model parentModel = result.getParentModel(); if (parentModel != null) { - final String parentGroupId = inheritedGroupId(result, 1); - final String parentVersion = inheritedVersion(result, 1); + final String parentGroupId = getGroupId(parentModel); + final String parentVersion = getVersion(parentModel); project.setParentArtifact(repositorySystem.createProjectArtifact( parentGroupId, parentModel.getArtifactId(), parentVersion)); - // org.apache.maven.its.mng4834:parent:0.1 - String parentModelId = result.getModelIds().get(1); - Path parentPomFile = - result.getRawModel(parentModelId).map(Model::getPomFile).orElse(null); - MavenProject parent = parentPomFile != null ? projects.get(parentPomFile.toFile()) : null; + MavenProject parent = projectIndex.get(parentModel.getId()); if (parent == null) { // // At this point the DefaultModelBuildingListener has fired and it populates the @@ -1140,6 +718,7 @@ private void initParent(MavenProject project, Map projects, // defined repositories. // request.setRemoteRepositories(project.getRemoteArtifactRepositories()); + Path parentPomFile = parentModel.getPomFile(); if (parentPomFile != null) { project.setParentFile(parentPomFile.toFile()); try { @@ -1183,8 +762,7 @@ private ModelBuilderRequest.ModelBuilderRequestBuilder getModelBuildingRequest() InternalSession internalSession = InternalSession.from(session); modelBuildingRequest.session(internalSession); - modelBuildingRequest.validationLevel(request.getValidationLevel()); - modelBuildingRequest.processPlugins(request.isProcessPlugins()); + modelBuildingRequest.requestType(ModelBuilderRequest.RequestType.BUILD_POM); modelBuildingRequest.profiles( request.getProfiles() != null ? request.getProfiles().stream() @@ -1195,33 +773,11 @@ private ModelBuilderRequest.ModelBuilderRequestBuilder getModelBuildingRequest() modelBuildingRequest.inactiveProfileIds(request.getInactiveProfileIds()); modelBuildingRequest.systemProperties(toMap(request.getSystemProperties())); modelBuildingRequest.userProperties(toMap(request.getUserProperties())); - // bv4: modelBuildingRequest.setBuildStartTime(request.getBuildStartTime()); - modelBuildingRequest.modelResolver(modelResolver); - DefaultModelRepositoryHolder holder = new DefaultModelRepositoryHolder( - internalSession, - DefaultModelRepositoryHolder.RepositoryMerging.valueOf( - request.getRepositoryMerging().name()), - repositories.stream() - .map(internalSession::getRemoteRepository) - .toList()); - modelBuildingRequest.modelRepositoryHolder(holder); - modelBuildingRequest.transformerContextBuilder(transformerContextBuilder); + modelBuildingRequest.repositoryMerging(ModelBuilderRequest.RepositoryMerging.valueOf( + request.getRepositoryMerging().name())); modelBuildingRequest.repositories(request.getRemoteRepositories().stream() .map(r -> internalSession.getRemoteRepository(RepositoryUtils.toRepo(r))) .toList()); - /* TODO: bv4 - InternalMavenSession session = - (InternalMavenSession) this.session.getData().get(InternalMavenSession.class); - if (session != null) { - try { - modelBuildingRequest.setRootDirectory(session.getRootDirectory()); - } catch (IllegalStateException e) { - // can happen if root directory cannot be found, just ignore - } - } - */ - internalSession.getData().set(SessionData.key(ModelResolver.class), modelResolver); - return modelBuildingRequest; } @@ -1266,45 +822,29 @@ private List getProfileIds(List profiles) { } private static ModelSource createStubModelSource(Artifact artifact) { - StringBuilder buffer = new StringBuilder(1024); - - buffer.append(""); - buffer.append(""); - buffer.append("4.0.0"); - buffer.append("").append(artifact.getGroupId()).append(""); - buffer.append("").append(artifact.getArtifactId()).append(""); - buffer.append("").append(artifact.getBaseVersion()).append(""); - buffer.append("").append(artifact.getType()).append(""); - buffer.append(""); - - String xml = buffer.toString(); - + String xml = "" + "" + + "4.0.0" + + "" + + artifact.getGroupId() + "" + "" + + artifact.getArtifactId() + "" + "" + + artifact.getBaseVersion() + "" + "" + + artifact.getType() + "" + ""; return new StubModelSource(xml, artifact); } - private static String inheritedGroupId(final ModelBuilderResult result, final int modelIndex) { - String groupId = null; - final String modelId = result.getModelIds().get(modelIndex); - - if (!modelId.isEmpty()) { - final Model model = result.getRawModel(modelId).orElseThrow(); - groupId = model.getGroupId() != null ? model.getGroupId() : inheritedGroupId(result, modelIndex + 1); + static String getGroupId(Model model) { + String groupId = model.getGroupId(); + if (groupId == null && model.getParent() != null) { + groupId = model.getParent().getGroupId(); } - return groupId; } - private static String inheritedVersion(final ModelBuilderResult result, final int modelIndex) { - String version = null; - final String modelId = result.getModelIds().get(modelIndex); - - if (!modelId.isEmpty()) { - version = result.getRawModel(modelId).map(Model::getVersion).orElse(null); - if (version == null) { - version = inheritedVersion(result, modelIndex + 1); - } + static String getVersion(Model model) { + String version = model.getVersion(); + if (version == null && model.getParent() != null) { + version = model.getParent().getVersion(); } - return version; } @@ -1312,17 +852,11 @@ private static Map toMap(Properties properties) { if (properties != null && !properties.isEmpty()) { return properties.entrySet().stream() .collect(Collectors.toMap(e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue()))); - } else { return null; } } - @SuppressWarnings("unchecked") - static void uncheckedThrow(Throwable t) throws T { - throw (T) t; // rely on vacuous cast - } - static class LazyMap extends AbstractMap { private final Supplier> supplier; private volatile Map delegate; @@ -1344,81 +878,46 @@ public Set> entrySet() { } } - protected abstract class ModelResolverWrapper implements ModelResolver { + private Model injectLifecycleBindings( + Model model, + ModelBuilderRequest request, + ModelProblemCollector problems, + MavenProject project, + ProjectBuildingRequest projectBuildingRequest) { + org.apache.maven.model.Model model3 = new org.apache.maven.model.Model(model); + List remoteRepositories = projectBuildingRequest.getRemoteRepositories(); + List pluginRepositories = projectBuildingRequest.getPluginArtifactRepositories(); + try { + pluginRepositories = projectBuildingHelper.createArtifactRepositories( + model3.getPluginRepositories(), pluginRepositories, projectBuildingRequest); + } catch (Exception e) { + problems.add(Severity.ERROR, Version.BASE, "Invalid plugin repository: " + e.getMessage(), e); + } + project.setPluginArtifactRepositories(pluginRepositories); + + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { + try { + ProjectRealmCache.CacheRecord record = + projectBuildingHelper.createProjectRealm(project, model3, projectBuildingRequest); - protected abstract org.apache.maven.model.resolution.ModelResolver getResolver( - List repositories); + project.setClassRealm(record.getRealm()); + project.setExtensionDependencyFilter(record.getExtensionArtifactFilter()); + } catch (PluginResolutionException | PluginManagerException | PluginVersionResolutionException e) { - @Override - public ModelSource resolveModel( - Session session, - List repositories, - String groupId, - String artifactId, - String version, - Consumer resolved) - throws ModelResolverException { - try { - InternalSession internalSession = InternalSession.from(session); - org.apache.maven.model.resolution.ModelResolver resolver = getResolver(internalSession.toRepositories( - repositories != null ? repositories : internalSession.getRemoteRepositories())); - org.apache.maven.model.Parent p = new org.apache.maven.model.Parent(Parent.newBuilder() - .groupId(groupId) - .artifactId(artifactId) - .version(version) - .build()); - org.apache.maven.model.building.ModelSource modelSource = resolver.resolveModel(p); - if (!p.getVersion().equals(version)) { - resolved.accept(p.getVersion()); - } - return toSource(modelSource); - } catch (UnresolvableModelException e) { - throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e); + problems.add(Severity.ERROR, Version.BASE, "Unresolvable build extension: " + e.getMessage(), e); } + projectBuildingHelper.selectProjectRealm(project); } - @Override - public ModelSource resolveModel( - Session session, - List repositories, - Parent parent, - AtomicReference modified) - throws ModelResolverException { - try { - org.apache.maven.model.Parent p = new org.apache.maven.model.Parent(parent); - InternalSession internalSession = InternalSession.from(session); - org.apache.maven.model.resolution.ModelResolver resolver = getResolver(internalSession.toRepositories( - repositories != null ? repositories : internalSession.getRemoteRepositories())); - ModelSource source = toSource(resolver.resolveModel(p)); - if (p.getDelegate() != parent) { - modified.set(p.getDelegate()); - } - return source; - } catch (UnresolvableModelException e) { - throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e); - } + // build the regular repos after extensions are loaded to allow for custom layouts + try { + remoteRepositories = projectBuildingHelper.createArtifactRepositories( + model3.getRepositories(), remoteRepositories, projectBuildingRequest); + } catch (Exception e) { + problems.add(Severity.ERROR, Version.BASE, "Invalid artifact repository: " + e.getMessage(), e); } + project.setRemoteArtifactRepositories(remoteRepositories); - @Override - public ModelSource resolveModel( - Session session, - List repositories, - Dependency dependency, - AtomicReference modified) - throws ModelResolverException { - try { - org.apache.maven.model.Dependency d = new org.apache.maven.model.Dependency(dependency); - InternalSession internalSession = InternalSession.from(session); - org.apache.maven.model.resolution.ModelResolver resolver = getResolver(internalSession.toRepositories( - repositories != null ? repositories : internalSession.getRemoteRepositories())); - ModelSource source = toSource(resolver.resolveModel(d)); - if (d.getDelegate() != dependency) { - modified.set(d.getDelegate()); - } - return source; - } catch (UnresolvableModelException e) { - throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e); - } - } + return lifecycleBindingsInjector.injectLifecycleBindings(model3.getDelegate(), request, problems); } } diff --git a/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java b/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java index 183132a5e3e8..078ce850ed8d 100644 --- a/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java +++ b/maven-core/src/main/java/org/apache/maven/project/ReactorModelPool.java @@ -66,7 +66,9 @@ private String getVersion(Model model) { } void put(Path pomFile, Model model) { - modelsByPath.put(pomFile, model); + if (pomFile != null) { + modelsByPath.put(pomFile, model); + } modelsByGa .computeIfAbsent(new GAKey(getGroupId(model), model.getArtifactId()), k -> new HashSet<>()) .add(model); diff --git a/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java b/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java index 443e976129bb..3560082c6c03 100644 --- a/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java +++ b/maven-core/src/test/java/org/apache/maven/AbstractCoreMavenComponentTestCase.java @@ -38,6 +38,7 @@ import org.apache.maven.internal.impl.DefaultLookup; import org.apache.maven.internal.impl.DefaultSessionFactory; import org.apache.maven.internal.impl.InternalMavenSession; +import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.model.Build; import org.apache.maven.model.Dependency; import org.apache.maven.model.Exclusion; @@ -89,6 +90,7 @@ protected File getProject(String name) throws Exception { protected MavenExecutionRequest createMavenExecutionRequest(File pom) throws Exception { MavenExecutionRequest request = new DefaultMavenExecutionRequest() + .setRootDirectory(pom != null ? pom.toPath().getParent() : null) .setPom(pom) .setProjectPresent(true) .setShowErrors(true) @@ -119,16 +121,16 @@ protected MavenSession createMavenSession(File pom, Properties executionProperti protected MavenSession createMavenSession(File pom, Properties executionProperties, boolean includeModules) throws Exception { MavenExecutionRequest request = createMavenExecutionRequest(pom); - RepositorySystemSession rsession = MavenTestHelper.createSession(mavenRepositorySystem); ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest() - .setRepositorySession(rsession) .setLocalRepository(request.getLocalRepository()) .setRemoteRepositories(request.getRemoteRepositories()) .setPluginArtifactRepositories(request.getPluginArtifactRepositories()) .setSystemProperties(executionProperties) .setUserProperties(new Properties()); + initRepoSession(request, configuration); + List projects = new ArrayList<>(); if (pom != null) { @@ -151,34 +153,45 @@ protected MavenSession createMavenSession(File pom, Properties executionProperti projects.add(project); } - initRepoSession(configuration); + InternalSession iSession = InternalSession.from(configuration.getRepositorySession()); + InternalMavenSession mSession = InternalMavenSession.from(iSession); + MavenSession session = mSession.getMavenSession(); - DefaultSessionFactory defaultSessionFactory = - new DefaultSessionFactory(repositorySystem, null, new DefaultLookup(container), null); - - MavenSession session = new MavenSession( - getContainer(), configuration.getRepositorySession(), request, new DefaultMavenExecutionResult()); session.setProjects(projects); session.setAllProjects(session.getProjects()); - session.setSession(defaultSessionFactory.newSession(session)); - - SessionScope sessionScope = getContainer().lookup(SessionScope.class); - sessionScope.enter(); - sessionScope.seed(MavenSession.class, session); - sessionScope.seed(Session.class, session.getSession()); - sessionScope.seed(InternalMavenSession.class, InternalMavenSession.from(session.getSession())); return session; } - protected void initRepoSession(ProjectBuildingRequest request) throws Exception { - File localRepoDir = new File(request.getLocalRepository().getBasedir()); + protected void initRepoSession( + MavenExecutionRequest mavenExecutionRequest, ProjectBuildingRequest projectBuildingRequest) + throws Exception { + File localRepoDir = new File(projectBuildingRequest.getLocalRepository().getBasedir()); LocalRepository localRepo = new LocalRepository(localRepoDir, "simple"); + RepositorySystemSession session = new MavenSessionBuilderSupplier(repositorySystem) .get() .withLocalRepositories(localRepo) .build(); - request.setRepositorySession(session); + projectBuildingRequest.setRepositorySession(session); + + DefaultSessionFactory defaultSessionFactory = + new DefaultSessionFactory(repositorySystem, null, new DefaultLookup(container), null); + + MavenSession mSession = new MavenSession( + container, + projectBuildingRequest.getRepositorySession(), + mavenExecutionRequest, + new DefaultMavenExecutionResult()); + + InternalSession iSession = defaultSessionFactory.newSession(mSession); + mSession.setSession(iSession); + + SessionScope sessionScope = getContainer().lookup(SessionScope.class); + sessionScope.enter(); + sessionScope.seed(MavenSession.class, mSession); + sessionScope.seed(Session.class, iSession); + sessionScope.seed(InternalMavenSession.class, InternalMavenSession.from(iSession)); } protected MavenProject createStubMavenProject() { diff --git a/maven-core/src/test/java/org/apache/maven/MavenTestHelper.java b/maven-core/src/test/java/org/apache/maven/MavenTestHelper.java index 3176a9473bbc..358ab86ebe6d 100644 --- a/maven-core/src/test/java/org/apache/maven/MavenTestHelper.java +++ b/maven-core/src/test/java/org/apache/maven/MavenTestHelper.java @@ -18,33 +18,24 @@ */ package org.apache.maven; -import java.util.List; - import org.apache.maven.bridge.MavenRepositorySystem; import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.DefaultMavenExecutionResult; import org.apache.maven.execution.MavenSession; -import org.apache.maven.internal.impl.DefaultRepositoryFactory; +import org.apache.maven.internal.impl.DefaultLookup; import org.apache.maven.internal.impl.DefaultSession; import org.apache.maven.internal.impl.InternalSession; +import org.codehaus.plexus.PlexusContainer; import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider; -import org.eclipse.aether.internal.impl.DefaultRemoteRepositoryManager; -import org.eclipse.aether.internal.impl.DefaultUpdatePolicyAnalyzer; public class MavenTestHelper { - public static DefaultRepositorySystemSession createSession(MavenRepositorySystem repositorySystem) { + public static DefaultRepositorySystemSession createSession( + MavenRepositorySystem repositorySystem, PlexusContainer container) { DefaultRepositorySystemSession repoSession = new DefaultRepositorySystemSession(h -> false); DefaultMavenExecutionRequest request = new DefaultMavenExecutionRequest(); MavenSession mavenSession = new MavenSession(repoSession, request, new DefaultMavenExecutionResult()); - DefaultSession session = new DefaultSession( - mavenSession, - null, - null, - repositorySystem, - new SimpleLookup(List.of(new DefaultRepositoryFactory(new DefaultRemoteRepositoryManager( - new DefaultUpdatePolicyAnalyzer(), new DefaultChecksumPolicyProvider())))), - null); + DefaultSession session = + new DefaultSession(mavenSession, null, null, repositorySystem, new DefaultLookup(container), null); InternalSession.associate(repoSession, session); return repoSession; } diff --git a/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java b/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java index c21e8902381a..0cf6c5c92ca0 100644 --- a/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java +++ b/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomBuilderTest.java @@ -19,32 +19,35 @@ package org.apache.maven.internal.transformation.impl; import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; import java.io.InputStream; +import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import org.apache.maven.api.RemoteRepository; import org.apache.maven.api.Session; +import org.apache.maven.api.SessionData; import org.apache.maven.api.model.Model; -import org.apache.maven.api.services.ModelResolver; -import org.apache.maven.api.services.ModelResolverException; +import org.apache.maven.api.model.Parent; +import org.apache.maven.api.services.ModelBuilder; import org.apache.maven.api.services.ModelSource; -import org.apache.maven.artifact.repository.MavenArtifactRepository; -import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; +import org.apache.maven.api.services.model.ModelResolver; +import org.apache.maven.api.services.model.ModelResolverException; +import org.apache.maven.api.spi.ModelTransformer; +import org.apache.maven.api.spi.ModelTransformerException; +import org.apache.maven.di.Injector; import org.apache.maven.internal.impl.InternalMavenSession; import org.apache.maven.internal.impl.InternalSession; +import org.apache.maven.internal.impl.model.DefaultModelBuilder; import org.apache.maven.internal.transformation.AbstractRepositoryTestCase; import org.apache.maven.model.v4.MavenStaxReader; import org.apache.maven.project.MavenProject; -import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.sisu.Priority; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -55,6 +58,32 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase { @Inject ConsumerPomBuilder builder; + @BeforeEach + void setupTransformerContext() throws Exception { + // We need to hack things a bit here to get the transformer context to work + // * we cannot use the CIFriendlyVersionModelTransformer directly because + // it's a session scoped bean and all tests using a model builder would have + // to use a session and initialize the scope in order for DI to start + // * the transformer context is supposed to be immutable but in this case + // we don't build the full projects before, so we need to pass a mutable + // context to the model builder + // * we also need to bind the model resolver explicitly to avoid going + // to maven central + getContainer().lookup(Injector.class).bindImplicit(MyModelResolver.class); + InternalSession iSession = InternalSession.from(session); + // set up the transformers + List transformers = List.of(new CIFriendlyVersionModelTransformer(iSession)); + Field transformersField = DefaultModelBuilder.class.getDeclaredField("transformers"); + transformersField.setAccessible(true); + DefaultModelBuilder modelBuilder = (DefaultModelBuilder) getContainer().lookup(ModelBuilder.class); + transformersField.set(modelBuilder, transformers); + transformersField = DefaultConsumerPomBuilder.class.getDeclaredField("transformers"); + transformersField.setAccessible(true); + transformersField.set(builder, transformers); + // set up the model resolver + iSession.getData().set(SessionData.key(ModelResolver.class), new MyModelResolver()); + } + @Test void testTrivialConsumer() throws Exception { MavenProject project; @@ -63,11 +92,12 @@ void testTrivialConsumer() throws Exception { org.apache.maven.model.Model model = new org.apache.maven.model.Model(new MavenStaxReader().read(inputStream)); project = new MavenProject(model); - project.setRootDirectory(Paths.get("src/test/resources/consumer/trivial")); project.setOriginalModel(model); - project.setRemoteArtifactRepositories(Collections.singletonList(new MavenArtifactRepository( - "central", "http://repo.maven.apache.org/", new DefaultRepositoryLayout(), null, null))); } + InternalMavenSession.from(InternalSession.from(session)) + .getMavenSession() + .getRequest() + .setRootDirectory(Paths.get("src/test/resources/consumer/trivial")); Model model = builder.build(session, project, file); assertNotNull(model); @@ -77,14 +107,16 @@ void testTrivialConsumer() throws Exception { void testSimpleConsumer() throws Exception { MavenProject project; Path file = Paths.get("src/test/resources/consumer/simple/simple-parent/simple-weather/pom.xml"); - ((DefaultRepositorySystemSession) session).setUserProperty("changelist", "MNG6957"); + + InternalMavenSession.from(InternalSession.from(session)) + .getMavenSession() + .getRequest() + .getUserProperties() + .setProperty("changelist", "MNG6957"); try (InputStream inputStream = Files.newInputStream(file)) { org.apache.maven.model.Model model = new org.apache.maven.model.Model(new MavenStaxReader().read(inputStream)); project = new MavenProject(model); - project.setRootDirectory(Paths.get("src/test/resources/consumer/simple")); - project.setRemoteArtifactRepositories(Collections.singletonList(new MavenArtifactRepository( - "central", "http://repo.maven.apache.org/", new DefaultRepositoryLayout(), null, null))); project.setOriginalModel(model); } InternalMavenSession.from(InternalSession.from(session)) @@ -97,10 +129,7 @@ void testSimpleConsumer() throws Exception { assertTrue(model.getProfiles().isEmpty()); } - @Named - @Singleton - @Priority(10) - public static class MyModelResolver implements ModelResolver { + static class MyModelResolver implements ModelResolver { @Override public ModelSource resolveModel( Session session, @@ -121,4 +150,39 @@ public ModelSource resolveModel( return null; } } + + static class CIFriendlyVersionModelTransformer implements ModelTransformer { + private static final String SHA1_PROPERTY = "sha1"; + private static final String CHANGELIST_PROPERTY = "changelist"; + private static final String REVISION_PROPERTY = "revision"; + private final Session session; + + CIFriendlyVersionModelTransformer(Session session) { + this.session = session; + } + + @Override + public Model transformFileModel(Model model) throws ModelTransformerException { + return model.with() + .version(replaceCiFriendlyVersion(model.getVersion())) + .parent(replaceParent(model.getParent())) + .build(); + } + + Parent replaceParent(Parent parent) { + return parent != null ? parent.withVersion(replaceCiFriendlyVersion(parent.getVersion())) : null; + } + + String replaceCiFriendlyVersion(String version) { + if (version != null) { + for (String key : Arrays.asList(SHA1_PROPERTY, CHANGELIST_PROPERTY, REVISION_PROPERTY)) { + String val = session.getUserProperties().get(key); + if (val != null) { + version = version.replace("${" + key + "}", val); + } + } + } + return version; + } + } } diff --git a/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java b/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java index 4596e6981465..8fecf35980c4 100644 --- a/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java +++ b/maven-core/src/test/java/org/apache/maven/model/ModelBuilderTest.java @@ -21,6 +21,7 @@ import javax.inject.Inject; import java.io.File; +import java.nio.file.Paths; import java.util.Collections; import java.util.List; @@ -66,6 +67,7 @@ public class ModelBuilderTest { void testModelBuilder() throws Exception { MavenExecutionRequest mavenRequest = new DefaultMavenExecutionRequest(); mavenRequest.setLocalRepository(mavenRepositorySystem.createLocalRepository(new File("target/test-repo/"))); + mavenRequest.setRootDirectory(Paths.get("src/test/resources/projects/tree")); DefaultProjectBuildingRequest request = new DefaultProjectBuildingRequest(); RepositorySystemSession.CloseableSession rsession = repositorySessionFactory diff --git a/maven-core/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java b/maven-core/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java index d20ec632803e..652479a879ab 100644 --- a/maven-core/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java +++ b/maven-core/src/test/java/org/apache/maven/project/AbstractMavenProjectTestCase.java @@ -26,6 +26,7 @@ import java.net.URL; import java.util.Arrays; +import org.apache.maven.api.Session; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.bridge.MavenRepositorySystem; import org.apache.maven.execution.DefaultMavenExecutionRequest; @@ -34,17 +35,18 @@ import org.apache.maven.internal.impl.DefaultLookup; import org.apache.maven.internal.impl.DefaultSession; import org.apache.maven.internal.impl.DefaultSessionFactory; +import org.apache.maven.internal.impl.InternalMavenSession; import org.apache.maven.model.building.ModelBuildingException; import org.apache.maven.model.building.ModelProblem; +import org.apache.maven.session.scope.internal.SessionScope; import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.testing.PlexusTest; import org.eclipse.aether.DefaultRepositoryCache; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; import org.junit.jupiter.api.BeforeEach; -import static org.mockito.Mockito.mock; - /** */ @PlexusTest @@ -54,6 +56,9 @@ public abstract class AbstractMavenProjectTestCase { @Inject protected MavenRepositorySystem repositorySystem; + @Inject + protected RepositorySystem repoSystem; + @Inject protected PlexusContainer container; @@ -151,18 +156,22 @@ protected ProjectBuildingRequest newBuildingRequest() throws Exception { return configuration; } - protected void initRepoSession(ProjectBuildingRequest request) { + protected void initRepoSession(ProjectBuildingRequest request) throws ComponentLookupException { File localRepo = new File(request.getLocalRepository().getBasedir()); DefaultRepositorySystemSession repoSession = new DefaultRepositorySystemSession(h -> false); DefaultSessionFactory defaultSessionFactory = - new DefaultSessionFactory(mock(RepositorySystem.class), null, new DefaultLookup(container), null); + new DefaultSessionFactory(repoSystem, repositorySystem, new DefaultLookup(container), null); MavenSession session = new MavenSession( getContainer(), repoSession, new DefaultMavenExecutionRequest(), new DefaultMavenExecutionResult()); session.setSession(defaultSessionFactory.newSession(session)); - new DefaultSession(session, null, null, null, null, null); + DefaultSession s = new DefaultSession(session, null, null, null, null, null); + SessionScope scope = container.lookup(SessionScope.class); + scope.enter(); + scope.seed(Session.class, s); + scope.seed(InternalMavenSession.class, s); repoSession.setCache(new DefaultRepositoryCache()); repoSession.setLocalRepositoryManager(new LegacyLocalRepositoryManager(localRepo)); diff --git a/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java b/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java index 98f8ba925403..1a963559db60 100644 --- a/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java +++ b/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java @@ -29,6 +29,7 @@ import org.apache.maven.api.services.model.ModelCache; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.internal.impl.InternalMavenSession; import org.apache.maven.internal.impl.InternalSession; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -323,6 +324,11 @@ void rereadPom_mng7063() throws Exception { final Path pom = projectRoot.resolve("pom.xml"); final ProjectBuildingRequest buildingRequest = newBuildingRequest(); + InternalMavenSession.from(InternalSession.from(buildingRequest.getRepositorySession())) + .getMavenSession() + .getRequest() + .setRootDirectory(projectRoot); + try (InputStream pomResource = DefaultMavenProjectBuilderTest.class.getResourceAsStream("/projects/reread/pom1.xml")) { Files.copy(pomResource, pom, StandardCopyOption.REPLACE_EXISTING); @@ -419,6 +425,12 @@ public void testBuildParentVersionRangeExternallyWithChildRevisionExpression() t public void testSubprojectDiscovery() throws Exception { File pom = getTestFile("src/test/resources/projects/subprojects-discover/pom.xml"); ProjectBuildingRequest configuration = newBuildingRequest(); + InternalSession internalSession = InternalSession.from(configuration.getRepositorySession()); + InternalMavenSession mavenSession = InternalMavenSession.from(internalSession); + mavenSession + .getMavenSession() + .getRequest() + .setRootDirectory(pom.toPath().getParent()); List results = projectBuilder.build(List.of(pom), true, configuration); assertEquals(2, results.size()); diff --git a/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java b/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java index 61908e584e48..c1d6a8ad3ecd 100644 --- a/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java +++ b/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java @@ -21,6 +21,8 @@ import javax.inject.Inject; import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -30,6 +32,8 @@ import org.apache.maven.MavenTestHelper; import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; import org.apache.maven.bridge.MavenRepositorySystem; +import org.apache.maven.internal.impl.InternalMavenSession; +import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.model.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginExecution; @@ -38,6 +42,7 @@ import org.apache.maven.model.ReportSet; import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.project.harness.PomTestWrapper; +import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.testing.PlexusTest; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; @@ -73,6 +78,9 @@ class PomConstructionTest { @Inject private MavenRepositorySystem repositorySystem; + @Inject + private PlexusContainer container; + private File testDirectory; @BeforeEach @@ -1226,7 +1234,7 @@ void testPropertiesNoDuplication() throws Exception { @Test void testPomInheritance() throws Exception { - PomTestWrapper pom = buildPom("pom-inheritance/sub"); + PomTestWrapper pom = buildPom("pom-inheritance/child-1"); assertEquals("parent-description", pom.getValue("description")); assertEquals("jar", pom.getValue("packaging")); } @@ -1888,13 +1896,23 @@ private PomTestWrapper buildPom( ? ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 : ModelBuildingRequest.VALIDATION_LEVEL_STRICT); - DefaultRepositorySystemSession repoSession = MavenTestHelper.createSession(repositorySystem); + DefaultRepositorySystemSession repoSession = MavenTestHelper.createSession(repositorySystem, container); LocalRepository localRepo = new LocalRepository(config.getLocalRepository().getBasedir()); repoSession.setLocalRepositoryManager( new SimpleLocalRepositoryManagerFactory().newInstance(repoSession, localRepo)); config.setRepositorySession(repoSession); + InternalSession iSession = InternalSession.from(repoSession); + InternalMavenSession mSession = InternalMavenSession.from(iSession); + Path root = pomFile.getParentFile().toPath(); + while (root != null + && !Files.isDirectory(root.resolve(".mvn")) + && Files.isRegularFile(root.resolve("../pom.xml"))) { + root = root.getParent(); + } + mSession.getMavenSession().getRequest().setRootDirectory(root); + return new PomTestWrapper(pomFile, projectBuilder.build(pomFile, config).getProject()); } diff --git a/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java b/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java index b798c39bdf0f..ef4eede2846c 100644 --- a/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java +++ b/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java @@ -35,10 +35,8 @@ import org.apache.maven.model.Dependency; import org.apache.maven.model.InputLocation; import org.apache.maven.model.Plugin; -import org.apache.maven.model.building.FileModelSource; import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.model.building.ModelProblem; -import org.apache.maven.model.building.ModelSource; import org.codehaus.plexus.util.FileUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -85,10 +83,9 @@ void testBuildFromModelSource() throws Exception { MavenSession mavenSession = createMavenSession(pomFile); ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); configuration.setRepositorySession(mavenSession.getRepositorySession()); - ModelSource modelSource = new FileModelSource(pomFile); ProjectBuildingResult result = getContainer() .lookup(org.apache.maven.project.ProjectBuilder.class) - .build(modelSource, configuration); + .build(pomFile, configuration); assertNotNull(result.getProject().getParentFile()); } @@ -174,6 +171,7 @@ void testReadModifiedPoms(@TempDir Path tempDir) throws Exception { FileUtils.copyDirectoryStructure(new File("src/test/resources/projects/grandchild-check"), tempDir.toFile()); MavenSession mavenSession = createMavenSession(null); + mavenSession.getRequest().setRootDirectory(tempDir); ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); configuration.setRepositorySession(mavenSession.getRepositorySession()); org.apache.maven.project.ProjectBuilder projectBuilder = @@ -223,7 +221,7 @@ void testReadErroneousMavenProjectContainsReference() throws Exception { // multi projects build entry point ProjectBuildingException ex2 = assertThrows( ProjectBuildingException.class, - () -> projectBuilder.build(Collections.singletonList(pomFile), false, configuration)); + () -> projectBuilder.build(Collections.singletonList(pomFile), true, configuration)); assertEquals(1, ex2.getResults().size()); MavenProject project2 = ex2.getResults().get(0).getProject(); @@ -319,7 +317,8 @@ void testBuildProperties() throws Exception { ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); configuration.setRepositorySession(mavenSession.getRepositorySession()); configuration.setResolveDependencies(true); - List result = projectBuilder.build(Collections.singletonList(file), true, configuration); + List result = + projectBuilder.build(Collections.singletonList(file), false, configuration); MavenProject project = result.get(0).getProject(); // verify a few typical parameters are not duplicated assertEquals(1, project.getTestCompileSourceRoots().size()); @@ -346,10 +345,9 @@ void testBuildFromModelSourceResolvesBasedir() throws Exception { MavenSession mavenSession = createMavenSession(null); ProjectBuildingRequest configuration = new DefaultProjectBuildingRequest(); configuration.setRepositorySession(mavenSession.getRepositorySession()); - ModelSource modelSource = new FileModelSource(pomFile); ProjectBuildingResult result = getContainer() .lookup(org.apache.maven.project.ProjectBuilder.class) - .build(modelSource, configuration); + .build(pomFile, configuration); assertEquals( pomFile.getAbsoluteFile(), diff --git a/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java b/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java index 8b04f176e95c..9aca25a9d36a 100644 --- a/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java +++ b/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java @@ -36,6 +36,7 @@ import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.project.harness.PomTestWrapper; import org.apache.maven.settings.v4.SettingsStaxReader; +import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.testing.PlexusTest; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; @@ -58,6 +59,9 @@ class PomConstructionWithSettingsTest { @Inject private MavenRepositorySystem repositorySystem; + @Inject + private PlexusContainer container; + private File testDirectory; @BeforeEach @@ -111,7 +115,7 @@ private PomTestWrapper buildPom(String pomPath) throws Exception { "local", localRepoUrl, new DefaultRepositoryLayout(), null, null)); config.setActiveProfileIds(settings.getActiveProfiles()); - DefaultRepositorySystemSession repoSession = MavenTestHelper.createSession(repositorySystem); + DefaultRepositorySystemSession repoSession = MavenTestHelper.createSession(repositorySystem, container); LocalRepository localRepo = new LocalRepository(config.getLocalRepository().getBasedir()); repoSession.setLocalRepositoryManager( diff --git a/maven-core/src/test/projects/project-builder/MNG-6723/.mvn/.gitkeep b/maven-core/src/test/projects/project-builder/MNG-6723/.mvn/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/maven-core/src/test/resources-project-builder/complete-model/w-parent/pom.xml b/maven-core/src/test/resources-project-builder/complete-model/w-parent/pom.xml index ae49ae2fb057..439c77eaf735 100644 --- a/maven-core/src/test/resources-project-builder/complete-model/w-parent/pom.xml +++ b/maven-core/src/test/resources-project-builder/complete-model/w-parent/pom.xml @@ -28,4 +28,7 @@ under the License. pom + + sub + diff --git a/maven-core/src/test/resources-project-builder/complete-model/w-parent/sub/sub/pom.xml b/maven-core/src/test/resources-project-builder/complete-model/w-parent/sub/sub/pom.xml new file mode 100644 index 000000000000..bbee8d734860 --- /dev/null +++ b/maven-core/src/test/resources-project-builder/complete-model/w-parent/sub/sub/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + + org.apache.maven.its.mng + test + 0.2 + + + sub + diff --git a/maven-core/src/test/resources-project-builder/complete-model/wo-parent/sub/pom.xml b/maven-core/src/test/resources-project-builder/complete-model/wo-parent/sub/pom.xml new file mode 100644 index 000000000000..bbee8d734860 --- /dev/null +++ b/maven-core/src/test/resources-project-builder/complete-model/wo-parent/sub/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + + org.apache.maven.its.mng + test + 0.2 + + + sub + diff --git a/maven-core/src/test/resources-project-builder/plugin-exec-inheritance/pom.xml b/maven-core/src/test/resources-project-builder/plugin-exec-inheritance/pom.xml index cbed746d1e6a..5e76b70f1d85 100644 --- a/maven-core/src/test/resources-project-builder/plugin-exec-inheritance/pom.xml +++ b/maven-core/src/test/resources-project-builder/plugin-exec-inheritance/pom.xml @@ -33,8 +33,8 @@ under the License. - child-1 - child-2 + w-merge + wo-merge diff --git a/maven-core/src/test/resources-project-builder/pom-inheritance/sub/pom.xml b/maven-core/src/test/resources-project-builder/pom-inheritance/child-1/pom.xml similarity index 100% rename from maven-core/src/test/resources-project-builder/pom-inheritance/sub/pom.xml rename to maven-core/src/test/resources-project-builder/pom-inheritance/child-1/pom.xml diff --git a/maven-core/src/test/resources-project-builder/pom-inheritance/child-2/pom.xml b/maven-core/src/test/resources-project-builder/pom-inheritance/child-2/pom.xml new file mode 100644 index 000000000000..c175dc2a4ad1 --- /dev/null +++ b/maven-core/src/test/resources-project-builder/pom-inheritance/child-2/pom.xml @@ -0,0 +1,36 @@ + + + + + + 4.0.0 + + + + + org.apache.maven.its.mng3843 + parent-1 + 0.1 + + + child-2 + diff --git a/maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-inherit-plugin/pom.xml b/maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-inherit-plugin/pom.xml new file mode 100644 index 000000000000..60f057cce38e --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-inherit-plugin/pom.xml @@ -0,0 +1,12 @@ + + + org.ops4j.pax + construct + 1.0 + + + 4.0.0 + org.ops4j + maven-inherit-plugin + 1.1 + diff --git a/maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-pax-plugin/pom.xml b/maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-pax-plugin/pom.xml new file mode 100644 index 000000000000..422f76d023df --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module-inheritance/maven-pax-plugin/pom.xml @@ -0,0 +1,12 @@ + + + org.ops4j.pax + construct + 1.0 + + + 4.0.0 + org.ops4j + maven-pax-plugin + 1.1 + diff --git a/maven-core/src/test/resources-project-builder/profile-module-inheritance/sub/pom.xml b/maven-core/src/test/resources-project-builder/profile-module-inheritance/sub/pom.xml index 60f057cce38e..d64644d4c9a8 100644 --- a/maven-core/src/test/resources-project-builder/profile-module-inheritance/sub/pom.xml +++ b/maven-core/src/test/resources-project-builder/profile-module-inheritance/sub/pom.xml @@ -7,6 +7,6 @@ 4.0.0 org.ops4j - maven-inherit-plugin + sub 1.1 diff --git a/maven-core/src/test/resources-project-builder/profile-module/module-1/pom.xml b/maven-core/src/test/resources-project-builder/profile-module/module-1/pom.xml new file mode 100644 index 000000000000..3d475ef2acaa --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module/module-1/pom.xml @@ -0,0 +1,9 @@ + + 4.0.0 + + gid + aid + 1.0 + + module-1 + diff --git a/maven-core/src/test/resources-project-builder/profile-module/module-2/pom.xml b/maven-core/src/test/resources-project-builder/profile-module/module-2/pom.xml new file mode 100644 index 000000000000..08706a5a050f --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module/module-2/pom.xml @@ -0,0 +1,9 @@ + + 4.0.0 + + gid + aid + 1.0 + + module-2 + diff --git a/maven-core/src/test/resources-project-builder/profile-module/module-3/pom.xml b/maven-core/src/test/resources-project-builder/profile-module/module-3/pom.xml new file mode 100644 index 000000000000..5fb40a8a4961 --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module/module-3/pom.xml @@ -0,0 +1,9 @@ + + 4.0.0 + + gid + aid + 1.0 + + module-3 + diff --git a/maven-core/src/test/resources-project-builder/profile-module/module-4/pom.xml b/maven-core/src/test/resources-project-builder/profile-module/module-4/pom.xml new file mode 100644 index 000000000000..aae87db1d4cd --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module/module-4/pom.xml @@ -0,0 +1,9 @@ + + 4.0.0 + + gid + aid + 1.0 + + module-4 + diff --git a/maven-core/src/test/resources-project-builder/profile-module/module-5/pom.xml b/maven-core/src/test/resources-project-builder/profile-module/module-5/pom.xml new file mode 100644 index 000000000000..517e1505bcf9 --- /dev/null +++ b/maven-core/src/test/resources-project-builder/profile-module/module-5/pom.xml @@ -0,0 +1,9 @@ + + 4.0.0 + + gid + aid + 1.0 + + module-5 + diff --git a/maven-core/src/test/resources-project-builder/reporting-plugin-config/pom.xml b/maven-core/src/test/resources-project-builder/reporting-plugin-config/pom.xml index 4a5063805a00..25b7eef5899a 100644 --- a/maven-core/src/test/resources-project-builder/reporting-plugin-config/pom.xml +++ b/maven-core/src/test/resources-project-builder/reporting-plugin-config/pom.xml @@ -29,7 +29,7 @@ Test inheritance of reporting plugin configuration - child + sub diff --git a/maven-core/src/test/resources-project-builder/unc-path/pom.xml b/maven-core/src/test/resources-project-builder/unc-path/pom.xml index c1e63ba556b1..c1ce59926eb8 100644 --- a/maven-core/src/test/resources-project-builder/unc-path/pom.xml +++ b/maven-core/src/test/resources-project-builder/unc-path/pom.xml @@ -29,7 +29,7 @@ Test inheritance of UNC paths - child + sub diff --git a/maven-core/src/test/resources/consumer/trivial/pom.xml b/maven-core/src/test/resources/consumer/trivial/pom.xml index 69512db6c7a7..c739cc33b9d2 100644 --- a/maven-core/src/test/resources/consumer/trivial/pom.xml +++ b/maven-core/src/test/resources/consumer/trivial/pom.xml @@ -5,7 +5,7 @@ pom - child.xml + child diff --git a/maven-core/src/test/resources/projects/tree/consumer/pom.xml b/maven-core/src/test/resources/projects/tree/consumer/pom.xml index c52711230737..71597091b672 100644 --- a/maven-core/src/test/resources/projects/tree/consumer/pom.xml +++ b/maven-core/src/test/resources/projects/tree/consumer/pom.xml @@ -1,4 +1,4 @@ - + org.apache.maven.ut diff --git a/maven-core/src/test/resources/projects/tree/dep/pom.xml b/maven-core/src/test/resources/projects/tree/dep/pom.xml index 09ccb88d1d6b..93a3f45ef601 100644 --- a/maven-core/src/test/resources/projects/tree/dep/pom.xml +++ b/maven-core/src/test/resources/projects/tree/dep/pom.xml @@ -1,4 +1,4 @@ - + org.apache.maven.ut diff --git a/maven-core/src/test/resources/projects/tree/pom.xml b/maven-core/src/test/resources/projects/tree/pom.xml index 8534ac440ea3..98996f3a09d0 100644 --- a/maven-core/src/test/resources/projects/tree/pom.xml +++ b/maven-core/src/test/resources/projects/tree/pom.xml @@ -1,10 +1,10 @@ - + org.apache.maven.ut parent 1.0-SNAPSHOT pom - - dep - consumer - + + dep + consumer + diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java index da0da3b78a53..2e31d40c6321 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultTransformerContextBuilder.java @@ -201,7 +201,7 @@ public TransformerContext build() { } public FileModelSource getSource(String groupId, String artifactId) { - Set sources = mappedSources.get(groupId + ":" + artifactId); + Set sources = mappedSources.get(groupId != null ? groupId + ":" + artifactId : artifactId); if (sources == null) { return null; } @@ -218,5 +218,8 @@ public void putSource(String groupId, String artifactId, FileModelSource source) mappedSources .computeIfAbsent(groupId + ":" + artifactId, k -> new HashSet<>()) .add(source); + if (groupId != null) { + mappedSources.computeIfAbsent(artifactId, k -> new HashSet<>()).add(source); + } } } diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java index 9c0cc109c5a1..a68704c2a3b5 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblem.java @@ -18,8 +18,6 @@ */ package org.apache.maven.model.building; -import org.apache.maven.api.services.ModelBuilderResult; - /** * Describes a problem that was encountered during model building. A problem can either be an exception that was thrown * or a simple string message. In addition, a problem carries a hint about its source, e.g. the POM file that exhibits @@ -55,7 +53,6 @@ enum Version { * information that is available at the point the problem occurs and as such merely serves as best effort * to provide information to the user to track the problem back to its origin. * - * @see ModelBuilderResult#getModelIds() * @return The hint about the source of the problem or an empty string if unknown, never {@code null}. */ String getSource(); diff --git a/maven-model/src/main/java/org/apache/maven/model/BaseObject.java b/maven-model/src/main/java/org/apache/maven/model/BaseObject.java index 355453d39441..8691fff48040 100644 --- a/maven-model/src/main/java/org/apache/maven/model/BaseObject.java +++ b/maven-model/src/main/java/org/apache/maven/model/BaseObject.java @@ -20,6 +20,8 @@ import java.io.Serializable; +import static java.util.Objects.requireNonNull; + public abstract class BaseObject implements Serializable, Cloneable, InputLocationTracker { protected transient ChildrenTracking childrenTracking; @@ -28,12 +30,12 @@ public abstract class BaseObject implements Serializable, Cloneable, InputLocati public BaseObject() {} public BaseObject(Object delegate, BaseObject parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent != null ? parent::replace : null; } public BaseObject(Object delegate, ChildrenTracking parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent; } diff --git a/maven-repository-metadata/src/main/java/org/apache/maven/artifact/repository/metadata/BaseObject.java b/maven-repository-metadata/src/main/java/org/apache/maven/artifact/repository/metadata/BaseObject.java index c206298e2bc4..8c6f01f238f5 100644 --- a/maven-repository-metadata/src/main/java/org/apache/maven/artifact/repository/metadata/BaseObject.java +++ b/maven-repository-metadata/src/main/java/org/apache/maven/artifact/repository/metadata/BaseObject.java @@ -20,6 +20,8 @@ import java.io.Serializable; +import static java.util.Objects.requireNonNull; + public abstract class BaseObject implements Serializable, Cloneable { protected transient ChildrenTracking childrenTracking; @@ -28,12 +30,12 @@ public abstract class BaseObject implements Serializable, Cloneable { public BaseObject() {} public BaseObject(Object delegate, BaseObject parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent != null ? parent::replace : null; } public BaseObject(Object delegate, ChildrenTracking parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent; } diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java index 50daaf95fff2..754bbc236872 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java @@ -32,7 +32,6 @@ import org.apache.maven.internal.impl.DefaultPluginConfigurationExpander; import org.apache.maven.internal.impl.DefaultSuperPomProvider; import org.apache.maven.internal.impl.DefaultUrlNormalizer; -import org.apache.maven.internal.impl.model.BuildModelTransformer; import org.apache.maven.internal.impl.model.DefaultDependencyManagementImporter; import org.apache.maven.internal.impl.model.DefaultDependencyManagementInjector; import org.apache.maven.internal.impl.model.DefaultInheritanceAssembler; @@ -989,9 +988,7 @@ public final ArtifactDescriptorReader getArtifactDescriptorReader() { protected ArtifactDescriptorReader createArtifactDescriptorReader() { // from maven-resolver-provider return new DefaultArtifactDescriptorReader( - getRemoteRepositoryManager(), getVersionResolver(), - getVersionRangeResolver(), getArtifactResolver(), getModelBuilder(), getRepositoryEventDispatcher(), @@ -1058,13 +1055,12 @@ protected ModelBuilder createModelBuilder() { new DefaultPluginManagementInjector(), new DefaultDependencyManagementInjector(), new DefaultDependencyManagementImporter(), - (m, r, b) -> m, new DefaultPluginConfigurationExpander(), new ProfileActivationFilePathInterpolator(new DefaultPathTranslator(), new DefaultRootLocator()), - new BuildModelTransformer(), new DefaultModelVersionParser(getVersionScheme()), List.of(), - new DefaultModelCacheFactory()); + new DefaultModelCacheFactory(), + new org.apache.maven.internal.impl.resolver.DefaultModelResolver()); } private RepositorySystem repositorySystem; diff --git a/maven-settings/src/main/java/org/apache/maven/settings/BaseObject.java b/maven-settings/src/main/java/org/apache/maven/settings/BaseObject.java index 7b66110a8bef..b656ff7b42db 100644 --- a/maven-settings/src/main/java/org/apache/maven/settings/BaseObject.java +++ b/maven-settings/src/main/java/org/apache/maven/settings/BaseObject.java @@ -20,6 +20,8 @@ import java.io.Serializable; +import static java.util.Objects.requireNonNull; + public abstract class BaseObject implements Serializable, Cloneable { protected transient ChildrenTracking childrenTracking; @@ -28,12 +30,12 @@ public abstract class BaseObject implements Serializable, Cloneable { public BaseObject() {} public BaseObject(Object delegate, BaseObject parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent != null ? parent::replace : null; } public BaseObject(Object delegate, ChildrenTracking parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent; } diff --git a/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/BaseObject.java b/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/BaseObject.java index 28bea64b8ea7..3a844a77c928 100644 --- a/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/BaseObject.java +++ b/maven-toolchain-model/src/main/java/org/apache/maven/toolchain/model/BaseObject.java @@ -20,6 +20,8 @@ import java.io.Serializable; +import static java.util.Objects.requireNonNull; + public abstract class BaseObject implements Serializable, Cloneable { protected transient ChildrenTracking childrenTracking; @@ -28,12 +30,12 @@ public abstract class BaseObject implements Serializable, Cloneable { public BaseObject() {} public BaseObject(Object delegate, BaseObject parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent != null ? parent::replace : null; } public BaseObject(Object delegate, ChildrenTracking parent) { - this.delegate = delegate; + this.delegate = requireNonNull(delegate, "delegate cannot be null"); this.childrenTracking = parent; } diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index 4aae75769282..03f197d9004a 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -33,12 +33,12 @@ under the License. | 6. | `maven.installation.extensions` | `String` | Maven installation extensions. | `${maven.installation.conf}/extensions.xml` | 4.0.0 | User properties | | 7. | `maven.installation.settings` | `String` | Maven installation settings. | `${maven.installation.conf}/settings.xml` | 4.0.0 | User properties | | 8. | `maven.installation.toolchains` | `String` | Maven installation toolchains. | `${maven.installation.conf}/toolchains.xml` | 4.0.0 | User properties | -| 9. | `maven.plugin.validation` | `String` | Plugin validation level. | `inline` | 3.9.2 | User properties | -| 10. | `maven.plugin.validation.excludes` | `String` | Plugin validation exclusions. | - | 3.9.6 | User properties | -| 11. | `maven.project.conf` | `String` | Maven project configuration directory. | `${session.rootDirectory}/.mvn` | 4.0.0 | User properties | -| 12. | `maven.project.extensions` | `String` | Maven project extensions. | `${maven.project.conf}/extensions.xml` | 4.0.0 | User properties | -| 13. | `maven.project.settings` | `String` | Maven project settings. | `${maven.project.conf}/settings.xml` | 4.0.0 | User properties | -| 14. | `maven.projectBuilder.parallelism` | `Integer` | ProjectBuilder parallelism. | `cores/2 + 1` | 4.0.0 | User properties | +| 9. | `maven.modelBuilder.parallelism` | `Integer` | ProjectBuilder parallelism. | `cores/2 + 1` | 4.0.0 | User properties | +| 10. | `maven.plugin.validation` | `String` | Plugin validation level. | `inline` | 3.9.2 | User properties | +| 11. | `maven.plugin.validation.excludes` | `String` | Plugin validation exclusions. | - | 3.9.6 | User properties | +| 12. | `maven.project.conf` | `String` | Maven project configuration directory. | `${session.rootDirectory}/.mvn` | 4.0.0 | User properties | +| 13. | `maven.project.extensions` | `String` | Maven project extensions. | `${maven.project.conf}/extensions.xml` | 4.0.0 | User properties | +| 14. | `maven.project.settings` | `String` | Maven project settings. | `${maven.project.conf}/settings.xml` | 4.0.0 | User properties | | 15. | `maven.relocations.entries` | `String` | User controlled relocations. This property is a comma separated list of entries with the syntax GAV>GAV. The first GAV can contain \* for any elem (so \*:\*:\* would mean ALL, something you don't want). The second GAV is either fully specified, or also can contain \*, then it behaves as "ordinary relocation": the coordinate is preserved from relocated artifact. Finally, if right hand GAV is absent (line looks like GAV>), the left hand matching GAV is banned fully (from resolving).
Note: the > means project level, while >> means global (whole session level, so even plugins will get relocated artifacts) relocation.
For example,

maven.relocations.entries = org.foo:\*:\*>, \\
org.here:\*:\*>org.there:\*:\*, \\
javax.inject:javax.inject:1>>jakarta.inject:jakarta.inject:1.0.5
means: 3 entries, ban org.foo group (exactly, so org.foo.bar is allowed), relocate org.here to org.there and finally globally relocate (see >> above) javax.inject:javax.inject:1 to jakarta.inject:jakarta.inject:1.0.5. | - | 4.0.0 | User properties | | 16. | `maven.repo.central` | `String` | Maven central repository URL. The property will have the value of the MAVEN_REPO_CENTRAL environment variable if it is defined. | `https://repo.maven.apache.org/maven2` | 4.0.0 | User properties | | 17. | `maven.repo.local` | `String` | Maven local repository. | `${maven.user.conf}/repository` | 3.0.0 | User properties |