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 | 278 +- .../api/services/ModelBuilderResult.java | 56 +- .../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 | 2665 +++++++++-------- .../impl/model/DefaultModelBuilderResult.java | 193 +- .../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 | 807 +---- .../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, 2787 insertions(+), 3942 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 0967ba92b2..7303e8e61c 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 @@ import org.apache.maven.api.annotations.Nonnull; @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 @@ public interface Artifact { } /** - * {@return the group identifier of the artifact}. + * {@return the group identifier of the artifact} * * @see ArtifactCoordinates#getGroupId() */ @@ -66,7 +66,7 @@ public interface Artifact { String getGroupId(); /** - * {@return the identifier of the artifact}. + * {@return the identifier of the artifact} * * @see ArtifactCoordinates#getArtifactId() */ @@ -74,7 +74,8 @@ public interface Artifact { 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 @@ public interface Artifact { 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 @@ public interface Artifact { 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 de97967964..dab32ee48c 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 @@ import org.apache.maven.api.annotations.Nonnull; @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 3006de198a..00997bd445 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 5436a3e143..50fbb60353 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 @@ import org.apache.maven.api.annotations.Nonnull; @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 13999eaedf..a7330bcbb7 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 @@ import org.apache.maven.api.annotations.Nullable; @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 4672f6b49f..368d14443c 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 interface PathType { 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 47342ce8a7..219e21f246 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 @@ public interface Project { /** * 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 16a55a8055..f2968295e4 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 @@ import org.apache.maven.api.annotations.Provider; @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 c8f125eb7e..e2ab1c2a9e 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 dc502aeeb9..4362353185 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 0448904665..13d5176609 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 class ModelBuilderException extends MavenException { * @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 a72dd73049..ccd39248b1 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 @@ import static org.apache.maven.api.services.BaseRequest.nonNull; * 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 @@ import static org.apache.maven.api.services.BaseRequest.nonNull; 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; + 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 2.0. This validation level is meant as a compatibility mode to allow - * users to migrate their projects. + * The possible merge modes for combining remote repositories. */ - int VALIDATION_LEVEL_MAVEN_2_0 = 20; + enum RepositoryMerging { - /** - * Denotes validation as performed by Maven 3.0. This validation level is meant for existing projects. - */ - int VALIDATION_LEVEL_MAVEN_3_0 = 30; + /** + * The repositories declared in the POM have precedence over the repositories specified in the request. + */ + POM_DOMINANT, - /** - * 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; + /** + * 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,23 +124,14 @@ public interface ModelBuilderRequest { Map getUserProperties(); @Nonnull - ModelResolver getModelResolver(); - - @Nonnull - ModelRepositoryHolder getModelRepositoryHolder(); - - @Nullable - Object getListener(); - - @Nullable - ModelBuilderResult getInterimResult(); - - @Nullable - ModelTransformerContextBuilder getTransformerContextBuilder(); + RepositoryMerging getRepositoryMerging(); @Nullable List getRepositories(); + @Nullable + ModelTransformer getLifecycleBindingsInjector(); + @Nonnull static ModelBuilderRequest build(@Nonnull ModelBuilderRequest request, @Nonnull ModelSource source) { return builder(nonNull(request, "request cannot be null")) @@ -194,45 +168,35 @@ public interface ModelBuilderRequest { @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 interface ModelBuilderRequest { 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,21 +214,16 @@ public interface ModelBuilderRequest { return this; } + public ModelBuilderRequestBuilder recursive(boolean recursive) { + this.recursive = recursive; + return this; + } + public ModelBuilderRequestBuilder source(ModelSource source) { this.source = source; return this; } - public ModelBuilderRequestBuilder projectBuild(boolean projectBuild) { - this.projectBuild = projectBuild; - return this; - } - - public ModelBuilderRequestBuilder processPlugins(boolean processPlugins) { - this.processPlugins = processPlugins; - return this; - } - public ModelBuilderRequestBuilder profiles(List profiles) { this.profiles = profiles; return this; @@ -295,29 +249,8 @@ public interface ModelBuilderRequest { 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; - return this; - } - - public ModelBuilderRequestBuilder transformerContextBuilder( - ModelTransformerContextBuilder transformerContextBuilder) { - this.transformerContextBuilder = transformerContextBuilder; + public ModelBuilderRequestBuilder repositoryMerging(RepositoryMerging repositoryMerging) { + this.repositoryMerging = repositoryMerging; return this; } @@ -326,96 +259,76 @@ public interface ModelBuilderRequest { return this; } + 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; - } - - @Override - public boolean isTwoPhaseBuilding() { - return twoPhaseBuilding; + public RequestType getRequestType() { + return requestType; } @Override @@ -423,21 +336,17 @@ public interface ModelBuilderRequest { return locationTracking; } + @Override + public boolean isRecursive() { + return recursive; + } + @Nonnull @Override public ModelSource getSource() { return source; } - public boolean isProjectBuild() { - return projectBuild; - } - - @Override - public boolean isProcessPlugins() { - return processPlugins; - } - @Override public Collection getProfiles() { return profiles; @@ -464,32 +373,19 @@ public interface ModelBuilderRequest { } @Override - public ModelResolver getModelResolver() { - return modelResolver; - } - - @Override - public ModelRepositoryHolder getModelRepositoryHolder() { - return modelRepositoryHolder; - } - - public Object getListener() { - return listener; - } - - @Override - public ModelBuilderResult getInterimResult() { - return interimResult; - } - - public ModelTransformerContextBuilder getTransformerContextBuilder() { - return transformerContextBuilder; + public RepositoryMerging getRepositoryMerging() { + return repositoryMerging; } @Override public List getRepositories() { return repositories; } + + @Override + 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 4033aa2371..bf98e93391 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 @@ import org.apache.maven.api.model.Profile; 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. * @@ -69,6 +57,14 @@ public interface ModelBuilderResult { @Nonnull Model getRawModel(); + /** + * Gets the effective model of the parent POM. + * + * @return the effective model of the parent POM, never {@code null} + */ + @Nonnull + Model getParentModel(); + /** * Gets the assembled model with inheritance, interpolation and profile injection. * @@ -78,28 +74,12 @@ public interface ModelBuilderResult { Model getEffectiveModel(); /** - * 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 profiles that were active during model building. * - * @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 active profiles of the model or an empty list if the model has no active profiles. */ @Nonnull - Optional getRawModel(@Nonnull String modelId); - - /** - * 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()}. - * - * @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. - */ - @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 eda871cb9c..f074182881 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 @@ public interface ModelProblem extends BuilderProblem { * 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 06d68b09d2..9ef9aa861e 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 @@ package org.apache.maven.api.services; 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 @@ public interface ModelProblemCollector { 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 3be93256be..0000000000 --- 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 0c17ae41bc..5d2503ec31 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 a6e31720fa..0000000000 --- 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 9efb6e63ee..0000000000 --- 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 c68456beb4..0000000000 --- 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 d37982993f..525b353c6a 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.Experimental; 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 f2d8bf8619..0000000000 --- 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 a0c2bfb4a7..0000000000 --- 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 9bec734917..b3a0e2332e 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.Nonnull; 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 167e67070a..0c9f829210 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 de45f9d5a5..40a8e813de 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 @@ import org.apache.maven.api.services.ModelProblemCollector; * */ 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 @@ public interface ModelValidator { * 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 505dbbe12a..7f7d177a80 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 @@ package org.apache.maven.api.services.model; 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 @@ import org.apache.maven.api.annotations.Nullable; * 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 4dd18968d5..0000000000 --- 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 ece39133d3..b92ec7b8ee 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 @@ public abstract class AbstractSession implements InternalSession { 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 e6d130de42..39bf825174 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 @@ abstract class AetherDependencyWrapper { } /** - * {@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 @@ abstract class AetherDependencyWrapper { } /** - * {@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 @@ abstract class AetherDependencyWrapper { } /** - * {@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 @@ abstract class AetherDependencyWrapper { } /** - * {@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 @@ abstract class AetherDependencyWrapper { } /** - * {@return the scope (compile, test, …) of this dependency}. + * {@return the scope (compile, test, …) of this dependency} */ @Nonnull public DependencyScope getScope() { @@ -117,7 +117,7 @@ abstract class AetherDependencyWrapper { } /** - * {@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 d8e9e941dd..1b3cf9b007 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 @@ class 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 @@ class PathModularization { } /** - * {@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 0d0d43c32f..2fb4ad3d33 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 @@ package org.apache.maven.internal.impl; 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 @@ class Utils { } 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 b879649dfe..0000000000 --- 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 43d75e184b..5c4040aa4e 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.LinkedHashMap; 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.model.Reporting; 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 @@ public class DefaultInheritanceAssembler implements InheritanceAssembler { Object childDirectory = context.get(CHILD_DIRECTORY); Object childPathAdjustment = context.get(CHILD_PATH_ADJUSTMENT); - boolean isBlankParentUrl = true; - - if (parentUrl != null) { - for (int i = 0; i < parentUrl.length(); i++) { - if (!Character.isWhitespace(parentUrl.charAt(i))) { - isBlankParentUrl = false; - } - } - } - - if (isBlankParentUrl || childDirectory == null || childPathAdjustment == null || !appendPath) { + if (parentUrl == null + || parentUrl.isBlank() + || childDirectory == null + || childPathAdjustment == null + || !appendPath) { return parentUrl; } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java index a7a578659c..887d716d99 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java @@ -18,6 +18,7 @@ */ package org.apache.maven.internal.impl.model; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; @@ -25,8 +26,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; @@ -34,64 +37,62 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Properties; -import java.util.concurrent.Callable; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.maven.api.Constants; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; import org.apache.maven.api.SessionData; import org.apache.maven.api.Type; import org.apache.maven.api.VersionRange; -import org.apache.maven.api.annotations.Nullable; 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.Activation; import org.apache.maven.api.model.ActivationFile; -import org.apache.maven.api.model.Build; import org.apache.maven.api.model.Dependency; import org.apache.maven.api.model.DependencyManagement; import org.apache.maven.api.model.Exclusion; import org.apache.maven.api.model.InputLocation; -import org.apache.maven.api.model.InputLocationTracker; import org.apache.maven.api.model.InputSource; 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.PluginManagement; import org.apache.maven.api.model.Profile; +import org.apache.maven.api.model.Repository; +import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem.Severity; +import org.apache.maven.api.services.MavenException; 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.ModelProblemCollector; -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.ModelTransformer; -import org.apache.maven.api.services.ModelTransformerContext; -import org.apache.maven.api.services.ModelTransformerContextBuilder; -import org.apache.maven.api.services.ModelTransformerException; +import org.apache.maven.api.services.RepositoryFactory; import org.apache.maven.api.services.Source; import org.apache.maven.api.services.SuperPomProvider; import org.apache.maven.api.services.VersionParserException; import org.apache.maven.api.services.model.DependencyManagementImporter; import org.apache.maven.api.services.model.DependencyManagementInjector; import org.apache.maven.api.services.model.InheritanceAssembler; -import org.apache.maven.api.services.model.LifecycleBindingsInjector; -import org.apache.maven.api.services.model.ModelBuildingEvent; -import org.apache.maven.api.services.model.ModelBuildingListener; import org.apache.maven.api.services.model.ModelCache; import org.apache.maven.api.services.model.ModelCacheFactory; import org.apache.maven.api.services.model.ModelInterpolator; 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.ModelResolverException; import org.apache.maven.api.services.model.ModelUrlNormalizer; import org.apache.maven.api.services.model.ModelValidator; import org.apache.maven.api.services.model.ModelVersionParser; @@ -100,11 +101,12 @@ import org.apache.maven.api.services.model.PluginManagementInjector; 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.services.model.WorkspaceModelResolver; +import org.apache.maven.api.services.model.RootLocator; import org.apache.maven.api.services.xml.XmlReaderException; import org.apache.maven.api.services.xml.XmlReaderRequest; -import org.apache.maven.internal.impl.resolver.DefaultModelRepositoryHolder; -import org.apache.maven.internal.impl.resolver.DefaultModelResolver; +import org.apache.maven.api.spi.ModelParserException; +import org.apache.maven.api.spi.ModelTransformer; +import org.apache.maven.internal.impl.util.PhasingExecutor; import org.apache.maven.model.v4.MavenTransformer; import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.interpolation.Interpolator; @@ -115,6 +117,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** + * The model builder is responsible for building the {@link Model} from the POM file. + * There are two ways to main use cases: the first one is to build the model from a POM file + * on the file system in order to actually build the project. The second one is to build the + * model for a dependency or an external parent. */ @Named @Singleton @@ -141,13 +147,12 @@ public class DefaultModelBuilder implements ModelBuilder { private final PluginManagementInjector pluginManagementInjector; private final DependencyManagementInjector dependencyManagementInjector; private final DependencyManagementImporter dependencyManagementImporter; - private final LifecycleBindingsInjector lifecycleBindingsInjector; private final PluginConfigurationExpander pluginConfigurationExpander; private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator; - private final ModelTransformer transformer; private final ModelVersionParser versionParser; - private final List transformers; + private final List transformers; private final ModelCacheFactory modelCacheFactory; + private final ModelResolver modelResolver; @SuppressWarnings("checkstyle:ParameterNumber") @Inject @@ -165,13 +170,12 @@ public class DefaultModelBuilder implements ModelBuilder { 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 class DefaultModelBuilder implements ModelBuilder { 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; - private static ModelBuilderRequest fillRequestDefaults(ModelBuilderRequest request) { - ModelBuilderRequest.ModelBuilderRequestBuilder builder = ModelBuilderRequest.builder(request); - if (request.getModelRepositoryHolder() == null) { - builder.modelRepositoryHolder(new DefaultModelRepositoryHolder( + String source; + Model sourceModel; + Model rootModel; + + List pomRepositories; + List externalRepositories; + List repositories; + + DefaultModelBuilderSession(ModelBuilderRequest request) { + this( request.getSession(), - DefaultModelRepositoryHolder.RepositoryMerging.POM_DOMINANT, - request.getSession().getRemoteRepositories())); - } - if (request.getModelResolver() == null) { - builder.modelResolver(new DefaultModelResolver()); - } - return builder.build(); - } - - protected ModelBuilderResult build(ModelBuilderRequest request, Collection importIds) - throws ModelBuilderException { - // phase 1 - DefaultModelBuilderResult result = new DefaultModelBuilderResult(); - - DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result); - - // read and validate raw model - Model fileModel = readFileModel(request, problems); - result.setFileModel(fileModel); - - Model activatedFileModel = activateFileModel(fileModel, request, result, problems); - result.setActivatedFileModel(activatedFileModel); - - if (!request.isTwoPhaseBuilding()) { - return build(request, result, importIds); - } else if (hasModelErrors(problems)) { - throw problems.newModelBuilderException(); + request, + new DefaultModelBuilderResult(), + request.getSession() + .getData() + .computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance), + new Graph(), + new ConcurrentHashMap<>(64), + List.of(), + repos(request), + repos(request)); } - return result; - } + static List repos(ModelBuilderRequest request) { + return List.copyOf( + request.getRepositories() != null + ? request.getRepositories() + : request.getSession().getRemoteRepositories()); + } - private Model activateFileModel( - Model inputModel, - ModelBuilderRequest request, - DefaultModelBuilderResult result, - DefaultModelProblemCollector problems) - throws ModelBuilderException { - problems.setRootModel(inputModel); + @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()); + } - // profile activation - DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel); + DefaultModelBuilderSession derive(ModelSource source) { + return derive(source, new DefaultModelBuilderResult(result)); + } - problems.setSource("(external profiles)"); - List activeExternalProfiles = - profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, problems); + DefaultModelBuilderSession derive(ModelSource source, DefaultModelBuilderResult result) { + return derive(ModelBuilderRequest.build(request, source), result); + } - result.setActiveExternalProfiles(activeExternalProfiles); + /** + * Creates a new session, sharing cached datas and propagating errors. + */ + DefaultModelBuilderSession derive(ModelBuilderRequest request) { + return derive(request, new DefaultModelBuilderResult(result)); + } - if (!activeExternalProfiles.isEmpty()) { - Properties profileProps = new Properties(); - for (Profile profile : activeExternalProfiles) { - profileProps.putAll(profile.getProperties()); + DefaultModelBuilderSession derive(ModelBuilderRequest request, DefaultModelBuilderResult result) { + if (session != request.getSession()) { + throw new IllegalArgumentException("Session mismatch"); } - profileProps.putAll(profileActivationContext.getUserProperties()); - profileActivationContext.setUserProperties(profileProps); + return new DefaultModelBuilderSession( + session, + request, + result, + cache, + dag, + mappedSources, + pomRepositories, + externalRepositories, + repositories); } - profileActivationContext.setProjectProperties(inputModel.getProperties()); - problems.setSource(inputModel); - List activePomProfiles = - profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, problems); - - // model normalization - problems.setSource(inputModel); - inputModel = modelNormalizer.mergeDuplicates(inputModel, request, problems); - - Map interpolatedActivations = getProfileActivations(inputModel); - inputModel = injectProfileActivations(inputModel, interpolatedActivations); - - // profile injection - inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, problems); - inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, problems); - - return inputModel; - } - - @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(); + @Override + public String toString() { + return "ModelBuilderSession[" + "session=" + + session + ", " + "request=" + + request + ", " + "result=" + + result + ", " + "cache=" + + cache + ']'; } - inputModel = activateFileModel(inputModel, request, result, problems); - - problems.setRootModel(inputModel); - - 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; + PhasingExecutor createExecutor() { + return new PhasingExecutor(Executors.newFixedThreadPool(getParallelism())); } - ModelData superData = new ModelData(null, getSuperModel(superModelVersion)); - // 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()); + 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 } - profileProps.putAll(profileActivationContext.getUserProperties()); - profileActivationContext.setUserProperties(profileProps); + return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors())); } - Collection parentIds = new LinkedHashSet<>(); + 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; + } - List lineage = new ArrayList<>(); + 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; + } - for (ModelData currentData = resultData; ; ) { - String modelId = currentData.id(); - result.addModelId(modelId); + /** + * 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; + } + } - Model model = currentData.model(); - result.setRawModel(modelId, model); - problems.setSource(model); + 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 - model = modelNormalizer.mergeDuplicates(model, request, problems); + setSource(inputModel); + inputModel = modelNormalizer.mergeDuplicates(inputModel, request, this); + + Map interpolatedActivations = getProfileActivations(inputModel); + inputModel = injectProfileActivations(inputModel, interpolatedActivations); + + // 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, problems); + interpolateActivations(model.getProfiles(), profileActivationContext, this); // 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); - } + profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this); + result.setActivePomProfiles(activePomProfiles); + model = profileInjector.injectProfiles(model, activePomProfiles, request, this); + model = profileInjector.injectProfiles(model, activeExternalProfiles, request, this); - lineage.add(model); + // model interpolation + Model resultModel = model; + resultModel = interpolateModel(resultModel, request, this); - if (currentData == superData) { - break; - } + // url normalization + resultModel = modelUrlNormalizer.normalize(resultModel, request); - // 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(); + // 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("Merging repositories from " + model.getId() + "\n" + logger.debug("Replacing repositories from " + resultModel.getId() + "\n" + newRepos.stream().map(s -> " " + s).collect(Collectors.joining("\n"))); } } - // 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); + return resultModel; + } - 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(" -> "); + 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(); } - message.append(parentData.id()); + 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; + } - problems.add(Severity.FATAL, ModelProblem.Version.BASE, message.toString()); + add( + Severity.ERROR, + ModelProblem.Version.V20, + "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(), + e); + } - throw problems.newModelBuilderException(); - } else { - currentData = parentData; + 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(); } - } - Model tmpModel = lineage.get(0); - - // inject interpolated activations - List interpolated = interpolateActivations(tmpModel.getProfiles(), profileActivationContext, problems); - if (interpolated != tmpModel.getProfiles()) { - tmpModel = tmpModel.withProfiles(interpolated); - } - - // inject external profile into current model - tmpModel = profileInjector.injectProfiles(tmpModel, activeExternalProfiles, request, problems); - - lineage.set(0, tmpModel); - - checkPluginVersions(lineage, request, problems); - - // inheritance assembly - Model resultModel = assembleInheritance(lineage, request, problems); - - // consider caching inherited model - - problems.setSource(resultModel); - problems.setRootModel(resultModel); - - // model interpolation - resultModel = interpolateModel(resultModel, request, problems); - - // url normalization - resultModel = modelUrlNormalizer.normalize(resultModel, request); - - result.setEffectiveModel(resultModel); - - // 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"))); + if (model.getModelVersion() == null) { + String namespace = model.getNamespaceUri(); + if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) { + model = model.withModelVersion(namespace.substring(NAMESPACE_PREFIX.length())); + } } + + 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()); + } + } + } + } + } + + // 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); + } + } + } + + for (var transformer : transformers) { + model = transformer.transformFileModel(model); + } + + 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(); + } + + return model; } - return resultModel; + 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; + } + + private Model doReadRawModel() throws ModelBuilderException { + ModelBuilderRequest request = this.request; + Model rawModel = readFileModel(); + + if (!MODEL_VERSION_4_0_0.equals(rawModel.getModelVersion()) + && request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { + rawModel = transformFileToRaw(rawModel); + } + + for (var transformer : transformers) { + rawModel = transformer.transformRawModel(rawModel); + } + + modelValidator.validateRawModel( + rawModel, + request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM + ? ModelValidator.VALIDATION_LEVEL_STRICT + : ModelValidator.VALIDATION_LEVEL_MINIMAL, + request, + this); + + if (hasFatalErrors()) { + throw newModelBuilderException(); + } + + return rawModel; + } + + /** + * Reads the request source's parent. + */ + Model readAsParentModel() { + return cache(request.getSource(), PARENT, this::doReadAsParentModel); + } + + 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); + + return parent.withParent(null); + } + + private Model importDependencyManagement(Model model, 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(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, 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; + } + + 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); + add(Severity.ERROR, ModelProblem.Version.BASE, message.toString()); + return null; + } + + 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(); + } + + // [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; + } + + @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()); + + 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 (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("")); + } + } + + 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; + } + + importResult.getProblems().forEach(this::add); + + importModel = importResult.getEffectiveModel(); + + return importModel; + } + + 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; + } + + private T cache(String groupId, String artifactId, String version, String tag, Supplier supplier) { + return cache.computeIfAbsent(groupId, artifactId, version, tag, supplier); + } + + private T cache(Source source, String tag, Supplier supplier) { + return cache.computeIfAbsent(source, tag, supplier); + } + } + + @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 @@ public class DefaultModelBuilder implements ModelBuilder { 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,357 +1764,19 @@ public class DefaultModelBuilder implements ModelBuilder { 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(); + DefaultModelBuilderSession build = new DefaultModelBuilderSession(request); + Model model = build.readRawModel(); + if (((ModelProblemCollector) build).hasErrors()) { + throw build.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); - - if (hasFatalErrors(problems)) { - throw problems.newModelBuilderException(); - } - - return new ModelData(modelSource, rawModel); - } - static String getGroupId(Model model) { String groupId = model.getGroupId(); if (groupId == null && model.getParent() != null) { @@ -878,7 +1785,7 @@ public class DefaultModelBuilder implements ModelBuilder { 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 @@ public class DefaultModelBuilder implements 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 @@ public class DefaultModelBuilder implements ModelBuilder { 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 @@ public class DefaultModelBuilder implements ModelBuilder { } } - 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 @@ public class DefaultModelBuilder implements ModelBuilder { 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 @@ public class DefaultModelBuilder implements ModelBuilder { && (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 e10a7e8642..f834577a59 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,47 +29,38 @@ import org.apache.maven.api.model.Model; 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<>(); + this(null); } - DefaultModelBuilderResult(ModelBuilderResult result) { - this(); - this.activeExternalProfiles.addAll(result.getActiveExternalProfiles()); - this.effectiveModel = result.getEffectiveModel(); - this.fileModel = result.getFileModel(); - this.problems.addAll(result.getProblems()); + DefaultModelBuilderResult(DefaultModelBuilderResult problemHolder) { + this.problemHolder = problemHolder; + } - for (String modelId : result.getModelIds()) { - this.modelIds.add(modelId); - this.rawModels.put(modelId, result.getRawModel(modelId).orElseThrow()); - this.activePomProfiles.put(modelId, result.getActivePomProfiles(modelId)); - } + public ModelSource getSource() { + return source; + } + + public void setSource(ModelSource source) { + this.source = source; } @Override @@ -79,18 +68,26 @@ class DefaultModelBuilderResult implements ModelBuilderResult { return fileModel; } - public DefaultModelBuilderResult setFileModel(Model fileModel) { + public void setFileModel(Model fileModel) { this.fileModel = fileModel; - return this; } - public Model getActivatedFileModel() { - return activatedFileModel; + @Override + public Model getRawModel() { + return rawModel; } - public DefaultModelBuilderResult setActivatedFileModel(Model activatedFileModel) { - this.activatedFileModel = activatedFileModel; - return this; + public void setRawModel(Model rawModel) { + this.rawModel = rawModel; + } + + @Override + public Model getParentModel() { + return parentModel; + } + + public void setParentModel(Model parentModel) { + this.parentModel = parentModel; } @Override @@ -98,61 +95,17 @@ class DefaultModelBuilderResult implements ModelBuilderResult { return effectiveModel; } - public DefaultModelBuilderResult setEffectiveModel(Model model) { + public void setEffectiveModel(Model model) { this.effectiveModel = model; - return this; } @Override - public List getModelIds() { - return modelIds; + public List getActivePomProfiles() { + return activePomProfiles; } - 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; - } - - @Override - public Model getRawModel() { - return rawModels.get(modelIds.get(0)); - } - - @Override - public Optional getRawModel(String modelId) { - return Optional.ofNullable(rawModels.get(modelId)); - } - - 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); - - return this; - } - - @Override - public List getActivePomProfiles(String modelId) { - List profiles = activePomProfiles.get(modelId); - return profiles != null ? profiles : List.of(); - } - - 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 @@ class DefaultModelBuilderResult implements ModelBuilderResult { return activeExternalProfiles; } - public DefaultModelBuilderResult setActiveExternalProfiles(List activeProfiles) { - if (activeProfiles != null) { - this.activeExternalProfiles = new ArrayList<>(activeProfiles); - } else { - this.activeExternalProfiles.clear(); - } + public void setActiveExternalProfiles(List activeProfiles) { + this.activeExternalProfiles = activeProfiles; + } - return this; + /** + * 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 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); + } } @Override - public List getProblems() { - return problems; - } - - public DefaultModelBuilderResult setProblems(List problems) { - if (problems != null) { - this.problems = new ArrayList<>(problems); - } else { - this.problems.clear(); - } - - return this; + 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 @@ class DefaultModelBuilderResult implements ModelBuilderResult { 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 @@ class DefaultModelBuilderResult implements ModelBuilderResult { } 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 db76a06866..0000000000 --- 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 53262e4b8a..ea2eee3ead 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 @@ public class DefaultModelInterpolator implements ModelInterpolator { } 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 @@ public class DefaultModelInterpolator implements ModelInterpolator { 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 f30bffddf2..0787b0eec1 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 class DefaultModelProblem implements ModelProblem { 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 8df0190522..0000000000 --- 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 28c61d962a..0000000000 --- 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 76d94a6d31..0000000000 --- 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 07c26da3c9..65545d1b8e 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.model.Resource; 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 class DefaultModelValidator implements ModelValidator { @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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { } } - 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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { } @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 @@ public class DefaultModelValidator implements ModelValidator { 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 @@ public class DefaultModelValidator implements ModelValidator { @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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { "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 class DefaultModelValidator implements ModelValidator { 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 class DefaultModelValidator implements ModelValidator { } 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 class DefaultModelValidator implements ModelValidator { } 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 @@ public class DefaultModelValidator implements ModelValidator { 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 @@ public class DefaultModelValidator implements ModelValidator { } } 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 @@ public class DefaultModelValidator implements ModelValidator { 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 @@ public class DefaultModelValidator implements ModelValidator { } 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 @@ public class DefaultModelValidator implements ModelValidator { Dependency d, boolean management, String prefix, + int validationLevel, ModelBuilderRequest request) { validateCoordinatesId( prefix, @@ -1194,9 +1283,9 @@ public class DefaultModelValidator implements ModelValidator { 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 @@ public class DefaultModelValidator implements ModelValidator { List repositories, String prefix, String prefix2, + int validationLevel, ModelBuilderRequest request) { Map index = new HashMap<>(); @@ -1292,7 +1382,7 @@ public class DefaultModelValidator implements ModelValidator { 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 @@ public class DefaultModelValidator implements ModelValidator { } 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 @@ public class DefaultModelValidator implements ModelValidator { } 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 @@ public class DefaultModelValidator implements ModelValidator { 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 @@ public class DefaultModelValidator implements ModelValidator { 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 e50db13f24..a5a3649018 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.io.InputStream; 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 ffa8c10a93..ef0bf75f34 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 3c16120b20..82bae431cf 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.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.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.RequestTrace; 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 @@ import org.slf4j.LoggerFactory; @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 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader .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 155825408b..0000000000 --- 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 4b66104f3d..1eaf9f2c8f 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.Files; 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.DownloadedArtifact; 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 @@ import org.apache.maven.api.services.VersionRangeResolverException; @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 class DefaultModelResolver implements ModelResolver { && 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 class DefaultModelResolver implements ModelResolver { 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 class DefaultModelResolver implements ModelResolver { 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 class DefaultModelResolver implements ModelResolver { } } - 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 c27f5ce5b3..85d4ed5976 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 class PhasingExecutor implements Executor, AutoCloseable { }); } - 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 51daf7079a..42f3686382 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.RemoteRepository; 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 @@ class DefaultModelResolverTest { 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 @@ class DefaultModelResolverTest { 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 @@ class DefaultModelResolverTest { 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 @@ class DefaultModelResolverTest { 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 15a605ea20..05a3cfaa18 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 class ApiRunner { @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 558dea7f72..09eb857e85 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.DefaultModelXmlFactory; 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.DefaultProfileSelector; 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 class RepositorySystemSupplier implements Supplier { protected ArtifactDescriptorReader createArtifactDescriptorReader() { // from maven-resolver-provider return new DefaultArtifactDescriptorReader( - getRemoteRepositoryManager(), getVersionResolver(), - getVersionRangeResolver(), getArtifactResolver(), getModelBuilder(), getRepositoryEventDispatcher(), @@ -1056,13 +1054,12 @@ public class RepositorySystemSupplier implements Supplier { 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 5eec24147c..aa8a3db3e8 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 @@ class TestApiStandalone { 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 5c038cd072..623c24a3f4 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 36c9110f9e..0201b2fd0a 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.Arrays; 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.DefaultMavenExecutionRequest; 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.DefaultProjectBuildingRequest; 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 @@ public abstract class AbstractCoreMavenComponentTestCase { .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 @@ public abstract class AbstractCoreMavenComponentTestCase { 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 @@ public abstract class AbstractCoreMavenComponentTestCase { 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 @@ public abstract class AbstractCoreMavenComponentTestCase { 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 68d6827b7a..fffdbb85c6 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.URISyntaxException; 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 @@ public abstract class AbstractMavenProjectTestCase { 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 @@ public abstract class AbstractMavenProjectTestCase { 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 @@ public abstract class AbstractMavenProjectTestCase { 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 cfda6e1cac..0fc0ba33cc 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.io.File; 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 class TestProjectBuilder extends DefaultProjectBuilder { 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 2f4cf648c8..2f03e66543 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 class DefaultEvent implements Event { @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 ba1c6d708f..359f84094a 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 class DefaultProject implements Project { @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 a446f63b26..efc6d3e785 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 class DefaultProjectBuilder implements ProjectBuilder { @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 cd83f7c449..a931b9874b 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 class DefaultSession extends AbstractSession implements InternalMavenSess @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 1ee399c6c0..e165dfd8e6 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 java.util.List; 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 @@ public interface InternalMavenSession extends InternalSession { 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 ab2463adc6..2ad05360cf 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 @@ package org.apache.maven.internal.transformation.impl; 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.Model; 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.ModelInterpolator; 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.PluginManagementInjector; 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; + 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 InheritanceAssembler inheritanceAssembler; + @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; + } - @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; - - @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; - - 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 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder { 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 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder { 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 6f77b92781..d40e2e5ac4 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.ProjectDependencyGraph; 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 @@ public class BuildPlanExecutor { } 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 eef005b517..0000000000 --- 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 d90f09a5a1..09ab3fe9eb 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.charset.StandardCharsets; 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.Map; 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.DependencyManagement; 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 @@ import org.slf4j.LoggerFactory; @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 class DefaultProjectBuilder implements ProjectBuilder { @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 @@ public class DefaultProjectBuilder implements ProjectBuilder { @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 class DefaultProjectBuilder implements ProjectBuilder { @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 class DefaultProjectBuilder implements ProjectBuilder { @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 class DefaultProjectBuilder implements ProjectBuilder { 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<>(); + private final ModelBuilder.ModelBuilderSession modelBuilderSession; + private final Map projectIndex = new ConcurrentHashMap<>(256); - BuildSession(ProjectBuildingRequest request, boolean localProjects) { + 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { } 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { return null; } return problems.stream() - .map(p -> (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 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { } } - 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { // defined repositories. // request.setRemoteRepositories(project.getRemoteArtifactRepositories()); + Path parentPomFile = parentModel.getPomFile(); if (parentPomFile != null) { project.setParentFile(parentPomFile.toFile()); try { @@ -1183,8 +762,7 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { } 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 @@ public class DefaultProjectBuilder implements ProjectBuilder { 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 class DefaultProjectBuilder implements ProjectBuilder { } } - 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); - protected abstract org.apache.maven.model.resolution.ModelResolver getResolver( - List repositories); - - @Override - public ModelSource resolveModel( - Session session, - List repositories, - String groupId, - String artifactId, - String version, - Consumer resolved) - throws ModelResolverException { + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) { 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); + ProjectRealmCache.CacheRecord record = + projectBuildingHelper.createProjectRealm(project, model3, projectBuildingRequest); + + project.setClassRealm(record.getRealm()); + project.setExtensionDependencyFilter(record.getExtensionArtifactFilter()); + } catch (PluginResolutionException | PluginManagerException | PluginVersionResolutionException 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 183132a5e3..078ce850ed 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 @@ class ReactorModelPool { } 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 443e976129..3560082c6c 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.execution.MavenSession; 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 @@ public abstract class AbstractCoreMavenComponentTestCase { 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 @@ public abstract class AbstractCoreMavenComponentTestCase { 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 @@ public abstract class AbstractCoreMavenComponentTestCase { 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 3176a9473b..358ab86ebe 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 c21e890238..0cf6c5c92c 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 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase { 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 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase { 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 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase { 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 class ConsumerPomBuilderTest extends AbstractRepositoryTestCase { 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 4596e69814..8fecf35980 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 @@ package org.apache.maven.model; 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 d20ec63280..652479a879 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.URISyntaxException; 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.execution.MavenSession; 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 @@ public abstract class AbstractMavenProjectTestCase { 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 98f8ba9254..1a963559db 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.SessionData; 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 @@ class DefaultMavenProjectBuilderTest extends AbstractMavenProjectTestCase { 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 @@ class DefaultMavenProjectBuilderTest extends AbstractMavenProjectTestCase { 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 61908e584e..c1d6a8ad3e 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 @@ package org.apache.maven.project; 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 java.util.Properties; 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.ReportPlugin; 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 @@ class PomConstructionTest { @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 @@ class PomConstructionTest { ? 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 b798c39bdf..ef4eede284 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.execution.MavenSession; 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 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase { 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 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase { 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 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase { // 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 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase { 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 @@ class ProjectBuilderTest extends AbstractCoreMavenComponentTestCase { 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 8b04f176e9..9aca25a9d3 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.DefaultProjectBuildingRequest; 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 @@ class PomConstructionWithSettingsTest { "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 0000000000..e69de29bb2 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 ae49ae2fb0..439c77eaf7 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 0000000000..bbee8d7348 --- /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 0000000000..bbee8d7348 --- /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 cbed746d1e..5e76b70f1d 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 0000000000..c175dc2a4a --- /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 0000000000..60f057cce3 --- /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 0000000000..422f76d023 --- /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 60f057cce3..d64644d4c9 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 0000000000..3d475ef2ac --- /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 0000000000..08706a5a05 --- /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 0000000000..5fb40a8a49 --- /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 0000000000..aae87db1d4 --- /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 0000000000..517e1505bc --- /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 4a5063805a..25b7eef589 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 c1e63ba556..c1ce59926e 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 69512db6c7..c739cc33b9 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 c527112307..71597091b6 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 09ccb88d1d..93a3f45ef6 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 8534ac440e..98996f3a09 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 da0da3b78a..2e31d40c63 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 @@ class DefaultTransformerContextBuilder implements TransformerContextBuilder { } 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 @@ class DefaultTransformerContextBuilder implements TransformerContextBuilder { 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 9c0cc109c5..a68704c2a3 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 @@ public interface ModelProblem { * 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 355453d394..8691fff480 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 @@ package org.apache.maven.model; 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 c206298e2b..8c6f01f238 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 @@ package org.apache.maven.artifact.repository.metadata; 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 50daaf95ff..754bbc2368 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.DefaultModelXmlFactory; 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 class MavenRepositorySystemSupplier implements Supplier protected ArtifactDescriptorReader createArtifactDescriptorReader() { // from maven-resolver-provider return new DefaultArtifactDescriptorReader( - getRemoteRepositoryManager(), getVersionResolver(), - getVersionRangeResolver(), getArtifactResolver(), getModelBuilder(), getRepositoryEventDispatcher(), @@ -1058,13 +1055,12 @@ public class MavenRepositorySystemSupplier implements Supplier 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 7b66110a8b..b656ff7b42 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 @@ package org.apache.maven.settings; 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 28bea64b8e..3a844a77c9 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 @@ package org.apache.maven.toolchain.model; 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 4aae757692..03f197d900 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 |