Last active
October 10, 2025 09:28
-
-
Save gnodet/f8b97e2929ff3ce4e9645dbb30b88492 to your computer and use it in GitHub Desktop.
Diff between 4.0.x and master
This file has been truncated, but you can view the full file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/.asf.yaml b/.asf.yaml | |
| index a6530be680..3344951e1a 100644 | |
| --- a/.asf.yaml | |
| +++ b/.asf.yaml | |
| @@ -16,10 +16,16 @@ github: | |
| - MNG | |
| pull_requests: | |
| del_branch_on_merge: true | |
| + protected_branches: | |
| + master: {} | |
| + maven-4.0.x: {} | |
| + maven-3.9.x: {} | |
| + maven-3.8.x: {} | |
| + maven-3.0.x: {} | |
| + pre-reset-master: {} | |
| features: | |
| issues: true | |
| notifications: | |
| commits: [email protected] | |
| issues: [email protected] | |
| pullrequests: [email protected] | |
| - jira_options: link label | |
| diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml | |
| index 10fb08b82c..b27d663311 100644 | |
| --- a/.github/ISSUE_TEMPLATE/config.yml | |
| +++ b/.github/ISSUE_TEMPLATE/config.yml | |
| @@ -24,7 +24,3 @@ contact_links: | |
| - name: Project Mailing Lists | |
| url: https://maven.apache.org/mailing-lists.html | |
| about: Please ask a question or discuss here | |
| - | |
| - - name: Old JIRA Issues | |
| - url: https://issues.apache.org/jira/browse/MNG | |
| - about: Please search old JIRA issues | |
| diff --git a/.github/dependabot.yml b/.github/dependabot.yml | |
| index 098e0d129b..1576b20f13 100644 | |
| --- a/.github/dependabot.yml | |
| +++ b/.github/dependabot.yml | |
| @@ -22,6 +22,15 @@ updates: | |
| schedule: | |
| interval: "daily" | |
| + - package-ecosystem: "maven" | |
| + directory: "/" | |
| + schedule: | |
| + interval: "daily" | |
| + target-branch: "maven-4.0.x" | |
| + labels: | |
| + - "mvn40" | |
| + - "dependencies" | |
| + | |
| - package-ecosystem: "maven" | |
| directory: "/" | |
| schedule: | |
| @@ -36,6 +45,16 @@ updates: | |
| schedule: | |
| interval: "daily" | |
| + - package-ecosystem: "github-actions" | |
| + directory: "/" | |
| + schedule: | |
| + interval: "daily" | |
| + target-branch: "maven-4.0.x" | |
| + labels: | |
| + - "mvn40" | |
| + - "dependencies" | |
| + - "github_actions" | |
| + | |
| - package-ecosystem: "github-actions" | |
| directory: "/" | |
| schedule: | |
| @@ -44,3 +63,4 @@ updates: | |
| labels: | |
| - "mvn3" | |
| - "dependencies" | |
| + - "github_actions" | |
| diff --git a/.github/release-drafter-3.x.yml b/.github/release-drafter-3.x.yml | |
| index 7438f11173..da2e7a556c 100644 | |
| --- a/.github/release-drafter-3.x.yml | |
| +++ b/.github/release-drafter-3.x.yml | |
| @@ -16,3 +16,4 @@ | |
| # under the License. | |
| _extends: maven-gh-actions-shared:.github/release-drafter.yml | |
| +tag-template: maven-$RESOLVED_VERSION | |
| diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml | |
| index eaea796b4d..add27b1def 100644 | |
| --- a/.github/release-drafter.yml | |
| +++ b/.github/release-drafter.yml | |
| @@ -16,6 +16,7 @@ | |
| # under the License. | |
| _extends: maven-gh-actions-shared | |
| +tag-template: maven-$RESOLVED_VERSION | |
| include-pre-releases: true | |
| prerelease: true | |
| diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml | |
| index c30c639ebf..19a0265f14 100644 | |
| --- a/.github/workflows/maven.yml | |
| +++ b/.github/workflows/maven.yml | |
| @@ -19,9 +19,9 @@ name: Java CI | |
| on: | |
| push: | |
| - branches: [ maven-4.0.x ] | |
| + branches: [ master ] | |
| pull_request: | |
| - branches: [ maven-4.0.x ] | |
| + branches: [ master ] | |
| # allow single build per branch or PR | |
| concurrency: | |
| @@ -35,6 +35,7 @@ env: | |
| MIMIR_VERSION: 0.9.4 | |
| MIMIR_BASEDIR: ~/.mimir | |
| MIMIR_LOCAL: ~/.mimir/local | |
| + MAVEN_OPTS: -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./target/java_heapdump.hprof | |
| jobs: | |
| initial-build: | |
| @@ -64,7 +65,7 @@ jobs: | |
| id: restore-cache | |
| with: | |
| path: ${{ env.MIMIR_LOCAL }} | |
| - key: mvn40-${{ runner.os }}-initial | |
| + key: mvn-${{ runner.os }}-initial | |
| - name: Set up Maven | |
| shell: bash | |
| @@ -146,10 +147,10 @@ jobs: | |
| id: restore-cache | |
| with: | |
| path: ${{ env.MIMIR_LOCAL }} | |
| - key: mvn40-full-${{ matrix.os }}-${{ matrix.java }} | |
| + key: mvn-full-${{ matrix.os }}-${{ matrix.java }} | |
| restore-keys: | | |
| - mvn40-full-${{ matrix.os }}- | |
| - mvn40-full- | |
| + mvn-full-${{ matrix.os }}- | |
| + mvn-full- | |
| - name: Download Maven distribution | |
| uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4 | |
| @@ -198,7 +199,9 @@ jobs: | |
| if: failure() || cancelled() | |
| with: | |
| name: ${{ github.run_number }}-full-build-artifact-${{ runner.os }}-${{ matrix.java }} | |
| - path: '**/target/surefire-reports/*' | |
| + path: | | |
| + **/target/surefire-reports/* | |
| + **/target/java_heapdump.hprof | |
| integration-tests: | |
| needs: initial-build | |
| @@ -234,10 +237,10 @@ jobs: | |
| id: restore-cache | |
| with: | |
| path: ${{ env.MIMIR_LOCAL }} | |
| - key: mvn40-its-${{ matrix.os }}-${{ matrix.java }} | |
| + key: mvn-its-${{ matrix.os }}-${{ matrix.java }} | |
| restore-keys: | | |
| - mvn40-its-${{ matrix.os }}- | |
| - mvn40-its- | |
| + mvn-its-${{ matrix.os }}- | |
| + mvn-its- | |
| - name: Download Maven distribution | |
| uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v4 | |
| @@ -282,4 +285,6 @@ jobs: | |
| if: failure() || cancelled() | |
| with: | |
| name: ${{ github.run_number }}-integration-test-artifact-${{ runner.os }}-${{ matrix.java }} | |
| - path: ./its/core-it-suite/target/test-classes/ | |
| + path: | | |
| + ./its/core-it-suite/target/test-classes/ | |
| + **/target/java_heapdump.hprof | |
| diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml | |
| index 0af2d2b844..96eaa60a0f 100644 | |
| --- a/.github/workflows/release-drafter.yml | |
| +++ b/.github/workflows/release-drafter.yml | |
| @@ -19,11 +19,9 @@ name: Release Drafter | |
| on: | |
| push: | |
| branches: | |
| - - maven-4.0.x | |
| + - master | |
| workflow_dispatch: | |
| jobs: | |
| update_release_draft: | |
| uses: apache/maven-gh-actions-shared/.github/workflows/release-drafter.yml@v4 | |
| - with: | |
| - commits-since: '2025-06-18T10:29:46Z' # date of first commit on maven-4.0.x branch, bed0f8174bf728978f86fac533aa38a9511f3872 | |
| diff --git a/.gitignore b/.gitignore | |
| index 4e85f56fb4..b3f36670ba 100644 | |
| --- a/.gitignore | |
| +++ b/.gitignore | |
| @@ -14,7 +14,6 @@ | |
| .java-version | |
| .checkstyle | |
| .factorypath | |
| -repo/ | |
| # VSCode | |
| .vscode/ | |
| diff --git a/.mvn/maven.config b/.mvn/maven.config | |
| index f3b0cd90b1..c6f922ecc2 100644 | |
| --- a/.mvn/maven.config | |
| +++ b/.mvn/maven.config | |
| @@ -1,2 +1 @@ | |
| -# A hack to pass on this property for Maven 3 as well; Maven 4 supports this property out of the box | |
| -DsessionRootDirectory=${session.rootDirectory} | |
| \ No newline at end of file | |
| diff --git a/apache-maven/pom.xml b/apache-maven/pom.xml | |
| index 28c4a2fcaf..940657ce0d 100644 | |
| --- a/apache-maven/pom.xml | |
| +++ b/apache-maven/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>apache-maven</artifactId> | |
| diff --git a/apache-maven/src/assembly/maven/conf/maven-user.properties b/apache-maven/src/assembly/maven/conf/maven-user.properties | |
| index b218c3d138..c664d06cc5 100644 | |
| --- a/apache-maven/src/assembly/maven/conf/maven-user.properties | |
| +++ b/apache-maven/src/assembly/maven/conf/maven-user.properties | |
| @@ -27,8 +27,36 @@ | |
| # Comma-separated list of files to include. | |
| # Each item may be enclosed in quotes to gracefully include spaces. Items are trimmed before being loaded. | |
| # If the first character of an item is a question mark, the load will silently fail if the file does not exist. | |
| -# Note: the maven.properties will be removed in Maven 4.1.0 and only maven-user.properties will be loaded! | |
| ${includes} = ?"${maven.user.conf}/maven-user.properties", \ | |
| - ?"${maven.user.conf}/maven.properties", \ | |
| - ?"${maven.project.conf}/maven-user.properties", \ | |
| - ?"${maven.project.conf}/maven.properties", | |
| + ?"${maven.project.conf}/maven-user.properties" | |
| + | |
| +# | |
| +# Maven Cache Configuration | |
| +# | |
| +# Controls caching behavior for different request types during Maven builds. | |
| +# Format: RequestType { scope:SCOPE ref:REFERENCE_TYPE } | |
| +# | |
| +# SCOPE OPTIONS: | |
| +# session - Cache for entire Maven session (all modules in multi-module build) | |
| +# request - Cache only for current request + its child requests | |
| +# none - Disable caching entirely | |
| +# | |
| +# REFERENCE OPTIONS: | |
| +# hard - Strong reference (stays in memory, faster access) | |
| +# soft - Weak reference (can be garbage collected under memory pressure) | |
| +# | |
| +# CONFIGURATION BREAKDOWN: | |
| +# SourceCacheKey - File and RAW model requests (session/hard for consistent file access) | |
| +# DefaultArtifactResolverRequest - Artifact resolution results (session/hard to avoid re-resolving) | |
| +# DefaultVersionResolverRequest - Version resolution results (session/soft, less critical than artifacts) | |
| +# RgavCacheKey - BOM import caching (session/soft, typically resolved once) | |
| +# DefaultModelBuilderRequest - Model building operations (none, ensures fresh dynamic builds) | |
| +# * - Fallback for all other requests (request/soft for temporary child request caching) | |
| +# | |
| +maven.cache.config = \ | |
| + SourceCacheKey { scope:session ref:hard } \ | |
| + DefaultArtifactResolverRequest { scope:session ref:hard } \ | |
| + DefaultVersionResolverRequest { scope:session ref:soft } \ | |
| + RgavCacheKey { scope:session ref:soft } \ | |
| + DefaultModelBuilderRequest { scope:none } \ | |
| + * { scope:request ref:soft } | |
| diff --git a/api/maven-api-annotations/pom.xml b/api/maven-api-annotations/pom.xml | |
| index 40872a8f74..350873e35a 100644 | |
| --- a/api/maven-api-annotations/pom.xml | |
| +++ b/api/maven-api-annotations/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-annotations</artifactId> | |
| diff --git a/api/maven-api-cli/pom.xml b/api/maven-api-cli/pom.xml | |
| index 509f0dad6b..6ed5c71eff 100644 | |
| --- a/api/maven-api-cli/pom.xml | |
| +++ b/api/maven-api-cli/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-cli</artifactId> | |
| diff --git a/api/maven-api-core/pom.xml b/api/maven-api-core/pom.xml | |
| index 104d6375b7..339ea83192 100644 | |
| --- a/api/maven-api-core/pom.xml | |
| +++ b/api/maven-api-core/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-core</artifactId> | |
| 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 7303e8e61c..d26972c006 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Artifact.java | |
| @@ -39,7 +39,7 @@ | |
| @Immutable | |
| public interface Artifact { | |
| /** | |
| - * {@return a unique identifier for this artifact} | |
| + * {@return a unique identifier for this artifact}. | |
| * The identifier is composed of groupId, artifactId, extension, classifier, and version. | |
| * | |
| * @see ArtifactCoordinates#getId() | |
| @@ -58,7 +58,7 @@ default String key() { | |
| } | |
| /** | |
| - * {@return the group identifier of the artifact} | |
| + * {@return the group identifier of the artifact}. | |
| * | |
| * @see ArtifactCoordinates#getGroupId() | |
| */ | |
| @@ -66,7 +66,7 @@ default String key() { | |
| String getGroupId(); | |
| /** | |
| - * {@return the identifier of the artifact} | |
| + * {@return the identifier of the artifact}. | |
| * | |
| * @see ArtifactCoordinates#getArtifactId() | |
| */ | |
| @@ -74,7 +74,7 @@ default String key() { | |
| String getArtifactId(); | |
| /** | |
| - * {@return the version of the artifact} | |
| + * {@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}, | |
| @@ -86,7 +86,7 @@ default String key() { | |
| Version getVersion(); | |
| /** | |
| - * {@return the version or meta-version of the artifact} | |
| + * {@return the version or meta-version of the artifact}. | |
| * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. | |
| * Meta-versions are represented in a base version by their symbols (e.g., {@code SNAPSHOT}), | |
| * while they are replaced by, for example, the actual timestamp in the {@linkplain #getVersion() version}. | |
| @@ -122,7 +122,7 @@ default String key() { | |
| boolean isSnapshot(); | |
| /** | |
| - * {@return coordinates with the same identifiers as this artifact} | |
| + * {@return coordinates with the same identifiers as this artifact}. | |
| * This is a shortcut for {@code session.createArtifactCoordinates(artifact)}. | |
| * | |
| * @see org.apache.maven.api.Session#createArtifactCoordinates(Artifact) | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java | |
| index dab32ee48c..89a7e37aa5 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinates.java | |
| @@ -33,13 +33,13 @@ | |
| @Immutable | |
| public interface ArtifactCoordinates { | |
| /** | |
| - * {@return the group identifier of the artifact} | |
| + * {@return the group identifier of the artifact}. | |
| */ | |
| @Nonnull | |
| String getGroupId(); | |
| /** | |
| - * {@return the identifier of the artifact} | |
| + * {@return the identifier of the artifact}. | |
| */ | |
| @Nonnull | |
| String getArtifactId(); | |
| @@ -53,7 +53,7 @@ public interface ArtifactCoordinates { | |
| String getClassifier(); | |
| /** | |
| - * {@return the specific version, range of versions or meta-version of the artifact} | |
| + * {@return the specific version, range of versions, or meta-version of the artifact}. | |
| * A meta-version is a version suffixed with the {@code SNAPSHOT} keyword. | |
| */ | |
| @Nonnull | |
| @@ -69,7 +69,7 @@ public interface ArtifactCoordinates { | |
| String getExtension(); | |
| /** | |
| - * {@return a unique string representation identifying this artifact} | |
| + * {@return a unique string identifying this artifact}. | |
| * | |
| * The default implementation returns a colon-separated list of group | |
| * identifier, artifact identifier, extension, classifier and version. | |
| 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 db96d3f7f6..8dc9bf106d 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 | |
| @@ -352,6 +352,7 @@ public final class Constants { | |
| * <li>"h" or "h(num)" - highest version or top list of highest ones filter</li> | |
| * <li>"l" or "l(num)" - lowest version or bottom list of lowest ones filter</li> | |
| * <li>"s" - contextual snapshot filter</li> | |
| + * <li>"ns" - unconditional snapshot filter (no snapshots selected from ranges)</li> | |
| * <li>"e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)</li> | |
| * </ul> | |
| * Example filter expression: <code>"h(5);s;e(org.foo:bar:1)</code> will cause: ranges are filtered for "top 5" (instead | |
| @@ -409,9 +410,9 @@ public final class Constants { | |
| /** | |
| * User property for selecting dependency manager behaviour regarding transitive dependencies and dependency | |
| - * management entries in their POMs. Maven 3 targeted full backward compatibility with Maven2, hence it ignored | |
| - * dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default, hence | |
| - * unlike Maven2, obeys dependency management entries deep in dependency graph as well. | |
| + * management entries in their POMs. Maven 3 targeted full backward compatibility with Maven 2. Hence, it ignored | |
| + * dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default. Hence | |
| + * unlike Maven 3, it obeys dependency management entries deep in the dependency graph as well. | |
| * <br/> | |
| * Default: <code>"true"</code>. | |
| * | |
| @@ -464,7 +465,7 @@ public final class Constants { | |
| /** | |
| * User property for controlling "maven personality". If activated Maven will behave | |
| - * as previous major version, Maven 3. | |
| + * like the previous major version, Maven 3. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| @@ -494,6 +495,19 @@ public final class Constants { | |
| @Config(type = "java.lang.Integer") | |
| public static final String MAVEN_DEPLOY_SNAPSHOT_BUILD_NUMBER = "maven.deploy.snapshot.buildNumber"; | |
| + /** | |
| + * User property for controlling whether build POMs are deployed alongside consumer POMs. | |
| + * When set to <code>false</code>, only the consumer POM will be deployed, and the build POM | |
| + * will be excluded from deployment. This is useful to avoid deploying internal build information | |
| + * that is not needed by consumers of the artifact. | |
| + * <br/> | |
| + * Default: <code>"true"</code>. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + @Config(type = "java.lang.Boolean", defaultValue = "true") | |
| + public static final String MAVEN_DEPLOY_BUILD_POM = "maven.deploy.buildPom"; | |
| + | |
| /** | |
| * User property used to store the build timestamp. | |
| * | |
| @@ -648,5 +662,70 @@ public final class Constants { | |
| */ | |
| public static final String MAVEN_LOGGER_LOG_PREFIX = MAVEN_LOGGER_PREFIX + "log."; | |
| + /** | |
| + * User property key for cache configuration. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + public static final String MAVEN_CACHE_CONFIG_PROPERTY = "maven.cache.config"; | |
| + | |
| + /** | |
| + * User property to enable cache statistics display at the end of the build. | |
| + * When set to true, detailed cache statistics including hit/miss ratios, | |
| + * request type breakdowns, and retention policy effectiveness will be displayed | |
| + * when the build completes. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + @Config(type = "java.lang.Boolean", defaultValue = "false") | |
| + public static final String MAVEN_CACHE_STATS = "maven.cache.stats"; | |
| + | |
| + /** | |
| + * User property to configure separate reference types for cache keys. | |
| + * This enables fine-grained analysis of cache misses caused by key vs value evictions. | |
| + * Supported values are {@code HARD}, {@code SOFT} and {@code WEAK}. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + public static final String MAVEN_CACHE_KEY_REFS = "maven.cache.keyValueRefs"; | |
| + | |
| + /** | |
| + * User property to configure separate reference types for cache values. | |
| + * This enables fine-grained analysis of cache misses caused by key vs value evictions. | |
| + * Supported values are {@code HARD}, {@code SOFT} and {@code WEAK}. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + public static final String MAVEN_CACHE_VALUE_REFS = "maven.cache.keyValueRefs"; | |
| + | |
| + /** | |
| + * User property key for configuring which object types are pooled by ModelObjectProcessor. | |
| + * Value should be a comma-separated list of simple class names (e.g., "Dependency,Plugin,Build"). | |
| + * Default is "Dependency" for backward compatibility. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + @Config(defaultValue = "Dependency") | |
| + public static final String MAVEN_MODEL_PROCESSOR_POOLED_TYPES = "maven.model.processor.pooledTypes"; | |
| + | |
| + /** | |
| + * User property key for configuring the default reference type used by ModelObjectProcessor. | |
| + * Valid values are: "SOFT", "HARD", "WEAK", "NONE". | |
| + * Default is "HARD" for optimal performance. | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + @Config(defaultValue = "HARD") | |
| + public static final String MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE = "maven.model.processor.referenceType"; | |
| + | |
| + /** | |
| + * User property key prefix for configuring per-object-type reference types. | |
| + * Format: maven.model.processor.referenceType.{ClassName} = {ReferenceType} | |
| + * Example: maven.model.processor.referenceType.Dependency = SOFT | |
| + * | |
| + * @since 4.1.0 | |
| + */ | |
| + public static final String MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE_PREFIX = "maven.model.processor.referenceType."; | |
| + | |
| private Constants() {} | |
| } | |
| 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 5a2ad2b9d4..95dc5d689e 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,7 @@ | |
| @Immutable | |
| public interface Dependency extends Artifact { | |
| /** | |
| - * {@return the type of the dependency} | |
| + * {@return the type of the dependency}. | |
| * A dependency can be a <abbr>JAR</abbr> file, | |
| * a modular-<abbr>JAR</abbr> if it is intended to be placed on the module path, | |
| * a <abbr>JAR</abbr> containing test classes, <i>etc.</i> | |
| @@ -47,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}. | |
| * It may be, for example, at compile time only, at run time or at test time. | |
| * | |
| * @see DependencyCoordinates#getScope() | |
| @@ -66,7 +66,7 @@ public interface Dependency extends Artifact { | |
| boolean isOptional(); | |
| /** | |
| - * {@return coordinates with the same identifiers as this dependency} | |
| + * {@return coordinates with the same identifiers as this dependency}. | |
| */ | |
| @Nonnull | |
| @Override | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java | |
| index 10e9deb898..f01cb2acb9 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DownloadedArtifact.java | |
| @@ -33,7 +33,7 @@ | |
| public interface DownloadedArtifact extends Artifact { | |
| /** | |
| - * {@return the actual file that has been downloaded in the file system} | |
| + * {@return the a path to the file that has been downloaded to the file system}. | |
| */ | |
| Path getPath(); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java | |
| index 58812db8b2..b2e98ed3c6 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java | |
| @@ -263,8 +263,8 @@ final String[] format(String moduleName, Iterable<? extends Path> paths) { | |
| if (option == null) { | |
| throw new IllegalStateException("No option is associated to this path type."); | |
| } | |
| - String prefix = (moduleName == null) ? "" : (moduleName + '='); | |
| - StringJoiner joiner = new StringJoiner(File.pathSeparator, prefix, ""); | |
| + String prefix = (moduleName == null) ? "\"" : (moduleName + "=\""); | |
| + StringJoiner joiner = new StringJoiner(File.pathSeparator, prefix, "\""); | |
| joiner.setEmptyValue(""); | |
| for (Path p : paths) { | |
| joiner.add(p.toString()); | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java b/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java | |
| index c60f6befda..4a4aa4f561 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/SourceRoot.java | |
| @@ -68,9 +68,8 @@ default Path directory() { | |
| * If no syntax is specified, or if its length is 1 character (interpreted as a Windows drive), | |
| * the default is a Maven-specific variation of the {@code "glob"} pattern. | |
| * | |
| - * <p> | |
| - * The default implementation returns an empty list, which means to apply a language-dependent pattern. | |
| - * For example, for the Java language, the pattern includes all files with the {@code .java} suffix. | |
| + * <p>The default implementation returns an empty list, which means to apply a language-dependent pattern. | |
| + * For example, for the Java language, the pattern includes all files with the {@code .java} suffix.</p> | |
| * | |
| * @see java.nio.file.FileSystem#getPathMatcher(String) | |
| */ | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java | |
| index 91f6b9f350..acc3d92f5c 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/feature/Features.java | |
| @@ -19,7 +19,6 @@ | |
| package org.apache.maven.api.feature; | |
| import java.util.Map; | |
| -import java.util.Properties; | |
| import org.apache.maven.api.Constants; | |
| import org.apache.maven.api.annotations.Nullable; | |
| @@ -48,8 +47,11 @@ public static boolean consumerPom(@Nullable Map<String, ?> userProperties) { | |
| return doGet(userProperties, Constants.MAVEN_CONSUMER_POM, !mavenMaven3Personality(userProperties)); | |
| } | |
| - private static boolean doGet(Properties userProperties, String key, boolean def) { | |
| - return doGet(userProperties != null ? userProperties.get(key) : null, def); | |
| + /** | |
| + * Check if build POM deployment is enabled. | |
| + */ | |
| + public static boolean deployBuildPom(@Nullable Map<String, ?> userProperties) { | |
| + return doGet(userProperties, Constants.MAVEN_DEPLOY_BUILD_POM, true); | |
| } | |
| private static boolean doGet(Map<String, ?> userProperties, String key, boolean def) { | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java | |
| index 6604c0392a..c17884fd97 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactDeployerException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,12 +28,6 @@ | |
| @Experimental | |
| public class ArtifactDeployerException extends MavenException { | |
| - /** | |
| - * | |
| - */ | |
| - @Serial | |
| - private static final long serialVersionUID = 7421964724059077698L; | |
| - | |
| /** | |
| * @param message the message of the error | |
| * @param e {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java | |
| index 8405fc3017..c683928810 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -28,12 +26,6 @@ | |
| @Experimental | |
| public class ArtifactInstallerException extends MavenException { | |
| - /** | |
| - * | |
| - */ | |
| - @Serial | |
| - private static final long serialVersionUID = 3652561971360586373L; | |
| - | |
| /** | |
| * @param message the message of the error | |
| * @param e {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java | |
| index 22f398c065..22d5af16f3 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class ArtifactResolverException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = 7252294837746943917L; | |
| - | |
| private final ArtifactResolverResult result; | |
| /** | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java | |
| index a42546e828..7aaed5632f 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java | |
| @@ -18,16 +18,11 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| @Experimental | |
| public class ChecksumAlgorithmServiceException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = 1201171469179367694L; | |
| - | |
| public ChecksumAlgorithmServiceException(String message, Throwable cause) { | |
| super(message, cause); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java | |
| index 3bc03d1190..a26a88c6f7 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverException.java | |
| @@ -18,16 +18,11 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| @Experimental | |
| public class DependencyResolverException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = 1101171569179057614L; | |
| - | |
| public DependencyResolverException(String message, Throwable cause) { | |
| super(message, cause); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java | |
| index b7ca808a78..e3e100ecc0 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/InterpolatorException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -31,9 +29,6 @@ | |
| @Experimental | |
| public class InterpolatorException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -1219149033636851813L; | |
| - | |
| /** | |
| * Constructs a new InterpolatorException with {@code null} as its | |
| * detail message. The cause is not initialized, and may subsequently be | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java | |
| index 97670213c1..ee67fa9bdc 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LookupException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class LookupException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -6259322450070320286L; | |
| - | |
| public LookupException(String message) { | |
| super(message); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java | |
| index 5121ce8c75..cdd9ced96c 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MavenException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class MavenException extends RuntimeException { | |
| - @Serial | |
| - private static final long serialVersionUID = 9027638326336093132L; | |
| - | |
| public MavenException() {} | |
| public MavenException(String message) { | |
| 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 f13a60def8..ea8e263392 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 | |
| @@ -29,7 +29,9 @@ public interface ModelBuilder extends Service { | |
| String MODEL_VERSION_4_1_0 = "4.1.0"; | |
| - List<String> KNOWN_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0); | |
| + String MODEL_VERSION_4_2_0 = "4.2.0"; | |
| + | |
| + List<String> KNOWN_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0, MODEL_VERSION_4_2_0); | |
| ModelBuilderSession newSession(); | |
| 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 c01fa138ab..cd6b9b5062 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 | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class ModelBuilderException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -1865447022070650896L; | |
| - | |
| private final ModelBuilderResult result; | |
| /** | |
| 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 07ba7bbe92..63bad258bf 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 | |
| @@ -66,7 +66,12 @@ enum Version { | |
| /** | |
| * Validation for Maven 4.1 POM format. | |
| */ | |
| - V41 | |
| + V41, | |
| + | |
| + /** | |
| + * Validation for Maven 4.2 POM format. | |
| + */ | |
| + V42 | |
| } | |
| /** | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java | |
| index 325b1d4419..a259ba52cd 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelSource.java | |
| @@ -42,6 +42,22 @@ | |
| */ | |
| public interface ModelSource extends Source { | |
| + /** | |
| + * Returns the model identifier in the format {@code groupId:artifactId:version} | |
| + * if this source represents a resolved artifact with known coordinates. | |
| + * <p> | |
| + * This method is primarily used by resolved sources to provide the model ID | |
| + * without requiring the XML to be parsed. For build sources, this typically | |
| + * returns {@code null} since the coordinates are determined by parsing the POM. | |
| + * | |
| + * @return the model identifier, or {@code null} if not available or not applicable | |
| + * @since 4.0.0 | |
| + */ | |
| + @Nullable | |
| + default String getModelId() { | |
| + return null; | |
| + } | |
| + | |
| /** | |
| * Interface for locating POM files within a project structure. | |
| * Implementations of this interface provide the ability to find POM files | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java | |
| index 6e5a9d29f6..11fbb4bce0 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProblemCollector.java | |
| @@ -26,6 +26,7 @@ | |
| import java.util.concurrent.CopyOnWriteArrayList; | |
| import java.util.concurrent.atomic.AtomicInteger; | |
| import java.util.concurrent.atomic.LongAdder; | |
| +import java.util.function.Predicate; | |
| import java.util.stream.Stream; | |
| import org.apache.maven.api.Constants; | |
| @@ -181,13 +182,27 @@ public Stream<P> problems(BuilderProblem.Severity severity) { | |
| static <P extends BuilderProblem> ProblemCollector<P> create(@Nullable ProtoSession protoSession) { | |
| if (protoSession != null | |
| && protoSession.getUserProperties().containsKey(Constants.MAVEN_BUILDER_MAX_PROBLEMS)) { | |
| - return new Impl<>( | |
| - Integer.parseInt(protoSession.getUserProperties().get(Constants.MAVEN_BUILDER_MAX_PROBLEMS))); | |
| + int limit = Integer.parseInt(protoSession.getUserProperties().get(Constants.MAVEN_BUILDER_MAX_PROBLEMS)); | |
| + return create(limit, p -> true); | |
| } else { | |
| return create(100); | |
| } | |
| } | |
| + /** | |
| + * Creates new instance of problem collector with the specified maximum problem count limit, | |
| + * but only preserves problems that match the given filter. | |
| + * | |
| + * @param <P> the type of problem | |
| + * @param maxCountLimit the maximum number of problems to preserve | |
| + * @param filter predicate to decide which problems to record | |
| + * @return a new filtered problem collector instance | |
| + */ | |
| + @Nonnull | |
| + static <P extends BuilderProblem> ProblemCollector<P> create(int maxCountLimit, Predicate<? super P> filter) { | |
| + return new Impl<>(maxCountLimit, filter); | |
| + } | |
| + | |
| /** | |
| * Creates new instance of problem collector with the specified maximum problem count limit. | |
| * Visible for testing only. | |
| @@ -198,7 +213,7 @@ static <P extends BuilderProblem> ProblemCollector<P> create(@Nullable ProtoSess | |
| */ | |
| @Nonnull | |
| static <P extends BuilderProblem> ProblemCollector<P> create(int maxCountLimit) { | |
| - return new Impl<>(maxCountLimit); | |
| + return create(maxCountLimit, p -> true); | |
| } | |
| /** | |
| @@ -212,13 +227,14 @@ static <P extends BuilderProblem> ProblemCollector<P> create(int maxCountLimit) | |
| private final AtomicInteger totalCount; | |
| private final ConcurrentMap<BuilderProblem.Severity, LongAdder> counters; | |
| private final ConcurrentMap<BuilderProblem.Severity, List<P>> problems; | |
| + private final Predicate<? super P> filter; | |
| private static final List<BuilderProblem.Severity> REVERSED_ORDER = Arrays.stream( | |
| BuilderProblem.Severity.values()) | |
| .sorted(Comparator.reverseOrder()) | |
| .toList(); | |
| - private Impl(int maxCountLimit) { | |
| + private Impl(int maxCountLimit, Predicate<? super P> filter) { | |
| if (maxCountLimit < 0) { | |
| throw new IllegalArgumentException("maxCountLimit must be non-negative"); | |
| } | |
| @@ -226,6 +242,7 @@ private Impl(int maxCountLimit) { | |
| this.totalCount = new AtomicInteger(); | |
| this.counters = new ConcurrentHashMap<>(); | |
| this.problems = new ConcurrentHashMap<>(); | |
| + this.filter = requireNonNull(filter, "filter"); | |
| } | |
| @Override | |
| @@ -245,6 +262,11 @@ public boolean problemsOverflow() { | |
| @Override | |
| public boolean reportProblem(P problem) { | |
| requireNonNull(problem, "problem"); | |
| + // first apply filter | |
| + if (!filter.test(problem)) { | |
| + // drop without counting towards preserved problems | |
| + return false; | |
| + } | |
| int currentCount = totalCount.incrementAndGet(); | |
| getCounter(problem.getSeverity()).increment(); | |
| if (currentCount <= maxCountLimit || dropProblemWithLowerSeverity(problem.getSeverity())) { | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java | |
| index d6adcaee2a..baee35bd43 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class ProjectBuilderException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -7629871850875943799L; | |
| - | |
| /** | |
| * @param message the message to give | |
| * @param e the {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java | |
| index ca17937dfc..421baa8f76 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PrompterException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class PrompterException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -3505070928479515081L; | |
| - | |
| public PrompterException(String message) { | |
| super(message); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java | |
| index a8ccd0f47f..2c81d87399 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class SettingsBuilderException extends MavenBuilderException { | |
| - @Serial | |
| - private static final long serialVersionUID = 4714858598345418083L; | |
| - | |
| /** | |
| * @param message the message to give | |
| * @param e the {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java | |
| index 4acab6006a..73d8978424 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Sources.java | |
| @@ -84,13 +84,13 @@ public static ModelSource buildSource(@Nonnull Path path) { | |
| * other sources. | |
| * | |
| * @param path the path to the POM file or project directory | |
| - * @param location optional logical location of the source, used for reporting purposes | |
| + * @param modelId the modelId (groupId:artifactId:version coordinates) | |
| * @return a new ModelSource instance configured as a resolved source | |
| * @throws NullPointerException if path is null | |
| */ | |
| @Nonnull | |
| - public static ModelSource resolvedSource(@Nonnull Path path, @Nullable String location) { | |
| - return new ResolvedPathSource(requireNonNull(path, "path"), location); | |
| + public static ModelSource resolvedSource(@Nonnull Path path, @Nullable String modelId) { | |
| + return new ResolvedPathSource(requireNonNull(path, "path"), path.toString(), modelId); | |
| } | |
| /** | |
| @@ -170,8 +170,12 @@ public String toString() { | |
| * from repositories. Does not support resolving related sources. | |
| */ | |
| static class ResolvedPathSource extends PathSource implements ModelSource { | |
| - ResolvedPathSource(Path path, String location) { | |
| + @Nullable | |
| + private final String modelId; | |
| + | |
| + ResolvedPathSource(Path path, String location, String modelId) { | |
| super(path, location); | |
| + this.modelId = modelId; | |
| } | |
| @Override | |
| @@ -179,6 +183,12 @@ public Path getPath() { | |
| return null; | |
| } | |
| + @Override | |
| + @Nullable | |
| + public String getModelId() { | |
| + return modelId; | |
| + } | |
| + | |
| @Override | |
| public Source resolve(String relative) { | |
| return null; | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java | |
| index 8014e28b3a..4aa6890ca3 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SuperPomProviderException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| /** | |
| * Exceptions thrown by the {@link SuperPomProvider} service. | |
| * | |
| @@ -27,9 +25,6 @@ | |
| */ | |
| public class SuperPomProviderException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -8659892034004509331L; | |
| - | |
| public SuperPomProviderException() { | |
| super(); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java | |
| index 38812cee2c..574f7dc144 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainManagerException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class ToolchainManagerException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -9465854226608498L; | |
| - | |
| /** | |
| * @param message the message to give | |
| * @param e the {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java | |
| index 10bd3b5dc5..826cc5f7e9 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class ToolchainsBuilderException extends MavenBuilderException { | |
| - @Serial | |
| - private static final long serialVersionUID = 7899871809665729349L; | |
| - | |
| /** | |
| * @param message the message to give | |
| * @param e the {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java | |
| index 3347d73539..c78fa37e24 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TransportProviderException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Consumer; | |
| import org.apache.maven.api.annotations.Experimental; | |
| @@ -30,9 +28,6 @@ | |
| @Consumer | |
| public class TransportProviderException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -6066070072576465969L; | |
| - | |
| public TransportProviderException(String message, Throwable cause) { | |
| super(message, cause); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java | |
| index 38bedc1275..e22235afa2 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParserException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Experimental; | |
| /** | |
| @@ -30,9 +28,6 @@ | |
| @Experimental | |
| public class VersionParserException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = 1504740189114877333L; | |
| - | |
| /** | |
| * @param message the message to give | |
| * @param e the {@link Exception} | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java | |
| index d32d0fae87..dd0746844f 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Consumer; | |
| import org.apache.maven.api.annotations.Experimental; | |
| @@ -30,9 +28,6 @@ | |
| @Consumer | |
| public class VersionRangeResolverException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = 4455478418692494141L; | |
| - | |
| public VersionRangeResolverException(String message, Throwable cause) { | |
| super(message, cause); | |
| } | |
| diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java | |
| index 3b650a135c..6eb5b0989c 100644 | |
| --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java | |
| +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverException.java | |
| @@ -18,8 +18,6 @@ | |
| */ | |
| package org.apache.maven.api.services; | |
| -import java.io.Serial; | |
| - | |
| import org.apache.maven.api.annotations.Consumer; | |
| import org.apache.maven.api.annotations.Experimental; | |
| @@ -30,9 +28,6 @@ | |
| @Consumer | |
| public class VersionResolverException extends MavenException { | |
| - @Serial | |
| - private static final long serialVersionUID = -2105433586719466573L; | |
| - | |
| public VersionResolverException(String message, Throwable cause) { | |
| super(message, cause); | |
| } | |
| diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/JavaPathTypeTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/JavaPathTypeTest.java | |
| new file mode 100644 | |
| index 0000000000..701a82775b | |
| --- /dev/null | |
| +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/JavaPathTypeTest.java | |
| @@ -0,0 +1,83 @@ | |
| +/* | |
| + * 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; | |
| + | |
| +import java.io.File; | |
| +import java.nio.file.Path; | |
| +import java.util.List; | |
| + | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotEquals; | |
| + | |
| +public class JavaPathTypeTest { | |
| + /** | |
| + * {@return dummy paths to use in tests}. | |
| + */ | |
| + private static List<Path> paths() { | |
| + return List.of(Path.of("src", "foo.java"), Path.of("src", "bar.java")); | |
| + } | |
| + | |
| + /** | |
| + * Converts paths from Unix style to platform-dependent style. | |
| + * | |
| + * @param expected the option value expected by the test | |
| + * @return the expected value with separators of the host | |
| + */ | |
| + private static String toPlatformSpecific(String expected) { | |
| + return expected.replace("/", File.separator).replace(":", File.pathSeparator); | |
| + } | |
| + | |
| + /** | |
| + * Tests the formatting of an option without module name. | |
| + */ | |
| + @Test | |
| + public void testOption() { | |
| + String[] formatted = JavaPathType.MODULES.option(paths()); | |
| + assertEquals(2, formatted.length); | |
| + assertEquals("--module-path", formatted[0]); | |
| + assertEquals(toPlatformSpecific("\"src/foo.java:src/bar.java\""), formatted[1]); | |
| + } | |
| + | |
| + /** | |
| + * Tests the formatting of an option with a module name. | |
| + */ | |
| + @Test | |
| + public void testModularOption() { | |
| + String[] formatted = JavaPathType.patchModule("my.module").option(paths()); | |
| + assertEquals(2, formatted.length); | |
| + assertEquals("--patch-module", formatted[0]); | |
| + assertEquals(toPlatformSpecific("my.module=\"src/foo.java:src/bar.java\""), formatted[1]); | |
| + } | |
| + | |
| + /** | |
| + * Tests the {@code equals} and {@code hashCode} methods of options. | |
| + */ | |
| + @Test | |
| + public void testEqualsHashCode() { | |
| + JavaPathType.Modular foo1 = JavaPathType.patchModule("foo"); | |
| + JavaPathType.Modular foo2 = JavaPathType.patchModule("foo"); | |
| + JavaPathType.Modular bar = JavaPathType.patchModule("bar"); | |
| + assertEquals(foo1, foo2); | |
| + assertEquals(foo1.hashCode(), foo2.hashCode()); | |
| + assertNotEquals(foo1, bar); | |
| + assertNotEquals(foo1.hashCode(), bar.hashCode()); | |
| + } | |
| +} | |
| diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/feature/FeaturesTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/feature/FeaturesTest.java | |
| new file mode 100644 | |
| index 0000000000..0b938b9df3 | |
| --- /dev/null | |
| +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/feature/FeaturesTest.java | |
| @@ -0,0 +1,194 @@ | |
| +/* | |
| + * 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.feature; | |
| + | |
| +import java.util.HashMap; | |
| +import java.util.Map; | |
| + | |
| +import org.apache.maven.api.Constants; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * Unit tests for the Features class. | |
| + */ | |
| +class FeaturesTest { | |
| + | |
| + @Test | |
| + void testDeployBuildPomDefaultValue() { | |
| + // Test that deployBuildPom returns true by default (when property is not set) | |
| + Map<String, Object> emptyProperties = Map.of(); | |
| + assertTrue(Features.deployBuildPom(emptyProperties)); | |
| + | |
| + // Test with null properties | |
| + assertTrue(Features.deployBuildPom(null)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithStringTrue() { | |
| + // Test with string "true" | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "true"); | |
| + assertTrue(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithStringFalse() { | |
| + // Test with string "false" | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithBooleanTrue() { | |
| + // Test with Boolean.TRUE | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, Boolean.TRUE); | |
| + assertTrue(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithBooleanFalse() { | |
| + // Test with Boolean.FALSE | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, Boolean.FALSE); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithStringTrueUpperCase() { | |
| + // Test case-insensitive string parsing - TRUE | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "TRUE"); | |
| + assertTrue(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithStringFalseUpperCase() { | |
| + // Test case-insensitive string parsing - FALSE | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "FALSE"); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithStringTrueMixedCase() { | |
| + // Test case-insensitive string parsing - True | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "True"); | |
| + assertTrue(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithStringFalseMixedCase() { | |
| + // Test case-insensitive string parsing - False | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "False"); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithInvalidStringValue() { | |
| + // Test that invalid string values default to false (Boolean.parseBoolean behavior) | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "invalid"); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithEmptyString() { | |
| + // Test that empty string defaults to false | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, ""); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithYesString() { | |
| + // Test that "yes" string defaults to false (not a valid boolean) | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "yes"); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithNumericString() { | |
| + // Test that numeric string defaults to false | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "1"); | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithIntegerOne() { | |
| + // Test with integer 1 (should use toString() and then parseBoolean) | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, 1); | |
| + assertFalse(Features.deployBuildPom(properties)); // "1".parseBoolean() = false | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithIntegerZero() { | |
| + // Test with integer 0 (should use toString() and then parseBoolean) | |
| + Map<String, Object> properties = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, 0); | |
| + assertFalse(Features.deployBuildPom(properties)); // "0".parseBoolean() = false | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithMutableMap() { | |
| + // Test with a mutable map to ensure the method doesn't modify the input | |
| + Map<String, Object> properties = new HashMap<>(); | |
| + properties.put(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); | |
| + | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + | |
| + // Verify the map wasn't modified | |
| + assertEquals(1, properties.size()); | |
| + assertEquals("false", properties.get(Constants.MAVEN_DEPLOY_BUILD_POM)); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithOtherProperties() { | |
| + // Test that other properties don't interfere | |
| + Map<String, Object> properties = Map.of( | |
| + Constants.MAVEN_CONSUMER_POM, | |
| + "false", | |
| + Constants.MAVEN_MAVEN3_PERSONALITY, | |
| + "true", | |
| + "some.other.property", | |
| + "value", | |
| + Constants.MAVEN_DEPLOY_BUILD_POM, | |
| + "false"); | |
| + | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testConsistencyWithOtherFeatureMethodsFalse() { | |
| + // Test that deployBuildPom behaves consistently with other feature methods when false | |
| + Map<String, Object> properties = Map.of( | |
| + Constants.MAVEN_DEPLOY_BUILD_POM, "false", | |
| + Constants.MAVEN_CONSUMER_POM, "false"); | |
| + | |
| + assertFalse(Features.deployBuildPom(properties)); | |
| + assertFalse(Features.consumerPom(properties)); | |
| + } | |
| + | |
| + @Test | |
| + void testConsistencyWithOtherFeatureMethodsTrue() { | |
| + // Test that deployBuildPom behaves consistently with other feature methods when true | |
| + Map<String, Object> properties = Map.of( | |
| + Constants.MAVEN_DEPLOY_BUILD_POM, "true", | |
| + Constants.MAVEN_CONSUMER_POM, "true"); | |
| + | |
| + assertTrue(Features.deployBuildPom(properties)); | |
| + assertTrue(Features.consumerPom(properties)); | |
| + } | |
| +} | |
| diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/ModelSourceTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/ModelSourceTest.java | |
| new file mode 100644 | |
| index 0000000000..7a293a255b | |
| --- /dev/null | |
| +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/ModelSourceTest.java | |
| @@ -0,0 +1,71 @@ | |
| +/* | |
| + * 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 java.nio.file.Paths; | |
| + | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * Test for ModelSource interface and its implementations. | |
| + */ | |
| +class ModelSourceTest { | |
| + | |
| + @Test | |
| + void testBuildSourceHasNoModelId() { | |
| + Path path = Paths.get("/tmp/pom.xml"); | |
| + ModelSource source = Sources.buildSource(path); | |
| + | |
| + assertNotNull(source); | |
| + assertNull(source.getModelId(), "Build sources should not have a modelId"); | |
| + assertEquals(path, source.getPath()); | |
| + } | |
| + | |
| + @Test | |
| + void testResolvedSourceWithModelId() { | |
| + String location = "/tmp/resolved-pom.xml"; | |
| + Path path = Paths.get(location); | |
| + String modelId = "org.apache.maven:maven-core:4.0.0"; | |
| + | |
| + ModelSource source = Sources.resolvedSource(path, modelId); | |
| + | |
| + assertNotNull(source); | |
| + assertEquals(modelId, source.getModelId(), "Resolved source should return the provided modelId"); | |
| + assertNull(source.getPath(), "Resolved sources should return null for getPath()"); | |
| + assertEquals(path.toString(), source.getLocation()); | |
| + } | |
| + | |
| + @Test | |
| + void testModelIdFormat() { | |
| + String location = "/tmp/test.xml"; | |
| + Path path = Paths.get(location); | |
| + String modelId = "com.example:test-artifact:1.2.3"; | |
| + | |
| + ModelSource source = Sources.resolvedSource(path, modelId); | |
| + | |
| + assertEquals(modelId, source.getModelId()); | |
| + assertTrue(modelId.matches("^[^:]+:[^:]+:[^:]+$"), "ModelId should follow groupId:artifactId:version format"); | |
| + } | |
| +} | |
| diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java | |
| index eef64a1388..694956063e 100644 | |
| --- a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java | |
| +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java | |
| @@ -69,8 +69,8 @@ void testArtifactResolverRequestEquality() { | |
| // Test toString | |
| String toString = request1.toString(); | |
| - assertTrue(toString.contains("coordinates=")); | |
| - assertTrue(toString.contains("repositories=")); | |
| + assertTrue(toString.contains("coordinates="), "Expected " + toString + " to contain " + "coordinates="); | |
| + assertTrue(toString.contains("repositories="), "Expected " + toString + " to contain " + "repositories="); | |
| } | |
| @Test | |
| @@ -110,7 +110,7 @@ void testDependencyResolverRequestEquality() { | |
| assertNotEquals(request1, request3); | |
| String toString = request1.toString(); | |
| - assertTrue(toString.contains("requestType=")); | |
| - assertTrue(toString.contains("pathScope=")); | |
| + assertTrue(toString.contains("requestType="), "Expected " + toString + " to contain " + "requestType="); | |
| + assertTrue(toString.contains("pathScope="), "Expected " + toString + " to contain " + "pathScope="); | |
| } | |
| } | |
| diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java | |
| index f9abbe66d2..58aec58f1e 100644 | |
| --- a/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java | |
| +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/SourcesTest.java | |
| @@ -64,14 +64,16 @@ void testBuildSource() { | |
| @Test | |
| void testResolvedSource() { | |
| - Path path = Paths.get("/tmp"); | |
| - String location = "custom-location"; | |
| - ModelSource source = Sources.resolvedSource(path, location); | |
| + String location = "/tmp"; | |
| + Path path = Paths.get(location); | |
| + String modelId = "org.example:test:1.0.0"; | |
| + ModelSource source = Sources.resolvedSource(path, modelId); | |
| assertNotNull(source); | |
| assertInstanceOf(Sources.ResolvedPathSource.class, source); | |
| assertNull(source.getPath()); | |
| - assertEquals(location, source.getLocation()); | |
| + assertEquals(path.toString(), source.getLocation()); | |
| + assertEquals(modelId, source.getModelId()); | |
| } | |
| @Test | |
| @@ -109,12 +111,14 @@ void testBuildPathSourceFunctionality() { | |
| @Test | |
| void testResolvedPathSourceFunctionality() { | |
| // Test resolved source functionality | |
| - Path path = Paths.get("/tmp"); | |
| - String location = "custom-location"; | |
| - Sources.ResolvedPathSource source = (Sources.ResolvedPathSource) Sources.resolvedSource(path, location); | |
| + String location = "/tmp"; | |
| + Path path = Paths.get(location); | |
| + String modelId = "org.example:test:1.0.0"; | |
| + Sources.ResolvedPathSource source = (Sources.ResolvedPathSource) Sources.resolvedSource(path, modelId); | |
| assertNull(source.getPath()); | |
| - assertEquals(location, source.getLocation()); | |
| + assertEquals(path.toString(), source.getLocation()); | |
| + assertEquals(modelId, source.getModelId()); | |
| assertNull(source.resolve("subdir")); | |
| ModelSource.ModelLocator locator = mock(ModelSource.ModelLocator.class); | |
| @@ -140,6 +144,6 @@ void testStreamReading() throws IOException { | |
| void testNullHandling() { | |
| assertThrows(NullPointerException.class, () -> Sources.fromPath(null)); | |
| assertThrows(NullPointerException.class, () -> Sources.buildSource(null)); | |
| - assertThrows(NullPointerException.class, () -> Sources.resolvedSource(null, "location")); | |
| + assertThrows(NullPointerException.class, () -> Sources.resolvedSource(null, "modelId")); | |
| } | |
| } | |
| diff --git a/api/maven-api-di/pom.xml b/api/maven-api-di/pom.xml | |
| index c2cc62e36f..d2341a7a42 100644 | |
| --- a/api/maven-api-di/pom.xml | |
| +++ b/api/maven-api-di/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-di</artifactId> | |
| diff --git a/api/maven-api-metadata/pom.xml b/api/maven-api-metadata/pom.xml | |
| index 174816e59e..311aad02c4 100644 | |
| --- a/api/maven-api-metadata/pom.xml | |
| +++ b/api/maven-api-metadata/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-metadata</artifactId> | |
| diff --git a/api/maven-api-model/pom.xml b/api/maven-api-model/pom.xml | |
| index 37e0555cbe..8294a3e887 100644 | |
| --- a/api/maven-api-model/pom.xml | |
| +++ b/api/maven-api-model/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-model</artifactId> | |
| @@ -48,7 +48,7 @@ under the License. | |
| <groupId>org.codehaus.modello</groupId> | |
| <artifactId>modello-maven-plugin</artifactId> | |
| <configuration> | |
| - <version>4.1.0</version> | |
| + <version>4.2.0</version> | |
| <velocityBasedir>${project.basedir}/../../src/mdo</velocityBasedir> | |
| <models> | |
| <model>src/main/mdo/maven.mdo</model> | |
| @@ -59,6 +59,8 @@ under the License. | |
| <params> | |
| <param>packageModelV4=org.apache.maven.api.model</param> | |
| <param>isMavenModel=true</param> | |
| + <param>locationTracking=true</param> | |
| + <param>generateLocationClasses=true</param> | |
| </params> | |
| </configuration> | |
| <executions> | |
| diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocation.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocation.java | |
| deleted file mode 100644 | |
| index cde5c51b23..0000000000 | |
| --- a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocation.java | |
| +++ /dev/null | |
| @@ -1,210 +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.model; | |
| - | |
| -import java.io.Serializable; | |
| -import java.util.Collection; | |
| -import java.util.Collections; | |
| -import java.util.LinkedHashMap; | |
| -import java.util.Map; | |
| -import java.util.Objects; | |
| - | |
| -/** | |
| - * Represents the location of an element within a model source file. | |
| - * <p> | |
| - * This class tracks the line and column numbers of elements in source files like POM files. | |
| - * It's used for error reporting and debugging to help identify where specific model elements | |
| - * are defined in the source files. | |
| - * | |
| - * @since 4.0.0 | |
| - */ | |
| -public class InputLocation implements Serializable, InputLocationTracker { | |
| - private final int lineNumber; | |
| - private final int columnNumber; | |
| - private final InputSource source; | |
| - private final Map<Object, InputLocation> locations; | |
| - private final InputLocation importedFrom; | |
| - | |
| - public InputLocation(InputSource source) { | |
| - this.lineNumber = -1; | |
| - this.columnNumber = -1; | |
| - this.source = source; | |
| - this.locations = Collections.singletonMap(0, this); | |
| - this.importedFrom = null; | |
| - } | |
| - | |
| - public InputLocation(int lineNumber, int columnNumber) { | |
| - this(lineNumber, columnNumber, null, null); | |
| - } | |
| - | |
| - public InputLocation(int lineNumber, int columnNumber, InputSource source) { | |
| - this(lineNumber, columnNumber, source, null); | |
| - } | |
| - | |
| - public InputLocation(int lineNumber, int columnNumber, InputSource source, Object selfLocationKey) { | |
| - this.lineNumber = lineNumber; | |
| - this.columnNumber = columnNumber; | |
| - this.source = source; | |
| - this.locations = | |
| - selfLocationKey != null ? Collections.singletonMap(selfLocationKey, this) : Collections.emptyMap(); | |
| - this.importedFrom = null; | |
| - } | |
| - | |
| - public InputLocation(int lineNumber, int columnNumber, InputSource source, Map<Object, InputLocation> locations) { | |
| - this.lineNumber = lineNumber; | |
| - this.columnNumber = columnNumber; | |
| - this.source = source; | |
| - this.locations = ImmutableCollections.copy(locations); | |
| - this.importedFrom = null; | |
| - } | |
| - | |
| - public InputLocation(InputLocation original) { | |
| - this.lineNumber = original.lineNumber; | |
| - this.columnNumber = original.columnNumber; | |
| - this.source = original.source; | |
| - this.locations = original.locations; | |
| - this.importedFrom = original.importedFrom; | |
| - } | |
| - | |
| - public int getLineNumber() { | |
| - return lineNumber; | |
| - } | |
| - | |
| - public int getColumnNumber() { | |
| - return columnNumber; | |
| - } | |
| - | |
| - public InputSource getSource() { | |
| - return source; | |
| - } | |
| - | |
| - @Override | |
| - public InputLocation getLocation(Object key) { | |
| - Objects.requireNonNull(key, "key"); | |
| - return locations != null ? locations.get(key) : null; | |
| - } | |
| - | |
| - public Map<Object, InputLocation> getLocations() { | |
| - return locations; | |
| - } | |
| - | |
| - /** | |
| - * Gets the parent InputLocation where this InputLocation may have been imported from. | |
| - * Can return {@code null}. | |
| - * | |
| - * @return InputLocation | |
| - * @since 4.0.0 | |
| - */ | |
| - @Override | |
| - public InputLocation getImportedFrom() { | |
| - return importedFrom; | |
| - } | |
| - | |
| - /** | |
| - * Merges the {@code source} location into the {@code target} location. | |
| - * | |
| - * @param target the target location | |
| - * @param source the source location | |
| - * @param sourceDominant the boolean indicating of {@code source} is dominant compared to {@code target} | |
| - * @return the merged location | |
| - */ | |
| - public static InputLocation merge(InputLocation target, InputLocation source, boolean sourceDominant) { | |
| - if (source == null) { | |
| - return target; | |
| - } else if (target == null) { | |
| - return source; | |
| - } | |
| - | |
| - Map<Object, InputLocation> locations; | |
| - Map<Object, InputLocation> sourceLocations = source.locations; | |
| - Map<Object, InputLocation> targetLocations = target.locations; | |
| - if (sourceLocations == null) { | |
| - locations = targetLocations; | |
| - } else if (targetLocations == null) { | |
| - locations = sourceLocations; | |
| - } else { | |
| - locations = new LinkedHashMap<>(); | |
| - locations.putAll(sourceDominant ? targetLocations : sourceLocations); | |
| - locations.putAll(sourceDominant ? sourceLocations : targetLocations); | |
| - } | |
| - | |
| - return new InputLocation(-1, -1, InputSource.merge(source.getSource(), target.getSource()), locations); | |
| - } // -- InputLocation merge( InputLocation, InputLocation, boolean ) | |
| - | |
| - /** | |
| - * Merges the {@code source} location into the {@code target} location. | |
| - * This method is used when the locations refer to lists and also merges the indices. | |
| - * | |
| - * @param target the target location | |
| - * @param source the source location | |
| - * @param indices the list of integers for the indices | |
| - * @return the merged location | |
| - */ | |
| - public static InputLocation merge(InputLocation target, InputLocation source, Collection<Integer> indices) { | |
| - if (source == null) { | |
| - return target; | |
| - } else if (target == null) { | |
| - return source; | |
| - } | |
| - | |
| - Map<Object, InputLocation> locations; | |
| - Map<Object, InputLocation> sourceLocations = source.locations; | |
| - Map<Object, InputLocation> targetLocations = target.locations; | |
| - if (sourceLocations == null) { | |
| - locations = targetLocations; | |
| - } else if (targetLocations == null) { | |
| - locations = sourceLocations; | |
| - } else { | |
| - locations = new LinkedHashMap<>(); | |
| - for (int index : indices) { | |
| - InputLocation location; | |
| - if (index < 0) { | |
| - location = sourceLocations.get(~index); | |
| - } else { | |
| - location = targetLocations.get(index); | |
| - } | |
| - locations.put(locations.size(), location); | |
| - } | |
| - } | |
| - | |
| - return new InputLocation(-1, -1, InputSource.merge(source.getSource(), target.getSource()), locations); | |
| - } // -- InputLocation merge( InputLocation, InputLocation, java.util.Collection ) | |
| - | |
| - /** | |
| - * Class StringFormatter. | |
| - * | |
| - * @version $Revision$ $Date$ | |
| - */ | |
| - public interface StringFormatter { | |
| - | |
| - // -----------/ | |
| - // - Methods -/ | |
| - // -----------/ | |
| - | |
| - /** | |
| - * Method toString. | |
| - */ | |
| - String toString(InputLocation location); | |
| - } | |
| - | |
| - @Override | |
| - public String toString() { | |
| - return String.format("%s @ %d:%d", source != null ? source.getLocation() : "n/a", lineNumber, columnNumber); | |
| - } | |
| -} | |
| diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java | |
| deleted file mode 100644 | |
| index 65d43007d0..0000000000 | |
| --- a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java | |
| +++ /dev/null | |
| @@ -1,48 +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.model; | |
| - | |
| -/** | |
| - * Tracks input source locations for model fields. | |
| - * <p> | |
| - * Implementations provide a mapping from keys (typically field names or indices) to | |
| - * {@link InputLocation} instances to support precise error reporting and diagnostics. | |
| - * Keys must be non-null. | |
| - * | |
| - * @since 4.0.0 | |
| - */ | |
| -public interface InputLocationTracker { | |
| - /** | |
| - * Gets the location of the specified field in the input source. | |
| - * | |
| - * @param field the key of the field, must not be {@code null} | |
| - * @return the location of the field in the input source or {@code null} if unknown | |
| - * @throws NullPointerException if {@code field} is {@code null} | |
| - */ | |
| - InputLocation getLocation(Object field); | |
| - | |
| - /** | |
| - * Gets the parent InputLocation where this InputLocation may have been imported from. | |
| - * Can return {@code null}. | |
| - * | |
| - * @return InputLocation | |
| - * @since 4.0.0 | |
| - */ | |
| - InputLocation getImportedFrom(); | |
| -} | |
| diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputSource.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputSource.java | |
| deleted file mode 100644 | |
| index f4d5e7fc67..0000000000 | |
| --- a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputSource.java | |
| +++ /dev/null | |
| @@ -1,125 +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.model; | |
| - | |
| -import java.io.Serializable; | |
| -import java.util.Collection; | |
| -import java.util.List; | |
| -import java.util.Objects; | |
| -import java.util.stream.Collectors; | |
| -import java.util.stream.Stream; | |
| - | |
| -/** | |
| - * Represents the source of a model input, such as a POM file. | |
| - * <p> | |
| - * This class tracks the origin of model elements, including their location in source files | |
| - * and relationships between imported models. It's used for error reporting and debugging | |
| - * to help identify where specific model elements came from. | |
| - * | |
| - * @since 4.0.0 | |
| - */ | |
| -public class InputSource implements Serializable { | |
| - | |
| - private final String modelId; | |
| - private final String location; | |
| - private final List<InputSource> inputs; | |
| - private final InputLocation importedFrom; | |
| - | |
| - public InputSource(String modelId, String location) { | |
| - this(modelId, location, null); | |
| - } | |
| - | |
| - public InputSource(String modelId, String location, InputLocation importedFrom) { | |
| - this.modelId = modelId; | |
| - this.location = location; | |
| - this.inputs = null; | |
| - this.importedFrom = importedFrom; | |
| - } | |
| - | |
| - public InputSource(Collection<InputSource> inputs) { | |
| - this.modelId = null; | |
| - this.location = null; | |
| - this.inputs = ImmutableCollections.copy(inputs); | |
| - this.importedFrom = null; | |
| - } | |
| - | |
| - /** | |
| - * Get the path/URL of the POM or {@code null} if unknown. | |
| - * | |
| - * @return the location | |
| - */ | |
| - public String getLocation() { | |
| - return this.location; | |
| - } | |
| - | |
| - /** | |
| - * Get the identifier of the POM in the format {@code <groupId>:<artifactId>:<version>}. | |
| - * | |
| - * @return the model id | |
| - */ | |
| - public String getModelId() { | |
| - return this.modelId; | |
| - } | |
| - | |
| - /** | |
| - * Gets the parent InputLocation where this InputLocation may have been imported from. | |
| - * Can return {@code null}. | |
| - * | |
| - * @return InputLocation | |
| - * @since 4.0.0 | |
| - */ | |
| - public InputLocation getImportedFrom() { | |
| - return importedFrom; | |
| - } | |
| - | |
| - @Override | |
| - public boolean equals(Object o) { | |
| - if (this == o) { | |
| - return true; | |
| - } | |
| - if (o == null || getClass() != o.getClass()) { | |
| - return false; | |
| - } | |
| - InputSource that = (InputSource) o; | |
| - return Objects.equals(modelId, that.modelId) | |
| - && Objects.equals(location, that.location) | |
| - && Objects.equals(inputs, that.inputs); | |
| - } | |
| - | |
| - @Override | |
| - public int hashCode() { | |
| - return Objects.hash(modelId, location, inputs); | |
| - } | |
| - | |
| - Stream<InputSource> sources() { | |
| - return inputs != null ? inputs.stream() : Stream.of(this); | |
| - } | |
| - | |
| - @Override | |
| - public String toString() { | |
| - if (inputs != null) { | |
| - return inputs.stream().map(InputSource::toString).collect(Collectors.joining(", ", "merged[", "]")); | |
| - } | |
| - return getModelId() + " " + getLocation(); | |
| - } | |
| - | |
| - public static InputSource merge(InputSource src1, InputSource src2) { | |
| - return new InputSource(Stream.concat(src1.sources(), src2.sources()).collect(Collectors.toSet())); | |
| - } | |
| -} | |
| diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/ModelObjectProcessor.java b/api/maven-api-model/src/main/java/org/apache/maven/api/model/ModelObjectProcessor.java | |
| new file mode 100644 | |
| index 0000000000..f1030709c0 | |
| --- /dev/null | |
| +++ b/api/maven-api-model/src/main/java/org/apache/maven/api/model/ModelObjectProcessor.java | |
| @@ -0,0 +1,114 @@ | |
| +/* | |
| + * 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.model; | |
| + | |
| +import java.util.ServiceLoader; | |
| +import java.util.concurrent.atomic.AtomicReference; | |
| + | |
| +/** | |
| + * A pluggable service for processing model objects during model building. | |
| + * | |
| + * <p>This service allows implementations to:</p> | |
| + * <ul> | |
| + * <li>Pool identical objects to reduce memory footprint</li> | |
| + * <li>Intern objects for faster equality comparisons</li> | |
| + * <li>Apply custom optimization strategies</li> | |
| + * <li>Transform or modify objects during building</li> | |
| + * </ul> | |
| + * | |
| + * <p>Implementations are discovered via the Java ServiceLoader mechanism and should | |
| + * be registered in {@code META-INF/services/org.apache.maven.api.model.ModelObjectProcessor}.</p> | |
| + * | |
| + * <p>The service is called during model building for all model objects, allowing | |
| + * implementations to decide which objects to process and how to optimize them.</p> | |
| + * | |
| + * @since 4.0.0 | |
| + */ | |
| +public interface ModelObjectProcessor { | |
| + | |
| + /** | |
| + * Process a model object, potentially returning a pooled or optimized version. | |
| + * | |
| + * <p>This method is called during model building for various model objects. | |
| + * Implementations can:</p> | |
| + * <ul> | |
| + * <li>Return the same object if no processing is desired</li> | |
| + * <li>Return a pooled equivalent object to reduce memory usage</li> | |
| + * <li>Return a modified or optimized version of the object</li> | |
| + * </ul> | |
| + * | |
| + * <p>The implementation must ensure that the returned object is functionally | |
| + * equivalent to the input object from the perspective of the Maven model.</p> | |
| + * | |
| + * @param <T> the type of the model object | |
| + * @param object the model object to process | |
| + * @return the processed object (may be the same instance, a pooled instance, or a modified instance) | |
| + * @throws IllegalArgumentException if the object cannot be processed | |
| + */ | |
| + <T> T process(T object); | |
| + | |
| + /** | |
| + * Process a model object using the first available processor implementation. | |
| + * | |
| + * <p>This method discovers processor implementations via ServiceLoader and | |
| + * uses the first one found. If no implementations are available, the object | |
| + * is returned unchanged. The processor is cached for performance.</p> | |
| + * | |
| + * @param <T> the type of the model object | |
| + * @param object the model object to process | |
| + * @return the processed object | |
| + */ | |
| + static <T> T processObject(T object) { | |
| + class ProcessorHolder { | |
| + /** | |
| + * Cached processor instance for performance. | |
| + */ | |
| + private static final AtomicReference<ModelObjectProcessor> CACHED_PROCESSOR = new AtomicReference<>(); | |
| + } | |
| + | |
| + ModelObjectProcessor processor = ProcessorHolder.CACHED_PROCESSOR.get(); | |
| + if (processor == null) { | |
| + processor = loadProcessor(); | |
| + ProcessorHolder.CACHED_PROCESSOR.compareAndSet(null, processor); | |
| + processor = ProcessorHolder.CACHED_PROCESSOR.get(); | |
| + } | |
| + return processor.process(object); | |
| + } | |
| + | |
| + /** | |
| + * Load the first available processor implementation. | |
| + */ | |
| + private static ModelObjectProcessor loadProcessor() { | |
| + /* | |
| + * No-op processor that returns objects unchanged. | |
| + */ | |
| + class NoOpProcessor implements ModelObjectProcessor { | |
| + @Override | |
| + public <T> T process(T object) { | |
| + return object; | |
| + } | |
| + } | |
| + | |
| + ServiceLoader<ModelObjectProcessor> loader = ServiceLoader.load(ModelObjectProcessor.class); | |
| + for (ModelObjectProcessor processor : loader) { | |
| + return processor; | |
| + } | |
| + return new NoOpProcessor(); | |
| + } | |
| +} | |
| diff --git a/api/maven-api-model/src/main/mdo/maven.mdo b/api/maven-api-model/src/main/mdo/maven.mdo | |
| index 48df570aec..8863ef0164 100644 | |
| --- a/api/maven-api-model/src/main/mdo/maven.mdo | |
| +++ b/api/maven-api-model/src/main/mdo/maven.mdo | |
| @@ -111,6 +111,20 @@ | |
| </association> | |
| </field> | |
| + <!-- ====================================================================== --> | |
| + <!-- Mixins --> | |
| + <!-- ====================================================================== --> | |
| + | |
| + <field xdoc.separator="blank"> | |
| + <name>mixins</name> | |
| + <version>4.2.0+</version> | |
| + <description>Mixins...</description> | |
| + <association> | |
| + <type>Mixin</type> | |
| + <multiplicity>*</multiplicity> | |
| + </association> | |
| + </field> | |
| + | |
| <!-- ====================================================================== --> | |
| <!-- groupId/artifactId/Version/Packaging --> | |
| <!-- ====================================================================== --> | |
| @@ -526,7 +540,7 @@ | |
| <fields> | |
| <field xdoc.separator="blank"> | |
| <name>modules</name> | |
| - <version>4.0.0/4.1.0</version> | |
| + <version>4.0.0/4.2.0</version> | |
| <description> | |
| @deprecated Use {@link #subprojects} instead. | |
| </description> | |
| @@ -540,7 +554,7 @@ | |
| </field> | |
| <field xdoc.separator="blank"> | |
| <name>subprojects</name> | |
| - <version>4.1.0</version> | |
| + <version>4.1.0+</version> | |
| <description>The subprojects (formerly called modules) to build as a part of this | |
| project. Each subproject listed is a relative path to the directory containing the subproject. | |
| To be consistent with the way default URLs are calculated from parent, it is recommended | |
| @@ -1836,6 +1850,23 @@ | |
| </codeSegments> | |
| </class> | |
| + <class> | |
| + <name>Mixin</name> | |
| + <version>4.1.0+</version> | |
| + <superClass>Parent</superClass> | |
| + <fields> | |
| + <field> | |
| + <name>classifier</name> | |
| + <version>4.1.0+</version> | |
| + <type>String</type> | |
| + </field> | |
| + <field> | |
| + <name>extension</name> | |
| + <version>4.1.0+</version> | |
| + <type>String</type> | |
| + </field> | |
| + </fields> | |
| + </class> | |
| <class> | |
| <name>Scm</name> | |
| <version>4.0.0+</version> | |
| diff --git a/api/maven-api-plugin/pom.xml b/api/maven-api-plugin/pom.xml | |
| index ecc36001df..2e9c1706ed 100644 | |
| --- a/api/maven-api-plugin/pom.xml | |
| +++ b/api/maven-api-plugin/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-plugin</artifactId> | |
| diff --git a/api/maven-api-settings/pom.xml b/api/maven-api-settings/pom.xml | |
| index 426a03af58..a73c2ce1de 100644 | |
| --- a/api/maven-api-settings/pom.xml | |
| +++ b/api/maven-api-settings/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-settings</artifactId> | |
| diff --git a/api/maven-api-spi/pom.xml b/api/maven-api-spi/pom.xml | |
| index 9e11c42fe5..19c06b04a2 100644 | |
| --- a/api/maven-api-spi/pom.xml | |
| +++ b/api/maven-api-spi/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-spi</artifactId> | |
| diff --git a/api/maven-api-toolchain/pom.xml b/api/maven-api-toolchain/pom.xml | |
| index 108275549d..64d53566d3 100644 | |
| --- a/api/maven-api-toolchain/pom.xml | |
| +++ b/api/maven-api-toolchain/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-toolchain</artifactId> | |
| diff --git a/api/maven-api-xml/pom.xml b/api/maven-api-xml/pom.xml | |
| index 5ce4808d44..4842b6541f 100644 | |
| --- a/api/maven-api-xml/pom.xml | |
| +++ b/api/maven-api-xml/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-api</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api-xml</artifactId> | |
| diff --git a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java | |
| index ca1c4707e8..80abc22030 100644 | |
| --- a/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java | |
| +++ b/api/maven-api-xml/src/main/java/org/apache/maven/api/xml/ImmutableCollections.java | |
| @@ -19,45 +19,31 @@ | |
| package org.apache.maven.api.xml; | |
| import java.io.Serializable; | |
| -import java.util.AbstractList; | |
| import java.util.AbstractMap; | |
| import java.util.AbstractSet; | |
| import java.util.Collection; | |
| -import java.util.Comparator; | |
| import java.util.Iterator; | |
| import java.util.List; | |
| -import java.util.ListIterator; | |
| import java.util.Map; | |
| import java.util.NoSuchElementException; | |
| -import java.util.Properties; | |
| -import java.util.RandomAccess; | |
| import java.util.Set; | |
| import java.util.function.BiFunction; | |
| import java.util.function.Function; | |
| import java.util.function.Predicate; | |
| -import java.util.function.UnaryOperator; | |
| +/** | |
| + * This should be removed when https://bugs.openjdk.org/browse/JDK-8323729 | |
| + * is released in our minimum JDK. | |
| + */ | |
| class ImmutableCollections { | |
| - private static final List<?> EMPTY_LIST = new AbstractImmutableList<Object>() { | |
| - @Override | |
| - public Object get(int index) { | |
| - throw new IndexOutOfBoundsException(); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return 0; | |
| - } | |
| - }; | |
| - | |
| - private static final Map<?, ?> EMPTY_MAP = new AbstractImmutableMap<Object, Object>() { | |
| + private static final Map<?, ?> EMPTY_MAP = new AbstractImmutableMap<>() { | |
| @Override | |
| public Set<Entry<Object, Object>> entrySet() { | |
| - return new AbstractImmutableSet<Entry<Object, Object>>() { | |
| + return new AbstractImmutableSet<>() { | |
| @Override | |
| public Iterator<Entry<Object, Object>> iterator() { | |
| - return new Iterator<Entry<Object, Object>>() { | |
| + return new Iterator<>() { | |
| @Override | |
| public boolean hasNext() { | |
| return false; | |
| @@ -78,31 +64,8 @@ public int size() { | |
| } | |
| }; | |
| - static <E> List<E> copy(Collection<E> collection) { | |
| - if (collection == null) { | |
| - return emptyList(); | |
| - } else if (collection instanceof AbstractImmutableList) { | |
| - return (List<E>) collection; | |
| - } else { | |
| - return switch (collection.size()) { | |
| - case 0 -> emptyList(); | |
| - case 1 -> singletonList(collection.iterator().next()); | |
| - case 2 -> { | |
| - Iterator<E> it = collection.iterator(); | |
| - yield new List2<>(it.next(), it.next()); | |
| - } | |
| - default -> new ListN<>(collection); | |
| - }; | |
| - } | |
| - } | |
| - | |
| - @SuppressWarnings("unchecked") | |
| - static <E> List<E> emptyList() { | |
| - return (List<E>) EMPTY_LIST; | |
| - } | |
| - | |
| - static <E> List<E> singletonList(E element) { | |
| - return new List1<>(element); | |
| + static <E1, E2 extends E1> List<E1> copy(Collection<E2> collection) { | |
| + return collection == null ? List.of() : List.copyOf(collection); | |
| } | |
| static <K, V> Map<K, V> copy(Map<K, V> map) { | |
| @@ -111,14 +74,15 @@ static <K, V> Map<K, V> copy(Map<K, V> map) { | |
| } else if (map instanceof AbstractImmutableMap) { | |
| return map; | |
| } else { | |
| - return switch (map.size()) { | |
| - case 0 -> emptyMap(); | |
| - case 1 -> { | |
| + switch (map.size()) { | |
| + case 0: | |
| + return emptyMap(); | |
| + case 1: | |
| Map.Entry<K, V> entry = map.entrySet().iterator().next(); | |
| - yield singletonMap(entry.getKey(), entry.getValue()); | |
| - } | |
| - default -> new MapN<>(map); | |
| - }; | |
| + return singletonMap(entry.getKey(), entry.getValue()); | |
| + default: | |
| + return new MapN<>(map); | |
| + } | |
| } | |
| } | |
| @@ -131,315 +95,6 @@ static <K, V> Map<K, V> singletonMap(K key, V value) { | |
| return new Map1<>(key, value); | |
| } | |
| - static Properties copy(Properties properties) { | |
| - if (properties instanceof ROProperties) { | |
| - return properties; | |
| - } | |
| - return new ROProperties(properties); | |
| - } | |
| - | |
| - private static class List1<E> extends AbstractImmutableList<E> { | |
| - private final E element; | |
| - | |
| - private List1(E element) { | |
| - this.element = element; | |
| - } | |
| - | |
| - @Override | |
| - public E get(int index) { | |
| - if (index == 0) { | |
| - return element; | |
| - } | |
| - throw outOfBounds(index); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return 1; | |
| - } | |
| - } | |
| - | |
| - private static class List2<E> extends AbstractImmutableList<E> { | |
| - private final E element1; | |
| - private final E element2; | |
| - | |
| - private List2(E element1, E element2) { | |
| - this.element1 = element1; | |
| - this.element2 = element2; | |
| - } | |
| - | |
| - @Override | |
| - public E get(int index) { | |
| - if (index == 0) { | |
| - return element1; | |
| - } else if (index == 1) { | |
| - return element2; | |
| - } | |
| - throw outOfBounds(index); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return 2; | |
| - } | |
| - } | |
| - | |
| - private static class ListN<E> extends AbstractImmutableList<E> { | |
| - private final Object[] elements; | |
| - | |
| - private ListN(Collection<E> elements) { | |
| - this.elements = elements.toArray(); | |
| - } | |
| - | |
| - @SuppressWarnings("unchecked") | |
| - @Override | |
| - public E get(int index) { | |
| - return (E) elements[index]; | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return elements.length; | |
| - } | |
| - } | |
| - | |
| - private abstract static class AbstractImmutableList<E> extends AbstractList<E> | |
| - implements RandomAccess, Serializable { | |
| - @Override | |
| - public boolean add(E e) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean remove(Object o) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean addAll(Collection<? extends E> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean removeAll(Collection<?> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean retainAll(Collection<?> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void clear() { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean removeIf(Predicate<? super E> filter) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void replaceAll(UnaryOperator<E> operator) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void sort(Comparator<? super E> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Iterator<E> iterator() { | |
| - return new Itr(0); | |
| - } | |
| - | |
| - @Override | |
| - public ListIterator<E> listIterator() { | |
| - return new Itr(0); | |
| - } | |
| - | |
| - @Override | |
| - public ListIterator<E> listIterator(int index) { | |
| - if (index < 0 || index > size()) { | |
| - throw outOfBounds(index); | |
| - } | |
| - return new Itr(index); | |
| - } | |
| - | |
| - @Override | |
| - public List<E> subList(int fromIndex, int toIndex) { | |
| - if (fromIndex < 0) { | |
| - throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); | |
| - } | |
| - if (toIndex > size()) { | |
| - throw new IndexOutOfBoundsException("toIndex = " + toIndex); | |
| - } | |
| - if (fromIndex > toIndex) { | |
| - throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); | |
| - } | |
| - return new SubList(fromIndex, toIndex); | |
| - } | |
| - | |
| - protected IndexOutOfBoundsException outOfBounds(int index) { | |
| - return new IndexOutOfBoundsException("Index: " + index + ", Size: " + size()); | |
| - } | |
| - | |
| - private class SubList extends AbstractImmutableList<E> { | |
| - private final int fromIndex; | |
| - private final int toIndex; | |
| - | |
| - private SubList(int fromIndex, int toIndex) { | |
| - this.fromIndex = fromIndex; | |
| - this.toIndex = toIndex; | |
| - } | |
| - | |
| - @Override | |
| - public E get(int index) { | |
| - if (index < 0 || index > size()) { | |
| - throw outOfBounds(index); | |
| - } | |
| - return AbstractImmutableList.this.get(fromIndex + index); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return toIndex - fromIndex; | |
| - } | |
| - } | |
| - | |
| - private class Itr implements ListIterator<E> { | |
| - int index; | |
| - | |
| - private Itr(int index) { | |
| - this.index = index; | |
| - } | |
| - | |
| - @Override | |
| - public boolean hasNext() { | |
| - return index < size(); | |
| - } | |
| - | |
| - @Override | |
| - public E next() { | |
| - return get(index++); | |
| - } | |
| - | |
| - @Override | |
| - public boolean hasPrevious() { | |
| - return index > 0; | |
| - } | |
| - | |
| - @Override | |
| - public E previous() { | |
| - return get(--index); | |
| - } | |
| - | |
| - @Override | |
| - public int nextIndex() { | |
| - return index; | |
| - } | |
| - | |
| - @Override | |
| - public int previousIndex() { | |
| - return index - 1; | |
| - } | |
| - | |
| - @Override | |
| - public void remove() { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void set(E e) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void add(E e) { | |
| - throw uoe(); | |
| - } | |
| - } | |
| - } | |
| - | |
| - private static class ROProperties extends Properties { | |
| - private ROProperties(Properties props) { | |
| - super(); | |
| - if (props != null) { | |
| - // Do not use super.putAll, as it may delegate to put which throws an UnsupportedOperationException | |
| - for (Map.Entry<Object, Object> e : props.entrySet()) { | |
| - super.put(e.getKey(), e.getValue()); | |
| - } | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public Object put(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object remove(Object key) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void putAll(Map<?, ?> t) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void clear() { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object putIfAbsent(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean remove(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean replace(Object key, Object oldValue, Object newValue) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object replace(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object computeIfAbsent(Object key, Function<? super Object, ?> mappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object compute(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object merge(Object key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - } | |
| - | |
| private static class Map1<K, V> extends AbstractImmutableMap<K, V> { | |
| private final Entry<K, V> entry; | |
| @@ -449,10 +104,10 @@ private Map1(K key, V value) { | |
| @Override | |
| public Set<Entry<K, V>> entrySet() { | |
| - return new AbstractImmutableSet<Entry<K, V>>() { | |
| + return new AbstractImmutableSet<>() { | |
| @Override | |
| public Iterator<Entry<K, V>> iterator() { | |
| - return new Iterator<Entry<K, V>>() { | |
| + return new Iterator<>() { | |
| int index = 0; | |
| @Override | |
| @@ -495,10 +150,10 @@ private MapN(Map<K, V> map) { | |
| @Override | |
| public Set<Entry<K, V>> entrySet() { | |
| - return new AbstractImmutableSet<Entry<K, V>>() { | |
| + return new AbstractImmutableSet<>() { | |
| @Override | |
| public Iterator<Entry<K, V>> iterator() { | |
| - return new Iterator<Entry<K, V>>() { | |
| + return new Iterator<>() { | |
| int index = 0; | |
| @Override | |
| diff --git a/api/pom.xml b/api/pom.xml | |
| index ba5c47f3a1..b90c9f605c 100644 | |
| --- a/api/pom.xml | |
| +++ b/api/pom.xml | |
| @@ -23,7 +23,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-api</artifactId> | |
| diff --git a/compat/maven-artifact/pom.xml b/compat/maven-artifact/pom.xml | |
| index 381253f437..42d5bf80f5 100644 | |
| --- a/compat/maven-artifact/pom.xml | |
| +++ b/compat/maven-artifact/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-artifact</artifactId> | |
| diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java | |
| index af1203095b..2dc3cf5ac0 100644 | |
| --- a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java | |
| +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java | |
| @@ -184,7 +184,7 @@ private void validateIdentity() { | |
| } | |
| public static boolean empty(String value) { | |
| - return (value == null) || (value.trim().length() < 1); | |
| + return value == null || value.isBlank(); | |
| } | |
| @Override | |
| diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java | |
| index e92e0bb450..df3c034528 100644 | |
| --- a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java | |
| +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/ComparableVersion.java | |
| @@ -607,7 +607,7 @@ public int compareTo(Item item) { | |
| public String toString() { | |
| StringBuilder buffer = new StringBuilder(); | |
| for (Item item : this) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append((item instanceof ListItem) ? '-' : '.'); | |
| } | |
| buffer.append(item); | |
| diff --git a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java | |
| index 1869dcc584..152e8365c6 100644 | |
| --- a/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java | |
| +++ b/compat/maven-artifact/src/main/java/org/apache/maven/artifact/versioning/VersionRange.java | |
| @@ -255,7 +255,7 @@ public VersionRange restrict(VersionRange restriction) { | |
| } | |
| ArtifactVersion version = null; | |
| - if (restrictions.size() > 0) { | |
| + if (!restrictions.isEmpty()) { | |
| for (Restriction r : restrictions) { | |
| if (recommendedVersion != null && r.containsVersion(recommendedVersion)) { | |
| // if we find the original, use that | |
| @@ -398,7 +398,7 @@ public ArtifactVersion getSelectedVersion(Artifact artifact) throws OverConstrai | |
| if (recommendedVersion != null) { | |
| version = recommendedVersion; | |
| } else { | |
| - if (restrictions.size() == 0) { | |
| + if (restrictions.isEmpty()) { | |
| throw new OverConstrainedVersionException("The artifact has no valid ranges", artifact); | |
| } | |
| @@ -412,7 +412,7 @@ public boolean isSelectedVersionKnown(Artifact artifact) throws OverConstrainedV | |
| if (recommendedVersion != null) { | |
| value = true; | |
| } else { | |
| - if (restrictions.size() == 0) { | |
| + if (restrictions.isEmpty()) { | |
| throw new OverConstrainedVersionException("The artifact has no valid ranges", artifact); | |
| } | |
| } | |
| diff --git a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java | |
| index 370b0f0c5c..c1530cbe20 100644 | |
| --- a/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java | |
| +++ b/compat/maven-artifact/src/test/java/org/apache/maven/artifact/versioning/DefaultArtifactVersionTest.java | |
| @@ -176,7 +176,7 @@ void testSnapshotVsReleases() { | |
| void testHashCode() { | |
| ArtifactVersion v1 = newArtifactVersion("1"); | |
| ArtifactVersion v2 = newArtifactVersion("1.0"); | |
| - assertTrue(v1.equals(v2)); | |
| + assertTrue(v1.equals(v2), "Expected " + v1 + " to equal " + v2); | |
| assertEquals(v1.hashCode(), v2.hashCode()); | |
| } | |
| diff --git a/compat/maven-builder-support/pom.xml b/compat/maven-builder-support/pom.xml | |
| index 2297ad4d72..2f4b6bd61a 100644 | |
| --- a/compat/maven-builder-support/pom.xml | |
| +++ b/compat/maven-builder-support/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-builder-support</artifactId> | |
| diff --git a/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java b/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java | |
| index ad5c72e5a2..8cfa77d3c7 100644 | |
| --- a/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java | |
| +++ b/compat/maven-builder-support/src/main/java/org/apache/maven/building/DefaultProblem.java | |
| @@ -82,21 +82,21 @@ public String getLocation() { | |
| StringBuilder buffer = new StringBuilder(256); | |
| if (!getSource().isEmpty()) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append(getSource()); | |
| } | |
| if (getLineNumber() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append("line ").append(getLineNumber()); | |
| } | |
| if (getColumnNumber() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append("column ").append(getColumnNumber()); | |
| diff --git a/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java b/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java | |
| index 6705992bef..140035bb03 100644 | |
| --- a/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java | |
| +++ b/compat/maven-builder-support/src/test/java/org/apache/maven/building/ProblemCollectorFactoryTest.java | |
| @@ -19,6 +19,7 @@ | |
| package org.apache.maven.building; | |
| import java.util.Collections; | |
| +import java.util.List; | |
| import org.junit.jupiter.api.Test; | |
| @@ -38,4 +39,18 @@ void testNewInstance() { | |
| assertEquals(0, collector1.getProblems().size()); | |
| assertEquals(1, collector2.getProblems().size()); | |
| } | |
| + | |
| + @Test | |
| + void testAddProblem() { | |
| + ProblemCollector collector = ProblemCollectorFactory.newInstance(null); | |
| + collector.setSource("pom.xml"); | |
| + collector.add(Problem.Severity.ERROR, "Error message", 10, 5, null); | |
| + collector.add(Problem.Severity.WARNING, "Warning message", 15, 3, null); | |
| + | |
| + List<Problem> problems = collector.getProblems(); | |
| + assertEquals(2, problems.size(), "Should collect both problems"); | |
| + assertEquals(Problem.Severity.ERROR, problems.get(0).getSeverity(), "First problem should be ERROR"); | |
| + assertEquals("Error message", problems.get(0).getMessage(), "First problem should have correct message"); | |
| + assertEquals(Problem.Severity.WARNING, problems.get(1).getSeverity(), "Second problem should be WARNING"); | |
| + } | |
| } | |
| diff --git a/compat/maven-compat/pom.xml b/compat/maven-compat/pom.xml | |
| index ee2f6cdd5b..e102a5c674 100644 | |
| --- a/compat/maven-compat/pom.xml | |
| +++ b/compat/maven-compat/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-compat</artifactId> | |
| @@ -161,7 +161,7 @@ under the License. | |
| <dependency> | |
| <groupId>org.codehaus.plexus</groupId> | |
| <artifactId>plexus-component-annotations</artifactId> | |
| - <version>2.2.0</version> | |
| + <version>2.1.0</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.eclipse.sisu</groupId> | |
| @@ -174,11 +174,7 @@ under the License. | |
| <artifactId>junit-jupiter-api</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| + | |
| <dependency> | |
| <groupId>com.google.inject</groupId> | |
| <artifactId>guice</artifactId> | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java | |
| index 66d11b5187..568bc263e8 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/artifact/MavenMetadataSource.java | |
| @@ -203,8 +203,8 @@ public ResolutionGroup retrieve(MetadataResolutionRequest request) throws Artifa | |
| if (session.getProjects() != null) { | |
| pomRepositories = session.getProjects().stream() | |
| .filter(p -> artifact.equals(p.getArtifact())) | |
| - .map(MavenProject::getRemoteArtifactRepositories) | |
| .findFirst() | |
| + .map(MavenProject::getRemoteArtifactRepositories) | |
| .orElseGet(() -> getRepositoriesFromModel(repositorySession, model)); | |
| } else { | |
| pomRepositories = getRepositoriesFromModel(repositorySession, model); | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java | |
| index c9d941a446..c7cea8d13b 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java | |
| @@ -264,7 +264,9 @@ private void traverseObjectWithParents(Class<?> cls, Object target) throws Model | |
| private boolean isQualifiedForInterpolation(Class<?> cls) { | |
| return !cls.getPackage().getName().startsWith("java") | |
| - && !cls.getPackage().getName().startsWith("sun.nio.fs"); | |
| + && !cls.getPackage().getName().startsWith("sun.nio.fs") | |
| + // org.apache.maven.api.model.InputLocation can be self-referencing | |
| + && !cls.getName().equals("org.apache.maven.api.model.InputLocation"); | |
| } | |
| private boolean isQualifiedForInterpolation(Field field, Class<?> fieldType) { | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java | |
| index feae2be0ca..693227ebcc 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/project/validation/ModelValidationResult.java | |
| @@ -59,7 +59,7 @@ public String toString() { | |
| } | |
| public String render(String indentation) { | |
| - if (messages.size() == 0) { | |
| + if (messages.isEmpty()) { | |
| return indentation + "There were no validation errors."; | |
| } | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java | |
| index 4a0b44fb12..37c6b9f423 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/ClasspathContainer.java | |
| @@ -70,7 +70,7 @@ public List<ArtifactMetadata> getClasspath() { | |
| // ------------------------------------------------------------------------------------------- | |
| public MetadataTreeNode getClasspathAsTree() throws MetadataResolutionException { | |
| - if (classpath == null || classpath.size() < 1) { | |
| + if (classpath == null || classpath.isEmpty()) { | |
| return null; | |
| } | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java | |
| index e0b5fb64e6..e7bc8f9f96 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultClasspathTransformation.java | |
| @@ -104,7 +104,7 @@ protected void visit(MetadataGraphVertex node) { | |
| List<MetadataGraphEdge> exits = graph.getExcidentEdges(node); | |
| - if (exits != null && exits.size() > 0) { | |
| + if (exits != null && !exits.isEmpty()) { | |
| MetadataGraphEdge[] sortedExits = exits.toArray(new MetadataGraphEdge[0]); | |
| Arrays.sort(sortedExits, (e1, e2) -> { | |
| if (e1.getDepth() == e2.getDepth()) { | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java | |
| index 35a0e06168..d4d3a1e3a1 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/DefaultGraphConflictResolver.java | |
| @@ -144,7 +144,7 @@ private MetadataGraph findLinkedSubgraph(MetadataGraph g) { | |
| } | |
| } | |
| - if (dropList.size() < 1) { | |
| + if (dropList.isEmpty()) { | |
| return g; | |
| } | |
| @@ -167,7 +167,7 @@ private void visit(MetadataGraphVertex from, List<MetadataGraphVertex> visited, | |
| List<MetadataGraphEdge> exitList = graph.getExcidentEdges(from); | |
| // String s = "|---> "+from.getMd().toString()+" - "+(exitList == null ? -1 : exitList.size()) + " exit links"; | |
| - if (exitList != null && exitList.size() > 0) { | |
| + if (exitList != null && !exitList.isEmpty()) { | |
| for (MetadataGraphEdge e : graph.getExcidentEdges(from)) { | |
| visit(e.getTarget(), visited, graph); | |
| } | |
| diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java | |
| index e6e0024f80..fe785515a1 100644 | |
| --- a/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java | |
| +++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/metadata/MetadataGraph.java | |
| @@ -152,7 +152,7 @@ private void processTreeNodes(MetadataGraphVertex parentVertex, MetadataTreeNode | |
| } | |
| // ------------------------------------------------------------------------ | |
| public MetadataGraphVertex findVertex(ArtifactMetadata md) { | |
| - if (md == null || vertices == null || vertices.size() < 1) { | |
| + if (md == null || vertices == null || vertices.isEmpty()) { | |
| return null; | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java b/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java | |
| index 723ef0111d..7f4f445aa2 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/ProjectDependenciesResolverTest.java | |
| @@ -32,9 +32,8 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.endsWith; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @Deprecated | |
| class ProjectDependenciesResolverTest extends AbstractCoreMavenComponentTestCase { | |
| @@ -82,6 +81,6 @@ void testSystemScopeDependencyIsPresentInTheCompileClasspathElements() throws Ex | |
| @SuppressWarnings("deprecation") | |
| List<Artifact> artifacts = project.getCompileArtifacts(); | |
| assertEquals(1, artifacts.size()); | |
| - assertThat(artifacts.get(0).getFile().getName(), endsWith("tools.jar")); | |
| + assertTrue(artifacts.get(0).getFile().getName().endsWith("tools.jar")); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java | |
| index 0435e11952..367ec6bcd2 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/deployer/ArtifactDeployerTest.java | |
| @@ -67,7 +67,7 @@ void testArtifactInstallation() throws Exception { | |
| ArtifactRepository remoteRepository = remoteRepository(); | |
| File deployedFile = new File(remoteRepository.getBasedir(), remoteRepository.pathOf(artifact)); | |
| - assertTrue(deployedFile.exists()); | |
| + assertTrue(deployedFile.exists(), "Expected " + deployedFile + ".exists() to return true"); | |
| assertEquals("dummy", new String(Files.readAllBytes(deployedFile.toPath()), StandardCharsets.UTF_8).trim()); | |
| } finally { | |
| sessionScope.exit(); | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java | |
| index f529ae822b..2b005e1599 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/repository/MavenArtifactRepositoryTest.java | |
| @@ -47,10 +47,10 @@ void testHashCodeEquals() { | |
| assertTrue(r1.hashCode() == r2.hashCode()); | |
| assertFalse(r1.hashCode() == r3.hashCode()); | |
| - assertTrue(r1.equals(r2)); | |
| - assertTrue(r2.equals(r1)); | |
| + assertTrue(r1.equals(r2), "Expected " + r1 + " to equal " + r2); | |
| + assertTrue(r2.equals(r1), "Expected " + r2 + " to equal " + r1); | |
| - assertFalse(r1.equals(r3)); | |
| - assertFalse(r3.equals(r1)); | |
| + assertFalse(r1.equals(r3), "Expected " + r1 + " to not equal " + r3); | |
| + assertFalse(r3.equals(r1), "Expected " + r3 + " to not equal " + r1); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java | |
| index fe0343c857..cfcd65eab4 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/AndArtifactFilterTest.java | |
| @@ -42,11 +42,11 @@ void testEquals() { | |
| AndArtifactFilter filter2 = new AndArtifactFilter(Arrays.asList(newSubFilter())); | |
| - assertFalse(filter1.equals(null)); | |
| - assertTrue(filter1.equals(filter1)); | |
| + assertFalse(filter1.equals(null), "Expected " + filter1 + " to not equal " + null); | |
| + assertTrue(filter1.equals(filter1), "Expected " + filter1 + " to equal " + filter1); | |
| assertEquals(filter1.hashCode(), filter1.hashCode()); | |
| - assertFalse(filter1.equals(filter2)); | |
| - assertFalse(filter2.equals(filter1)); | |
| + assertFalse(filter1.equals(filter2), "Expected " + filter1 + " to not equal " + filter2); | |
| + assertFalse(filter2.equals(filter1), "Expected " + filter2 + " to not equal " + filter1); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java | |
| index 53a26db4ea..3ccb13e85a 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/FilterHashEqualsTest.java | |
| @@ -37,12 +37,12 @@ void testIncludesExcludesArtifactFilter() { | |
| IncludesArtifactFilter f2 = new IncludesArtifactFilter(patterns); | |
| - assertTrue(f1.equals(f2)); | |
| - assertTrue(f2.equals(f1)); | |
| + assertTrue(f1.equals(f2), "Expected " + f1 + " to equal " + f2); | |
| + assertTrue(f2.equals(f1), "Expected " + f2 + " to equal " + f1); | |
| assertTrue(f1.hashCode() == f2.hashCode()); | |
| IncludesArtifactFilter f3 = new IncludesArtifactFilter(Arrays.asList("d", "c", "e")); | |
| - assertTrue(f1.equals(f3)); | |
| + assertTrue(f1.equals(f3), "Expected " + f1 + " to equal " + f3); | |
| assertTrue(f1.hashCode() == f3.hashCode()); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java | |
| index 6c8cda6802..2ca82a783b 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/resolver/filter/OrArtifactFilterTest.java | |
| @@ -43,11 +43,11 @@ void testEquals() { | |
| OrArtifactFilter filter2 = new OrArtifactFilter(Arrays.asList(newSubFilter())); | |
| - assertFalse(filter1.equals(null)); | |
| - assertTrue(filter1.equals(filter1)); | |
| + assertFalse(filter1.equals(null), "Expected " + filter1 + " to not equal " + null); | |
| + assertTrue(filter1.equals(filter1), "Expected " + filter1 + " to equal " + filter1); | |
| assertEquals(filter1.hashCode(), filter1.hashCode()); | |
| - assertFalse(filter1.equals(filter2)); | |
| - assertFalse(filter2.equals(filter1)); | |
| + assertFalse(filter1.equals(filter2), "Expected " + filter1 + " to not equal " + filter2); | |
| + assertFalse(filter2.equals(filter1), "Expected " + filter2 + " to not equal " + filter1); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java b/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java | |
| index 47372855ca..1e41a1f679 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/artifact/testutils/TestFileManager.java | |
| @@ -120,9 +120,9 @@ public void assertFileExistence(File dir, String filename, boolean shouldExist) | |
| File file = new File(dir, filename); | |
| if (shouldExist) { | |
| - assertTrue(file.exists()); | |
| + assertTrue(file.exists(), "Expected " + file + ".exists() to return true"); | |
| } else { | |
| - assertFalse(file.exists()); | |
| + assertFalse(file.exists(), "Expected " + file + ".exists() to return false"); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java | |
| index e6ffa7cf23..0cbcfc7bfc 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t04/ProjectInheritanceTest.java | |
| @@ -27,6 +27,7 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @@ -68,7 +69,7 @@ void testDependencyManagementOverridesTransitiveDependencyVersion() throws Excep | |
| assertEquals(pom0Basedir, project1.getParent().getBasedir()); | |
| Set set = project1.getArtifacts(); | |
| assertNotNull(set, "No artifacts"); | |
| - assertTrue(set.size() > 0, "No Artifacts"); | |
| + assertFalse(set.isEmpty(), "No Artifacts"); | |
| assertTrue(set.size() == 3, "Set size should be 3, is " + set.size()); | |
| for (Object aSet : set) { | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java | |
| index a0d610d210..c97114514e 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t05/ProjectInheritanceTest.java | |
| @@ -27,6 +27,7 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @@ -64,7 +65,7 @@ void testDependencyManagement() throws Exception { | |
| assertEquals(pom0Basedir, project1.getParent().getBasedir()); | |
| Set set = project1.getArtifacts(); | |
| assertNotNull(set, "No artifacts"); | |
| - assertTrue(set.size() > 0, "No Artifacts"); | |
| + assertFalse(set.isEmpty(), "No Artifacts"); | |
| for (Object aSet : set) { | |
| Artifact artifact = (Artifact) aSet; | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java | |
| index 43a666040f..7dd2159889 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t06/ProjectInheritanceTest.java | |
| @@ -28,6 +28,7 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @@ -65,7 +66,7 @@ void testDependencyManagement() throws Exception { | |
| assertEquals(pom0Basedir, project1.getParent().getBasedir()); | |
| Set set = project1.getArtifacts(); | |
| assertNotNull(set, "No artifacts"); | |
| - assertTrue(set.size() > 0, "No Artifacts"); | |
| + assertFalse(set.isEmpty(), "No Artifacts"); | |
| Iterator iter = set.iterator(); | |
| assertTrue(set.size() == 4, "Set size should be 4, is " + set.size()); | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java | |
| index 2d1bc2a940..a25950845e 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t07/ProjectInheritanceTest.java | |
| @@ -64,7 +64,7 @@ void testDependencyManagement() throws Exception { | |
| System.out.println("Project " + project1.getId() + " " + project1); | |
| Set set = project1.getArtifacts(); | |
| assertNotNull(set, "No artifacts"); | |
| - assertTrue(set.size() > 0, "No Artifacts"); | |
| + assertFalse(set.isEmpty(), "No Artifacts"); | |
| assertTrue(set.size() == 3, "Set size should be 3, is " + set.size()); | |
| for (Object aSet : set) { | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java | |
| index 5158f52e29..aa5277a7ff 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t08/ProjectInheritanceTest.java | |
| @@ -28,6 +28,7 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @@ -66,7 +67,7 @@ void testDependencyManagement() throws Exception { | |
| System.out.println("Project " + project1.getId() + " " + project1); | |
| Set set = project1.getArtifacts(); | |
| assertNotNull(set, "No artifacts"); | |
| - assertTrue(set.size() > 0, "No Artifacts"); | |
| + assertFalse(set.isEmpty(), "No Artifacts"); | |
| Iterator iter = set.iterator(); | |
| assertTrue(set.size() == 4, "Set size should be 4, is " + set.size()); | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java | |
| index 149b7b04e1..7236385f90 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t09/ProjectInheritanceTest.java | |
| @@ -78,7 +78,7 @@ void testDependencyManagementExclusionsExcludeTransitively() throws Exception { | |
| Map map = project1.getArtifactMap(); | |
| assertNotNull(map, "No artifacts"); | |
| - assertTrue(map.size() > 0, "No Artifacts"); | |
| + assertFalse(map.isEmpty(), "No Artifacts"); | |
| assertTrue(map.size() == 2, "Set size should be 2, is " + map.size()); | |
| assertTrue(map.containsKey("maven-test:t09-a"), "maven-test:t09-a is not in the project"); | |
| @@ -111,7 +111,7 @@ void testDependencyManagementExclusionDoesNotOverrideGloballyForTransitives() th | |
| assertEquals(pom0Basedir, project2.getParent().getBasedir()); | |
| Map map = project2.getArtifactMap(); | |
| assertNotNull(map, "No artifacts"); | |
| - assertTrue(map.size() > 0, "No Artifacts"); | |
| + assertFalse(map.isEmpty(), "No Artifacts"); | |
| assertTrue(map.size() == 4, "Set size should be 4, is " + map.size()); | |
| assertTrue(map.containsKey("maven-test:t09-a"), "maven-test:t09-a is not in the project"); | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java | |
| index e9004e0113..3fa5698a3b 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/inheritance/t10/ProjectInheritanceTest.java | |
| @@ -27,6 +27,7 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @@ -71,7 +72,7 @@ void testDependencyManagementOverridesTransitiveDependencyVersion() throws Excep | |
| System.out.println("Project " + project1.getId() + " " + project1); | |
| Map map = project1.getArtifactMap(); | |
| assertNotNull(map, "No artifacts"); | |
| - assertTrue(map.size() > 0, "No Artifacts"); | |
| + assertFalse(map.isEmpty(), "No Artifacts"); | |
| assertTrue(map.size() == 3, "Set size should be 3, is " + map.size()); | |
| Artifact a = (Artifact) map.get("maven-test:t10-a"); | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/project/interpolation/StringSearchModelInterpolatorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/project/interpolation/StringSearchModelInterpolatorTest.java | |
| new file mode 100644 | |
| index 0000000000..b5c1f64188 | |
| --- /dev/null | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/project/interpolation/StringSearchModelInterpolatorTest.java | |
| @@ -0,0 +1,41 @@ | |
| +/* | |
| + * 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.interpolation; | |
| + | |
| +import java.util.Map; | |
| + | |
| +import org.apache.maven.api.model.InputLocation; | |
| +import org.apache.maven.api.model.InputSource; | |
| +import org.apache.maven.api.model.Model; | |
| +import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +class StringSearchModelInterpolatorTest { | |
| + | |
| + @Test | |
| + void interpolate() throws ModelInterpolationException, InitializationException { | |
| + Model model = Model.newBuilder() | |
| + .groupId("group") | |
| + .location("groupId", InputLocation.of(InputSource.of("model", null))) | |
| + .build(); | |
| + StringSearchModelInterpolator interpolator = new StringSearchModelInterpolator(); | |
| + interpolator.initialize(); | |
| + interpolator.interpolate(new org.apache.maven.model.Model(model), Map.of()); | |
| + } | |
| +} | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java | |
| index 8f9709a175..dbdb15523b 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/LegacyRepositorySystemTest.java | |
| @@ -185,7 +185,7 @@ public void setPath(ProducedArtifact artifact, Path path) { | |
| // | |
| d.setScope(Artifact.SCOPE_SYSTEM); | |
| File file = new File(getBasedir(), "src/test/repository-system/maven-core-2.1.0.jar"); | |
| - assertTrue(file.exists()); | |
| + assertTrue(file.exists(), "Expected " + file + ".exists() to return true"); | |
| d.setSystemPath(file.getCanonicalPath()); | |
| artifact = repositorySystem.createDependencyArtifact(d); | |
| @@ -208,7 +208,7 @@ public void setPath(ProducedArtifact artifact, Path path) { | |
| // Put in a bogus file to make sure missing files cause the resolution to fail. | |
| // | |
| file = new File(getBasedir(), "src/test/repository-system/maven-monkey-2.1.0.jar"); | |
| - assertFalse(file.exists()); | |
| + assertFalse(file.exists(), "Expected " + file + ".exists() to return false"); | |
| d.setSystemPath(file.getCanonicalPath()); | |
| artifact = repositorySystem.createDependencyArtifact(d); | |
| @@ -226,7 +226,7 @@ public void setPath(ProducedArtifact artifact, Path path) { | |
| result = repositorySystem.resolve(request); | |
| resolutionErrorHandler.throwErrors(request, result); | |
| } catch (Exception e) { | |
| - assertTrue(result.hasMissingArtifacts()); | |
| + assertTrue(result.hasMissingArtifacts(), "Expected " + result + ".hasMissingArtifacts() to return true"); | |
| } | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java | |
| index e06318c1db..6e61442f3a 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/DefaultUpdateCheckManagerTest.java | |
| @@ -108,7 +108,7 @@ void testMissingArtifact() throws Exception { | |
| assertFalse(updateCheckManager.isUpdateRequired(a, remoteRepository)); | |
| - assertFalse(file.exists()); | |
| + assertFalse(file.exists(), "Expected " + file + ".exists() to return false"); | |
| assertNotNull( | |
| updateCheckManager.readLastUpdated(touchFile, updateCheckManager.getRepositoryKey(remoteRepository))); | |
| } | |
| @@ -161,7 +161,7 @@ void testMissingPom() throws Exception { | |
| assertFalse(updateCheckManager.isUpdateRequired(a, remoteRepository)); | |
| - assertFalse(file.exists()); | |
| + assertFalse(file.exists(), "Expected " + file + ".exists() to return false"); | |
| assertNotNull( | |
| updateCheckManager.readLastUpdated(touchFile, updateCheckManager.getRepositoryKey(remoteRepository))); | |
| } | |
| diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java | |
| index 641d3034df..72209b3dce 100644 | |
| --- a/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java | |
| +++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/legacy/resolver/DefaultArtifactCollectorTest.java | |
| @@ -333,7 +333,7 @@ void testIncompatibleRanges() throws ArtifactResolutionException, InvalidVersion | |
| ArtifactResolutionResult res = collect(a); | |
| - assertTrue(res.hasVersionRangeViolations()); | |
| + assertTrue(res.hasVersionRangeViolations(), "Expected " + res + ".hasVersionRangeViolations() to return true"); | |
| } | |
| @Test | |
| @@ -346,7 +346,7 @@ void testUnboundedRangeWhenVersionUnavailable() | |
| ArtifactResolutionResult res = collect(a); | |
| - assertTrue(res.hasVersionRangeViolations()); | |
| + assertTrue(res.hasVersionRangeViolations(), "Expected " + res + ".hasVersionRangeViolations() to return true"); | |
| } | |
| @Test | |
| @@ -371,7 +371,7 @@ void testUnboundedRangeAboveLastRelease() throws ArtifactResolutionException, In | |
| ArtifactResolutionResult res = collect(a); | |
| - assertTrue(res.hasVersionRangeViolations()); | |
| + assertTrue(res.hasVersionRangeViolations(), "Expected " + res + ".hasVersionRangeViolations() to return true"); | |
| } | |
| @Test | |
| @@ -665,7 +665,7 @@ void testSnapshotNotIncluded() throws ArtifactResolutionException, InvalidVersio | |
| ArtifactResolutionResult res = collect(a); | |
| - assertTrue(res.hasVersionRangeViolations()); | |
| + assertTrue(res.hasVersionRangeViolations(), "Expected " + res + ".hasVersionRangeViolations() to return true"); | |
| /* | |
| * try { ArtifactResolutionResult res = collect( a ); fail( "Expected b not to resolve: " + res ); } catch ( | |
| diff --git a/compat/maven-embedder/pom.xml b/compat/maven-embedder/pom.xml | |
| index 7c1045dfcf..2df8588ec1 100644 | |
| --- a/compat/maven-embedder/pom.xml | |
| +++ b/compat/maven-embedder/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-embedder</artifactId> | |
| @@ -205,11 +205,7 @@ under the License. | |
| <artifactId>junit-jupiter-params</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| + | |
| <dependency> | |
| <groupId>org.mockito</groupId> | |
| <artifactId>mockito-core</artifactId> | |
| diff --git a/compat/maven-embedder/src/examples/simple-project/settings.xml b/compat/maven-embedder/src/examples/simple-project/settings.xml | |
| index 39709fdef3..63cf96e1fd 100644 | |
| --- a/compat/maven-embedder/src/examples/simple-project/settings.xml | |
| +++ b/compat/maven-embedder/src/examples/simple-project/settings.xml | |
| @@ -18,7 +18,7 @@ specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| -<settings> | |
| +<settings xmlns='http://maven.apache.org/SETTINGS/1.0.0'> | |
| <pluginGroups> | |
| <pluginGroup>org.codehaus.tycho</pluginGroup> | |
| <pluginGroup>org.sonatype.pwt</pluginGroup> | |
| diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java | |
| index 3cc0d38a67..3d930b10cf 100644 | |
| --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java | |
| +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java | |
| @@ -188,7 +188,7 @@ public CLIManager() { | |
| options.addOption(Option.builder(Character.toString(ACTIVATE_PROFILES)) | |
| .longOpt("activate-profiles") | |
| .desc( | |
| - "Comma-delimited list of profiles to activate. Prefixing a profile with ! excludes it, and ? marks it as optional") | |
| + "Comma-delimited list of profiles to activate. Don't use spaces between commas or double quote the full list. Prefixing a profile with ! excludes it, and ? marks it as optional.") | |
| .hasArg() | |
| .build()); | |
| options.addOption(Option.builder(Character.toString(BATCH_MODE)) | |
| @@ -271,7 +271,7 @@ public CLIManager() { | |
| options.addOption(Option.builder(PROJECT_LIST) | |
| .longOpt("projects") | |
| .desc( | |
| - "Comma-delimited list of specified reactor projects to build instead of all projects. A project can be specified by [groupId]:artifactId or by its relative path. Prefixing a project with ! excludes it, and ? marks it as optional") | |
| + "Comma-delimited list of specified reactor projects to build instead of all projects. Don't use spaces between commas or double quote the full list. A project can be specified by [groupId]:artifactId or by its relative path. Prefixing a project with ! excludes it, and ? marks it as optional.") | |
| .hasArg() | |
| .build()); | |
| options.addOption(Option.builder(ALSO_MAKE) | |
| diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java | |
| index 648d96a158..9e1d1a1932 100644 | |
| --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java | |
| +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java | |
| @@ -860,7 +860,7 @@ private List<CoreExtension> readCoreExtensionsDescriptor(String extensionsFile) | |
| if (Files.exists(extensionsPath)) { | |
| try (InputStream is = Files.newInputStream(extensionsPath)) { | |
| return new CoreExtensionsStaxReader() | |
| - .read(is, true, new InputSource(extensionsFile)) | |
| + .read(is, true, InputSource.of(extensionsFile)) | |
| .getExtensions(); | |
| } | |
| } | |
| @@ -1675,9 +1675,6 @@ void populateProperties( | |
| Path userPropertiesFile = mavenConf.resolve("maven-user.properties"); | |
| MavenPropertiesLoader.loadProperties(userProperties, userPropertiesFile, callback, false); | |
| - // Warn about deprecated maven.properties files | |
| - warnAboutDeprecatedPropertiesFiles(systemProperties); | |
| - | |
| // ---------------------------------------------------------------------- | |
| // I'm leaving the setting of system properties here as not to break | |
| // the SystemPropertyProfileActivator. This won't harm embedding. jvz. | |
| @@ -1756,29 +1753,6 @@ protected ModelProcessor createModelProcessor(PlexusContainer container) throws | |
| return container.lookup(ModelProcessor.class); | |
| } | |
| - private void warnAboutDeprecatedPropertiesFiles(Properties systemProperties) { | |
| - // Check for deprecated ~/.m2/maven.properties | |
| - String userConfig = systemProperties.getProperty("maven.user.conf"); | |
| - Path userMavenProperties = userConfig != null ? Path.of(userConfig).resolve("maven.properties") : null; | |
| - if (userMavenProperties != null && Files.exists(userMavenProperties)) { | |
| - slf4jLogger.warn( | |
| - "Loading deprecated properties file: {}. " + "Please rename to 'maven-user.properties'. " | |
| - + "Support for 'maven.properties' will be removed in Maven 4.1.0.", | |
| - userMavenProperties); | |
| - } | |
| - | |
| - // Check for deprecated .mvn/maven.properties in project directory | |
| - String projectConfig = systemProperties.getProperty("maven.project.conf"); | |
| - Path projectMavenProperties = | |
| - projectConfig != null ? Path.of(projectConfig).resolve("maven.properties") : null; | |
| - if (projectMavenProperties != null && Files.exists(projectMavenProperties)) { | |
| - slf4jLogger.warn( | |
| - "Loading deprecated properties file: {}. " + "Please rename to 'maven-user.properties'. " | |
| - + "Support for 'maven.properties' will be removed in Maven 4.1.0.", | |
| - projectMavenProperties); | |
| - } | |
| - } | |
| - | |
| public void setFileSystem(FileSystem fileSystem) { | |
| this.fileSystem = fileSystem; | |
| } | |
| diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/props/MavenProperties.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/props/MavenProperties.java | |
| index 63c6c09d4b..f35d49d2fb 100644 | |
| --- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/props/MavenProperties.java | |
| +++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/props/MavenProperties.java | |
| @@ -871,7 +871,7 @@ public String readProperty() throws IOException { | |
| line = line.substring(0, line.length() - 1); | |
| } | |
| valueLines.add(line); | |
| - while (line.length() > 0 && contains(WHITE_SPACE, line.charAt(0))) { | |
| + while (!line.isEmpty() && contains(WHITE_SPACE, line.charAt(0))) { | |
| line = line.substring(1, line.length()); | |
| } | |
| buffer.append(line); | |
| diff --git a/compat/maven-embedder/src/main/java/org/eclipse/sisu/plexus/PlexusXmlBeanConverter.java b/compat/maven-embedder/src/main/java/org/eclipse/sisu/plexus/PlexusXmlBeanConverter.java | |
| index 5f06757069..0ff3f5b60d 100644 | |
| --- a/compat/maven-embedder/src/main/java/org/eclipse/sisu/plexus/PlexusXmlBeanConverter.java | |
| +++ b/compat/maven-embedder/src/main/java/org/eclipse/sisu/plexus/PlexusXmlBeanConverter.java | |
| @@ -391,6 +391,6 @@ private Object convertText(final String value, final TypeLiteral<?> toType) { | |
| } | |
| // last chance => attempt to create an instance of the expected type: use the string if non-empty | |
| - return text.length() == 0 ? newImplementation(rawType) : newImplementation(rawType, text); | |
| + return text.isEmpty() ? newImplementation(rawType) : newImplementation(rawType, text); | |
| } | |
| } | |
| diff --git a/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java b/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java | |
| index 5867a4537c..bbaa3e622d 100644 | |
| --- a/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java | |
| +++ b/compat/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java | |
| @@ -26,6 +26,7 @@ | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| +import java.util.Collection; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import java.util.stream.Stream; | |
| @@ -56,7 +57,6 @@ | |
| import org.codehaus.plexus.DefaultPlexusContainer; | |
| import org.codehaus.plexus.PlexusContainer; | |
| import org.eclipse.aether.transfer.TransferListener; | |
| -import org.hamcrest.CoreMatchers; | |
| import org.junit.jupiter.api.AfterEach; | |
| import org.junit.jupiter.api.BeforeEach; | |
| import org.junit.jupiter.api.Test; | |
| @@ -68,14 +68,11 @@ | |
| import static java.util.Arrays.asList; | |
| import static org.apache.maven.cli.MavenCli.performProfileActivation; | |
| import static org.apache.maven.cli.MavenCli.performProjectActivation; | |
| -import static org.hamcrest.CoreMatchers.equalTo; | |
| -import static org.hamcrest.CoreMatchers.is; | |
| -import static org.hamcrest.CoreMatchers.notNullValue; | |
| -import static org.hamcrest.CoreMatchers.nullValue; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; | |
| +import static org.junit.jupiter.api.Assertions.assertArrayEquals; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.junit.jupiter.api.Assumptions.assumeTrue; | |
| @@ -105,6 +102,14 @@ void tearDown() throws Exception { | |
| } | |
| } | |
| + // Helper method for containsExactlyInAnyOrder assertion | |
| + private static <T> void assertContainsExactlyInAnyOrder(Collection<T> actual, T... expected) { | |
| + assertEquals(expected.length, actual.size(), "Collection size mismatch"); | |
| + for (T item : expected) { | |
| + assertTrue(actual.contains(item), "Collection should contain: " + item); | |
| + } | |
| + } | |
| + | |
| @Test | |
| void testPerformProfileActivation() throws ParseException { | |
| final CommandLineParser parser = new DefaultParser(); | |
| @@ -118,19 +123,19 @@ void testPerformProfileActivation() throws ParseException { | |
| activation = new ProfileActivation(); | |
| performProfileActivation(parser.parse(options, new String[] {"-P", "test1,+test2,?test3,+?test4"}), activation); | |
| - assertThat(activation.getRequiredActiveProfileIds(), containsInAnyOrder("test1", "test2")); | |
| - assertThat(activation.getOptionalActiveProfileIds(), containsInAnyOrder("test3", "test4")); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredActiveProfileIds(), "test1", "test2"); | |
| + assertContainsExactlyInAnyOrder(activation.getOptionalActiveProfileIds(), "test3", "test4"); | |
| activation = new ProfileActivation(); | |
| performProfileActivation( | |
| parser.parse(options, new String[] {"-P", "!test1,-test2,-?test3,!?test4"}), activation); | |
| - assertThat(activation.getRequiredInactiveProfileIds(), containsInAnyOrder("test1", "test2")); | |
| - assertThat(activation.getOptionalInactiveProfileIds(), containsInAnyOrder("test3", "test4")); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProfileIds(), "test1", "test2"); | |
| + assertContainsExactlyInAnyOrder(activation.getOptionalInactiveProfileIds(), "test3", "test4"); | |
| activation = new ProfileActivation(); | |
| performProfileActivation(parser.parse(options, new String[] {"-P", "-test1,+test2"}), activation); | |
| - assertThat(activation.getRequiredActiveProfileIds(), containsInAnyOrder("test2")); | |
| - assertThat(activation.getRequiredInactiveProfileIds(), containsInAnyOrder("test1")); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredActiveProfileIds(), "test2"); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProfileIds(), "test1"); | |
| } | |
| @Test | |
| @@ -145,19 +150,19 @@ void testDetermineProjectActivation() throws ParseException { | |
| activation = new ProjectActivation(); | |
| performProjectActivation( | |
| parser.parse(options, new String[] {"-pl", "test1,+test2,?test3,+?test4"}), activation); | |
| - assertThat(activation.getRequiredActiveProjectSelectors(), containsInAnyOrder("test1", "test2")); | |
| - assertThat(activation.getOptionalActiveProjectSelectors(), containsInAnyOrder("test3", "test4")); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredActiveProjectSelectors(), "test1", "test2"); | |
| + assertContainsExactlyInAnyOrder(activation.getOptionalActiveProjectSelectors(), "test3", "test4"); | |
| activation = new ProjectActivation(); | |
| performProjectActivation( | |
| parser.parse(options, new String[] {"-pl", "!test1,-test2,-?test3,!?test4"}), activation); | |
| - assertThat(activation.getRequiredInactiveProjectSelectors(), containsInAnyOrder("test1", "test2")); | |
| - assertThat(activation.getOptionalInactiveProjectSelectors(), containsInAnyOrder("test3", "test4")); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProjectSelectors(), "test1", "test2"); | |
| + assertContainsExactlyInAnyOrder(activation.getOptionalInactiveProjectSelectors(), "test3", "test4"); | |
| activation = new ProjectActivation(); | |
| performProjectActivation(parser.parse(options, new String[] {"-pl", "-test1,+test2"}), activation); | |
| - assertThat(activation.getRequiredActiveProjectSelectors(), containsInAnyOrder("test2")); | |
| - assertThat(activation.getRequiredInactiveProjectSelectors(), containsInAnyOrder("test1")); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredActiveProjectSelectors(), "test2"); | |
| + assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProjectSelectors(), "test1"); | |
| } | |
| @Test | |
| @@ -347,21 +352,21 @@ void testStyleColors() throws Exception { | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertFalse(MessageUtils.isColorEnabled()); | |
| + assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false"); | |
| MessageUtils.setColorEnabled(true); | |
| request = new CliRequest(new String[] {"--non-interactive"}, null); | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertFalse(MessageUtils.isColorEnabled()); | |
| + assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false"); | |
| MessageUtils.setColorEnabled(true); | |
| request = new CliRequest(new String[] {"--force-interactive", "--non-interactive"}, null); | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertTrue(MessageUtils.isColorEnabled()); | |
| + assertTrue(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return true"); | |
| MessageUtils.setColorEnabled(true); | |
| request = new CliRequest(new String[] {"-l", "target/temp/mvn.log"}, null); | |
| @@ -369,21 +374,21 @@ void testStyleColors() throws Exception { | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertFalse(MessageUtils.isColorEnabled()); | |
| + assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false"); | |
| MessageUtils.setColorEnabled(false); | |
| request = new CliRequest(new String[] {"-Dstyle.color=always"}, null); | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertTrue(MessageUtils.isColorEnabled()); | |
| + assertTrue(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return true"); | |
| MessageUtils.setColorEnabled(true); | |
| request = new CliRequest(new String[] {"-Dstyle.color=never"}, null); | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertFalse(MessageUtils.isColorEnabled()); | |
| + assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false"); | |
| MessageUtils.setColorEnabled(false); | |
| request = new CliRequest(new String[] {"-Dstyle.color=always", "-B", "-l", "target/temp/mvn.log"}, null); | |
| @@ -391,7 +396,7 @@ void testStyleColors() throws Exception { | |
| cli.cli(request); | |
| cli.properties(request); | |
| cli.logging(request); | |
| - assertTrue(MessageUtils.isColorEnabled()); | |
| + assertTrue(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return true"); | |
| MessageUtils.setColorEnabled(false); | |
| CliRequest maybeColorRequest = | |
| @@ -445,7 +450,7 @@ void resumeFromSelectorIsSuggestedWithoutGroupId() { | |
| String selector = cli.getResumeFromSelector(allProjects, failedProject); | |
| - assertThat(selector, is(":module-a")); | |
| + assertEquals(":module-a", selector); | |
| } | |
| @Test | |
| @@ -456,7 +461,7 @@ void resumeFromSelectorContainsGroupIdWhenArtifactIdIsNotUnique() { | |
| String selector = cli.getResumeFromSelector(allProjects, failedProject); | |
| - assertThat(selector, is("group-a:module")); | |
| + assertEquals("group-a:module", selector); | |
| } | |
| @Test | |
| @@ -469,19 +474,23 @@ void verifyLocalRepositoryPath() throws Exception { | |
| // Use default | |
| cli.cli(request); | |
| executionRequest = cli.populateRequest(request); | |
| - assertThat(executionRequest.getLocalRepositoryPath(), is(nullValue())); | |
| + assertNull(executionRequest.getLocalRepositoryPath()); | |
| // System-properties override default | |
| request.getSystemProperties().setProperty(Constants.MAVEN_REPO_LOCAL, "." + File.separatorChar + "custom1"); | |
| executionRequest = cli.populateRequest(request); | |
| - assertThat(executionRequest.getLocalRepositoryPath(), is(notNullValue())); | |
| - assertThat(executionRequest.getLocalRepositoryPath().toString(), is("." + File.separatorChar + "custom1")); | |
| + assertNotNull(executionRequest.getLocalRepositoryPath()); | |
| + assertEquals( | |
| + "." + File.separatorChar + "custom1", | |
| + executionRequest.getLocalRepositoryPath().toString()); | |
| // User-properties override system properties | |
| request.getUserProperties().setProperty(Constants.MAVEN_REPO_LOCAL, "." + File.separatorChar + "custom2"); | |
| executionRequest = cli.populateRequest(request); | |
| - assertThat(executionRequest.getLocalRepositoryPath(), is(notNullValue())); | |
| - assertThat(executionRequest.getLocalRepositoryPath().toString(), is("." + File.separatorChar + "custom2")); | |
| + assertNotNull(executionRequest.getLocalRepositoryPath()); | |
| + assertEquals( | |
| + "." + File.separatorChar + "custom2", | |
| + executionRequest.getLocalRepositoryPath().toString()); | |
| } | |
| /** | |
| @@ -522,7 +531,7 @@ void populatePropertiesCanContainEqualsSign() throws Exception { | |
| cli.properties(request); | |
| // Assert | |
| - assertThat(request.getUserProperties().getProperty("w"), is("x=y")); | |
| + assertEquals("x=y", request.getUserProperties().getProperty("w")); | |
| } | |
| @Test | |
| @@ -535,7 +544,7 @@ void populatePropertiesSpace() throws Exception { | |
| cli.properties(request); | |
| // Assert | |
| - assertThat(request.getUserProperties().getProperty("z"), is("2")); | |
| + assertEquals("2", request.getUserProperties().getProperty("z")); | |
| } | |
| @Test | |
| @@ -548,7 +557,7 @@ void populatePropertiesShorthand() throws Exception { | |
| cli.properties(request); | |
| // Assert | |
| - assertThat(request.getUserProperties().getProperty("x"), is("true")); | |
| + assertEquals("true", request.getUserProperties().getProperty("x")); | |
| } | |
| @Test | |
| @@ -561,8 +570,8 @@ void populatePropertiesMultiple() throws Exception { | |
| cli.properties(request); | |
| // Assert | |
| - assertThat(request.getUserProperties().getProperty("x"), is("1")); | |
| - assertThat(request.getUserProperties().getProperty("y"), is("true")); | |
| + assertEquals("1", request.getUserProperties().getProperty("x")); | |
| + assertEquals("true", request.getUserProperties().getProperty("y")); | |
| } | |
| @Test | |
| @@ -575,7 +584,7 @@ void populatePropertiesOverwrite() throws Exception { | |
| cli.properties(request); | |
| // Assert | |
| - assertThat(request.getUserProperties().getProperty("x"), is("false")); | |
| + assertEquals("false", request.getUserProperties().getProperty("x")); | |
| } | |
| @Test | |
| @@ -628,18 +637,20 @@ public void testPropertiesInterpolation() throws Exception { | |
| cli.properties(request); | |
| // Assert | |
| - assertThat(request.getUserProperties().getProperty("fro"), CoreMatchers.startsWith("chti")); | |
| - assertThat(request.getUserProperties().getProperty("valFound"), is("sbari")); | |
| - assertThat(request.getUserProperties().getProperty("valNotFound"), is("s${foz}i")); | |
| - assertThat(request.getUserProperties().getProperty("valRootDirectory"), is("C:\\myRootDirectory/.mvn/foo")); | |
| - assertThat( | |
| - request.getUserProperties().getProperty("valTopDirectory"), | |
| - is("C:\\myRootDirectory\\myTopDirectory/pom.xml")); | |
| - assertThat(request.getCommandLine().getOptionValue('f'), is("C:\\myRootDirectory/my-child")); | |
| - assertThat(request.getCommandLine().getArgs(), equalTo(new String[] {"prefix:3.0.0:bar", "validate"})); | |
| + assertTrue(request.getUserProperties().getProperty("fro").startsWith("chti")); | |
| + assertEquals("sbari", request.getUserProperties().getProperty("valFound")); | |
| + assertEquals("s${foz}i", request.getUserProperties().getProperty("valNotFound")); | |
| + assertEquals("C:\\myRootDirectory/.mvn/foo", request.getUserProperties().getProperty("valRootDirectory")); | |
| + assertEquals( | |
| + "C:\\myRootDirectory\\myTopDirectory/pom.xml", | |
| + request.getUserProperties().getProperty("valTopDirectory")); | |
| + assertEquals("C:\\myRootDirectory/my-child", request.getCommandLine().getOptionValue('f')); | |
| + assertArrayEquals( | |
| + new String[] {"prefix:3.0.0:bar", "validate"}, | |
| + request.getCommandLine().getArgs()); | |
| Path p = fs.getPath(request.getUserProperties().getProperty("valTopDirectory")); | |
| - assertThat(p.toString(), is("C:\\myRootDirectory\\myTopDirectory\\pom.xml")); | |
| + assertEquals("C:\\myRootDirectory\\myTopDirectory\\pom.xml", p.toString()); | |
| } | |
| @Test | |
| @@ -667,7 +678,7 @@ public void activateBatchMode(boolean ciEnv, String[] cliArgs, boolean isBatchMo | |
| boolean batchMode = !cli.populateRequest(request).isInteractiveMode(); | |
| - assertThat(batchMode, is(isBatchMode)); | |
| + assertEquals(isBatchMode, batchMode); | |
| } | |
| public static Stream<Arguments> activateBatchModeArguments() { | |
| @@ -699,7 +710,7 @@ public void calculateTransferListener(boolean ciEnv, String[] cliArgs, Class<Tra | |
| transferListener = simplexTransferListener.getDelegate(); | |
| } | |
| - assertThat(transferListener.getClass(), is(expectedSubClass)); | |
| + assertEquals(expectedSubClass, transferListener.getClass()); | |
| } | |
| public static Stream<Arguments> calculateTransferListenerArguments() { | |
| diff --git a/compat/maven-model-builder/pom.xml b/compat/maven-model-builder/pom.xml | |
| index 8c2719f1f6..79ebf1b143 100644 | |
| --- a/compat/maven-model-builder/pom.xml | |
| +++ b/compat/maven-model-builder/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-model-builder</artifactId> | |
| @@ -86,14 +86,10 @@ under the License. | |
| <artifactId>junit-jupiter-api</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| + | |
| <dependency> | |
| <groupId>org.xmlunit</groupId> | |
| - <artifactId>xmlunit-matchers</artifactId> | |
| + <artifactId>xmlunit-core</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| </dependencies> | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProblem.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProblem.java | |
| index 7c42f2b237..97cdab3bb6 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProblem.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelProblem.java | |
| @@ -140,7 +140,7 @@ public Exception getException() { | |
| public String getMessage() { | |
| String msg; | |
| - if (message != null && message.length() > 0) { | |
| + if (message != null && !message.isEmpty()) { | |
| msg = message; | |
| } else { | |
| msg = exception.getMessage(); | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/FileToRawModelMerger.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/FileToRawModelMerger.java | |
| index 9055ca8b91..d623217abe 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/FileToRawModelMerger.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/FileToRawModelMerger.java | |
| @@ -138,6 +138,12 @@ protected void mergeModel_Profiles( | |
| .collect(Collectors.toList())); | |
| } | |
| + @Override | |
| + protected void mergeModel_Mixins( | |
| + Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) { | |
| + // don't merge | |
| + } | |
| + | |
| @Override | |
| protected void mergeModelBase_Dependencies( | |
| ModelBase.Builder builder, | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingException.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingException.java | |
| index 540d891002..28c6618e03 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingException.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelBuildingException.java | |
| @@ -138,7 +138,7 @@ static String toMessage(String modelId, List<ModelProblem> problems) { | |
| writer.print(problems.size()); | |
| writer.print((problems.size() == 1) ? " problem was " : " problems were "); | |
| writer.print("encountered while building the effective model"); | |
| - if (modelId != null && modelId.length() > 0) { | |
| + if (modelId != null && !modelId.isEmpty()) { | |
| writer.print(" for "); | |
| writer.print(modelId); | |
| } | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblemUtils.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblemUtils.java | |
| index 8a694d122c..1956c941e4 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblemUtils.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/building/ModelProblemUtils.java | |
| @@ -100,11 +100,11 @@ static String toId(Model model) { | |
| static String toId(String groupId, String artifactId, String version) { | |
| StringBuilder buffer = new StringBuilder(128); | |
| - buffer.append((groupId != null && groupId.length() > 0) ? groupId : "[unknown-group-id]"); | |
| + buffer.append((groupId != null && !groupId.isEmpty()) ? groupId : "[unknown-group-id]"); | |
| buffer.append(':'); | |
| - buffer.append((artifactId != null && artifactId.length() > 0) ? artifactId : "[unknown-artifact-id]"); | |
| + buffer.append((artifactId != null && !artifactId.isEmpty()) ? artifactId : "[unknown-artifact-id]"); | |
| buffer.append(':'); | |
| - buffer.append((version != null && version.length() > 0) ? version : "[unknown-version]"); | |
| + buffer.append((version != null && !version.isEmpty()) ? version : "[unknown-version]"); | |
| return buffer.toString(); | |
| } | |
| @@ -125,8 +125,8 @@ public static String formatLocation(ModelProblem problem, String projectId) { | |
| if (!problem.getModelId().equals(projectId)) { | |
| buffer.append(problem.getModelId()); | |
| - if (problem.getSource().length() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!problem.getSource().isEmpty()) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append(problem.getSource()); | |
| @@ -134,14 +134,14 @@ public static String formatLocation(ModelProblem problem, String projectId) { | |
| } | |
| if (problem.getLineNumber() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append("line ").append(problem.getLineNumber()); | |
| } | |
| if (problem.getColumnNumber() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append("column ").append(problem.getColumnNumber()); | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java | |
| index 1057d87695..d165f12959 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/inheritance/DefaultInheritanceAssembler.java | |
| @@ -154,7 +154,7 @@ private String appendPath(String parentUrl, String childPath, String pathAdjustm | |
| StringBuilder url = new StringBuilder(parentUrl.length() | |
| + pathAdjustment.length() | |
| + childPath.length() | |
| - + ((pathAdjustment.length() == 0) ? 1 : 2)); | |
| + + ((pathAdjustment.isEmpty()) ? 1 : 2)); | |
| url.append(parentUrl); | |
| concatPath(url, pathAdjustment); | |
| @@ -164,7 +164,7 @@ private String appendPath(String parentUrl, String childPath, String pathAdjustm | |
| } | |
| private void concatPath(StringBuilder url, String path) { | |
| - if (path.length() > 0) { | |
| + if (!path.isEmpty()) { | |
| boolean initialUrlEndsWithSlash = url.charAt(url.length() - 1) == '/'; | |
| boolean pathStartsWithSlash = path.charAt(0) == '/'; | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java | |
| index 828b347965..a69c269583 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/interpolation/ProblemDetectingValueSource.java | |
| @@ -56,7 +56,7 @@ public Object getValue(String expression) { | |
| if (value != null && expression.startsWith(bannedPrefix)) { | |
| String msg = "The expression ${" + expression + "} is deprecated."; | |
| - if (newPrefix != null && newPrefix.length() > 0) { | |
| + if (newPrefix != null && !newPrefix.isEmpty()) { | |
| msg += " Please use ${" + newPrefix + expression.substring(bannedPrefix.length()) + "} instead."; | |
| } | |
| problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.V20).setMessage(msg)); | |
| diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java | |
| index 5e2d47329d..4bde6c4998 100644 | |
| --- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java | |
| +++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java | |
| @@ -785,10 +785,10 @@ private void validateEffectiveDependencies( | |
| prefix, "version", problems, errOn30, Version.V20, d.getVersion(), d.getManagementKey(), d); | |
| /* | |
| - * TODO Extensions like Flex Mojos use custom scopes like "merged", "internal", "external", etc. In | |
| - * order to don't break backward-compat with those, only warn but don't error out. | |
| + * Extensions like Flex Mojos use custom scopes like "merged", "internal", "external", etc. In | |
| + * order to not break backward-compat with those, only warn but don't error out. | |
| */ | |
| - validateEnum( | |
| + validateDependencyScope( | |
| prefix, | |
| "scope", | |
| problems, | |
| @@ -797,15 +797,11 @@ private void validateEffectiveDependencies( | |
| d.getScope(), | |
| d.getManagementKey(), | |
| d, | |
| - "provided", | |
| - "compile", | |
| - "runtime", | |
| - "test", | |
| - "system"); | |
| + false); | |
| validateEffectiveModelAgainstDependency(prefix, problems, m, d, request); | |
| } else { | |
| - validateEnum( | |
| + validateDependencyScope( | |
| prefix, | |
| "scope", | |
| problems, | |
| @@ -814,12 +810,7 @@ private void validateEffectiveDependencies( | |
| d.getScope(), | |
| d.getManagementKey(), | |
| d, | |
| - "provided", | |
| - "compile", | |
| - "runtime", | |
| - "test", | |
| - "system", | |
| - "import"); | |
| + true); | |
| } | |
| } | |
| } | |
| @@ -1337,7 +1328,7 @@ private boolean validateStringNotEmpty( | |
| return false; | |
| } | |
| - if (string.length() > 0) { | |
| + if (!string.isEmpty()) { | |
| return true; | |
| } | |
| @@ -1462,6 +1453,58 @@ private boolean validateEnum( | |
| return false; | |
| } | |
| + @SuppressWarnings("checkstyle:parameternumber") | |
| + private boolean validateDependencyScope( | |
| + String prefix, | |
| + String fieldName, | |
| + ModelProblemCollector problems, | |
| + Severity severity, | |
| + Version version, | |
| + String scope, | |
| + String sourceHint, | |
| + InputLocationTracker tracker, | |
| + boolean isDependencyManagement) { | |
| + if (scope == null || scope.length() <= 0) { | |
| + return true; | |
| + } | |
| + | |
| + String[] validScopes; | |
| + if (isDependencyManagement) { | |
| + validScopes = new String[] {"provided", "compile", "runtime", "test", "system", "import"}; | |
| + } else { | |
| + validScopes = new String[] {"provided", "compile", "runtime", "test", "system"}; | |
| + } | |
| + | |
| + List<String> values = Arrays.asList(validScopes); | |
| + | |
| + if (values.contains(scope)) { | |
| + return true; | |
| + } | |
| + | |
| + // Provide a more helpful error message for the 'import' scope | |
| + if ("import".equals(scope) && !isDependencyManagement) { | |
| + addViolation( | |
| + problems, | |
| + severity, | |
| + version, | |
| + prefix + fieldName, | |
| + sourceHint, | |
| + "has scope 'import'. The 'import' scope is only valid in <dependencyManagement> sections.", | |
| + tracker); | |
| + } else { | |
| + addViolation( | |
| + problems, | |
| + severity, | |
| + version, | |
| + prefix + fieldName, | |
| + sourceHint, | |
| + "must be one of " + values + " but is '" + scope + "'.", | |
| + tracker); | |
| + } | |
| + | |
| + return false; | |
| + } | |
| + | |
| @SuppressWarnings("checkstyle:parameternumber") | |
| private boolean validateModelVersion( | |
| ModelProblemCollector problems, String string, InputLocationTracker tracker, String... validVersions) { | |
| diff --git a/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.1.0.xml b/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.1.0.xml | |
| index ff7738614f..754912f099 100644 | |
| --- a/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.1.0.xml | |
| +++ b/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.1.0.xml | |
| @@ -20,8 +20,10 @@ under the License. | |
| --> | |
| <!-- START SNIPPET: superpom --> | |
| -<project> | |
| - <modelVersion>4.0.0</modelVersion> | |
| +<project xmlns="http://maven.apache.org/POM/4.1.0" | |
| + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"> | |
| + <modelVersion>4.1.0</modelVersion> | |
| <properties> | |
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| diff --git a/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.2.0.xml b/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.2.0.xml | |
| new file mode 100644 | |
| index 0000000000..9b1b358f80 | |
| --- /dev/null | |
| +++ b/compat/maven-model-builder/src/main/resources/org/apache/maven/model/pom-4.2.0.xml | |
| @@ -0,0 +1,68 @@ | |
| +<?xml version="1.0" encoding="UTF-8"?> | |
| + | |
| +<!-- | |
| +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. | |
| +--> | |
| + | |
| +<!-- START SNIPPET: superpom --> | |
| +<project xmlns="http://maven.apache.org/POM/4.2.0" | |
| + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.2.0 https://maven.apache.org/xsd/maven-4.2.0.xsd"> | |
| + <modelVersion>4.2.0</modelVersion> | |
| + | |
| + <properties> | |
| + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |
| + <!-- Fixed date for reproducible build --> | |
| + <project.build.outputTimestamp>1980-02-01T00:00:00Z</project.build.outputTimestamp> | |
| + </properties> | |
| + | |
| + <build> | |
| + <directory>${project.basedir}/target</directory> | |
| + <outputDirectory>${project.build.directory}/classes</outputDirectory> | |
| + <finalName>${project.artifactId}-${project.version}</finalName> | |
| + <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory> | |
| + <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> | |
| + <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory> | |
| + <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> | |
| + <resources> | |
| + <resource> | |
| + <directory>${project.basedir}/src/main/resources</directory> | |
| + </resource> | |
| + <resource> | |
| + <directory>${project.basedir}/src/main/resources-filtered</directory> | |
| + <filtering>true</filtering> | |
| + </resource> | |
| + </resources> | |
| + <testResources> | |
| + <testResource> | |
| + <directory>${project.basedir}/src/test/resources</directory> | |
| + </testResource> | |
| + <testResource> | |
| + <directory>${project.basedir}/src/test/resources-filtered</directory> | |
| + <filtering>true</filtering> | |
| + </testResource> | |
| + </testResources> | |
| + </build> | |
| + | |
| + <reporting> | |
| + <outputDirectory>${project.build.directory}/site</outputDirectory> | |
| + </reporting> | |
| + | |
| +</project> | |
| +<!-- END SNIPPET: superpom --> | |
| diff --git a/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileModelSourceTest.java b/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileModelSourceTest.java | |
| index a03184de6b..9f5d43427f 100644 | |
| --- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileModelSourceTest.java | |
| +++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileModelSourceTest.java | |
| @@ -43,9 +43,9 @@ void testEquals() throws Exception { | |
| File tempFile = createTempFile("pomTest"); | |
| FileModelSource instance = new FileModelSource(tempFile); | |
| - assertFalse(instance.equals(null)); | |
| + assertFalse(instance.equals(null), "Expected " + instance + " to not equal " + null); | |
| assertFalse(instance.equals(new Object())); | |
| - assertTrue(instance.equals(instance)); | |
| + assertTrue(instance.equals(instance), "Expected " + instance + " to equal " + instance); | |
| assertTrue(instance.equals(new FileModelSource(tempFile))); | |
| } | |
| @@ -60,7 +60,9 @@ void testWindowsPaths() throws Exception { | |
| FileModelSource upperCaseFileSource = new FileModelSource(upperCaseFile); | |
| FileModelSource lowerCaseFileSource = new FileModelSource(lowerCaseFile); | |
| - assertTrue(upperCaseFileSource.equals(lowerCaseFileSource)); | |
| + assertTrue( | |
| + upperCaseFileSource.equals(lowerCaseFileSource), | |
| + "Expected " + upperCaseFileSource + " to equal " + lowerCaseFileSource); | |
| } | |
| private File createTempFile(String name) throws IOException { | |
| diff --git a/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileToRawModelMergerTest.java b/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileToRawModelMergerTest.java | |
| index e5ad5b01d3..640579cb88 100644 | |
| --- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileToRawModelMergerTest.java | |
| +++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/FileToRawModelMergerTest.java | |
| @@ -28,8 +28,7 @@ | |
| import org.apache.maven.model.v4.MavenMerger; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.hasItems; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @Deprecated | |
| class FileToRawModelMergerTest { | |
| @@ -65,6 +64,8 @@ void testOverriddenMergeMethods() { | |
| .filter(m -> m.startsWith("merge")) | |
| .collect(Collectors.toList()); | |
| - assertThat(overriddenMethods, hasItems(methodNames.toArray(new String[0]))); | |
| + assertTrue( | |
| + overriddenMethods.containsAll(methodNames), | |
| + "Expected overriddenMethods " + overriddenMethods + " to contain all methodNames " + methodNames); | |
| } | |
| } | |
| diff --git a/compat/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java b/compat/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java | |
| index 72d742a136..cf3e45267f 100644 | |
| --- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java | |
| +++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/inheritance/DefaultInheritanceAssemblerTest.java | |
| @@ -28,9 +28,10 @@ | |
| import org.apache.maven.model.io.ModelWriter; | |
| import org.junit.jupiter.api.BeforeEach; | |
| import org.junit.jupiter.api.Test; | |
| -import org.xmlunit.matchers.CompareMatcher; | |
| +import org.xmlunit.builder.DiffBuilder; | |
| +import org.xmlunit.diff.Diff; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @@ -190,8 +191,12 @@ public void testInheritance(String baseName, boolean fromRepo) throws IOExceptio | |
| // check with getPom( baseName + "-expected" ) | |
| File expected = getPom(baseName + "-expected"); | |
| - assertThat( | |
| - actual, CompareMatcher.isIdenticalTo(expected).ignoreComments().ignoreWhitespace()); | |
| + Diff diff = DiffBuilder.compare(expected) | |
| + .withTest(actual) | |
| + .ignoreComments() | |
| + .ignoreWhitespace() | |
| + .build(); | |
| + assertFalse(diff.hasDifferences(), "XML files should be identical: " + diff.toString()); | |
| } | |
| @Test | |
| @@ -211,7 +216,11 @@ void testModulePathNotArtifactId() throws IOException { | |
| // check with getPom( "module-path-not-artifactId-effective" ) | |
| File expected = getPom("module-path-not-artifactId-expected"); | |
| - assertThat( | |
| - actual, CompareMatcher.isIdenticalTo(expected).ignoreComments().ignoreWhitespace()); | |
| + Diff diff = DiffBuilder.compare(expected) | |
| + .withTest(actual) | |
| + .ignoreComments() | |
| + .ignoreWhitespace() | |
| + .build(); | |
| + assertFalse(diff.hasDifferences(), "XML files should be identical: " + diff.toString()); | |
| } | |
| } | |
| diff --git a/compat/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java b/compat/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java | |
| index 84f5d4390a..a5ba09c686 100644 | |
| --- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java | |
| +++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/profile/DefaultProfileSelectorTest.java | |
| @@ -64,7 +64,8 @@ public boolean presentInConfig( | |
| DefaultProfileActivationContext context = new DefaultProfileActivationContext(); | |
| SimpleProblemCollector problems = new SimpleProblemCollector(); | |
| List<Profile> active = selector.getActiveProfiles(profiles, context, problems); | |
| - assertTrue(active.isEmpty()); | |
| + assertTrue( | |
| + active.isEmpty(), "Expected collection to be empty but had " + active.size() + " elements: " + active); | |
| assertEquals(1, problems.getErrors().size()); | |
| assertEquals( | |
| "Failed to determine activation for profile one: BOOM", | |
| diff --git a/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java b/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java | |
| index cdfa9caac1..a5f5d8548c 100644 | |
| --- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java | |
| +++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java | |
| @@ -267,10 +267,18 @@ void testMissingAll() throws Exception { | |
| List<String> messages = result.getErrors(); | |
| - assertTrue(messages.contains("'modelVersion' is missing.")); | |
| - assertTrue(messages.contains("'groupId' is missing.")); | |
| - assertTrue(messages.contains("'artifactId' is missing.")); | |
| - assertTrue(messages.contains("'version' is missing.")); | |
| + assertTrue( | |
| + messages.contains("'modelVersion' is missing."), | |
| + "Expected " + messages + " to contain " + "'modelVersion' is missing."); | |
| + assertTrue( | |
| + messages.contains("'groupId' is missing."), | |
| + "Expected " + messages + " to contain " + "'groupId' is missing."); | |
| + assertTrue( | |
| + messages.contains("'artifactId' is missing."), | |
| + "Expected " + messages + " to contain " + "'artifactId' is missing."); | |
| + assertTrue( | |
| + messages.contains("'version' is missing."), | |
| + "Expected " + messages + " to contain " + "'version' is missing."); | |
| // type is inherited from the super pom | |
| } | |
| @@ -355,6 +363,10 @@ void testBadDependencyScope() throws Exception { | |
| assertViolations(result, 0, 0, 2); | |
| assertTrue(result.getWarnings().get(0).contains("test:f")); | |
| + // Check that the import scope error message is more helpful | |
| + assertTrue(result.getWarnings() | |
| + .get(0) | |
| + .contains("has scope 'import'. The 'import' scope is only valid in <dependencyManagement> sections")); | |
| assertTrue(result.getWarnings().get(1).contains("test:g")); | |
| } | |
| diff --git a/compat/maven-model/pom.xml b/compat/maven-model/pom.xml | |
| index 772ddcec12..fc1eecff4b 100644 | |
| --- a/compat/maven-model/pom.xml | |
| +++ b/compat/maven-model/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-model</artifactId> | |
| @@ -63,11 +63,6 @@ under the License. | |
| <artifactId>junit-jupiter-api</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| <dependency> | |
| <groupId>org.openjdk.jmh</groupId> | |
| <artifactId>jmh-core</artifactId> | |
| diff --git a/compat/maven-model/src/main/java/org/apache/maven/model/InputLocation.java b/compat/maven-model/src/main/java/org/apache/maven/model/InputLocation.java | |
| index 70ef9bb688..080f73f963 100644 | |
| --- a/compat/maven-model/src/main/java/org/apache/maven/model/InputLocation.java | |
| +++ b/compat/maven-model/src/main/java/org/apache/maven/model/InputLocation.java | |
| @@ -64,10 +64,21 @@ public final class InputLocation implements java.io.Serializable, Cloneable, Inp | |
| */ | |
| private InputLocation importedFrom; | |
| + /** | |
| + * Cached hashCode for performance. | |
| + */ | |
| + private volatile int hashCode = 0; | |
| + | |
| // ----------------/ | |
| // - Constructors -/ | |
| // ----------------/ | |
| + /** | |
| + * Creates a new InputLocation from an API model InputLocation. | |
| + * This constructor is used for converting between the API model and the compat model. | |
| + * | |
| + * @param location the API model InputLocation to convert from | |
| + */ | |
| public InputLocation(org.apache.maven.api.model.InputLocation location) { | |
| this.lineNumber = location.getLineNumber(); | |
| this.columnNumber = location.getColumnNumber(); | |
| @@ -137,10 +148,10 @@ public int getLineNumber() { | |
| } // -- int getLineNumber() | |
| /** | |
| + * Gets the InputLocation for a specific nested element key. | |
| * | |
| - * | |
| - * @param key | |
| - * @return InputLocation | |
| + * @param key the key to look up | |
| + * @return the InputLocation for the specified key, or null if not found | |
| */ | |
| @Override | |
| public InputLocation getLocation(Object key) { | |
| @@ -159,19 +170,19 @@ public InputLocation getLocation(Object key) { | |
| } // -- InputLocation getLocation( Object ) | |
| /** | |
| + * Gets the map of nested element locations within this location. | |
| * | |
| - * | |
| - * @return Map | |
| + * @return a map of keys to InputLocation instances for nested elements, or null if none | |
| */ | |
| public java.util.Map<Object, InputLocation> getLocations() { | |
| return locations; | |
| } // -- java.util.Map<Object, InputLocation> getLocations() | |
| /** | |
| + * Sets the InputLocation for a specific nested element key. | |
| * | |
| - * | |
| - * @param key | |
| - * @param location | |
| + * @param key the key to set the location for | |
| + * @param location the InputLocation to associate with the key | |
| */ | |
| @Override | |
| public void setLocation(Object key, InputLocation location) { | |
| @@ -192,10 +203,11 @@ public void setLocation(Object key, InputLocation location) { | |
| } // -- void setLocation( Object, InputLocation ) | |
| /** | |
| + * Sets the InputLocation for a specific nested element key in the locations map. | |
| + * This is a helper method that manages the internal locations map. | |
| * | |
| - * | |
| - * @param key | |
| - * @param location | |
| + * @param key the key to set the location for | |
| + * @param location the InputLocation to associate with the key | |
| */ | |
| public void setOtherLocation(Object key, InputLocation location) { | |
| if (location != null) { | |
| @@ -331,20 +343,26 @@ public void setLocations(java.util.Map<Object, InputLocation> locations) { | |
| this.locations = locations; | |
| } // -- void setLocations( java.util.Map ) | |
| + /** | |
| + * Converts this compat model InputLocation to an API model InputLocation. | |
| + * This method is used for converting between the compat model and the API model. | |
| + * | |
| + * @return the equivalent API model InputLocation | |
| + */ | |
| public org.apache.maven.api.model.InputLocation toApiLocation() { | |
| if (locations != null && locations.values().contains(this)) { | |
| if (locations.size() == 1 && locations.values().iterator().next() == this) { | |
| - return new org.apache.maven.api.model.InputLocation( | |
| + return org.apache.maven.api.model.InputLocation.of( | |
| lineNumber, | |
| columnNumber, | |
| source != null ? source.toApiSource() : null, | |
| locations.keySet().iterator().next()); | |
| } else { | |
| - return new org.apache.maven.api.model.InputLocation( | |
| + return org.apache.maven.api.model.InputLocation.of( | |
| lineNumber, columnNumber, source != null ? source.toApiSource() : null); | |
| } | |
| } else { | |
| - return new org.apache.maven.api.model.InputLocation( | |
| + return org.apache.maven.api.model.InputLocation.of( | |
| lineNumber, | |
| columnNumber, | |
| source != null ? source.toApiSource() : null, | |
| @@ -379,6 +397,32 @@ public abstract static class StringFormatter { | |
| public abstract String toString(InputLocation location); | |
| } | |
| + @Override | |
| + public boolean equals(Object o) { | |
| + if (this == o) { | |
| + return true; | |
| + } | |
| + if (o == null || getClass() != o.getClass()) { | |
| + return false; | |
| + } | |
| + InputLocation that = (InputLocation) o; | |
| + return lineNumber == that.lineNumber | |
| + && columnNumber == that.columnNumber | |
| + && java.util.Objects.equals(source, that.source) | |
| + && java.util.Objects.equals(locations, that.locations) | |
| + && java.util.Objects.equals(importedFrom, that.importedFrom); | |
| + } | |
| + | |
| + @Override | |
| + public int hashCode() { | |
| + int result = hashCode; | |
| + if (result == 0) { | |
| + result = java.util.Objects.hash(lineNumber, columnNumber, source, locations, importedFrom); | |
| + hashCode = result; | |
| + } | |
| + return result; | |
| + } | |
| + | |
| @Override | |
| public String toString() { | |
| return getLineNumber() + " : " + getColumnNumber() + ", " + getSource(); | |
| diff --git a/compat/maven-model/src/main/java/org/apache/maven/model/InputSource.java b/compat/maven-model/src/main/java/org/apache/maven/model/InputSource.java | |
| index 1bd81e925e..8e66eea476 100644 | |
| --- a/compat/maven-model/src/main/java/org/apache/maven/model/InputSource.java | |
| +++ b/compat/maven-model/src/main/java/org/apache/maven/model/InputSource.java | |
| @@ -58,12 +58,26 @@ public class InputSource implements java.io.Serializable, Cloneable { | |
| */ | |
| private InputLocation importedFrom; | |
| + /** | |
| + * Cached hashCode for performance. | |
| + */ | |
| + private volatile int hashCode = 0; | |
| + | |
| // ----------------/ | |
| // - Constructors -/ | |
| // ----------------/ | |
| + /** | |
| + * Default constructor for InputSource. | |
| + */ | |
| public InputSource() {} | |
| + /** | |
| + * Creates a new InputSource from an API model InputSource. | |
| + * This constructor is used for converting between the API model and the compat model. | |
| + * | |
| + * @param source the API model InputSource to convert from | |
| + */ | |
| public InputSource(org.apache.maven.api.model.InputSource source) { | |
| this.modelId = source.getModelId(); | |
| this.location = source.getLocation(); | |
| @@ -129,9 +143,10 @@ public void setModelId(String modelId) { | |
| } // -- void setModelId( String ) | |
| /** | |
| - * Get the location of the POM from which this POM was | |
| + * Get the location of the POM from which this POM was imported from. | |
| + * Can return {@code null} if this POM was not imported. | |
| * | |
| - * @return | |
| + * @return the InputLocation where this POM was imported from, or null if not imported | |
| */ | |
| public InputLocation getImportedFrom() { | |
| return importedFrom; | |
| @@ -140,18 +155,48 @@ public InputLocation getImportedFrom() { | |
| /** | |
| * Set the location of the POM from which this POM was imported from. | |
| * | |
| - * @param importedFrom | |
| + * @param importedFrom the InputLocation where this POM was imported from, or null if not imported | |
| */ | |
| public void setImportedFrom(InputLocation importedFrom) { | |
| this.importedFrom = importedFrom; | |
| } | |
| + @Override | |
| + public boolean equals(Object o) { | |
| + if (this == o) { | |
| + return true; | |
| + } | |
| + if (o == null || getClass() != o.getClass()) { | |
| + return false; | |
| + } | |
| + InputSource that = (InputSource) o; | |
| + return java.util.Objects.equals(modelId, that.modelId) | |
| + && java.util.Objects.equals(location, that.location) | |
| + && java.util.Objects.equals(importedFrom, that.importedFrom); | |
| + } | |
| + | |
| + @Override | |
| + public int hashCode() { | |
| + int result = hashCode; | |
| + if (result == 0) { | |
| + result = java.util.Objects.hash(modelId, location, importedFrom); | |
| + hashCode = result; | |
| + } | |
| + return result; | |
| + } | |
| + | |
| @Override | |
| public String toString() { | |
| return getModelId() + " " + getLocation(); | |
| } | |
| + /** | |
| + * Converts this compat model InputSource to an API model InputSource. | |
| + * This method is used for converting between the compat model and the API model. | |
| + * | |
| + * @return the equivalent API model InputSource | |
| + */ | |
| public org.apache.maven.api.model.InputSource toApiSource() { | |
| - return new org.apache.maven.api.model.InputSource(modelId, location); | |
| + return org.apache.maven.api.model.InputSource.of(modelId, location); | |
| } | |
| } | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationFileTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationFileTest.java | |
| index 5f5dcd1876..bba906b8a6 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationFileTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationFileTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| ActivationFile thing = new ActivationFile(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationOSTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationOSTest.java | |
| index 5ca7bec65e..7d5777a413 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationOSTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationOSTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| ActivationOS thing = new ActivationOS(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationPropertyTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationPropertyTest.java | |
| index 32b69dc046..54f0ea8f76 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationPropertyTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationPropertyTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| ActivationProperty thing = new ActivationProperty(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationTest.java | |
| index 545aab5a78..13c58db2b5 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ActivationTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ActivationTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Activation thing = new Activation(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/BuildTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/BuildTest.java | |
| index bd78314954..bb1e74f822 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/BuildTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/BuildTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Build thing = new Build(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/CiManagementTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/CiManagementTest.java | |
| index 71a402747b..4b24446627 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/CiManagementTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/CiManagementTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| CiManagement thing = new CiManagement(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ContributorTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ContributorTest.java | |
| index f6ddcc4699..eca3b4cb0a 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ContributorTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ContributorTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Contributor thing = new Contributor(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/DependencyManagementTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/DependencyManagementTest.java | |
| index 1a32e0e8e3..2cfa2a7f0d 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/DependencyManagementTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/DependencyManagementTest.java | |
| @@ -46,7 +46,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| DependencyManagement thing = new DependencyManagement(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/DependencyTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/DependencyTest.java | |
| index 39d7bb30d7..8a82a302f6 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/DependencyTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/DependencyTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Dependency thing = new Dependency(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/DeploymentRepositoryTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/DeploymentRepositoryTest.java | |
| index 8b25cc6263..6c9820c8bb 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/DeploymentRepositoryTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/DeploymentRepositoryTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| DeploymentRepository thing = new DeploymentRepository(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/DeveloperTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/DeveloperTest.java | |
| index b6897aea37..90d870623a 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/DeveloperTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/DeveloperTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Developer thing = new Developer(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/DistributionManagementTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/DistributionManagementTest.java | |
| index 18c57a8c90..e4c6d6ba2c 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/DistributionManagementTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/DistributionManagementTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| DistributionManagement thing = new DistributionManagement(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ExclusionTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ExclusionTest.java | |
| index 625f20ec2c..5b7cc96537 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ExclusionTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ExclusionTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Exclusion thing = new Exclusion(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ExtensionTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ExtensionTest.java | |
| index 3afa12c8ec..98d49f93ed 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ExtensionTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ExtensionTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Extension thing = new Extension(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/IssueManagementTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/IssueManagementTest.java | |
| index a6905a28be..634636c66f 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/IssueManagementTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/IssueManagementTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| IssueManagement thing = new IssueManagement(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/LicenseTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/LicenseTest.java | |
| index 270993c20b..31d2f95a49 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/LicenseTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/LicenseTest.java | |
| @@ -46,7 +46,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| License thing = new License(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/MailingListTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/MailingListTest.java | |
| index c758c7e76e..1ab66b6f05 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/MailingListTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/MailingListTest.java | |
| @@ -46,7 +46,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| MailingList thing = new MailingList(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/NotifierTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/NotifierTest.java | |
| index 33f1c139d9..c2c25ee805 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/NotifierTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/NotifierTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Notifier thing = new Notifier(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/OrganizationTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/OrganizationTest.java | |
| index 62e51a1608..14e499cb4e 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/OrganizationTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/OrganizationTest.java | |
| @@ -46,7 +46,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Organization thing = new Organization(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ParentTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ParentTest.java | |
| index 79f33785ba..552b29a372 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ParentTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ParentTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Parent thing = new Parent(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/PluginConfigurationTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/PluginConfigurationTest.java | |
| index 996bf56bd8..304ec91e9f 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/PluginConfigurationTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/PluginConfigurationTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| PluginConfiguration thing = new PluginConfiguration(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/PluginContainerTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/PluginContainerTest.java | |
| index bc4c61404e..e7e1cbdf73 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/PluginContainerTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/PluginContainerTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| PluginContainer thing = new PluginContainer(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/PluginExecutionTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/PluginExecutionTest.java | |
| index 110ed253d7..0a4a6c7008 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/PluginExecutionTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/PluginExecutionTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| PluginExecution thing = new PluginExecution(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/PluginManagementTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/PluginManagementTest.java | |
| index c657890a95..1038725ad7 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/PluginManagementTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/PluginManagementTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| PluginManagement thing = new PluginManagement(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/PluginTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/PluginTest.java | |
| index c023199854..8bf07fe4f5 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/PluginTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/PluginTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Plugin thing = new Plugin(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/PrerequisitesTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/PrerequisitesTest.java | |
| index c886fd86a3..2e4e965842 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/PrerequisitesTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/PrerequisitesTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Prerequisites thing = new Prerequisites(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ProfileTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ProfileTest.java | |
| index aa8b0dbc95..13ad3b208e 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ProfileTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ProfileTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Profile thing = new Profile(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/RelocationTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/RelocationTest.java | |
| index 9956b12a92..751cbbdcad 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/RelocationTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/RelocationTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Relocation thing = new Relocation(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ReportPluginTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ReportPluginTest.java | |
| index 37f0acf336..8562c67dee 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ReportPluginTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ReportPluginTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| ReportPlugin thing = new ReportPlugin(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ReportSetTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ReportSetTest.java | |
| index 7314675b26..4403c7488b 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ReportSetTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ReportSetTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| ReportSet thing = new ReportSet(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ReportingTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ReportingTest.java | |
| index 8d3d7d93ac..0ba2195c00 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ReportingTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ReportingTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Reporting thing = new Reporting(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryPolicyTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryPolicyTest.java | |
| index 24b9e049a4..01236ee1f8 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryPolicyTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryPolicyTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| RepositoryPolicy thing = new RepositoryPolicy(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryTest.java | |
| index afcc13b88d..e97699ba50 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/RepositoryTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Repository thing = new Repository(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ResourceTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ResourceTest.java | |
| index 7ed52946a6..9382da4f2f 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ResourceTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ResourceTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Resource thing = new Resource(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/ScmTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/ScmTest.java | |
| index 0dde84e4a9..a14f2a6077 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/ScmTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/ScmTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Scm thing = new Scm(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/SiteTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/SiteTest.java | |
| index 41565017b3..a01ae688e1 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/SiteTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/SiteTest.java | |
| @@ -45,7 +45,7 @@ void testEqualsNullSafe() { | |
| @Test | |
| void testEqualsIdentity() { | |
| Site thing = new Site(); | |
| - assertTrue(thing.equals(thing)); | |
| + assertTrue(thing.equals(thing), "Expected " + thing + " to equal " + thing); | |
| } | |
| @Test | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/merge/MavenMergerTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/merge/MavenMergerTest.java | |
| index 92c8c55122..f2d9767259 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/merge/MavenMergerTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/merge/MavenMergerTest.java | |
| @@ -26,9 +26,8 @@ | |
| import org.apache.maven.model.v4.MavenMerger; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.contains; | |
| -import static org.hamcrest.Matchers.is; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| * MavenMerger is based on same instances, subclasses should override KeyComputer per type | |
| @@ -43,10 +42,16 @@ void mergeArtifactId() { | |
| Model source = Model.newBuilder().artifactId("SOURCE").build(); | |
| Model merged = mavenMerger.merge(target, source, true, null); | |
| - assertThat(merged.getArtifactId(), is("SOURCE")); | |
| + assertEquals( | |
| + "SOURCE", | |
| + merged.getArtifactId(), | |
| + "Expected merged artifactId to be SOURCE but was " + merged.getArtifactId()); | |
| merged = mavenMerger.merge(target, source, false, null); | |
| - assertThat(merged.getArtifactId(), is("TARGET")); | |
| + assertEquals( | |
| + "TARGET", | |
| + merged.getArtifactId(), | |
| + "Expected merged artifactId to be TARGET but was " + merged.getArtifactId()); | |
| } | |
| @Test | |
| @@ -62,7 +67,10 @@ void mergeSameContributors() { | |
| Model merged = mavenMerger.merge(target, source, true, null); | |
| - assertThat(merged.getContributors(), contains(contributor)); | |
| + assertEquals(1, merged.getContributors().size(), "Expected exactly 1 contributor"); | |
| + assertTrue( | |
| + merged.getContributors().contains(contributor), | |
| + "Expected contributors to contain " + contributor + " but was " + merged.getContributors()); | |
| } | |
| @Test | |
| @@ -81,6 +89,9 @@ void mergeSameDependencies() { | |
| Model merged = mavenMerger.merge(target, source, true, null); | |
| - assertThat(merged.getDependencies(), contains(dependency)); | |
| + assertEquals(1, merged.getDependencies().size(), "Expected exactly 1 dependency"); | |
| + assertTrue( | |
| + merged.getDependencies().contains(dependency), | |
| + "Expected dependencies to contain " + dependency + " but was " + merged.getDependencies()); | |
| } | |
| } | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/v4/ModelXmlTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/v4/ModelXmlTest.java | |
| index b7d17e808d..048c17cfe1 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/v4/ModelXmlTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/v4/ModelXmlTest.java | |
| @@ -83,7 +83,7 @@ void testNamespaceInXmlNode() throws XMLStreamException { | |
| assertEquals("", myConfig.prefix()); | |
| assertEquals("myConfig", myConfig.name()); | |
| String config = node.toString(); | |
| - assertFalse(config.isEmpty()); | |
| + assertFalse(config.isEmpty(), "Expected collection to not be empty but was empty"); | |
| } | |
| String toXml(Model model) throws IOException, XMLStreamException { | |
| diff --git a/compat/maven-model/src/test/java/org/apache/maven/model/v4/Xpp3DomPerfTest.java b/compat/maven-model/src/test/java/org/apache/maven/model/v4/Xpp3DomPerfTest.java | |
| index 090292cffa..131a152ddc 100644 | |
| --- a/compat/maven-model/src/test/java/org/apache/maven/model/v4/Xpp3DomPerfTest.java | |
| +++ b/compat/maven-model/src/test/java/org/apache/maven/model/v4/Xpp3DomPerfTest.java | |
| @@ -73,7 +73,7 @@ public int readWithStax(AdditionState state) throws IOException, XMLStreamExcept | |
| try (InputStream is = Files.newInputStream(pom)) { | |
| MavenStaxReader reader = new MavenStaxReader(); | |
| reader.setAddLocationInformation(false); | |
| - reader.read(is, true, new InputSource("id", pom.toString())); | |
| + reader.read(is, true, InputSource.of("id", pom.toString())); | |
| i++; | |
| } catch (XMLStreamException e) { | |
| throw new RuntimeException("Error parsing: " + pom, e); | |
| diff --git a/compat/maven-plugin-api/pom.xml b/compat/maven-plugin-api/pom.xml | |
| index dffdb05609..6bd39596de 100644 | |
| --- a/compat/maven-plugin-api/pom.xml | |
| +++ b/compat/maven-plugin-api/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-plugin-api</artifactId> | |
| diff --git a/compat/maven-plugin-api/src/test/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilderTest.java b/compat/maven-plugin-api/src/test/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilderTest.java | |
| index f9839369e2..5b4eeaa284 100644 | |
| --- a/compat/maven-plugin-api/src/test/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilderTest.java | |
| +++ b/compat/maven-plugin-api/src/test/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilderTest.java | |
| @@ -54,8 +54,8 @@ void testBuildReader() throws Exception { | |
| assertEquals("2.3-SNAPSHOT", pd.getVersion()); | |
| assertEquals("jar", pd.getGoalPrefix()); | |
| assertEquals("plugin-description", pd.getDescription()); | |
| - assertFalse(pd.isIsolatedRealm()); | |
| - assertTrue(pd.isInheritedByDefault()); | |
| + assertFalse(pd.isIsolatedRealm(), "Expected " + pd + ".isIsolatedRealm() to return false"); | |
| + assertTrue(pd.isInheritedByDefault(), "Expected " + pd + ".isInheritedByDefault() to return true"); | |
| assertEquals(2, pd.getMojos().size()); | |
| assertEquals(1, pd.getDependencies().size()); | |
| @@ -65,12 +65,12 @@ void testBuildReader() throws Exception { | |
| assertEquals("mojo-description", md.getDescription()); | |
| assertEquals("runtime", md.getDependencyResolutionRequired()); | |
| assertEquals("test", md.getDependencyCollectionRequired()); | |
| - assertFalse(md.isAggregator()); | |
| - assertFalse(md.isDirectInvocationOnly()); | |
| - assertTrue(md.isInheritedByDefault()); | |
| - assertFalse(md.isOnlineRequired()); | |
| - assertTrue(md.isProjectRequired()); | |
| - assertFalse(md.isThreadSafe()); | |
| + assertFalse(md.isAggregator(), "Expected " + md + ".isAggregator() to return false"); | |
| + assertFalse(md.isDirectInvocationOnly(), "Expected " + md + ".isDirectInvocationOnly() to return false"); | |
| + assertTrue(md.isInheritedByDefault(), "Expected " + md + ".isInheritedByDefault() to return true"); | |
| + assertFalse(md.isOnlineRequired(), "Expected " + md + ".isOnlineRequired() to return false"); | |
| + assertTrue(md.isProjectRequired(), "Expected " + md + ".isProjectRequired() to return true"); | |
| + assertFalse(md.isThreadSafe(), "Expected " + md + ".isThreadSafe() to return false"); | |
| assertEquals("package", md.getPhase()); | |
| assertEquals("org.apache.maven.plugin.jar.JarMojo", md.getImplementation()); | |
| assertEquals("antrun", md.getComponentConfigurator()); | |
| @@ -99,8 +99,8 @@ void testBuildReader() throws Exception { | |
| assertEquals("jarName", mp.getAlias()); | |
| assertEquals("java.lang.String", mp.getType()); | |
| assertEquals("java.lang.String", mp.getImplementation()); | |
| - assertTrue(mp.isEditable()); | |
| - assertFalse(mp.isRequired()); | |
| + assertTrue(mp.isEditable(), "Expected " + mp + ".isEditable() to return true"); | |
| + assertFalse(mp.isRequired(), "Expected " + mp + ".isRequired() to return false"); | |
| assertEquals("parameter-description", mp.getDescription()); | |
| assertEquals("deprecated-parameter", mp.getDeprecated()); | |
| assertEquals("${jar.finalName}", mp.getExpression()); | |
| @@ -125,6 +125,6 @@ void testBuildReader() throws Exception { | |
| assertEquals("war", md.getGoal()); | |
| assertNull(md.getDependencyResolutionRequired()); | |
| assertNull(md.getDependencyCollectionRequired()); | |
| - assertTrue(md.isThreadSafe()); | |
| + assertTrue(md.isThreadSafe(), "Expected " + md + ".isThreadSafe() to return true"); | |
| } | |
| } | |
| diff --git a/compat/maven-repository-metadata/pom.xml b/compat/maven-repository-metadata/pom.xml | |
| index bb12e5216b..3a6151f4b9 100644 | |
| --- a/compat/maven-repository-metadata/pom.xml | |
| +++ b/compat/maven-repository-metadata/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-repository-metadata</artifactId> | |
| diff --git a/compat/maven-resolver-provider/pom.xml b/compat/maven-resolver-provider/pom.xml | |
| index 79574d50d7..28ba5230de 100644 | |
| --- a/compat/maven-resolver-provider/pom.xml | |
| +++ b/compat/maven-resolver-provider/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-resolver-provider</artifactId> | |
| diff --git a/compat/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java b/compat/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java | |
| index ce33186a73..a8547b58eb 100644 | |
| --- a/compat/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java | |
| +++ b/compat/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/RepositorySystemTest.java | |
| @@ -73,7 +73,7 @@ void testReadArtifactDescriptor() throws Exception { | |
| */ | |
| private void checkUtSimpleArtifactDependencies(Dependency dep1, Dependency dep2) { | |
| assertEquals("compile", dep1.getScope()); | |
| - assertFalse(dep1.isOptional()); | |
| + assertFalse(dep1.isOptional(), "Expected " + dep1 + ".isOptional() to return false"); | |
| assertEquals(0, dep1.getExclusions().size()); | |
| Artifact depArtifact = dep1.getArtifact(); | |
| assertEquals("ut.simple", depArtifact.getGroupId()); | |
| @@ -81,7 +81,7 @@ private void checkUtSimpleArtifactDependencies(Dependency dep1, Dependency dep2) | |
| assertEquals("1.0", depArtifact.getVersion()); | |
| assertEquals("1.0", depArtifact.getBaseVersion()); | |
| assertNull(depArtifact.getFile()); | |
| - assertFalse(depArtifact.isSnapshot()); | |
| + assertFalse(depArtifact.isSnapshot(), "Expected " + depArtifact + ".isSnapshot() to return false"); | |
| assertEquals("", depArtifact.getClassifier()); | |
| assertEquals("jar", depArtifact.getExtension()); | |
| assertEquals("java", depArtifact.getProperty("language", null)); | |
| @@ -91,7 +91,7 @@ private void checkUtSimpleArtifactDependencies(Dependency dep1, Dependency dep2) | |
| assertEquals(4, depArtifact.getProperties().size()); | |
| assertEquals("compile", dep2.getScope()); | |
| - assertFalse(dep2.isOptional()); | |
| + assertFalse(dep2.isOptional(), "Expected " + dep2 + ".isOptional() to return false"); | |
| assertEquals(0, dep2.getExclusions().size()); | |
| depArtifact = dep2.getArtifact(); | |
| assertEquals("ut.simple", depArtifact.getGroupId()); | |
| @@ -99,7 +99,7 @@ private void checkUtSimpleArtifactDependencies(Dependency dep1, Dependency dep2) | |
| assertEquals("1.0", depArtifact.getVersion()); | |
| assertEquals("1.0", depArtifact.getBaseVersion()); | |
| assertNull(depArtifact.getFile()); | |
| - assertFalse(depArtifact.isSnapshot()); | |
| + assertFalse(depArtifact.isSnapshot(), "Expected " + depArtifact + ".isSnapshot() to return false"); | |
| assertEquals("sources", depArtifact.getClassifier()); | |
| assertEquals("jar", depArtifact.getExtension()); | |
| assertEquals("java", depArtifact.getProperty("language", null)); | |
| @@ -152,8 +152,8 @@ void testResolveArtifact() throws Exception { | |
| } | |
| private void checkArtifactResult(ArtifactResult result, String filename) { | |
| - assertFalse(result.isMissing()); | |
| - assertTrue(result.isResolved()); | |
| + assertFalse(result.isMissing(), "Expected " + result + ".isMissing() to return false"); | |
| + assertTrue(result.isResolved(), "Expected " + result + ".isResolved() to return true"); | |
| Artifact artifact = result.getArtifact(); | |
| assertNotNull(artifact.getFile()); | |
| assertEquals(filename, artifact.getFile().getName()); | |
| diff --git a/compat/maven-settings-builder/pom.xml b/compat/maven-settings-builder/pom.xml | |
| index dc7806e20a..24e62ee656 100644 | |
| --- a/compat/maven-settings-builder/pom.xml | |
| +++ b/compat/maven-settings-builder/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-settings-builder</artifactId> | |
| diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java | |
| index 051b8f4472..140393bf06 100644 | |
| --- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java | |
| +++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsBuilder.java | |
| @@ -105,7 +105,7 @@ public SettingsBuildingResult build(SettingsBuildingRequest request) throws Sett | |
| // for the special case of a drive-relative Windows path, make sure it's absolute to save plugins from trouble | |
| String localRepository = userSettings.getLocalRepository(); | |
| - if (localRepository != null && localRepository.length() > 0) { | |
| + if (localRepository != null && !localRepository.isEmpty()) { | |
| File file = new File(localRepository); | |
| if (!file.isAbsolute() && file.getPath().startsWith(File.separator)) { | |
| userSettings.setLocalRepository(file.getAbsolutePath()); | |
| diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsProblem.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsProblem.java | |
| index e4cbc82674..e245a63839 100644 | |
| --- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsProblem.java | |
| +++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/building/DefaultSettingsProblem.java | |
| @@ -81,21 +81,21 @@ public String getLocation() { | |
| StringBuilder buffer = new StringBuilder(256); | |
| if (!getSource().isEmpty()) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append(getSource()); | |
| } | |
| if (getLineNumber() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append("line ").append(getLineNumber()); | |
| } | |
| if (getColumnNumber() > 0) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append("column ").append(getColumnNumber()); | |
| diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/io/DefaultSettingsReader.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/io/DefaultSettingsReader.java | |
| index f4ea4bdf4c..db54b50234 100644 | |
| --- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/io/DefaultSettingsReader.java | |
| +++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/io/DefaultSettingsReader.java | |
| @@ -49,7 +49,7 @@ public Settings read(File input, Map<String, ?> options) throws IOException { | |
| Objects.requireNonNull(input, "input cannot be null"); | |
| try (InputStream in = Files.newInputStream(input.toPath())) { | |
| - InputSource source = new InputSource(input.toString()); | |
| + InputSource source = InputSource.of(input.toString()); | |
| return new Settings(new SettingsStaxReader().read(in, isStrict(options), source)); | |
| } catch (XMLStreamException e) { | |
| throw new SettingsParseException( | |
| diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java | |
| index 6df108748c..f1913e00de 100644 | |
| --- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java | |
| +++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/validation/DefaultSettingsValidator.java | |
| @@ -223,7 +223,7 @@ private static boolean validateStringNotEmpty( | |
| return false; | |
| } | |
| - if (string.length() > 0) { | |
| + if (!string.isEmpty()) { | |
| return true; | |
| } | |
| diff --git a/compat/maven-settings-builder/src/test/resources/settings/factory/simple.xml b/compat/maven-settings-builder/src/test/resources/settings/factory/simple.xml | |
| index ea664bacdf..abcda726c7 100644 | |
| --- a/compat/maven-settings-builder/src/test/resources/settings/factory/simple.xml | |
| +++ b/compat/maven-settings-builder/src/test/resources/settings/factory/simple.xml | |
| @@ -19,6 +19,6 @@ specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| -<settings> | |
| +<settings xmlns='http://maven.apache.org/SETTINGS/1.0.0'> | |
| <localRepository>${user.home}/.m2/repository</localRepository> | |
| </settings> | |
| diff --git a/compat/maven-settings/pom.xml b/compat/maven-settings/pom.xml | |
| index 3a825e9ddc..6aaa0e4e42 100644 | |
| --- a/compat/maven-settings/pom.xml | |
| +++ b/compat/maven-settings/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-settings</artifactId> | |
| diff --git a/compat/maven-toolchain-builder/pom.xml b/compat/maven-toolchain-builder/pom.xml | |
| index 635a58d7ea..e5e8916c90 100644 | |
| --- a/compat/maven-toolchain-builder/pom.xml | |
| +++ b/compat/maven-toolchain-builder/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-toolchain-builder</artifactId> | |
| diff --git a/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/io/DefaultToolchainsReader.java b/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/io/DefaultToolchainsReader.java | |
| index 3e0665b57c..191adcd111 100644 | |
| --- a/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/io/DefaultToolchainsReader.java | |
| +++ b/compat/maven-toolchain-builder/src/main/java/org/apache/maven/toolchain/io/DefaultToolchainsReader.java | |
| @@ -50,7 +50,7 @@ public PersistedToolchains read(File input, Map<String, ?> options) throws IOExc | |
| Objects.requireNonNull(input, "input cannot be null"); | |
| try (InputStream in = Files.newInputStream(input.toPath())) { | |
| - InputSource source = new InputSource(input.toString()); | |
| + InputSource source = InputSource.of(input.toString()); | |
| return new PersistedToolchains(new MavenToolchainsStaxReader().read(in, isStrict(options), source)); | |
| } catch (XMLStreamException e) { | |
| throw new ToolchainsParseException( | |
| diff --git a/compat/maven-toolchain-model/pom.xml b/compat/maven-toolchain-model/pom.xml | |
| index 8f309312c8..218bdbed07 100644 | |
| --- a/compat/maven-toolchain-model/pom.xml | |
| +++ b/compat/maven-toolchain-model/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-compat-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-toolchain-model</artifactId> | |
| diff --git a/compat/pom.xml b/compat/pom.xml | |
| index 3032d65a4d..bfa23c8cb1 100644 | |
| --- a/compat/pom.xml | |
| +++ b/compat/pom.xml | |
| @@ -22,7 +22,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-compat-modules</artifactId> | |
| diff --git a/doap_Maven.rdf b/doap_Maven.rdf | |
| index 525ab5fdd5..859f72d2f0 100644 | |
| --- a/doap_Maven.rdf | |
| +++ b/doap_Maven.rdf | |
| @@ -99,6 +99,17 @@ under the License. | |
| <release> | |
| <Version> | |
| <name>Latest stable release</name> | |
| + <created>2025-07-12</created> | |
| + <revision>3.9.11</revision> | |
| + <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.11/binaries/apache-maven-3.9.11-bin.zip</file-release> | |
| + <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.11/binaries/apache-maven-3.9.11-bin.tar.gz</file-release> | |
| + <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.11/source/apache-maven-3.9.11-src.zip</file-release> | |
| + <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.11/source/apache-maven-3.9.11-src.tar.gz</file-release> | |
| + </Version> | |
| + </release> | |
| + <release> | |
| + <Version> | |
| + <name>Apache Maven 3.9.10</name> | |
| <created>2025-06-01</created> | |
| <revision>3.9.10</revision> | |
| <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.zip</file-release> | |
| @@ -106,6 +117,8 @@ under the License. | |
| <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.10/source/apache-maven-3.9.10-src.zip</file-release> | |
| <file-release>https://archive.apache.org/dist/maven/maven-3/3.9.10/source/apache-maven-3.9.10-src.tar.gz</file-release> | |
| </Version> | |
| + </release> | |
| + <release> | |
| <Version> | |
| <name>Apache Maven 3.9.9</name> | |
| <created>2024-08-17</created> | |
| diff --git a/impl/maven-cli/pom.xml b/impl/maven-cli/pom.xml | |
| index 85dbb25a99..14af725619 100644 | |
| --- a/impl/maven-cli/pom.xml | |
| +++ b/impl/maven-cli/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-cli</artifactId> | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java | |
| index 4c9b6528ac..bd8a6e6693 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/BaseParser.java | |
| @@ -466,9 +466,6 @@ protected Map<String, String> populateUserProperties(LocalContext context) { | |
| throw new IllegalStateException("Error loading properties from " + propertiesFile, e); | |
| } | |
| - // Warn about deprecated maven.properties files | |
| - warnAboutDeprecatedPropertiesFiles(context); | |
| - | |
| // CLI specified properties are most dominant | |
| userProperties.putAll(userSpecifiedProperties); | |
| @@ -519,7 +516,7 @@ protected List<CoreExtension> readCoreExtensionsDescriptorFromFile(Path extensio | |
| return validateCoreExtensionsDescriptorFromFile( | |
| extensionsFile, | |
| List.copyOf(new CoreExtensionsStaxReader() | |
| - .read(is, true, new InputSource(extensionsFile.toString())) | |
| + .read(is, true, InputSource.of(extensionsFile.toString())) | |
| .getExtensions()), | |
| allowMetaVersions); | |
| } | |
| @@ -578,31 +575,4 @@ protected CIInfo detectCI(LocalContext context) { | |
| } | |
| return detected.get(0); | |
| } | |
| - | |
| - private void warnAboutDeprecatedPropertiesFiles(LocalContext context) { | |
| - Map<String, String> systemProperties = context.systemProperties; | |
| - | |
| - // Check for deprecated ~/.m2/maven.properties | |
| - String userConfig = systemProperties.get("maven.user.conf"); | |
| - Path userMavenProperties = userConfig != null ? Path.of(userConfig).resolve("maven.properties") : null; | |
| - if (userMavenProperties != null && Files.exists(userMavenProperties)) { | |
| - context.parserRequest | |
| - .logger() | |
| - .warn("Loading deprecated properties file: " + userMavenProperties + ". " | |
| - + "Please rename to 'maven-user.properties'. " | |
| - + "Support for 'maven.properties' will be removed in Maven 4.1.0."); | |
| - } | |
| - | |
| - // Check for deprecated .mvn/maven.properties in project directory | |
| - String projectConfig = systemProperties.get("maven.project.conf"); | |
| - Path projectMavenProperties = | |
| - projectConfig != null ? Path.of(projectConfig).resolve("maven.properties") : null; | |
| - if (projectMavenProperties != null && Files.exists(projectMavenProperties)) { | |
| - context.parserRequest | |
| - .logger() | |
| - .warn("Loading deprecated properties file: " + projectMavenProperties + ". " | |
| - + "Please rename to 'maven-user.properties'. " | |
| - + "Support for 'maven.properties' will be removed in Maven 4.1.0."); | |
| - } | |
| - } | |
| } | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/CommonsCliMavenOptions.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/CommonsCliMavenOptions.java | |
| index b70c38666e..233e22f15e 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/CommonsCliMavenOptions.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/CommonsCliMavenOptions.java | |
| @@ -273,7 +273,7 @@ protected void prepareOptions(org.apache.commons.cli.Options options) { | |
| options.addOption(Option.builder(ACTIVATE_PROFILES) | |
| .longOpt("activate-profiles") | |
| .desc( | |
| - "Comma-delimited list of profiles to activate. Prefixing a profile with ! excludes it, and ? marks it as optional") | |
| + "Comma-delimited list of profiles to activate. Don't use spaces between commas or double quote the full list. Prefixing a profile with ! excludes it, and ? marks it as optional.") | |
| .hasArg() | |
| .get()); | |
| options.addOption(Option.builder(SUPPRESS_SNAPSHOT_UPDATES) | |
| @@ -313,7 +313,7 @@ protected void prepareOptions(org.apache.commons.cli.Options options) { | |
| options.addOption(Option.builder(PROJECT_LIST) | |
| .longOpt("projects") | |
| .desc( | |
| - "Comma-delimited list of specified reactor projects to build instead of all projects. A project can be specified by [groupId]:artifactId or by its relative path. Prefixing a project with ! excludes it, and ? marks it as optional") | |
| + "Comma-delimited list of specified reactor projects to build instead of all projects. Don't use spaces between commas or double quote the full list. A project can be specified by [groupId]:artifactId or by its relative path. Prefixing a project with ! excludes it, and ? marks it as optional.") | |
| .hasArg() | |
| .get()); | |
| options.addOption(Option.builder(ALSO_MAKE) | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java | |
| index 69e09e0a0d..11d7276f8f 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java | |
| @@ -520,8 +520,8 @@ private Path findParentPomInMap( | |
| && Objects.equals(gav.artifactId(), artifactId) | |
| && (version == null || Objects.equals(gav.version(), version)); | |
| }) | |
| - .map(Map.Entry::getKey) | |
| .findFirst() | |
| + .map(Map.Entry::getKey) | |
| .orElse(null); | |
| } | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelUpgradeStrategy.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelUpgradeStrategy.java | |
| index 23c582660a..6a6d18bbe0 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelUpgradeStrategy.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelUpgradeStrategy.java | |
| @@ -39,8 +39,10 @@ | |
| import static org.apache.maven.cling.invoker.mvnup.goals.ModelVersionUtils.getSchemaLocationForModelVersion; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_0_0; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_1_0; | |
| +import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_2_0; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_0_0_NAMESPACE; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_1_0_NAMESPACE; | |
| +import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_2_0_NAMESPACE; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlAttributes.SCHEMA_LOCATION; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlAttributes.XSI_NAMESPACE_PREFIX; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlAttributes.XSI_NAMESPACE_URI; | |
| @@ -256,7 +258,9 @@ private String determineTargetModelVersion(UpgradeContext context) { | |
| * Gets the namespace URI for a model version. | |
| */ | |
| private String getNamespaceForModelVersion(String modelVersion) { | |
| - if (MODEL_VERSION_4_1_0.equals(modelVersion)) { | |
| + if (MODEL_VERSION_4_2_0.equals(modelVersion)) { | |
| + return MAVEN_4_2_0_NAMESPACE; | |
| + } else if (MODEL_VERSION_4_1_0.equals(modelVersion)) { | |
| return MAVEN_4_1_0_NAMESPACE; | |
| } else { | |
| return MAVEN_4_0_0_NAMESPACE; | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtils.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtils.java | |
| index 8d37407789..1a916bd23a 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtils.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtils.java | |
| @@ -24,9 +24,12 @@ | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_0_0; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_1_0; | |
| +import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.ModelVersions.MODEL_VERSION_4_2_0; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_0_0_NAMESPACE; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_1_0_NAMESPACE; | |
| +import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Namespaces.MAVEN_4_2_0_NAMESPACE; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.SchemaLocations.MAVEN_4_1_0_SCHEMA_LOCATION; | |
| +import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.SchemaLocations.MAVEN_4_2_0_SCHEMA_LOCATION; | |
| import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.XmlElements.MODEL_VERSION; | |
| /** | |
| @@ -60,7 +63,9 @@ public static String detectModelVersion(Document pomDocument) { | |
| // Fallback to namespace URI detection | |
| String namespaceUri = namespace.getURI(); | |
| - if (MAVEN_4_1_0_NAMESPACE.equals(namespaceUri)) { | |
| + if (MAVEN_4_2_0_NAMESPACE.equals(namespaceUri)) { | |
| + return MODEL_VERSION_4_2_0; | |
| + } else if (MAVEN_4_1_0_NAMESPACE.equals(namespaceUri)) { | |
| return MODEL_VERSION_4_1_0; | |
| } else if (MAVEN_4_0_0_NAMESPACE.equals(namespaceUri)) { | |
| return MODEL_VERSION_4_0_0; | |
| @@ -72,13 +77,15 @@ public static String detectModelVersion(Document pomDocument) { | |
| /** | |
| * Checks if a model version is valid for upgrade operations. | |
| - * Currently only supports 4.0.0 and 4.1.0. | |
| + * Currently supports 4.0.0, 4.1.0, and 4.2.0. | |
| * | |
| * @param modelVersion the model version to validate | |
| * @return true if the model version is valid | |
| */ | |
| public static boolean isValidModelVersion(String modelVersion) { | |
| - return MODEL_VERSION_4_0_0.equals(modelVersion) || MODEL_VERSION_4_1_0.equals(modelVersion); | |
| + return MODEL_VERSION_4_0_0.equals(modelVersion) | |
| + || MODEL_VERSION_4_1_0.equals(modelVersion) | |
| + || MODEL_VERSION_4_2_0.equals(modelVersion); | |
| } | |
| /** | |
| @@ -93,8 +100,15 @@ public static boolean canUpgrade(String fromVersion, String toVersion) { | |
| return false; | |
| } | |
| - // Currently only support 4.0.0 → 4.1.0 upgrade | |
| - return MODEL_VERSION_4_0_0.equals(fromVersion) && MODEL_VERSION_4_1_0.equals(toVersion); | |
| + // Support upgrades: 4.0.0 → 4.1.0, 4.0.0 → 4.2.0, 4.1.0 → 4.2.0 | |
| + if (MODEL_VERSION_4_0_0.equals(fromVersion)) { | |
| + return MODEL_VERSION_4_1_0.equals(toVersion) || MODEL_VERSION_4_2_0.equals(toVersion); | |
| + } | |
| + if (MODEL_VERSION_4_1_0.equals(fromVersion)) { | |
| + return MODEL_VERSION_4_2_0.equals(toVersion); | |
| + } | |
| + | |
| + return false; | |
| } | |
| /** | |
| @@ -105,7 +119,9 @@ public static boolean canUpgrade(String fromVersion, String toVersion) { | |
| * @return true if eligible for inference | |
| */ | |
| public static boolean isEligibleForInference(String modelVersion) { | |
| - return MODEL_VERSION_4_0_0.equals(modelVersion) || MODEL_VERSION_4_1_0.equals(modelVersion); | |
| + return MODEL_VERSION_4_0_0.equals(modelVersion) | |
| + || MODEL_VERSION_4_1_0.equals(modelVersion) | |
| + || MODEL_VERSION_4_2_0.equals(modelVersion); | |
| } | |
| /** | |
| @@ -220,8 +236,13 @@ public static boolean removeModelVersion(Document pomDocument) { | |
| * @return the schema location | |
| */ | |
| public static String getSchemaLocationForModelVersion(String modelVersion) { | |
| - if (MODEL_VERSION_4_1_0.equals(modelVersion) || isNewerThan410(modelVersion)) { | |
| + if (MODEL_VERSION_4_2_0.equals(modelVersion)) { | |
| + return MAVEN_4_2_0_SCHEMA_LOCATION; | |
| + } else if (MODEL_VERSION_4_1_0.equals(modelVersion)) { | |
| return MAVEN_4_1_0_SCHEMA_LOCATION; | |
| + } else if (isNewerThan410(modelVersion)) { | |
| + // For versions newer than 4.1.0 but not specifically 4.2.0, use 4.2.0 schema | |
| + return MAVEN_4_2_0_SCHEMA_LOCATION; | |
| } | |
| return UpgradeConstants.SchemaLocations.MAVEN_4_0_0_SCHEMA_LOCATION; | |
| } | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeConstants.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeConstants.java | |
| index 004ad070b0..f8fd5494bc 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeConstants.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeConstants.java | |
| @@ -38,6 +38,9 @@ public static final class ModelVersions { | |
| /** Maven 4.1.0 model version */ | |
| public static final String MODEL_VERSION_4_1_0 = "4.1.0"; | |
| + /** Maven 4.2.0 model version */ | |
| + public static final String MODEL_VERSION_4_2_0 = "4.2.0"; | |
| + | |
| private ModelVersions() { | |
| // Utility class | |
| } | |
| @@ -185,6 +188,9 @@ public static final class Namespaces { | |
| /** Maven 4.1.0 namespace URI */ | |
| public static final String MAVEN_4_1_0_NAMESPACE = "http://maven.apache.org/POM/4.1.0"; | |
| + /** Maven 4.2.0 namespace URI */ | |
| + public static final String MAVEN_4_2_0_NAMESPACE = "http://maven.apache.org/POM/4.2.0"; | |
| + | |
| private Namespaces() { | |
| // Utility class | |
| } | |
| @@ -202,6 +208,10 @@ public static final class SchemaLocations { | |
| public static final String MAVEN_4_1_0_SCHEMA_LOCATION = | |
| Namespaces.MAVEN_4_1_0_NAMESPACE + " https://maven.apache.org/xsd/maven-4.1.0.xsd"; | |
| + /** Schema location for 4.2.0 models */ | |
| + public static final String MAVEN_4_2_0_SCHEMA_LOCATION = | |
| + Namespaces.MAVEN_4_2_0_NAMESPACE + " https://maven.apache.org/xsd/maven-4.2.0.xsd"; | |
| + | |
| private SchemaLocations() { | |
| // Utility class | |
| } | |
| diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/props/MavenProperties.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/props/MavenProperties.java | |
| index 27f4ef8052..2bc3264ed4 100644 | |
| --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/props/MavenProperties.java | |
| +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/props/MavenProperties.java | |
| @@ -870,7 +870,7 @@ public String readProperty() throws IOException { | |
| line = line.substring(0, line.length() - 1); | |
| } | |
| valueLines.add(line); | |
| - while (line.length() > 0 && contains(WHITE_SPACE, line.charAt(0))) { | |
| + while (!line.isEmpty() && contains(WHITE_SPACE, line.charAt(0))) { | |
| line = line.substring(1, line.length()); | |
| } | |
| buffer.append(line); | |
| diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java | |
| index 90b533fd33..9053ec88f2 100644 | |
| --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java | |
| +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/BaseParserTest.java | |
| @@ -77,7 +77,8 @@ void notHappy() { | |
| .build()); | |
| Assertions.assertFalse(invokerRequest.options().isPresent()); | |
| - Assertions.assertTrue(invokerRequest.parsingFailed()); | |
| + Assertions.assertTrue( | |
| + invokerRequest.parsingFailed(), "Expected " + invokerRequest + ".parsingFailed() to return true"); | |
| } | |
| @Test | |
| diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/GAVUtilsTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/GAVUtilsTest.java | |
| index 1b14223a3a..5585616ec4 100644 | |
| --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/GAVUtilsTest.java | |
| +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/GAVUtilsTest.java | |
| @@ -233,7 +233,7 @@ void shouldHandleEmptyPOMMap() { | |
| Set<GAV> gavs = GAVUtils.computeAllGAVs(context, pomMap); | |
| assertNotNull(gavs); | |
| - assertTrue(gavs.isEmpty()); | |
| + assertTrue(gavs.isEmpty(), "Expected collection to be empty but had " + gavs.size() + " elements: " + gavs); | |
| } | |
| @Test | |
| diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtilsTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtilsTest.java | |
| index 215e6b8e48..531c52c1ab 100644 | |
| --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtilsTest.java | |
| +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/ModelVersionUtilsTest.java | |
| @@ -129,14 +129,14 @@ void shouldDetectVersionFromNamespaceWhenModelVersionMissing() throws Exception | |
| class ModelVersionValidationTests { | |
| @ParameterizedTest | |
| - @ValueSource(strings = {"4.0.0", "4.1.0"}) | |
| + @ValueSource(strings = {"4.0.0", "4.1.0", "4.2.0"}) | |
| @DisplayName("should validate supported model versions") | |
| void shouldValidateSupportedModelVersions(String version) { | |
| assertTrue(ModelVersionUtils.isValidModelVersion(version)); | |
| } | |
| @ParameterizedTest | |
| - @ValueSource(strings = {"3.0.0", "5.0.0", "4.2.0", "2.0.0", "6.0.0"}) | |
| + @ValueSource(strings = {"3.0.0", "5.0.0", "2.0.0", "6.0.0"}) | |
| @DisplayName("should reject unsupported model versions") | |
| void shouldRejectUnsupportedModelVersions(String version) { | |
| assertFalse(ModelVersionUtils.isValidModelVersion(version)); | |
| @@ -366,7 +366,7 @@ class SchemaLocationOperationTests { | |
| void shouldGetSchemaLocationForModelVersion() { | |
| String schemaLocation410 = ModelVersionUtils.getSchemaLocationForModelVersion("4.1.0"); | |
| assertNotNull(schemaLocation410); | |
| - assertTrue(schemaLocation410.contains("4.1.0")); | |
| + assertTrue(schemaLocation410.contains("4.1.0"), "Expected " + schemaLocation410 + " to contain " + "4.1.0"); | |
| } | |
| @Test | |
| @@ -374,18 +374,18 @@ void shouldGetSchemaLocationForModelVersion() { | |
| void shouldGetSchemaLocationFor400() { | |
| String schemaLocation400 = ModelVersionUtils.getSchemaLocationForModelVersion("4.0.0"); | |
| assertNotNull(schemaLocation400); | |
| - assertTrue(schemaLocation400.contains("4.0.0")); | |
| + assertTrue(schemaLocation400.contains("4.0.0"), "Expected " + schemaLocation400 + " to contain " + "4.0.0"); | |
| } | |
| @Test | |
| @DisplayName("should handle unknown model version in schema location") | |
| void shouldHandleUnknownModelVersionInSchemaLocation() { | |
| String schemaLocation = ModelVersionUtils.getSchemaLocationForModelVersion("5.0.0"); | |
| - assertNotNull(schemaLocation); // Should return 4.1.0 schema for newer versions | |
| - // The method returns the 4.1.0 schema location for versions newer than 4.1.0 | |
| + assertNotNull(schemaLocation); // Should return 4.2.0 schema for newer versions | |
| + // The method returns the 4.2.0 schema location for versions newer than 4.1.0 | |
| assertTrue( | |
| - schemaLocation.contains("4.1.0"), | |
| - "Expected schema location to contain '4.1.0', but was: " + schemaLocation); | |
| + schemaLocation.contains("4.2.0"), | |
| + "Expected schema location to contain '4.2.0', but was: " + schemaLocation); | |
| } | |
| } | |
| diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/StrategyOrchestratorTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/StrategyOrchestratorTest.java | |
| index 88ceb5b548..452a16c7fd 100644 | |
| --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/StrategyOrchestratorTest.java | |
| +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/StrategyOrchestratorTest.java | |
| @@ -24,7 +24,6 @@ | |
| import java.util.Map; | |
| import java.util.Set; | |
| -import org.apache.maven.api.cli.mvnup.UpgradeOptions; | |
| import org.apache.maven.cling.invoker.mvnup.UpgradeContext; | |
| import org.jdom2.Document; | |
| import org.junit.jupiter.api.BeforeEach; | |
| @@ -61,14 +60,6 @@ private UpgradeContext createMockContext() { | |
| return TestUtils.createMockContext(); | |
| } | |
| - private UpgradeContext createMockContext(UpgradeOptions options) { | |
| - return TestUtils.createMockContext(options); | |
| - } | |
| - | |
| - private UpgradeOptions createDefaultOptions() { | |
| - return TestUtils.createDefaultOptions(); | |
| - } | |
| - | |
| @Nested | |
| @DisplayName("Strategy Execution") | |
| class StrategyExecutionTests { | |
| diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeWorkflowIntegrationTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeWorkflowIntegrationTest.java | |
| index 5a652d557f..ea75e835ba 100644 | |
| --- a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeWorkflowIntegrationTest.java | |
| +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/UpgradeWorkflowIntegrationTest.java | |
| @@ -165,7 +165,9 @@ void applyShouldModifyFiles() throws Exception { | |
| // Verify POM was potentially modified (depending on strategy applicability) | |
| String pomContent = Files.readString(pomFile); | |
| - assertTrue(pomContent.contains("<groupId>com.example</groupId>")); | |
| + assertTrue( | |
| + pomContent.contains("<groupId>com.example</groupId>"), | |
| + "Expected " + pomContent + " to contain " + "<groupId>com.example</groupId>"); | |
| // Note: The exact modifications depend on which strategies are applicable | |
| // This test mainly verifies that apply goal can modify files | |
| } | |
| diff --git a/impl/maven-core/pom.xml b/impl/maven-core/pom.xml | |
| index 08da7144ae..534838b51d 100644 | |
| --- a/impl/maven-core/pom.xml | |
| +++ b/impl/maven-core/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-core</artifactId> | |
| @@ -199,11 +199,7 @@ under the License. | |
| <artifactId>junit-jupiter-params</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| + | |
| <dependency> | |
| <groupId>org.slf4j</groupId> | |
| <artifactId>slf4j-simple</artifactId> | |
| @@ -216,7 +212,7 @@ under the License. | |
| </dependency> | |
| <dependency> | |
| <groupId>org.xmlunit</groupId> | |
| - <artifactId>xmlunit-assertj</artifactId> | |
| + <artifactId>xmlunit-core</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| <dependency> | |
| @@ -235,11 +231,7 @@ under the License. | |
| <artifactId>mockito-core</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.assertj</groupId> | |
| - <artifactId>assertj-core</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| + | |
| <dependency> | |
| <groupId>org.openjdk.jmh</groupId> | |
| <artifactId>jmh-core</artifactId> | |
| @@ -250,6 +242,11 @@ under the License. | |
| <artifactId>jmh-generator-annprocess</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| + <dependency> | |
| + <groupId>com.google.jimfs</groupId> | |
| + <artifactId>jimfs</artifactId> | |
| + <scope>test</scope> | |
| + </dependency> | |
| <!-- Used in TestApi --> | |
| <dependency> | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/RepositoryUtils.java b/impl/maven-core/src/main/java/org/apache/maven/RepositoryUtils.java | |
| index ec9af20fc4..5836b78357 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/RepositoryUtils.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/RepositoryUtils.java | |
| @@ -259,13 +259,15 @@ public static ArtifactHandler newHandler(Artifact artifact) { | |
| } | |
| public static ArtifactType newArtifactType(String id, ArtifactHandler handler) { | |
| - return new DefaultArtifactType( | |
| - id, | |
| - handler.getExtension(), | |
| - handler.getClassifier(), | |
| - handler.getLanguage(), | |
| - handler.isAddedToClasspath(), | |
| - handler.isIncludesDependencies()); | |
| + return handler != null | |
| + ? new DefaultArtifactType( | |
| + id, | |
| + handler.getExtension(), | |
| + handler.getClassifier(), | |
| + handler.getLanguage(), | |
| + handler.isAddedToClasspath(), | |
| + handler.isIncludesDependencies()) | |
| + : null; | |
| } | |
| public static Dependency toDependency( | |
| @@ -275,8 +277,8 @@ public static Dependency toDependency( | |
| stereotype = new DefaultArtifactType(dependency.getType()); | |
| } | |
| - boolean system = | |
| - dependency.getSystemPath() != null && dependency.getSystemPath().length() > 0; | |
| + boolean system = dependency.getSystemPath() != null | |
| + && !dependency.getSystemPath().isEmpty(); | |
| Map<String, String> props = null; | |
| if (system) { | |
| @@ -307,23 +309,12 @@ private static Exclusion toExclusion(org.apache.maven.model.Exclusion exclusion) | |
| return new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*"); | |
| } | |
| + /** | |
| + * @deprecated Use by maven-artifact-transfer. | |
| + */ | |
| + @Deprecated | |
| public static ArtifactTypeRegistry newArtifactTypeRegistry(ArtifactHandlerManager handlerManager) { | |
| - return new MavenArtifactTypeRegistry(handlerManager); | |
| - } | |
| - | |
| - static class MavenArtifactTypeRegistry implements ArtifactTypeRegistry { | |
| - | |
| - private final ArtifactHandlerManager handlerManager; | |
| - | |
| - MavenArtifactTypeRegistry(ArtifactHandlerManager handlerManager) { | |
| - this.handlerManager = handlerManager; | |
| - } | |
| - | |
| - @Override | |
| - public ArtifactType get(String stereotypeId) { | |
| - ArtifactHandler handler = handlerManager.getArtifactHandler(stereotypeId); | |
| - return newArtifactType(stereotypeId, handler); | |
| - } | |
| + return typeId -> newArtifactType(typeId, handlerManager.getArtifactHandler(typeId)); | |
| } | |
| public static Collection<Artifact> toArtifacts(Collection<org.apache.maven.artifact.Artifact> artifactsToConvert) { | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java b/impl/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java | |
| index 9ff2d200e0..723d659c93 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java | |
| @@ -52,12 +52,12 @@ public static List<String> format(String... lines) { | |
| sb.setLength(0); | |
| String[] words = S_FILTER.split(line); | |
| for (String word : words) { | |
| - if (sb.length() >= remainder - word.length() - (sb.length() > 0 ? 1 : 0)) { | |
| + if (sb.length() >= remainder - word.length() - (!sb.isEmpty() ? 1 : 0)) { | |
| repeat(sb, ' ', remainder - sb.length()); | |
| result.add(BOX_CHAR + " " + sb + " " + BOX_CHAR); | |
| sb.setLength(0); | |
| } | |
| - if (sb.length() > 0) { | |
| + if (!sb.isEmpty()) { | |
| sb.append(' '); | |
| } | |
| sb.append(word); | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java | |
| index 6638b6a46f..541c8364ed 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java | |
| @@ -63,6 +63,7 @@ | |
| import org.eclipse.aether.util.graph.version.HighestVersionFilter; | |
| import org.eclipse.aether.util.graph.version.LowestVersionFilter; | |
| import org.eclipse.aether.util.graph.version.PredicateVersionFilter; | |
| +import org.eclipse.aether.util.graph.version.SnapshotVersionFilter; | |
| import org.eclipse.aether.util.listener.ChainedRepositoryListener; | |
| import org.eclipse.aether.util.repository.AuthenticationBuilder; | |
| import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager; | |
| @@ -435,6 +436,8 @@ private VersionFilter buildVersionFilter(String filterExpression) { | |
| filters.add(new LowestVersionFilter(num)); | |
| } else if ("s".equals(expression)) { | |
| filters.add(new ContextualSnapshotVersionFilter()); | |
| + } else if ("ns".equals(expression)) { | |
| + filters.add(new SnapshotVersionFilter()); | |
| } else if (expression.startsWith("e(") && expression.endsWith(")")) { | |
| Artifact artifact = new DefaultArtifact(expression.substring(2, expression.length() - 1)); | |
| VersionRange versionRange = | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java | |
| index 96e65dfcea..b802e8a44c 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java | |
| @@ -40,15 +40,12 @@ public ArtifactType get(String typeId) { | |
| if (type instanceof ArtifactType artifactType) { | |
| return artifactType; | |
| } | |
| - if (type != null) { | |
| - return new DefaultType( | |
| - type.id(), | |
| - type.getLanguage(), | |
| - type.getExtension(), | |
| - type.getClassifier(), | |
| - type.isIncludesDependencies(), | |
| - type.getPathTypes().toArray(new PathType[0])); | |
| - } | |
| - return null; | |
| + return new DefaultType( | |
| + type.id(), | |
| + type.getLanguage(), | |
| + type.getExtension(), | |
| + type.getClassifier(), | |
| + type.isIncludesDependencies(), | |
| + type.getPathTypes().toArray(new PathType[0])); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java | |
| index 84abf9fbb7..a0b9ff8a0a 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java | |
| @@ -92,7 +92,7 @@ public class DefaultLifecycleRegistry implements LifecycleRegistry { | |
| + ":default-lifecycle-bindings"; | |
| public static final InputLocation DEFAULT_LIFECYCLE_INPUT_LOCATION = | |
| - new InputLocation(new InputSource(DEFAULT_LIFECYCLE_MODELID, null)); | |
| + InputLocation.of(InputSource.of(DEFAULT_LIFECYCLE_MODELID, null)); | |
| public static final String SCOPE_COMPILE = DependencyScope.COMPILE.id(); | |
| public static final String SCOPE_RUNTIME = DependencyScope.RUNTIME.id(); | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java | |
| index 4b2722405c..347eef4f75 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformer.java | |
| @@ -129,7 +129,8 @@ private void doDeleteFiles() { | |
| @Override | |
| public InstallRequest remapInstallArtifacts(RepositorySystemSession session, InstallRequest request) { | |
| if (consumerPomPresent(request.getArtifacts())) { | |
| - request.setArtifacts(replacePom(request.getArtifacts())); | |
| + // For install, we always include build POMs as they may be needed locally | |
| + request.setArtifacts(replacePom(request.getArtifacts(), true)); | |
| } | |
| return request; | |
| } | |
| @@ -137,7 +138,8 @@ public InstallRequest remapInstallArtifacts(RepositorySystemSession session, Ins | |
| @Override | |
| public DeployRequest remapDeployArtifacts(RepositorySystemSession session, DeployRequest request) { | |
| if (consumerPomPresent(request.getArtifacts())) { | |
| - request.setArtifacts(replacePom(request.getArtifacts())); | |
| + boolean deployBuildPom = Features.deployBuildPom(session.getConfigProperties()); | |
| + request.setArtifacts(replacePom(request.getArtifacts(), deployBuildPom)); | |
| } | |
| return request; | |
| } | |
| @@ -147,7 +149,7 @@ private boolean consumerPomPresent(Collection<Artifact> artifacts) { | |
| .anyMatch(a -> "pom".equals(a.getExtension()) && CONSUMER_POM_CLASSIFIER.equals(a.getClassifier())); | |
| } | |
| - private Collection<Artifact> replacePom(Collection<Artifact> artifacts) { | |
| + private Collection<Artifact> replacePom(Collection<Artifact> artifacts, boolean deployBuildPom) { | |
| List<Artifact> consumers = new ArrayList<>(); | |
| List<Artifact> mains = new ArrayList<>(); | |
| for (Artifact artifact : artifacts) { | |
| @@ -163,17 +165,22 @@ private Collection<Artifact> replacePom(Collection<Artifact> artifacts) { | |
| ArrayList<Artifact> result = new ArrayList<>(artifacts); | |
| for (Artifact main : mains) { | |
| result.remove(main); | |
| - result.add(new DefaultArtifact( | |
| - main.getGroupId(), | |
| - main.getArtifactId(), | |
| - BUILD_POM_CLASSIFIER, | |
| - main.getExtension(), | |
| - main.getVersion(), | |
| - main.getProperties(), | |
| - main.getPath())); | |
| + if (deployBuildPom) { | |
| + // Add the main POM as a build POM with "build" classifier | |
| + result.add(new DefaultArtifact( | |
| + main.getGroupId(), | |
| + main.getArtifactId(), | |
| + BUILD_POM_CLASSIFIER, | |
| + main.getExtension(), | |
| + main.getVersion(), | |
| + main.getProperties(), | |
| + main.getPath())); | |
| + } | |
| + // If deployBuildPom is false, we simply don't add the build POM to the result | |
| } | |
| for (Artifact consumer : consumers) { | |
| result.remove(consumer); | |
| + // Replace the consumer POM as the main POM (no classifier) | |
| result.add(new DefaultArtifact( | |
| consumer.getGroupId(), | |
| consumer.getArtifactId(), | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java | |
| index b452b1fd38..0cd11ffcd7 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java | |
| @@ -235,6 +235,7 @@ static Model transformNonPom(Model model, MavenProject project) { | |
| .preserveModelVersion(false) | |
| .root(false) | |
| .parent(null) | |
| + .mixins(null) | |
| .build(null), | |
| model) | |
| .mailingLists(null) | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ConcurrencyDependencyGraph.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ConcurrencyDependencyGraph.java | |
| index ed0c36a3d5..9bfffcb6e3 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ConcurrencyDependencyGraph.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ConcurrencyDependencyGraph.java | |
| @@ -45,9 +45,12 @@ public class ConcurrencyDependencyGraph { | |
| private final Set<MavenProject> finishedProjects = new HashSet<>(); | |
| + private final SmartProjectComparator projectComparator; | |
| + | |
| public ConcurrencyDependencyGraph(ProjectBuildList projectBuilds, ProjectDependencyGraph projectDependencyGraph) { | |
| this.projectDependencyGraph = projectDependencyGraph; | |
| this.projectBuilds = projectBuilds; | |
| + this.projectComparator = new SmartProjectComparator(projectDependencyGraph); | |
| } | |
| public int getNumberOfBuilds() { | |
| @@ -55,9 +58,9 @@ public int getNumberOfBuilds() { | |
| } | |
| /** | |
| - * Gets all the builds that have no reactor-dependencies | |
| + * Gets all the builds that have no reactor-dependencies, ordered by critical path priority | |
| * | |
| - * @return A set of all the initial builds | |
| + * @return A list of all the initial builds, ordered by priority (critical path first) | |
| */ | |
| public List<MavenProject> getRootSchedulableBuilds() { | |
| Set<MavenProject> result = new LinkedHashSet<>(); | |
| @@ -68,11 +71,15 @@ public List<MavenProject> getRootSchedulableBuilds() { | |
| result.add(projectBuild.getProject()); | |
| } | |
| } | |
| - if (result.isEmpty() && projectBuilds.size() > 0) { | |
| + if (result.isEmpty() && !projectBuilds.isEmpty()) { | |
| // Must return at least one project | |
| result.add(projectBuilds.get(0).getProject()); | |
| } | |
| - return new ArrayList<>(result); | |
| + | |
| + // Sort by critical path priority (projects with longer critical paths first) | |
| + List<MavenProject> sortedResult = new ArrayList<>(result); | |
| + sortedResult.sort(projectComparator.getComparator()); | |
| + return sortedResult; | |
| } | |
| /** | |
| @@ -96,6 +103,9 @@ private List<MavenProject> getSchedulableNewProcesses(MavenProject finishedProje | |
| result.add(dependentProject); | |
| } | |
| } | |
| + | |
| + // Sort newly schedulable projects by critical path priority | |
| + result.sort(projectComparator.getComparator()); | |
| return result; | |
| } | |
| @@ -140,4 +150,13 @@ public List<MavenProject> getActiveDependencies(MavenProject p) { | |
| activeDependencies.removeAll(finishedProjects); | |
| return activeDependencies; | |
| } | |
| + | |
| + /** | |
| + * Gets the smart project comparator used for critical path scheduling. | |
| + * | |
| + * @return the project comparator | |
| + */ | |
| + public SmartProjectComparator getProjectComparator() { | |
| + return projectComparator; | |
| + } | |
| } | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java | |
| index 18157c06f2..c61889cda5 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java | |
| @@ -99,6 +99,7 @@ public void build( | |
| try { | |
| ConcurrencyDependencyGraph analyzer = | |
| new ConcurrencyDependencyGraph(segmentProjectBuilds, session.getProjectDependencyGraph()); | |
| + | |
| multiThreadedProjectTaskSegmentBuild( | |
| analyzer, reactorContext, session, service, taskSegment, projectBuildMap); | |
| if (reactorContext.getReactorBuildStatus().isHalted()) { | |
| @@ -131,7 +132,7 @@ private void multiThreadedProjectTaskSegmentBuild( | |
| .map(Map.Entry::getKey) | |
| .collect(Collectors.toSet()); | |
| - // schedule independent projects | |
| + // schedule independent projects (ordered by critical path priority) | |
| for (MavenProject mavenProject : analyzer.getRootSchedulableBuilds()) { | |
| ProjectSegment projectSegment = projectBuildList.get(mavenProject); | |
| logger.debug("Scheduling: {}", projectSegment.getProject()); | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/SmartProjectComparator.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/SmartProjectComparator.java | |
| new file mode 100644 | |
| index 0000000000..829c6e4df7 | |
| --- /dev/null | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/SmartProjectComparator.java | |
| @@ -0,0 +1,117 @@ | |
| +/* | |
| + * 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.lifecycle.internal.builder.multithreaded; | |
| + | |
| +import java.util.Comparator; | |
| +import java.util.Map; | |
| +import java.util.concurrent.ConcurrentHashMap; | |
| +import java.util.function.ToLongFunction; | |
| + | |
| +import org.apache.maven.execution.ProjectDependencyGraph; | |
| +import org.apache.maven.project.MavenProject; | |
| + | |
| +/** | |
| + * Smart project comparator that orders projects based on critical path analysis. | |
| + * Projects with longer downstream dependency chains are prioritized to maximize | |
| + * parallel execution efficiency. | |
| + * | |
| + * <p>The algorithm calculates a weight for each project as: | |
| + * weight = 1 + max(downstream_project_weights) | |
| + * | |
| + * <p>Projects are then sorted by weight in descending order, ensuring that | |
| + * projects with longer dependency chains are built first. When projects have | |
| + * the same weight, they are ordered by project ID for deterministic results. | |
| + * | |
| + * <p><b>Example:</b> | |
| + * <p>Consider projects with dependencies: A → B → D, A → C → D | |
| + * <ul> | |
| + * <li>Project D: weight = 1 (no downstream dependencies)</li> | |
| + * <li>Project B: weight = 2 (1 + max(D=1))</li> | |
| + * <li>Project C: weight = 2 (1 + max(D=1))</li> | |
| + * <li>Project A: weight = 3 (1 + max(B=2, C=2))</li> | |
| + * </ul> | |
| + * <p>Build order: A (weight=3), then B and C (weight=2, ordered by project ID), then D (weight=1) | |
| + * <p>If projects have identical weights and IDs, the order is deterministic but may not preserve | |
| + * the original declaration order. | |
| + * | |
| + * @since 4.0.0 | |
| + */ | |
| +public class SmartProjectComparator { | |
| + | |
| + private final ProjectDependencyGraph dependencyGraph; | |
| + private final Map<MavenProject, Long> projectWeights; | |
| + private final Comparator<MavenProject> comparator; | |
| + | |
| + public SmartProjectComparator(ProjectDependencyGraph dependencyGraph) { | |
| + this.dependencyGraph = dependencyGraph; | |
| + this.projectWeights = new ConcurrentHashMap<>(); | |
| + this.comparator = createComparator(); | |
| + } | |
| + | |
| + /** | |
| + * Gets the comparator for ordering projects by critical path priority. | |
| + * | |
| + * @return comparator that orders projects with longer dependency chains first | |
| + */ | |
| + public Comparator<MavenProject> getComparator() { | |
| + return comparator; | |
| + } | |
| + | |
| + /** | |
| + * Gets the calculated weight for a project, representing its dependency chain length. | |
| + * | |
| + * @param project the project | |
| + * @return the project's weight (higher means longer dependency chain) | |
| + */ | |
| + public long getProjectWeight(MavenProject project) { | |
| + // First check if weight is already calculated | |
| + Long existingWeight = projectWeights.get(project); | |
| + if (existingWeight != null) { | |
| + return existingWeight; | |
| + } | |
| + | |
| + // Calculate weight without using computeIfAbsent to avoid recursive update issues | |
| + long weight = calculateWeight(project); | |
| + | |
| + // Use putIfAbsent to handle concurrent access safely | |
| + Long previousWeight = projectWeights.putIfAbsent(project, weight); | |
| + return previousWeight != null ? previousWeight : weight; | |
| + } | |
| + | |
| + private Comparator<MavenProject> createComparator() { | |
| + return Comparator.comparingLong((ToLongFunction<MavenProject>) this::getProjectWeight) | |
| + .reversed() // Higher weights first | |
| + .thenComparing(this::getProjectId); // Secondary sort for deterministic ordering | |
| + } | |
| + | |
| + private long calculateWeight(MavenProject project) { | |
| + // Calculate maximum weight of downstream dependencies | |
| + long maxDownstreamWeight = dependencyGraph.getDownstreamProjects(project, false).stream() | |
| + .mapToLong(this::getProjectWeight) | |
| + .max() | |
| + .orElse(0L); | |
| + | |
| + // Weight = 1 + max downstream weight (similar to Takari Smart Builder) | |
| + return 1L + maxDownstreamWeight; | |
| + } | |
| + | |
| + private String getProjectId(MavenProject project) { | |
| + return project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(); | |
| + } | |
| +} | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterException.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterException.java | |
| index 73a19257d0..296b5f7dbe 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterException.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginParameterException.java | |
| @@ -52,7 +52,7 @@ private static String format(List<Parameter> parameters) { | |
| StringBuilder buffer = new StringBuilder(128); | |
| if (parameters != null) { | |
| for (Parameter parameter : parameters) { | |
| - if (buffer.length() > 0) { | |
| + if (!buffer.isEmpty()) { | |
| buffer.append(", "); | |
| } | |
| buffer.append('\'').append(parameter.getName()).append('\''); | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionRequest.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionRequest.java | |
| index 92c05ca420..9bc9d39cde 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionRequest.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionRequest.java | |
| @@ -22,7 +22,9 @@ | |
| import org.eclipse.aether.graph.DependencyFilter; | |
| /** | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public class DefaultDependencyResolutionRequest implements DependencyResolutionRequest { | |
| private MavenProject project; | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionResult.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionResult.java | |
| index 9bb33ff58b..d813ccb8df 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionResult.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultDependencyResolutionResult.java | |
| @@ -28,7 +28,9 @@ | |
| import org.eclipse.aether.graph.DependencyNode; | |
| /** | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| class DefaultDependencyResolutionResult implements DependencyResolutionResult { | |
| private DependencyNode root; | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java | |
| index 47f252cd52..1de9eeccc5 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java | |
| @@ -44,7 +44,6 @@ | |
| 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.ArtifactCoordinates; | |
| import org.apache.maven.api.Language; | |
| @@ -105,7 +104,10 @@ | |
| /** | |
| * DefaultProjectBuilder | |
| + * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| @Named | |
| @Singleton | |
| public class DefaultProjectBuilder implements ProjectBuilder { | |
| @@ -503,10 +505,14 @@ List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive) throws | |
| .findAny() | |
| .orElse(null); | |
| if (cycle != null) { | |
| - throw new RuntimeException(new ProjectCycleException( | |
| + final CycleDetectedException cde = (CycleDetectedException) cycle.getException(); | |
| + throw new ProjectBuildingException( | |
| + null, | |
| "The projects in the reactor contain a cyclic reference: " + cycle.getMessage(), | |
| - (CycleDetectedException) cycle.getException())); | |
| + null, | |
| + cde); | |
| } | |
| + | |
| throw new ProjectBuildingException(results); | |
| } | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java | |
| index 7f1fbddcb8..343ebbc458 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java | |
| @@ -33,7 +33,10 @@ | |
| /** | |
| * DefaultProjectBuildingRequest | |
| + * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public class DefaultProjectBuildingRequest implements ProjectBuildingRequest { | |
| private RepositorySystemSession repositorySession; | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingResult.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingResult.java | |
| index 7c0eed6fce..7e6f8057b1 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingResult.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingResult.java | |
| @@ -27,7 +27,9 @@ | |
| /** | |
| * Collects the output of the project builder. | |
| * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| class DefaultProjectBuildingResult implements ProjectBuildingResult { | |
| private final String projectId; | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectDependenciesResolver.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectDependenciesResolver.java | |
| index 20123cc954..56c8339d46 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectDependenciesResolver.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectDependenciesResolver.java | |
| @@ -53,7 +53,9 @@ | |
| import org.slf4j.LoggerFactory; | |
| /** | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| @Named | |
| @Singleton | |
| public class DefaultProjectDependenciesResolver implements ProjectDependenciesResolver { | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionException.java b/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionException.java | |
| index 278e06ed19..d7392eed54 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionException.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionException.java | |
| @@ -23,7 +23,9 @@ | |
| import org.eclipse.aether.graph.Dependency; | |
| /** | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public class DependencyResolutionException extends Exception { | |
| private final transient DependencyResolutionResult result; | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionRequest.java b/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionRequest.java | |
| index 6388adf5e0..725c2978c9 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionRequest.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionRequest.java | |
| @@ -24,7 +24,9 @@ | |
| /** | |
| * A request to resolve the dependencies of a project. | |
| * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public interface DependencyResolutionRequest { | |
| /** | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionResult.java b/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionResult.java | |
| index ce02b39616..380f15388c 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionResult.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/DependencyResolutionResult.java | |
| @@ -26,7 +26,9 @@ | |
| /** | |
| * The result of a project dependency resolution. | |
| * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public interface DependencyResolutionResult { | |
| /** | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuilder.java | |
| index 2542269657..3f3f8b122a 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuilder.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuilder.java | |
| @@ -26,7 +26,10 @@ | |
| /** | |
| * Builds in-memory descriptions of projects. | |
| + * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public interface ProjectBuilder { | |
| /** | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java | |
| index d8b3ca5ae8..89ca05d43a 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingException.java | |
| @@ -21,8 +21,12 @@ | |
| import java.io.File; | |
| import java.util.List; | |
| +import org.apache.maven.model.building.ModelProblem; | |
| + | |
| /** | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public class ProjectBuildingException extends Exception { | |
| private final String projectId; | |
| @@ -59,7 +63,7 @@ protected ProjectBuildingException(String projectId, String message, File pomFil | |
| } | |
| public ProjectBuildingException(List<ProjectBuildingResult> results) { | |
| - super("Some problems were encountered while processing the POMs"); | |
| + super(createMessage(results)); | |
| this.projectId = ""; | |
| this.results = results; | |
| } | |
| @@ -97,4 +101,84 @@ private static String createMessage(String message, String projectId, File pomFi | |
| } | |
| return buffer.toString(); | |
| } | |
| + | |
| + private static String createMessage(List<ProjectBuildingResult> results) { | |
| + if (results == null || results.isEmpty()) { | |
| + return "Some problems were encountered while processing the POMs"; | |
| + } | |
| + | |
| + long totalProblems = 0; | |
| + long errorProblems = 0; | |
| + | |
| + for (ProjectBuildingResult result : results) { | |
| + List<ModelProblem> problems = result.getProblems(); | |
| + totalProblems += problems.size(); | |
| + | |
| + for (ModelProblem problem : problems) { | |
| + if (problem.getSeverity() != ModelProblem.Severity.WARNING) { | |
| + errorProblems++; | |
| + } | |
| + } | |
| + } | |
| + | |
| + StringBuilder buffer = new StringBuilder(1024); | |
| + buffer.append(totalProblems); | |
| + buffer.append(totalProblems == 1 ? " problem was " : " problems were "); | |
| + buffer.append("encountered while processing the POMs"); | |
| + | |
| + if (errorProblems > 0) { | |
| + buffer.append(" (") | |
| + .append(errorProblems) | |
| + .append(" ") | |
| + .append(errorProblems > 1 ? "errors" : "error") | |
| + .append(")"); | |
| + } | |
| + | |
| + buffer.append(":\n"); | |
| + | |
| + for (ProjectBuildingResult result : results) { | |
| + if (!result.getProblems().isEmpty()) { | |
| + String projectInfo = result.getProjectId(); | |
| + if (projectInfo.trim().isEmpty()) { | |
| + projectInfo = | |
| + result.getPomFile() != null ? result.getPomFile().getName() : "unknown project"; | |
| + } | |
| + | |
| + buffer.append("\n[").append(projectInfo).append("]\n"); | |
| + | |
| + for (ModelProblem problem : result.getProblems()) { | |
| + if (errorProblems > 0 && problem.getSeverity() == ModelProblem.Severity.WARNING) { | |
| + continue; | |
| + } | |
| + | |
| + buffer.append(" [").append(problem.getSeverity()).append("] "); | |
| + buffer.append(problem.getMessage()); | |
| + | |
| + String location = ""; | |
| + if (!problem.getSource().trim().isEmpty()) { | |
| + location = problem.getSource(); | |
| + } | |
| + if (problem.getLineNumber() > 0) { | |
| + if (!location.isEmpty()) { | |
| + location += ", "; | |
| + } | |
| + location += "line " + problem.getLineNumber(); | |
| + } | |
| + if (problem.getColumnNumber() > 0) { | |
| + if (!location.isEmpty()) { | |
| + location += ", "; | |
| + } | |
| + location += "column " + problem.getColumnNumber(); | |
| + } | |
| + | |
| + if (!location.isEmpty()) { | |
| + buffer.append(" @ ").append(location); | |
| + } | |
| + buffer.append("\n"); | |
| + } | |
| + } | |
| + } | |
| + | |
| + return buffer.toString(); | |
| + } | |
| } | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java | |
| index c2f8fb3bf3..cb32cd07fa 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java | |
| @@ -29,7 +29,10 @@ | |
| /** | |
| * ProjectBuildingRequest | |
| + * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public interface ProjectBuildingRequest { | |
| ProjectBuildingRequest setLocalRepository(ArtifactRepository localRepository); | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingResult.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingResult.java | |
| index 897b0555a0..ea589802b0 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingResult.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingResult.java | |
| @@ -26,7 +26,9 @@ | |
| /** | |
| * Collects the output of the project builder. | |
| * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public interface ProjectBuildingResult { | |
| /** | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectDependenciesResolver.java b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectDependenciesResolver.java | |
| index d5794bea94..5ab53b3a26 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectDependenciesResolver.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectDependenciesResolver.java | |
| @@ -21,7 +21,9 @@ | |
| /** | |
| * Resolves the transitive dependencies of a project. | |
| * | |
| + * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead | |
| */ | |
| +@Deprecated(since = "4.0.0") | |
| public interface ProjectDependenciesResolver { | |
| /** | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/artifact/DefaultProjectArtifactsCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/artifact/DefaultProjectArtifactsCache.java | |
| index 5b0665b5e3..40ed1720f5 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/artifact/DefaultProjectArtifactsCache.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/artifact/DefaultProjectArtifactsCache.java | |
| @@ -27,13 +27,12 @@ | |
| import java.util.HashSet; | |
| import java.util.LinkedHashSet; | |
| import java.util.List; | |
| -import java.util.Map; | |
| import java.util.Objects; | |
| import java.util.Set; | |
| -import java.util.concurrent.ConcurrentHashMap; | |
| import org.apache.maven.RepositoryUtils; | |
| import org.apache.maven.artifact.Artifact; | |
| +import org.apache.maven.impl.cache.Cache; | |
| import org.apache.maven.lifecycle.LifecycleExecutionException; | |
| import org.apache.maven.lifecycle.internal.SetWithResolutionResult; | |
| import org.apache.maven.project.MavenProject; | |
| @@ -158,8 +157,9 @@ public boolean equals(Object o) { | |
| } | |
| } | |
| - protected final Map<Key, CacheRecord> cache = new ConcurrentHashMap<>(); | |
| - protected final Map<Key, Key> keys = new ConcurrentHashMap<>(); | |
| + protected final Cache<Key, CacheRecord> cache = | |
| + Cache.newCache(Cache.ReferenceType.SOFT, "ProjectArtifactsCache-Records"); | |
| + protected final Cache<Key, Key> keys = Cache.newCache(Cache.ReferenceType.SOFT, "ProjectArtifactsCache-Keys"); | |
| @Override | |
| public Key createKey( | |
| @@ -201,18 +201,14 @@ public CacheRecord put(Key key, Set<Artifact> projectArtifacts) { | |
| throw new IllegalArgumentException("projectArtifacts must implement ArtifactsSetWithResult"); | |
| } | |
| - CacheRecord record = new CacheRecord(artifacts); | |
| - cache.put(key, record); | |
| - return record; | |
| + return cache.computeIfAbsent(key, k -> new CacheRecord(artifacts)); | |
| } | |
| @Override | |
| public CacheRecord put(Key key, LifecycleExecutionException exception) { | |
| Objects.requireNonNull(exception, "exception cannot be null"); | |
| assertUniqueKey(key); | |
| - CacheRecord record = new CacheRecord(exception); | |
| - cache.put(key, record); | |
| - return record; | |
| + return cache.computeIfAbsent(key, k -> new CacheRecord(exception)); | |
| } | |
| protected void assertUniqueKey(Key key) { | |
| diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java b/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java | |
| index 828e546c1c..0be344384a 100644 | |
| --- a/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java | |
| +++ b/impl/maven-core/src/main/java/org/apache/maven/project/collector/DefaultProjectsSelector.java | |
| @@ -75,7 +75,7 @@ public List<MavenProject> selectProjects(List<File> files, MavenExecutionRequest | |
| "{} {} encountered while building the effective model for '{}' (use -e to see details)", | |
| problemsCount, | |
| (problemsCount == 1) ? "problem was" : "problems were", | |
| - result.getProject().getId()); | |
| + result.getProjectId()); | |
| if (request.isShowErrors()) { // this means -e or -X (as -X enables -e as well) | |
| for (ModelProblem problem : result.getProblems()) { | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerTest.java b/impl/maven-core/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerTest.java | |
| index a603b2429e..122da1e15a 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerTest.java | |
| @@ -99,6 +99,6 @@ private String trimApt(String content, String type) { | |
| private String trimApt(String content) { | |
| content = content.replace('<', ' ').replace('>', ' ').trim(); | |
| - return (content.length() == 0) ? null : content; | |
| + return (content.isEmpty()) ? null : content; | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java b/impl/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java | |
| index 4347e1a8dc..eba03f5fa7 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/artifact/resolver/filter/ExclusionArtifactFilterTest.java | |
| @@ -26,8 +26,8 @@ | |
| import org.junit.jupiter.api.BeforeEach; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.CoreMatchers.is; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.Mockito.mock; | |
| import static org.mockito.Mockito.when; | |
| @@ -53,7 +53,7 @@ void testExcludeExact() { | |
| exclusion.setArtifactId("maven-core"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| + assertFalse(filter.include(artifact), "Artifact should be excluded by exact match"); | |
| } | |
| @Test | |
| @@ -63,7 +63,7 @@ void testExcludeNoMatch() { | |
| exclusion.setArtifactId("maven-model"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(true)); | |
| + assertTrue(filter.include(artifact), "Artifact should not be excluded when no match"); | |
| } | |
| @Test | |
| @@ -73,7 +73,7 @@ void testExcludeGroupIdWildcard() { | |
| exclusion.setArtifactId("maven-core"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| + assertFalse(filter.include(artifact), "Artifact should be excluded by groupId wildcard"); | |
| } | |
| @Test | |
| @@ -83,7 +83,9 @@ void testExcludeGroupIdWildcardNoMatch() { | |
| exclusion.setArtifactId("maven-compat"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(true)); | |
| + assertTrue( | |
| + filter.include(artifact), | |
| + "Artifact should not be excluded when groupId wildcard doesn't match artifactId"); | |
| } | |
| @Test | |
| @@ -93,7 +95,7 @@ void testExcludeArtifactIdWildcard() { | |
| exclusion.setArtifactId("*"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| + assertFalse(filter.include(artifact), "Artifact should be excluded by artifactId wildcard"); | |
| } | |
| @Test | |
| @@ -103,7 +105,9 @@ void testExcludeArtifactIdWildcardNoMatch() { | |
| exclusion.setArtifactId("*"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(true)); | |
| + assertTrue( | |
| + filter.include(artifact), | |
| + "Artifact should not be excluded when artifactId wildcard doesn't match groupId"); | |
| } | |
| @Test | |
| @@ -113,7 +117,7 @@ void testExcludeAllWildcard() { | |
| exclusion.setArtifactId("*"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| + assertFalse(filter.include(artifact), "Artifact should be excluded by all wildcard"); | |
| } | |
| @Test | |
| @@ -128,7 +132,7 @@ void testMultipleExclusionsExcludeArtifactIdWildcard() { | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Arrays.asList(exclusion1, exclusion2)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| + assertFalse(filter.include(artifact), "Artifact should be excluded by multiple exclusions"); | |
| } | |
| @Test | |
| @@ -143,7 +147,8 @@ void testMultipleExclusionsExcludeGroupIdWildcard() { | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Arrays.asList(exclusion1, exclusion2)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| + assertFalse( | |
| + filter.include(artifact), "Artifact should be excluded by multiple exclusions with groupId wildcard"); | |
| } | |
| @Test | |
| @@ -153,8 +158,8 @@ void testExcludeWithGlob() { | |
| exclusion.setArtifactId("maven-*"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| - assertThat(filter.include(artifact2), is(true)); | |
| + assertFalse(filter.include(artifact), "Maven artifact should be excluded by glob pattern"); | |
| + assertTrue(filter.include(artifact2), "JUnit artifact should not be excluded by maven-* glob pattern"); | |
| } | |
| @Test | |
| @@ -164,7 +169,7 @@ void testExcludeWithGlobStar() { | |
| exclusion.setArtifactId("maven-**"); | |
| ExclusionArtifactFilter filter = new ExclusionArtifactFilter(Collections.singletonList(exclusion)); | |
| - assertThat(filter.include(artifact), is(false)); | |
| - assertThat(filter.include(artifact2), is(true)); | |
| + assertFalse(filter.include(artifact), "Maven artifact should be excluded by glob star pattern"); | |
| + assertTrue(filter.include(artifact2), "JUnit artifact should not be excluded by maven-** glob star pattern"); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java | |
| index c90e75bece..411a483b22 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/classrealm/DefaultClassRealmManagerTest.java | |
| @@ -38,9 +38,8 @@ | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.endsWith; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.Mockito.calls; | |
| import static org.mockito.Mockito.inOrder; | |
| import static org.mockito.Mockito.mock; | |
| @@ -119,7 +118,9 @@ void testDebugEnabled() throws PlexusContainerException { | |
| assertEquals(classRealmManager.getMavenApiRealm(), classRealm.getParentClassLoader()); | |
| assertEquals("project>modelGroup1:modelArtifact1:modelVersion1", classRealm.getId()); | |
| assertEquals(1, classRealm.getURLs().length); | |
| - assertThat(classRealm.getURLs()[0].getPath(), endsWith("local/repository/some/path")); | |
| + assertTrue( | |
| + classRealm.getURLs()[0].getPath().endsWith("local/repository/some/path"), | |
| + "ClassRealm URL should end with local repository path"); | |
| verifier.verify(logger, calls(1)).debug("Importing foreign packages into class realm {}", "maven.api"); | |
| verifier.verify(logger, calls(1)).debug(" Imported: {} < {}", "group1:artifact1", "test"); | |
| @@ -153,7 +154,9 @@ void testDebugDisabled() throws PlexusContainerException { | |
| assertEquals(classRealmManager.getMavenApiRealm(), classRealm.getParentClassLoader()); | |
| assertEquals("project>modelGroup1:modelArtifact1:modelVersion1", classRealm.getId()); | |
| assertEquals(1, classRealm.getURLs().length); | |
| - assertThat(classRealm.getURLs()[0].getPath(), endsWith("local/repository/some/path")); | |
| + assertTrue( | |
| + classRealm.getURLs()[0].getPath().endsWith("local/repository/some/path"), | |
| + "ClassRealm URL should end with local repository path"); | |
| verifier.verify(logger, calls(1)).debug("Importing foreign packages into class realm {}", "maven.api"); | |
| verifier.verify(logger, calls(1)).debug(" Imported: {} < {}", "group1:artifact1", "test"); | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedCompositeBeanHelperTest.java b/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedCompositeBeanHelperTest.java | |
| index 031c62b69b..ab954855a8 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedCompositeBeanHelperTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/configuration/internal/EnhancedCompositeBeanHelperTest.java | |
| @@ -124,7 +124,7 @@ void testPerformanceWithRepeatedCalls() throws Exception { | |
| // Second call should be faster (though this is not guaranteed in all environments) | |
| // We mainly verify that both calls work correctly | |
| - assertTrue(time2 >= 0); // Just verify it completed | |
| + assertTrue(time2 >= 0, "Expected " + time2 + " to be >= " + 0); // Just verify it completed | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionAnalyzerTest.java b/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionAnalyzerTest.java | |
| index 6ed0ecfb8b..010fef95c0 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionAnalyzerTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionAnalyzerTest.java | |
| @@ -28,8 +28,9 @@ | |
| import static java.util.Arrays.asList; | |
| import static java.util.Collections.singletonList; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.is; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| class DefaultBuildResumptionAnalyzerTest { | |
| private final DefaultBuildResumptionAnalyzer analyzer = new DefaultBuildResumptionAnalyzer(); | |
| @@ -49,8 +50,8 @@ void resumeFromGetsDetermined() { | |
| Optional<BuildResumptionData> result = analyzer.determineBuildResumptionData(executionResult); | |
| - assertThat(result.isPresent(), is(true)); | |
| - assertThat(result.get().getRemainingProjects(), is(asList("test:B"))); | |
| + assertTrue(result.isPresent(), "Expected " + result + ".isPresent() to return true"); | |
| + assertEquals(asList("test:B"), result.get().getRemainingProjects()); | |
| } | |
| @Test | |
| @@ -61,7 +62,7 @@ void resumeFromIsIgnoredWhenFirstProjectFails() { | |
| Optional<BuildResumptionData> result = analyzer.determineBuildResumptionData(executionResult); | |
| - assertThat(result.isPresent(), is(false)); | |
| + assertFalse(result.isPresent(), "Expected " + result + ".isPresent() to return false"); | |
| } | |
| @Test | |
| @@ -73,8 +74,8 @@ void projectsSucceedingAfterFailedProjectsAreExcluded() { | |
| Optional<BuildResumptionData> result = analyzer.determineBuildResumptionData(executionResult); | |
| - assertThat(result.isPresent(), is(true)); | |
| - assertThat(result.get().getRemainingProjects(), is(asList("test:B"))); | |
| + assertTrue(result.isPresent(), "Expected " + result + ".isPresent() to return true"); | |
| + assertEquals(asList("test:B"), result.get().getRemainingProjects()); | |
| } | |
| @Test | |
| @@ -87,8 +88,8 @@ void projectsDependingOnFailedProjectsAreNotExcluded() { | |
| Optional<BuildResumptionData> result = analyzer.determineBuildResumptionData(executionResult); | |
| - assertThat(result.isPresent(), is(true)); | |
| - assertThat(result.get().getRemainingProjects(), is(asList("test:B", "test:C"))); | |
| + assertTrue(result.isPresent(), "Expected " + result + ".isPresent() to return true"); | |
| + assertEquals(asList("test:B", "test:C"), result.get().getRemainingProjects()); | |
| } | |
| @Test | |
| @@ -101,8 +102,8 @@ void projectsFailingAfterAnotherFailedProjectAreNotExcluded() { | |
| Optional<BuildResumptionData> result = analyzer.determineBuildResumptionData(executionResult); | |
| - assertThat(result.isPresent(), is(true)); | |
| - assertThat(result.get().getRemainingProjects(), is(asList("test:B", "test:D"))); | |
| + assertTrue(result.isPresent(), "Expected " + result + ".isPresent() to return true"); | |
| + assertEquals(asList("test:B", "test:D"), result.get().getRemainingProjects()); | |
| } | |
| private MavenProject createMavenProject(String artifactId) { | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionDataRepositoryTest.java b/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionDataRepositoryTest.java | |
| index 009c7e7529..1dbb0e5ee3 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionDataRepositoryTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultBuildResumptionDataRepositoryTest.java | |
| @@ -27,10 +27,8 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static java.util.Collections.singleton; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.containsInAnyOrder; | |
| -import static org.hamcrest.Matchers.empty; | |
| -import static org.hamcrest.Matchers.is; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| class DefaultBuildResumptionDataRepositoryTest { | |
| private final DefaultBuildResumptionDataRepository repository = new DefaultBuildResumptionDataRepository(); | |
| @@ -43,7 +41,7 @@ void resumeFromPropertyGetsApplied() { | |
| repository.applyResumptionProperties(request, properties); | |
| - assertThat(request.getProjectActivation().getOptionalActiveProjectSelectors(), is(singleton(":module-a"))); | |
| + assertEquals(singleton(":module-a"), request.getProjectActivation().getOptionalActiveProjectSelectors()); | |
| } | |
| @Test | |
| @@ -55,7 +53,7 @@ void resumeFromPropertyDoesNotOverrideExistingRequestParameters() { | |
| repository.applyResumptionProperties(request, properties); | |
| - assertThat(request.getResumeFrom(), is(":module-b")); | |
| + assertEquals(":module-b", request.getResumeFrom()); | |
| } | |
| @Test | |
| @@ -69,9 +67,11 @@ void projectsFromPropertyGetsAddedToExistingRequestParameters() { | |
| repository.applyResumptionProperties(request, properties); | |
| - assertThat( | |
| - request.getProjectActivation().getOptionalActiveProjectSelectors(), | |
| - containsInAnyOrder(":module-a", ":module-b", ":module-c")); | |
| + var selectors = request.getProjectActivation().getOptionalActiveProjectSelectors(); | |
| + assertEquals(3, selectors.size()); | |
| + assertTrue(selectors.contains(":module-a"), "Expected selectors " + selectors + " to contain :module-a"); | |
| + assertTrue(selectors.contains(":module-b"), "Expected selectors " + selectors + " to contain :module-b"); | |
| + assertTrue(selectors.contains(":module-c"), "Expected selectors " + selectors + " to contain :module-c"); | |
| } | |
| @Test | |
| @@ -82,7 +82,9 @@ void selectedProjectsAreNotAddedWhenPropertyValueIsEmpty() { | |
| repository.applyResumptionProperties(request, properties); | |
| - assertThat(request.getProjectActivation().getOptionalActiveProjectSelectors(), is(empty())); | |
| + assertTrue(request.getProjectActivation() | |
| + .getOptionalActiveProjectSelectors() | |
| + .isEmpty()); | |
| } | |
| @Test | |
| @@ -95,8 +97,7 @@ void applyResumptionDataShouldLoadData() { | |
| repository.applyResumptionData(request, rootProject); | |
| - assertThat( | |
| - request.getProjectActivation().getOptionalActiveProjectSelectors(), | |
| - containsInAnyOrder("example:module-c")); | |
| + assertEquals( | |
| + singleton("example:module-c"), request.getProjectActivation().getOptionalActiveProjectSelectors()); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultMavenExecutionTest.java b/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultMavenExecutionTest.java | |
| index 605137abae..b486b6abc3 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultMavenExecutionTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/execution/DefaultMavenExecutionTest.java | |
| @@ -23,11 +23,9 @@ | |
| import org.apache.maven.project.MavenProject; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.empty; | |
| -import static org.hamcrest.Matchers.is; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertNotSame; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| */ | |
| @@ -46,6 +44,8 @@ void testResultWithNullTopologicallySortedProjectsIsEmptyList() { | |
| result.setTopologicallySortedProjects(null); | |
| List<MavenProject> projects = result.getTopologicallySortedProjects(); | |
| assertNotNull(projects); | |
| - assertThat(projects, is(empty())); | |
| + assertTrue( | |
| + projects.isEmpty(), | |
| + "Expected collection to be empty but had " + projects.size() + " elements: " + projects); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java | |
| index cabbf41e18..07bcbf90d4 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultGraphBuilderTest.java | |
| @@ -63,8 +63,9 @@ | |
| import static org.apache.maven.execution.MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM; | |
| import static org.apache.maven.execution.MavenExecutionRequest.REACTOR_MAKE_UPSTREAM; | |
| import static org.apache.maven.graph.DefaultGraphBuilderTest.ScenarioBuilder.scenario; | |
| -import static org.assertj.core.api.Assertions.assertThat; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.ArgumentMatchers.any; | |
| import static org.mockito.ArgumentMatchers.anyBoolean; | |
| import static org.mockito.ArgumentMatchers.anyList; | |
| @@ -314,25 +315,22 @@ void testGetReactorProjects( | |
| // Then | |
| if (parameterExpectedResult instanceof SelectedProjectsResult selectedProjectsResult) { | |
| - assertThat(result.hasErrors()) | |
| - .withFailMessage("Expected result not to have errors") | |
| - .isFalse(); | |
| + assertFalse(result.hasErrors(), "Expected result not to have errors"); | |
| List<String> expectedProjectNames = selectedProjectsResult.projectNames; | |
| List<MavenProject> actualReactorProjects = result.get().getSortedProjects(); | |
| List<MavenProject> expectedReactorProjects = | |
| expectedProjectNames.stream().map(artifactIdProjectMap::get).collect(toList()); | |
| assertEquals(expectedReactorProjects, actualReactorProjects, parameterDescription); | |
| } else { | |
| - assertThat(result.hasErrors()) | |
| - .withFailMessage("Expected result to have errors") | |
| - .isTrue(); | |
| + assertTrue(result.hasErrors(), "Expected result to have errors"); | |
| Class<? extends Throwable> expectedException = ((ExceptionThrown) parameterExpectedResult).expected; | |
| String partOfMessage = ((ExceptionThrown) parameterExpectedResult).partOfMessage; | |
| - assertThat(result.getProblems()).hasSize(1); | |
| - result.getProblems().forEach(p -> assertThat(p.getException()) | |
| - .isInstanceOf(expectedException) | |
| - .hasMessageContaining(partOfMessage)); | |
| + assertEquals(1, ((Collection) result.getProblems()).size()); | |
| + result.getProblems().forEach(p -> { | |
| + assertTrue(expectedException.isInstance(p.getException())); | |
| + assertTrue(p.getException().getMessage().contains(partOfMessage)); | |
| + }); | |
| } | |
| } | |
| @@ -368,9 +366,7 @@ void testProcessPackagingAttribute() throws ProjectBuildingException { | |
| Result<ProjectDependencyGraph> result = graphBuilder.build(session); | |
| - assertThat(result.hasErrors()) | |
| - .withFailMessage("Expected result not to have errors") | |
| - .isFalse(); | |
| + assertFalse(result.hasErrors(), "Expected result not to have errors"); | |
| List<MavenProject> actualReactorProjects = result.get().getSortedProjects(); | |
| assertEquals(2, actualReactorProjects.size()); | |
| assertEquals("pom", actualReactorProjects.get(1).getPackaging()); | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java b/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java | |
| index 090702135f..9774625ccf 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/graph/DefaultProjectDependencyGraphTest.java | |
| @@ -100,8 +100,8 @@ public void testGetDownstreamDoesNotDuplicateProjects() throws CycleDetectedExce | |
| graph = new FilteredProjectDependencyGraph(graph, Arrays.asList(aProject, dProject, eProject)); | |
| final List<MavenProject> downstreamProjects = graph.getDownstreamProjects(aProject, false); | |
| assertEquals(2, downstreamProjects.size()); | |
| - assertTrue(downstreamProjects.contains(dProject)); | |
| - assertTrue(downstreamProjects.contains(eProject)); | |
| + assertTrue(downstreamProjects.contains(dProject), "Expected " + downstreamProjects + " to contain " + dProject); | |
| + assertTrue(downstreamProjects.contains(eProject), "Expected " + downstreamProjects + " to contain " + eProject); | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/graph/ProjectSelectorTest.java b/impl/maven-core/src/test/java/org/apache/maven/graph/ProjectSelectorTest.java | |
| index 2cdeac39b8..9c897f8bec 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/graph/ProjectSelectorTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/graph/ProjectSelectorTest.java | |
| @@ -35,13 +35,11 @@ | |
| import org.junit.jupiter.params.provider.EmptySource; | |
| import org.junit.jupiter.params.provider.ValueSource; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.contains; | |
| -import static org.hamcrest.Matchers.containsString; | |
| -import static org.hamcrest.Matchers.is; | |
| -import static org.hamcrest.Matchers.notNullValue; | |
| -import static org.hamcrest.Matchers.nullValue; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.Mockito.mock; | |
| import static org.mockito.Mockito.when; | |
| @@ -55,7 +53,7 @@ void getBaseDirectoryFromRequestWhenDirectoryIsNullReturnNull() { | |
| final File baseDirectoryFromRequest = sut.getBaseDirectoryFromRequest(mavenExecutionRequest); | |
| - assertThat(baseDirectoryFromRequest, nullValue()); | |
| + assertNull(baseDirectoryFromRequest); | |
| } | |
| @Test | |
| @@ -64,8 +62,8 @@ void getBaseDirectoryFromRequestWhenDirectoryIsValidReturnFile() { | |
| final File baseDirectoryFromRequest = sut.getBaseDirectoryFromRequest(mavenExecutionRequest); | |
| - assertThat(baseDirectoryFromRequest, notNullValue()); | |
| - assertThat(baseDirectoryFromRequest.getPath(), is(new File("path/to/file").getPath())); | |
| + assertNotNull(baseDirectoryFromRequest); | |
| + assertEquals(new File("path/to/file").getPath(), baseDirectoryFromRequest.getPath()); | |
| } | |
| @ParameterizedTest | |
| @@ -73,14 +71,14 @@ void getBaseDirectoryFromRequestWhenDirectoryIsValidReturnFile() { | |
| @EmptySource | |
| void isMatchingProjectNoMatchOnSelectorReturnsFalse(String selector) { | |
| final boolean result = sut.isMatchingProject(createMavenProject("maven-core"), selector, null); | |
| - assertThat(result, is(false)); | |
| + assertEquals(false, result); | |
| } | |
| @ParameterizedTest | |
| @ValueSource(strings = {":maven-core", "org.apache.maven:maven-core"}) | |
| void isMatchingProjectMatchOnSelectorReturnsTrue(String selector) { | |
| final boolean result = sut.isMatchingProject(createMavenProject("maven-core"), selector, null); | |
| - assertThat(result, is(true)); | |
| + assertEquals(true, result); | |
| } | |
| @Test | |
| @@ -93,7 +91,7 @@ void isMatchingProjectMatchOnFileReturnsTrue() throws IOException { | |
| final boolean result = sut.isMatchingProject(mavenProject, selector, tempFile.getParentFile()); | |
| tempFile.delete(); | |
| - assertThat(result, is(true)); | |
| + assertEquals(true, result); | |
| } | |
| @Test | |
| @@ -107,7 +105,7 @@ void isMatchingProjectMatchOnDirectoryReturnsTrue(@TempDir File tempDir) { | |
| final boolean result = sut.isMatchingProject(mavenProject, selector, tempDir); | |
| tempProjectDir.delete(); | |
| - assertThat(result, is(true)); | |
| + assertEquals(true, result); | |
| } | |
| @Test | |
| @@ -122,8 +120,8 @@ void getOptionalProjectsBySelectorsReturnsMatches() { | |
| final Set<MavenProject> optionalProjectsBySelectors = | |
| sut.getOptionalProjectsBySelectors(mavenExecutionRequest, listOfProjects, selectors); | |
| - assertThat(optionalProjectsBySelectors.size(), is(1)); | |
| - assertThat(optionalProjectsBySelectors, contains(mavenProject)); | |
| + assertEquals(1, optionalProjectsBySelectors.size()); | |
| + assertEquals(List.of(mavenProject), List.copyOf(optionalProjectsBySelectors)); | |
| } | |
| @Test | |
| @@ -138,8 +136,8 @@ void getRequiredProjectsBySelectorsThrowsMavenExecutionException() { | |
| final MavenExecutionException exception = assertThrows( | |
| MavenExecutionException.class, | |
| () -> sut.getRequiredProjectsBySelectors(mavenExecutionRequest, listOfProjects, selectors)); | |
| - assertThat(exception.getMessage(), containsString("Could not find")); | |
| - assertThat(exception.getMessage(), containsString(":required")); | |
| + assertTrue(exception.getMessage().contains("Could not find")); | |
| + assertTrue(exception.getMessage().contains(":required")); | |
| } | |
| @Test | |
| @@ -153,8 +151,8 @@ void getRequiredProjectsBySelectorsReturnsProject() throws MavenExecutionExcepti | |
| final Set<MavenProject> requiredProjectsBySelectors = | |
| sut.getRequiredProjectsBySelectors(mavenExecutionRequest, listOfProjects, selectors); | |
| - assertThat(requiredProjectsBySelectors.size(), is(1)); | |
| - assertThat(requiredProjectsBySelectors, contains(mavenProject)); | |
| + assertEquals(1, requiredProjectsBySelectors.size()); | |
| + assertEquals(List.of(mavenProject), List.copyOf(requiredProjectsBySelectors)); | |
| } | |
| @Test | |
| @@ -172,8 +170,8 @@ void getRequiredProjectsBySelectorsReturnsProjectWithChildProjects() throws Mave | |
| final Set<MavenProject> requiredProjectsBySelectors = | |
| sut.getRequiredProjectsBySelectors(mavenExecutionRequest, listOfProjects, selectors); | |
| - assertThat(requiredProjectsBySelectors.size(), is(2)); | |
| - assertThat(requiredProjectsBySelectors, contains(mavenProject, child)); | |
| + assertEquals(2, requiredProjectsBySelectors.size()); | |
| + assertEquals(List.of(mavenProject, child), List.copyOf(requiredProjectsBySelectors)); | |
| } | |
| @Test | |
| @@ -191,8 +189,8 @@ void getOptionalProjectsBySelectorsReturnsProjectWithChildProjects() { | |
| final Set<MavenProject> optionalProjectsBySelectors = | |
| sut.getOptionalProjectsBySelectors(mavenExecutionRequest, listOfProjects, selectors); | |
| - assertThat(optionalProjectsBySelectors.size(), is(2)); | |
| - assertThat(optionalProjectsBySelectors, contains(mavenProject, child)); | |
| + assertEquals(2, optionalProjectsBySelectors.size()); | |
| + assertEquals(List.of(mavenProject, child), List.copyOf(optionalProjectsBySelectors)); | |
| } | |
| private MavenProject createMavenProject(String artifactId) { | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListenerTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListenerTest.java | |
| index 8e7210b979..55698bb093 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListenerTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListenerTest.java | |
| @@ -27,10 +27,10 @@ | |
| import org.eclipse.aether.repository.LocalRepository; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.CoreMatchers.equalTo; | |
| -import static org.hamcrest.CoreMatchers.nullValue; | |
| -import static org.hamcrest.CoreMatchers.sameInstance; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertNull; | |
| +import static org.junit.jupiter.api.Assertions.assertSame; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.Mockito.mock; | |
| import static org.mockito.Mockito.when; | |
| @@ -51,12 +51,9 @@ void isLocalRepositoryArtifactTest() { | |
| Artifact nonLocalReposioryArtifact = mock(Artifact.class); | |
| when(nonLocalReposioryArtifact.getFile()).thenReturn(new File("something/completely/different")); | |
| - assertThat( | |
| - ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, localRepositoryArtifact), | |
| - equalTo(true)); | |
| - assertThat( | |
| - ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, nonLocalReposioryArtifact), | |
| - equalTo(false)); | |
| + assertTrue(ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, localRepositoryArtifact)); | |
| + assertFalse( | |
| + ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, nonLocalReposioryArtifact)); | |
| } | |
| @Test | |
| @@ -69,16 +66,14 @@ void isMissingArtifactTest() { | |
| Artifact localRepositoryArtifact = mock(Artifact.class); | |
| when(localRepositoryArtifact.getFile()).thenReturn(null); | |
| - assertThat( | |
| - ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, localRepositoryArtifact), | |
| - equalTo(true)); | |
| + assertTrue(ReverseTreeRepositoryListener.isLocalRepositoryArtifactOrMissing(session, localRepositoryArtifact)); | |
| } | |
| @Test | |
| void lookupCollectStepDataTest() { | |
| RequestTrace doesNotHaveIt = | |
| RequestTrace.newChild(null, "foo").newChild("bar").newChild("baz"); | |
| - assertThat(ReverseTreeRepositoryListener.lookupCollectStepData(doesNotHaveIt), nullValue()); | |
| + assertNull(ReverseTreeRepositoryListener.lookupCollectStepData(doesNotHaveIt)); | |
| final CollectStepData data = mock(CollectStepData.class); | |
| @@ -86,18 +81,18 @@ void lookupCollectStepDataTest() { | |
| .newChild("foo") | |
| .newChild("bar") | |
| .newChild("baz"); | |
| - assertThat(ReverseTreeRepositoryListener.lookupCollectStepData(haveItFirst), sameInstance(data)); | |
| + assertSame(data, ReverseTreeRepositoryListener.lookupCollectStepData(haveItFirst)); | |
| RequestTrace haveItLast = RequestTrace.newChild(null, "foo") | |
| .newChild("bar") | |
| .newChild("baz") | |
| .newChild(data); | |
| - assertThat(ReverseTreeRepositoryListener.lookupCollectStepData(haveItLast), sameInstance(data)); | |
| + assertSame(data, ReverseTreeRepositoryListener.lookupCollectStepData(haveItLast)); | |
| RequestTrace haveIt = RequestTrace.newChild(null, "foo") | |
| .newChild("bar") | |
| .newChild(data) | |
| .newChild("baz"); | |
| - assertThat(ReverseTreeRepositoryListener.lookupCollectStepData(haveIt), sameInstance(data)); | |
| + assertSame(data, ReverseTreeRepositoryListener.lookupCollectStepData(haveIt)); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/PropertiesAsMapTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/PropertiesAsMapTest.java | |
| index b0afd8632c..b3b0ec7f2e 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/PropertiesAsMapTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/PropertiesAsMapTest.java | |
| @@ -47,13 +47,13 @@ void testPropertiesAsMap() { | |
| assertEquals(2, set.size()); | |
| Iterator<Entry<String, String>> iterator = set.iterator(); | |
| assertNotNull(iterator); | |
| - assertTrue(iterator.hasNext()); | |
| - assertTrue(iterator.hasNext()); | |
| + assertTrue(iterator.hasNext(), "Expected " + iterator + ".hasNext() to return true"); | |
| + assertTrue(iterator.hasNext(), "Expected " + iterator + ".hasNext() to return true"); | |
| Entry<String, String> entry = iterator.next(); | |
| assertNotNull(entry); | |
| entry = iterator.next(); | |
| assertNotNull(entry); | |
| assertThrows(NoSuchElementException.class, () -> iterator.next()); | |
| - assertFalse(iterator.hasNext()); | |
| + assertFalse(iterator.hasNext(), "Expected " + iterator + ".hasNext() to return false"); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java | |
| index 82526ddebd..04802ccbee 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java | |
| @@ -157,7 +157,7 @@ void testCreateAndResolveArtifact() { | |
| assertNotNull(resolved); | |
| assertNotNull(resolved.getPath()); | |
| Optional<Path> op = session.getArtifactPath(resolved); | |
| - assertTrue(op.isPresent()); | |
| + assertTrue(op.isPresent(), "Expected " + op + ".isPresent() to return true"); | |
| assertEquals(resolved.getPath(), op.get()); | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java | |
| index ec08041f24..37c98e39b5 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/internal/transformation/impl/ConsumerPomArtifactTransformerTest.java | |
| @@ -23,17 +23,28 @@ | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| +import java.util.Collection; | |
| +import java.util.List; | |
| +import java.util.Map; | |
| +import org.apache.maven.api.Constants; | |
| import org.apache.maven.model.Model; | |
| import org.apache.maven.model.v4.MavenStaxReader; | |
| import org.apache.maven.project.MavenProject; | |
| import org.eclipse.aether.RepositorySystemSession; | |
| import org.eclipse.aether.SessionData; | |
| +import org.eclipse.aether.artifact.Artifact; | |
| +import org.eclipse.aether.artifact.DefaultArtifact; | |
| +import org.eclipse.aether.deployment.DeployRequest; | |
| +import org.eclipse.aether.installation.InstallRequest; | |
| import org.junit.jupiter.api.Test; | |
| import org.mockito.Mockito; | |
| -import org.xmlunit.assertj.XmlAssert; | |
| +import org.xmlunit.builder.DiffBuilder; | |
| +import org.xmlunit.diff.Diff; | |
| -import static org.assertj.core.api.Assertions.assertThat; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.Mockito.when; | |
| class ConsumerPomArtifactTransformerTest { | |
| @@ -62,7 +73,12 @@ void transform() throws Exception { | |
| t.transform(project, systemSessionMock, beforePomFile, tempFile); | |
| } | |
| - XmlAssert.assertThat(tempFile.toFile()).and(afterPomFile.toFile()).areIdentical(); | |
| + Diff diff = DiffBuilder.compare(afterPomFile.toFile()) | |
| + .withTest(tempFile.toFile()) | |
| + .ignoreComments() | |
| + .ignoreWhitespace() | |
| + .build(); | |
| + assertFalse(diff.hasDifferences(), "XML files should be identical: " + diff.toString()); | |
| } | |
| @Test | |
| @@ -89,7 +105,12 @@ void transformJarConsumerPom() throws Exception { | |
| t.transform(project, systemSessionMock, beforePomFile, tempFile); | |
| } | |
| - XmlAssert.assertThat(afterPomFile.toFile()).and(tempFile.toFile()).areIdentical(); | |
| + Diff diff = DiffBuilder.compare(afterPomFile.toFile()) | |
| + .withTest(tempFile.toFile()) | |
| + .ignoreComments() | |
| + .ignoreWhitespace() | |
| + .build(); | |
| + assertFalse(diff.hasDifferences(), "XML files should be identical: " + diff.toString()); | |
| } | |
| @Test | |
| @@ -103,6 +124,158 @@ void injectTransformedArtifactsWithoutPomShouldNotInjectAnyArtifacts() throws IO | |
| new ConsumerPomArtifactTransformer((session, project, src) -> null) | |
| .injectTransformedArtifacts(systemSessionMock, emptyProject); | |
| - assertThat(emptyProject.getAttachedArtifacts()).isEmpty(); | |
| + assertTrue(emptyProject.getAttachedArtifacts().isEmpty()); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomEnabledByDefault() { | |
| + // Test that build POM deployment is enabled by default | |
| + ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null); | |
| + | |
| + RepositorySystemSession session = createMockSession(Map.of()); | |
| + DeployRequest request = createDeployRequestWithConsumerPom(); | |
| + | |
| + DeployRequest result = transformer.remapDeployArtifacts(session, request); | |
| + | |
| + // Should have both consumer POM (no classifier) and build POM (with "build" classifier) | |
| + Collection<Artifact> artifacts = result.getArtifacts(); | |
| + assertEquals(3, artifacts.size()); // original jar + consumer pom + build pom | |
| + | |
| + assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier()))); | |
| + assertTrue( | |
| + artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier()))); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomDisabled() { | |
| + // Test that build POM deployment can be disabled | |
| + ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null); | |
| + | |
| + Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); | |
| + RepositorySystemSession session = createMockSession(configProps); | |
| + DeployRequest request = createDeployRequestWithConsumerPom(); | |
| + | |
| + DeployRequest result = transformer.remapDeployArtifacts(session, request); | |
| + | |
| + // Should have only consumer POM (no classifier), no build POM | |
| + Collection<Artifact> artifacts = result.getArtifacts(); | |
| + assertEquals(2, artifacts.size()); // original jar + consumer pom (no build pom) | |
| + | |
| + assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier()))); | |
| + assertTrue( | |
| + artifacts.stream().noneMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier()))); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomExplicitlyEnabled() { | |
| + // Test that build POM deployment can be explicitly enabled | |
| + ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null); | |
| + | |
| + Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "true"); | |
| + RepositorySystemSession session = createMockSession(configProps); | |
| + DeployRequest request = createDeployRequestWithConsumerPom(); | |
| + | |
| + DeployRequest result = transformer.remapDeployArtifacts(session, request); | |
| + | |
| + // Should have both consumer POM (no classifier) and build POM (with "build" classifier) | |
| + Collection<Artifact> artifacts = result.getArtifacts(); | |
| + assertEquals(3, artifacts.size()); // original jar + consumer pom + build pom | |
| + | |
| + assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier()))); | |
| + assertTrue( | |
| + artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier()))); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployBuildPomWithBooleanValue() { | |
| + // Test that build POM deployment works with Boolean values (not just strings) | |
| + ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null); | |
| + | |
| + Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, Boolean.FALSE); | |
| + RepositorySystemSession session = createMockSession(configProps); | |
| + DeployRequest request = createDeployRequestWithConsumerPom(); | |
| + | |
| + DeployRequest result = transformer.remapDeployArtifacts(session, request); | |
| + | |
| + // Should have only consumer POM (no classifier), no build POM | |
| + Collection<Artifact> artifacts = result.getArtifacts(); | |
| + assertEquals(2, artifacts.size()); // original jar + consumer pom (no build pom) | |
| + | |
| + assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier()))); | |
| + assertTrue( | |
| + artifacts.stream().noneMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier()))); | |
| + } | |
| + | |
| + @Test | |
| + void testInstallAlwaysIncludesBuildPom() { | |
| + // Test that install always includes build POM regardless of the deployment setting | |
| + ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null); | |
| + | |
| + Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); | |
| + RepositorySystemSession session = createMockSession(configProps); | |
| + InstallRequest request = createInstallRequestWithConsumerPom(); | |
| + | |
| + InstallRequest result = transformer.remapInstallArtifacts(session, request); | |
| + | |
| + // Should have both consumer POM and build POM even when deployment is disabled | |
| + Collection<Artifact> artifacts = result.getArtifacts(); | |
| + assertEquals(3, artifacts.size()); // original jar + consumer pom + build pom | |
| + | |
| + assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier()))); | |
| + assertTrue( | |
| + artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier()))); | |
| + } | |
| + | |
| + @Test | |
| + void testDeployWithoutConsumerPomIsUnaffected() { | |
| + // Test that requests without consumer POMs are not affected by the setting | |
| + ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null); | |
| + | |
| + Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false"); | |
| + RepositorySystemSession session = createMockSession(configProps); | |
| + DeployRequest request = createDeployRequestWithoutConsumerPom(); | |
| + | |
| + DeployRequest result = transformer.remapDeployArtifacts(session, request); | |
| + | |
| + // Should be unchanged since there's no consumer POM | |
| + assertEquals(request.getArtifacts(), result.getArtifacts()); | |
| + } | |
| + | |
| + private RepositorySystemSession createMockSession(Map<String, Object> configProperties) { | |
| + RepositorySystemSession session = Mockito.mock(RepositorySystemSession.class); | |
| + when(session.getConfigProperties()).thenReturn(configProperties); | |
| + return session; | |
| + } | |
| + | |
| + private DeployRequest createDeployRequestWithConsumerPom() { | |
| + DeployRequest request = new DeployRequest(); | |
| + List<Artifact> artifacts = List.of( | |
| + new DefaultArtifact("com.example", "test", "", "jar", "1.0.0"), | |
| + new DefaultArtifact("com.example", "test", "", "pom", "1.0.0"), // main POM | |
| + new DefaultArtifact("com.example", "test", "consumer", "pom", "1.0.0") // consumer POM | |
| + ); | |
| + request.setArtifacts(artifacts); | |
| + return request; | |
| + } | |
| + | |
| + private InstallRequest createInstallRequestWithConsumerPom() { | |
| + InstallRequest request = new InstallRequest(); | |
| + List<Artifact> artifacts = List.of( | |
| + new DefaultArtifact("com.example", "test", "", "jar", "1.0.0"), | |
| + new DefaultArtifact("com.example", "test", "", "pom", "1.0.0"), // main POM | |
| + new DefaultArtifact("com.example", "test", "consumer", "pom", "1.0.0") // consumer POM | |
| + ); | |
| + request.setArtifacts(artifacts); | |
| + return request; | |
| + } | |
| + | |
| + private DeployRequest createDeployRequestWithoutConsumerPom() { | |
| + DeployRequest request = new DeployRequest(); | |
| + List<Artifact> artifacts = List.of( | |
| + new DefaultArtifact("com.example", "test", "", "jar", "1.0.0"), | |
| + new DefaultArtifact("com.example", "test", "", "pom", "1.0.0") // only main POM | |
| + ); | |
| + request.setArtifacts(artifacts); | |
| + return request; | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java | |
| index 0856e5ce6e..0b36378871 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java | |
| @@ -34,10 +34,7 @@ | |
| import org.codehaus.plexus.testing.PlexusTest; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.arrayWithSize; | |
| -import static org.hamcrest.Matchers.hasSize; | |
| -import static org.hamcrest.Matchers.is; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.mockito.Mockito.mock; | |
| import static org.mockito.Mockito.when; | |
| @@ -51,29 +48,29 @@ class DefaultLifecyclesTest { | |
| @Test | |
| void testDefaultLifecycles() { | |
| final List<Lifecycle> lifecycles = defaultLifeCycles.getLifeCycles(); | |
| - assertThat(lifecycles, hasSize(3)); | |
| - assertThat(DefaultLifecycles.STANDARD_LIFECYCLES, arrayWithSize(3)); | |
| + assertEquals(3, lifecycles.size()); | |
| + assertEquals(3, DefaultLifecycles.STANDARD_LIFECYCLES.length); | |
| } | |
| @Test | |
| void testDefaultLifecycle() { | |
| final Lifecycle lifecycle = getLifeCycleById("default"); | |
| - assertThat(lifecycle.getId(), is("default")); | |
| - assertThat(lifecycle.getPhases(), hasSize(54)); | |
| + assertEquals("default", lifecycle.getId()); | |
| + assertEquals(54, lifecycle.getPhases().size()); | |
| } | |
| @Test | |
| void testCleanLifecycle() { | |
| final Lifecycle lifecycle = getLifeCycleById("clean"); | |
| - assertThat(lifecycle.getId(), is("clean")); | |
| - assertThat(lifecycle.getPhases(), hasSize(3)); | |
| + assertEquals("clean", lifecycle.getId()); | |
| + assertEquals(3, lifecycle.getPhases().size()); | |
| } | |
| @Test | |
| void testSiteLifecycle() { | |
| final Lifecycle lifecycle = getLifeCycleById("site"); | |
| - assertThat(lifecycle.getId(), is("site")); | |
| - assertThat(lifecycle.getPhases(), hasSize(6)); | |
| + assertEquals("site", lifecycle.getId()); | |
| + assertEquals(6, lifecycle.getPhases().size()); | |
| } | |
| @Test | |
| @@ -93,10 +90,10 @@ void testCustomLifecycle() throws ComponentLookupException { | |
| List.of(new DefaultLifecycleRegistry.LifecycleWrapperProvider(mockedPlexusContainer))), | |
| new DefaultLookup(mockedPlexusContainer)); | |
| - assertThat(dl.getLifeCycles().get(0).getId(), is("clean")); | |
| - assertThat(dl.getLifeCycles().get(1).getId(), is("default")); | |
| - assertThat(dl.getLifeCycles().get(2).getId(), is("site")); | |
| - assertThat(dl.getLifeCycles().get(3).getId(), is("etl")); | |
| + assertEquals("clean", dl.getLifeCycles().get(0).getId()); | |
| + assertEquals("default", dl.getLifeCycles().get(1).getId()); | |
| + assertEquals("site", dl.getLifeCycles().get(2).getId()); | |
| + assertEquals("etl", dl.getLifeCycles().get(3).getId()); | |
| } | |
| private Lifecycle getLifeCycleById(String id) { | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java | |
| index ca45e941b4..09f1027e99 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/LifecycleExecutorTest.java | |
| @@ -49,8 +49,6 @@ | |
| import org.apache.maven.project.MavenProject; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.hasSize; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertNull; | |
| @@ -288,7 +286,7 @@ void testLifecycleQueryingUsingADefaultLifecyclePhase() throws Exception { | |
| void testLifecyclePluginsRetrievalForDefaultLifecycle() throws Exception { | |
| List<Plugin> plugins = new ArrayList<>(lifecycleExecutor.getPluginsBoundByDefaultToAllLifecycles("jar")); | |
| - assertThat(plugins.toString(), plugins, hasSize(8)); | |
| + assertEquals(8, plugins.size(), plugins.toString()); | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java | |
| index 761ac82ec4..676a7b3f94 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/ProjectBuildListTest.java | |
| @@ -22,10 +22,8 @@ | |
| import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.greaterThanOrEqualTo; | |
| -import static org.hamcrest.Matchers.is; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| */ | |
| @@ -35,10 +33,9 @@ void testGetByTaskSegment() throws Exception { | |
| final MavenSession session = ProjectDependencyGraphStub.getMavenSession(); | |
| ProjectBuildList projectBuildList = ProjectDependencyGraphStub.getProjectBuildList(session); | |
| TaskSegment taskSegment = projectBuildList.get(0).getTaskSegment(); | |
| - assertThat( | |
| - "This test assumes there are at least 6 elements in projectBuilds", | |
| - projectBuildList.size(), | |
| - is(greaterThanOrEqualTo(6))); | |
| + assertTrue( | |
| + projectBuildList.size() >= 6, | |
| + "Expected size " + projectBuildList.size() + " to be >= 6 for collection: " + projectBuildList); | |
| final ProjectBuildList byTaskSegment = projectBuildList.getByTaskSegment(taskSegment); | |
| assertEquals(projectBuildList.size(), byTaskSegment.size()); // TODO Make multiple segments on projectBuildList | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/builder/multithreaded/SmartProjectComparatorTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/builder/multithreaded/SmartProjectComparatorTest.java | |
| new file mode 100644 | |
| index 0000000000..50cac26e1b | |
| --- /dev/null | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/builder/multithreaded/SmartProjectComparatorTest.java | |
| @@ -0,0 +1,211 @@ | |
| +/* | |
| + * 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.lifecycle.internal.builder.multithreaded; | |
| + | |
| +import java.util.Arrays; | |
| +import java.util.List; | |
| +import java.util.concurrent.CountDownLatch; | |
| +import java.util.concurrent.ExecutorService; | |
| +import java.util.concurrent.Executors; | |
| +import java.util.concurrent.TimeUnit; | |
| +import java.util.concurrent.atomic.AtomicReference; | |
| + | |
| +import org.apache.maven.execution.ProjectDependencyGraph; | |
| +import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub; | |
| +import org.apache.maven.project.MavenProject; | |
| +import org.junit.jupiter.api.BeforeEach; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * Test for SmartProjectComparator to verify critical path scheduling logic. | |
| + */ | |
| +class SmartProjectComparatorTest { | |
| + | |
| + private SmartProjectComparator comparator; | |
| + private ProjectDependencyGraph dependencyGraph; | |
| + | |
| + @BeforeEach | |
| + void setUp() { | |
| + dependencyGraph = new ProjectDependencyGraphStub(); | |
| + comparator = new SmartProjectComparator(dependencyGraph); | |
| + } | |
| + | |
| + @Test | |
| + void testProjectWeightCalculation() { | |
| + // Test that projects with longer downstream chains get higher weights | |
| + // Graph: A -> B,C; B -> X,Y; C -> X,Z | |
| + MavenProject projectA = ProjectDependencyGraphStub.A; | |
| + MavenProject projectB = ProjectDependencyGraphStub.B; | |
| + MavenProject projectC = ProjectDependencyGraphStub.C; | |
| + MavenProject projectX = ProjectDependencyGraphStub.X; | |
| + | |
| + long weightA = comparator.getProjectWeight(projectA); | |
| + long weightB = comparator.getProjectWeight(projectB); | |
| + long weightC = comparator.getProjectWeight(projectC); | |
| + long weightX = comparator.getProjectWeight(projectX); | |
| + | |
| + // Project A should have the highest weight as it's at the root | |
| + assertTrue(weightA > weightB, "Project A should have weight > Project B"); | |
| + assertTrue(weightA > weightC, "Project A should have weight > Project C"); | |
| + assertTrue(weightB > weightX, "Project B should have weight > Project X"); | |
| + assertTrue(weightC > weightX, "Project C should have weight > Project X"); | |
| + } | |
| + | |
| + @Test | |
| + void testComparatorOrdering() { | |
| + List<MavenProject> projects = Arrays.asList( | |
| + ProjectDependencyGraphStub.X, | |
| + ProjectDependencyGraphStub.C, | |
| + ProjectDependencyGraphStub.A, | |
| + ProjectDependencyGraphStub.B); | |
| + | |
| + // Sort using the comparator | |
| + projects.sort(comparator.getComparator()); | |
| + | |
| + // Project A should come first (highest weight) | |
| + assertEquals( | |
| + ProjectDependencyGraphStub.A, | |
| + projects.get(0), | |
| + "Project A should be first (highest critical path weight)"); | |
| + | |
| + // B and C should come before X (they have higher weights) | |
| + assertTrue( | |
| + projects.indexOf(ProjectDependencyGraphStub.B) < projects.indexOf(ProjectDependencyGraphStub.X), | |
| + "Project B should come before X"); | |
| + assertTrue( | |
| + projects.indexOf(ProjectDependencyGraphStub.C) < projects.indexOf(ProjectDependencyGraphStub.X), | |
| + "Project C should come before X"); | |
| + } | |
| + | |
| + @Test | |
| + void testWeightConsistency() { | |
| + // Test that weights are consistent across multiple calls | |
| + MavenProject project = ProjectDependencyGraphStub.A; | |
| + | |
| + long weight1 = comparator.getProjectWeight(project); | |
| + long weight2 = comparator.getProjectWeight(project); | |
| + | |
| + assertEquals(weight1, weight2, "Project weight should be consistent"); | |
| + } | |
| + | |
| + @Test | |
| + void testDependencyChainLength() { | |
| + // Test that projects with longer dependency chains get higher weights | |
| + // In the stub: A -> B,C; B -> X,Y; C -> X,Z | |
| + long weightA = comparator.getProjectWeight(ProjectDependencyGraphStub.A); | |
| + long weightB = comparator.getProjectWeight(ProjectDependencyGraphStub.B); | |
| + long weightC = comparator.getProjectWeight(ProjectDependencyGraphStub.C); | |
| + long weightX = comparator.getProjectWeight(ProjectDependencyGraphStub.X); | |
| + long weightY = comparator.getProjectWeight(ProjectDependencyGraphStub.Y); | |
| + long weightZ = comparator.getProjectWeight(ProjectDependencyGraphStub.Z); | |
| + | |
| + // Verify the actual chain length calculation | |
| + // Leaf nodes (no downstream dependencies) | |
| + assertEquals(1L, weightX, "Project X should have weight 1 (1 + 0)"); | |
| + assertEquals(1L, weightY, "Project Y should have weight 1 (1 + 0)"); | |
| + assertEquals(1L, weightZ, "Project Z should have weight 1 (1 + 0)"); | |
| + | |
| + // Middle nodes | |
| + assertEquals(2L, weightB, "Project B should have weight 2 (1 + max(X=1, Y=1))"); | |
| + assertEquals(2L, weightC, "Project C should have weight 2 (1 + max(X=1, Z=1))"); | |
| + | |
| + // Root node | |
| + assertEquals(3L, weightA, "Project A should have weight 3 (1 + max(B=2, C=2))"); | |
| + } | |
| + | |
| + @Test | |
| + void testSameWeightOrdering() { | |
| + // Test that projects with the same weight are ordered by project ID | |
| + // Projects B and C both have weight 2, so they should be ordered by project ID | |
| + List<MavenProject> projects = Arrays.asList( | |
| + ProjectDependencyGraphStub.C, // weight=2, ID contains "C" | |
| + ProjectDependencyGraphStub.B // weight=2, ID contains "B" | |
| + ); | |
| + | |
| + projects.sort(comparator.getComparator()); | |
| + | |
| + // Both have same weight (2), so ordering should be by project ID | |
| + // Project B should come before C alphabetically by project ID | |
| + assertEquals( | |
| + ProjectDependencyGraphStub.B, | |
| + projects.get(0), | |
| + "Project B should come before C when they have the same weight (ordered by project ID)"); | |
| + assertEquals( | |
| + ProjectDependencyGraphStub.C, | |
| + projects.get(1), | |
| + "Project C should come after B when they have the same weight (ordered by project ID)"); | |
| + | |
| + // Verify they actually have the same weight | |
| + long weightB = comparator.getProjectWeight(ProjectDependencyGraphStub.B); | |
| + long weightC = comparator.getProjectWeight(ProjectDependencyGraphStub.C); | |
| + assertEquals(weightB, weightC, "Projects B and C should have the same weight"); | |
| + } | |
| + | |
| + @Test | |
| + void testConcurrentWeightCalculation() throws Exception { | |
| + // Test that concurrent weight calculation doesn't cause recursive update issues | |
| + // This test simulates the scenario that causes the IllegalStateException | |
| + | |
| + int numThreads = 10; | |
| + int numIterations = 100; | |
| + ExecutorService executor = Executors.newFixedThreadPool(numThreads); | |
| + CountDownLatch latch = new CountDownLatch(numThreads); | |
| + AtomicReference<Exception> exception = new AtomicReference<>(); | |
| + | |
| + for (int i = 0; i < numThreads; i++) { | |
| + executor.submit(() -> { | |
| + try { | |
| + for (int j = 0; j < numIterations; j++) { | |
| + // Simulate concurrent access to weight calculation | |
| + // This can trigger the recursive update issue | |
| + List<MavenProject> projects = Arrays.asList( | |
| + ProjectDependencyGraphStub.A, | |
| + ProjectDependencyGraphStub.B, | |
| + ProjectDependencyGraphStub.C, | |
| + ProjectDependencyGraphStub.X, | |
| + ProjectDependencyGraphStub.Y, | |
| + ProjectDependencyGraphStub.Z); | |
| + | |
| + // Sort projects concurrently - this triggers weight calculation | |
| + projects.sort(comparator.getComparator()); | |
| + | |
| + // Also directly access weights to increase contention | |
| + for (MavenProject project : projects) { | |
| + comparator.getProjectWeight(project); | |
| + } | |
| + } | |
| + } catch (Exception e) { | |
| + exception.set(e); | |
| + } finally { | |
| + latch.countDown(); | |
| + } | |
| + }); | |
| + } | |
| + | |
| + latch.await(30, TimeUnit.SECONDS); | |
| + executor.shutdown(); | |
| + | |
| + if (exception.get() != null) { | |
| + throw exception.get(); | |
| + } | |
| + } | |
| +} | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java | |
| index 2a12af0725..fc85cf772c 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/internal/stub/ProjectDependencyGraphStubTest.java | |
| @@ -43,21 +43,29 @@ void testADependencies() { | |
| void testBDependencies() { | |
| final List<MavenProject> bProjects = stub.getUpstreamProjects(ProjectDependencyGraphStub.B, false); | |
| assertEquals(1, bProjects.size()); | |
| - assertTrue(bProjects.contains(ProjectDependencyGraphStub.A)); | |
| + assertTrue( | |
| + bProjects.contains(ProjectDependencyGraphStub.A), | |
| + "Expected " + bProjects + " to contain " + ProjectDependencyGraphStub.A); | |
| } | |
| @Test | |
| void testCDependencies() { | |
| final List<MavenProject> cProjects = stub.getUpstreamProjects(ProjectDependencyGraphStub.C, false); | |
| assertEquals(1, cProjects.size()); | |
| - assertTrue(cProjects.contains(ProjectDependencyGraphStub.A)); | |
| + assertTrue( | |
| + cProjects.contains(ProjectDependencyGraphStub.A), | |
| + "Expected " + cProjects + " to contain " + ProjectDependencyGraphStub.A); | |
| } | |
| @Test | |
| void testXDependencies() { | |
| final List<MavenProject> cProjects = stub.getUpstreamProjects(ProjectDependencyGraphStub.X, false); | |
| assertEquals(2, cProjects.size()); | |
| - assertTrue(cProjects.contains(ProjectDependencyGraphStub.C)); | |
| - assertTrue(cProjects.contains(ProjectDependencyGraphStub.B)); | |
| + assertTrue( | |
| + cProjects.contains(ProjectDependencyGraphStub.C), | |
| + "Expected " + cProjects + " to contain " + ProjectDependencyGraphStub.C); | |
| + assertTrue( | |
| + cProjects.contains(ProjectDependencyGraphStub.B), | |
| + "Expected " + cProjects + " to contain " + ProjectDependencyGraphStub.B); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenPluginValidatorTest.java b/impl/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenPluginValidatorTest.java | |
| index 12c0bba71d..8456e54cdf 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenPluginValidatorTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/plugin/internal/MavenPluginValidatorTest.java | |
| @@ -59,7 +59,8 @@ void testValidate() { | |
| descriptor.setVersion("0.1"); | |
| List<String> errors = new ArrayList<>(); | |
| mavenPluginValidator.validate(plugin, descriptor, errors); | |
| - assertTrue(errors.isEmpty()); | |
| + assertTrue( | |
| + errors.isEmpty(), "Expected collection to be empty but had " + errors.size() + " elements: " + errors); | |
| } | |
| @Test | |
| @@ -78,7 +79,7 @@ void testInvalidGroupId() { | |
| descriptor.setVersion("0.1"); | |
| List<String> errors = new ArrayList<>(); | |
| mavenPluginValidator.validate(plugin, descriptor, errors); | |
| - assertFalse(errors.isEmpty()); | |
| + assertFalse(errors.isEmpty(), "Expected collection to not be empty but was empty"); | |
| } | |
| @Test | |
| @@ -97,7 +98,7 @@ void testInvalidArtifactId() { | |
| descriptor.setVersion("0.1"); | |
| List<String> errors = new ArrayList<>(); | |
| mavenPluginValidator.validate(plugin, descriptor, errors); | |
| - assertFalse(errors.isEmpty()); | |
| + assertFalse(errors.isEmpty(), "Expected collection to not be empty but was empty"); | |
| } | |
| @Test | |
| @@ -115,6 +116,6 @@ void testInvalidVersion() { | |
| descriptor.setArtifactId("maven-it-plugin"); | |
| List<String> errors = new ArrayList<>(); | |
| mavenPluginValidator.validate(plugin, descriptor, errors); | |
| - assertFalse(errors.isEmpty()); | |
| + assertFalse(errors.isEmpty(), "Expected collection to not be empty but was empty"); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java | |
| index 13e766a957..3f6261f27f 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java | |
| @@ -20,13 +20,19 @@ | |
| import java.io.File; | |
| import java.io.InputStream; | |
| +import java.nio.file.FileSystem; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.nio.file.StandardCopyOption; | |
| import java.util.List; | |
| +import java.util.stream.Stream; | |
| +import com.google.common.jimfs.Configuration; | |
| +import com.google.common.jimfs.Jimfs; | |
| import org.apache.maven.api.model.InputLocation; | |
| import org.apache.maven.api.model.InputSource; | |
| +import org.apache.maven.api.services.ModelSource; | |
| +import org.apache.maven.api.services.Sources; | |
| import org.apache.maven.artifact.Artifact; | |
| import org.apache.maven.execution.MavenSession; | |
| import org.apache.maven.impl.InternalSession; | |
| @@ -37,15 +43,12 @@ | |
| import org.junit.jupiter.api.Disabled; | |
| import org.junit.jupiter.api.Test; | |
| import org.junit.jupiter.api.io.TempDir; | |
| +import org.junit.jupiter.params.ParameterizedTest; | |
| +import org.junit.jupiter.params.provider.Arguments; | |
| +import org.junit.jupiter.params.provider.MethodSource; | |
| import org.mockito.Mockito; | |
| -import static org.apache.maven.project.ProjectBuildingResultWithProblemMessageMatcher.projectBuildingResultWithProblemMessage; | |
| import static org.codehaus.plexus.testing.PlexusExtension.getTestFile; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.contains; | |
| -import static org.hamcrest.Matchers.containsString; | |
| -import static org.hamcrest.Matchers.greaterThan; | |
| -import static org.hamcrest.Matchers.is; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| @@ -60,6 +63,16 @@ class DefaultMavenProjectBuilderTest extends AbstractMavenProjectTestCase { | |
| @TempDir | |
| Path projectRoot; | |
| + /** | |
| + * Provides file system configurations for testing both Windows and Unix path behaviors. | |
| + * This allows us to test cross-platform path handling on any development machine. | |
| + */ | |
| + static Stream<Arguments> fileSystemConfigurations() { | |
| + return Stream.of( | |
| + Arguments.of("Unix", Configuration.unix(), "/"), | |
| + Arguments.of("Windows", Configuration.windows(), "\\")); | |
| + } | |
| + | |
| @Override | |
| @BeforeEach | |
| public void setUp() throws Exception { | |
| @@ -107,7 +120,7 @@ void testFutureModelVersion() throws Exception { | |
| ProjectBuildingException e = assertThrows( | |
| ProjectBuildingException.class, () -> getProject(f1), "Expected to fail for future versions"); | |
| - assertThat(e.getMessage(), containsString("Building this project requires a newer version of Maven")); | |
| + assertTrue(e.getMessage().contains("Building this project requires a newer version of Maven")); | |
| } | |
| @Test | |
| @@ -118,7 +131,7 @@ void testPastModelVersion() throws Exception { | |
| ProjectBuildingException e = assertThrows( | |
| ProjectBuildingException.class, () -> getProject(f1), "Expected to fail for past versions"); | |
| - assertThat(e.getMessage(), containsString("Building this project requires an older version of Maven")); | |
| + assertTrue(e.getMessage().contains("Building this project requires an older version of Maven")); | |
| } | |
| @Test | |
| @@ -127,7 +140,7 @@ void testFutureSchemaModelVersion() throws Exception { | |
| ProjectBuildingException e = assertThrows( | |
| ProjectBuildingException.class, () -> getProject(f1), "Expected to fail for future versions"); | |
| - assertThat(e.getMessage(), containsString("Building this project requires a newer version of Maven")); | |
| + assertTrue(e.getMessage().contains("Building this project requires a newer version of Maven")); | |
| } | |
| @Test | |
| @@ -146,7 +159,7 @@ void testBuildStubModelForMissingRemotePom() throws Exception { | |
| assertNull(project.getParent()); | |
| assertNull(project.getParentArtifact()); | |
| - assertFalse(project.isExecutionRoot()); | |
| + assertFalse(project.isExecutionRoot(), "Expected " + project + ".isExecutionRoot() to return false"); | |
| } | |
| @Test | |
| @@ -200,7 +213,9 @@ void testBuildParentVersionRangeLocallyWithoutChildVersion() throws Exception { | |
| ProjectBuildingException.class, | |
| () -> getProject(f1), | |
| "Expected 'ProjectBuildingException' not thrown."); | |
| - assertThat(e.getResults(), contains(projectBuildingResultWithProblemMessage("Version must be a constant"))); | |
| + assertEquals(1, e.getResults().size()); | |
| + ProjectBuildingResultWithProblemMessageAssert.assertThat(e.getResults().get(0)) | |
| + .hasProblemMessage("Version must be a constant"); | |
| } | |
| /** | |
| @@ -215,7 +230,9 @@ void testBuildParentVersionRangeLocallyWithChildProjectVersionExpression() throw | |
| ProjectBuildingException.class, | |
| () -> getProject(f1), | |
| "Expected 'ProjectBuildingException' not thrown."); | |
| - assertThat(e.getResults(), contains(projectBuildingResultWithProblemMessage("Version must be a constant"))); | |
| + assertEquals(1, e.getResults().size()); | |
| + ProjectBuildingResultWithProblemMessageAssert.assertThat(e.getResults().get(0)) | |
| + .hasProblemMessage("Version must be a constant"); | |
| } | |
| /** | |
| @@ -278,7 +295,9 @@ void testBuildParentVersionRangeExternallyWithoutChildVersion() throws Exception | |
| ProjectBuildingException.class, | |
| () -> getProjectFromRemoteRepository(f1), | |
| "Expected 'ProjectBuildingException' not thrown."); | |
| - assertThat(e.getResults(), contains(projectBuildingResultWithProblemMessage("Version must be a constant"))); | |
| + assertEquals(1, e.getResults().size()); | |
| + ProjectBuildingResultWithProblemMessageAssert.assertThat(e.getResults().get(0)) | |
| + .hasProblemMessage("Version must be a constant"); | |
| } | |
| /** | |
| @@ -293,7 +312,9 @@ void testBuildParentVersionRangeExternallyWithChildProjectVersionExpression() th | |
| ProjectBuildingException.class, | |
| () -> getProjectFromRemoteRepository(f1), | |
| "Expected 'ProjectBuildingException' not thrown."); | |
| - assertThat(e.getResults(), contains(projectBuildingResultWithProblemMessage("Version must be a constant"))); | |
| + assertEquals(1, e.getResults().size()); | |
| + ProjectBuildingResultWithProblemMessageAssert.assertThat(e.getResults().get(0)) | |
| + .hasProblemMessage("Version must be a constant"); | |
| } | |
| /** | |
| @@ -316,7 +337,7 @@ void rereadPomMng7063() throws Exception { | |
| MavenProject project = | |
| projectBuilder.build(pom.toFile(), buildingRequest).getProject(); | |
| - assertThat(project.getName(), is("aid")); // inherited from artifactId | |
| + assertEquals("aid", project.getName()); // inherited from artifactId | |
| try (InputStream pomResource = | |
| DefaultMavenProjectBuilderTest.class.getResourceAsStream("/projects/reread/pom2.xml")) { | |
| @@ -324,7 +345,7 @@ void rereadPomMng7063() throws Exception { | |
| } | |
| project = projectBuilder.build(pom.toFile(), buildingRequest).getProject(); | |
| - assertThat(project.getName(), is("PROJECT NAME")); | |
| + assertEquals("PROJECT NAME", project.getName()); | |
| } | |
| @Test | |
| @@ -345,79 +366,177 @@ void testActivatedProfileBySource() throws Exception { | |
| project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("active-by-default"::equals)); | |
| } | |
| - @Test | |
| - void testActivatedDefaultProfileBySource() throws Exception { | |
| + /** | |
| + * Parameterized version of testActivatedDefaultProfileBySource that demonstrates | |
| + * cross-platform path behavior using JIMFS to simulate both Windows and Unix file systems. | |
| + * This test shows how the path separator expectations differ between platforms. | |
| + */ | |
| + @ParameterizedTest(name = "testActivatedDefaultProfileBySource[{0}]") | |
| + @MethodSource("fileSystemConfigurations") | |
| + void testActivatedDefaultProfileBySource(String fsName, Configuration fsConfig, String separator) throws Exception { | |
| File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml"); | |
| - ProjectBuildingRequest request = newBuildingRequest(); | |
| - request.setLocalRepository(getLocalRepository()); | |
| - | |
| - MavenProject project = projectBuilder.build(testPom, request).getProject(); | |
| - | |
| - assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId()))); | |
| - assertTrue(project.getInjectedProfileIds().get("external").isEmpty()); | |
| - assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile1"::equals)); | |
| - assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile2"::equals)); | |
| - assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().anyMatch("active-by-default"::equals)); | |
| - | |
| - InternalMavenSession session = Mockito.mock(InternalMavenSession.class); | |
| - List<org.apache.maven.api.model.Profile> activeProfiles = | |
| - new DefaultProject(session, project).getDeclaredActiveProfiles(); | |
| - assertEquals(1, activeProfiles.size()); | |
| - org.apache.maven.api.model.Profile profile = activeProfiles.get(0); | |
| - assertEquals("active-by-default", profile.getId()); | |
| - InputLocation location = profile.getLocation(""); | |
| - assertNotNull(location); | |
| - assertThat(location.getLineNumber(), greaterThan(0)); | |
| - assertThat(location.getColumnNumber(), greaterThan(0)); | |
| - assertNotNull(location.getSource()); | |
| - assertThat(location.getSource().getLocation(), containsString("pom-with-profiles/pom.xml")); | |
| + try (FileSystem fs = Jimfs.newFileSystem(fsName, fsConfig)) { | |
| + Path path = fs.getPath("projects", "pom-with-profiles", "pom.xml"); | |
| + Files.createDirectories(path.getParent()); | |
| + Files.copy(testPom.toPath(), path); | |
| + ModelSource source = Sources.buildSource(path); | |
| + | |
| + ProjectBuildingRequest request = newBuildingRequest(); | |
| + request.setLocalRepository(getLocalRepository()); | |
| + | |
| + MavenProject project = projectBuilder.build(source, request).getProject(); | |
| + | |
| + assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId()))); | |
| + assertTrue(project.getInjectedProfileIds().get("external").isEmpty()); | |
| + assertTrue(project.getInjectedProfileIds().get(project.getId()).stream() | |
| + .noneMatch("profile1"::equals)); | |
| + assertTrue(project.getInjectedProfileIds().get(project.getId()).stream() | |
| + .noneMatch("profile2"::equals)); | |
| + assertTrue(project.getInjectedProfileIds().get(project.getId()).stream() | |
| + .anyMatch("active-by-default"::equals)); | |
| + | |
| + InternalMavenSession session = Mockito.mock(InternalMavenSession.class); | |
| + List<org.apache.maven.api.model.Profile> activeProfiles = | |
| + new DefaultProject(session, project).getDeclaredActiveProfiles(); | |
| + assertEquals(1, activeProfiles.size()); | |
| + org.apache.maven.api.model.Profile profile = activeProfiles.get(0); | |
| + assertEquals("active-by-default", profile.getId()); | |
| + InputLocation location = profile.getLocation(""); | |
| + assertNotNull(location, "Profile location should not be null for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getLineNumber() > 0, | |
| + "Profile location line number should be positive, but was: " + location.getLineNumber() | |
| + + " for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getColumnNumber() > 0, | |
| + "Profile location column number should be positive, but was: " + location.getColumnNumber() | |
| + + " for profile: " + profile.getId()); | |
| + assertNotNull( | |
| + location.getSource(), "Profile location source should not be null for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getSource().getLocation().contains("pom-with-profiles/pom.xml"), | |
| + "Profile location should contain 'pom-with-profiles/pom.xml', but was: " | |
| + + location.getSource().getLocation() + " for profile: " + profile.getId()); | |
| + | |
| + // This demonstrates the cross-platform path behavior: | |
| + // - On Unix systems, paths use forward slashes (/) | |
| + // - On Windows systems, paths use backslashes (\) | |
| + // - The actual file system being used determines the separator | |
| + String actualLocation = location.getSource().getLocation(); | |
| + String expectedPath = "pom-with-profiles" + separator + "pom.xml"; | |
| + | |
| + // The test will pass with File.separator but this shows the platform differences | |
| + assertTrue( | |
| + actualLocation.contains("pom-with-profiles/pom.xml"), | |
| + "Location should contain path with proper separators for " + fsName + " (actual: " + actualLocation | |
| + + ")\n" | |
| + + "=== Cross-Platform Path Test [" + fsName + "] ===\n" | |
| + + "Expected path pattern: " + expectedPath + "\n" | |
| + + "Actual location: " + actualLocation + "\n" | |
| + + "Contains expected pattern: " + actualLocation.contains(expectedPath) + "\n" | |
| + + "File.separator on this system: '" + File.separator + "'"); | |
| + } | |
| } | |
| - @Test | |
| - void testActivatedExternalProfileBySource() throws Exception { | |
| + /** | |
| + * Parameterized version of testActivatedExternalProfileBySource that demonstrates | |
| + * cross-platform path behavior using JIMFS to simulate both Windows and Unix file systems. | |
| + * This test shows how the path separator expectations differ between platforms. | |
| + */ | |
| + @ParameterizedTest(name = "testActivatedExternalProfileBySource[{0}]") | |
| + @MethodSource("fileSystemConfigurations") | |
| + void testActivatedExternalProfileBySource(String fsName, Configuration fsConfig, String separator) | |
| + throws Exception { | |
| File testPom = getTestFile("src/test/resources/projects/pom-with-profiles/pom.xml"); | |
| - ProjectBuildingRequest request = newBuildingRequest(); | |
| - request.setLocalRepository(getLocalRepository()); | |
| - | |
| - final Profile externalProfile = new Profile(); | |
| - externalProfile.setLocation( | |
| - "", | |
| - new org.apache.maven.model.InputLocation( | |
| - 1, 1, new org.apache.maven.model.InputSource(new InputSource(null, "settings.xml", null)))); | |
| - externalProfile.setId("external-profile"); | |
| - request.addProfile(externalProfile); | |
| - request.setActiveProfileIds(List.of(externalProfile.getId())); | |
| - | |
| - MavenProject project = projectBuilder.build(testPom, request).getProject(); | |
| - | |
| - assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId()))); | |
| - assertTrue(project.getInjectedProfileIds().get("external").stream().anyMatch("external-profile"::equals)); | |
| - assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile1"::equals)); | |
| - assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().noneMatch("profile2"::equals)); | |
| - assertTrue(project.getInjectedProfileIds().get(project.getId()).stream().anyMatch("active-by-default"::equals)); | |
| - | |
| - InternalMavenSession session = Mockito.mock(InternalMavenSession.class); | |
| - List<org.apache.maven.api.model.Profile> activeProfiles = | |
| - new DefaultProject(session, project).getDeclaredActiveProfiles(); | |
| - assertEquals(2, activeProfiles.size()); | |
| - org.apache.maven.api.model.Profile profile = activeProfiles.get(0); | |
| - assertEquals("active-by-default", profile.getId()); | |
| - InputLocation location = profile.getLocation(""); | |
| - assertNotNull(location); | |
| - assertThat(location.getLineNumber(), greaterThan(0)); | |
| - assertThat(location.getColumnNumber(), greaterThan(0)); | |
| - assertNotNull(location.getSource()); | |
| - assertThat(location.getSource().getLocation(), containsString("pom-with-profiles/pom.xml")); | |
| - profile = activeProfiles.get(1); | |
| - assertEquals("external-profile", profile.getId()); | |
| - location = profile.getLocation(""); | |
| - assertNotNull(location); | |
| - assertThat(location.getLineNumber(), greaterThan(0)); | |
| - assertThat(location.getColumnNumber(), greaterThan(0)); | |
| - assertNotNull(location.getSource()); | |
| - assertThat(location.getSource().getLocation(), containsString("settings.xml")); | |
| + try (FileSystem fs = Jimfs.newFileSystem(fsName, fsConfig)) { | |
| + Path path = fs.getPath("projects", "pom-with-profiles", "pom.xml"); | |
| + Files.createDirectories(path.getParent()); | |
| + Files.copy(testPom.toPath(), path); | |
| + ModelSource source = Sources.buildSource(path); | |
| + | |
| + ProjectBuildingRequest request = newBuildingRequest(); | |
| + request.setLocalRepository(getLocalRepository()); | |
| + | |
| + final Profile externalProfile = new Profile(); | |
| + externalProfile.setLocation( | |
| + "", | |
| + new org.apache.maven.model.InputLocation( | |
| + 1, 1, new org.apache.maven.model.InputSource(InputSource.of(null, "settings.xml", null)))); | |
| + externalProfile.setId("external-profile"); | |
| + request.addProfile(externalProfile); | |
| + request.setActiveProfileIds(List.of(externalProfile.getId())); | |
| + | |
| + MavenProject project = projectBuilder.build(source, request).getProject(); | |
| + | |
| + assertTrue(project.getInjectedProfileIds().keySet().containsAll(List.of("external", project.getId()))); | |
| + assertTrue(project.getInjectedProfileIds().get("external").stream().anyMatch("external-profile"::equals)); | |
| + assertTrue(project.getInjectedProfileIds().get(project.getId()).stream() | |
| + .noneMatch("profile1"::equals)); | |
| + assertTrue(project.getInjectedProfileIds().get(project.getId()).stream() | |
| + .noneMatch("profile2"::equals)); | |
| + assertTrue(project.getInjectedProfileIds().get(project.getId()).stream() | |
| + .anyMatch("active-by-default"::equals)); | |
| + | |
| + InternalMavenSession session = Mockito.mock(InternalMavenSession.class); | |
| + List<org.apache.maven.api.model.Profile> activeProfiles = | |
| + new DefaultProject(session, project).getDeclaredActiveProfiles(); | |
| + assertEquals(2, activeProfiles.size()); | |
| + org.apache.maven.api.model.Profile profile = activeProfiles.get(0); | |
| + assertEquals("active-by-default", profile.getId()); | |
| + InputLocation location = profile.getLocation(""); | |
| + assertNotNull(location, "Profile location should not be null for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getLineNumber() > 0, | |
| + "Profile location line number should be positive, but was: " + location.getLineNumber() | |
| + + " for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getColumnNumber() > 0, | |
| + "Profile location column number should be positive, but was: " + location.getColumnNumber() | |
| + + " for profile: " + profile.getId()); | |
| + assertNotNull( | |
| + location.getSource(), "Profile location source should not be null for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getSource().getLocation().contains("pom-with-profiles/pom.xml"), | |
| + "Profile location should contain 'pom-with-profiles/pom.xml', but was: " | |
| + + location.getSource().getLocation() + " for profile: " + profile.getId()); | |
| + | |
| + // This demonstrates the cross-platform path behavior for the POM file | |
| + String actualLocation = location.getSource().getLocation(); | |
| + String expectedPath = "pom-with-profiles" + separator + "pom.xml"; | |
| + | |
| + // The test will pass with File.separator but this shows the platform differences | |
| + assertTrue( | |
| + actualLocation.contains("pom-with-profiles/pom.xml"), | |
| + "Location should contain path with proper separators for " + fsName + " (actual: " + actualLocation | |
| + + ")\n" | |
| + + "=== Cross-Platform Path Test [" + fsName + "] - External Profile ===\n" | |
| + + "Expected path pattern: " + expectedPath + "\n" | |
| + + "Actual location: " + actualLocation + "\n" | |
| + + "Contains expected pattern: " + actualLocation.contains(expectedPath) + "\n" | |
| + + "File.separator on this system: '" + File.separator + "'"); | |
| + | |
| + profile = activeProfiles.get(1); | |
| + assertEquals("external-profile", profile.getId()); | |
| + location = profile.getLocation(""); | |
| + assertNotNull(location, "External profile location should not be null for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getLineNumber() > 0, | |
| + "External profile location line number should be positive, but was: " + location.getLineNumber() | |
| + + " for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getColumnNumber() > 0, | |
| + "External profile location column number should be positive, but was: " + location.getColumnNumber() | |
| + + " for profile: " + profile.getId()); | |
| + assertNotNull( | |
| + location.getSource(), | |
| + "External profile location source should not be null for profile: " + profile.getId()); | |
| + assertTrue( | |
| + location.getSource().getLocation().contains("settings.xml"), | |
| + "External profile location should contain 'settings.xml', but was: " | |
| + + location.getSource().getLocation() + " for profile: " + profile.getId()); | |
| + } | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java | |
| index 5e73daa279..4232e08c23 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ExtensionDescriptorBuilderTest.java | |
| @@ -27,11 +27,9 @@ | |
| import org.junit.jupiter.api.BeforeEach; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.empty; | |
| -import static org.hamcrest.Matchers.is; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| * Tests {@link ExtensionDescriptorBuilder}. | |
| @@ -63,9 +61,9 @@ void testEmptyDescriptor() throws Exception { | |
| assertNotNull(ed); | |
| assertNotNull(ed.getExportedPackages()); | |
| - assertThat(ed.getExportedPackages(), is(empty())); | |
| + assertTrue(ed.getExportedPackages().isEmpty()); | |
| assertNotNull(ed.getExportedArtifacts()); | |
| - assertThat(ed.getExportedArtifacts(), is(empty())); | |
| + assertTrue(ed.getExportedArtifacts().isEmpty()); | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/GraphTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/GraphTest.java | |
| index ef2b83278d..d96551e449 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/GraphTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/GraphTest.java | |
| @@ -78,10 +78,10 @@ public void testGraph() throws CycleDetectedException { | |
| Set<String> labels = graph.getVertices().stream().map(Vertex::getLabel).collect(Collectors.toSet()); | |
| assertEquals(4, labels.size()); | |
| - assertTrue(labels.contains("a")); | |
| - assertTrue(labels.contains("b")); | |
| - assertTrue(labels.contains("c")); | |
| - assertTrue(labels.contains("d")); | |
| + assertTrue(labels.contains("a"), "Expected " + labels + " to contain " + "a"); | |
| + assertTrue(labels.contains("b"), "Expected " + labels + " to contain " + "b"); | |
| + assertTrue(labels.contains("c"), "Expected " + labels + " to contain " + "c"); | |
| + assertTrue(labels.contains("d"), "Expected " + labels + " to contain " + "d"); | |
| addEdge(graph, "a", "d"); | |
| assertEquals(2, a.getChildren().size()); | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/MavenProjectTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/MavenProjectTest.java | |
| index 402fd3014f..67af48b502 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/MavenProjectTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/MavenProjectTest.java | |
| @@ -213,6 +213,6 @@ void testAddDotFile() { | |
| } | |
| private void assertNoNulls(List<String> elements) { | |
| - assertFalse(elements.contains(null)); | |
| + assertFalse(elements.contains(null), "Expected " + elements + " to not contain " + null); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java | |
| index d53b81cc4e..a6513e4091 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/PomConstructionTest.java | |
| @@ -51,10 +51,6 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.endsWith; | |
| -import static org.hamcrest.Matchers.lessThan; | |
| -import static org.hamcrest.Matchers.startsWith; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertFalse; | |
| import static org.junit.jupiter.api.Assertions.assertNotEquals; | |
| @@ -1033,13 +1029,17 @@ void testMergedFilterOrder() throws Exception { | |
| PomTestWrapper pom = buildPom("merged-filter-order/sub"); | |
| assertEquals(7, ((List<?>) pom.getValue("build/filters")).size()); | |
| - assertThat(pom.getValue("build/filters[1]").toString(), endsWith("child-a.properties")); | |
| - assertThat(pom.getValue("build/filters[2]").toString(), endsWith("child-c.properties")); | |
| - assertThat(pom.getValue("build/filters[3]").toString(), endsWith("child-b.properties")); | |
| - assertThat(pom.getValue("build/filters[4]").toString(), endsWith("child-d.properties")); | |
| - assertThat(pom.getValue("build/filters[5]").toString(), endsWith("parent-c.properties")); | |
| - assertThat(pom.getValue("build/filters[6]").toString(), endsWith("parent-b.properties")); | |
| - assertThat(pom.getValue("build/filters[7]").toString(), endsWith("parent-d.properties")); | |
| + assertTrue( | |
| + pom.getValue("build/filters[1]").toString().endsWith("child-a.properties"), | |
| + "Expected " + pom.getValue("build/filters[1]") + " to end with child-a.properties"); | |
| + assertTrue( | |
| + pom.getValue("build/filters[2]").toString().endsWith("child-c.properties"), | |
| + "Expected " + pom.getValue("build/filters[2]") + " to end with child-c.properties"); | |
| + assertTrue(pom.getValue("build/filters[3]").toString().endsWith("child-b.properties")); | |
| + assertTrue(pom.getValue("build/filters[4]").toString().endsWith("child-d.properties")); | |
| + assertTrue(pom.getValue("build/filters[5]").toString().endsWith("parent-c.properties")); | |
| + assertTrue(pom.getValue("build/filters[6]").toString().endsWith("parent-b.properties")); | |
| + assertTrue(pom.getValue("build/filters[7]").toString().endsWith("parent-d.properties")); | |
| } | |
| /** MNG-4027*/ | |
| @@ -1180,7 +1180,7 @@ void testInterpolationOfRfc3986BaseUri() throws Exception { | |
| PomTestWrapper pom = buildPom("baseuri-interpolation/pom.xml"); | |
| String prop1 = pom.getValue("properties/prop1").toString(); | |
| assertEquals(pom.getBasedir().toPath().toUri().toASCIIString(), prop1); | |
| - assertThat(prop1, startsWith("file:///")); | |
| + assertTrue(prop1.startsWith("file:///"), "Expected " + prop1 + " to start with " + "file:///"); | |
| } | |
| /* MNG-3811*/ | |
| @@ -1771,10 +1771,10 @@ void testPluginDeclarationsRetainPomOrderAfterInjectionOfDefaultPlugins() throws | |
| for (int i = 0; i < plugins.size(); i++) { | |
| Plugin plugin = plugins.get(i); | |
| if ("maven-resources-plugin".equals(plugin.getArtifactId())) { | |
| - assertThat(resourcesPlugin, lessThan(0)); | |
| + assertTrue(resourcesPlugin < 0, "Expected " + resourcesPlugin + " to be < " + 0); | |
| resourcesPlugin = i; | |
| } else if ("maven-it-plugin-log-file".equals(plugin.getArtifactId())) { | |
| - assertThat(customPlugin, lessThan(0)); | |
| + assertTrue(customPlugin < 0, "Expected " + customPlugin + " to be < " + 0); | |
| customPlugin = i; | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java | |
| index 541ac8063c..69b1aef227 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java | |
| @@ -39,15 +39,6 @@ | |
| import org.junit.jupiter.api.Test; | |
| import org.junit.jupiter.api.io.TempDir; | |
| -import static org.apache.maven.project.ProjectBuildingResultWithLocationMatcher.projectBuildingResultWithLocation; | |
| -import static org.apache.maven.project.ProjectBuildingResultWithProblemMessageMatcher.projectBuildingResultWithProblemMessage; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.contains; | |
| -import static org.hamcrest.Matchers.containsString; | |
| -import static org.hamcrest.Matchers.empty; | |
| -import static org.hamcrest.Matchers.greaterThan; | |
| -import static org.hamcrest.Matchers.hasKey; | |
| -import static org.hamcrest.Matchers.is; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| @@ -98,12 +89,12 @@ void testVersionlessManagedDependency() throws Exception { | |
| ProjectBuildingException e = assertThrows(ProjectBuildingException.class, () -> getContainer() | |
| .lookup(org.apache.maven.project.ProjectBuilder.class) | |
| .build(pomFile, configuration)); | |
| - assertThat( | |
| - e.getResults(), | |
| - contains( | |
| - projectBuildingResultWithProblemMessage( | |
| - "'dependencies.dependency.version' for groupId='org.apache.maven.its', artifactId='a', type='jar' is missing"))); | |
| - assertThat(e.getResults(), contains(projectBuildingResultWithLocation(5, 9))); | |
| + assertEquals(1, e.getResults().size()); | |
| + ProjectBuildingResultWithProblemMessageAssert.assertThat(e.getResults().get(0)) | |
| + .hasProblemMessage( | |
| + "'dependencies.dependency.version' for groupId='org.apache.maven.its', artifactId='a', type='jar' is missing"); | |
| + ProjectBuildingResultWithLocationAssert.assertThat(e.getResults().get(0)) | |
| + .hasLocation(5, 9); | |
| } | |
| @Test | |
| @@ -187,7 +178,7 @@ void testReadModifiedPoms(@TempDir Path tempDir) throws Exception { | |
| Files.write(parent.toPath(), parentContent.getBytes(StandardCharsets.UTF_8)); | |
| // re-build pom with modified parent | |
| ProjectBuildingResult result = projectBuilder.build(child, configuration); | |
| - assertThat(result.getProject().getProperties(), hasKey((Object) "addedProperty")); | |
| + assertTrue(result.getProject().getProperties().containsKey("addedProperty")); | |
| } | |
| @Test | |
| @@ -235,7 +226,7 @@ void testReadInvalidPom() throws Exception { | |
| // single project build entry point | |
| Exception ex = assertThrows(Exception.class, () -> projectBuilder.build(pomFile, configuration)); | |
| - assertThat(ex.getMessage(), containsString("Received non-all-whitespace CHARACTERS or CDATA event")); | |
| + assertTrue(ex.getMessage().contains("Received non-all-whitespace CHARACTERS or CDATA event")); | |
| // multi projects build entry point | |
| ProjectBuildingException pex = assertThrows( | |
| @@ -243,11 +234,10 @@ void testReadInvalidPom() throws Exception { | |
| () -> projectBuilder.build(Collections.singletonList(pomFile), false, configuration)); | |
| assertEquals(1, pex.getResults().size()); | |
| assertNotNull(pex.getResults().get(0).getPomFile()); | |
| - assertThat(pex.getResults().get(0).getProblems().size(), greaterThan(0)); | |
| - assertThat( | |
| - pex.getResults(), | |
| - contains(projectBuildingResultWithProblemMessage( | |
| - "Received non-all-whitespace CHARACTERS or CDATA event in nextTag()"))); | |
| + assertTrue(pex.getResults().get(0).getProblems().size() > 0); | |
| + ProjectBuildingResultWithProblemMessageAssert.assertThat( | |
| + pex.getResults().get(0)) | |
| + .hasProblemMessage("Received non-all-whitespace CHARACTERS or CDATA event in nextTag()"); | |
| } | |
| @Test | |
| @@ -298,7 +288,7 @@ private MavenProject findChildProject(List<ProjectBuildingResult> results) { | |
| private void assertResultShowNoError(List<ProjectBuildingResult> results) { | |
| for (ProjectBuildingResult result : results) { | |
| - assertThat(result.getProblems(), is(empty())); | |
| + assertTrue(result.getProblems().isEmpty()); | |
| assertNotNull(result.getProject()); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java | |
| new file mode 100644 | |
| index 0000000000..fae4355e0e | |
| --- /dev/null | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingExceptionTest.java | |
| @@ -0,0 +1,163 @@ | |
| +/* | |
| + * 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.io.File; | |
| +import java.util.ArrayList; | |
| +import java.util.Collections; | |
| +import java.util.List; | |
| + | |
| +import org.apache.maven.model.building.DefaultModelProblem; | |
| +import org.apache.maven.model.building.ModelProblem; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * Test for {@link ProjectBuildingException} message generation. | |
| + */ | |
| +@SuppressWarnings("deprecation") | |
| +class ProjectBuildingExceptionTest { | |
| + | |
| + @Test | |
| + void testDetailedExceptionMessageWithMultipleProblems() { | |
| + List<ProjectBuildingResult> results = new ArrayList<>(); | |
| + | |
| + List<ModelProblem> problems1 = new ArrayList<>(); | |
| + Collections.addAll( | |
| + problems1, | |
| + new DefaultModelProblem( | |
| + "Missing required dependency", | |
| + ModelProblem.Severity.ERROR, | |
| + null, | |
| + "pom.xml", | |
| + 25, | |
| + 10, | |
| + null, | |
| + null), | |
| + new DefaultModelProblem( | |
| + "Invalid version format", ModelProblem.Severity.ERROR, null, "pom.xml", 30, 5, null, null)); | |
| + DefaultProjectBuildingResult result1 = | |
| + new DefaultProjectBuildingResult("com.example:project1:1.0", new File("project1/pom.xml"), problems1); | |
| + results.add(result1); | |
| + | |
| + List<ModelProblem> problems2 = new ArrayList<>(); | |
| + Collections.addAll( | |
| + problems2, | |
| + new DefaultModelProblem( | |
| + "Deprecated plugin usage", ModelProblem.Severity.WARNING, null, "pom.xml", 15, 3, null, null)); | |
| + DefaultProjectBuildingResult result2 = | |
| + new DefaultProjectBuildingResult("com.example:project2:1.0", new File("project2/pom.xml"), problems2); | |
| + results.add(result2); | |
| + | |
| + ProjectBuildingException exception = new ProjectBuildingException(results); | |
| + String message = exception.getMessage(); | |
| + | |
| + assertTrue( | |
| + message.contains("3 problems were encountered while processing the POMs (2 errors)"), | |
| + "Message should contain problem count and error count"); | |
| + | |
| + assertTrue(message.contains("[com.example:project1:1.0]"), "Message should contain project1 identifier"); | |
| + | |
| + assertTrue(message.contains("[com.example:project2:1.0]"), "Message should contain project2 identifier"); | |
| + | |
| + assertTrue( | |
| + message.contains("[ERROR] Missing required dependency @ pom.xml, line 25, column 10"), | |
| + "Message should contain error details with location"); | |
| + | |
| + assertTrue( | |
| + message.contains("[ERROR] Invalid version format @ pom.xml, line 30, column 5"), | |
| + "Message should contain second error details"); | |
| + | |
| + assertTrue( | |
| + !message.contains("[WARNING]") || message.contains("[WARNING] Deprecated plugin usage"), | |
| + "Warnings should be filtered when errors are present or shown if explicitly included"); | |
| + } | |
| + | |
| + @Test | |
| + void testExceptionMessageWithOnlyWarnings() { | |
| + List<ProjectBuildingResult> results = new ArrayList<>(); | |
| + | |
| + List<ModelProblem> problems = new ArrayList<>(); | |
| + Collections.addAll( | |
| + problems, | |
| + new DefaultModelProblem( | |
| + "Deprecated feature used", ModelProblem.Severity.WARNING, null, "pom.xml", 10, 1, null, null)); | |
| + DefaultProjectBuildingResult result = | |
| + new DefaultProjectBuildingResult("com.example:project:1.0", new File("project/pom.xml"), problems); | |
| + results.add(result); | |
| + | |
| + ProjectBuildingException exception = new ProjectBuildingException(results); | |
| + String message = exception.getMessage(); | |
| + | |
| + assertTrue( | |
| + message.contains("1 problem was encountered while processing the POMs"), | |
| + "Message should use singular form for single problem"); | |
| + | |
| + assertTrue( | |
| + message.contains("[WARNING] Deprecated feature used"), | |
| + "Message should contain warning when no errors are present"); | |
| + | |
| + assertTrue( | |
| + !message.contains("(") || !message.contains("error"), | |
| + "Message should not contain error count when there are no errors"); | |
| + } | |
| + | |
| + @Test | |
| + void testExceptionMessageWithEmptyResults() { | |
| + List<ProjectBuildingResult> results = Collections.emptyList(); | |
| + | |
| + ProjectBuildingException exception = new ProjectBuildingException(results); | |
| + String message = exception.getMessage(); | |
| + | |
| + assertEquals( | |
| + "Some problems were encountered while processing the POMs", | |
| + message, | |
| + "Empty results should fall back to generic message"); | |
| + } | |
| + | |
| + @Test | |
| + void testExceptionMessageWithNullResults() { | |
| + ProjectBuildingException exception = new ProjectBuildingException((List<ProjectBuildingResult>) null); | |
| + String message = exception.getMessage(); | |
| + | |
| + assertEquals( | |
| + "Some problems were encountered while processing the POMs", | |
| + message, | |
| + "Null results should fall back to generic message"); | |
| + } | |
| + | |
| + @Test | |
| + void testExceptionMessageWithUnknownProject() { | |
| + List<ProjectBuildingResult> results = new ArrayList<>(); | |
| + | |
| + List<ModelProblem> problems = new ArrayList<>(); | |
| + Collections.addAll( | |
| + problems, | |
| + new DefaultModelProblem("Some error", ModelProblem.Severity.ERROR, null, "unknown", 1, 1, null, null)); | |
| + DefaultProjectBuildingResult result = new DefaultProjectBuildingResult(null, null, problems); | |
| + results.add(result); | |
| + | |
| + ProjectBuildingException exception = new ProjectBuildingException(results); | |
| + String message = exception.getMessage(); | |
| + | |
| + assertTrue(message.contains("[unknown project]"), "Message should handle unknown project gracefully"); | |
| + } | |
| +} | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithLocationAssert.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithLocationAssert.java | |
| new file mode 100644 | |
| index 0000000000..3e15ce14ad | |
| --- /dev/null | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithLocationAssert.java | |
| @@ -0,0 +1,66 @@ | |
| +/* | |
| + * 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 static java.util.stream.Collectors.joining; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * JUnit custom assertion to help create fluent assertions about {@link ProjectBuildingResult} instances. | |
| + */ | |
| +class ProjectBuildingResultWithLocationAssert { | |
| + private final ProjectBuildingResult actual; | |
| + | |
| + ProjectBuildingResultWithLocationAssert(ProjectBuildingResult actual) { | |
| + this.actual = actual; | |
| + } | |
| + | |
| + public static ProjectBuildingResultWithLocationAssert assertThat(ProjectBuildingResult actual) { | |
| + return new ProjectBuildingResultWithLocationAssert(actual); | |
| + } | |
| + | |
| + public ProjectBuildingResultWithLocationAssert hasLocation(int columnNumber, int lineNumber) { | |
| + assertNotNull(actual); | |
| + | |
| + boolean hasLocation = actual.getProblems().stream() | |
| + .anyMatch(p -> p.getLineNumber() == lineNumber && p.getColumnNumber() == columnNumber); | |
| + | |
| + if (!hasLocation) { | |
| + String actualLocations = actual.getProblems().stream() | |
| + .map(p -> formatLocation(p.getColumnNumber(), p.getLineNumber())) | |
| + .collect(joining(", ")); | |
| + String message = String.format( | |
| + "Expected ProjectBuildingResult to have location <%s> but had locations <%s>", | |
| + formatLocation(columnNumber, lineNumber), actualLocations); | |
| + assertTrue(false, message); | |
| + } | |
| + | |
| + return this; | |
| + } | |
| + | |
| + private String formatLocation(int columnNumber, int lineNumber) { | |
| + return String.format("line %d, column %d", lineNumber, columnNumber); | |
| + } | |
| + | |
| + // Helper method for backward compatibility | |
| + static ProjectBuildingResultWithLocationAssert projectBuildingResultWithLocation(int columnNumber, int lineNumber) { | |
| + return new ProjectBuildingResultWithLocationAssert(null).hasLocation(columnNumber, lineNumber); | |
| + } | |
| +} | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithLocationMatcher.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithLocationMatcher.java | |
| deleted file mode 100644 | |
| index ac46ca37e5..0000000000 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithLocationMatcher.java | |
| +++ /dev/null | |
| @@ -1,76 +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 org.hamcrest.BaseMatcher; | |
| -import org.hamcrest.Description; | |
| -import org.hamcrest.Matcher; | |
| - | |
| -import static java.util.stream.Collectors.joining; | |
| - | |
| -/** | |
| - * Hamcrest matcher to help create fluent assertions about {@link ProjectBuildingResult} instances. | |
| - */ | |
| -class ProjectBuildingResultWithLocationMatcher extends BaseMatcher<ProjectBuildingResult> { | |
| - private final int columnNumber; | |
| - private final int lineNumber; | |
| - | |
| - ProjectBuildingResultWithLocationMatcher(int columnNumber, int lineNumber) { | |
| - this.columnNumber = columnNumber; | |
| - this.lineNumber = lineNumber; | |
| - } | |
| - | |
| - @Override | |
| - public boolean matches(Object o) { | |
| - if (o instanceof ProjectBuildingResult r) { | |
| - return r.getProblems().stream() | |
| - .anyMatch(p -> p.getLineNumber() == lineNumber && p.getColumnNumber() == columnNumber); | |
| - } else { | |
| - return false; | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public void describeTo(Description description) { | |
| - description | |
| - .appendText("a ProjectBuildingResult with location ") | |
| - .appendText(formatLocation(columnNumber, lineNumber)); | |
| - } | |
| - | |
| - private String formatLocation(int columnNumber, int lineNumber) { | |
| - return String.format("line %d, column %d", lineNumber, columnNumber); | |
| - } | |
| - | |
| - @Override | |
| - public void describeMismatch(final Object o, final Description description) { | |
| - if (o instanceof ProjectBuildingResult r) { | |
| - description.appendText("was a ProjectBuildingResult with locations "); | |
| - String messages = r.getProblems().stream() | |
| - .map(p -> formatLocation(p.getColumnNumber(), p.getLineNumber())) | |
| - .collect(joining(", ")); | |
| - description.appendText(messages); | |
| - } else { | |
| - super.describeMismatch(o, description); | |
| - } | |
| - } | |
| - | |
| - static Matcher<ProjectBuildingResult> projectBuildingResultWithLocation(int columnNumber, int lineNumber) { | |
| - return new ProjectBuildingResultWithLocationMatcher(columnNumber, lineNumber); | |
| - } | |
| -} | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithProblemMessageAssert.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithProblemMessageAssert.java | |
| new file mode 100644 | |
| index 0000000000..ed7a4c9558 | |
| --- /dev/null | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithProblemMessageAssert.java | |
| @@ -0,0 +1,65 @@ | |
| +/* | |
| + * 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 org.apache.maven.model.building.ModelProblem; | |
| + | |
| +import static java.util.stream.Collectors.joining; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * JUnit custom assertion to help create fluent assertions about {@link ProjectBuildingResult} instances. | |
| + */ | |
| +class ProjectBuildingResultWithProblemMessageAssert { | |
| + private final ProjectBuildingResult actual; | |
| + | |
| + ProjectBuildingResultWithProblemMessageAssert(ProjectBuildingResult actual) { | |
| + this.actual = actual; | |
| + } | |
| + | |
| + public static ProjectBuildingResultWithProblemMessageAssert assertThat(ProjectBuildingResult actual) { | |
| + return new ProjectBuildingResultWithProblemMessageAssert(actual); | |
| + } | |
| + | |
| + public ProjectBuildingResultWithProblemMessageAssert hasProblemMessage(String problemMessage) { | |
| + assertNotNull(actual); | |
| + | |
| + boolean hasMessage = | |
| + actual.getProblems().stream().anyMatch(p -> p.getMessage().contains(problemMessage)); | |
| + | |
| + if (!hasMessage) { | |
| + String actualMessages = actual.getProblems().stream() | |
| + .map(ModelProblem::getMessage) | |
| + .map(m -> "\"" + m + "\"") | |
| + .collect(joining(", ")); | |
| + String message = String.format( | |
| + "Expected ProjectBuildingResult to have problem message containing <%s> but had messages <%s>", | |
| + problemMessage, actualMessages); | |
| + assertTrue(false, message); | |
| + } | |
| + | |
| + return this; | |
| + } | |
| + | |
| + // Helper method for backward compatibility | |
| + static ProjectBuildingResultWithProblemMessageAssert projectBuildingResultWithProblemMessage(String message) { | |
| + return new ProjectBuildingResultWithProblemMessageAssert(null).hasProblemMessage(message); | |
| + } | |
| +} | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithProblemMessageMatcher.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithProblemMessageMatcher.java | |
| deleted file mode 100644 | |
| index a180175c2a..0000000000 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuildingResultWithProblemMessageMatcher.java | |
| +++ /dev/null | |
| @@ -1,69 +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 org.apache.maven.model.building.ModelProblem; | |
| -import org.hamcrest.BaseMatcher; | |
| -import org.hamcrest.Description; | |
| -import org.hamcrest.Matcher; | |
| - | |
| -import static java.util.stream.Collectors.joining; | |
| - | |
| -/** | |
| - * Hamcrest matcher to help create fluent assertions about {@link ProjectBuildingResult} instances. | |
| - */ | |
| -class ProjectBuildingResultWithProblemMessageMatcher extends BaseMatcher<ProjectBuildingResult> { | |
| - private final String problemMessage; | |
| - | |
| - ProjectBuildingResultWithProblemMessageMatcher(String problemMessage) { | |
| - this.problemMessage = problemMessage; | |
| - } | |
| - | |
| - @Override | |
| - public boolean matches(Object o) { | |
| - if (o instanceof ProjectBuildingResult r) { | |
| - return r.getProblems().stream().anyMatch(p -> p.getMessage().contains(problemMessage)); | |
| - } else { | |
| - return false; | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public void describeTo(Description description) { | |
| - description.appendText("a ProjectBuildingResult with message ").appendValue(problemMessage); | |
| - } | |
| - | |
| - @Override | |
| - public void describeMismatch(final Object o, final Description description) { | |
| - if (o instanceof ProjectBuildingResult r) { | |
| - description.appendText("was a ProjectBuildingResult with messages "); | |
| - String messages = r.getProblems().stream() | |
| - .map(ModelProblem::getMessage) | |
| - .map(m -> "\"" + m + "\"" + System.lineSeparator()) | |
| - .collect(joining(", ")); | |
| - description.appendText(messages); | |
| - } else { | |
| - super.describeMismatch(o, description); | |
| - } | |
| - } | |
| - | |
| - static Matcher<ProjectBuildingResult> projectBuildingResultWithProblemMessage(String message) { | |
| - return new ProjectBuildingResultWithProblemMessageMatcher(message); | |
| - } | |
| -} | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectModelResolverTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectModelResolverTest.java | |
| index 522fc58e03..684ac70ef8 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectModelResolverTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectModelResolverTest.java | |
| @@ -35,11 +35,10 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.containsString; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| * Test cases for the project {@code ModelResolver} implementation. | |
| @@ -67,7 +66,7 @@ void testResolveParentThrowsUnresolvableModelExceptionWhenNotFound() throws Exce | |
| () -> newModelResolver().resolveModel(parent), | |
| "Expected 'UnresolvableModelException' not thrown."); | |
| assertNotNull(e.getMessage()); | |
| - assertThat(e.getMessage(), containsString("Could not find artifact org.apache:apache:pom:0 in central")); | |
| + assertTrue(e.getMessage().contains("Could not find artifact org.apache:apache:pom:0 in central")); | |
| } | |
| @Test | |
| @@ -132,7 +131,7 @@ void testResolveDependencyThrowsUnresolvableModelExceptionWhenNotFound() throws | |
| () -> newModelResolver().resolveModel(dependency), | |
| "Expected 'UnresolvableModelException' not thrown."); | |
| assertNotNull(e.getMessage()); | |
| - assertThat(e.getMessage(), containsString("Could not find artifact org.apache:apache:pom:0 in central")); | |
| + assertTrue(e.getMessage().contains("Could not find artifact org.apache:apache:pom:0 in central")); | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectSorterTest.java b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectSorterTest.java | |
| index ada0b514b4..209f3e44f9 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectSorterTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectSorterTest.java | |
| @@ -31,10 +31,9 @@ | |
| import org.apache.maven.model.PluginManagement; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.hasItem; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| * Test sorting projects by dependencies. | |
| @@ -239,8 +238,10 @@ void testPluginDependenciesInfluenceSorting() throws Exception { | |
| assertEquals(parentProject, projects.get(0)); | |
| // the order of these two is non-deterministic, based on when they're added to the reactor. | |
| - assertThat(projects, hasItem(pluginProject)); | |
| - assertThat(projects, hasItem(pluginLevelDepProject)); | |
| + assertTrue(projects.contains(pluginProject), "Expected " + projects + " to contain " + pluginProject); | |
| + assertTrue( | |
| + projects.contains(pluginLevelDepProject), | |
| + "Expected " + projects + " to contain " + pluginLevelDepProject); | |
| // the declaring project MUST be listed after the plugin and its plugin-level dep, though. | |
| assertEquals(declaringProject, projects.get(3)); | |
| @@ -276,8 +277,10 @@ void testPluginDependenciesInfluenceSortingDeclarationInParent() throws Exceptio | |
| assertEquals(parentProject, projects.get(0)); | |
| // the order of these two is non-deterministic, based on when they're added to the reactor. | |
| - assertThat(projects, hasItem(pluginProject)); | |
| - assertThat(projects, hasItem(pluginLevelDepProject)); | |
| + assertTrue(projects.contains(pluginProject), "Expected " + projects + " to contain " + pluginProject); | |
| + assertTrue( | |
| + projects.contains(pluginLevelDepProject), | |
| + "Expected " + projects + " to contain " + pluginLevelDepProject); | |
| } | |
| @Test | |
| @@ -294,8 +297,8 @@ void testPluginVersionsAreConsidered() throws Exception { | |
| projects = new ProjectSorter(projects).getSortedProjects(); | |
| - assertThat(projects, hasItem(pluginProjectA)); | |
| - assertThat(projects, hasItem(pluginProjectB)); | |
| + assertTrue(projects.contains(pluginProjectA), "Expected " + projects + " to contain " + pluginProjectA); | |
| + assertTrue(projects.contains(pluginProjectB), "Expected " + projects + " to contain " + pluginProjectB); | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/rtinfo/internal/DefaultRuntimeInformationTest.java b/impl/maven-core/src/test/java/org/apache/maven/rtinfo/internal/DefaultRuntimeInformationTest.java | |
| index e04b13aca0..1c474c69ed 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/rtinfo/internal/DefaultRuntimeInformationTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/rtinfo/internal/DefaultRuntimeInformationTest.java | |
| @@ -38,7 +38,7 @@ class DefaultRuntimeInformationTest { | |
| void testGetMavenVersion() { | |
| String mavenVersion = rtInfo.getMavenVersion(); | |
| assertNotNull(mavenVersion); | |
| - assertTrue(!mavenVersion.isEmpty()); | |
| + assertTrue(!mavenVersion.isEmpty(), "Expected Maven version to not be empty but was: " + mavenVersion); | |
| } | |
| @Test | |
| diff --git a/impl/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java b/impl/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java | |
| index ae1ee942cf..c7dc9998a9 100644 | |
| --- a/impl/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java | |
| +++ b/impl/maven-core/src/test/java/org/apache/maven/settings/PomConstructionWithSettingsTest.java | |
| @@ -131,7 +131,7 @@ private PomTestWrapper buildPom(String pomPath) throws Exception { | |
| private static Settings readSettingsFile(File settingsFile) throws IOException, XMLStreamException { | |
| try (InputStream is = Files.newInputStream(settingsFile.toPath())) { | |
| SettingsStaxReader reader = new SettingsStaxReader(); | |
| - InputSource source = new InputSource(settingsFile.toString()); | |
| + InputSource source = InputSource.of(settingsFile.toString()); | |
| return new Settings(reader.read(is, true, source)); | |
| } | |
| } | |
| diff --git a/impl/maven-core/src/test/projects/plugin-manager/project-with-plugin-classpath-ordering/sub/settings-template.xml b/impl/maven-core/src/test/projects/plugin-manager/project-with-plugin-classpath-ordering/sub/settings-template.xml | |
| index cde4215860..ddfed199ae 100644 | |
| --- a/impl/maven-core/src/test/projects/plugin-manager/project-with-plugin-classpath-ordering/sub/settings-template.xml | |
| +++ b/impl/maven-core/src/test/projects/plugin-manager/project-with-plugin-classpath-ordering/sub/settings-template.xml | |
| @@ -19,7 +19,7 @@ specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| -<settings> | |
| +<settings xmlns='http://maven.apache.org/SETTINGS/1.0.0'> | |
| <profiles> | |
| <profile> | |
| <id>maven-core-it-repo</id> | |
| diff --git a/impl/maven-core/src/test/resources-settings/repositories/settings.xml b/impl/maven-core/src/test/resources-settings/repositories/settings.xml | |
| index 6f96f0b6f8..a8eff40516 100644 | |
| --- a/impl/maven-core/src/test/resources-settings/repositories/settings.xml | |
| +++ b/impl/maven-core/src/test/resources-settings/repositories/settings.xml | |
| @@ -19,7 +19,7 @@ specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| -<settings> | |
| +<settings xmlns='http://maven.apache.org/SETTINGS/1.0.0'> | |
| <profiles> | |
| <profile> | |
| <id>maven-core-it-repo</id> | |
| diff --git a/impl/maven-core/src/test/resources-settings/test-pom-and-settings-interpolation/settings.xml b/impl/maven-core/src/test/resources-settings/test-pom-and-settings-interpolation/settings.xml | |
| index 2d42d495c5..536c6be0bb 100644 | |
| --- a/impl/maven-core/src/test/resources-settings/test-pom-and-settings-interpolation/settings.xml | |
| +++ b/impl/maven-core/src/test/resources-settings/test-pom-and-settings-interpolation/settings.xml | |
| @@ -19,7 +19,7 @@ specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| -<settings> | |
| +<settings xmlns='http://maven.apache.org/SETTINGS/1.0.0'> | |
| <profiles> | |
| <profile> | |
| <id>settings</id> | |
| diff --git a/impl/maven-di/pom.xml b/impl/maven-di/pom.xml | |
| index 5a6d5c3773..4068da194b 100644 | |
| --- a/impl/maven-di/pom.xml | |
| +++ b/impl/maven-di/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-di</artifactId> | |
| @@ -45,11 +45,6 @@ under the License. | |
| <artifactId>junit-jupiter-api</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.assertj</groupId> | |
| - <artifactId>assertj-core</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| </dependencies> | |
| </project> | |
| diff --git a/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java b/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java | |
| index e908b8e2fe..270ea6947c 100644 | |
| --- a/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java | |
| +++ b/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java | |
| @@ -176,4 +176,16 @@ static Injector create() { | |
| */ | |
| @Nonnull | |
| <T> T getInstance(@Nonnull Key<T> key); | |
| + | |
| + /** | |
| + * Disposes this Injector, clearing all internal state (bindings, caches, scopes, etc.). | |
| + * After calling this, the Injector should not be used again. | |
| + * @since 4.1 | |
| + */ | |
| + default void dispose() { | |
| + // delegate to the implementation | |
| + if (this instanceof InjectorImpl) { | |
| + ((InjectorImpl) this).dispose(); | |
| + } | |
| + } | |
| } | |
| diff --git a/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java b/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java | |
| index f2963b911b..b0672ff56f 100644 | |
| --- a/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java | |
| +++ b/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java | |
| @@ -279,8 +279,8 @@ protected <Q> Supplier<Q> compile(Binding<Q> binding) { | |
| if (binding.getScope() != null) { | |
| Scope scope = scopes.entrySet().stream() | |
| .filter(e -> e.getKey().isInstance(binding.getScope())) | |
| - .map(Map.Entry::getValue) | |
| .findFirst() | |
| + .map(Map.Entry::getValue) | |
| .orElseThrow(() -> new DIException("Scope not bound for annotation " | |
| + binding.getScope().annotationType())) | |
| .get(); | |
| @@ -467,4 +467,24 @@ public T get() { | |
| }); | |
| } | |
| } | |
| + | |
| + /** | |
| + * Release all internal state so this Injector can be GC’d | |
| + * (and so that subsequent tests start from a clean slate). | |
| + * @since 4.1 | |
| + */ | |
| + public void dispose() { | |
| + // First, clear any singleton‐scope caches | |
| + scopes.values().stream() | |
| + .map(Supplier::get) | |
| + .filter(scope -> scope instanceof SingletonScope) | |
| + .map(scope -> (SingletonScope) scope) | |
| + .forEach(singleton -> singleton.cache.clear()); | |
| + | |
| + // Now clear everything else | |
| + bindings.clear(); | |
| + scopes.clear(); | |
| + loadedUrls.clear(); | |
| + resolutionStack.remove(); | |
| + } | |
| } | |
| diff --git a/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java b/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java | |
| index f02eb011d9..46ea1b3318 100644 | |
| --- a/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java | |
| +++ b/impl/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java | |
| @@ -38,7 +38,6 @@ | |
| import org.junit.jupiter.api.Test; | |
| import static java.lang.annotation.RetentionPolicy.RUNTIME; | |
| -import static org.assertj.core.api.Assertions.assertThat; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertInstanceOf; | |
| import static org.junit.jupiter.api.Assertions.assertNotEquals; | |
| @@ -46,6 +45,7 @@ | |
| import static org.junit.jupiter.api.Assertions.assertNotSame; | |
| import static org.junit.jupiter.api.Assertions.assertNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| @SuppressWarnings("unused") | |
| public class InjectorImplTest { | |
| @@ -416,11 +416,20 @@ void testCircularPriorityDependency() { | |
| DIException exception = assertThrows(DIException.class, () -> { | |
| injector.getInstance(CircularPriorityTest.MyService.class); | |
| }); | |
| - assertThat(exception).isInstanceOf(DIException.class).hasMessageContaining("HighPriorityServiceImpl"); | |
| - assertThat(exception.getCause()) | |
| - .isInstanceOf(DIException.class) | |
| - .hasMessageContaining("Cyclic dependency detected") | |
| - .hasMessageContaining("MyService"); | |
| + assertInstanceOf(DIException.class, exception, "Expected exception to be DIException"); | |
| + assertTrue( | |
| + exception.getMessage().contains("HighPriorityServiceImpl"), | |
| + "Expected exception message to contain 'HighPriorityServiceImpl' but was: " + exception.getMessage()); | |
| + | |
| + assertInstanceOf(DIException.class, exception.getCause(), "Expected cause to be DIException"); | |
| + assertTrue( | |
| + exception.getCause().getMessage().contains("Cyclic dependency detected"), | |
| + "Expected cause message to contain 'Cyclic dependency detected' but was: " | |
| + + exception.getCause().getMessage()); | |
| + assertTrue( | |
| + exception.getCause().getMessage().contains("MyService"), | |
| + "Expected cause message to contain 'MyService' but was: " | |
| + + exception.getCause().getMessage()); | |
| } | |
| @Test | |
| @@ -475,4 +484,34 @@ static class MediumPriorityServiceImpl implements MyService {} | |
| @Named | |
| static class DefaultPriorityServiceImpl implements MyService {} | |
| } | |
| + | |
| + @Test | |
| + void testDisposeClearsBindingsAndCache() { | |
| + final Injector injector = Injector.create() | |
| + // bind two simple beans | |
| + .bindImplicit(DisposeTest.Foo.class) | |
| + .bindImplicit(DisposeTest.Bar.class); | |
| + | |
| + // make sure they really get created | |
| + assertNotNull(injector.getInstance(DisposeTest.Foo.class)); | |
| + assertNotNull(injector.getInstance(DisposeTest.Bar.class)); | |
| + | |
| + // now dispose | |
| + injector.dispose(); | |
| + | |
| + // after dispose, bindings should be gone => DIException on lookup | |
| + assertThrows(DIException.class, () -> injector.getInstance(DisposeTest.Foo.class)); | |
| + assertThrows(DIException.class, () -> injector.getInstance(DisposeTest.Bar.class)); | |
| + } | |
| + | |
| + /** | |
| + * Simple test classes for dispose(). | |
| + */ | |
| + static class DisposeTest { | |
| + @Named | |
| + static class Foo {} | |
| + | |
| + @Named | |
| + static class Bar {} | |
| + } | |
| } | |
| diff --git a/impl/maven-executor/pom.xml b/impl/maven-executor/pom.xml | |
| index ef87e64255..093adffc95 100644 | |
| --- a/impl/maven-executor/pom.xml | |
| +++ b/impl/maven-executor/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-executor</artifactId> | |
| diff --git a/impl/maven-impl/pom.xml b/impl/maven-impl/pom.xml | |
| index a9d4010a28..ca1b5ddbe8 100644 | |
| --- a/impl/maven-impl/pom.xml | |
| +++ b/impl/maven-impl/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-impl</artifactId> | |
| @@ -145,16 +145,6 @@ under the License. | |
| <artifactId>mockito-junit-jupiter</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.assertj</groupId> | |
| - <artifactId>assertj-core</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.resolver</groupId> | |
| <artifactId>maven-resolver-transport-file</artifactId> | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java | |
| index 0a7cbb621c..668f450bc7 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java | |
| @@ -85,8 +85,16 @@ record ModelResolverRequest( | |
| @Nonnull String groupId, | |
| @Nonnull String artifactId, | |
| @Nonnull String version, | |
| - @Nullable String classifier) | |
| + @Nullable String classifier, | |
| + @Nullable String extension) | |
| implements Request<Session> { | |
| + public ModelResolverRequest { | |
| + Objects.requireNonNull(session, "session cannot be null"); | |
| + Objects.requireNonNull(groupId, "groupId cannot be null"); | |
| + Objects.requireNonNull(artifactId, "artifactId cannot be null"); | |
| + Objects.requireNonNull(version, "version cannot be null"); | |
| + } | |
| + | |
| @Nonnull | |
| @Override | |
| public Session getSession() { | |
| @@ -106,12 +114,13 @@ public boolean equals(Object o) { | |
| && Objects.equals(groupId, that.groupId) | |
| && Objects.equals(artifactId, that.artifactId) | |
| && Objects.equals(version, that.version) | |
| - && Objects.equals(classifier, that.classifier); | |
| + && Objects.equals(classifier, that.classifier) | |
| + && Objects.equals(extension, that.extension); | |
| } | |
| @Override | |
| public int hashCode() { | |
| - return Objects.hash(repositories, groupId, artifactId, version, classifier); | |
| + return Objects.hash(repositories, groupId, artifactId, version, classifier, extension); | |
| } | |
| @Override | |
| @@ -123,6 +132,7 @@ public String toString() { | |
| + ", artifactId=" + artifactId | |
| + ", version=" + version | |
| + ", classifier=" + classifier | |
| + + ", extension=" + extension | |
| + ']'; | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java | |
| index 8d0e5cbdac..116b918959 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelValidator.java | |
| @@ -49,10 +49,18 @@ public interface ModelValidator { | |
| * Denotes validation as performed by Maven 4.0. This validation level is meant for new projects. | |
| */ | |
| int VALIDATION_LEVEL_MAVEN_4_0 = 40; | |
| + /** | |
| + * Denotes validation as performed by Maven 4.1. This validation level is meant for new projects. | |
| + */ | |
| + int VALIDATION_LEVEL_MAVEN_4_1 = 41; | |
| + /** | |
| + * Denotes validation as performed by Maven 4.2. This validation level is meant for new projects. | |
| + */ | |
| + int VALIDATION_LEVEL_MAVEN_4_2 = 42; | |
| /** | |
| * Denotes strict validation as recommended by the current Maven version. | |
| */ | |
| - int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_4_0; | |
| + int VALIDATION_LEVEL_STRICT = VALIDATION_LEVEL_MAVEN_4_2; | |
| /** | |
| * Checks the specified file model for missing or invalid values. This model is directly created from the POM | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java | |
| index af40c643c6..7992df53b0 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java | |
| @@ -30,7 +30,6 @@ | |
| import java.util.Objects; | |
| import java.util.Optional; | |
| import java.util.Set; | |
| -import java.util.WeakHashMap; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.concurrent.CopyOnWriteArrayList; | |
| import java.util.function.Function; | |
| @@ -103,6 +102,7 @@ | |
| import org.apache.maven.di.Key; | |
| import org.apache.maven.di.impl.Binding; | |
| import org.apache.maven.di.impl.InjectorImpl; | |
| +import org.apache.maven.impl.cache.Cache; | |
| import org.eclipse.aether.DefaultRepositorySystemSession; | |
| import org.eclipse.aether.RepositorySystem; | |
| import org.eclipse.aether.RepositorySystemSession; | |
| @@ -122,14 +122,14 @@ public abstract class AbstractSession implements InternalSession { | |
| protected final Injector injector; | |
| private final Map<Class<? extends Service>, Service> services = new ConcurrentHashMap<>(); | |
| private final List<Listener> listeners = new CopyOnWriteArrayList<>(); | |
| - private final Map<org.eclipse.aether.graph.DependencyNode, Node> allNodes = | |
| - Collections.synchronizedMap(new WeakHashMap<>()); | |
| - private final Map<Class<? extends Artifact>, Map<org.eclipse.aether.artifact.Artifact, Artifact>> allArtifacts = | |
| + private final Cache<org.eclipse.aether.graph.DependencyNode, Node> allNodes = | |
| + Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Nodes"); | |
| + private final Map<Class<? extends Artifact>, Cache<org.eclipse.aether.artifact.Artifact, Artifact>> allArtifacts = | |
| new ConcurrentHashMap<>(); | |
| - private final Map<org.eclipse.aether.repository.RemoteRepository, RemoteRepository> allRepositories = | |
| - Collections.synchronizedMap(new WeakHashMap<>()); | |
| - private final Map<org.eclipse.aether.graph.Dependency, Dependency> allDependencies = | |
| - Collections.synchronizedMap(new WeakHashMap<>()); | |
| + private final Cache<org.eclipse.aether.repository.RemoteRepository, RemoteRepository> allRepositories = | |
| + Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Repositories"); | |
| + private final Cache<org.eclipse.aether.graph.Dependency, Dependency> allDependencies = | |
| + Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Dependencies"); | |
| private volatile RequestCache requestCache; | |
| static { | |
| @@ -251,8 +251,8 @@ public Artifact getArtifact(@Nonnull org.eclipse.aether.artifact.Artifact artifa | |
| @SuppressWarnings("unchecked") | |
| @Override | |
| public <T extends Artifact> T getArtifact(Class<T> clazz, org.eclipse.aether.artifact.Artifact artifact) { | |
| - Map<org.eclipse.aether.artifact.Artifact, Artifact> map = | |
| - allArtifacts.computeIfAbsent(clazz, c -> Collections.synchronizedMap(new WeakHashMap<>())); | |
| + Cache<org.eclipse.aether.artifact.Artifact, Artifact> map = allArtifacts.computeIfAbsent( | |
| + clazz, c -> Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Artifacts-" + c.getSimpleName())); | |
| if (clazz == Artifact.class) { | |
| return (T) map.computeIfAbsent(artifact, a -> new DefaultArtifact(this, a)); | |
| } else if (clazz == DownloadedArtifact.class) { | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java | |
| index e7a699e3b0..e9ac14d848 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultModelXmlFactory.java | |
| @@ -18,6 +18,15 @@ | |
| */ | |
| package org.apache.maven.impl; | |
| +import javax.xml.stream.XMLInputFactory; | |
| +import javax.xml.stream.XMLStreamConstants; | |
| +import javax.xml.stream.XMLStreamException; | |
| +import javax.xml.stream.XMLStreamReader; | |
| + | |
| +import java.io.ByteArrayInputStream; | |
| +import java.io.ByteArrayOutputStream; | |
| +import java.io.CharArrayReader; | |
| +import java.io.CharArrayWriter; | |
| import java.io.InputStream; | |
| import java.io.OutputStream; | |
| import java.io.Reader; | |
| @@ -88,10 +97,36 @@ private Model doRead(XmlReaderRequest request) throws XmlReaderException { | |
| throw new IllegalArgumentException("path, url, reader or inputStream must be non null"); | |
| } | |
| try { | |
| + // If modelId is not provided and we're reading from a file, try to extract it | |
| + String modelId = request.getModelId(); | |
| + String location = request.getLocation(); | |
| + | |
| + if (modelId == null) { | |
| + if (inputStream != null) { | |
| + ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| + inputStream.transferTo(baos); | |
| + byte[] buf = baos.toByteArray(); | |
| + modelId = extractModelId(new ByteArrayInputStream(buf)); | |
| + inputStream = new ByteArrayInputStream(buf); | |
| + } else if (reader != null) { | |
| + CharArrayWriter caw = new CharArrayWriter(); | |
| + reader.transferTo(caw); | |
| + char[] buf = caw.toCharArray(); | |
| + modelId = extractModelId(new CharArrayReader(buf)); | |
| + reader = new CharArrayReader(buf); | |
| + } else if (path != null) { | |
| + try (InputStream is = Files.newInputStream(path)) { | |
| + modelId = extractModelId(is); | |
| + if (location == null) { | |
| + location = path.toUri().toString(); | |
| + } | |
| + } | |
| + } | |
| + } | |
| + | |
| InputSource source = null; | |
| - if (request.getModelId() != null || request.getLocation() != null) { | |
| - source = new InputSource( | |
| - request.getModelId(), path != null ? path.toUri().toString() : null); | |
| + if (modelId != null || location != null) { | |
| + source = InputSource.of(modelId, path != null ? path.toUri().toString() : null); | |
| } | |
| MavenStaxReader xml = request.getTransformer() != null | |
| ? new MavenStaxReader(request.getTransformer()::transform) | |
| @@ -152,6 +187,147 @@ public void write(XmlWriterRequest<Model> request) throws XmlWriterException { | |
| } | |
| } | |
| + static class InputFactoryHolder { | |
| + static final XMLInputFactory XML_INPUT_FACTORY; | |
| + | |
| + static { | |
| + XMLInputFactory factory = XMLInputFactory.newFactory(); | |
| + factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true); | |
| + factory.setProperty(XMLInputFactory.IS_COALESCING, true); | |
| + XML_INPUT_FACTORY = factory; | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Extracts the modelId (groupId:artifactId:version) from a POM XML stream | |
| + * by parsing just enough XML to get the GAV coordinates. | |
| + * | |
| + * @param inputStream the input stream to read from | |
| + * @return the modelId in format "groupId:artifactId:version" or null if not determinable | |
| + */ | |
| + private String extractModelId(InputStream inputStream) { | |
| + try { | |
| + XMLStreamReader reader = InputFactoryHolder.XML_INPUT_FACTORY.createXMLStreamReader(inputStream); | |
| + try { | |
| + return extractModelId(reader); | |
| + } finally { | |
| + reader.close(); | |
| + } | |
| + } catch (Exception e) { | |
| + // If extraction fails, return null and let the normal parsing handle it | |
| + // This is not a critical failure | |
| + return null; | |
| + } | |
| + } | |
| + | |
| + private String extractModelId(Reader reader) { | |
| + try { | |
| + // Use a buffered stream to allow efficient reading | |
| + XMLStreamReader xmlReader = InputFactoryHolder.XML_INPUT_FACTORY.createXMLStreamReader(reader); | |
| + try { | |
| + return extractModelId(xmlReader); | |
| + } finally { | |
| + xmlReader.close(); | |
| + } | |
| + } catch (Exception e) { | |
| + // If extraction fails, return null and let the normal parsing handle it | |
| + // This is not a critical failure | |
| + return null; | |
| + } | |
| + } | |
| + | |
| + private static String extractModelId(XMLStreamReader reader) throws XMLStreamException { | |
| + String groupId = null; | |
| + String artifactId = null; | |
| + String version = null; | |
| + String parentGroupId = null; | |
| + String parentVersion = null; | |
| + | |
| + boolean inProject = false; | |
| + boolean inParent = false; | |
| + String currentElement = null; | |
| + | |
| + while (reader.hasNext()) { | |
| + int event = reader.next(); | |
| + | |
| + if (event == XMLStreamConstants.START_ELEMENT) { | |
| + String localName = reader.getLocalName(); | |
| + | |
| + if ("project".equals(localName)) { | |
| + inProject = true; | |
| + } else if ("parent".equals(localName) && inProject) { | |
| + inParent = true; | |
| + } else if (inProject | |
| + && ("groupId".equals(localName) | |
| + || "artifactId".equals(localName) | |
| + || "version".equals(localName))) { | |
| + currentElement = localName; | |
| + } | |
| + } else if (event == XMLStreamConstants.END_ELEMENT) { | |
| + String localName = reader.getLocalName(); | |
| + | |
| + if ("parent".equals(localName)) { | |
| + inParent = false; | |
| + } else if ("project".equals(localName)) { | |
| + break; // We've processed the main project element | |
| + } | |
| + currentElement = null; | |
| + } else if (event == XMLStreamConstants.CHARACTERS && currentElement != null) { | |
| + String text = reader.getText().trim(); | |
| + if (!text.isEmpty()) { | |
| + if (inParent) { | |
| + switch (currentElement) { | |
| + case "groupId": | |
| + parentGroupId = text; | |
| + break; | |
| + case "version": | |
| + parentVersion = text; | |
| + break; | |
| + default: | |
| + // Ignore other elements | |
| + break; | |
| + } | |
| + } else { | |
| + switch (currentElement) { | |
| + case "groupId": | |
| + groupId = text; | |
| + break; | |
| + case "artifactId": | |
| + artifactId = text; | |
| + break; | |
| + case "version": | |
| + version = text; | |
| + break; | |
| + default: | |
| + // Ignore other elements | |
| + break; | |
| + } | |
| + } | |
| + } | |
| + } | |
| + | |
| + // Early exit if we have enough information | |
| + if (artifactId != null && groupId != null && version != null) { | |
| + break; | |
| + } | |
| + } | |
| + | |
| + // Use parent values as fallback | |
| + if (groupId == null) { | |
| + groupId = parentGroupId; | |
| + } | |
| + if (version == null) { | |
| + version = parentVersion; | |
| + } | |
| + | |
| + // Return modelId if we have all required components | |
| + if (groupId != null && artifactId != null && version != null) { | |
| + return groupId + ":" + artifactId + ":" + version; | |
| + } | |
| + | |
| + return null; | |
| + } | |
| + | |
| /** | |
| * Simply parse the given xml string. | |
| * | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsValidator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsValidator.java | |
| index 21284afa0c..b2df07a6e2 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsValidator.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsValidator.java | |
| @@ -271,7 +271,7 @@ private void validateRepositories( | |
| */ | |
| private static boolean validateStringEmpty( | |
| ProblemCollector<BuilderProblem> problems, String fieldName, String string, String message) { | |
| - if (string == null || string.length() == 0) { | |
| + if (string == null || string.isEmpty()) { | |
| return true; | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java | |
| index 348c8a9a8b..0aecc0460b 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsXmlFactory.java | |
| @@ -53,7 +53,7 @@ public Settings read(@Nonnull XmlReaderRequest request) throws XmlReaderExceptio | |
| try { | |
| InputSource source = null; | |
| if (request.getModelId() != null || request.getLocation() != null) { | |
| - source = new InputSource(request.getLocation()); | |
| + source = InputSource.of(request.getLocation()); | |
| } | |
| SettingsStaxReader xml = request.getTransformer() != null | |
| ? new SettingsStaxReader(request.getTransformer()::transform) | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java | |
| index 18a6bd8368..89452d1367 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsXmlFactory.java | |
| @@ -55,7 +55,7 @@ public PersistedToolchains read(@Nonnull XmlReaderRequest request) throws XmlRea | |
| try { | |
| InputSource source = null; | |
| if (request.getModelId() != null || request.getLocation() != null) { | |
| - source = new InputSource(request.getLocation()); | |
| + source = InputSource.of(request.getLocation()); | |
| } | |
| MavenToolchainsStaxReader xml = request.getTransformer() != null | |
| ? new MavenToolchainsStaxReader(request.getTransformer()::transform) | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java | |
| index 20d68cd7bb..a8cc3da2ec 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java | |
| @@ -449,18 +449,21 @@ private static String[] normalizePatterns(final Collection<String> patterns, fin | |
| pattern = pattern.substring(3); | |
| } | |
| pattern = pattern.replace("/**/**/", "/**/"); | |
| + | |
| + // Escape special characters, including braces | |
| + // Braces from user input must be literals; we'll inject our own braces for expansion below | |
| pattern = pattern.replace("\\", "\\\\") | |
| .replace("[", "\\[") | |
| .replace("]", "\\]") | |
| .replace("{", "\\{") | |
| .replace("}", "\\}"); | |
| + | |
| + // Transform ** patterns to use brace expansion for POSIX behavior | |
| + // This replaces the complex addPatternsWithOneDirRemoved logic | |
| + // We perform this after escaping so that only these injected braces participate in expansion | |
| + pattern = pattern.replace("**/", "{**/,}"); | |
| + | |
| normalized.add(DEFAULT_SYNTAX + pattern); | |
| - /* | |
| - * If the pattern starts or ends with "**", Java GLOB expects a directory level at | |
| - * that location while Maven seems to consider that "**" can mean "no directory". | |
| - * Add another pattern for reproducing this effect. | |
| - */ | |
| - addPatternsWithOneDirRemoved(normalized, pattern, 0); | |
| } else { | |
| normalized.add(pattern); | |
| } | |
| @@ -469,37 +472,6 @@ private static String[] normalizePatterns(final Collection<String> patterns, fin | |
| return simplify(normalized, excludes); | |
| } | |
| - /** | |
| - * Adds all variants of the given pattern with {@code **} removed. | |
| - * This is used for simulating the Maven behavior where {@code "**} may match zero directory. | |
| - * Tests suggest that we need an explicit GLOB pattern with no {@code "**"} for matching an absence of directory. | |
| - * | |
| - * @param patterns where to add the derived patterns | |
| - * @param pattern the pattern for which to add derived forms, without the "glob:" syntax prefix | |
| - * @param end should be 0 (reserved for recursive invocations of this method) | |
| - */ | |
| - private static void addPatternsWithOneDirRemoved(final Set<String> patterns, final String pattern, int end) { | |
| - final int length = pattern.length(); | |
| - int start; | |
| - while ((start = pattern.indexOf("**", end)) >= 0) { | |
| - end = start + 2; // 2 is the length of "**". | |
| - if (end < length) { | |
| - if (pattern.charAt(end) != '/') { | |
| - continue; | |
| - } | |
| - if (start == 0) { | |
| - end++; // Ommit the leading slash if there is nothing before it. | |
| - } | |
| - } | |
| - if (start > 0 && pattern.charAt(--start) != '/') { | |
| - continue; | |
| - } | |
| - String reduced = pattern.substring(0, start) + pattern.substring(end); | |
| - patterns.add(DEFAULT_SYNTAX + reduced); | |
| - addPatternsWithOneDirRemoved(patterns, reduced, start); | |
| - } | |
| - } | |
| - | |
| /** | |
| * Applies some heuristic rules for simplifying the set of patterns, | |
| * then returns the patterns as an array. | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/SettingsUtilsV4.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/SettingsUtilsV4.java | |
| index 8a5d1e81a2..0290464ebb 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/SettingsUtilsV4.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/SettingsUtilsV4.java | |
| @@ -361,10 +361,10 @@ private static org.apache.maven.api.settings.InputLocation toLocation( | |
| org.apache.maven.api.model.InputSource source = location.getSource(); | |
| Map<Object, org.apache.maven.api.settings.InputLocation> locs = location.getLocations().entrySet().stream() | |
| .collect(Collectors.toMap(Map.Entry::getKey, e -> toLocation(e.getValue()))); | |
| - return new org.apache.maven.api.settings.InputLocation( | |
| + return org.apache.maven.api.settings.InputLocation.of( | |
| location.getLineNumber(), | |
| location.getColumnNumber(), | |
| - source != null ? new org.apache.maven.api.settings.InputSource(source.getLocation()) : null, | |
| + source != null ? org.apache.maven.api.settings.InputSource.of(source.getLocation()) : null, | |
| locs); | |
| } else { | |
| return null; | |
| @@ -377,10 +377,10 @@ private static org.apache.maven.api.model.InputLocation toLocation( | |
| org.apache.maven.api.settings.InputSource source = location.getSource(); | |
| Map<Object, InputLocation> locs = location.getLocations().entrySet().stream() | |
| .collect(Collectors.toMap(Map.Entry::getKey, e -> toLocation(e.getValue()))); | |
| - return new org.apache.maven.api.model.InputLocation( | |
| + return org.apache.maven.api.model.InputLocation.of( | |
| location.getLineNumber(), | |
| location.getColumnNumber(), | |
| - source != null ? new org.apache.maven.api.model.InputSource("", source.getLocation()) : null, | |
| + source != null ? org.apache.maven.api.model.InputSource.of("", source.getLocation()) : null, | |
| locs); | |
| } else { | |
| return null; | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/Cache.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/Cache.java | |
| new file mode 100644 | |
| index 0000000000..440a0a860e | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/Cache.java | |
| @@ -0,0 +1,789 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.lang.ref.Reference; | |
| +import java.lang.ref.ReferenceQueue; | |
| +import java.lang.ref.SoftReference; | |
| +import java.lang.ref.WeakReference; | |
| +import java.util.Collection; | |
| +import java.util.Map; | |
| +import java.util.Objects; | |
| +import java.util.Set; | |
| +import java.util.concurrent.ConcurrentHashMap; | |
| +import java.util.concurrent.atomic.AtomicLong; | |
| +import java.util.function.BiPredicate; | |
| +import java.util.function.Function; | |
| + | |
| +/** | |
| + * A cache interface that provides configurable reference types for both keys and values, | |
| + * and supports automatic cleanup of garbage-collected entries. | |
| + * <p> | |
| + * This cache is designed for scenarios where: | |
| + * <ul> | |
| + * <li>Values should be eligible for garbage collection when memory is low</li> | |
| + * <li>Concurrent access is required</li> | |
| + * <li>Automatic cleanup of stale entries is desired</li> | |
| + * </ul> | |
| + * <p> | |
| + * The cache can use different reference types (none, soft, weak, hard) for both keys and values, | |
| + * depending on the factory method used to create the cache instance. | |
| + * <p> | |
| + * Note: All implementations are thread-safe and optimized for concurrent read access. | |
| + * | |
| + * @param <K> the type of keys maintained by this cache | |
| + * @param <V> the type of cached values | |
| + */ | |
| +public interface Cache<K, V> { | |
| + | |
| + /** | |
| + * Enumeration of reference types that can be used for individual entries. | |
| + */ | |
| + enum ReferenceType { | |
| + /** No caching - always compute the value */ | |
| + NONE, | |
| + /** Soft references - cleared before OutOfMemoryError */ | |
| + SOFT, | |
| + /** Weak references - cleared more aggressively */ | |
| + WEAK, | |
| + /** Hard references - never cleared by GC */ | |
| + HARD | |
| + } | |
| + | |
| + /** | |
| + * Computes a value for the given key if it's not already present, using the specified reference type. | |
| + * <p> | |
| + * This method allows fine-grained control over the reference type used for individual entries, | |
| + * overriding the cache's default reference type for this specific key-value pair. | |
| + * | |
| + * @param key the key whose associated value is to be returned or computed | |
| + * @param mappingFunction the function to compute a value | |
| + * @param referenceType the reference type to use for this entry (null uses cache default) | |
| + * @return the current (existing or computed) value associated with the specified key | |
| + */ | |
| + V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction, ReferenceType referenceType); | |
| + | |
| + default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { | |
| + return computeIfAbsent(key, mappingFunction, null); | |
| + } | |
| + | |
| + void removeIf(BiPredicate<K, V> o); | |
| + | |
| + V get(K key); | |
| + | |
| + int size(); | |
| + | |
| + void clear(); | |
| + | |
| + default boolean containsKey(K key) { | |
| + return get(key) != null; | |
| + } | |
| + | |
| + static <K, V> Cache<K, V> newCache(ReferenceType referenceType) { | |
| + return RefConcurrentMap.newCache(referenceType); | |
| + } | |
| + | |
| + static <K, V> Cache<K, V> newCache(ReferenceType referenceType, String name) { | |
| + return RefConcurrentMap.newCache(referenceType, name); | |
| + } | |
| + | |
| + /** | |
| + * Creates a new cache with separate reference types for keys and values. | |
| + * This allows fine-grained control over eviction behavior and enables | |
| + * better tracking of cache misses caused by key vs value evictions. | |
| + * | |
| + * @param keyReferenceType the reference type to use for keys | |
| + * @param valueReferenceType the reference type to use for values | |
| + * @return a new cache instance | |
| + */ | |
| + static <K, V> Cache<K, V> newCache(ReferenceType keyReferenceType, ReferenceType valueReferenceType) { | |
| + return RefConcurrentMap.newCache(keyReferenceType, valueReferenceType); | |
| + } | |
| + | |
| + static <K, V> Cache<K, V> newCache(ReferenceType keyReferenceType, ReferenceType valueReferenceType, String name) { | |
| + return RefConcurrentMap.newCache(keyReferenceType, valueReferenceType, name); | |
| + } | |
| + | |
| + /** | |
| + * Interface for listening to cache eviction events. | |
| + */ | |
| + interface EvictionListener { | |
| + /** | |
| + * Called when a key is evicted from the cache. | |
| + */ | |
| + void onKeyEviction(); | |
| + | |
| + /** | |
| + * Called when a value is evicted from the cache. | |
| + */ | |
| + void onValueEviction(); | |
| + } | |
| + | |
| + /** | |
| + * A concurrent map implementation that uses configurable reference types for both keys and values, | |
| + * and supports automatic cleanup of garbage-collected entries. | |
| + * <p> | |
| + * This implementation is package-private and accessed through the {@link Cache} interface. | |
| + * | |
| + * @param <K> the type of keys maintained by this map | |
| + * @param <V> the type of mapped values | |
| + */ | |
| + class RefConcurrentMap<K, V> implements Map<K, V>, Cache<K, V> { | |
| + | |
| + private final ReferenceQueue<K> keyQueue = new ReferenceQueue<>(); | |
| + private final ReferenceQueue<V> valueQueue = new ReferenceQueue<>(); | |
| + private final ConcurrentHashMap<RefConcurrentReference<K>, ComputeReference<V>> map = new ConcurrentHashMap<>(); | |
| + | |
| + // Default reference types for this map | |
| + private final ReferenceType defaultReferenceType; | |
| + private final ReferenceType keyReferenceType; | |
| + private final ReferenceType valueReferenceType; | |
| + | |
| + // Cache name for debugging | |
| + private final String name; | |
| + | |
| + // Eviction statistics | |
| + private final AtomicLong keyEvictions = new AtomicLong(0); | |
| + private final AtomicLong valueEvictions = new AtomicLong(0); | |
| + | |
| + // Eviction listener (weak reference to avoid memory leaks) | |
| + private volatile EvictionListener evictionListener; | |
| + | |
| + /** | |
| + * Private constructor - use factory methods to create instances. | |
| + */ | |
| + private RefConcurrentMap(ReferenceType defaultReferenceType) { | |
| + this.defaultReferenceType = defaultReferenceType; | |
| + this.keyReferenceType = defaultReferenceType; | |
| + this.valueReferenceType = defaultReferenceType; | |
| + this.name = "Cache-" + defaultReferenceType; | |
| + } | |
| + | |
| + /** | |
| + * Private constructor with separate key and value reference types. | |
| + */ | |
| + private RefConcurrentMap(ReferenceType keyReferenceType, ReferenceType valueReferenceType) { | |
| + this.defaultReferenceType = keyReferenceType; // For backward compatibility | |
| + this.keyReferenceType = keyReferenceType; | |
| + this.valueReferenceType = valueReferenceType; | |
| + this.name = "Cache-" + keyReferenceType + "/" + valueReferenceType; | |
| + } | |
| + | |
| + /** | |
| + * Private constructor with name. | |
| + */ | |
| + private RefConcurrentMap(ReferenceType defaultReferenceType, String name) { | |
| + this.defaultReferenceType = defaultReferenceType; | |
| + this.keyReferenceType = defaultReferenceType; | |
| + this.valueReferenceType = defaultReferenceType; | |
| + this.name = name; | |
| + } | |
| + | |
| + /** | |
| + * Private constructor with separate key and value reference types and name. | |
| + */ | |
| + private RefConcurrentMap(ReferenceType keyReferenceType, ReferenceType valueReferenceType, String name) { | |
| + this.defaultReferenceType = keyReferenceType; // For backward compatibility | |
| + this.keyReferenceType = keyReferenceType; | |
| + this.valueReferenceType = valueReferenceType; | |
| + this.name = name; | |
| + | |
| + // Debug logging to verify constructor assignment (disabled) | |
| + // System.err.println("DEBUG: RefConcurrentMap constructor - name=" + name + ", keyReferenceType=" | |
| + // + keyReferenceType + ", valueReferenceType=" + valueReferenceType); | |
| + } | |
| + | |
| + static <K, V> RefConcurrentMap<K, V> newCache(ReferenceType referenceType) { | |
| + return new RefConcurrentMap<>(referenceType); | |
| + } | |
| + | |
| + static <K, V> RefConcurrentMap<K, V> newCache( | |
| + ReferenceType keyReferenceType, ReferenceType valueReferenceType) { | |
| + return new RefConcurrentMap<>(keyReferenceType, valueReferenceType); | |
| + } | |
| + | |
| + static <K, V> RefConcurrentMap<K, V> newCache(ReferenceType referenceType, String name) { | |
| + return new RefConcurrentMap<>(referenceType, name); | |
| + } | |
| + | |
| + static <K, V> RefConcurrentMap<K, V> newCache( | |
| + ReferenceType keyReferenceType, ReferenceType valueReferenceType, String name) { | |
| + return new RefConcurrentMap<>(keyReferenceType, valueReferenceType, name); | |
| + } | |
| + | |
| + /** | |
| + * Sets an eviction listener to be notified of eviction events. | |
| + */ | |
| + public void setEvictionListener(EvictionListener listener) { | |
| + this.evictionListener = listener; | |
| + } | |
| + | |
| + // Base class for reference implementations | |
| + private abstract static class RefConcurrentReference<T> { | |
| + protected final int hash; | |
| + | |
| + RefConcurrentReference(T referent) { | |
| + this.hash = referent.hashCode(); | |
| + } | |
| + | |
| + public abstract Reference<T> getReference(); | |
| + | |
| + public abstract T get(); | |
| + | |
| + @Override | |
| + public boolean equals(Object obj) { | |
| + if (this == obj) { | |
| + return true; | |
| + } | |
| + if (!(obj instanceof RefConcurrentMap.RefConcurrentReference<?> other)) { | |
| + return false; | |
| + } | |
| + T thisRef = this.get(); | |
| + Object otherRef = other.get(); | |
| + // Use equals() for proper object comparison | |
| + return thisRef != null && thisRef.equals(otherRef); | |
| + } | |
| + | |
| + @Override | |
| + public int hashCode() { | |
| + return hash; | |
| + } | |
| + } | |
| + | |
| + // Soft reference implementation | |
| + private static class SoftRefConcurrentReference<T> extends RefConcurrentReference<T> { | |
| + final SoftReference<T> softRef; | |
| + | |
| + SoftRefConcurrentReference(T referent, ReferenceQueue<T> queue) { | |
| + super(referent); | |
| + this.softRef = new SoftReference<>(referent, queue); | |
| + } | |
| + | |
| + @Override | |
| + public SoftReference<T> getReference() { | |
| + return softRef; | |
| + } | |
| + | |
| + @Override | |
| + public T get() { | |
| + return softRef.get(); | |
| + } | |
| + } | |
| + | |
| + // Weak reference implementation | |
| + private static class WeakRefConcurrentReference<T> extends RefConcurrentReference<T> { | |
| + final WeakReference<T> weakRef; | |
| + | |
| + WeakRefConcurrentReference(T referent, ReferenceQueue<T> queue) { | |
| + super(referent); | |
| + this.weakRef = new WeakReference<>(referent, queue); | |
| + } | |
| + | |
| + @Override | |
| + public Reference<T> getReference() { | |
| + return weakRef; | |
| + } | |
| + | |
| + @Override | |
| + public T get() { | |
| + return weakRef.get(); | |
| + } | |
| + } | |
| + | |
| + // Hard reference implementation (strong references) | |
| + private static class HardRefConcurrentReference<T> extends RefConcurrentReference<T> { | |
| + private final T referent; | |
| + | |
| + HardRefConcurrentReference(T referent, ReferenceQueue<T> queue) { | |
| + super(referent); | |
| + this.referent = referent; | |
| + // Note: queue is ignored for hard references since they're never GC'd | |
| + } | |
| + | |
| + @Override | |
| + public Reference<T> getReference() { | |
| + // Return null since hard references don't use Reference objects | |
| + return null; | |
| + } | |
| + | |
| + @Override | |
| + public T get() { | |
| + return referent; | |
| + } | |
| + } | |
| + | |
| + // Base class for compute references | |
| + private abstract static class ComputeReference<V> { | |
| + protected final boolean computing; | |
| + | |
| + ComputeReference(boolean computing) { | |
| + this.computing = computing; | |
| + } | |
| + | |
| + public abstract V get(); | |
| + | |
| + public abstract Reference<V> getReference(); | |
| + } | |
| + | |
| + // Soft compute reference implementation | |
| + private static class SoftComputeReference<V> extends ComputeReference<V> { | |
| + final SoftReference<V> softRef; | |
| + | |
| + SoftComputeReference(V value, ReferenceQueue<V> queue) { | |
| + super(false); | |
| + this.softRef = new SoftReference<>(value, queue); | |
| + } | |
| + | |
| + private SoftComputeReference(ReferenceQueue<V> queue) { | |
| + super(true); | |
| + this.softRef = new SoftReference<>(null, queue); | |
| + } | |
| + | |
| + static <V> SoftComputeReference<V> computing(ReferenceQueue<V> queue) { | |
| + return new SoftComputeReference<>(queue); | |
| + } | |
| + | |
| + @Override | |
| + public V get() { | |
| + return softRef.get(); | |
| + } | |
| + | |
| + @Override | |
| + public Reference<V> getReference() { | |
| + return softRef; | |
| + } | |
| + } | |
| + | |
| + // Weak compute reference implementation | |
| + private static class WeakComputeReference<V> extends ComputeReference<V> { | |
| + final WeakReference<V> weakRef; | |
| + | |
| + WeakComputeReference(V value, ReferenceQueue<V> queue) { | |
| + super(false); | |
| + this.weakRef = new WeakReference<>(value, queue); | |
| + } | |
| + | |
| + private WeakComputeReference(ReferenceQueue<V> queue) { | |
| + super(true); | |
| + this.weakRef = new WeakReference<>(null, queue); | |
| + } | |
| + | |
| + static <V> WeakComputeReference<V> computing(ReferenceQueue<V> queue) { | |
| + return new WeakComputeReference<>(queue); | |
| + } | |
| + | |
| + @Override | |
| + public V get() { | |
| + return weakRef.get(); | |
| + } | |
| + | |
| + @Override | |
| + public Reference<V> getReference() { | |
| + return weakRef; | |
| + } | |
| + } | |
| + | |
| + // Hard compute reference implementation (strong references) | |
| + private static class HardComputeReference<V> extends ComputeReference<V> { | |
| + private final V value; | |
| + | |
| + HardComputeReference(V value, ReferenceQueue<V> queue) { | |
| + super(false); | |
| + this.value = value; | |
| + // Note: queue is ignored for hard references since they're never GC'd | |
| + } | |
| + | |
| + private HardComputeReference(ReferenceQueue<V> queue) { | |
| + super(true); | |
| + this.value = null; | |
| + } | |
| + | |
| + static <V> HardComputeReference<V> computing(ReferenceQueue<V> queue) { | |
| + return new HardComputeReference<>(queue); | |
| + } | |
| + | |
| + @Override | |
| + public V get() { | |
| + return value; | |
| + } | |
| + | |
| + @Override | |
| + public Reference<V> getReference() { | |
| + // Return null since hard references don't use Reference objects | |
| + return null; | |
| + } | |
| + } | |
| + | |
| + @Override | |
| + public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { | |
| + return computeIfAbsent(key, mappingFunction, null); | |
| + } | |
| + | |
| + /** | |
| + * Computes a value for the given key if it's not already present, using the specified reference type. | |
| + * <p> | |
| + * This method allows fine-grained control over the reference type used for individual entries, | |
| + * overriding the map's default reference type for this specific key-value pair. | |
| + * | |
| + * @param key the key whose associated value is to be returned or computed | |
| + * @param mappingFunction the function to compute a value | |
| + * @param referenceType the reference type to use for this entry (null uses map default) | |
| + * @return the current (existing or computed) value associated with the specified key | |
| + */ | |
| + public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction, ReferenceType referenceType) { | |
| + Objects.requireNonNull(key); | |
| + Objects.requireNonNull(mappingFunction); | |
| + | |
| + // Use the cache's configured key reference type, and the specified reference type for values (or fall back | |
| + // to default) | |
| + ReferenceType keyRefType = keyReferenceType; | |
| + ReferenceType valueRefType = referenceType != null ? referenceType : valueReferenceType; | |
| + | |
| + // Handle NONE reference type - always compute, never cache | |
| + if (keyRefType == ReferenceType.NONE || valueRefType == ReferenceType.NONE) { | |
| + return mappingFunction.apply(key); | |
| + } | |
| + | |
| + while (true) { | |
| + expungeStaleEntries(); | |
| + | |
| + RefConcurrentReference<K> keyRef = getKeyReference(key, keyRefType); | |
| + | |
| + // Try to get existing value | |
| + ComputeReference<V> valueRef = map.get(keyRef); | |
| + if (valueRef != null && !valueRef.computing) { | |
| + V value = valueRef.get(); | |
| + if (value != null) { | |
| + return value; | |
| + } | |
| + // Value was GC'd, remove it | |
| + map.remove(keyRef, valueRef); | |
| + } | |
| + | |
| + // Try to claim computation | |
| + ComputeReference<V> computingRef = getComputingReference(valueRefType); | |
| + valueRef = map.putIfAbsent(keyRef, computingRef); | |
| + | |
| + if (valueRef == null) { | |
| + // We claimed the computation | |
| + try { | |
| + V newValue = mappingFunction.apply(key); | |
| + if (newValue == null) { | |
| + map.remove(keyRef, computingRef); | |
| + return null; | |
| + } | |
| + | |
| + ComputeReference<V> newValueRef = getValueReference(newValue, valueRefType); | |
| + map.replace(keyRef, computingRef, newValueRef); | |
| + return newValue; | |
| + } catch (Throwable t) { | |
| + map.remove(keyRef, computingRef); | |
| + throw t; | |
| + } | |
| + } else if (!valueRef.computing) { | |
| + // Another thread has a value | |
| + V value = valueRef.get(); | |
| + if (value != null) { | |
| + return value; | |
| + } | |
| + // Value was GC'd | |
| + if (map.remove(keyRef, valueRef)) { | |
| + continue; | |
| + } | |
| + } | |
| + // Another thread is computing or the reference changed, try again | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Creates a computing reference using the specified reference type. | |
| + * If referenceType is null, uses the map's default reference type. | |
| + */ | |
| + private ComputeReference<V> getComputingReference(ReferenceType referenceType) { | |
| + return switch (referenceType) { | |
| + case SOFT -> SoftComputeReference.computing(valueQueue); | |
| + case WEAK -> WeakComputeReference.computing(valueQueue); | |
| + case HARD -> HardComputeReference.computing(valueQueue); | |
| + case NONE -> throw new IllegalArgumentException( | |
| + "NONE reference type should be handled before calling this method"); | |
| + }; | |
| + } | |
| + | |
| + /** | |
| + * Creates a value reference using the specified reference type. | |
| + * If referenceType is null, uses the map's value reference type. | |
| + */ | |
| + private ComputeReference<V> getValueReference(V value, ReferenceType referenceType) { | |
| + ReferenceType refType = referenceType != null ? referenceType : valueReferenceType; | |
| + return switch (refType) { | |
| + case SOFT -> new SoftComputeReference<>(value, valueQueue); | |
| + case WEAK -> new WeakComputeReference<>(value, valueQueue); | |
| + case HARD -> new HardComputeReference<>(value, valueQueue); | |
| + case NONE -> throw new IllegalArgumentException( | |
| + "NONE reference type should be handled before calling this method"); | |
| + }; | |
| + } | |
| + | |
| + /** | |
| + * Creates a key reference using the specified reference type. | |
| + * If referenceType is null, uses the map's key reference type. | |
| + */ | |
| + private RefConcurrentReference<K> getKeyReference(K key, ReferenceType referenceType) { | |
| + ReferenceType refType = referenceType != null ? referenceType : keyReferenceType; | |
| + return switch (refType) { | |
| + case SOFT -> new SoftRefConcurrentReference<>(key, keyQueue); | |
| + case WEAK -> new WeakRefConcurrentReference<>(key, keyQueue); | |
| + case HARD -> new HardRefConcurrentReference<>(key, keyQueue); | |
| + case NONE -> throw new IllegalArgumentException( | |
| + "NONE reference type should be handled before calling this method"); | |
| + }; | |
| + } | |
| + | |
| + private void expungeStaleEntries() { | |
| + // Remove entries where the key has been garbage collected | |
| + Reference<?> ref; | |
| + int keyEvictionCount = 0; | |
| + while ((ref = keyQueue.poll()) != null) { | |
| + keyEvictionCount++; | |
| + // System.err.println("DEBUG: Key reference polled from queue: " | |
| + // + ref.getClass().getSimpleName() + " (cache=" + name + ", keyRefType=" + keyReferenceType | |
| + // + ", valueRefType=" | |
| + // + valueReferenceType + ")"); | |
| + final Reference<?> finalRef = ref; | |
| + // Find and remove map entries where the key reference matches | |
| + // Hard references return null from getReference(), so they won't match | |
| + boolean removed = map.entrySet().removeIf(entry -> { | |
| + Reference<?> keyRef = entry.getKey().getReference(); | |
| + return keyRef != null && keyRef == finalRef; | |
| + }); | |
| + if (removed) { | |
| + keyEvictions.incrementAndGet(); | |
| + // Debug logging to understand what's happening | |
| + // System.err.println( | |
| + // "DEBUG: Key eviction detected - cache=" + name + ", keyReferenceType=" + keyReferenceType | |
| + // + ", valueReferenceType=" + valueReferenceType + ", finalRef=" | |
| + // + finalRef.getClass().getSimpleName() + ", mapSize=" + map.size()); | |
| + // Notify eviction listener | |
| + EvictionListener listener = evictionListener; | |
| + if (listener != null) { | |
| + listener.onKeyEviction(); | |
| + } | |
| + } | |
| + } | |
| + // Remove entries where the value has been garbage collected | |
| + int valueEvictionCount = 0; | |
| + while ((ref = valueQueue.poll()) != null) { | |
| + valueEvictionCount++; | |
| + // System.err.println("DEBUG: Value reference polled from queue: " | |
| + // + ref.getClass().getSimpleName() + " (cache=" + name + ", keyRefType=" + keyReferenceType | |
| + // + ", valueRefType=" | |
| + // + valueReferenceType + ")"); | |
| + final Reference<?> finalRef = ref; | |
| + // Find and remove map entries where the value reference matches | |
| + // Hard references return null from getReference(), so they won't match | |
| + boolean removed = map.entrySet().removeIf(entry -> { | |
| + Reference<?> valueRef = entry.getValue().getReference(); | |
| + return valueRef != null && valueRef == finalRef; | |
| + }); | |
| + if (removed) { | |
| + valueEvictions.incrementAndGet(); | |
| + // Debug logging to understand what's happening | |
| + // System.err.println( | |
| + // "DEBUG: Value eviction detected - cache=" + name + ", keyReferenceType=" + | |
| + // keyReferenceType | |
| + // + ", valueReferenceType=" + valueReferenceType + ", finalRef=" | |
| + // + finalRef.getClass().getSimpleName() + ", mapSize=" + map.size()); | |
| + // Notify eviction listener | |
| + EvictionListener listener = evictionListener; | |
| + if (listener != null) { | |
| + listener.onValueEviction(); | |
| + } | |
| + } | |
| + } | |
| + | |
| + // Debug logging for eviction activity (only if system property is set) | |
| + if ((keyEvictionCount > 0 || valueEvictionCount > 0) && Boolean.getBoolean("maven.cache.debug.evictions")) { | |
| + System.err.println("DEBUG: expungeStaleEntries() - cache=" + name + ", keyEvictions: " | |
| + + keyEvictionCount + ", valueEvictions: " + valueEvictionCount + ", mapSize: " + map.size()); | |
| + } | |
| + } | |
| + | |
| + @Override | |
| + public int size() { | |
| + expungeStaleEntries(); | |
| + return map.size(); | |
| + } | |
| + | |
| + @Override | |
| + public boolean isEmpty() { | |
| + expungeStaleEntries(); | |
| + return map.isEmpty(); | |
| + } | |
| + | |
| + @Override | |
| + public boolean containsKey(Object key) { | |
| + expungeStaleEntries(); | |
| + | |
| + // Handle NONE reference type - always compute, never cache | |
| + if (keyReferenceType == ReferenceType.NONE) { | |
| + return false; | |
| + } | |
| + | |
| + return map.containsKey(getKeyReference((K) key, null)); | |
| + } | |
| + | |
| + @Override | |
| + public boolean containsValue(Object value) { | |
| + expungeStaleEntries(); | |
| + | |
| + // Handle NONE reference type - always compute, never cache | |
| + if (valueReferenceType == ReferenceType.NONE) { | |
| + return false; | |
| + } | |
| + | |
| + for (ComputeReference<V> ref : map.values()) { | |
| + V v = ref.get(); | |
| + if (v != null && Objects.equals(v, value)) { | |
| + return true; | |
| + } | |
| + } | |
| + return false; | |
| + } | |
| + | |
| + @Override | |
| + public V get(Object key) { | |
| + expungeStaleEntries(); | |
| + | |
| + // Handle NONE reference type - always compute, never cache | |
| + if (keyReferenceType == ReferenceType.NONE) { | |
| + return null; | |
| + } | |
| + | |
| + ComputeReference<V> ref = map.get(getKeyReference((K) key, null)); | |
| + return ref != null ? ref.get() : null; | |
| + } | |
| + | |
| + @Override | |
| + public V put(K key, V value) { | |
| + Objects.requireNonNull(key); | |
| + Objects.requireNonNull(value); | |
| + expungeStaleEntries(); | |
| + | |
| + // Handle NONE reference type - always compute, never cache | |
| + if (keyReferenceType == ReferenceType.NONE || valueReferenceType == ReferenceType.NONE) { | |
| + return null; | |
| + } | |
| + | |
| + ComputeReference<V> oldValueRef = map.put(getKeyReference(key, null), getValueReference(value, null)); | |
| + | |
| + return oldValueRef != null ? oldValueRef.get() : null; | |
| + } | |
| + | |
| + @Override | |
| + public V remove(Object key) { | |
| + expungeStaleEntries(); | |
| + ComputeReference<V> valueRef = map.remove(getKeyReference((K) key, null)); | |
| + return valueRef != null ? valueRef.get() : null; | |
| + } | |
| + | |
| + public void removeIf(BiPredicate<K, V> filter) { | |
| + expungeStaleEntries(); | |
| + map.entrySet() | |
| + .removeIf(e -> filter.test(e.getKey().get(), e.getValue().get())); | |
| + } | |
| + | |
| + @Override | |
| + public void putAll(Map<? extends K, ? extends V> m) { | |
| + Objects.requireNonNull(m); | |
| + for (Entry<? extends K, ? extends V> e : m.entrySet()) { | |
| + put(e.getKey(), e.getValue()); | |
| + } | |
| + } | |
| + | |
| + @Override | |
| + public void clear() { | |
| + map.clear(); | |
| + expungeStaleEntries(); | |
| + } | |
| + | |
| + @Override | |
| + public Set<K> keySet() { | |
| + throw new UnsupportedOperationException("keySet not supported"); | |
| + } | |
| + | |
| + @Override | |
| + public Collection<V> values() { | |
| + throw new UnsupportedOperationException("values not supported"); | |
| + } | |
| + | |
| + @Override | |
| + public Set<Entry<K, V>> entrySet() { | |
| + throw new UnsupportedOperationException("entrySet not supported"); | |
| + } | |
| + | |
| + /** | |
| + * Returns the number of entries evicted due to key garbage collection. | |
| + */ | |
| + long getKeyEvictions() { | |
| + return keyEvictions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the number of entries evicted due to value garbage collection. | |
| + */ | |
| + long getValueEvictions() { | |
| + return valueEvictions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the total number of evictions (keys + values). | |
| + */ | |
| + long getTotalEvictions() { | |
| + return keyEvictions.get() + valueEvictions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the key reference type used by this cache. | |
| + */ | |
| + public ReferenceType getKeyReferenceType() { | |
| + return keyReferenceType; | |
| + } | |
| + | |
| + /** | |
| + * Returns the value reference type used by this cache. | |
| + */ | |
| + public ReferenceType getValueReferenceType() { | |
| + return valueReferenceType; | |
| + } | |
| + | |
| + /** | |
| + * Returns a string representation of the reference type combination. | |
| + */ | |
| + public String getReferenceTypeKey() { | |
| + return keyReferenceType + "/" + valueReferenceType; | |
| + } | |
| + | |
| + /** | |
| + * Returns the cache name for debugging purposes. | |
| + */ | |
| + public String getName() { | |
| + return name; | |
| + } | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheConfig.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheConfig.java | |
| new file mode 100644 | |
| index 0000000000..34ae9ac4ec | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheConfig.java | |
| @@ -0,0 +1,93 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| + | |
| +/** | |
| + * Configuration for cache behavior including scope and reference types. | |
| + * Supports separate reference types for keys and values to enable fine-grained | |
| + * control over eviction behavior and better cache miss analysis. | |
| + * | |
| + * @param scope the cache retention scope | |
| + * @param referenceType the reference type to use for cache entries (backward compatibility) | |
| + * @param keyReferenceType the reference type to use for keys (null means use referenceType) | |
| + * @param valueReferenceType the reference type to use for values (null means use referenceType) | |
| + */ | |
| +public record CacheConfig( | |
| + CacheRetention scope, | |
| + Cache.ReferenceType referenceType, | |
| + Cache.ReferenceType keyReferenceType, | |
| + Cache.ReferenceType valueReferenceType) { | |
| + | |
| + /** | |
| + * Backward compatibility constructor. | |
| + */ | |
| + public CacheConfig(CacheRetention scope, Cache.ReferenceType referenceType) { | |
| + this(scope, referenceType, null, null); | |
| + } | |
| + | |
| + /** | |
| + * Default cache configuration with REQUEST_SCOPED and SOFT reference type. | |
| + */ | |
| + public static final CacheConfig DEFAULT = new CacheConfig(CacheRetention.REQUEST_SCOPED, Cache.ReferenceType.SOFT); | |
| + | |
| + /** | |
| + * Creates a cache configuration with the specified scope and default SOFT reference type. | |
| + */ | |
| + public static CacheConfig withScope(CacheRetention scope) { | |
| + return new CacheConfig(scope, Cache.ReferenceType.SOFT); | |
| + } | |
| + | |
| + /** | |
| + * Creates a cache configuration with the specified reference type and default REQUEST_SCOPED scope. | |
| + */ | |
| + public static CacheConfig withReferenceType(Cache.ReferenceType referenceType) { | |
| + return new CacheConfig(CacheRetention.REQUEST_SCOPED, referenceType); | |
| + } | |
| + | |
| + /** | |
| + * Creates a cache configuration with separate key and value reference types. | |
| + */ | |
| + public static CacheConfig withKeyValueReferenceTypes( | |
| + CacheRetention scope, Cache.ReferenceType keyReferenceType, Cache.ReferenceType valueReferenceType) { | |
| + return new CacheConfig(scope, keyReferenceType, keyReferenceType, valueReferenceType); | |
| + } | |
| + | |
| + /** | |
| + * Returns the effective key reference type. | |
| + */ | |
| + public Cache.ReferenceType getEffectiveKeyReferenceType() { | |
| + return keyReferenceType != null ? keyReferenceType : referenceType; | |
| + } | |
| + | |
| + /** | |
| + * Returns the effective value reference type. | |
| + */ | |
| + public Cache.ReferenceType getEffectiveValueReferenceType() { | |
| + return valueReferenceType != null ? valueReferenceType : referenceType; | |
| + } | |
| + | |
| + /** | |
| + * Returns true if this configuration uses separate reference types for keys and values. | |
| + */ | |
| + public boolean hasSeparateKeyValueReferenceTypes() { | |
| + return keyReferenceType != null || valueReferenceType != null; | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheConfigurationResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheConfigurationResolver.java | |
| new file mode 100644 | |
| index 0000000000..d0b162ba0f | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheConfigurationResolver.java | |
| @@ -0,0 +1,185 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.List; | |
| +import java.util.concurrent.ConcurrentHashMap; | |
| +import java.util.concurrent.ConcurrentMap; | |
| + | |
| +import org.apache.maven.api.Constants; | |
| +import org.apache.maven.api.Session; | |
| +import org.apache.maven.api.cache.CacheMetadata; | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| +import org.apache.maven.api.services.Request; | |
| +import org.slf4j.Logger; | |
| +import org.slf4j.LoggerFactory; | |
| + | |
| +/** | |
| + * Resolves cache configuration for requests based on user-defined selectors. | |
| + */ | |
| +public class CacheConfigurationResolver { | |
| + private static final Logger LOGGER = LoggerFactory.getLogger(CacheConfigurationResolver.class); | |
| + | |
| + /** | |
| + * Cache for parsed selectors per session to avoid re-parsing. | |
| + */ | |
| + private static final ConcurrentMap<String, List<CacheSelector>> SELECTOR_CACHE = new ConcurrentHashMap<>(); | |
| + | |
| + /** | |
| + * Resolves cache configuration for the given request and session. | |
| + * | |
| + * @param req the request to resolve configuration for | |
| + * @param session the session containing user properties | |
| + * @return the resolved cache configuration | |
| + */ | |
| + public static CacheConfig resolveConfig(Request<?> req, Session session) { | |
| + // First check if request implements CacheMetadata for backward compatibility | |
| + CacheRetention legacyRetention = null; | |
| + if (req instanceof CacheMetadata metadata) { | |
| + legacyRetention = metadata.getCacheRetention(); | |
| + } | |
| + | |
| + // Check for key reference type configuration | |
| + Cache.ReferenceType keyRefType = null; | |
| + String keyRefsString = session.getUserProperties().get(Constants.MAVEN_CACHE_KEY_REFS); | |
| + if (keyRefsString != null && !keyRefsString.trim().isEmpty()) { | |
| + try { | |
| + keyRefType = Cache.ReferenceType.valueOf(keyRefsString.trim().toUpperCase()); | |
| + } catch (IllegalArgumentException e) { | |
| + LOGGER.warn("Invalid key reference types '{}', using defaults", keyRefsString); | |
| + } | |
| + } | |
| + | |
| + // Check for value reference type configuration | |
| + Cache.ReferenceType valueRefType = null; | |
| + String valueRefsString = session.getUserProperties().get(Constants.MAVEN_CACHE_VALUE_REFS); | |
| + if (valueRefsString != null && !valueRefsString.trim().isEmpty()) { | |
| + try { | |
| + valueRefType = | |
| + Cache.ReferenceType.valueOf(valueRefsString.trim().toUpperCase()); | |
| + } catch (IllegalArgumentException e) { | |
| + LOGGER.warn("Invalid value reference types '{}', using defaults", valueRefsString); | |
| + } | |
| + } | |
| + | |
| + // Get user-defined configuration | |
| + String configString = session.getUserProperties().get(Constants.MAVEN_CACHE_CONFIG_PROPERTY); | |
| + if (configString == null || configString.trim().isEmpty()) { | |
| + // No user configuration, use legacy behavior or defaults | |
| + if (legacyRetention != null) { | |
| + CacheConfig config = new CacheConfig( | |
| + legacyRetention, getDefaultReferenceType(legacyRetention), keyRefType, valueRefType); | |
| + return config; | |
| + } | |
| + if (keyRefType != null && valueRefType != null) { | |
| + return new CacheConfig( | |
| + CacheConfig.DEFAULT.scope(), CacheConfig.DEFAULT.referenceType(), keyRefType, valueRefType); | |
| + } | |
| + return CacheConfig.DEFAULT; | |
| + } | |
| + | |
| + // Parse and cache selectors | |
| + List<CacheSelector> selectors = SELECTOR_CACHE.computeIfAbsent(configString, CacheSelectorParser::parse); | |
| + | |
| + // Find all matching selectors and merge them (most specific first) | |
| + PartialCacheConfig mergedConfig = null; | |
| + for (CacheSelector selector : selectors) { | |
| + if (selector.matches(req)) { | |
| + if (mergedConfig == null) { | |
| + mergedConfig = selector.config(); | |
| + LOGGER.debug( | |
| + "Cache config for {}: matched selector '{}' with config {}", | |
| + req.getClass().getSimpleName(), | |
| + selector, | |
| + selector.config()); | |
| + } else { | |
| + PartialCacheConfig previousConfig = mergedConfig; | |
| + mergedConfig = mergedConfig.mergeWith(selector.config()); | |
| + LOGGER.debug( | |
| + "Cache config for {}: merged selector '{}' with previous config {} -> {}", | |
| + req.getClass().getSimpleName(), | |
| + selector, | |
| + previousConfig, | |
| + mergedConfig); | |
| + } | |
| + | |
| + // If we have a complete configuration, we can stop | |
| + if (mergedConfig.isComplete()) { | |
| + break; | |
| + } | |
| + } | |
| + } | |
| + | |
| + // Convert merged partial config to complete config | |
| + if (mergedConfig != null && !mergedConfig.isEmpty()) { | |
| + CacheConfig finalConfig = mergedConfig.toComplete(); | |
| + // Apply key/value reference types if specified | |
| + if (keyRefType != null && valueRefType != null) { | |
| + finalConfig = | |
| + new CacheConfig(finalConfig.scope(), finalConfig.referenceType(), keyRefType, valueRefType); | |
| + } | |
| + LOGGER.debug("Final cache config for {}: {}", req.getClass().getSimpleName(), finalConfig); | |
| + return finalConfig; | |
| + } | |
| + | |
| + // No selector matched, use legacy behavior or defaults | |
| + if (legacyRetention != null) { | |
| + CacheConfig config = new CacheConfig( | |
| + legacyRetention, getDefaultReferenceType(legacyRetention), keyRefType, valueRefType); | |
| + LOGGER.debug( | |
| + "Cache config for {}: {} (legacy CacheMetadata)", | |
| + req.getClass().getSimpleName(), | |
| + config); | |
| + return config; | |
| + } | |
| + | |
| + if (keyRefType != null && valueRefType != null) { | |
| + CacheConfig config = new CacheConfig( | |
| + CacheConfig.DEFAULT.scope(), CacheConfig.DEFAULT.referenceType(), keyRefType, valueRefType); | |
| + LOGGER.debug( | |
| + "Cache config for {}: {} (with key/value refs)", | |
| + req.getClass().getSimpleName(), | |
| + config); | |
| + return config; | |
| + } | |
| + | |
| + LOGGER.debug("Cache config for {}: {} (default)", req.getClass().getSimpleName(), CacheConfig.DEFAULT); | |
| + return CacheConfig.DEFAULT; | |
| + } | |
| + | |
| + /** | |
| + * Gets the default reference type for a given cache retention. | |
| + * This maintains backward compatibility with the original hardcoded behavior. | |
| + */ | |
| + private static Cache.ReferenceType getDefaultReferenceType(CacheRetention retention) { | |
| + return switch (retention) { | |
| + case SESSION_SCOPED -> Cache.ReferenceType.SOFT; | |
| + case REQUEST_SCOPED -> Cache.ReferenceType.SOFT; // Changed from HARD to SOFT for consistency | |
| + case PERSISTENT -> Cache.ReferenceType.HARD; | |
| + case DISABLED -> Cache.ReferenceType.NONE; | |
| + }; | |
| + } | |
| + | |
| + /** | |
| + * Clears the selector cache. Useful for testing. | |
| + */ | |
| + public static void clearCache() { | |
| + SELECTOR_CACHE.clear(); | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheSelector.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheSelector.java | |
| new file mode 100644 | |
| index 0000000000..ecfbb16601 | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheSelector.java | |
| @@ -0,0 +1,160 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.Objects; | |
| + | |
| +import org.apache.maven.api.services.Request; | |
| +import org.apache.maven.api.services.RequestTrace; | |
| + | |
| +/** | |
| + * A cache selector that matches requests based on their type and optional parent request type. | |
| + * | |
| + * Supports CSS-like selectors: | |
| + * - "RequestType" matches any request of that type | |
| + * - "ParentType RequestType" matches RequestType with ParentType as parent | |
| + * - "ParentType *" matches any request with ParentType as parent | |
| + * - "* RequestType" matches RequestType with any parent (equivalent to just "RequestType") | |
| + * | |
| + * @param parentRequestType | |
| + * @param requestType | |
| + * @param config | |
| + */ | |
| +public record CacheSelector(String parentRequestType, String requestType, PartialCacheConfig config) { | |
| + | |
| + public CacheSelector { | |
| + Objects.requireNonNull(requestType, "requestType cannot be null"); | |
| + Objects.requireNonNull(config, "config cannot be null"); | |
| + } | |
| + | |
| + /** | |
| + * Creates a selector that matches any request of the specified type. | |
| + */ | |
| + public static CacheSelector forRequestType(String requestType, PartialCacheConfig config) { | |
| + return new CacheSelector(null, requestType, config); | |
| + } | |
| + | |
| + /** | |
| + * Creates a selector that matches requests with a specific parent type. | |
| + */ | |
| + public static CacheSelector forParentAndRequestType( | |
| + String parentRequestType, String requestType, PartialCacheConfig config) { | |
| + return new CacheSelector(parentRequestType, requestType, config); | |
| + } | |
| + | |
| + /** | |
| + * Checks if this selector matches the given request. | |
| + * | |
| + * @param req the request to match | |
| + * @return true if this selector matches the request | |
| + */ | |
| + public boolean matches(Request<?> req) { | |
| + // Check if request type matches any of the implemented interfaces | |
| + if (!"*".equals(requestType) && !matchesAnyInterface(req.getClass(), requestType)) { | |
| + return false; | |
| + } | |
| + | |
| + // If no parent type specified, it matches | |
| + if (parentRequestType == null) { | |
| + return true; | |
| + } | |
| + | |
| + // Check parent request type | |
| + if (!matchesParentRequestType(req, parentRequestType)) { | |
| + return false; | |
| + } | |
| + | |
| + return true; | |
| + } | |
| + | |
| + /** | |
| + * Checks if a class or any of its implemented interfaces matches the given type name. | |
| + * | |
| + * @param clazz the class to check | |
| + * @param typeName the type name to match against | |
| + * @return true if the class or any of its interfaces matches the type name | |
| + */ | |
| + private boolean matchesAnyInterface(Class<?> clazz, String typeName) { | |
| + // Check the class itself first | |
| + if (typeName.equals(getShortClassName(clazz))) { | |
| + return true; | |
| + } | |
| + | |
| + // Check all implemented interfaces | |
| + for (Class<?> iface : clazz.getInterfaces()) { | |
| + if (typeName.equals(getShortClassName(iface))) { | |
| + return true; | |
| + } | |
| + // Recursively check parent interfaces | |
| + if (matchesAnyInterface(iface, typeName)) { | |
| + return true; | |
| + } | |
| + } | |
| + | |
| + // Check superclass if it exists | |
| + Class<?> superClass = clazz.getSuperclass(); | |
| + if (superClass != null && superClass != Object.class) { | |
| + return matchesAnyInterface(superClass, typeName); | |
| + } | |
| + | |
| + return false; | |
| + } | |
| + | |
| + /** | |
| + * Checks if the parent request type matches the given selector pattern. | |
| + * | |
| + * @param req the request to check | |
| + * @param parentRequestType the parent request type pattern to match | |
| + * @return true if the parent matches the pattern | |
| + */ | |
| + private boolean matchesParentRequestType(Request<?> req, String parentRequestType) { | |
| + if ("*".equals(parentRequestType)) { | |
| + return true; | |
| + } | |
| + | |
| + RequestTrace trace = req.getTrace(); | |
| + if (trace == null || trace.parent() == null) { | |
| + return false; | |
| + } | |
| + | |
| + Object parentData = trace.parent().data(); | |
| + if (!(parentData instanceof Request<?> parentReq)) { | |
| + return false; | |
| + } | |
| + | |
| + // Check if parent request matches any interface with the given name | |
| + return matchesAnyInterface(parentReq.getClass(), parentRequestType); | |
| + } | |
| + | |
| + /** | |
| + * Gets the short class name (without package) of a class. | |
| + */ | |
| + private String getShortClassName(Class<?> clazz) { | |
| + String name = clazz.getSimpleName(); | |
| + return name.isEmpty() ? clazz.getName() : name; | |
| + } | |
| + | |
| + @Override | |
| + public String toString() { | |
| + if (parentRequestType == null) { | |
| + return requestType; | |
| + } | |
| + return parentRequestType + " " + requestType; | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheSelectorParser.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheSelectorParser.java | |
| new file mode 100644 | |
| index 0000000000..143a8b694e | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheSelectorParser.java | |
| @@ -0,0 +1,193 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.ArrayList; | |
| +import java.util.List; | |
| +import java.util.Locale; | |
| +import java.util.regex.Matcher; | |
| +import java.util.regex.Pattern; | |
| + | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| +import org.slf4j.Logger; | |
| +import org.slf4j.LoggerFactory; | |
| + | |
| +/** | |
| + * Parser for cache selector configuration strings. | |
| + * | |
| + * Supports syntax like: | |
| + * <pre> | |
| + * ArtifactResolutionRequest { scope: session, ref: soft } | |
| + * ModelBuildRequest { scope: request, ref: soft } | |
| + * ModelBuilderRequest VersionRangeRequest { ref: hard } | |
| + * ModelBuildRequest * { ref: hard } | |
| + * VersionRangeRequest { scope: session } | |
| + * * { ref: weak } | |
| + * </pre> | |
| + */ | |
| +public class CacheSelectorParser { | |
| + private static final Logger LOGGER = LoggerFactory.getLogger(CacheSelectorParser.class); | |
| + | |
| + // Pattern to match selector rules: "[ParentType] RequestType { properties }" | |
| + private static final Pattern RULE_PATTERN = | |
| + Pattern.compile("([\\w*]+)(?:\\s+([\\w*]+))?\\s*\\{([^}]+)\\}", Pattern.MULTILINE); | |
| + | |
| + // Pattern to match properties within braces: "key: value" | |
| + private static final Pattern PROPERTY_PATTERN = Pattern.compile("(\\w+)\\s*:\\s*([\\w]+)"); | |
| + | |
| + /** | |
| + * Parses a cache configuration string into a list of cache selectors. | |
| + * | |
| + * @param configString the configuration string to parse | |
| + * @return list of parsed cache selectors, ordered by specificity (most specific first) | |
| + */ | |
| + public static List<CacheSelector> parse(String configString) { | |
| + List<CacheSelector> selectors = new ArrayList<>(); | |
| + | |
| + if (configString == null || configString.trim().isEmpty()) { | |
| + return selectors; | |
| + } | |
| + | |
| + Matcher ruleMatcher = RULE_PATTERN.matcher(configString); | |
| + while (ruleMatcher.find()) { | |
| + CacheSelector selector = parseRule(ruleMatcher); | |
| + selectors.add(selector); | |
| + } | |
| + | |
| + // Sort by specificity (most specific first) | |
| + selectors.sort((a, b) -> compareSpecificity(b, a)); | |
| + | |
| + return selectors; | |
| + } | |
| + | |
| + /** | |
| + * Parses a single rule from a regex matcher. | |
| + */ | |
| + private static CacheSelector parseRule(Matcher ruleMatcher) { | |
| + String firstType = ruleMatcher.group(1); | |
| + String secondType = ruleMatcher.group(2); | |
| + String properties = ruleMatcher.group(3); | |
| + | |
| + // Determine parent and request types | |
| + String parentType = null; | |
| + String requestType = firstType; | |
| + | |
| + if (secondType != null) { | |
| + parentType = firstType; | |
| + requestType = secondType; | |
| + } | |
| + | |
| + // Parse properties | |
| + PartialCacheConfig config = parseProperties(properties); | |
| + return new CacheSelector(parentType, requestType, config); | |
| + } | |
| + | |
| + /** | |
| + * Parses properties string into a PartialCacheConfig. | |
| + */ | |
| + private static PartialCacheConfig parseProperties(String properties) { | |
| + CacheRetention scope = null; | |
| + Cache.ReferenceType referenceType = null; | |
| + | |
| + Matcher propMatcher = PROPERTY_PATTERN.matcher(properties); | |
| + while (propMatcher.find()) { | |
| + String key = propMatcher.group(1); | |
| + String value = propMatcher.group(2); | |
| + | |
| + switch (key.toLowerCase(Locale.ENGLISH)) { | |
| + case "scope": | |
| + scope = parseScope(value); | |
| + break; | |
| + case "ref": | |
| + case "reference": | |
| + referenceType = parseReferenceType(value); | |
| + break; | |
| + default: | |
| + LOGGER.warn("Unknown cache configuration property: {}", key); | |
| + } | |
| + } | |
| + | |
| + // Return partial configuration (null values are allowed) | |
| + return new PartialCacheConfig(scope, referenceType); | |
| + } | |
| + | |
| + /** | |
| + * Parses a scope string into CacheRetention. | |
| + */ | |
| + private static CacheRetention parseScope(String value) { | |
| + return switch (value.toLowerCase(Locale.ENGLISH)) { | |
| + case "session" -> CacheRetention.SESSION_SCOPED; | |
| + case "request" -> CacheRetention.REQUEST_SCOPED; | |
| + case "persistent" -> CacheRetention.PERSISTENT; | |
| + case "disabled", "none" -> CacheRetention.DISABLED; | |
| + default -> { | |
| + LOGGER.warn("Unknown cache scope: {}, using default REQUEST_SCOPED", value); | |
| + yield CacheRetention.REQUEST_SCOPED; | |
| + } | |
| + }; | |
| + } | |
| + | |
| + /** | |
| + * Parses a reference type string into Cache.ReferenceType. | |
| + */ | |
| + private static Cache.ReferenceType parseReferenceType(String value) { | |
| + return switch (value.toLowerCase(Locale.ENGLISH)) { | |
| + case "soft" -> Cache.ReferenceType.SOFT; | |
| + case "hard" -> Cache.ReferenceType.HARD; | |
| + case "weak" -> Cache.ReferenceType.WEAK; | |
| + case "none" -> Cache.ReferenceType.NONE; | |
| + default -> { | |
| + LOGGER.warn("Unknown reference type: {}, using default SOFT", value); | |
| + yield Cache.ReferenceType.SOFT; | |
| + } | |
| + }; | |
| + } | |
| + | |
| + /** | |
| + * Compares specificity of two selectors. More specific selectors should be checked first. | |
| + * Specificity order: parent + request > request only > wildcard | |
| + */ | |
| + private static int compareSpecificity(CacheSelector a, CacheSelector b) { | |
| + int aScore = getSpecificityScore(a); | |
| + int bScore = getSpecificityScore(b); | |
| + return Integer.compare(aScore, bScore); | |
| + } | |
| + | |
| + private static int getSpecificityScore(CacheSelector selector) { | |
| + int score = 0; | |
| + | |
| + // Parent type specificity | |
| + if (selector.parentRequestType() != null) { | |
| + if (!"*".equals(selector.parentRequestType())) { | |
| + score += 100; // Specific parent type | |
| + } else { | |
| + score += 50; // Wildcard parent type | |
| + } | |
| + } | |
| + | |
| + // Request type specificity | |
| + if (!"*".equals(selector.requestType())) { | |
| + score += 10; // Specific request type | |
| + } else { | |
| + score += 1; // Wildcard request type | |
| + } | |
| + | |
| + return score; | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheStatistics.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheStatistics.java | |
| new file mode 100644 | |
| index 0000000000..d78536be9c | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/CacheStatistics.java | |
| @@ -0,0 +1,416 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.Map; | |
| +import java.util.concurrent.ConcurrentHashMap; | |
| +import java.util.concurrent.atomic.AtomicLong; | |
| +import java.util.function.Supplier; | |
| + | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| + | |
| +/** | |
| + * Cache statistics that tracks detailed metrics | |
| + * about cache performance and usage patterns. | |
| + * <p> | |
| + * This implementation integrates with the improved cache architecture and | |
| + * provides thread-safe statistics tracking with minimal performance overhead. | |
| + * </p> | |
| + */ | |
| +public class CacheStatistics { | |
| + | |
| + private final AtomicLong totalRequests = new AtomicLong(); | |
| + private final AtomicLong cacheHits = new AtomicLong(); | |
| + private final AtomicLong cacheMisses = new AtomicLong(); | |
| + private final AtomicLong cachedExceptions = new AtomicLong(); | |
| + | |
| + // Enhanced eviction tracking | |
| + private final AtomicLong keyEvictions = new AtomicLong(); | |
| + private final AtomicLong valueEvictions = new AtomicLong(); | |
| + private final AtomicLong totalEvictions = new AtomicLong(); | |
| + | |
| + private final Map<String, RequestTypeStatistics> requestTypeStats = new ConcurrentHashMap<>(); | |
| + private final Map<CacheRetention, RetentionStatistics> retentionStats = new ConcurrentHashMap<>(); | |
| + private final Map<CacheRetention, Supplier<Long>> cacheSizeSuppliers = new ConcurrentHashMap<>(); | |
| + | |
| + // Reference type statistics | |
| + private final Map<String, ReferenceTypeStatistics> referenceTypeStats = new ConcurrentHashMap<>(); | |
| + | |
| + public long getTotalRequests() { | |
| + return totalRequests.get(); | |
| + } | |
| + | |
| + public long getCacheHits() { | |
| + return cacheHits.get(); | |
| + } | |
| + | |
| + public long getCacheMisses() { | |
| + return cacheMisses.get(); | |
| + } | |
| + | |
| + public double getHitRatio() { | |
| + long total = getTotalRequests(); | |
| + return total == 0 ? 0.0 : (getCacheHits() * 100.0) / total; | |
| + } | |
| + | |
| + public double getMissRatio() { | |
| + long total = getTotalRequests(); | |
| + return total == 0 ? 0.0 : (getCacheMisses() * 100.0) / total; | |
| + } | |
| + | |
| + public Map<String, RequestTypeStatistics> getRequestTypeStatistics() { | |
| + return Map.copyOf(requestTypeStats); | |
| + } | |
| + | |
| + public Map<CacheRetention, RetentionStatistics> getRetentionStatistics() { | |
| + return Map.copyOf(retentionStats); | |
| + } | |
| + | |
| + public Map<String, ReferenceTypeStatistics> getReferenceTypeStatistics() { | |
| + return Map.copyOf(referenceTypeStats); | |
| + } | |
| + | |
| + public Map<CacheRetention, Long> getCacheSizes() { | |
| + Map<CacheRetention, Long> sizes = new ConcurrentHashMap<>(); | |
| + cacheSizeSuppliers.forEach((retention, supplier) -> sizes.put(retention, supplier.get())); | |
| + return sizes; | |
| + } | |
| + | |
| + public long getCachedExceptions() { | |
| + return cachedExceptions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the total number of key evictions across all caches. | |
| + */ | |
| + public long getKeyEvictions() { | |
| + return keyEvictions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the total number of value evictions across all caches. | |
| + */ | |
| + public long getValueEvictions() { | |
| + return valueEvictions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the total number of evictions (keys + values). | |
| + */ | |
| + public long getTotalEvictions() { | |
| + return totalEvictions.get(); | |
| + } | |
| + | |
| + /** | |
| + * Returns the ratio of key evictions to total evictions. | |
| + */ | |
| + public double getKeyEvictionRatio() { | |
| + long total = getTotalEvictions(); | |
| + return total == 0 ? 0.0 : (getKeyEvictions() * 100.0) / total; | |
| + } | |
| + | |
| + /** | |
| + * Returns the ratio of value evictions to total evictions. | |
| + */ | |
| + public double getValueEvictionRatio() { | |
| + long total = getTotalEvictions(); | |
| + return total == 0 ? 0.0 : (getValueEvictions() * 100.0) / total; | |
| + } | |
| + | |
| + /** | |
| + * Records a cache hit for the given request type and retention policy. | |
| + */ | |
| + public void recordHit(String requestType, CacheRetention retention) { | |
| + totalRequests.incrementAndGet(); | |
| + cacheHits.incrementAndGet(); | |
| + | |
| + requestTypeStats | |
| + .computeIfAbsent(requestType, RequestTypeStatistics::new) | |
| + .recordHit(); | |
| + retentionStats.computeIfAbsent(retention, RetentionStatistics::new).recordHit(); | |
| + } | |
| + | |
| + /** | |
| + * Records a cache miss for the given request type and retention policy. | |
| + */ | |
| + public void recordMiss(String requestType, CacheRetention retention) { | |
| + totalRequests.incrementAndGet(); | |
| + cacheMisses.incrementAndGet(); | |
| + | |
| + requestTypeStats | |
| + .computeIfAbsent(requestType, RequestTypeStatistics::new) | |
| + .recordMiss(); | |
| + retentionStats.computeIfAbsent(retention, RetentionStatistics::new).recordMiss(); | |
| + } | |
| + | |
| + /** | |
| + * Records a cached exception. | |
| + */ | |
| + public void recordCachedException() { | |
| + cachedExceptions.incrementAndGet(); | |
| + } | |
| + | |
| + /** | |
| + * Records a key eviction for the specified retention policy. | |
| + */ | |
| + public void recordKeyEviction(CacheRetention retention) { | |
| + keyEvictions.incrementAndGet(); | |
| + totalEvictions.incrementAndGet(); | |
| + retentionStats.computeIfAbsent(retention, RetentionStatistics::new).recordKeyEviction(); | |
| + } | |
| + | |
| + /** | |
| + * Records a value eviction for the specified retention policy. | |
| + */ | |
| + public void recordValueEviction(CacheRetention retention) { | |
| + valueEvictions.incrementAndGet(); | |
| + totalEvictions.incrementAndGet(); | |
| + retentionStats.computeIfAbsent(retention, RetentionStatistics::new).recordValueEviction(); | |
| + } | |
| + | |
| + /** | |
| + * Registers a cache size supplier for the given retention policy. | |
| + */ | |
| + public void registerCacheSizeSupplier(CacheRetention retention, Supplier<Long> sizeSupplier) { | |
| + cacheSizeSuppliers.put(retention, sizeSupplier); | |
| + retentionStats.computeIfAbsent(retention, RetentionStatistics::new).setSizeSupplier(sizeSupplier); | |
| + } | |
| + | |
| + /** | |
| + * Returns eviction statistics by retention policy. | |
| + */ | |
| + public Map<CacheRetention, Long> getKeyEvictionsByRetention() { | |
| + Map<CacheRetention, Long> evictions = new ConcurrentHashMap<>(); | |
| + retentionStats.forEach((retention, stats) -> evictions.put(retention, stats.getKeyEvictions())); | |
| + return evictions; | |
| + } | |
| + | |
| + /** | |
| + * Returns value eviction statistics by retention policy. | |
| + */ | |
| + public Map<CacheRetention, Long> getValueEvictionsByRetention() { | |
| + Map<CacheRetention, Long> evictions = new ConcurrentHashMap<>(); | |
| + retentionStats.forEach((retention, stats) -> evictions.put(retention, stats.getValueEvictions())); | |
| + return evictions; | |
| + } | |
| + | |
| + /** | |
| + * Records cache creation with specific reference types. | |
| + */ | |
| + public void recordCacheCreation(String keyRefType, String valueRefType, CacheRetention retention) { | |
| + String refTypeKey = keyRefType + "/" + valueRefType; | |
| + referenceTypeStats | |
| + .computeIfAbsent(refTypeKey, ReferenceTypeStatistics::new) | |
| + .recordCacheCreation(retention); | |
| + } | |
| + | |
| + /** | |
| + * Records cache access for specific reference types. | |
| + */ | |
| + public void recordCacheAccess(String keyRefType, String valueRefType, boolean hit) { | |
| + String refTypeKey = keyRefType + "/" + valueRefType; | |
| + ReferenceTypeStatistics stats = referenceTypeStats.computeIfAbsent(refTypeKey, ReferenceTypeStatistics::new); | |
| + if (hit) { | |
| + stats.recordHit(); | |
| + } else { | |
| + stats.recordMiss(); | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Default implementation of request type statistics. | |
| + */ | |
| + public static class RequestTypeStatistics { | |
| + private final String requestType; | |
| + private final AtomicLong hits = new AtomicLong(); | |
| + private final AtomicLong misses = new AtomicLong(); | |
| + | |
| + RequestTypeStatistics(String requestType) { | |
| + this.requestType = requestType; | |
| + } | |
| + | |
| + public String getRequestType() { | |
| + return requestType; | |
| + } | |
| + | |
| + public long getHits() { | |
| + return hits.get(); | |
| + } | |
| + | |
| + public long getMisses() { | |
| + return misses.get(); | |
| + } | |
| + | |
| + public long getTotal() { | |
| + return getHits() + getMisses(); | |
| + } | |
| + | |
| + public double getHitRatio() { | |
| + long total = getTotal(); | |
| + return total == 0 ? 0.0 : (getHits() * 100.0) / total; | |
| + } | |
| + | |
| + void recordHit() { | |
| + hits.incrementAndGet(); | |
| + } | |
| + | |
| + void recordMiss() { | |
| + misses.incrementAndGet(); | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Default implementation of retention statistics. | |
| + */ | |
| + public static class RetentionStatistics { | |
| + private final CacheRetention retention; | |
| + private final AtomicLong hits = new AtomicLong(); | |
| + private final AtomicLong misses = new AtomicLong(); | |
| + private final AtomicLong keyEvictions = new AtomicLong(); | |
| + private final AtomicLong valueEvictions = new AtomicLong(); | |
| + private volatile Supplier<Long> sizeSupplier = () -> 0L; | |
| + | |
| + RetentionStatistics(CacheRetention retention) { | |
| + this.retention = retention; | |
| + } | |
| + | |
| + public CacheRetention getRetention() { | |
| + return retention; | |
| + } | |
| + | |
| + public long getHits() { | |
| + return hits.get(); | |
| + } | |
| + | |
| + public long getMisses() { | |
| + return misses.get(); | |
| + } | |
| + | |
| + public long getTotal() { | |
| + return getHits() + getMisses(); | |
| + } | |
| + | |
| + public double getHitRatio() { | |
| + long total = getTotal(); | |
| + return total == 0 ? 0.0 : (getHits() * 100.0) / total; | |
| + } | |
| + | |
| + public long getCurrentSize() { | |
| + return sizeSupplier.get(); | |
| + } | |
| + | |
| + public long getKeyEvictions() { | |
| + return keyEvictions.get(); | |
| + } | |
| + | |
| + public long getValueEvictions() { | |
| + return valueEvictions.get(); | |
| + } | |
| + | |
| + public long getTotalEvictions() { | |
| + return getKeyEvictions() + getValueEvictions(); | |
| + } | |
| + | |
| + public double getKeyEvictionRatio() { | |
| + long total = getTotalEvictions(); | |
| + return total == 0 ? 0.0 : (getKeyEvictions() * 100.0) / total; | |
| + } | |
| + | |
| + void recordHit() { | |
| + hits.incrementAndGet(); | |
| + } | |
| + | |
| + void recordMiss() { | |
| + misses.incrementAndGet(); | |
| + } | |
| + | |
| + void recordKeyEviction() { | |
| + keyEvictions.incrementAndGet(); | |
| + } | |
| + | |
| + void recordValueEviction() { | |
| + valueEvictions.incrementAndGet(); | |
| + } | |
| + | |
| + void setSizeSupplier(Supplier<Long> sizeSupplier) { | |
| + this.sizeSupplier = sizeSupplier; | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Statistics for specific reference type combinations. | |
| + */ | |
| + public static class ReferenceTypeStatistics { | |
| + private final String referenceTypeKey; | |
| + private final AtomicLong hits = new AtomicLong(); | |
| + private final AtomicLong misses = new AtomicLong(); | |
| + private final AtomicLong cacheCreations = new AtomicLong(); | |
| + private final Map<CacheRetention, AtomicLong> creationsByRetention = new ConcurrentHashMap<>(); | |
| + | |
| + ReferenceTypeStatistics(String referenceTypeKey) { | |
| + this.referenceTypeKey = referenceTypeKey; | |
| + } | |
| + | |
| + public String getReferenceTypeKey() { | |
| + return referenceTypeKey; | |
| + } | |
| + | |
| + public long getHits() { | |
| + return hits.get(); | |
| + } | |
| + | |
| + public long getMisses() { | |
| + return misses.get(); | |
| + } | |
| + | |
| + public long getTotal() { | |
| + return getHits() + getMisses(); | |
| + } | |
| + | |
| + public double getHitRatio() { | |
| + long total = getTotal(); | |
| + return total == 0 ? 0.0 : (getHits() * 100.0) / total; | |
| + } | |
| + | |
| + public long getCacheCreations() { | |
| + return cacheCreations.get(); | |
| + } | |
| + | |
| + public Map<CacheRetention, Long> getCreationsByRetention() { | |
| + Map<CacheRetention, Long> result = new ConcurrentHashMap<>(); | |
| + creationsByRetention.forEach((retention, count) -> result.put(retention, count.get())); | |
| + return result; | |
| + } | |
| + | |
| + void recordHit() { | |
| + hits.incrementAndGet(); | |
| + } | |
| + | |
| + void recordMiss() { | |
| + misses.incrementAndGet(); | |
| + } | |
| + | |
| + void recordCacheCreation(CacheRetention retention) { | |
| + cacheCreations.incrementAndGet(); | |
| + creationsByRetention | |
| + .computeIfAbsent(retention, k -> new AtomicLong()) | |
| + .incrementAndGet(); | |
| + } | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCache.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCache.java | |
| index e525aa013a..c42bef5a63 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCache.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCache.java | |
| @@ -18,12 +18,12 @@ | |
| */ | |
| package org.apache.maven.impl.cache; | |
| -import java.util.Map; | |
| -import java.util.Objects; | |
| -import java.util.concurrent.ConcurrentHashMap; | |
| -import java.util.concurrent.ConcurrentMap; | |
| +import java.util.ArrayList; | |
| +import java.util.List; | |
| +import java.util.Locale; | |
| import java.util.function.Function; | |
| +import org.apache.maven.api.Constants; | |
| import org.apache.maven.api.Session; | |
| import org.apache.maven.api.SessionData; | |
| import org.apache.maven.api.cache.CacheMetadata; | |
| @@ -31,45 +31,453 @@ | |
| import org.apache.maven.api.services.Request; | |
| import org.apache.maven.api.services.RequestTrace; | |
| import org.apache.maven.api.services.Result; | |
| +import org.apache.maven.impl.InternalSession; | |
| +import org.slf4j.Logger; | |
| +import org.slf4j.LoggerFactory; | |
| public class DefaultRequestCache extends AbstractRequestCache { | |
| - protected static final SessionData.Key<ConcurrentMap> KEY = | |
| - SessionData.key(ConcurrentMap.class, CacheMetadata.class); | |
| + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRequestCache.class); | |
| + | |
| + protected static final SessionData.Key<Cache> KEY = SessionData.key(Cache.class, CacheMetadata.class); | |
| protected static final Object ROOT = new Object(); | |
| - protected final Map<Object, CachingSupplier<?, ?>> forever = new ConcurrentHashMap<>(); | |
| + // Comprehensive cache statistics | |
| + private final CacheStatistics statistics = new CacheStatistics(); | |
| + | |
| + private static volatile boolean shutdownHookRegistered = false; | |
| + private static final List<CacheStatistics> ALL_STATISTICS = new ArrayList<CacheStatistics>(); | |
| + | |
| + // Synchronized method to ensure shutdown hook is registered only once | |
| + private static synchronized void ensureShutdownHookRegistered() { | |
| + if (!shutdownHookRegistered) { | |
| + Runtime.getRuntime() | |
| + .addShutdownHook(new Thread( | |
| + () -> { | |
| + // Check if cache stats should be displayed | |
| + for (CacheStatistics statistics : ALL_STATISTICS) { | |
| + if (statistics.getTotalRequests() > 0) { | |
| + System.err.println("[INFO] " + formatCacheStatistics(statistics)); | |
| + } | |
| + } | |
| + }, | |
| + "DefaultRequestCache-Statistics")); | |
| + shutdownHookRegistered = true; | |
| + } | |
| + } | |
| + | |
| + public DefaultRequestCache() { | |
| + // Register cache size suppliers for different retention policies | |
| + // Note: These provide approximate sizes since the improved cache architecture | |
| + // uses distributed caches across sessions | |
| + statistics.registerCacheSizeSupplier(CacheRetention.PERSISTENT, () -> 0L); | |
| + statistics.registerCacheSizeSupplier(CacheRetention.SESSION_SCOPED, () -> 0L); | |
| + statistics.registerCacheSizeSupplier(CacheRetention.REQUEST_SCOPED, () -> 0L); | |
| + | |
| + synchronized (ALL_STATISTICS) { | |
| + ALL_STATISTICS.add(statistics); | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Formats comprehensive cache statistics for display. | |
| + * | |
| + * @param stats the cache statistics to format | |
| + * @return a formatted string containing cache statistics | |
| + */ | |
| + static String formatCacheStatistics(CacheStatistics stats) { | |
| + StringBuilder sb = new StringBuilder(); | |
| + sb.append("Request Cache Statistics:\n"); | |
| + sb.append(" Total requests: ").append(stats.getTotalRequests()).append("\n"); | |
| + sb.append(" Cache hits: ").append(stats.getCacheHits()).append("\n"); | |
| + sb.append(" Cache misses: ").append(stats.getCacheMisses()).append("\n"); | |
| + sb.append(" Hit ratio: ") | |
| + .append(String.format(Locale.ENGLISH, "%.2f%%", stats.getHitRatio())) | |
| + .append("\n"); | |
| + | |
| + // Show eviction statistics | |
| + long totalEvictions = stats.getTotalEvictions(); | |
| + if (totalEvictions > 0) { | |
| + sb.append(" Evictions:\n"); | |
| + sb.append(" Key evictions: ") | |
| + .append(stats.getKeyEvictions()) | |
| + .append(" (") | |
| + .append(String.format(Locale.ENGLISH, "%.1f%%", stats.getKeyEvictionRatio())) | |
| + .append(")\n"); | |
| + sb.append(" Value evictions: ") | |
| + .append(stats.getValueEvictions()) | |
| + .append(" (") | |
| + .append(String.format(Locale.ENGLISH, "%.1f%%", stats.getValueEvictionRatio())) | |
| + .append(")\n"); | |
| + sb.append(" Total evictions: ").append(totalEvictions).append("\n"); | |
| + } | |
| + | |
| + // Show retention policy breakdown | |
| + var retentionStats = stats.getRetentionStatistics(); | |
| + if (!retentionStats.isEmpty()) { | |
| + sb.append(" By retention policy:\n"); | |
| + retentionStats.forEach((retention, retStats) -> { | |
| + sb.append(" ") | |
| + .append(retention) | |
| + .append(": ") | |
| + .append(retStats.getHits()) | |
| + .append(" hits, ") | |
| + .append(retStats.getMisses()) | |
| + .append(" misses (") | |
| + .append(String.format(Locale.ENGLISH, "%.1f%%", retStats.getHitRatio())) | |
| + .append(" hit ratio)"); | |
| + | |
| + // Add eviction info for this retention policy | |
| + long retKeyEvictions = retStats.getKeyEvictions(); | |
| + long retValueEvictions = retStats.getValueEvictions(); | |
| + if (retKeyEvictions > 0 || retValueEvictions > 0) { | |
| + sb.append(", ") | |
| + .append(retKeyEvictions) | |
| + .append(" key evictions, ") | |
| + .append(retValueEvictions) | |
| + .append(" value evictions"); | |
| + } | |
| + sb.append("\n"); | |
| + }); | |
| + } | |
| + | |
| + // Show reference type statistics | |
| + var refTypeStats = stats.getReferenceTypeStatistics(); | |
| + if (!refTypeStats.isEmpty()) { | |
| + sb.append(" Reference type usage:\n"); | |
| + refTypeStats.entrySet().stream() | |
| + .sorted((e1, e2) -> | |
| + Long.compare(e2.getValue().getTotal(), e1.getValue().getTotal())) | |
| + .forEach(entry -> { | |
| + var refStats = entry.getValue(); | |
| + sb.append(" ") | |
| + .append(entry.getKey()) | |
| + .append(": ") | |
| + .append(refStats.getCacheCreations()) | |
| + .append(" caches, ") | |
| + .append(refStats.getTotal()) | |
| + .append(" accesses (") | |
| + .append(String.format(Locale.ENGLISH, "%.1f%%", refStats.getHitRatio())) | |
| + .append(" hit ratio)\n"); | |
| + }); | |
| + } | |
| + | |
| + // Show top request types | |
| + var requestStats = stats.getRequestTypeStatistics(); | |
| + if (!requestStats.isEmpty()) { | |
| + sb.append(" Top request types:\n"); | |
| + requestStats.entrySet().stream() | |
| + .sorted((e1, e2) -> | |
| + Long.compare(e2.getValue().getTotal(), e1.getValue().getTotal())) | |
| + // .limit(5) | |
| + .forEach(entry -> { | |
| + var reqStats = entry.getValue(); | |
| + sb.append(" ") | |
| + .append(entry.getKey()) | |
| + .append(": ") | |
| + .append(reqStats.getTotal()) | |
| + .append(" requests (") | |
| + .append(String.format(Locale.ENGLISH, "%.1f%%", reqStats.getHitRatio())) | |
| + .append(" hit ratio)\n"); | |
| + }); | |
| + } | |
| + | |
| + return sb.toString(); | |
| + } | |
| + | |
| + public CacheStatistics getStatistics() { | |
| + return statistics; | |
| + } | |
| @Override | |
| - @SuppressWarnings("unchecked") | |
| + @SuppressWarnings({"unchecked", "checkstyle:MethodLength"}) | |
| protected <REQ extends Request<?>, REP extends Result<REQ>> CachingSupplier<REQ, REP> doCache( | |
| REQ req, Function<REQ, REP> supplier) { | |
| - CacheRetention retention = Objects.requireNonNullElse( | |
| - req instanceof CacheMetadata metadata ? metadata.getCacheRetention() : null, | |
| - CacheRetention.SESSION_SCOPED); | |
| - | |
| - Map<Object, CachingSupplier<?, ?>> cache = null; | |
| - if ((retention == CacheRetention.REQUEST_SCOPED || retention == CacheRetention.SESSION_SCOPED) | |
| - && req.getSession() instanceof Session session) { | |
| - Object key = retention == CacheRetention.REQUEST_SCOPED ? doGetOuterRequest(req) : ROOT; | |
| - Map<Object, Map<Object, CachingSupplier<?, ?>>> caches = | |
| - session.getData().computeIfAbsent(KEY, ConcurrentHashMap::new); | |
| - cache = caches.computeIfAbsent(key, k -> new SoftIdentityMap<>()); | |
| + // Early return for non-Session requests (e.g., ProtoSession) | |
| + if (!(req.getSession() instanceof Session session)) { | |
| + // Record as a miss since no caching is performed for non-Session requests | |
| + statistics.recordMiss(req.getClass().getSimpleName(), CacheRetention.DISABLED); | |
| + return new CachingSupplier<>(supplier); | |
| + } | |
| + | |
| + // Register shutdown hook for conditional statistics display | |
| + boolean cacheStatsEnabled = isCacheStatsEnabled(session); | |
| + if (cacheStatsEnabled) { | |
| + ensureShutdownHookRegistered(); | |
| + } | |
| + | |
| + CacheConfig config = getCacheConfig(req, session); | |
| + CacheRetention retention = config.scope(); | |
| + Cache.ReferenceType referenceType = config.referenceType(); | |
| + Cache.ReferenceType keyReferenceType = config.getEffectiveKeyReferenceType(); | |
| + Cache.ReferenceType valueReferenceType = config.getEffectiveValueReferenceType(); | |
| + | |
| + // Debug logging to verify reference types (disabled) | |
| + // System.err.println("DEBUG: Cache config for " + req.getClass().getSimpleName() + ": retention=" + retention | |
| + // + ", keyRef=" + keyReferenceType + ", valueRef=" + valueReferenceType); | |
| + if (LOGGER.isDebugEnabled()) { | |
| + LOGGER.debug( | |
| + "Cache config for {}: retention={}, keyRef={}, valueRef={}", | |
| + req.getClass().getSimpleName(), | |
| + retention, | |
| + keyReferenceType, | |
| + valueReferenceType); | |
| + } | |
| + | |
| + // Handle disabled caching | |
| + if (retention == CacheRetention.DISABLED | |
| + || keyReferenceType == Cache.ReferenceType.NONE | |
| + || valueReferenceType == Cache.ReferenceType.NONE) { | |
| + // Record as a miss since no caching is performed | |
| + statistics.recordMiss(req.getClass().getSimpleName(), retention); | |
| + return new CachingSupplier<>(supplier); | |
| + } | |
| + | |
| + Cache<Object, CachingSupplier<?, ?>> cache = null; | |
| + String cacheType = "NONE"; | |
| + | |
| + if (retention == CacheRetention.SESSION_SCOPED) { | |
| + Cache<Object, Cache<Object, CachingSupplier<?, ?>>> caches = session.getData() | |
| + .computeIfAbsent(KEY, () -> { | |
| + if (config.hasSeparateKeyValueReferenceTypes()) { | |
| + LOGGER.debug( | |
| + "Creating SESSION_SCOPED parent cache with key={}, value={}", | |
| + keyReferenceType, | |
| + valueReferenceType); | |
| + return Cache.newCache(keyReferenceType, valueReferenceType, "RequestCache-SESSION-Parent"); | |
| + } else { | |
| + return Cache.newCache(Cache.ReferenceType.SOFT, "RequestCache-SESSION-Parent"); | |
| + } | |
| + }); | |
| + | |
| + // Use separate key/value reference types if configured | |
| + if (config.hasSeparateKeyValueReferenceTypes()) { | |
| + cache = caches.computeIfAbsent(ROOT, k -> { | |
| + LOGGER.debug( | |
| + "Creating SESSION_SCOPED cache with key={}, value={}", | |
| + keyReferenceType, | |
| + valueReferenceType); | |
| + Cache<Object, CachingSupplier<?, ?>> newCache = | |
| + Cache.newCache(keyReferenceType, valueReferenceType, "RequestCache-SESSION"); | |
| + statistics.recordCacheCreation( | |
| + keyReferenceType.toString(), valueReferenceType.toString(), retention); | |
| + setupEvictionListenerIfNeeded(newCache, retention); | |
| + | |
| + // Debug logging to verify actual reference types (disabled) | |
| + // if (newCache instanceof Cache.RefConcurrentMap<?, ?> refMap) { | |
| + // System.err.println("DEBUG: Created cache '" + refMap.getName() + "' - requested key=" | |
| + // + keyReferenceType | |
| + // + ", value=" + valueReferenceType + ", actual key=" + refMap.getKeyReferenceType() | |
| + // + ", actual value=" + refMap.getValueReferenceType()); | |
| + // } | |
| + return newCache; | |
| + }); | |
| + } else { | |
| + cache = caches.computeIfAbsent(ROOT, k -> { | |
| + Cache<Object, CachingSupplier<?, ?>> newCache = | |
| + Cache.newCache(referenceType, "RequestCache-SESSION"); | |
| + statistics.recordCacheCreation(referenceType.toString(), referenceType.toString(), retention); | |
| + setupEvictionListenerIfNeeded(newCache, retention); | |
| + return newCache; | |
| + }); | |
| + } | |
| + cacheType = "SESSION_SCOPED"; | |
| + // Debug logging for cache sizes | |
| + if (LOGGER.isDebugEnabled()) { | |
| + LOGGER.debug( | |
| + "Cache access: type={}, request={}, cacheSize={}, totalCaches={}, key={}", | |
| + cacheType, | |
| + req.getClass().getSimpleName(), | |
| + cache.size(), | |
| + caches.size(), | |
| + ROOT); | |
| + } | |
| + } else if (retention == CacheRetention.REQUEST_SCOPED) { | |
| + Object key = doGetOuterRequest(req); | |
| + Cache<Object, Cache<Object, CachingSupplier<?, ?>>> caches = session.getData() | |
| + .computeIfAbsent(KEY, () -> { | |
| + if (config.hasSeparateKeyValueReferenceTypes()) { | |
| + LOGGER.debug( | |
| + "Creating REQUEST_SCOPED parent cache with key={}, value={}", | |
| + keyReferenceType, | |
| + valueReferenceType); | |
| + return Cache.newCache(keyReferenceType, valueReferenceType, "RequestCache-REQUEST-Parent"); | |
| + } else { | |
| + return Cache.newCache(Cache.ReferenceType.SOFT, "RequestCache-REQUEST-Parent"); | |
| + } | |
| + }); | |
| + | |
| + // Use separate key/value reference types if configured | |
| + if (config.hasSeparateKeyValueReferenceTypes()) { | |
| + cache = caches.computeIfAbsent(key, k -> { | |
| + LOGGER.debug( | |
| + "Creating REQUEST_SCOPED cache with key={}, value={}", | |
| + keyReferenceType, | |
| + valueReferenceType); | |
| + Cache<Object, CachingSupplier<?, ?>> newCache = | |
| + Cache.newCache(keyReferenceType, valueReferenceType, "RequestCache-REQUEST"); | |
| + statistics.recordCacheCreation( | |
| + keyReferenceType.toString(), valueReferenceType.toString(), retention); | |
| + setupEvictionListenerIfNeeded(newCache, retention); | |
| + return newCache; | |
| + }); | |
| + } else { | |
| + cache = caches.computeIfAbsent(key, k -> { | |
| + Cache<Object, CachingSupplier<?, ?>> newCache = | |
| + Cache.newCache(referenceType, "RequestCache-REQUEST"); | |
| + statistics.recordCacheCreation(referenceType.toString(), referenceType.toString(), retention); | |
| + setupEvictionListenerIfNeeded(newCache, retention); | |
| + return newCache; | |
| + }); | |
| + } | |
| + cacheType = "REQUEST_SCOPED"; | |
| + | |
| + // Debug logging for cache sizes | |
| + if (LOGGER.isDebugEnabled()) { | |
| + LOGGER.debug( | |
| + "Cache access: type={}, request={}, cacheSize={}, totalCaches={}, key={}", | |
| + cacheType, | |
| + req.getClass().getSimpleName(), | |
| + cache.size(), | |
| + caches.size(), | |
| + key.getClass().getSimpleName()); | |
| + } | |
| + | |
| } else if (retention == CacheRetention.PERSISTENT) { | |
| - cache = forever; | |
| + Cache<Object, Cache<Object, CachingSupplier<?, ?>>> caches = session.getData() | |
| + .computeIfAbsent(KEY, () -> { | |
| + if (config.hasSeparateKeyValueReferenceTypes()) { | |
| + LOGGER.debug( | |
| + "Creating PERSISTENT parent cache with key={}, value={}", | |
| + keyReferenceType, | |
| + valueReferenceType); | |
| + return Cache.newCache( | |
| + keyReferenceType, valueReferenceType, "RequestCache-PERSISTENT-Parent"); | |
| + } else { | |
| + return Cache.newCache(Cache.ReferenceType.SOFT, "RequestCache-PERSISTENT-Parent"); | |
| + } | |
| + }); | |
| + | |
| + // Use separate key/value reference types if configured | |
| + if (config.hasSeparateKeyValueReferenceTypes()) { | |
| + cache = caches.computeIfAbsent(KEY, k -> { | |
| + LOGGER.debug( | |
| + "Creating PERSISTENT cache with key={}, value={}", keyReferenceType, valueReferenceType); | |
| + Cache<Object, CachingSupplier<?, ?>> newCache = | |
| + Cache.newCache(keyReferenceType, valueReferenceType, "RequestCache-PERSISTENT"); | |
| + statistics.recordCacheCreation( | |
| + keyReferenceType.toString(), valueReferenceType.toString(), retention); | |
| + setupEvictionListenerIfNeeded(newCache, retention); | |
| + return newCache; | |
| + }); | |
| + } else { | |
| + cache = caches.computeIfAbsent(KEY, k -> { | |
| + Cache<Object, CachingSupplier<?, ?>> newCache = | |
| + Cache.newCache(referenceType, "RequestCache-PERSISTENT"); | |
| + statistics.recordCacheCreation(referenceType.toString(), referenceType.toString(), retention); | |
| + setupEvictionListenerIfNeeded(newCache, retention); | |
| + return newCache; | |
| + }); | |
| + } | |
| + cacheType = "PERSISTENT"; | |
| + | |
| + if (LOGGER.isDebugEnabled()) { | |
| + LOGGER.debug( | |
| + "Cache access: type={}, request={}, cacheSize={}", | |
| + cacheType, | |
| + req.getClass().getSimpleName(), | |
| + cache.size()); | |
| + } | |
| } | |
| + | |
| if (cache != null) { | |
| - return (CachingSupplier<REQ, REP>) cache.computeIfAbsent(req, r -> new CachingSupplier<>(supplier)); | |
| + // Set up eviction listener if this is a RefConcurrentMap | |
| + setupEvictionListenerIfNeeded(cache, retention); | |
| + | |
| + boolean isNewEntry = !cache.containsKey(req); | |
| + CachingSupplier<REQ, REP> result = (CachingSupplier<REQ, REP>) | |
| + cache.computeIfAbsent(req, r -> new CachingSupplier<>(supplier), referenceType); | |
| + | |
| + // Record statistics using the comprehensive system | |
| + String requestType = req.getClass().getSimpleName(); | |
| + | |
| + // Record reference type statistics | |
| + if (cache instanceof Cache.RefConcurrentMap<?, ?> refMap) { | |
| + statistics.recordCacheAccess( | |
| + refMap.getKeyReferenceType().toString(), | |
| + refMap.getValueReferenceType().toString(), | |
| + !isNewEntry); | |
| + } | |
| + | |
| + if (isNewEntry) { | |
| + statistics.recordMiss(requestType, retention); | |
| + if (LOGGER.isTraceEnabled()) { | |
| + LOGGER.trace( | |
| + "Cache MISS: type={}, request={}, newCacheSize={}", cacheType, requestType, cache.size()); | |
| + } | |
| + } else { | |
| + statistics.recordHit(requestType, retention); | |
| + if (LOGGER.isTraceEnabled()) { | |
| + LOGGER.trace("Cache HIT: type={}, request={}", cacheType, requestType); | |
| + } | |
| + } | |
| + return result; | |
| } else { | |
| + // Record as a miss since no cache was available | |
| + statistics.recordMiss(req.getClass().getSimpleName(), retention); | |
| + if (LOGGER.isTraceEnabled()) { | |
| + LOGGER.trace("No cache: request={}", req.getClass().getSimpleName()); | |
| + } | |
| return new CachingSupplier<>(supplier); | |
| } | |
| } | |
| + private static boolean isCacheStatsEnabled(Session session) { | |
| + String showStats = session.getUserProperties().get(Constants.MAVEN_CACHE_STATS); | |
| + return Boolean.parseBoolean(showStats); | |
| + } | |
| + | |
| + /** | |
| + * Sets up eviction listener for the cache if it's a RefConcurrentMap. | |
| + * This avoids memory leaks by having the cache push events to statistics | |
| + * instead of statistics holding references to caches. | |
| + */ | |
| + private void setupEvictionListenerIfNeeded(Cache<Object, CachingSupplier<?, ?>> cache, CacheRetention retention) { | |
| + if (cache instanceof Cache.RefConcurrentMap<?, ?> refMap) { | |
| + // Set up the eviction listener (it's safe to set multiple times) | |
| + refMap.setEvictionListener(new Cache.EvictionListener() { | |
| + @Override | |
| + public void onKeyEviction() { | |
| + statistics.recordKeyEviction(retention); | |
| + } | |
| + | |
| + @Override | |
| + public void onValueEviction() { | |
| + statistics.recordValueEviction(retention); | |
| + } | |
| + }); | |
| + } | |
| + } | |
| + | |
| private <REQ extends Request<?>> Object doGetOuterRequest(REQ req) { | |
| RequestTrace trace = req.getTrace(); | |
| + if (trace == null && req.getSession() instanceof Session session) { | |
| + trace = InternalSession.from(session).getCurrentTrace(); | |
| + } | |
| while (trace != null && trace.parent() != null) { | |
| trace = trace.parent(); | |
| } | |
| return trace != null && trace.data() != null ? trace.data() : req; | |
| } | |
| + | |
| + /** | |
| + * Gets the cache configuration for the given request and session. | |
| + * | |
| + * @param req the request to get configuration for | |
| + * @param session the session containing user properties | |
| + * @return the resolved cache configuration | |
| + */ | |
| + private <REQ extends Request<?>> CacheConfig getCacheConfig(REQ req, Session session) { | |
| + return CacheConfigurationResolver.resolveConfig(req, session); | |
| + } | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCacheFactory.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCacheFactory.java | |
| index fa268de59e..667adb7b9a 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCacheFactory.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/DefaultRequestCacheFactory.java | |
| @@ -27,8 +27,10 @@ | |
| @Singleton | |
| public class DefaultRequestCacheFactory implements RequestCacheFactory { | |
| + private static final RequestCache REQUEST_CACHE = new DefaultRequestCache(); | |
| + | |
| @Override | |
| public RequestCache createCache() { | |
| - return new DefaultRequestCache(); | |
| + return REQUEST_CACHE; | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/PartialCacheConfig.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/PartialCacheConfig.java | |
| new file mode 100644 | |
| index 0000000000..49cb9e731b | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/PartialCacheConfig.java | |
| @@ -0,0 +1,96 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| + | |
| +/** | |
| + * Partial cache configuration that allows specifying only scope or reference type. | |
| + * Used for merging configurations from multiple selectors. | |
| + * | |
| + * @param scope the cache retention scope (nullable) | |
| + * @param referenceType the reference type to use for cache entries (nullable) | |
| + */ | |
| +public record PartialCacheConfig(CacheRetention scope, Cache.ReferenceType referenceType) { | |
| + | |
| + /** | |
| + * Creates a partial configuration with only scope specified. | |
| + */ | |
| + public static PartialCacheConfig withScope(CacheRetention scope) { | |
| + return new PartialCacheConfig(scope, null); | |
| + } | |
| + | |
| + /** | |
| + * Creates a partial configuration with only reference type specified. | |
| + */ | |
| + public static PartialCacheConfig withReferenceType(Cache.ReferenceType referenceType) { | |
| + return new PartialCacheConfig(null, referenceType); | |
| + } | |
| + | |
| + /** | |
| + * Creates a complete partial configuration with both scope and reference type. | |
| + */ | |
| + public static PartialCacheConfig complete(CacheRetention scope, Cache.ReferenceType referenceType) { | |
| + return new PartialCacheConfig(scope, referenceType); | |
| + } | |
| + | |
| + /** | |
| + * Merges this configuration with another, with this configuration taking precedence | |
| + * for non-null values. | |
| + * | |
| + * @param other the other configuration to merge with | |
| + * @return a new merged configuration | |
| + */ | |
| + public PartialCacheConfig mergeWith(PartialCacheConfig other) { | |
| + if (other == null) { | |
| + return this; | |
| + } | |
| + | |
| + CacheRetention mergedScope = this.scope != null ? this.scope : other.scope; | |
| + Cache.ReferenceType mergedRefType = this.referenceType != null ? this.referenceType : other.referenceType; | |
| + | |
| + return new PartialCacheConfig(mergedScope, mergedRefType); | |
| + } | |
| + | |
| + /** | |
| + * Converts this partial configuration to a complete CacheConfig, using defaults for missing values. | |
| + * | |
| + * @return a complete CacheConfig | |
| + */ | |
| + public CacheConfig toComplete() { | |
| + CacheRetention finalScope = scope != null ? scope : CacheRetention.REQUEST_SCOPED; | |
| + Cache.ReferenceType finalRefType = referenceType != null ? referenceType : Cache.ReferenceType.SOFT; | |
| + | |
| + return new CacheConfig(finalScope, finalRefType); | |
| + } | |
| + | |
| + /** | |
| + * Checks if this configuration is empty (both values are null). | |
| + */ | |
| + public boolean isEmpty() { | |
| + return scope == null && referenceType == null; | |
| + } | |
| + | |
| + /** | |
| + * Checks if this configuration is complete (both values are non-null). | |
| + */ | |
| + public boolean isComplete() { | |
| + return scope != null && referenceType != null; | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/SoftIdentityMap.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/SoftIdentityMap.java | |
| deleted file mode 100644 | |
| index 2c6c51b660..0000000000 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/cache/SoftIdentityMap.java | |
| +++ /dev/null | |
| @@ -1,239 +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.impl.cache; | |
| - | |
| -import java.lang.ref.Reference; | |
| -import java.lang.ref.ReferenceQueue; | |
| -import java.lang.ref.SoftReference; | |
| -import java.util.Collection; | |
| -import java.util.Map; | |
| -import java.util.Objects; | |
| -import java.util.Set; | |
| -import java.util.concurrent.ConcurrentHashMap; | |
| -import java.util.function.Function; | |
| - | |
| -/** | |
| - * A Map implementation that uses soft references for both keys and values, | |
| - * and compares keys using identity (==) rather than equals(). | |
| - * | |
| - * @param <K> the type of keys maintained by this map | |
| - * @param <V> the type of mapped values | |
| - */ | |
| -public class SoftIdentityMap<K, V> implements Map<K, V> { | |
| - | |
| - private final ReferenceQueue<K> keyQueue = new ReferenceQueue<>(); | |
| - private final ReferenceQueue<V> valueQueue = new ReferenceQueue<>(); | |
| - private final ConcurrentHashMap<SoftIdentityReference<K>, ComputeReference<V>> map = new ConcurrentHashMap<>(); | |
| - | |
| - private static class SoftIdentityReference<T> extends SoftReference<T> { | |
| - private final int hash; | |
| - | |
| - SoftIdentityReference(T referent, ReferenceQueue<T> queue) { | |
| - super(referent, queue); | |
| - this.hash = referent.hashCode(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean equals(Object obj) { | |
| - if (this == obj) { | |
| - return true; | |
| - } | |
| - if (!(obj instanceof SoftIdentityReference<?> other)) { | |
| - return false; | |
| - } | |
| - T thisRef = this.get(); | |
| - Object otherRef = other.get(); | |
| - return thisRef != null && thisRef.equals(otherRef); | |
| - } | |
| - | |
| - @Override | |
| - public int hashCode() { | |
| - return hash; | |
| - } | |
| - } | |
| - | |
| - private static class ComputeReference<V> extends SoftReference<V> { | |
| - private final boolean computing; | |
| - | |
| - ComputeReference(V value, ReferenceQueue<V> queue) { | |
| - super(value, queue); | |
| - this.computing = false; | |
| - } | |
| - | |
| - private ComputeReference(ReferenceQueue<V> queue) { | |
| - super(null, queue); | |
| - this.computing = true; | |
| - } | |
| - | |
| - static <V> ComputeReference<V> computing(ReferenceQueue<V> queue) { | |
| - return new ComputeReference<>(queue); | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { | |
| - Objects.requireNonNull(key); | |
| - Objects.requireNonNull(mappingFunction); | |
| - | |
| - while (true) { | |
| - expungeStaleEntries(); | |
| - | |
| - SoftIdentityReference<K> softKey = new SoftIdentityReference<>(key, keyQueue); | |
| - | |
| - // Try to get existing value | |
| - ComputeReference<V> valueRef = map.get(softKey); | |
| - if (valueRef != null && !valueRef.computing) { | |
| - V value = valueRef.get(); | |
| - if (value != null) { | |
| - return value; | |
| - } | |
| - // Value was GC'd, remove it | |
| - map.remove(softKey, valueRef); | |
| - } | |
| - | |
| - // Try to claim computation | |
| - ComputeReference<V> computingRef = ComputeReference.computing(valueQueue); | |
| - valueRef = map.putIfAbsent(softKey, computingRef); | |
| - | |
| - if (valueRef == null) { | |
| - // We claimed the computation | |
| - try { | |
| - V newValue = mappingFunction.apply(key); | |
| - if (newValue == null) { | |
| - map.remove(softKey, computingRef); | |
| - return null; | |
| - } | |
| - | |
| - ComputeReference<V> newValueRef = new ComputeReference<>(newValue, valueQueue); | |
| - map.replace(softKey, computingRef, newValueRef); | |
| - return newValue; | |
| - } catch (Throwable t) { | |
| - map.remove(softKey, computingRef); | |
| - throw t; | |
| - } | |
| - } else if (!valueRef.computing) { | |
| - // Another thread has a value | |
| - V value = valueRef.get(); | |
| - if (value != null) { | |
| - return value; | |
| - } | |
| - // Value was GC'd | |
| - if (map.remove(softKey, valueRef)) { | |
| - continue; | |
| - } | |
| - } | |
| - // Another thread is computing or the reference changed, try again | |
| - } | |
| - } | |
| - | |
| - private void expungeStaleEntries() { | |
| - Reference<?> ref; | |
| - while ((ref = keyQueue.poll()) != null) { | |
| - map.remove(ref); | |
| - } | |
| - while ((ref = valueQueue.poll()) != null) { | |
| - map.values().remove(ref); | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - expungeStaleEntries(); | |
| - return map.size(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean isEmpty() { | |
| - expungeStaleEntries(); | |
| - return map.isEmpty(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean containsKey(Object key) { | |
| - expungeStaleEntries(); | |
| - return map.containsKey(new SoftIdentityReference<>((K) key, null)); | |
| - } | |
| - | |
| - @Override | |
| - public boolean containsValue(Object value) { | |
| - expungeStaleEntries(); | |
| - for (Reference<V> ref : map.values()) { | |
| - V v = ref.get(); | |
| - if (v != null && v == value) { | |
| - return true; | |
| - } | |
| - } | |
| - return false; | |
| - } | |
| - | |
| - @Override | |
| - public V get(Object key) { | |
| - expungeStaleEntries(); | |
| - Reference<V> ref = map.get(new SoftIdentityReference<>((K) key, null)); | |
| - return ref != null ? ref.get() : null; | |
| - } | |
| - | |
| - @Override | |
| - public V put(K key, V value) { | |
| - Objects.requireNonNull(key); | |
| - Objects.requireNonNull(value); | |
| - expungeStaleEntries(); | |
| - | |
| - Reference<V> oldValueRef = | |
| - map.put(new SoftIdentityReference<>(key, keyQueue), new ComputeReference<>(value, valueQueue)); | |
| - | |
| - return oldValueRef != null ? oldValueRef.get() : null; | |
| - } | |
| - | |
| - @Override | |
| - public V remove(Object key) { | |
| - expungeStaleEntries(); | |
| - Reference<V> valueRef = map.remove(new SoftIdentityReference<>((K) key, null)); | |
| - return valueRef != null ? valueRef.get() : null; | |
| - } | |
| - | |
| - @Override | |
| - public void putAll(Map<? extends K, ? extends V> m) { | |
| - Objects.requireNonNull(m); | |
| - for (Entry<? extends K, ? extends V> e : m.entrySet()) { | |
| - put(e.getKey(), e.getValue()); | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public void clear() { | |
| - map.clear(); | |
| - expungeStaleEntries(); | |
| - } | |
| - | |
| - @Override | |
| - public Set<K> keySet() { | |
| - throw new UnsupportedOperationException("keySet not supported"); | |
| - } | |
| - | |
| - @Override | |
| - public Collection<V> values() { | |
| - throw new UnsupportedOperationException("values not supported"); | |
| - } | |
| - | |
| - @Override | |
| - public Set<Entry<K, V>> entrySet() { | |
| - throw new UnsupportedOperationException("entrySet not supported"); | |
| - } | |
| -} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultDependencyManagementImporter.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultDependencyManagementImporter.java | |
| index 19ed567329..45de07f83a 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultDependencyManagementImporter.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultDependencyManagementImporter.java | |
| @@ -185,8 +185,6 @@ static Dependency updateWithImportedFrom(Dependency dependency, DependencyManage | |
| // We modify the input location that is used for the whole file. | |
| // This is likely correct because the POM hierarchy applies to the whole POM, not just one dependency. | |
| - return Dependency.newBuilder(dependency, true) | |
| - .importedFrom(new InputLocation(bomLocation)) | |
| - .build(); | |
| + return Dependency.newBuilder(dependency, true).importedFrom(bomLocation).build(); | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInheritanceAssembler.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInheritanceAssembler.java | |
| index f9c4a9883a..aeacef7b53 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInheritanceAssembler.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInheritanceAssembler.java | |
| @@ -194,6 +194,16 @@ private void concatPath(StringBuilder url, String path) { | |
| } | |
| } | |
| + @Override | |
| + protected void mergeModel_Mixins( | |
| + Model.Builder builder, | |
| + Model target, | |
| + Model source, | |
| + boolean sourceDominant, | |
| + Map<Object, Object> context) { | |
| + // do not merge | |
| + } | |
| + | |
| @Override | |
| protected void mergeModelBase_Properties( | |
| ModelBase.Builder builder, | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java | |
| index dfd124cc3e..1ac6aff664 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java | |
| @@ -21,7 +21,6 @@ | |
| import java.io.File; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| -import java.lang.reflect.Field; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| @@ -51,6 +50,7 @@ | |
| 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.Nonnull; | |
| @@ -68,7 +68,7 @@ | |
| import org.apache.maven.api.model.DistributionManagement; | |
| import org.apache.maven.api.model.Exclusion; | |
| import org.apache.maven.api.model.InputLocation; | |
| -import org.apache.maven.api.model.InputSource; | |
| +import org.apache.maven.api.model.Mixin; | |
| import org.apache.maven.api.model.Model; | |
| import org.apache.maven.api.model.Parent; | |
| import org.apache.maven.api.model.Profile; | |
| @@ -118,6 +118,7 @@ | |
| import org.apache.maven.api.spi.ModelTransformer; | |
| import org.apache.maven.impl.InternalSession; | |
| import org.apache.maven.impl.RequestTraceHelper; | |
| +import org.apache.maven.impl.cache.Cache; | |
| import org.apache.maven.impl.util.PhasingExecutor; | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| @@ -246,6 +247,16 @@ public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilder | |
| } | |
| return session.result; | |
| } finally { | |
| + // Clean up REQUEST_SCOPED cache entries to prevent memory leaks | |
| + // This is especially important for BUILD_PROJECT requests which are top-level requests | |
| + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) { | |
| + try { | |
| + clearRequestScopedCache(request); | |
| + } catch (Exception e) { | |
| + // Log but don't fail the build due to cache cleanup issues | |
| + logger.debug("Failed to clear REQUEST_SCOPED cache for request: {}", request, e); | |
| + } | |
| + } | |
| RequestTraceHelper.exit(trace); | |
| } | |
| } | |
| @@ -1055,6 +1066,9 @@ private Model readParentLocally( | |
| Set<String> parentChain) | |
| throws ModelBuilderException { | |
| ModelSource candidateSource; | |
| + | |
| + boolean isParentOrSimpleMixin = !(parent instanceof Mixin) | |
| + || (((Mixin) parent).getClassifier() == null && ((Mixin) parent).getExtension() == null); | |
| String parentPath = parent.getRelativePath(); | |
| if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) { | |
| if (parentPath != null && !parentPath.isEmpty()) { | |
| @@ -1063,14 +1077,16 @@ private Model readParentLocally( | |
| wrongParentRelativePath(childModel); | |
| return null; | |
| } | |
| - } else { | |
| + } else if (isParentOrSimpleMixin) { | |
| candidateSource = | |
| resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); | |
| if (candidateSource == null && parentPath == null) { | |
| candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, ".."); | |
| } | |
| + } else { | |
| + candidateSource = null; | |
| } | |
| - } else { | |
| + } else if (isParentOrSimpleMixin) { | |
| candidateSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); | |
| if (candidateSource == null) { | |
| if (parentPath == null) { | |
| @@ -1080,6 +1096,8 @@ private Model readParentLocally( | |
| candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, parentPath); | |
| } | |
| } | |
| + } else { | |
| + candidateSource = null; | |
| } | |
| if (candidateSource == null) { | |
| @@ -1111,11 +1129,10 @@ private Model readParentLocally( | |
| String version = getVersion(candidateModel); | |
| // Ensure that relative path and GA match, if both are provided | |
| - if (groupId == null | |
| - || !groupId.equals(parent.getGroupId()) | |
| - || artifactId == null | |
| - || !artifactId.equals(parent.getArtifactId())) { | |
| - mismatchRelativePathAndGA(childModel, groupId, artifactId); | |
| + if (parent.getGroupId() != null && (groupId == null || !groupId.equals(parent.getGroupId())) | |
| + || parent.getArtifactId() != null | |
| + && (artifactId == null || !artifactId.equals(parent.getArtifactId()))) { | |
| + mismatchRelativePathAndGA(childModel, parent, groupId, artifactId); | |
| return null; | |
| } | |
| @@ -1159,8 +1176,7 @@ private Model readParentLocally( | |
| } | |
| } | |
| - private void mismatchRelativePathAndGA(Model childModel, String groupId, String artifactId) { | |
| - Parent parent = childModel.getParent(); | |
| + private void mismatchRelativePathAndGA(Model childModel, Parent parent, String groupId, String artifactId) { | |
| StringBuilder buffer = new StringBuilder(256); | |
| buffer.append("'parent.relativePath'"); | |
| if (childModel != getRootModel()) { | |
| @@ -1203,6 +1219,8 @@ Model resolveAndReadParentExternally( | |
| String groupId = parent.getGroupId(); | |
| String artifactId = parent.getArtifactId(); | |
| String version = parent.getVersion(); | |
| + String classifier = parent instanceof Mixin ? ((Mixin) parent).getClassifier() : null; | |
| + String extension = parent instanceof Mixin ? ((Mixin) parent).getExtension() : null; | |
| // add repositories specified by the current model so that we can resolve the parent | |
| if (!childModel.getRepositories().isEmpty()) { | |
| @@ -1220,12 +1238,23 @@ Model resolveAndReadParentExternally( | |
| ModelSource modelSource; | |
| try { | |
| - modelSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); | |
| + modelSource = classifier == null && extension == null | |
| + ? resolveReactorModel(groupId, artifactId, version) | |
| + : null; | |
| if (modelSource == null) { | |
| - AtomicReference<Parent> modified = new AtomicReference<>(); | |
| - modelSource = modelResolver.resolveModel(request.getSession(), repositories, parent, modified); | |
| - if (modified.get() != null) { | |
| - parent = modified.get(); | |
| + ModelResolver.ModelResolverRequest req = new ModelResolver.ModelResolverRequest( | |
| + request.getSession(), | |
| + null, | |
| + repositories, | |
| + groupId, | |
| + artifactId, | |
| + version, | |
| + classifier, | |
| + extension != null ? extension : "pom"); | |
| + ModelResolver.ModelResolverResult result = modelResolver.resolveModel(req); | |
| + modelSource = result.source(); | |
| + if (result.version() != null) { | |
| + parent = parent.withVersion(result.version()); | |
| } | |
| } | |
| } catch (ModelResolverException e) { | |
| @@ -1364,6 +1393,15 @@ private Model readEffectiveModel() throws ModelBuilderException { | |
| Model model = inheritanceAssembler.assembleModelInheritance(inputModel, parentModel, request, this); | |
| + // Mixins | |
| + for (Mixin mixin : model.getMixins()) { | |
| + Model parent = resolveParent(model, mixin, profileActivationContext, parentChain); | |
| + model = inheritanceAssembler.assembleModelInheritance(model, parent, request, this); | |
| + } | |
| + | |
| + // model normalization | |
| + model = modelNormalizer.mergeDuplicates(model, request, this); | |
| + | |
| // profile activation | |
| profileActivationContext.setModel(model); | |
| @@ -1446,6 +1484,7 @@ Model doReadFileModel() throws ModelBuilderException { | |
| model = modelProcessor.read(XmlReaderRequest.builder() | |
| .strict(strict) | |
| .location(modelSource.getLocation()) | |
| + .modelId(modelSource.getModelId()) | |
| .path(modelSource.getPath()) | |
| .rootDirectory(rootDirectory) | |
| .inputStream(is) | |
| @@ -1459,6 +1498,7 @@ Model doReadFileModel() throws ModelBuilderException { | |
| model = modelProcessor.read(XmlReaderRequest.builder() | |
| .strict(false) | |
| .location(modelSource.getLocation()) | |
| + .modelId(modelSource.getModelId()) | |
| .path(modelSource.getPath()) | |
| .rootDirectory(rootDirectory) | |
| .inputStream(is) | |
| @@ -1475,19 +1515,6 @@ Model doReadFileModel() throws ModelBuilderException { | |
| "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) { | |
| add( | |
| Severity.FATAL, | |
| @@ -1548,7 +1575,7 @@ Model doReadFileModel() throws ModelBuilderException { | |
| .version(parentVersion) | |
| .build()); | |
| } else { | |
| - mismatchRelativePathAndGA(model, parentGroupId, parentArtifactId); | |
| + mismatchRelativePathAndGA(model, parent, parentGroupId, parentArtifactId); | |
| } | |
| } else { | |
| if (!MODEL_VERSION_4_0_0.equals(model.getModelVersion()) && path != null) { | |
| @@ -1807,7 +1834,8 @@ private ParentModelWithProfiles doReadAsParentModel( | |
| throws ModelBuilderException { | |
| Model raw = readRawModel(); | |
| Model parentData = readParent(raw, raw.getParent(), childProfileActivationContext, parentChain); | |
| - Model parent = new DefaultInheritanceAssembler(new DefaultInheritanceAssembler.InheritanceModelMerger() { | |
| + DefaultInheritanceAssembler defaultInheritanceAssembler = | |
| + new DefaultInheritanceAssembler(new DefaultInheritanceAssembler.InheritanceModelMerger() { | |
| @Override | |
| protected void mergeModel_Modules( | |
| Model.Builder builder, | |
| @@ -1823,8 +1851,12 @@ protected void mergeModel_Subprojects( | |
| Model source, | |
| boolean sourceDominant, | |
| Map<Object, Object> context) {} | |
| - }) | |
| - .assembleModelInheritance(raw, parentData, request, this); | |
| + }); | |
| + Model parent = defaultInheritanceAssembler.assembleModelInheritance(raw, parentData, request, this); | |
| + for (Mixin mixin : parent.getMixins()) { | |
| + Model parentModel = resolveParent(parent, mixin, childProfileActivationContext, parentChain); | |
| + parent = defaultInheritanceAssembler.assembleModelInheritance(parent, parentModel, request, this); | |
| + } | |
| // Profile injection SHOULD be performed on parent models to ensure | |
| // that profile content becomes part of the parent model before inheritance. | |
| @@ -2157,6 +2189,13 @@ public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderExcep | |
| } | |
| return model; | |
| } finally { | |
| + // Clean up REQUEST_SCOPED cache entries for raw model building as well | |
| + try { | |
| + clearRequestScopedCache(request); | |
| + } catch (Exception e) { | |
| + // Log but don't fail the build due to cache cleanup issues | |
| + logger.debug("Failed to clear REQUEST_SCOPED cache for raw model request: {}", request, e); | |
| + } | |
| RequestTraceHelper.exit(trace); | |
| } | |
| } | |
| @@ -2352,7 +2391,9 @@ public RequestTrace getTrace() { | |
| @Override | |
| public CacheRetention getCacheRetention() { | |
| - return source instanceof CacheMetadata cacheMetadata ? cacheMetadata.getCacheRetention() : null; | |
| + return source instanceof CacheMetadata cacheMetadata | |
| + ? cacheMetadata.getCacheRetention() | |
| + : CacheRetention.REQUEST_SCOPED; | |
| } | |
| @Override | |
| @@ -2481,6 +2522,56 @@ Set<String> getContexts() { | |
| } | |
| } | |
| + /** | |
| + * Clears REQUEST_SCOPED cache entries for a specific request. | |
| + * <p> | |
| + * The method identifies the outer request and removes the corresponding cache entry from the session data. | |
| + * | |
| + * @param req the request whose REQUEST_SCOPED cache should be cleared | |
| + * @param <REQ> the request type | |
| + */ | |
| + private <REQ extends Request<?>> void clearRequestScopedCache(REQ req) { | |
| + if (req.getSession() instanceof Session session) { | |
| + // Use the same key as DefaultRequestCache | |
| + SessionData.Key<Cache> key = SessionData.key(Cache.class, CacheMetadata.class); | |
| + | |
| + // Get the outer request key using the same logic as DefaultRequestCache | |
| + Object outerRequestKey = getOuterRequest(req); | |
| + | |
| + Cache<Object, Object> caches = session.getData().get(key); | |
| + if (caches != null) { | |
| + Object removedCache = caches.get(outerRequestKey); | |
| + if (removedCache instanceof Cache<?, ?> map) { | |
| + int beforeSize = map.size(); | |
| + map.removeIf((k, v) -> !(k instanceof RgavCacheKey) && !(k instanceof SourceCacheKey)); | |
| + int afterSize = map.size(); | |
| + if (logger.isDebugEnabled()) { | |
| + logger.debug( | |
| + "Cleared REQUEST_SCOPED cache for request: {}, removed {} entries, remaining entries: {}", | |
| + outerRequestKey.getClass().getSimpleName(), | |
| + afterSize - beforeSize, | |
| + afterSize); | |
| + } | |
| + } | |
| + } | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Gets the outer request for cache key purposes. | |
| + * This replicates the logic from DefaultRequestCache.doGetOuterRequest(). | |
| + */ | |
| + private Object getOuterRequest(Request<?> req) { | |
| + RequestTrace trace = req.getTrace(); | |
| + if (trace != null) { | |
| + RequestTrace parent = trace.parent(); | |
| + if (parent != null && parent.data() instanceof Request<?> parentRequest) { | |
| + return getOuterRequest(parentRequest); | |
| + } | |
| + } | |
| + return req; | |
| + } | |
| + | |
| private static <T, A> List<T> map(List<T> resources, BiFunction<T, A, T> mapper, A argument) { | |
| List<T> newResources = null; | |
| if (resources != null) { | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java | |
| new file mode 100644 | |
| index 0000000000..bec34fe0fa | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java | |
| @@ -0,0 +1,349 @@ | |
| +/* | |
| + * 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.impl.model; | |
| + | |
| +import java.util.Arrays; | |
| +import java.util.Map; | |
| +import java.util.Objects; | |
| +import java.util.Set; | |
| +import java.util.concurrent.ConcurrentHashMap; | |
| +import java.util.concurrent.atomic.AtomicLong; | |
| +import java.util.stream.Collectors; | |
| + | |
| +import org.apache.maven.api.Constants; | |
| +import org.apache.maven.api.model.Dependency; | |
| +import org.apache.maven.api.model.ModelObjectProcessor; | |
| +import org.apache.maven.impl.cache.Cache; | |
| +import org.slf4j.Logger; | |
| +import org.slf4j.LoggerFactory; | |
| + | |
| +/** | |
| + * Default implementation of ModelObjectProcessor that provides memory optimization | |
| + * through object pooling and interning. | |
| + * | |
| + * <p>This implementation can pool any model object type based on configuration. | |
| + * By default, it pools {@link Dependency} objects, which are frequently duplicated | |
| + * in large Maven projects. Other model objects are passed through unchanged unless | |
| + * explicitly configured for pooling.</p> | |
| + * | |
| + * <p>The pool uses configurable reference types and provides thread-safe access | |
| + * through ConcurrentHashMap-based caches.</p> | |
| + * | |
| + * @since 4.0.0 | |
| + */ | |
| +public class DefaultModelObjectPool implements ModelObjectProcessor { | |
| + | |
| + // Cache for each pooled object type | |
| + private static final Map<Class<?>, Cache<PoolKey, Object>> OBJECT_POOLS = new ConcurrentHashMap<>(); | |
| + | |
| + // Statistics tracking | |
| + private static final Map<Class<?>, AtomicLong> TOTAL_CALLS = new ConcurrentHashMap<>(); | |
| + private static final Map<Class<?>, AtomicLong> CACHE_HITS = new ConcurrentHashMap<>(); | |
| + private static final Map<Class<?>, AtomicLong> CACHE_MISSES = new ConcurrentHashMap<>(); | |
| + | |
| + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultModelObjectPool.class); | |
| + | |
| + private final Map<?, ?> properties; | |
| + | |
| + public DefaultModelObjectPool() { | |
| + this(System.getProperties()); | |
| + } | |
| + | |
| + DefaultModelObjectPool(Map<?, ?> properties) { | |
| + this.properties = properties; | |
| + } | |
| + | |
| + @Override | |
| + @SuppressWarnings("unchecked") | |
| + public <T> T process(T object) { | |
| + if (object == null) { | |
| + return null; | |
| + } | |
| + | |
| + Class<?> objectType = object.getClass(); | |
| + String simpleClassName = objectType.getSimpleName(); | |
| + | |
| + // Check if this object type should be pooled (read configuration dynamically) | |
| + if (!getPooledTypes(properties).contains(simpleClassName)) { | |
| + return object; | |
| + } | |
| + | |
| + // Get or create cache for this object type | |
| + Cache<PoolKey, Object> cache = OBJECT_POOLS.computeIfAbsent(objectType, this::createCacheForType); | |
| + | |
| + return (T) internObject(object, cache, objectType); | |
| + } | |
| + | |
| + private String getProperty(String name, String defaultValue) { | |
| + Object value = properties.get(name); | |
| + return value instanceof String str ? str : defaultValue; | |
| + } | |
| + | |
| + /** | |
| + * Gets the set of object types that should be pooled. | |
| + */ | |
| + private Set<String> getPooledTypes(Map<?, ?> properties) { | |
| + String pooledTypesProperty = getProperty(Constants.MAVEN_MODEL_PROCESSOR_POOLED_TYPES, "Dependency"); | |
| + return Arrays.stream(pooledTypesProperty.split(",")) | |
| + .map(String::trim) | |
| + .filter(s -> !s.isEmpty()) | |
| + .collect(Collectors.toSet()); | |
| + } | |
| + | |
| + /** | |
| + * Creates a cache for the specified object type with the appropriate reference type. | |
| + */ | |
| + private Cache<PoolKey, Object> createCacheForType(Class<?> objectType) { | |
| + Cache.ReferenceType referenceType = getReferenceTypeForClass(objectType); | |
| + return Cache.newCache(referenceType); | |
| + } | |
| + | |
| + /** | |
| + * Gets the reference type to use for a specific object type. | |
| + * Checks for per-type configuration first, then falls back to default. | |
| + */ | |
| + private Cache.ReferenceType getReferenceTypeForClass(Class<?> objectType) { | |
| + String className = objectType.getSimpleName(); | |
| + | |
| + // Check for per-type configuration first | |
| + String perTypeProperty = Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE_PREFIX + className; | |
| + String perTypeValue = getProperty(perTypeProperty, null); | |
| + | |
| + if (perTypeValue != null) { | |
| + try { | |
| + return Cache.ReferenceType.valueOf(perTypeValue.toUpperCase()); | |
| + } catch (IllegalArgumentException e) { | |
| + LOGGER.warn("Unknown reference type for " + className + ": " + perTypeValue + ", using default"); | |
| + } | |
| + } | |
| + | |
| + // Fall back to default reference type | |
| + return getDefaultReferenceType(); | |
| + } | |
| + | |
| + /** | |
| + * Gets the default reference type from system properties. | |
| + */ | |
| + private Cache.ReferenceType getDefaultReferenceType() { | |
| + try { | |
| + String referenceTypeProperty = | |
| + getProperty(Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE, Cache.ReferenceType.HARD.name()); | |
| + return Cache.ReferenceType.valueOf(referenceTypeProperty.toUpperCase()); | |
| + } catch (IllegalArgumentException e) { | |
| + LOGGER.warn("Unknown default reference type, using HARD"); | |
| + return Cache.ReferenceType.HARD; | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Interns an object in the appropriate pool. | |
| + */ | |
| + private Object internObject(Object object, Cache<PoolKey, Object> cache, Class<?> objectType) { | |
| + // Update statistics | |
| + TOTAL_CALLS.computeIfAbsent(objectType, k -> new AtomicLong(0)).incrementAndGet(); | |
| + | |
| + PoolKey key = new PoolKey(object); | |
| + Object existing = cache.get(key); | |
| + if (existing != null) { | |
| + CACHE_HITS.computeIfAbsent(objectType, k -> new AtomicLong(0)).incrementAndGet(); | |
| + return existing; | |
| + } | |
| + | |
| + // Use computeIfAbsent to handle concurrent access | |
| + existing = cache.computeIfAbsent(key, k -> object); | |
| + if (existing == object) { | |
| + // We added the object to the cache | |
| + CACHE_MISSES.computeIfAbsent(objectType, k -> new AtomicLong(0)).incrementAndGet(); | |
| + } else { | |
| + // Another thread added it first | |
| + CACHE_HITS.computeIfAbsent(objectType, k -> new AtomicLong(0)).incrementAndGet(); | |
| + } | |
| + | |
| + return existing; | |
| + } | |
| + | |
| + /** | |
| + * Key class for pooling any model object based on their content. | |
| + * Uses custom equality strategies for different object types. | |
| + */ | |
| + private static class PoolKey { | |
| + private final Object object; | |
| + private final int hashCode; | |
| + | |
| + PoolKey(Object object) { | |
| + this.object = object; | |
| + this.hashCode = computeHashCode(object); | |
| + } | |
| + | |
| + @Override | |
| + public boolean equals(Object obj) { | |
| + if (this == obj) { | |
| + return true; | |
| + } | |
| + if (!(obj instanceof PoolKey other)) { | |
| + return false; | |
| + } | |
| + | |
| + return objectsEqual(object, other.object); | |
| + } | |
| + | |
| + @Override | |
| + public int hashCode() { | |
| + return hashCode; | |
| + } | |
| + | |
| + /** | |
| + * Custom equality check for different object types. | |
| + */ | |
| + private static boolean objectsEqual(Object obj1, Object obj2) { | |
| + if (obj1 == obj2) { | |
| + return true; | |
| + } | |
| + if (obj1 == null || obj2 == null) { | |
| + return false; | |
| + } | |
| + if (obj1.getClass() != obj2.getClass()) { | |
| + return false; | |
| + } | |
| + | |
| + // Custom equality for Dependency objects | |
| + if (obj1 instanceof org.apache.maven.api.model.Dependency) { | |
| + return dependenciesEqual( | |
| + (org.apache.maven.api.model.Dependency) obj1, (org.apache.maven.api.model.Dependency) obj2); | |
| + } | |
| + | |
| + // For other objects, use default equals | |
| + return obj1.equals(obj2); | |
| + } | |
| + | |
| + /** | |
| + * Custom equality check for Dependency objects based on all fields. | |
| + */ | |
| + private static boolean dependenciesEqual( | |
| + org.apache.maven.api.model.Dependency dep1, org.apache.maven.api.model.Dependency dep2) { | |
| + return Objects.equals(dep1.getGroupId(), dep2.getGroupId()) | |
| + && Objects.equals(dep1.getArtifactId(), dep2.getArtifactId()) | |
| + && Objects.equals(dep1.getVersion(), dep2.getVersion()) | |
| + && Objects.equals(dep1.getType(), dep2.getType()) | |
| + && Objects.equals(dep1.getClassifier(), dep2.getClassifier()) | |
| + && Objects.equals(dep1.getScope(), dep2.getScope()) | |
| + && Objects.equals(dep1.getSystemPath(), dep2.getSystemPath()) | |
| + && Objects.equals(dep1.getExclusions(), dep2.getExclusions()) | |
| + && Objects.equals(dep1.getOptional(), dep2.getOptional()) | |
| + && Objects.equals(dep1.getLocationKeys(), dep2.getLocationKeys()) | |
| + && locationsEqual(dep1, dep2) | |
| + && Objects.equals(dep1.getImportedFrom(), dep2.getImportedFrom()); | |
| + } | |
| + | |
| + /** | |
| + * Compare locations maps for two dependencies. | |
| + */ | |
| + private static boolean locationsEqual( | |
| + org.apache.maven.api.model.Dependency dep1, org.apache.maven.api.model.Dependency dep2) { | |
| + var keys1 = dep1.getLocationKeys(); | |
| + var keys2 = dep2.getLocationKeys(); | |
| + | |
| + if (!Objects.equals(keys1, keys2)) { | |
| + return false; | |
| + } | |
| + | |
| + for (Object key : keys1) { | |
| + if (!Objects.equals(dep1.getLocation(key), dep2.getLocation(key))) { | |
| + return false; | |
| + } | |
| + } | |
| + return true; | |
| + } | |
| + | |
| + /** | |
| + * Custom hash code computation for different object types. | |
| + */ | |
| + private static int computeHashCode(Object obj) { | |
| + if (obj instanceof org.apache.maven.api.model.Dependency) { | |
| + return dependencyHashCode((org.apache.maven.api.model.Dependency) obj); | |
| + } | |
| + return obj.hashCode(); | |
| + } | |
| + | |
| + /** | |
| + * Custom hash code for Dependency objects based on all fields. | |
| + */ | |
| + private static int dependencyHashCode(org.apache.maven.api.model.Dependency dep) { | |
| + return Objects.hash( | |
| + dep.getGroupId(), | |
| + dep.getArtifactId(), | |
| + dep.getVersion(), | |
| + dep.getType(), | |
| + dep.getClassifier(), | |
| + dep.getScope(), | |
| + dep.getSystemPath(), | |
| + dep.getExclusions(), | |
| + dep.getOptional(), | |
| + dep.getLocationKeys(), | |
| + locationsHashCode(dep), | |
| + dep.getImportedFrom()); | |
| + } | |
| + | |
| + /** | |
| + * Compute hash code for locations map. | |
| + */ | |
| + private static int locationsHashCode(org.apache.maven.api.model.Dependency dep) { | |
| + int hash = 1; | |
| + for (Object key : dep.getLocationKeys()) { | |
| + hash = 31 * hash + Objects.hashCode(key); | |
| + hash = 31 * hash + Objects.hashCode(dep.getLocation(key)); | |
| + } | |
| + return hash; | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * Get statistics for a specific object type. | |
| + * Useful for monitoring and debugging. | |
| + */ | |
| + public static String getStatistics(Class<?> objectType) { | |
| + AtomicLong totalCalls = TOTAL_CALLS.get(objectType); | |
| + AtomicLong hits = CACHE_HITS.get(objectType); | |
| + AtomicLong misses = CACHE_MISSES.get(objectType); | |
| + | |
| + if (totalCalls == null) { | |
| + return objectType.getSimpleName() + ": No statistics available"; | |
| + } | |
| + | |
| + long total = totalCalls.get(); | |
| + long hitCount = hits != null ? hits.get() : 0; | |
| + long missCount = misses != null ? misses.get() : 0; | |
| + double hitRatio = total > 0 ? (double) hitCount / total : 0.0; | |
| + | |
| + return String.format( | |
| + "%s: Total=%d, Hits=%d, Misses=%d, Hit Ratio=%.2f%%", | |
| + objectType.getSimpleName(), total, hitCount, missCount, hitRatio * 100); | |
| + } | |
| + | |
| + /** | |
| + * Get statistics for all pooled object types. | |
| + */ | |
| + public static String getAllStatistics() { | |
| + StringBuilder sb = new StringBuilder("ModelObjectPool Statistics:\n"); | |
| + for (Class<?> type : OBJECT_POOLS.keySet()) { | |
| + sb.append(" ").append(getStatistics(type)).append("\n"); | |
| + } | |
| + return sb.toString(); | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java | |
| index d087dcee83..c3bc8e7320 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java | |
| @@ -39,6 +39,7 @@ | |
| import java.util.stream.Collectors; | |
| import java.util.stream.StreamSupport; | |
| +import org.apache.maven.api.DependencyScope; | |
| import org.apache.maven.api.Session; | |
| import org.apache.maven.api.annotations.Nullable; | |
| import org.apache.maven.api.di.Inject; | |
| @@ -77,7 +78,6 @@ | |
| import org.apache.maven.impl.InternalSession; | |
| import org.apache.maven.model.v4.MavenModelVersion; | |
| import org.apache.maven.model.v4.MavenTransformer; | |
| -import org.eclipse.aether.scope.DependencyScope; | |
| import org.eclipse.aether.scope.ScopeManager; | |
| /** | |
| @@ -302,9 +302,9 @@ public DefaultModelValidator() {} | |
| @Override | |
| @SuppressWarnings("checkstyle:MethodLength") | |
| - public void validateFileModel(Session s, Model m, int validationLevel, ModelProblemCollector problems) { | |
| + public void validateFileModel(Session session, Model model, int validationLevel, ModelProblemCollector problems) { | |
| - Parent parent = m.getParent(); | |
| + Parent parent = model.getParent(); | |
| if (parent != null) { | |
| validateStringNotEmpty( | |
| "parent.groupId", problems, Severity.FATAL, Version.BASE, parent.getGroupId(), parent); | |
| @@ -312,7 +312,8 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| validateStringNotEmpty( | |
| "parent.artifactId", problems, Severity.FATAL, Version.BASE, parent.getArtifactId(), parent); | |
| - if (equals(parent.getGroupId(), m.getGroupId()) && equals(parent.getArtifactId(), m.getArtifactId())) { | |
| + if (equals(parent.getGroupId(), model.getGroupId()) | |
| + && equals(parent.getArtifactId(), model.getArtifactId())) { | |
| addViolation( | |
| problems, | |
| Severity.FATAL, | |
| @@ -341,8 +342,8 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| || parent.getArtifactId() != null | |
| && !parent.getArtifactId().isEmpty()) | |
| && validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_4_0 | |
| - && ModelBuilder.KNOWN_MODEL_VERSIONS.contains(m.getModelVersion()) | |
| - && !Objects.equals(m.getModelVersion(), ModelBuilder.MODEL_VERSION_4_0_0)) { | |
| + && ModelBuilder.KNOWN_MODEL_VERSIONS.contains(model.getModelVersion()) | |
| + && !Objects.equals(model.getModelVersion(), ModelBuilder.MODEL_VERSION_4_0_0)) { | |
| addViolation( | |
| problems, | |
| Severity.WARNING, | |
| @@ -354,10 +355,47 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| } | |
| } | |
| + // Validate mixins | |
| + if (!model.getMixins().isEmpty()) { | |
| + // Ensure model version is at least 4.2.0 when using mixins | |
| + if (compareModelVersions("4.2.0", model.getModelVersion()) < 0) { | |
| + addViolation( | |
| + problems, | |
| + Severity.ERROR, | |
| + Version.V40, | |
| + "mixins", | |
| + null, | |
| + "Mixins are only supported in modelVersion 4.2.0 or higher, but found '" | |
| + + model.getModelVersion() + "'.", | |
| + model); | |
| + } | |
| + | |
| + // Validate each mixin | |
| + for (Parent mixin : model.getMixins()) { | |
| + if (mixin.getRelativePath() != null | |
| + && !mixin.getRelativePath().isEmpty() | |
| + && (mixin.getGroupId() != null && !mixin.getGroupId().isEmpty() | |
| + || mixin.getArtifactId() != null | |
| + && !mixin.getArtifactId().isEmpty()) | |
| + && validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_4_0 | |
| + && ModelBuilder.KNOWN_MODEL_VERSIONS.contains(model.getModelVersion()) | |
| + && !Objects.equals(model.getModelVersion(), ModelBuilder.MODEL_VERSION_4_0_0)) { | |
| + addViolation( | |
| + problems, | |
| + Severity.WARNING, | |
| + Version.BASE, | |
| + "mixins.mixin.relativePath", | |
| + null, | |
| + "only specify relativePath or groupId/artifactId for mixin", | |
| + mixin); | |
| + } | |
| + } | |
| + } | |
| + | |
| if (validationLevel == ModelValidator.VALIDATION_LEVEL_MINIMAL) { | |
| // profiles: they are essential for proper model building (may contribute profiles, dependencies...) | |
| HashSet<String> minProfileIds = new HashSet<>(); | |
| - for (Profile profile : m.getProfiles()) { | |
| + for (Profile profile : model.getProfiles()) { | |
| if (!minProfileIds.add(profile.getId())) { | |
| addViolation( | |
| problems, | |
| @@ -370,27 +408,28 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| } | |
| } | |
| } else if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { | |
| - validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), m); | |
| + validateStringNotEmpty( | |
| + "modelVersion", problems, Severity.ERROR, Version.V20, model.getModelVersion(), model); | |
| - validateModelVersion(problems, m.getModelVersion(), m, ModelBuilder.KNOWN_MODEL_VERSIONS); | |
| + validateModelVersion(session, problems, model.getModelVersion(), model, ModelBuilder.KNOWN_MODEL_VERSIONS); | |
| Set<String> modules = new HashSet<>(); | |
| - for (int i = 0, n = m.getModules().size(); i < n; i++) { | |
| - String module = m.getModules().get(i); | |
| + for (int index = 0, size = model.getModules().size(); index < size; index++) { | |
| + String module = model.getModules().get(index); | |
| if (!modules.add(module)) { | |
| addViolation( | |
| problems, | |
| Severity.ERROR, | |
| Version.V20, | |
| - "modules.module[" + i + "]", | |
| + "modules.module[" + index + "]", | |
| null, | |
| "specifies duplicate child module " + module, | |
| - m.getLocation("modules")); | |
| + model.getLocation("modules")); | |
| } | |
| } | |
| - String modelVersion = m.getModelVersion(); | |
| + String modelVersion = model.getModelVersion(); | |
| if (Objects.equals(modelVersion, ModelBuilder.MODEL_VERSION_4_0_0)) { | |
| - if (!m.getSubprojects().isEmpty()) { | |
| + if (!model.getSubprojects().isEmpty()) { | |
| addViolation( | |
| problems, | |
| Severity.ERROR, | |
| @@ -398,21 +437,21 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| "subprojects", | |
| null, | |
| "unexpected subprojects element", | |
| - m.getLocation("subprojects")); | |
| + model.getLocation("subprojects")); | |
| } | |
| } else { | |
| Set<String> subprojects = new HashSet<>(); | |
| - for (int i = 0, n = m.getSubprojects().size(); i < n; i++) { | |
| - String subproject = m.getSubprojects().get(i); | |
| + for (int index = 0, size = model.getSubprojects().size(); index < size; index++) { | |
| + String subproject = model.getSubprojects().get(index); | |
| if (!subprojects.add(subproject)) { | |
| addViolation( | |
| problems, | |
| Severity.ERROR, | |
| Version.V41, | |
| - "subprojects.subproject[" + i + "]", | |
| + "subprojects.subproject[" + index + "]", | |
| null, | |
| "specifies duplicate subproject " + subproject, | |
| - m.getLocation("subprojects")); | |
| + model.getLocation("subprojects")); | |
| } | |
| } | |
| if (!modules.isEmpty()) { | |
| @@ -424,7 +463,7 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| "modules", | |
| null, | |
| "deprecated modules element, use subprojects instead", | |
| - m.getLocation("modules")); | |
| + model.getLocation("modules")); | |
| } else { | |
| addViolation( | |
| problems, | |
| @@ -433,68 +472,77 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| "modules", | |
| null, | |
| "cannot use both modules and subprojects element", | |
| - m.getLocation("modules")); | |
| + model.getLocation("modules")); | |
| } | |
| } | |
| } | |
| Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); | |
| - boolean isModelVersion41OrMore = !Objects.equals(ModelBuilder.MODEL_VERSION_4_0_0, m.getModelVersion()); | |
| + boolean isModelVersion41OrMore = !Objects.equals(ModelBuilder.MODEL_VERSION_4_0_0, model.getModelVersion()); | |
| if (isModelVersion41OrMore) { | |
| - validateStringNoExpression("groupId", problems, Severity.FATAL, Version.V41, m.getGroupId(), m); | |
| + validateStringNoExpression("groupId", problems, Severity.FATAL, Version.V41, model.getGroupId(), model); | |
| - validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m); | |
| - validateStringNoExpression("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m); | |
| + validateStringNotEmpty( | |
| + "artifactId", problems, Severity.FATAL, Version.V20, model.getArtifactId(), model); | |
| + validateStringNoExpression( | |
| + "artifactId", problems, Severity.FATAL, Version.V20, model.getArtifactId(), model); | |
| - validateVersionNoExpression("version", problems, Severity.FATAL, Version.V41, m.getVersion(), m); | |
| + validateVersionNoExpression( | |
| + "version", problems, Severity.FATAL, Version.V41, model.getVersion(), model); | |
| if (parent != null) { | |
| validateStringNoExpression( | |
| - "groupId", problems, Severity.FATAL, Version.V41, parent.getGroupId(), m); | |
| + "groupId", problems, Severity.FATAL, Version.V41, parent.getGroupId(), model); | |
| validateStringNoExpression( | |
| - "artifactId", problems, Severity.FATAL, Version.V41, parent.getArtifactId(), m); | |
| + "artifactId", problems, Severity.FATAL, Version.V41, parent.getArtifactId(), model); | |
| validateVersionNoExpression( | |
| - "version", problems, Severity.FATAL, Version.V41, parent.getVersion(), m); | |
| + "version", problems, Severity.FATAL, Version.V41, parent.getVersion(), model); | |
| } | |
| } else { | |
| - validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m); | |
| + validateStringNoExpression( | |
| + "groupId", problems, Severity.WARNING, Version.V20, model.getGroupId(), model); | |
| if (parent == null) { | |
| - validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, m.getGroupId(), m); | |
| + validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, model.getGroupId(), model); | |
| } | |
| - validateStringNoExpression("artifactId", problems, Severity.WARNING, Version.V20, m.getArtifactId(), m); | |
| - validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m); | |
| + validateStringNoExpression( | |
| + "artifactId", problems, Severity.WARNING, Version.V20, model.getArtifactId(), model); | |
| + validateStringNotEmpty( | |
| + "artifactId", problems, Severity.FATAL, Version.V20, model.getArtifactId(), model); | |
| - validateVersionNoExpression("version", problems, Severity.WARNING, Version.V20, m.getVersion(), m); | |
| + validateVersionNoExpression( | |
| + "version", problems, Severity.WARNING, Version.V20, model.getVersion(), model); | |
| if (parent == null) { | |
| - validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m); | |
| + validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, model.getVersion(), model); | |
| } | |
| } | |
| - validateStringNoExpression("packaging", problems, Severity.WARNING, Version.V20, m.getPackaging(), m); | |
| + validateStringNoExpression( | |
| + "packaging", problems, Severity.WARNING, Version.V20, model.getPackaging(), model); | |
| validate20RawDependencies( | |
| problems, | |
| - m.getDependencies(), | |
| + model.getDependencies(), | |
| "dependencies.dependency.", | |
| EMPTY, | |
| isModelVersion41OrMore, | |
| validationLevel); | |
| - validate20RawDependenciesSelfReferencing(problems, m, m.getDependencies(), "dependencies.dependency"); | |
| + validate20RawDependenciesSelfReferencing( | |
| + problems, model, model.getDependencies(), "dependencies.dependency"); | |
| - if (m.getDependencyManagement() != null) { | |
| + if (model.getDependencyManagement() != null) { | |
| validate20RawDependencies( | |
| problems, | |
| - m.getDependencyManagement().getDependencies(), | |
| + model.getDependencyManagement().getDependencies(), | |
| "dependencyManagement.dependencies.dependency.", | |
| EMPTY, | |
| isModelVersion41OrMore, | |
| validationLevel); | |
| } | |
| - Build build = m.getBuild(); | |
| + Build build = model.getBuild(); | |
| if (build != null) { | |
| validate20RawPlugins(problems, build.getPlugins(), "build.plugins.plugin.", EMPTY, validationLevel); | |
| @@ -511,10 +559,10 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| Set<String> profileIds = new HashSet<>(); | |
| - for (Profile profile : m.getProfiles()) { | |
| + for (Profile profile : model.getProfiles()) { | |
| String prefix = "profiles.profile[" + profile.getId() + "]."; | |
| - validateProfileId(prefix, "id", problems, Severity.ERROR, Version.V40, profile.getId(), null, m); | |
| + validateProfileId(prefix, "id", problems, Severity.ERROR, Version.V40, profile.getId(), null, model); | |
| if (!profileIds.add(profile.getId())) { | |
| addViolation( | |
| @@ -566,11 +614,11 @@ public void validateFileModel(Session s, Model m, int validationLevel, ModelProb | |
| } | |
| @Override | |
| - public void validateRawModel(Session s, Model m, int validationLevel, ModelProblemCollector problems) { | |
| + public void validateRawModel(Session session, Model model, int validationLevel, ModelProblemCollector problems) { | |
| // Check that the model version is correctly set wrt the model definition, i.e., that the | |
| // user does not use an attribute or element that is not available in the modelVersion used. | |
| - String minVersion = new MavenModelVersion().getModelVersion(m); | |
| - if (m.getModelVersion() != null && compareModelVersions(minVersion, m.getModelVersion()) > 0) { | |
| + String minVersion = new MavenModelVersion().getModelVersion(model); | |
| + if (model.getModelVersion() != null && compareModelVersions(minVersion, model.getModelVersion()) > 0) { | |
| addViolation( | |
| problems, | |
| Severity.FATAL, | |
| @@ -578,10 +626,10 @@ public void validateRawModel(Session s, Model m, int validationLevel, ModelProbl | |
| "model", | |
| null, | |
| "the model contains elements that require a model version of " + minVersion, | |
| - m); | |
| + model); | |
| } | |
| - Parent parent = m.getParent(); | |
| + Parent parent = model.getParent(); | |
| if (parent != null) { | |
| validateStringNotEmpty( | |
| @@ -593,7 +641,8 @@ public void validateRawModel(Session s, Model m, int validationLevel, ModelProbl | |
| validateStringNotEmpty( | |
| "parent.version", problems, Severity.FATAL, Version.BASE, parent.getVersion(), parent); | |
| - if (equals(parent.getGroupId(), m.getGroupId()) && equals(parent.getArtifactId(), m.getArtifactId())) { | |
| + if (equals(parent.getGroupId(), model.getGroupId()) | |
| + && equals(parent.getArtifactId(), model.getArtifactId())) { | |
| addViolation( | |
| problems, | |
| Severity.FATAL, | |
| @@ -618,16 +667,17 @@ public void validateRawModel(Session s, Model m, int validationLevel, ModelProbl | |
| } | |
| if (validationLevel > VALIDATION_LEVEL_MINIMAL) { | |
| - validateRawRepositories(problems, m.getRepositories(), "repositories.repository.", EMPTY, validationLevel); | |
| + validateRawRepositories( | |
| + problems, model.getRepositories(), "repositories.repository.", EMPTY, validationLevel); | |
| validateRawRepositories( | |
| problems, | |
| - m.getPluginRepositories(), | |
| + model.getPluginRepositories(), | |
| "pluginRepositories.pluginRepository.", | |
| EMPTY, | |
| validationLevel); | |
| - for (Profile profile : m.getProfiles()) { | |
| + for (Profile profile : model.getProfiles()) { | |
| String prefix = "profiles.profile[" + profile.getId() + "]."; | |
| validateRawRepositories( | |
| @@ -641,7 +691,7 @@ public void validateRawModel(Session s, Model m, int validationLevel, ModelProbl | |
| validationLevel); | |
| } | |
| - DistributionManagement distMgmt = m.getDistributionManagement(); | |
| + DistributionManagement distMgmt = model.getDistributionManagement(); | |
| if (distMgmt != null) { | |
| validateRawRepository( | |
| problems, distMgmt.getRepository(), "distributionManagement.repository.", "", true); | |
| @@ -672,17 +722,19 @@ private void validate30RawProfileActivation(ModelProblemCollector problems, Acti | |
| if (stk.size() < 2) { | |
| return null; | |
| } | |
| - Iterator<ActivationFrame> f = stk.iterator(); | |
| + Iterator<ActivationFrame> frameIterator = stk.iterator(); | |
| - String location = f.next().location; | |
| - ActivationFrame parent = f.next(); | |
| + String location = frameIterator.next().location; | |
| + ActivationFrame parent = frameIterator.next(); | |
| - return parent.parent.map(p -> p.getLocation(location)).orElse(null); | |
| + return parent.parent | |
| + .map(parentTracker -> parentTracker.getLocation(location)) | |
| + .orElse(null); | |
| }; | |
| - final UnaryOperator<String> transformer = s -> { | |
| - if (hasProjectExpression(s)) { | |
| + final UnaryOperator<String> transformer = stringValue -> { | |
| + if (hasProjectExpression(stringValue)) { | |
| String path = pathSupplier.get(); | |
| - Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s); | |
| + Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(stringValue); | |
| while (matcher.find()) { | |
| String propertyName = matcher.group(0); | |
| @@ -695,12 +747,12 @@ private void validate30RawProfileActivation(ModelProblemCollector problems, Acti | |
| Version.V30, | |
| prefix + path, | |
| null, | |
| - "Failed to interpolate profile activation property " + s + ": " + propertyName | |
| + "Failed to interpolate profile activation property " + stringValue + ": " + propertyName | |
| + " expressions are not supported during profile activation.", | |
| locationSupplier.get()); | |
| } | |
| } | |
| - return s; | |
| + return stringValue; | |
| }; | |
| new ActivationWalker(stk, transformer).transformActivation(activation); | |
| } | |
| @@ -848,35 +900,36 @@ private void validateXmlNode( | |
| @Override | |
| @SuppressWarnings("checkstyle:MethodLength") | |
| - public void validateEffectiveModel(Session s, Model m, int validationLevel, ModelProblemCollector problems) { | |
| - validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.BASE, m.getModelVersion(), m); | |
| + public void validateEffectiveModel( | |
| + Session session, Model model, int validationLevel, ModelProblemCollector problems) { | |
| + validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.BASE, model.getModelVersion(), model); | |
| - validateCoordinatesId("groupId", problems, m.getGroupId(), m); | |
| + validateCoordinatesId("groupId", problems, model.getGroupId(), model); | |
| - validateCoordinatesId("artifactId", problems, m.getArtifactId(), m); | |
| + validateCoordinatesId("artifactId", problems, model.getArtifactId(), model); | |
| - validateStringNotEmpty("packaging", problems, Severity.ERROR, Version.BASE, m.getPackaging(), m); | |
| + validateStringNotEmpty("packaging", problems, Severity.ERROR, Version.BASE, model.getPackaging(), model); | |
| - if (!m.getModules().isEmpty()) { | |
| - if (!"pom".equals(m.getPackaging())) { | |
| + if (!model.getModules().isEmpty()) { | |
| + if (!"pom".equals(model.getPackaging())) { | |
| addViolation( | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| "packaging", | |
| null, | |
| - "with value '" + m.getPackaging() + "' is invalid. Aggregator projects " | |
| + "with value '" + model.getPackaging() + "' is invalid. Aggregator projects " | |
| + "require 'pom' as packaging.", | |
| - m); | |
| + model); | |
| } | |
| - for (int i = 0, n = m.getModules().size(); i < n; i++) { | |
| - String module = m.getModules().get(i); | |
| + for (int index = 0, size = model.getModules().size(); index < size; index++) { | |
| + String module = model.getModules().get(index); | |
| boolean isBlankModule = true; | |
| if (module != null) { | |
| - for (int j = 0; j < module.length(); j++) { | |
| - if (!Character.isWhitespace(module.charAt(j))) { | |
| + for (int charIndex = 0; charIndex < module.length(); charIndex++) { | |
| + if (!Character.isWhitespace(module.charAt(charIndex))) { | |
| isBlankModule = false; | |
| } | |
| } | |
| @@ -887,36 +940,44 @@ public void validateEffectiveModel(Session s, Model m, int validationLevel, Mode | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| - "modules.module[" + i + "]", | |
| + "modules.module[" + index + "]", | |
| null, | |
| "has been specified without a path to the project directory.", | |
| - m.getLocation("modules")); | |
| + model.getLocation("modules")); | |
| } | |
| } | |
| } | |
| - validateStringNotEmpty("version", problems, Severity.ERROR, Version.BASE, m.getVersion(), m); | |
| + validateStringNotEmpty("version", problems, Severity.ERROR, Version.BASE, model.getVersion(), model); | |
| Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); | |
| - validateEffectiveDependencies(s, problems, m, m.getDependencies(), false, validationLevel); | |
| + validateEffectiveDependencies(session, problems, model, model.getDependencies(), false, validationLevel); | |
| - DependencyManagement mgmt = m.getDependencyManagement(); | |
| + DependencyManagement mgmt = model.getDependencyManagement(); | |
| if (mgmt != null) { | |
| - validateEffectiveDependencies(s, problems, m, mgmt.getDependencies(), true, validationLevel); | |
| + validateEffectiveDependencies(session, problems, model, mgmt.getDependencies(), true, validationLevel); | |
| } | |
| 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); | |
| - validate20ProperSnapshotVersion("version", problems, errOn31, Version.V20, m.getVersion(), null, m); | |
| - if (hasExpression(m.getVersion())) { | |
| + EMPTY, | |
| + "version", | |
| + problems, | |
| + errOn31, | |
| + Version.V20, | |
| + model.getVersion(), | |
| + null, | |
| + model, | |
| + ILLEGAL_VERSION_CHARS); | |
| + validate20ProperSnapshotVersion("version", problems, errOn31, Version.V20, model.getVersion(), null, model); | |
| + if (hasExpression(model.getVersion())) { | |
| Severity versionExpressionSeverity = Severity.ERROR; | |
| - if (m.getProperties() != null | |
| + if (model.getProperties() != null | |
| && Boolean.parseBoolean( | |
| - m.getProperties().get(BUILD_ALLOW_EXPRESSION_IN_EFFECTIVE_PROJECT_VERSION))) { | |
| + model.getProperties().get(BUILD_ALLOW_EXPRESSION_IN_EFFECTIVE_PROJECT_VERSION))) { | |
| versionExpressionSeverity = Severity.WARNING; | |
| } | |
| addViolation( | |
| @@ -925,30 +986,35 @@ public void validateEffectiveModel(Session s, Model m, int validationLevel, Mode | |
| Version.V20, | |
| "version", | |
| null, | |
| - "must be a constant version but is '" + m.getVersion() + "'.", | |
| - m); | |
| + "must be a constant version but is '" + model.getVersion() + "'.", | |
| + model); | |
| } | |
| - Build build = m.getBuild(); | |
| + Build build = model.getBuild(); | |
| if (build != null) { | |
| - for (Plugin p : build.getPlugins()) { | |
| + for (Plugin plugin : build.getPlugins()) { | |
| validateStringNotEmpty( | |
| "build.plugins.plugin.artifactId", | |
| problems, | |
| Severity.ERROR, | |
| Version.V20, | |
| - p.getArtifactId(), | |
| - p); | |
| + plugin.getArtifactId(), | |
| + plugin); | |
| validateStringNotEmpty( | |
| - "build.plugins.plugin.groupId", problems, Severity.ERROR, Version.V20, p.getGroupId(), p); | |
| + "build.plugins.plugin.groupId", | |
| + problems, | |
| + Severity.ERROR, | |
| + Version.V20, | |
| + plugin.getGroupId(), | |
| + plugin); | |
| validate20PluginVersion( | |
| "build.plugins.plugin.version", | |
| problems, | |
| - p.getVersion(), | |
| - SourceHint.pluginKey(p), | |
| - p, | |
| + plugin.getVersion(), | |
| + SourceHint.pluginKey(plugin), | |
| + plugin, | |
| validationLevel); | |
| validateBoolean( | |
| @@ -957,9 +1023,9 @@ public void validateEffectiveModel(Session s, Model m, int validationLevel, Mode | |
| problems, | |
| errOn30, | |
| Version.V20, | |
| - p.getInherited(), | |
| - SourceHint.pluginKey(p), | |
| - p); | |
| + plugin.getInherited(), | |
| + SourceHint.pluginKey(plugin), | |
| + plugin); | |
| validateBoolean( | |
| "build.plugins.plugin.extensions", | |
| @@ -967,11 +1033,11 @@ public void validateEffectiveModel(Session s, Model m, int validationLevel, Mode | |
| problems, | |
| errOn30, | |
| Version.V20, | |
| - p.getExtensions(), | |
| - SourceHint.pluginKey(p), | |
| - p); | |
| + plugin.getExtensions(), | |
| + SourceHint.pluginKey(plugin), | |
| + plugin); | |
| - validate20EffectivePluginDependencies(problems, p, validationLevel); | |
| + validate20EffectivePluginDependencies(problems, plugin, validationLevel); | |
| } | |
| validate20RawResources(problems, build.getResources(), "build.resources.resource.", validationLevel); | |
| @@ -980,37 +1046,37 @@ public void validateEffectiveModel(Session s, Model m, int validationLevel, Mode | |
| problems, build.getTestResources(), "build.testResources.testResource.", validationLevel); | |
| } | |
| - Reporting reporting = m.getReporting(); | |
| + Reporting reporting = model.getReporting(); | |
| if (reporting != null) { | |
| - for (ReportPlugin p : reporting.getPlugins()) { | |
| + for (ReportPlugin reportPlugin : reporting.getPlugins()) { | |
| validateStringNotEmpty( | |
| "reporting.plugins.plugin.artifactId", | |
| problems, | |
| Severity.ERROR, | |
| Version.V20, | |
| - p.getArtifactId(), | |
| - p); | |
| + reportPlugin.getArtifactId(), | |
| + reportPlugin); | |
| validateStringNotEmpty( | |
| "reporting.plugins.plugin.groupId", | |
| problems, | |
| Severity.ERROR, | |
| Version.V20, | |
| - p.getGroupId(), | |
| - p); | |
| + reportPlugin.getGroupId(), | |
| + reportPlugin); | |
| } | |
| } | |
| - for (Repository repository : m.getRepositories()) { | |
| + for (Repository repository : model.getRepositories()) { | |
| validate20EffectiveRepository(problems, repository, "repositories.repository.", validationLevel); | |
| } | |
| - for (Repository repository : m.getPluginRepositories()) { | |
| + for (Repository repository : model.getPluginRepositories()) { | |
| validate20EffectiveRepository( | |
| problems, repository, "pluginRepositories.pluginRepository.", validationLevel); | |
| } | |
| - DistributionManagement distMgmt = m.getDistributionManagement(); | |
| + DistributionManagement distMgmt = model.getDistributionManagement(); | |
| if (distMgmt != null) { | |
| if (distMgmt.getStatus() != null) { | |
| addViolation( | |
| @@ -1109,6 +1175,25 @@ private void validate20RawDependencies( | |
| } | |
| } | |
| + // MNG-8750: New dependency scopes are only supported starting with modelVersion 4.1.0 | |
| + // When using modelVersion 4.0.0, fail validation if one of the new scopes is present | |
| + if (!is41OrBeyond) { | |
| + String scope = dependency.getScope(); | |
| + if (DependencyScope.COMPILE_ONLY.id().equals(scope) | |
| + || DependencyScope.TEST_ONLY.id().equals(scope) | |
| + || DependencyScope.TEST_RUNTIME.id().equals(scope)) { | |
| + addViolation( | |
| + problems, | |
| + Severity.ERROR, | |
| + Version.V20, | |
| + prefix + prefix2 + "scope", | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + "scope '" + scope + "' is not supported with modelVersion 4.0.0; " | |
| + + "use modelVersion 4.1.0 or remove this scope.", | |
| + dependency); | |
| + } | |
| + } | |
| + | |
| if (equals("LATEST", dependency.getVersion()) || equals("RELEASE", dependency.getVersion())) { | |
| addViolation( | |
| problems, | |
| @@ -1146,7 +1231,7 @@ private void validate20RawDependencies( | |
| } | |
| private void validate20RawDependenciesSelfReferencing( | |
| - ModelProblemCollector problems, Model m, List<Dependency> dependencies, String prefix) { | |
| + ModelProblemCollector problems, Model model, List<Dependency> dependencies, String prefix) { | |
| // We only check for groupId/artifactId/version/classifier cause if there is another | |
| // module with the same groupId/artifactId/version/classifier this will fail the build | |
| // earlier like "Project '...' is duplicated in the reactor. | |
| @@ -1155,8 +1240,8 @@ private void validate20RawDependenciesSelfReferencing( | |
| for (Dependency dependency : dependencies) { | |
| String key = dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion() | |
| + (dependency.getClassifier() != null ? ":" + dependency.getClassifier() : EMPTY); | |
| - String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion(); | |
| - if (key.equals(mKey)) { | |
| + String modelKey = model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion(); | |
| + if (key.equals(modelKey)) { | |
| // This means a module which is build has a dependency which has the same | |
| // groupId, artifactId, version and classifier coordinates. This is in consequence | |
| // a self reference or in other words a circular reference which can not being resolved. | |
| @@ -1173,9 +1258,9 @@ private void validate20RawDependenciesSelfReferencing( | |
| } | |
| private void validateEffectiveDependencies( | |
| - Session s, | |
| + Session session, | |
| ModelProblemCollector problems, | |
| - Model m, | |
| + Model model, | |
| List<Dependency> dependencies, | |
| boolean management, | |
| int validationLevel) { | |
| @@ -1183,8 +1268,8 @@ private void validateEffectiveDependencies( | |
| String prefix = management ? "dependencyManagement.dependencies.dependency." : "dependencies.dependency."; | |
| - for (Dependency d : dependencies) { | |
| - validateEffectiveDependency(problems, d, management, prefix, validationLevel); | |
| + for (Dependency dependency : dependencies) { | |
| + validateEffectiveDependency(problems, dependency, management, prefix, validationLevel); | |
| if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { | |
| validateBoolean( | |
| @@ -1193,9 +1278,9 @@ private void validateEffectiveDependencies( | |
| problems, | |
| errOn30, | |
| Version.V20, | |
| - d.getOptional(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getOptional(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| if (!management) { | |
| validateVersion( | |
| @@ -1204,59 +1289,61 @@ private void validateEffectiveDependencies( | |
| problems, | |
| errOn30, | |
| Version.V20, | |
| - d.getVersion(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getVersion(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| /* | |
| - * TODO Extensions like Flex Mojos use custom scopes like "merged", "internal", "external", etc. In | |
| - * order to don't break backward-compat with those, only warn but don't error out. | |
| + * Extensions like Flex Mojos use custom scopes like "merged", "internal", "external", etc. In | |
| + * order to not break backward-compat with those, only warn but don't error out. | |
| */ | |
| ScopeManager scopeManager = | |
| - InternalSession.from(s).getSession().getScopeManager(); | |
| - validateEnum( | |
| + InternalSession.from(session).getSession().getScopeManager(); | |
| + validateDependencyScope( | |
| prefix, | |
| "scope", | |
| problems, | |
| Severity.WARNING, | |
| Version.V20, | |
| - d.getScope(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d, | |
| + dependency.getScope(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency, | |
| scopeManager.getDependencyScopeUniverse().stream() | |
| - .map(DependencyScope::getId) | |
| + .map(org.eclipse.aether.scope.DependencyScope::getId) | |
| .distinct() | |
| - .toArray(String[]::new)); | |
| + .toArray(String[]::new), | |
| + false); | |
| - validateEffectiveModelAgainstDependency(prefix, problems, m, d); | |
| + validateEffectiveModelAgainstDependency(prefix, problems, model, dependency); | |
| } else { | |
| ScopeManager scopeManager = | |
| - InternalSession.from(s).getSession().getScopeManager(); | |
| + InternalSession.from(session).getSession().getScopeManager(); | |
| Set<String> scopes = scopeManager.getDependencyScopeUniverse().stream() | |
| - .map(DependencyScope::getId) | |
| + .map(org.eclipse.aether.scope.DependencyScope::getId) | |
| .collect(Collectors.toCollection(HashSet::new)); | |
| scopes.add("import"); | |
| - validateEnum( | |
| + validateDependencyScope( | |
| prefix, | |
| "scope", | |
| problems, | |
| Severity.WARNING, | |
| Version.V20, | |
| - d.getScope(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d, | |
| - scopes.toArray(new String[0])); | |
| + dependency.getScope(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency, | |
| + scopes.toArray(new String[0]), | |
| + true); | |
| } | |
| } | |
| } | |
| } | |
| private void validateEffectiveModelAgainstDependency( | |
| - String prefix, ModelProblemCollector problems, Model m, Dependency d) { | |
| - String key = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion() | |
| - + (d.getClassifier() != null ? ":" + d.getClassifier() : EMPTY); | |
| - String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion(); | |
| - if (key.equals(mKey)) { | |
| + String prefix, ModelProblemCollector problems, Model model, Dependency dependency) { | |
| + String key = dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion() | |
| + + (dependency.getClassifier() != null ? ":" + dependency.getClassifier() : EMPTY); | |
| + String modelKey = model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion(); | |
| + if (key.equals(modelKey)) { | |
| // This means a module which is build has a dependency which has the same | |
| // groupId, artifactId, version and classifier coordinates. This is in consequence | |
| // a self reference or in other words a circular reference which can not being resolved. | |
| @@ -1267,7 +1354,7 @@ private void validateEffectiveModelAgainstDependency( | |
| prefix + "[" + key + "]", | |
| SourceHint.gav(key), | |
| "is referencing itself.", | |
| - d); | |
| + dependency); | |
| } | |
| } | |
| @@ -1280,8 +1367,8 @@ private void validate20EffectivePluginDependencies( | |
| Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0); | |
| - for (Dependency d : dependencies) { | |
| - validateEffectiveDependency(problems, d, false, prefix, validationLevel); | |
| + for (Dependency dependency : dependencies) { | |
| + validateEffectiveDependency(problems, dependency, false, prefix, validationLevel); | |
| validateVersion( | |
| prefix, | |
| @@ -1289,9 +1376,9 @@ private void validate20EffectivePluginDependencies( | |
| problems, | |
| errOn30, | |
| Version.BASE, | |
| - d.getVersion(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getVersion(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| validateEnum( | |
| prefix, | |
| @@ -1299,9 +1386,9 @@ private void validate20EffectivePluginDependencies( | |
| problems, | |
| errOn30, | |
| Version.BASE, | |
| - d.getScope(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d, | |
| + dependency.getScope(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency, | |
| "compile", | |
| "runtime", | |
| "system"); | |
| @@ -1310,16 +1397,20 @@ private void validate20EffectivePluginDependencies( | |
| } | |
| private void validateEffectiveDependency( | |
| - ModelProblemCollector problems, Dependency d, boolean management, String prefix, int validationLevel) { | |
| + ModelProblemCollector problems, | |
| + Dependency dependency, | |
| + boolean management, | |
| + String prefix, | |
| + int validationLevel) { | |
| validateCoordinatesId( | |
| prefix, | |
| "artifactId", | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| - d.getArtifactId(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getArtifactId(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| validateCoordinatesId( | |
| prefix, | |
| @@ -1327,9 +1418,9 @@ private void validateEffectiveDependency( | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| - d.getGroupId(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getGroupId(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| if (!management) { | |
| validateStringNotEmpty( | |
| @@ -1338,15 +1429,15 @@ private void validateEffectiveDependency( | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| - d.getType(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getType(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| - validateDependencyVersion(problems, d, prefix); | |
| + validateDependencyVersion(problems, dependency, prefix); | |
| } | |
| - if ("system".equals(d.getScope())) { | |
| - String systemPath = d.getSystemPath(); | |
| + if ("system".equals(dependency.getScope())) { | |
| + String systemPath = dependency.getSystemPath(); | |
| if (systemPath == null || systemPath.isEmpty()) { | |
| addViolation( | |
| @@ -1354,9 +1445,9 @@ private void validateEffectiveDependency( | |
| Severity.ERROR, | |
| Version.BASE, | |
| prefix + "systemPath", | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| "is missing.", | |
| - d); | |
| + dependency); | |
| } else { | |
| File sysFile = new File(systemPath); | |
| if (!sysFile.isAbsolute()) { | |
| @@ -1365,9 +1456,9 @@ private void validateEffectiveDependency( | |
| Severity.ERROR, | |
| Version.BASE, | |
| prefix + "systemPath", | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| "must specify an absolute path but is " + systemPath, | |
| - d); | |
| + dependency); | |
| } else if (!sysFile.isFile()) { | |
| String msg = "refers to a non-existing file " + sysFile.getAbsolutePath() + "."; | |
| addViolation( | |
| @@ -1375,24 +1466,25 @@ private void validateEffectiveDependency( | |
| Severity.WARNING, | |
| Version.BASE, | |
| prefix + "systemPath", | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| msg, | |
| - d); | |
| + dependency); | |
| } | |
| } | |
| - } else if (d.getSystemPath() != null && !d.getSystemPath().isEmpty()) { | |
| + } else if (dependency.getSystemPath() != null | |
| + && !dependency.getSystemPath().isEmpty()) { | |
| addViolation( | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| prefix + "systemPath", | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| "must be omitted. This field may only be specified for a dependency with system scope.", | |
| - d); | |
| + dependency); | |
| } | |
| if (validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_2_0) { | |
| - for (Exclusion exclusion : d.getExclusions()) { | |
| + for (Exclusion exclusion : dependency.getExclusions()) { | |
| if (validationLevel < ModelValidator.VALIDATION_LEVEL_MAVEN_3_0) { | |
| validateCoordinatesId( | |
| prefix, | |
| @@ -1401,7 +1493,7 @@ private void validateEffectiveDependency( | |
| Severity.WARNING, | |
| Version.V20, | |
| exclusion.getGroupId(), | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| exclusion); | |
| validateCoordinatesId( | |
| @@ -1411,7 +1503,7 @@ private void validateEffectiveDependency( | |
| Severity.WARNING, | |
| Version.V20, | |
| exclusion.getArtifactId(), | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| exclusion); | |
| } else { | |
| validateCoordinatesIdWithWildcards( | |
| @@ -1421,7 +1513,7 @@ private void validateEffectiveDependency( | |
| Severity.WARNING, | |
| Version.V30, | |
| exclusion.getGroupId(), | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| exclusion); | |
| validateCoordinatesIdWithWildcards( | |
| @@ -1431,7 +1523,7 @@ private void validateEffectiveDependency( | |
| Severity.WARNING, | |
| Version.V30, | |
| exclusion.getArtifactId(), | |
| - SourceHint.dependencyManagementKey(d), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| exclusion); | |
| } | |
| } | |
| @@ -1441,16 +1533,16 @@ private void validateEffectiveDependency( | |
| /** | |
| * @since 3.2.4 | |
| */ | |
| - protected void validateDependencyVersion(ModelProblemCollector problems, Dependency d, String prefix) { | |
| + protected void validateDependencyVersion(ModelProblemCollector problems, Dependency dependency, String prefix) { | |
| validateStringNotEmpty( | |
| prefix, | |
| "version", | |
| problems, | |
| Severity.ERROR, | |
| Version.BASE, | |
| - d.getVersion(), | |
| - SourceHint.dependencyManagementKey(d), | |
| - d); | |
| + dependency.getVersion(), | |
| + SourceHint.dependencyManagementKey(dependency), | |
| + dependency); | |
| } | |
| private void validateRawRepositories( | |
| @@ -1647,17 +1739,22 @@ private boolean validateCoordinatesId( | |
| } | |
| private boolean isValidCoordinatesId(String id) { | |
| - for (int i = 0; i < id.length(); i++) { | |
| - char c = id.charAt(i); | |
| - if (!isValidCoordinatesIdCharacter(c)) { | |
| + for (int index = 0; index < id.length(); index++) { | |
| + char character = id.charAt(index); | |
| + if (!isValidCoordinatesIdCharacter(character)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| - private boolean isValidCoordinatesIdCharacter(char c) { | |
| - return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.'; | |
| + private boolean isValidCoordinatesIdCharacter(char character) { | |
| + return character >= 'a' && character <= 'z' | |
| + || character >= 'A' && character <= 'Z' | |
| + || character >= '0' && character <= '9' | |
| + || character == '-' | |
| + || character == '_' | |
| + || character == '.'; | |
| } | |
| @SuppressWarnings("checkstyle:parameternumber") | |
| @@ -1731,17 +1828,17 @@ private boolean validateCoordinatesIdWithWildcards( | |
| } | |
| private boolean isValidCoordinatesIdWithWildCards(String id) { | |
| - for (int i = 0; i < id.length(); i++) { | |
| - char c = id.charAt(i); | |
| - if (!isValidCoordinatesIdWithWildCardCharacter(c)) { | |
| + for (int index = 0; index < id.length(); index++) { | |
| + char character = id.charAt(index); | |
| + if (!isValidCoordinatesIdWithWildCardCharacter(character)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| - private boolean isValidCoordinatesIdWithWildCardCharacter(char c) { | |
| - return isValidCoordinatesIdCharacter(c) || c == '?' || c == '*'; | |
| + private boolean isValidCoordinatesIdWithWildCardCharacter(char character) { | |
| + return isValidCoordinatesIdCharacter(character) || character == '?' || character == '*'; | |
| } | |
| private boolean validateStringNoExpression( | |
| @@ -1778,8 +1875,8 @@ private boolean validateVersionNoExpression( | |
| return true; | |
| } | |
| - Matcher m = EXPRESSION_NAME_PATTERN.matcher(string.trim()); | |
| - if (m.find()) { | |
| + Matcher matcher = EXPRESSION_NAME_PATTERN.matcher(string.trim()); | |
| + if (matcher.find()) { | |
| addViolation( | |
| problems, | |
| severity, | |
| @@ -1990,21 +2087,71 @@ private boolean validateEnum( | |
| return false; | |
| } | |
| + @SuppressWarnings("checkstyle:parameternumber") | |
| + private boolean validateDependencyScope( | |
| + String prefix, | |
| + String fieldName, | |
| + ModelProblemCollector problems, | |
| + Severity severity, | |
| + Version version, | |
| + String scope, | |
| + @Nullable SourceHint sourceHint, | |
| + InputLocationTracker tracker, | |
| + String[] validScopes, | |
| + boolean isDependencyManagement) { | |
| + if (scope == null || scope.isEmpty()) { | |
| + return true; | |
| + } | |
| + | |
| + List<String> values = Arrays.asList(validScopes); | |
| + | |
| + if (values.contains(scope)) { | |
| + return true; | |
| + } | |
| + | |
| + // Provide a more helpful error message for the 'import' scope | |
| + if ("import".equals(scope) && !isDependencyManagement) { | |
| + addViolation( | |
| + problems, | |
| + severity, | |
| + version, | |
| + prefix + fieldName, | |
| + sourceHint, | |
| + "has scope 'import'. The 'import' scope is only valid in <dependencyManagement> sections.", | |
| + tracker); | |
| + } else { | |
| + addViolation( | |
| + problems, | |
| + severity, | |
| + version, | |
| + prefix + fieldName, | |
| + sourceHint, | |
| + "must be one of " + values + " but is '" + scope + "'.", | |
| + tracker); | |
| + } | |
| + | |
| + return false; | |
| + } | |
| + | |
| @SuppressWarnings("checkstyle:parameternumber") | |
| private boolean validateModelVersion( | |
| - ModelProblemCollector problems, String string, InputLocationTracker tracker, List<String> validVersions) { | |
| - if (string == null || string.isEmpty()) { | |
| + Session session, | |
| + ModelProblemCollector problems, | |
| + String requestedModel, | |
| + InputLocationTracker tracker, | |
| + List<String> validVersions) { | |
| + if (requestedModel == null || requestedModel.isEmpty()) { | |
| return true; | |
| } | |
| - if (validVersions.contains(string)) { | |
| + if (validVersions.contains(requestedModel)) { | |
| return true; | |
| } | |
| boolean newerThanAll = true; | |
| boolean olderThanAll = true; | |
| for (String validValue : validVersions) { | |
| - final int comparison = compareModelVersions(validValue, string); | |
| + final int comparison = compareModelVersions(validValue, requestedModel); | |
| newerThanAll = newerThanAll && comparison < 0; | |
| olderThanAll = olderThanAll && comparison > 0; | |
| } | |
| @@ -2016,8 +2163,10 @@ private boolean validateModelVersion( | |
| Version.V20, | |
| "modelVersion", | |
| null, | |
| - "of '" + string + "' is newer than the versions supported by this version of Maven: " | |
| - + validVersions + ". Building this project requires a newer version of Maven.", | |
| + requestedModel + "' is not supported by this Maven version (" | |
| + + getMavenVersionString(session) | |
| + + "). Supported modelVersions are: " + validVersions | |
| + + ". Building this project requires a newer version of Maven.", | |
| tracker); | |
| } else if (olderThanAll) { | |
| @@ -2028,8 +2177,10 @@ private boolean validateModelVersion( | |
| Version.V20, | |
| "modelVersion", | |
| null, | |
| - "of '" + string + "' is older than the versions supported by this version of Maven: " | |
| - + validVersions + ". Building this project requires an older version of Maven.", | |
| + requestedModel + "' is not supported by this Maven version (" | |
| + + getMavenVersionString(session) | |
| + + "). Supported modelVersions are: " + validVersions | |
| + + ". Building this project requires an older version of Maven.", | |
| tracker); | |
| } else { | |
| @@ -2039,13 +2190,22 @@ private boolean validateModelVersion( | |
| Version.V20, | |
| "modelVersion", | |
| null, | |
| - "must be one of " + validVersions + " but is '" + string + "'.", | |
| + "must be one of " + validVersions + " but is '" + requestedModel + "'.", | |
| tracker); | |
| } | |
| return false; | |
| } | |
| + private String getMavenVersionString(Session session) { | |
| + try { | |
| + return session.getMavenVersion().toString(); | |
| + } catch (Exception exception) { | |
| + // Fallback for test contexts where RuntimeInformation might not be available | |
| + return "unknown"; | |
| + } | |
| + } | |
| + | |
| /** | |
| * Compares two model versions. | |
| * | |
| @@ -2058,8 +2218,8 @@ private static int compareModelVersions(String first, String second) { | |
| // we use a dedicated comparator because we control our model version scheme. | |
| String[] firstSegments = first.split("\\."); | |
| String[] secondSegments = second.split("\\."); | |
| - for (int i = 0; i < Math.max(firstSegments.length, secondSegments.length); i++) { | |
| - int result = asLong(i, firstSegments).compareTo(asLong(i, secondSegments)); | |
| + for (int index = 0; index < Math.max(firstSegments.length, secondSegments.length); index++) { | |
| + int result = asLong(index, firstSegments).compareTo(asLong(index, secondSegments)); | |
| if (result != 0) { | |
| return result; | |
| } | |
| @@ -2070,7 +2230,7 @@ private static int compareModelVersions(String first, String second) { | |
| private static Long asLong(int index, String[] segments) { | |
| try { | |
| return Long.valueOf(index < segments.length ? segments[index] : "0"); | |
| - } catch (NumberFormatException e) { | |
| + } catch (NumberFormatException exception) { | |
| return 0L; | |
| } | |
| } | |
| @@ -2087,15 +2247,15 @@ private boolean validateBannedCharacters( | |
| InputLocationTracker tracker, | |
| String banned) { | |
| if (string != null) { | |
| - for (int i = string.length() - 1; i >= 0; i--) { | |
| - if (banned.indexOf(string.charAt(i)) >= 0) { | |
| + for (int index = string.length() - 1; index >= 0; index--) { | |
| + if (banned.indexOf(string.charAt(index)) >= 0) { | |
| addViolation( | |
| problems, | |
| severity, | |
| version, | |
| prefix + fieldName, | |
| sourceHint, | |
| - "must not contain any of these characters " + banned + " but found " + string.charAt(i), | |
| + "must not contain any of these characters " + banned + " but found " + string.charAt(index), | |
| tracker); | |
| return false; | |
| } | |
| @@ -2242,7 +2402,7 @@ private static InputLocation getLocation(String fieldName, InputLocationTracker | |
| key = fieldName.substring(fieldName.lastIndexOf('[') + 1, fieldName.length() - 1); | |
| try { | |
| key = Integer.valueOf(key.toString()); | |
| - } catch (NumberFormatException e) { | |
| + } catch (NumberFormatException exception) { | |
| // use key as is | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/FileToRawModelMerger.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/FileToRawModelMerger.java | |
| deleted file mode 100644 | |
| index b98d057b30..0000000000 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/FileToRawModelMerger.java | |
| +++ /dev/null | |
| @@ -1,216 +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.impl.model; | |
| - | |
| -import java.util.Iterator; | |
| -import java.util.Map; | |
| -import java.util.stream.Collectors; | |
| - | |
| -import org.apache.maven.api.model.Build; | |
| -import org.apache.maven.api.model.BuildBase; | |
| -import org.apache.maven.api.model.CiManagement; | |
| -import org.apache.maven.api.model.Dependency; | |
| -import org.apache.maven.api.model.DependencyManagement; | |
| -import org.apache.maven.api.model.Model; | |
| -import org.apache.maven.api.model.ModelBase; | |
| -import org.apache.maven.api.model.Plugin; | |
| -import org.apache.maven.api.model.PluginContainer; | |
| -import org.apache.maven.api.model.Profile; | |
| -import org.apache.maven.api.model.ReportPlugin; | |
| -import org.apache.maven.api.model.Reporting; | |
| -import org.apache.maven.model.v4.MavenMerger; | |
| - | |
| -/** | |
| - * As long as Maven controls the BuildPomXMLFilter, the entities that need merging are known. | |
| - * All others can simply be copied from source to target to restore the locationTracker | |
| - * | |
| - * @since 4.0.0 | |
| - */ | |
| -class FileToRawModelMerger extends MavenMerger { | |
| - | |
| - @Override | |
| - protected void mergeBuild_Extensions( | |
| - Build.Builder builder, Build target, Build source, boolean sourceDominant, Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeBuildBase_Resources( | |
| - BuildBase.Builder builder, | |
| - BuildBase target, | |
| - BuildBase source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeBuildBase_TestResources( | |
| - BuildBase.Builder builder, | |
| - BuildBase target, | |
| - BuildBase source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeCiManagement_Notifiers( | |
| - CiManagement.Builder builder, | |
| - CiManagement target, | |
| - CiManagement source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeDependencyManagement_Dependencies( | |
| - DependencyManagement.Builder builder, | |
| - DependencyManagement target, | |
| - DependencyManagement source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - Iterator<Dependency> sourceIterator = source.getDependencies().iterator(); | |
| - builder.dependencies(target.getDependencies().stream() | |
| - .map(d -> mergeDependency(d, sourceIterator.next(), sourceDominant, context)) | |
| - .collect(Collectors.toList())); | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeDependency_Exclusions( | |
| - Dependency.Builder builder, | |
| - Dependency target, | |
| - Dependency source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModel_Contributors( | |
| - Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModel_Developers( | |
| - Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModel_Licenses( | |
| - Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModel_MailingLists( | |
| - Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModel_Profiles( | |
| - Model.Builder builder, Model target, Model source, boolean sourceDominant, Map<Object, Object> context) { | |
| - Iterator<Profile> sourceIterator = source.getProfiles().iterator(); | |
| - builder.profiles(target.getProfiles().stream() | |
| - .map(d -> mergeProfile(d, sourceIterator.next(), sourceDominant, context)) | |
| - .collect(Collectors.toList())); | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModelBase_Dependencies( | |
| - ModelBase.Builder builder, | |
| - ModelBase target, | |
| - ModelBase source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - Iterator<Dependency> sourceIterator = source.getDependencies().iterator(); | |
| - builder.dependencies(target.getDependencies().stream() | |
| - .map(d -> mergeDependency(d, sourceIterator.next(), sourceDominant, context)) | |
| - .collect(Collectors.toList())); | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModelBase_PluginRepositories( | |
| - ModelBase.Builder builder, | |
| - ModelBase target, | |
| - ModelBase source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - builder.pluginRepositories(source.getPluginRepositories()); | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeModelBase_Repositories( | |
| - ModelBase.Builder builder, | |
| - ModelBase target, | |
| - ModelBase source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergePlugin_Dependencies( | |
| - Plugin.Builder builder, Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) { | |
| - Iterator<Dependency> sourceIterator = source.getDependencies().iterator(); | |
| - builder.dependencies(target.getDependencies().stream() | |
| - .map(d -> mergeDependency(d, sourceIterator.next(), sourceDominant, context)) | |
| - .collect(Collectors.toList())); | |
| - } | |
| - | |
| - @Override | |
| - protected void mergePlugin_Executions( | |
| - Plugin.Builder builder, Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeReporting_Plugins( | |
| - Reporting.Builder builder, | |
| - Reporting target, | |
| - Reporting source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergeReportPlugin_ReportSets( | |
| - ReportPlugin.Builder builder, | |
| - ReportPlugin target, | |
| - ReportPlugin source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| - | |
| - @Override | |
| - protected void mergePluginContainer_Plugins( | |
| - PluginContainer.Builder builder, | |
| - PluginContainer target, | |
| - PluginContainer source, | |
| - boolean sourceDominant, | |
| - Map<Object, Object> context) { | |
| - // don't merge | |
| - } | |
| -} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/IntrospectionException.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/IntrospectionException.java | |
| index bfe5e06119..726646e702 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/IntrospectionException.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/IntrospectionException.java | |
| @@ -18,16 +18,8 @@ | |
| */ | |
| package org.apache.maven.impl.model.reflection; | |
| -import java.io.Serial; | |
| - | |
| public class IntrospectionException extends Exception { | |
| - /** | |
| - * | |
| - */ | |
| - @Serial | |
| - private static final long serialVersionUID = -6090771282553728784L; | |
| - | |
| IntrospectionException(String message) { | |
| super(message); | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/MethodMap.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/MethodMap.java | |
| index 5071b86d39..d13343bf70 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/MethodMap.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/reflection/MethodMap.java | |
| @@ -18,7 +18,6 @@ | |
| */ | |
| package org.apache.maven.impl.model.reflection; | |
| -import java.io.Serial; | |
| import java.lang.reflect.Method; | |
| import java.util.ArrayList; | |
| import java.util.Hashtable; | |
| @@ -117,11 +116,7 @@ Method find(String methodName, Object... args) throws AmbiguousException { | |
| * simple distinguishable exception, used when | |
| * we run across ambiguous overloading | |
| */ | |
| - static class AmbiguousException extends Exception { | |
| - | |
| - @Serial | |
| - private static final long serialVersionUID = 751688436639650618L; | |
| - } | |
| + static class AmbiguousException extends Exception {} | |
| private static Method getMostSpecific(List<Method> methods, Class<?>... classes) throws AmbiguousException { | |
| LinkedList<Method> applicables = getApplicables(methods, classes); | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/ArtifactDescriptorReaderDelegate.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/ArtifactDescriptorReaderDelegate.java | |
| deleted file mode 100644 | |
| index fc105b0dfd..0000000000 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/ArtifactDescriptorReaderDelegate.java | |
| +++ /dev/null | |
| @@ -1,149 +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.impl.resolver; | |
| - | |
| -import java.util.ArrayList; | |
| -import java.util.Collections; | |
| -import java.util.HashMap; | |
| -import java.util.LinkedHashMap; | |
| -import java.util.List; | |
| -import java.util.Map; | |
| - | |
| -import org.apache.maven.api.Language; | |
| -import org.apache.maven.api.model.DependencyManagement; | |
| -import org.apache.maven.api.model.DistributionManagement; | |
| -import org.apache.maven.api.model.License; | |
| -import org.apache.maven.api.model.Model; | |
| -import org.apache.maven.api.model.Prerequisites; | |
| -import org.apache.maven.api.model.Repository; | |
| -import org.apache.maven.api.services.RepositoryFactory; | |
| -import org.apache.maven.impl.InternalSession; | |
| -import org.apache.maven.impl.resolver.artifact.MavenArtifactProperties; | |
| -import org.apache.maven.impl.resolver.type.DefaultType; | |
| -import org.eclipse.aether.artifact.Artifact; | |
| -import org.eclipse.aether.artifact.ArtifactProperties; | |
| -import org.eclipse.aether.artifact.ArtifactType; | |
| -import org.eclipse.aether.artifact.ArtifactTypeRegistry; | |
| -import org.eclipse.aether.artifact.DefaultArtifact; | |
| -import org.eclipse.aether.graph.Dependency; | |
| -import org.eclipse.aether.graph.Exclusion; | |
| -import org.eclipse.aether.resolution.ArtifactDescriptorResult; | |
| - | |
| -/** | |
| - * Populates Aether {@link ArtifactDescriptorResult} from Maven project {@link Model}. | |
| - * <p> | |
| - * <strong>Note:</strong> This class is part of work in progress and can be changed or removed without notice. | |
| - * @since 3.2.4 | |
| - */ | |
| -public class ArtifactDescriptorReaderDelegate { | |
| - public void populateResult(InternalSession session, ArtifactDescriptorResult result, Model model) { | |
| - ArtifactTypeRegistry stereotypes = session.getSession().getArtifactTypeRegistry(); | |
| - | |
| - for (Repository r : model.getRepositories()) { | |
| - result.addRepository(session.toRepository( | |
| - session.getService(RepositoryFactory.class).createRemote(r))); | |
| - } | |
| - | |
| - for (org.apache.maven.api.model.Dependency dependency : model.getDependencies()) { | |
| - result.addDependency(convert(dependency, stereotypes)); | |
| - } | |
| - | |
| - DependencyManagement mgmt = model.getDependencyManagement(); | |
| - if (mgmt != null) { | |
| - for (org.apache.maven.api.model.Dependency dependency : mgmt.getDependencies()) { | |
| - result.addManagedDependency(convert(dependency, stereotypes)); | |
| - } | |
| - } | |
| - | |
| - Map<String, Object> properties = new LinkedHashMap<>(); | |
| - | |
| - Prerequisites prerequisites = model.getPrerequisites(); | |
| - if (prerequisites != null) { | |
| - properties.put("prerequisites.maven", prerequisites.getMaven()); | |
| - } | |
| - | |
| - List<License> licenses = model.getLicenses(); | |
| - properties.put("license.count", licenses.size()); | |
| - for (int i = 0; i < licenses.size(); i++) { | |
| - License license = licenses.get(i); | |
| - properties.put("license." + i + ".name", license.getName()); | |
| - properties.put("license." + i + ".url", license.getUrl()); | |
| - properties.put("license." + i + ".comments", license.getComments()); | |
| - properties.put("license." + i + ".distribution", license.getDistribution()); | |
| - } | |
| - | |
| - result.setProperties(properties); | |
| - | |
| - setArtifactProperties(result, model); | |
| - } | |
| - | |
| - private Dependency convert(org.apache.maven.api.model.Dependency dependency, ArtifactTypeRegistry stereotypes) { | |
| - ArtifactType stereotype = stereotypes.get(dependency.getType()); | |
| - if (stereotype == null) { | |
| - // TODO: this here is fishy | |
| - stereotype = new DefaultType(dependency.getType(), Language.NONE, "", null, false); | |
| - } | |
| - | |
| - boolean system = dependency.getSystemPath() != null | |
| - && !dependency.getSystemPath().isEmpty(); | |
| - | |
| - Map<String, String> props = null; | |
| - if (system) { | |
| - props = Collections.singletonMap(MavenArtifactProperties.LOCAL_PATH, dependency.getSystemPath()); | |
| - } | |
| - | |
| - Artifact artifact = new DefaultArtifact( | |
| - dependency.getGroupId(), | |
| - dependency.getArtifactId(), | |
| - dependency.getClassifier(), | |
| - null, | |
| - dependency.getVersion(), | |
| - props, | |
| - stereotype); | |
| - | |
| - List<Exclusion> exclusions = new ArrayList<>(dependency.getExclusions().size()); | |
| - for (org.apache.maven.api.model.Exclusion exclusion : dependency.getExclusions()) { | |
| - exclusions.add(convert(exclusion)); | |
| - } | |
| - | |
| - return new Dependency( | |
| - artifact, | |
| - dependency.getScope(), | |
| - dependency.getOptional() != null ? dependency.isOptional() : null, | |
| - exclusions); | |
| - } | |
| - | |
| - private Exclusion convert(org.apache.maven.api.model.Exclusion exclusion) { | |
| - return new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*"); | |
| - } | |
| - | |
| - private void setArtifactProperties(ArtifactDescriptorResult result, Model model) { | |
| - String downloadUrl = null; | |
| - DistributionManagement distMgmt = model.getDistributionManagement(); | |
| - if (distMgmt != null) { | |
| - downloadUrl = distMgmt.getDownloadUrl(); | |
| - } | |
| - if (downloadUrl != null && !downloadUrl.isEmpty()) { | |
| - Artifact artifact = result.getArtifact(); | |
| - Map<String, String> props = new HashMap<>(artifact.getProperties()); | |
| - props.put(ArtifactProperties.DOWNLOAD_URL, downloadUrl); | |
| - result.setArtifact(artifact.setProperties(props)); | |
| - } | |
| - } | |
| -} | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java | |
| index 0b2badca84..10c58e0fb8 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java | |
| @@ -18,7 +18,10 @@ | |
| */ | |
| package org.apache.maven.impl.resolver; | |
| +import java.util.ArrayList; | |
| +import java.util.Collections; | |
| import java.util.HashMap; | |
| +import java.util.LinkedHashMap; | |
| import java.util.LinkedHashSet; | |
| import java.util.List; | |
| import java.util.Map; | |
| @@ -28,24 +31,37 @@ | |
| 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.DependencyManagement; | |
| +import org.apache.maven.api.model.DistributionManagement; | |
| +import org.apache.maven.api.model.License; | |
| import org.apache.maven.api.model.Model; | |
| +import org.apache.maven.api.model.Prerequisites; | |
| +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.ModelProblem; | |
| import org.apache.maven.api.services.ProblemCollector; | |
| +import org.apache.maven.api.services.RepositoryFactory; | |
| import org.apache.maven.api.services.Sources; | |
| import org.apache.maven.api.services.model.ModelResolverException; | |
| import org.apache.maven.impl.InternalSession; | |
| import org.apache.maven.impl.RequestTraceHelper; | |
| import org.apache.maven.impl.model.ModelProblemUtils; | |
| +import org.apache.maven.impl.resolver.artifact.MavenArtifactProperties; | |
| import org.eclipse.aether.RepositoryEvent; | |
| import org.eclipse.aether.RepositoryEvent.EventType; | |
| import org.eclipse.aether.RepositoryException; | |
| import org.eclipse.aether.RepositorySystemSession; | |
| import org.eclipse.aether.RequestTrace; | |
| import org.eclipse.aether.artifact.Artifact; | |
| +import org.eclipse.aether.artifact.ArtifactProperties; | |
| +import org.eclipse.aether.artifact.ArtifactType; | |
| +import org.eclipse.aether.artifact.ArtifactTypeRegistry; | |
| +import org.eclipse.aether.artifact.DefaultArtifact; | |
| +import org.eclipse.aether.graph.Dependency; | |
| +import org.eclipse.aether.graph.Exclusion; | |
| import org.eclipse.aether.impl.ArtifactDescriptorReader; | |
| import org.eclipse.aether.impl.ArtifactResolver; | |
| import org.eclipse.aether.impl.RepositoryEventDispatcher; | |
| @@ -77,7 +93,6 @@ public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader | |
| private final RepositoryEventDispatcher repositoryEventDispatcher; | |
| private final ModelBuilder modelBuilder; | |
| private final Map<String, MavenArtifactRelocationSource> artifactRelocationSources; | |
| - private final ArtifactDescriptorReaderDelegate delegate; | |
| private final Logger logger = LoggerFactory.getLogger(getClass()); | |
| @Inject | |
| @@ -94,7 +109,6 @@ public DefaultArtifactDescriptorReader( | |
| Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null"); | |
| this.artifactRelocationSources = | |
| Objects.requireNonNull(artifactRelocationSources, "artifactRelocationSources cannot be null"); | |
| - this.delegate = new ArtifactDescriptorReaderDelegate(); | |
| } | |
| @Override | |
| @@ -104,15 +118,7 @@ public ArtifactDescriptorResult readArtifactDescriptor( | |
| Model model = loadPom(session, request, result); | |
| if (model != null) { | |
| - Map<String, Object> config = session.getConfigProperties(); | |
| - ArtifactDescriptorReaderDelegate delegate = | |
| - (ArtifactDescriptorReaderDelegate) config.get(ArtifactDescriptorReaderDelegate.class.getName()); | |
| - | |
| - if (delegate == null) { | |
| - delegate = this.delegate; | |
| - } | |
| - | |
| - delegate.populateResult(InternalSession.from(session), result, model); | |
| + populateResult(InternalSession.from(session), result, model); | |
| } | |
| return result; | |
| @@ -207,7 +213,8 @@ private Model loadPom( | |
| .repositories(repositories) | |
| .build(); | |
| - ModelBuilderResult modelResult = modelBuilder.newSession().build(modelRequest); | |
| + ModelBuilder.ModelBuilderSession builderSession = modelBuilder.newSession(); | |
| + ModelBuilderResult modelResult = iSession.request(modelRequest, builderSession::build); | |
| // 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.getProblemCollector().hasWarningProblems()) { | |
| @@ -331,4 +338,94 @@ private int getPolicy(RepositorySystemSession session, Artifact a, ArtifactDescr | |
| } | |
| return policy.getPolicy(session, new ArtifactDescriptorPolicyRequest(a, request.getRequestContext())); | |
| } | |
| + | |
| + private void populateResult(InternalSession session, ArtifactDescriptorResult result, Model model) { | |
| + ArtifactTypeRegistry stereotypes = session.getSession().getArtifactTypeRegistry(); | |
| + | |
| + for (Repository repository : model.getRepositories()) { | |
| + result.addRepository(session.toRepository( | |
| + session.getService(RepositoryFactory.class).createRemote(repository))); | |
| + } | |
| + | |
| + for (org.apache.maven.api.model.Dependency dependency : model.getDependencies()) { | |
| + result.addDependency(convert(dependency, stereotypes)); | |
| + } | |
| + | |
| + DependencyManagement dependencyManagement = model.getDependencyManagement(); | |
| + if (dependencyManagement != null) { | |
| + for (org.apache.maven.api.model.Dependency dependency : dependencyManagement.getDependencies()) { | |
| + result.addManagedDependency(convert(dependency, stereotypes)); | |
| + } | |
| + } | |
| + | |
| + Map<String, Object> properties = new LinkedHashMap<>(); | |
| + | |
| + Prerequisites prerequisites = model.getPrerequisites(); | |
| + if (prerequisites != null) { | |
| + properties.put("prerequisites.maven", prerequisites.getMaven()); | |
| + } | |
| + | |
| + List<License> licenses = model.getLicenses(); | |
| + properties.put("license.count", licenses.size()); | |
| + for (int i = 0; i < licenses.size(); i++) { | |
| + License license = licenses.get(i); | |
| + properties.put("license." + i + ".name", license.getName()); | |
| + properties.put("license." + i + ".url", license.getUrl()); | |
| + properties.put("license." + i + ".comments", license.getComments()); | |
| + properties.put("license." + i + ".distribution", license.getDistribution()); | |
| + } | |
| + | |
| + result.setProperties(properties); | |
| + | |
| + setArtifactProperties(result, model); | |
| + } | |
| + | |
| + private Dependency convert(org.apache.maven.api.model.Dependency dependency, ArtifactTypeRegistry stereotypes) { | |
| + ArtifactType stereotype = stereotypes.get(dependency.getType()); | |
| + | |
| + boolean system = dependency.getSystemPath() != null | |
| + && !dependency.getSystemPath().isEmpty(); | |
| + | |
| + Map<String, String> properties = null; | |
| + if (system) { | |
| + properties = Collections.singletonMap(MavenArtifactProperties.LOCAL_PATH, dependency.getSystemPath()); | |
| + } | |
| + | |
| + Artifact artifact = new DefaultArtifact( | |
| + dependency.getGroupId(), | |
| + dependency.getArtifactId(), | |
| + dependency.getClassifier(), | |
| + null, | |
| + dependency.getVersion(), | |
| + properties, | |
| + stereotype); | |
| + | |
| + List<Exclusion> exclusions = new ArrayList<>(dependency.getExclusions().size()); | |
| + for (org.apache.maven.api.model.Exclusion exclusion : dependency.getExclusions()) { | |
| + exclusions.add(convert(exclusion)); | |
| + } | |
| + | |
| + return new Dependency( | |
| + artifact, | |
| + dependency.getScope(), | |
| + dependency.getOptional() != null ? dependency.isOptional() : null, | |
| + exclusions); | |
| + } | |
| + | |
| + private Exclusion convert(org.apache.maven.api.model.Exclusion exclusion) { | |
| + return new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*"); | |
| + } | |
| + | |
| + private void setArtifactProperties(ArtifactDescriptorResult result, Model model) { | |
| + DistributionManagement distributionManagement = model.getDistributionManagement(); | |
| + if (distributionManagement != null) { | |
| + String downloadUrl = distributionManagement.getDownloadUrl(); | |
| + if (downloadUrl != null && !downloadUrl.isEmpty()) { | |
| + Artifact artifact = result.getArtifact(); | |
| + Map<String, String> props = new LinkedHashMap<>(artifact.getProperties()); | |
| + props.put(ArtifactProperties.DOWNLOAD_URL, downloadUrl); | |
| + result.setArtifact(artifact.setProperties(props)); | |
| + } | |
| + } | |
| + } | |
| } | |
| diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultModelResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultModelResolver.java | |
| index 03973c981e..79b085e434 100644 | |
| --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultModelResolver.java | |
| +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultModelResolver.java | |
| @@ -68,7 +68,8 @@ public ModelSource resolveModel( | |
| parent.getGroupId(), | |
| parent.getArtifactId(), | |
| parent.getVersion(), | |
| - null), | |
| + null, | |
| + "pom"), | |
| parent.getLocation("version"), | |
| "parent"); | |
| if (result.version() != null) { | |
| @@ -93,7 +94,8 @@ public ModelSource resolveModel( | |
| dependency.getGroupId(), | |
| dependency.getArtifactId(), | |
| dependency.getVersion(), | |
| - dependency.getClassifier()), | |
| + dependency.getClassifier(), | |
| + "pom"), | |
| dependency.getLocation("version"), | |
| "dependency"); | |
| if (result.version() != null) { | |
| @@ -122,12 +124,13 @@ public ModelResolverResult doResolveModel( | |
| String artifactId = request.artifactId(); | |
| String version = request.version(); | |
| String classifier = request.classifier(); | |
| + String extension = request.extension(); | |
| List<RemoteRepository> repositories = request.repositories(); | |
| RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request); | |
| try { | |
| ArtifactCoordinates coords = | |
| - session.createArtifactCoordinates(groupId, artifactId, version, classifier, "pom", null); | |
| + session.createArtifactCoordinates(groupId, artifactId, version, classifier, extension, null); | |
| if (coords.getVersionConstraint().getVersionRange() != null | |
| && coords.getVersionConstraint().getVersionRange().getUpperBoundary() == null) { | |
| // Message below is checked for in the MNG-2199 core IT. | |
| @@ -149,11 +152,9 @@ public ModelResolverResult doResolveModel( | |
| version)) | |
| .toString(); | |
| String resultVersion = version.equals(newVersion) ? null : newVersion; | |
| - Path path = getPath(session, repositories, groupId, artifactId, newVersion, classifier); | |
| - return new ModelResolverResult( | |
| - request, | |
| - Sources.resolvedSource(path, groupId + ":" + artifactId + ":" + newVersion), | |
| - resultVersion); | |
| + Path path = getPath(session, repositories, groupId, artifactId, newVersion, classifier, extension); | |
| + String gav = groupId + ":" + artifactId + ":" + newVersion; | |
| + return new ModelResolverResult(request, Sources.resolvedSource(path, gav), resultVersion); | |
| } catch (VersionRangeResolverException | ArtifactResolverException e) { | |
| throw new ModelResolverException( | |
| e.getMessage() + " (remote repositories: " | |
| @@ -175,9 +176,11 @@ protected Path getPath( | |
| String groupId, | |
| String artifactId, | |
| String version, | |
| - String classifier) { | |
| + String classifier, | |
| + String extension) { | |
| DownloadedArtifact resolved = session.resolveArtifact( | |
| - session.createArtifactCoordinates(groupId, artifactId, version, classifier, "pom", null), repositories); | |
| + session.createArtifactCoordinates(groupId, artifactId, version, classifier, extension, null), | |
| + repositories); | |
| return resolved.getPath(); | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/main/resources/META-INF/services/org.apache.maven.api.model.ModelObjectProcessor b/impl/maven-impl/src/main/resources/META-INF/services/org.apache.maven.api.model.ModelObjectProcessor | |
| new file mode 100644 | |
| index 0000000000..97094f1ec0 | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/resources/META-INF/services/org.apache.maven.api.model.ModelObjectProcessor | |
| @@ -0,0 +1 @@ | |
| +org.apache.maven.impl.model.DefaultModelObjectPool | |
| diff --git a/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.1.0.xml b/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.1.0.xml | |
| index ff7738614f..754912f099 100644 | |
| --- a/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.1.0.xml | |
| +++ b/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.1.0.xml | |
| @@ -20,8 +20,10 @@ under the License. | |
| --> | |
| <!-- START SNIPPET: superpom --> | |
| -<project> | |
| - <modelVersion>4.0.0</modelVersion> | |
| +<project xmlns="http://maven.apache.org/POM/4.1.0" | |
| + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"> | |
| + <modelVersion>4.1.0</modelVersion> | |
| <properties> | |
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| diff --git a/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.2.0.xml b/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.2.0.xml | |
| new file mode 100644 | |
| index 0000000000..9b1b358f80 | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/main/resources/org/apache/maven/model/pom-4.2.0.xml | |
| @@ -0,0 +1,68 @@ | |
| +<?xml version="1.0" encoding="UTF-8"?> | |
| + | |
| +<!-- | |
| +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. | |
| +--> | |
| + | |
| +<!-- START SNIPPET: superpom --> | |
| +<project xmlns="http://maven.apache.org/POM/4.2.0" | |
| + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.2.0 https://maven.apache.org/xsd/maven-4.2.0.xsd"> | |
| + <modelVersion>4.2.0</modelVersion> | |
| + | |
| + <properties> | |
| + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
| + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |
| + <!-- Fixed date for reproducible build --> | |
| + <project.build.outputTimestamp>1980-02-01T00:00:00Z</project.build.outputTimestamp> | |
| + </properties> | |
| + | |
| + <build> | |
| + <directory>${project.basedir}/target</directory> | |
| + <outputDirectory>${project.build.directory}/classes</outputDirectory> | |
| + <finalName>${project.artifactId}-${project.version}</finalName> | |
| + <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory> | |
| + <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> | |
| + <scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory> | |
| + <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory> | |
| + <resources> | |
| + <resource> | |
| + <directory>${project.basedir}/src/main/resources</directory> | |
| + </resource> | |
| + <resource> | |
| + <directory>${project.basedir}/src/main/resources-filtered</directory> | |
| + <filtering>true</filtering> | |
| + </resource> | |
| + </resources> | |
| + <testResources> | |
| + <testResource> | |
| + <directory>${project.basedir}/src/test/resources</directory> | |
| + </testResource> | |
| + <testResource> | |
| + <directory>${project.basedir}/src/test/resources-filtered</directory> | |
| + <filtering>true</filtering> | |
| + </testResource> | |
| + </testResources> | |
| + </build> | |
| + | |
| + <reporting> | |
| + <outputDirectory>${project.build.directory}/site</outputDirectory> | |
| + </reporting> | |
| + | |
| +</project> | |
| +<!-- END SNIPPET: superpom --> | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultPluginXmlFactoryTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultPluginXmlFactoryTest.java | |
| index 4031fb9186..e566e4d9fc 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultPluginXmlFactoryTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultPluginXmlFactoryTest.java | |
| @@ -21,6 +21,7 @@ | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.IOException; | |
| +import java.io.InputStream; | |
| import java.io.StringReader; | |
| import java.io.StringWriter; | |
| import java.io.Writer; | |
| @@ -40,10 +41,11 @@ | |
| import org.junit.jupiter.api.io.TempDir; | |
| import static java.util.UUID.randomUUID; | |
| -import static org.assertj.core.api.Assertions.assertThat; | |
| -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertInstanceOf; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| import static org.mockito.Mockito.mock; | |
| import static org.mockito.Mockito.when; | |
| @@ -72,37 +74,37 @@ void readFromInputStreamParsesPluginDescriptorCorrectly() { | |
| PluginDescriptor descriptor = defaultPluginXmlFactory.read(XmlReaderRequest.builder() | |
| .inputStream(new ByteArrayInputStream(SAMPLE_PLUGIN_XML.getBytes())) | |
| .build()); | |
| - assertThat(descriptor.getName()).isEqualTo(NAME); | |
| - assertThat(descriptor.getGroupId()).isEqualTo("org.example"); | |
| - assertThat(descriptor.getArtifactId()).isEqualTo("sample-plugin"); | |
| - assertThat(descriptor.getVersion()).isEqualTo("1.0.0"); | |
| + assertEquals(NAME, descriptor.getName()); | |
| + assertEquals("org.example", descriptor.getGroupId()); | |
| + assertEquals("sample-plugin", descriptor.getArtifactId()); | |
| + assertEquals("1.0.0", descriptor.getVersion()); | |
| } | |
| @Test | |
| void parsePlugin() { | |
| - assertThat(defaultPluginXmlFactory | |
| - .read(XmlReaderRequest.builder() | |
| - .reader(new StringReader(SAMPLE_PLUGIN_XML)) | |
| - .build()) | |
| - .getName()) | |
| - .isEqualTo(NAME); | |
| + String actualName = defaultPluginXmlFactory | |
| + .read(XmlReaderRequest.builder() | |
| + .reader(new StringReader(SAMPLE_PLUGIN_XML)) | |
| + .build()) | |
| + .getName(); | |
| + assertEquals(NAME, actualName, "Expected plugin name to be " + NAME + " but was " + actualName); | |
| } | |
| @Test | |
| void readFromPathParsesPluginDescriptorCorrectly() throws Exception { | |
| Path xmlFile = tempDir.resolve("plugin.xml"); | |
| Files.write(xmlFile, SAMPLE_PLUGIN_XML.getBytes()); | |
| - assertThat(defaultPluginXmlFactory | |
| - .read(XmlReaderRequest.builder().path(xmlFile).build()) | |
| - .getName()) | |
| - .isEqualTo(NAME); | |
| + String actualName = defaultPluginXmlFactory | |
| + .read(XmlReaderRequest.builder().path(xmlFile).build()) | |
| + .getName(); | |
| + assertEquals(NAME, actualName, "Expected plugin name to be " + NAME + " but was " + actualName); | |
| } | |
| @Test | |
| void readWithNoInputThrowsIllegalArgumentException() { | |
| - assertThatExceptionOfType(IllegalArgumentException.class) | |
| - .isThrownBy(() -> | |
| - defaultPluginXmlFactory.read(XmlReaderRequest.builder().build())); | |
| + assertThrows( | |
| + IllegalArgumentException.class, | |
| + () -> defaultPluginXmlFactory.read(XmlReaderRequest.builder().build())); | |
| } | |
| @Test | |
| @@ -118,8 +120,12 @@ void writeToWriterGeneratesValidXml() { | |
| .build()) | |
| .build()); | |
| String output = writer.toString(); | |
| - assertThat(output).contains("<name>" + NAME + "</name>"); | |
| - assertThat(output).contains("<groupId>org.example</groupId>"); | |
| + assertTrue( | |
| + output.contains("<name>" + NAME + "</name>"), | |
| + "Expected " + output + " to contain " + "<name>" + NAME + "</name>"); | |
| + assertTrue( | |
| + output.contains("<groupId>org.example</groupId>"), | |
| + "Expected " + output + " to contain " + "<groupId>org.example</groupId>"); | |
| } | |
| @Test | |
| @@ -129,7 +135,10 @@ void writeToOutputStreamGeneratesValidXml() { | |
| .outputStream(outputStream) | |
| .content(PluginDescriptor.newBuilder().name(NAME).build()) | |
| .build()); | |
| - assertThat(outputStream.toString()).contains("<name>" + NAME + "</name>"); | |
| + String output = outputStream.toString(); | |
| + assertTrue( | |
| + output.contains("<name>" + NAME + "</name>"), | |
| + "Expected output to contain <name>" + NAME + "</name> but was: " + output); | |
| } | |
| @Test | |
| @@ -139,16 +148,31 @@ void writeToPathGeneratesValidXmlFile() throws Exception { | |
| .path(xmlFile) | |
| .content(PluginDescriptor.newBuilder().name(NAME).build()) | |
| .build()); | |
| - assertThat(Files.readString(xmlFile)).contains("<name>" + NAME + "</name>"); | |
| + String fileContent = Files.readString(xmlFile); | |
| + assertTrue( | |
| + fileContent.contains("<name>" + NAME + "</name>"), | |
| + "Expected file content to contain <name>" + NAME + "</name> but was: " + fileContent); | |
| } | |
| @Test | |
| void fromXmlStringParsesValidXml() { | |
| PluginDescriptor descriptor = defaultPluginXmlFactory.fromXmlString(SAMPLE_PLUGIN_XML); | |
| - assertThat(descriptor.getName()).isEqualTo(NAME); | |
| - assertThat(descriptor.getGroupId()).isEqualTo("org.example"); | |
| - assertThat(descriptor.getArtifactId()).isEqualTo("sample-plugin"); | |
| - assertThat(descriptor.getVersion()).isEqualTo("1.0.0"); | |
| + assertEquals( | |
| + NAME, | |
| + descriptor.getName(), | |
| + "Expected descriptor name to be " + NAME + " but was " + descriptor.getName()); | |
| + assertEquals( | |
| + "org.example", | |
| + descriptor.getGroupId(), | |
| + "Expected descriptor groupId to be org.example but was " + descriptor.getGroupId()); | |
| + assertEquals( | |
| + "sample-plugin", | |
| + descriptor.getArtifactId(), | |
| + "Expected descriptor artifactId to be sample-plugin but was " + descriptor.getArtifactId()); | |
| + assertEquals( | |
| + "1.0.0", | |
| + descriptor.getVersion(), | |
| + "Expected descriptor version to be 1.0.0 but was " + descriptor.getVersion()); | |
| } | |
| @Test | |
| @@ -159,19 +183,39 @@ void toXmlStringGeneratesValidXml() { | |
| .artifactId("sample-plugin") | |
| .version("1.0.0") | |
| .build()); | |
| - assertThat(xml).contains("<name>" + NAME + "</name>"); | |
| - assertThat(xml).contains("<groupId>org.example</groupId>"); | |
| - assertThat(xml).contains("<artifactId>sample-plugin</artifactId>"); | |
| - assertThat(xml).contains("<version>1.0.0</version>"); | |
| + assertTrue( | |
| + xml.contains("<name>" + NAME + "</name>"), | |
| + "Expected " + xml + " to contain " + "<name>" + NAME + "</name>"); | |
| + assertTrue( | |
| + xml.contains("<groupId>org.example</groupId>"), | |
| + "Expected " + xml + " to contain " + "<groupId>org.example</groupId>"); | |
| + assertTrue( | |
| + xml.contains("<artifactId>sample-plugin</artifactId>"), | |
| + "Expected " + xml + " to contain " + "<artifactId>sample-plugin</artifactId>"); | |
| + assertTrue( | |
| + xml.contains("<version>1.0.0</version>"), | |
| + "Expected " + xml + " to contain " + "<version>1.0.0</version>"); | |
| } | |
| @Test | |
| void staticFromXmlParsesValidXml() { | |
| PluginDescriptor descriptor = DefaultPluginXmlFactory.fromXml(SAMPLE_PLUGIN_XML); | |
| - assertThat(descriptor.getName()).isEqualTo(NAME); | |
| - assertThat(descriptor.getGroupId()).isEqualTo("org.example"); | |
| - assertThat(descriptor.getArtifactId()).isEqualTo("sample-plugin"); | |
| - assertThat(descriptor.getVersion()).isEqualTo("1.0.0"); | |
| + assertEquals( | |
| + NAME, | |
| + descriptor.getName(), | |
| + "Expected descriptor name to be " + NAME + " but was " + descriptor.getName()); | |
| + assertEquals( | |
| + "org.example", | |
| + descriptor.getGroupId(), | |
| + "Expected descriptor groupId to be org.example but was " + descriptor.getGroupId()); | |
| + assertEquals( | |
| + "sample-plugin", | |
| + descriptor.getArtifactId(), | |
| + "Expected descriptor artifactId to be sample-plugin but was " + descriptor.getArtifactId()); | |
| + assertEquals( | |
| + "1.0.0", | |
| + descriptor.getVersion(), | |
| + "Expected descriptor version to be 1.0.0 but was " + descriptor.getVersion()); | |
| } | |
| @Test | |
| @@ -182,19 +226,30 @@ void staticToXmlGeneratesValidXml() { | |
| .artifactId("sample-plugin") | |
| .version("1.0.0") | |
| .build()); | |
| - assertThat(xml).contains("<name>" + NAME + "</name>"); | |
| - assertThat(xml).contains("<name>" + NAME + "</name>"); | |
| - assertThat(xml).contains("<groupId>org.example</groupId>"); | |
| - assertThat(xml).contains("<artifactId>sample-plugin</artifactId>"); | |
| - assertThat(xml).contains("<version>1.0.0</version>"); | |
| + assertTrue( | |
| + xml.contains("<name>" + NAME + "</name>"), | |
| + "Expected " + xml + " to contain " + "<name>" + NAME + "</name>"); | |
| + assertTrue( | |
| + xml.contains("<name>" + NAME + "</name>"), | |
| + "Expected " + xml + " to contain " + "<name>" + NAME + "</name>"); | |
| + assertTrue( | |
| + xml.contains("<groupId>org.example</groupId>"), | |
| + "Expected " + xml + " to contain " + "<groupId>org.example</groupId>"); | |
| + assertTrue( | |
| + xml.contains("<artifactId>sample-plugin</artifactId>"), | |
| + "Expected " + xml + " to contain " + "<artifactId>sample-plugin</artifactId>"); | |
| + assertTrue( | |
| + xml.contains("<version>1.0.0</version>"), | |
| + "Expected " + xml + " to contain " + "<version>1.0.0</version>"); | |
| } | |
| @Test | |
| void writeWithFailingWriterThrowsXmlWriterException() { | |
| String unableToWritePlugin = "Unable to write plugin" + randomUUID(); | |
| String ioEx = "ioEx" + randomUUID(); | |
| - XmlWriterException exception = assertThatExceptionOfType(XmlWriterException.class) | |
| - .isThrownBy(() -> defaultPluginXmlFactory.write(XmlWriterRequest.<PluginDescriptor>builder() | |
| + XmlWriterException exception = assertThrows( | |
| + XmlWriterException.class, | |
| + () -> defaultPluginXmlFactory.write(XmlWriterRequest.<PluginDescriptor>builder() | |
| .writer(new Writer() { | |
| @Override | |
| public void write(char[] cbuf, int off, int len) { | |
| @@ -210,55 +265,72 @@ public void close() {} | |
| .content(PluginDescriptor.newBuilder() | |
| .name("Failing Plugin") | |
| .build()) | |
| - .build())) | |
| - .actual(); | |
| - assertThat(exception.getMessage()).contains(unableToWritePlugin); | |
| - assertThat(exception.getCause()).isInstanceOf(XmlWriterException.class); | |
| - assertThat(exception.getCause().getCause().getMessage()).isEqualTo(ioEx); | |
| - assertThat(exception.getCause().getMessage()).isEqualTo(unableToWritePlugin); | |
| + .build())); | |
| + assertTrue(exception.getMessage().contains(unableToWritePlugin)); | |
| + assertInstanceOf(XmlWriterException.class, exception.getCause()); | |
| + assertEquals(ioEx, exception.getCause().getCause().getMessage()); | |
| + assertEquals(unableToWritePlugin, exception.getCause().getMessage()); | |
| } | |
| @Test | |
| void writeWithNoTargetThrowsIllegalArgumentException() { | |
| - assertThat(assertThrows( | |
| - IllegalArgumentException.class, | |
| - () -> defaultPluginXmlFactory.write(XmlWriterRequest.<PluginDescriptor>builder() | |
| - .content(PluginDescriptor.newBuilder() | |
| - .name("No Output Plugin") | |
| - .build()) | |
| - .build())) | |
| - .getMessage()) | |
| - .isEqualTo("writer, outputStream or path must be non null"); | |
| + IllegalArgumentException exception = assertThrows( | |
| + IllegalArgumentException.class, | |
| + () -> defaultPluginXmlFactory.write(XmlWriterRequest.<PluginDescriptor>builder() | |
| + .content(PluginDescriptor.newBuilder() | |
| + .name("No Output Plugin") | |
| + .build()) | |
| + .build())); | |
| + assertEquals( | |
| + "writer, outputStream or path must be non null", | |
| + exception.getMessage(), | |
| + "Expected specific error message but was: " + exception.getMessage()); | |
| } | |
| @Test | |
| void readMalformedXmlThrowsXmlReaderException() { | |
| - XmlReaderException exception = assertThatExceptionOfType(XmlReaderException.class) | |
| - .isThrownBy(() -> defaultPluginXmlFactory.read(XmlReaderRequest.builder() | |
| + XmlReaderException exception = assertThrows( | |
| + XmlReaderException.class, | |
| + () -> defaultPluginXmlFactory.read(XmlReaderRequest.builder() | |
| .inputStream(new ByteArrayInputStream("<plugin><name>Broken Plugin".getBytes())) | |
| - .build())) | |
| - .actual(); | |
| - assertThat(exception.getMessage()).contains("Unable to read plugin"); | |
| - assertThat(exception.getCause()).isInstanceOf(WstxEOFException.class); | |
| + .build())); | |
| + assertTrue(exception.getMessage().contains("Unable to read plugin")); | |
| + assertInstanceOf(WstxEOFException.class, exception.getCause()); | |
| } | |
| @Test | |
| void locateExistingPomWithFilePathShouldReturnSameFileIfRegularFile() throws IOException { | |
| Path pomFile = Files.createTempFile(tempDir, "pom", ".xml"); | |
| DefaultModelProcessor processor = new DefaultModelProcessor(mock(ModelXmlFactory.class), List.of()); | |
| - assertThat(processor.locateExistingPom(pomFile)).isEqualTo(pomFile); | |
| + assertEquals( | |
| + pomFile, processor.locateExistingPom(pomFile), "Expected locateExistingPom to return the same file"); | |
| } | |
| @Test | |
| void readFromUrlParsesPluginDescriptorCorrectly() throws Exception { | |
| Path xmlFile = tempDir.resolve("plugin.xml"); | |
| Files.write(xmlFile, SAMPLE_PLUGIN_XML.getBytes()); | |
| - PluginDescriptor descriptor = defaultPluginXmlFactory.read( | |
| - XmlReaderRequest.builder().url(xmlFile.toUri().toURL()).build()); | |
| - assertThat(descriptor.getName()).isEqualTo(NAME); | |
| - assertThat(descriptor.getGroupId()).isEqualTo("org.example"); | |
| - assertThat(descriptor.getArtifactId()).isEqualTo("sample-plugin"); | |
| - assertThat(descriptor.getVersion()).isEqualTo("1.0.0"); | |
| + PluginDescriptor descriptor; | |
| + try (InputStream inputStream = xmlFile.toUri().toURL().openStream()) { | |
| + descriptor = defaultPluginXmlFactory.read( | |
| + XmlReaderRequest.builder().inputStream(inputStream).build()); | |
| + } | |
| + assertEquals( | |
| + NAME, | |
| + descriptor.getName(), | |
| + "Expected descriptor name to be " + NAME + " but was " + descriptor.getName()); | |
| + assertEquals( | |
| + "org.example", | |
| + descriptor.getGroupId(), | |
| + "Expected descriptor groupId to be org.example but was " + descriptor.getGroupId()); | |
| + assertEquals( | |
| + "sample-plugin", | |
| + descriptor.getArtifactId(), | |
| + "Expected descriptor artifactId to be sample-plugin but was " + descriptor.getArtifactId()); | |
| + assertEquals( | |
| + "1.0.0", | |
| + descriptor.getVersion(), | |
| + "Expected descriptor version to be 1.0.0 but was " + descriptor.getVersion()); | |
| } | |
| @Test | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultProblemCollectorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultProblemCollectorTest.java | |
| index b6623e0337..dbfba563fb 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultProblemCollectorTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultProblemCollectorTest.java | |
| @@ -37,16 +37,16 @@ void severityFatalDetection() { | |
| ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5); | |
| assertFalse(collector.hasProblemsFor(BuilderProblem.Severity.WARNING)); | |
| - assertFalse(collector.hasErrorProblems()); | |
| - assertFalse(collector.hasFatalProblems()); | |
| + assertFalse(collector.hasErrorProblems(), "Expected " + collector + ".hasErrorProblems() to return false"); | |
| + assertFalse(collector.hasFatalProblems(), "Expected " + collector + ".hasFatalProblems() to return false"); | |
| collector.reportProblem( | |
| new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.FATAL)); | |
| // fatal triggers all | |
| assertTrue(collector.hasProblemsFor(BuilderProblem.Severity.WARNING)); | |
| - assertTrue(collector.hasErrorProblems()); | |
| - assertTrue(collector.hasFatalProblems()); | |
| + assertTrue(collector.hasErrorProblems(), "Expected " + collector + ".hasErrorProblems() to return true"); | |
| + assertTrue(collector.hasFatalProblems(), "Expected " + collector + ".hasFatalProblems() to return true"); | |
| } | |
| @Test | |
| @@ -54,16 +54,16 @@ void severityErrorDetection() { | |
| ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5); | |
| assertFalse(collector.hasProblemsFor(BuilderProblem.Severity.WARNING)); | |
| - assertFalse(collector.hasErrorProblems()); | |
| - assertFalse(collector.hasFatalProblems()); | |
| + assertFalse(collector.hasErrorProblems(), "Expected " + collector + ".hasErrorProblems() to return false"); | |
| + assertFalse(collector.hasFatalProblems(), "Expected " + collector + ".hasFatalProblems() to return false"); | |
| collector.reportProblem( | |
| new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.ERROR)); | |
| // error triggers error + warning | |
| assertTrue(collector.hasProblemsFor(BuilderProblem.Severity.WARNING)); | |
| - assertTrue(collector.hasErrorProblems()); | |
| - assertFalse(collector.hasFatalProblems()); | |
| + assertTrue(collector.hasErrorProblems(), "Expected " + collector + ".hasErrorProblems() to return true"); | |
| + assertFalse(collector.hasFatalProblems(), "Expected " + collector + ".hasFatalProblems() to return false"); | |
| } | |
| @Test | |
| @@ -71,16 +71,16 @@ void severityWarningDetection() { | |
| ProblemCollector<BuilderProblem> collector = ProblemCollector.create(5); | |
| assertFalse(collector.hasProblemsFor(BuilderProblem.Severity.WARNING)); | |
| - assertFalse(collector.hasErrorProblems()); | |
| - assertFalse(collector.hasFatalProblems()); | |
| + assertFalse(collector.hasErrorProblems(), "Expected " + collector + ".hasErrorProblems() to return false"); | |
| + assertFalse(collector.hasFatalProblems(), "Expected " + collector + ".hasFatalProblems() to return false"); | |
| collector.reportProblem( | |
| new DefaultBuilderProblem("source", 0, 0, null, "message", BuilderProblem.Severity.WARNING)); | |
| // warning triggers warning only | |
| assertTrue(collector.hasProblemsFor(BuilderProblem.Severity.WARNING)); | |
| - assertFalse(collector.hasErrorProblems()); | |
| - assertFalse(collector.hasFatalProblems()); | |
| + assertFalse(collector.hasErrorProblems(), "Expected " + collector + ".hasErrorProblems() to return false"); | |
| + assertFalse(collector.hasFatalProblems(), "Expected " + collector + ".hasFatalProblems() to return false"); | |
| } | |
| @Test | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultToolchainManagerTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultToolchainManagerTest.java | |
| index c916d0ca5f..cec13a2019 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultToolchainManagerTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultToolchainManagerTest.java | |
| @@ -87,7 +87,8 @@ void getToolchainsWithValidTypeAndRequirements() { | |
| @Test | |
| void getToolchainsWithInvalidType() { | |
| List<Toolchain> result = manager.getToolchains(session, "invalid", null); | |
| - assertTrue(result.isEmpty()); | |
| + assertTrue( | |
| + result.isEmpty(), "Expected collection to be empty but had " + result.size() + " elements: " + result); | |
| } | |
| @Test | |
| @@ -106,7 +107,7 @@ void storeAndRetrieveToolchainFromBuildContext() { | |
| manager.storeToolchainToBuildContext(session, mockToolchain); | |
| Optional<Toolchain> result = manager.getToolchainFromBuildContext(session, "jdk"); | |
| - assertTrue(result.isPresent()); | |
| + assertTrue(result.isPresent(), "Expected " + result + ".isPresent() to return true"); | |
| assertEquals(mockToolchain, result.get()); | |
| } | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java | |
| index c7d860bc0b..cc5297c724 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java | |
| @@ -86,8 +86,81 @@ public void testExcludeOmission() { | |
| List<String> excludes = List.of("baz/**"); | |
| PathMatcher matcher = PathSelector.of(directory, includes, excludes, true); | |
| String s = matcher.toString(); | |
| - assertTrue(s.contains("glob:**/*.java")); | |
| - assertFalse(s.contains("project.pj")); // Unnecessary exclusion should have been omitted. | |
| - assertFalse(s.contains(".DS_Store")); | |
| + assertTrue( | |
| + s.contains("glob:**/*.java") || s.contains("glob:{**/,}*.java"), | |
| + "Expected " + s + " to contain " + "glob:**/*.java" + " or " + "glob:{**/,}*.java"); | |
| + assertFalse( | |
| + s.contains("project.pj"), | |
| + "Expected " + s + " to not contain " + "project.pj"); // Unnecessary exclusion should have been omitted. | |
| + assertFalse(s.contains(".DS_Store"), "Expected " + s + " to not contain " + ".DS_Store"); | |
| + } | |
| + | |
| + /** | |
| + * Test to verify the current behavior of ** patterns before implementing brace expansion improvement. | |
| + * This test documents the expected behavior that must be preserved after the optimization. | |
| + */ | |
| + @Test | |
| + public void testDoubleAsteriskPatterns(final @TempDir Path directory) throws IOException { | |
| + // Create a nested directory structure to test ** behavior | |
| + Path src = Files.createDirectory(directory.resolve("src")); | |
| + Path main = Files.createDirectory(src.resolve("main")); | |
| + Path java = Files.createDirectory(main.resolve("java")); | |
| + Path test = Files.createDirectory(src.resolve("test")); | |
| + Path testJava = Files.createDirectory(test.resolve("java")); | |
| + | |
| + // Create files at different levels | |
| + Files.createFile(directory.resolve("root.java")); | |
| + Files.createFile(src.resolve("src.java")); | |
| + Files.createFile(main.resolve("main.java")); | |
| + Files.createFile(java.resolve("deep.java")); | |
| + Files.createFile(test.resolve("test.java")); | |
| + Files.createFile(testJava.resolve("testdeep.java")); | |
| + | |
| + // Test that ** matches zero or more directories (POSIX behavior) | |
| + PathMatcher matcher = PathSelector.of(directory, List.of("src/**/test/**/*.java"), null, false); | |
| + | |
| + // Should match files in src/test/java/ (** matches zero dirs before test, zero dirs after test) | |
| + assertTrue(matcher.matches(testJava.resolve("testdeep.java"))); | |
| + | |
| + // Should also match files directly in src/test/ (** matches zero dirs after test) | |
| + assertTrue(matcher.matches(test.resolve("test.java"))); | |
| + | |
| + // Should NOT match files in other paths | |
| + assertFalse(matcher.matches(directory.resolve("root.java"))); | |
| + assertFalse(matcher.matches(src.resolve("src.java"))); | |
| + assertFalse(matcher.matches(main.resolve("main.java"))); | |
| + assertFalse(matcher.matches(java.resolve("deep.java"))); | |
| + } | |
| + | |
| + @Test | |
| + public void testLiteralBracesAreEscapedInMavenSyntax(@TempDir Path directory) throws IOException { | |
| + // Create a file with literal braces in the name | |
| + Files.createDirectories(directory.resolve("dir")); | |
| + Path file = directory.resolve("dir/foo{bar}.txt"); | |
| + Files.createFile(file); | |
| + | |
| + // In Maven syntax (no explicit glob:), user-provided braces must be treated literally | |
| + PathMatcher matcher = PathSelector.of(directory, List.of("**/foo{bar}.txt"), null, false); | |
| + | |
| + assertTrue(matcher.matches(file)); | |
| + } | |
| + | |
| + @Test | |
| + public void testBraceAlternationOnlyWithExplicitGlob(@TempDir Path directory) throws IOException { | |
| + // Create src/main/java and src/test/java with files | |
| + Path mainJava = Files.createDirectories(directory.resolve("src/main/java")); | |
| + Path testJava = Files.createDirectories(directory.resolve("src/test/java")); | |
| + Path mainFile = Files.createFile(mainJava.resolve("Main.java")); | |
| + Path testFile = Files.createFile(testJava.resolve("Test.java")); | |
| + | |
| + // Without explicit glob:, braces from user input are escaped and treated literally -> no matches | |
| + PathMatcher mavenSyntax = PathSelector.of(directory, List.of("src/{main,test}/**/*.java"), null, false); | |
| + assertFalse(mavenSyntax.matches(mainFile)); | |
| + assertFalse(mavenSyntax.matches(testFile)); | |
| + | |
| + // With explicit glob:, braces should act as alternation and match both | |
| + PathMatcher explicitGlob = PathSelector.of(directory, List.of("glob:src/{main,test}/**/*.java"), null, false); | |
| + assertTrue(explicitGlob.matches(mainFile)); | |
| + assertTrue(explicitGlob.matches(testFile)); | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java | |
| index 33294298ad..94acf3848c 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java | |
| @@ -56,7 +56,9 @@ void testInterpretTraceWithArtifactRequest() { | |
| String result = RequestTraceHelper.interpretTrace(false, trace); | |
| - assertTrue(result.startsWith("artifact request for ")); | |
| + assertTrue( | |
| + result.startsWith("artifact request for "), | |
| + "Expected " + result + " to start with " + "artifact request for "); | |
| } | |
| @Test | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/AbstractRequestCacheTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/AbstractRequestCacheTest.java | |
| index 6683bee0de..5de697c15e 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/AbstractRequestCacheTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/AbstractRequestCacheTest.java | |
| @@ -235,6 +235,10 @@ void addFailure(TestRequest request, RuntimeException exception) { | |
| failures.put(request, exception); | |
| } | |
| + public CacheStatistics getStatistics() { | |
| + return null; // Not implemented for test | |
| + } | |
| + | |
| @Override | |
| protected <REQ extends Request<?>, REP extends Result<REQ>> CachingSupplier<REQ, REP> doCache( | |
| REQ req, Function<REQ, REP> supplier) { | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/CacheConfigurationTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/CacheConfigurationTest.java | |
| new file mode 100644 | |
| index 0000000000..0edc19236d | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/CacheConfigurationTest.java | |
| @@ -0,0 +1,358 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.HashMap; | |
| +import java.util.List; | |
| +import java.util.Map; | |
| + | |
| +import org.apache.maven.api.Constants; | |
| +import org.apache.maven.api.RemoteRepository; | |
| +import org.apache.maven.api.Session; | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| +import org.apache.maven.api.model.Profile; | |
| +import org.apache.maven.api.services.ModelBuilderRequest; | |
| +import org.apache.maven.api.services.ModelSource; | |
| +import org.apache.maven.api.services.ModelTransformer; | |
| +import org.apache.maven.api.services.Request; | |
| +import org.apache.maven.api.services.RequestTrace; | |
| +import org.junit.jupiter.api.BeforeEach; | |
| +import org.junit.jupiter.api.Test; | |
| +import org.mockito.Mock; | |
| +import org.mockito.MockitoAnnotations; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| +import static org.mockito.Mockito.mock; | |
| +import static org.mockito.Mockito.when; | |
| + | |
| +/** | |
| + * Test for cache configuration functionality. | |
| + */ | |
| +class CacheConfigurationTest { | |
| + | |
| + @Mock | |
| + private Session session; | |
| + | |
| + @Mock | |
| + private Request<?> request; | |
| + | |
| + @Mock | |
| + private ModelBuilderRequest modelBuilderRequest; | |
| + | |
| + private Map<String, String> userProperties; | |
| + | |
| + @BeforeEach | |
| + void setUp() { | |
| + MockitoAnnotations.openMocks(this); | |
| + userProperties = new HashMap<>(); | |
| + when(session.getUserProperties()).thenReturn(userProperties); | |
| + when(request.getSession()).thenReturn(session); | |
| + when(modelBuilderRequest.getSession()).thenReturn(session); | |
| + } | |
| + | |
| + @Test | |
| + void testDefaultConfiguration() { | |
| + CacheConfig config = CacheConfigurationResolver.resolveConfig(request, session); | |
| + assertEquals(CacheRetention.REQUEST_SCOPED, config.scope()); | |
| + assertEquals(Cache.ReferenceType.SOFT, config.referenceType()); | |
| + } | |
| + | |
| + @Test | |
| + void testParseSimpleSelector() { | |
| + String configString = "ModelBuilderRequest { scope: session, ref: hard }"; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + | |
| + assertEquals(1, selectors.size()); | |
| + CacheSelector selector = selectors.get(0); | |
| + assertEquals("ModelBuilderRequest", selector.requestType()); | |
| + assertNull(selector.parentRequestType()); | |
| + assertEquals(CacheRetention.SESSION_SCOPED, selector.config().scope()); | |
| + assertEquals(Cache.ReferenceType.HARD, selector.config().referenceType()); | |
| + } | |
| + | |
| + @Test | |
| + void testParseParentChildSelector() { | |
| + String configString = "ModelBuildRequest ModelBuilderRequest { ref: weak }"; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + | |
| + assertEquals(1, selectors.size()); | |
| + CacheSelector selector = selectors.get(0); | |
| + assertEquals("ModelBuilderRequest", selector.requestType()); | |
| + assertEquals("ModelBuildRequest", selector.parentRequestType()); | |
| + assertNull(selector.config().scope()); // not specified | |
| + assertEquals(Cache.ReferenceType.WEAK, selector.config().referenceType()); | |
| + } | |
| + | |
| + @Test | |
| + void testParseWildcardSelector() { | |
| + String configString = "* ModelBuilderRequest { scope: persistent }"; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + | |
| + assertEquals(1, selectors.size()); | |
| + CacheSelector selector = selectors.get(0); | |
| + assertEquals("ModelBuilderRequest", selector.requestType()); | |
| + assertEquals("*", selector.parentRequestType()); | |
| + assertEquals(CacheRetention.PERSISTENT, selector.config().scope()); | |
| + assertNull(selector.config().referenceType()); // not specified | |
| + } | |
| + | |
| + @Test | |
| + void testParseMultipleSelectors() { | |
| + String configString = | |
| + """ | |
| + ModelBuilderRequest { scope: session, ref: soft } | |
| + ArtifactResolutionRequest { scope: request, ref: hard } | |
| + * VersionRangeRequest { ref: weak } | |
| + """; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + | |
| + assertEquals(3, selectors.size()); | |
| + | |
| + // Check first selector | |
| + CacheSelector first = selectors.get(0); | |
| + assertEquals("VersionRangeRequest", first.requestType()); | |
| + assertEquals("*", first.parentRequestType()); | |
| + | |
| + // Check second selector | |
| + CacheSelector second = selectors.get(1); | |
| + assertEquals("ModelBuilderRequest", second.requestType()); | |
| + assertNull(second.parentRequestType()); | |
| + | |
| + // Check third selector | |
| + CacheSelector third = selectors.get(2); | |
| + assertEquals("ArtifactResolutionRequest", third.requestType()); | |
| + assertNull(third.parentRequestType()); | |
| + } | |
| + | |
| + @Test | |
| + void testConfigurationResolution() { | |
| + userProperties.put(Constants.MAVEN_CACHE_CONFIG_PROPERTY, "ModelBuilderRequest { scope: session, ref: hard }"); | |
| + | |
| + ModelBuilderRequest request = new TestRequestImpl(); | |
| + | |
| + CacheConfig config = CacheConfigurationResolver.resolveConfig(request, session); | |
| + assertEquals(CacheRetention.SESSION_SCOPED, config.scope()); | |
| + assertEquals(Cache.ReferenceType.HARD, config.referenceType()); | |
| + } | |
| + | |
| + @Test | |
| + void testSelectorMatching() { | |
| + PartialCacheConfig config = | |
| + PartialCacheConfig.complete(CacheRetention.SESSION_SCOPED, Cache.ReferenceType.HARD); | |
| + CacheSelector selector = CacheSelector.forRequestType("ModelBuilderRequest", config); | |
| + | |
| + ModelBuilderRequest request = new TestRequestImpl(); | |
| + | |
| + assertTrue(selector.matches(request)); | |
| + } | |
| + | |
| + @Test | |
| + void testInterfaceMatching() { | |
| + // Test that selectors match against implemented interfaces, not just class names | |
| + PartialCacheConfig config = | |
| + PartialCacheConfig.complete(CacheRetention.SESSION_SCOPED, Cache.ReferenceType.HARD); | |
| + CacheSelector selector = CacheSelector.forRequestType("ModelBuilderRequest", config); | |
| + | |
| + // Create a test request instance that implements ModelBuilderRequest interface | |
| + TestRequestImpl testRequest = new TestRequestImpl(); | |
| + | |
| + // Should match because TestRequestImpl implements ModelBuilderRequest | |
| + assertTrue(selector.matches(testRequest)); | |
| + | |
| + // Test with a selector for a different interface | |
| + CacheSelector requestSelector = CacheSelector.forRequestType("Request", config); | |
| + assertTrue(requestSelector.matches(testRequest)); // Should match Request interface | |
| + } | |
| + | |
| + // Test implementation class that implements ModelBuilderRequest | |
| + private static class TestRequestImpl implements ModelBuilderRequest { | |
| + @Override | |
| + public Session getSession() { | |
| + return null; | |
| + } | |
| + | |
| + @Override | |
| + public RequestTrace getTrace() { | |
| + return null; | |
| + } | |
| + | |
| + @Override | |
| + public RequestType getRequestType() { | |
| + return RequestType.BUILD_PROJECT; | |
| + } | |
| + | |
| + @Override | |
| + public boolean isLocationTracking() { | |
| + return false; | |
| + } | |
| + | |
| + @Override | |
| + public boolean isRecursive() { | |
| + return false; | |
| + } | |
| + | |
| + @Override | |
| + public ModelSource getSource() { | |
| + return null; | |
| + } | |
| + | |
| + @Override | |
| + public java.util.Collection<Profile> getProfiles() { | |
| + return java.util.List.of(); | |
| + } | |
| + | |
| + @Override | |
| + public java.util.List<String> getActiveProfileIds() { | |
| + return java.util.List.of(); | |
| + } | |
| + | |
| + @Override | |
| + public java.util.List<String> getInactiveProfileIds() { | |
| + return java.util.List.of(); | |
| + } | |
| + | |
| + @Override | |
| + public java.util.Map<String, String> getSystemProperties() { | |
| + return java.util.Map.of(); | |
| + } | |
| + | |
| + @Override | |
| + public java.util.Map<String, String> getUserProperties() { | |
| + return java.util.Map.of(); | |
| + } | |
| + | |
| + @Override | |
| + public RepositoryMerging getRepositoryMerging() { | |
| + return RepositoryMerging.POM_DOMINANT; | |
| + } | |
| + | |
| + @Override | |
| + public java.util.List<RemoteRepository> getRepositories() { | |
| + return java.util.List.of(); | |
| + } | |
| + | |
| + @Override | |
| + public ModelTransformer getLifecycleBindingsInjector() { | |
| + return null; | |
| + } | |
| + } | |
| + | |
| + @Test | |
| + void testInvalidConfiguration() { | |
| + String configString = "InvalidSyntax without braces"; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + assertTrue(selectors.isEmpty()); | |
| + } | |
| + | |
| + @Test | |
| + void testEmptyConfiguration() { | |
| + String configString = ""; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + assertTrue(selectors.isEmpty()); | |
| + } | |
| + | |
| + @Test | |
| + void testPartialConfigurationMerging() { | |
| + userProperties.put( | |
| + Constants.MAVEN_CACHE_CONFIG_PROPERTY, | |
| + """ | |
| + ModelBuilderRequest { scope: session } | |
| + * ModelBuilderRequest { ref: hard } | |
| + """); | |
| + | |
| + ModelBuilderRequest request = new TestRequestImpl(); | |
| + | |
| + CacheConfig config = CacheConfigurationResolver.resolveConfig(request, session); | |
| + assertEquals(CacheRetention.SESSION_SCOPED, config.scope()); // from first selector | |
| + assertEquals(Cache.ReferenceType.HARD, config.referenceType()); // from second selector | |
| + } | |
| + | |
| + @Test | |
| + void testPartialConfigurationScopeOnly() { | |
| + String configString = "ModelBuilderRequest { scope: persistent }"; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + | |
| + assertEquals(1, selectors.size()); | |
| + CacheSelector selector = selectors.get(0); | |
| + assertEquals(CacheRetention.PERSISTENT, selector.config().scope()); | |
| + assertNull(selector.config().referenceType()); | |
| + | |
| + // Test conversion to complete config | |
| + CacheConfig complete = selector.config().toComplete(); | |
| + assertEquals(CacheRetention.PERSISTENT, complete.scope()); | |
| + assertEquals(Cache.ReferenceType.SOFT, complete.referenceType()); // default | |
| + } | |
| + | |
| + @Test | |
| + void testPartialConfigurationRefOnly() { | |
| + String configString = "ModelBuilderRequest { ref: weak }"; | |
| + List<CacheSelector> selectors = CacheSelectorParser.parse(configString); | |
| + | |
| + assertEquals(1, selectors.size()); | |
| + CacheSelector selector = selectors.get(0); | |
| + assertNull(selector.config().scope()); | |
| + assertEquals(Cache.ReferenceType.WEAK, selector.config().referenceType()); | |
| + | |
| + // Test conversion to complete config | |
| + CacheConfig complete = selector.config().toComplete(); | |
| + assertEquals(CacheRetention.REQUEST_SCOPED, complete.scope()); // default | |
| + assertEquals(Cache.ReferenceType.WEAK, complete.referenceType()); | |
| + } | |
| + | |
| + @Test | |
| + void testPartialConfigurationMergeLogic() { | |
| + PartialCacheConfig base = PartialCacheConfig.withScope(CacheRetention.SESSION_SCOPED); | |
| + PartialCacheConfig override = PartialCacheConfig.withReferenceType(Cache.ReferenceType.HARD); | |
| + | |
| + PartialCacheConfig merged = base.mergeWith(override); | |
| + assertEquals(CacheRetention.SESSION_SCOPED, merged.scope()); | |
| + assertEquals(Cache.ReferenceType.HARD, merged.referenceType()); | |
| + | |
| + // Test override precedence | |
| + PartialCacheConfig override2 = PartialCacheConfig.complete(CacheRetention.PERSISTENT, Cache.ReferenceType.WEAK); | |
| + PartialCacheConfig merged2 = base.mergeWith(override2); | |
| + assertEquals(CacheRetention.SESSION_SCOPED, merged2.scope()); // base takes precedence | |
| + assertEquals(Cache.ReferenceType.WEAK, merged2.referenceType()); // from override2 | |
| + } | |
| + | |
| + @Test | |
| + void testParentInterfaceMatching() { | |
| + // Test that parent request matching works with interfaces | |
| + PartialCacheConfig config = | |
| + PartialCacheConfig.complete(CacheRetention.SESSION_SCOPED, Cache.ReferenceType.HARD); | |
| + CacheSelector selector = CacheSelector.forParentAndRequestType("ModelBuilderRequest", "Request", config); | |
| + | |
| + // Create a child request with a parent that implements ModelBuilderRequest | |
| + TestRequestImpl parentRequest = new TestRequestImpl(); | |
| + | |
| + // Mock the trace to simulate parent-child relationship | |
| + RequestTrace parentTrace = mock(RequestTrace.class); | |
| + RequestTrace childTrace = mock(RequestTrace.class); | |
| + Request childRequest = mock(Request.class); | |
| + | |
| + when(parentTrace.data()).thenReturn(parentRequest); | |
| + when(childTrace.parent()).thenReturn(parentTrace); | |
| + when(childRequest.getTrace()).thenReturn(childTrace); | |
| + | |
| + // Should match because parent implements ModelBuilderRequest interface | |
| + assertTrue(selector.matches(childRequest)); | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/CacheStatisticsTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/CacheStatisticsTest.java | |
| new file mode 100644 | |
| index 0000000000..b7da34c0d9 | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/CacheStatisticsTest.java | |
| @@ -0,0 +1,183 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.Map; | |
| + | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * Test for cache statistics functionality with the improved cache architecture. | |
| + */ | |
| +class CacheStatisticsTest { | |
| + | |
| + private final CacheStatistics statistics = new CacheStatistics(); | |
| + | |
| + @Test | |
| + void testInitialStatistics() { | |
| + assertEquals(0, statistics.getTotalRequests()); | |
| + assertEquals(0, statistics.getCacheHits()); | |
| + assertEquals(0, statistics.getCacheMisses()); | |
| + assertEquals(0.0, statistics.getHitRatio(), 0.01); | |
| + assertEquals(0.0, statistics.getMissRatio(), 0.01); | |
| + assertEquals(0, statistics.getCachedExceptions()); | |
| + } | |
| + | |
| + @Test | |
| + void testBasicStatisticsTracking() { | |
| + // Record some hits and misses | |
| + statistics.recordMiss("TestRequest", CacheRetention.SESSION_SCOPED); | |
| + assertEquals(1, statistics.getTotalRequests()); | |
| + assertEquals(0, statistics.getCacheHits()); | |
| + assertEquals(1, statistics.getCacheMisses()); | |
| + assertEquals(0.0, statistics.getHitRatio(), 0.01); | |
| + assertEquals(100.0, statistics.getMissRatio(), 0.01); | |
| + | |
| + // Record a hit | |
| + statistics.recordHit("TestRequest", CacheRetention.SESSION_SCOPED); | |
| + assertEquals(2, statistics.getTotalRequests()); | |
| + assertEquals(1, statistics.getCacheHits()); | |
| + assertEquals(1, statistics.getCacheMisses()); | |
| + assertEquals(50.0, statistics.getHitRatio(), 0.01); | |
| + assertEquals(50.0, statistics.getMissRatio(), 0.01); | |
| + | |
| + // Record another miss | |
| + statistics.recordMiss("TestRequest", CacheRetention.SESSION_SCOPED); | |
| + assertEquals(3, statistics.getTotalRequests()); | |
| + assertEquals(1, statistics.getCacheHits()); | |
| + assertEquals(2, statistics.getCacheMisses()); | |
| + assertEquals(33.33, statistics.getHitRatio(), 0.01); | |
| + assertEquals(66.67, statistics.getMissRatio(), 0.01); | |
| + } | |
| + | |
| + @Test | |
| + void testRequestTypeStatistics() { | |
| + // Record statistics for different request types | |
| + statistics.recordMiss("TestRequestImpl", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordHit("TestRequestImpl", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordMiss("AnotherRequest", CacheRetention.PERSISTENT); | |
| + | |
| + Map<String, CacheStatistics.RequestTypeStatistics> requestStats = statistics.getRequestTypeStatistics(); | |
| + assertNotNull(requestStats); | |
| + assertTrue(requestStats.containsKey("TestRequestImpl")); | |
| + assertTrue(requestStats.containsKey("AnotherRequest")); | |
| + | |
| + CacheStatistics.RequestTypeStatistics testRequestStats = requestStats.get("TestRequestImpl"); | |
| + assertEquals("TestRequestImpl", testRequestStats.getRequestType()); | |
| + assertEquals(1, testRequestStats.getHits()); | |
| + assertEquals(1, testRequestStats.getMisses()); | |
| + assertEquals(2, testRequestStats.getTotal()); | |
| + assertEquals(50.0, testRequestStats.getHitRatio(), 0.01); | |
| + | |
| + CacheStatistics.RequestTypeStatistics anotherRequestStats = requestStats.get("AnotherRequest"); | |
| + assertEquals("AnotherRequest", anotherRequestStats.getRequestType()); | |
| + assertEquals(0, anotherRequestStats.getHits()); | |
| + assertEquals(1, anotherRequestStats.getMisses()); | |
| + assertEquals(1, anotherRequestStats.getTotal()); | |
| + assertEquals(0.0, anotherRequestStats.getHitRatio(), 0.01); | |
| + } | |
| + | |
| + @Test | |
| + void testRetentionStatistics() { | |
| + // Record statistics for different retention policies | |
| + statistics.recordMiss("TestRequest", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordHit("TestRequest", CacheRetention.PERSISTENT); | |
| + statistics.recordMiss("TestRequest", CacheRetention.REQUEST_SCOPED); | |
| + | |
| + Map<CacheRetention, CacheStatistics.RetentionStatistics> retentionStats = statistics.getRetentionStatistics(); | |
| + assertNotNull(retentionStats); | |
| + assertTrue(retentionStats.containsKey(CacheRetention.SESSION_SCOPED)); | |
| + assertTrue(retentionStats.containsKey(CacheRetention.PERSISTENT)); | |
| + assertTrue(retentionStats.containsKey(CacheRetention.REQUEST_SCOPED)); | |
| + | |
| + CacheStatistics.RetentionStatistics sessionStats = retentionStats.get(CacheRetention.SESSION_SCOPED); | |
| + assertEquals(CacheRetention.SESSION_SCOPED, sessionStats.getRetention()); | |
| + assertEquals(0, sessionStats.getHits()); | |
| + assertEquals(1, sessionStats.getMisses()); | |
| + assertEquals(1, sessionStats.getTotal()); | |
| + assertEquals(0.0, sessionStats.getHitRatio(), 0.01); | |
| + | |
| + CacheStatistics.RetentionStatistics persistentStats = retentionStats.get(CacheRetention.PERSISTENT); | |
| + assertEquals(CacheRetention.PERSISTENT, persistentStats.getRetention()); | |
| + assertEquals(1, persistentStats.getHits()); | |
| + assertEquals(0, persistentStats.getMisses()); | |
| + assertEquals(1, persistentStats.getTotal()); | |
| + assertEquals(100.0, persistentStats.getHitRatio(), 0.01); | |
| + | |
| + CacheStatistics.RetentionStatistics requestStats = retentionStats.get(CacheRetention.REQUEST_SCOPED); | |
| + assertEquals(CacheRetention.REQUEST_SCOPED, requestStats.getRetention()); | |
| + assertEquals(0, requestStats.getHits()); | |
| + assertEquals(1, requestStats.getMisses()); | |
| + assertEquals(1, requestStats.getTotal()); | |
| + assertEquals(0.0, requestStats.getHitRatio(), 0.01); | |
| + } | |
| + | |
| + @Test | |
| + void testCacheSizes() { | |
| + // Register some cache size suppliers | |
| + statistics.registerCacheSizeSupplier(CacheRetention.PERSISTENT, () -> 42L); | |
| + statistics.registerCacheSizeSupplier(CacheRetention.SESSION_SCOPED, () -> 17L); | |
| + statistics.registerCacheSizeSupplier(CacheRetention.REQUEST_SCOPED, () -> 3L); | |
| + | |
| + Map<CacheRetention, Long> cacheSizes = statistics.getCacheSizes(); | |
| + assertNotNull(cacheSizes); | |
| + assertTrue(cacheSizes.containsKey(CacheRetention.PERSISTENT)); | |
| + assertTrue(cacheSizes.containsKey(CacheRetention.SESSION_SCOPED)); | |
| + assertTrue(cacheSizes.containsKey(CacheRetention.REQUEST_SCOPED)); | |
| + | |
| + assertEquals(42L, cacheSizes.get(CacheRetention.PERSISTENT)); | |
| + assertEquals(17L, cacheSizes.get(CacheRetention.SESSION_SCOPED)); | |
| + assertEquals(3L, cacheSizes.get(CacheRetention.REQUEST_SCOPED)); | |
| + } | |
| + | |
| + @Test | |
| + void testCachedExceptions() { | |
| + assertEquals(0, statistics.getCachedExceptions()); | |
| + | |
| + statistics.recordCachedException(); | |
| + assertEquals(1, statistics.getCachedExceptions()); | |
| + | |
| + statistics.recordCachedException(); | |
| + statistics.recordCachedException(); | |
| + assertEquals(3, statistics.getCachedExceptions()); | |
| + } | |
| + | |
| + @Test | |
| + void testDefaultRequestCacheIntegration() { | |
| + DefaultRequestCache cache = new DefaultRequestCache(); | |
| + CacheStatistics stats = cache.getStatistics(); | |
| + | |
| + assertNotNull(stats); | |
| + assertEquals(0, stats.getTotalRequests()); | |
| + assertEquals(0, stats.getCacheHits()); | |
| + assertEquals(0, stats.getCacheMisses()); | |
| + | |
| + // Verify cache size suppliers are registered | |
| + Map<CacheRetention, Long> sizes = stats.getCacheSizes(); | |
| + assertNotNull(sizes); | |
| + assertTrue(sizes.containsKey(CacheRetention.PERSISTENT)); | |
| + assertTrue(sizes.containsKey(CacheRetention.SESSION_SCOPED)); | |
| + assertTrue(sizes.containsKey(CacheRetention.REQUEST_SCOPED)); | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/SoftIdentityMapTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/RefConcurrentMapTest.java | |
| similarity index 57% | |
| rename from impl/maven-impl/src/test/java/org/apache/maven/impl/cache/SoftIdentityMapTest.java | |
| rename to impl/maven-impl/src/test/java/org/apache/maven/impl/cache/RefConcurrentMapTest.java | |
| index 6f64e1b95c..86eb75ff55 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/SoftIdentityMapTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/RefConcurrentMapTest.java | |
| @@ -34,25 +34,69 @@ | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| import static org.junit.jupiter.api.Assertions.assertTrue; | |
| -class SoftIdentityMapTest { | |
| - private SoftIdentityMap<Object, String> map; | |
| +class RefConcurrentMapTest { | |
| + private Cache.RefConcurrentMap<Object, String> softMap; | |
| + private Cache.RefConcurrentMap<Object, String> weakMap; | |
| + private Cache.RefConcurrentMap<Object, String> hardMap; | |
| @BeforeEach | |
| void setUp() { | |
| - map = new SoftIdentityMap<>(); | |
| + softMap = Cache.RefConcurrentMap.newCache(Cache.ReferenceType.SOFT); | |
| + weakMap = Cache.RefConcurrentMap.newCache(Cache.ReferenceType.WEAK); | |
| + hardMap = Cache.RefConcurrentMap.newCache(Cache.ReferenceType.HARD); | |
| } | |
| @Test | |
| - void shouldComputeValueOnlyOnce() { | |
| + void shouldComputeValueOnlyOnceWithSoftMap() { | |
| Object key = new Object(); | |
| AtomicInteger computeCount = new AtomicInteger(0); | |
| - String result1 = map.computeIfAbsent(key, k -> { | |
| + String result1 = softMap.computeIfAbsent(key, k -> { | |
| computeCount.incrementAndGet(); | |
| return "value"; | |
| }); | |
| - String result2 = map.computeIfAbsent(key, k -> { | |
| + String result2 = softMap.computeIfAbsent(key, k -> { | |
| + computeCount.incrementAndGet(); | |
| + return "different value"; | |
| + }); | |
| + | |
| + assertEquals("value", result1); | |
| + assertEquals("value", result2); | |
| + assertEquals(1, computeCount.get()); | |
| + } | |
| + | |
| + @Test | |
| + void shouldComputeValueOnlyOnceWithWeakMap() { | |
| + Object key = new Object(); | |
| + AtomicInteger computeCount = new AtomicInteger(0); | |
| + | |
| + String result1 = weakMap.computeIfAbsent(key, k -> { | |
| + computeCount.incrementAndGet(); | |
| + return "value"; | |
| + }); | |
| + | |
| + String result2 = weakMap.computeIfAbsent(key, k -> { | |
| + computeCount.incrementAndGet(); | |
| + return "different value"; | |
| + }); | |
| + | |
| + assertEquals("value", result1); | |
| + assertEquals("value", result2); | |
| + assertEquals(1, computeCount.get()); | |
| + } | |
| + | |
| + @Test | |
| + void shouldComputeValueOnlyOnceWithHardMap() { | |
| + Object key = new Object(); | |
| + AtomicInteger computeCount = new AtomicInteger(0); | |
| + | |
| + String result1 = hardMap.computeIfAbsent(key, k -> { | |
| + computeCount.incrementAndGet(); | |
| + return "value"; | |
| + }); | |
| + | |
| + String result2 = hardMap.computeIfAbsent(key, k -> { | |
| computeCount.incrementAndGet(); | |
| return "different value"; | |
| }); | |
| @@ -89,7 +133,7 @@ void shouldBeThreadSafe() throws InterruptedException { | |
| // Synchronize threads at the start of each iteration | |
| iterationBarrier.await(); | |
| - String result = map.computeIfAbsent(key, k -> { | |
| + String result = softMap.computeIfAbsent(key, k -> { | |
| sink.accept("Computing value in thread " + threadId + " iteration " | |
| + iteration.get() + " current compute count: " | |
| + computeCount.get()); | |
| @@ -139,7 +183,7 @@ void shouldBeThreadSafe() throws InterruptedException { | |
| } | |
| @Test | |
| - void shouldUseIdentityComparison() { | |
| + void shouldUseEqualsComparison() { | |
| // Create two equal but distinct keys | |
| String key1 = new String("key"); | |
| String key2 = new String("key"); | |
| @@ -149,17 +193,17 @@ void shouldUseIdentityComparison() { | |
| AtomicInteger computeCount = new AtomicInteger(0); | |
| - map.computeIfAbsent(key1, k -> { | |
| + softMap.computeIfAbsent(key1, k -> { | |
| computeCount.incrementAndGet(); | |
| return "value1"; | |
| }); | |
| - map.computeIfAbsent(key2, k -> { | |
| + softMap.computeIfAbsent(key2, k -> { | |
| computeCount.incrementAndGet(); | |
| return "value2"; | |
| }); | |
| - assertEquals(1, computeCount.get(), "Should compute once for equal but distinct keys"); | |
| + assertEquals(1, computeCount.get(), "Should compute once for equal keys (using equals comparison)"); | |
| } | |
| @Test | |
| @@ -170,7 +214,7 @@ void shouldHandleSoftReferences() throws InterruptedException { | |
| // Use a block to ensure the key can be garbage collected | |
| { | |
| Object key = new Object(); | |
| - map.computeIfAbsent(key, k -> { | |
| + softMap.computeIfAbsent(key, k -> { | |
| computeCount.incrementAndGet(); | |
| return "value"; | |
| }); | |
| @@ -182,7 +226,7 @@ void shouldHandleSoftReferences() throws InterruptedException { | |
| // Create a new key and verify that computation happens again | |
| Object newKey = new Object(); | |
| - map.computeIfAbsent(newKey, k -> { | |
| + softMap.computeIfAbsent(newKey, k -> { | |
| computeCount.incrementAndGet(); | |
| return "new value"; | |
| }); | |
| @@ -190,11 +234,87 @@ void shouldHandleSoftReferences() throws InterruptedException { | |
| assertEquals(2, computeCount.get(), "Should compute again after original key is garbage collected"); | |
| } | |
| + @Test | |
| + @SuppressWarnings("checkstyle:AvoidNestedBlocks") | |
| + void shouldNotGarbageCollectHardReferences() throws InterruptedException { | |
| + AtomicInteger computeCount = new AtomicInteger(0); | |
| + Object originalKey; | |
| + | |
| + // Use a block to ensure the key can be garbage collected if it were a weak/soft reference | |
| + { | |
| + originalKey = new Object(); | |
| + hardMap.computeIfAbsent(originalKey, k -> { | |
| + computeCount.incrementAndGet(); | |
| + return "value"; | |
| + }); | |
| + } | |
| + | |
| + // Try to force garbage collection | |
| + System.gc(); | |
| + Thread.sleep(100); | |
| + | |
| + // The hard map should still contain the entry even after GC | |
| + String value = hardMap.get(originalKey); | |
| + assertEquals("value", value, "Hard references should not be garbage collected"); | |
| + assertEquals(1, computeCount.get(), "Should only compute once since hard references prevent GC"); | |
| + | |
| + // Verify the map still has the entry | |
| + assertEquals(1, hardMap.size(), "Hard map should still contain the entry after GC"); | |
| + } | |
| + | |
| @Test | |
| void shouldHandleNullInputs() { | |
| - assertThrows(NullPointerException.class, () -> map.computeIfAbsent(null, k -> "value")); | |
| + assertThrows(NullPointerException.class, () -> softMap.computeIfAbsent(null, k -> "value")); | |
| Object key = new Object(); | |
| - assertThrows(NullPointerException.class, () -> map.computeIfAbsent(key, null)); | |
| + assertThrows(NullPointerException.class, () -> softMap.computeIfAbsent(key, null)); | |
| + } | |
| + | |
| + @Test | |
| + void shouldCleanupGarbageCollectedEntries() throws InterruptedException { | |
| + int maxRetries = 3; | |
| + AssertionError lastException = new AssertionError("Test failed " + maxRetries + " times"); | |
| + for (int attempt = 1; attempt <= maxRetries; attempt++) { | |
| + try { | |
| + doShouldCleanupGarbageCollectedEntries(); | |
| + return; | |
| + } catch (AssertionError e) { | |
| + lastException.addSuppressed(e); | |
| + Thread.sleep(1000); | |
| + } | |
| + } | |
| + throw lastException; | |
| + } | |
| + | |
| + @SuppressWarnings("AvoidNestedBlocks") | |
| + private void doShouldCleanupGarbageCollectedEntries() throws InterruptedException { | |
| + // Test that the map properly cleans up entries when keys/values are GC'd | |
| + int initialSize = softMap.size(); | |
| + | |
| + // Add some entries that can be garbage collected | |
| + { | |
| + Object key1 = new Object(); | |
| + Object key2 = new Object(); | |
| + softMap.put(key1, "value1"); | |
| + softMap.put(key2, "value2"); | |
| + } | |
| + | |
| + // Verify entries were added | |
| + assertTrue(softMap.size() >= initialSize + 2, "Map should contain the new entries"); | |
| + | |
| + // Force garbage collection multiple times | |
| + for (int i = 0; i < 5; i++) { | |
| + System.gc(); | |
| + Thread.sleep(50); | |
| + // Trigger cleanup by calling a method that calls expungeStaleEntries() | |
| + softMap.size(); | |
| + } | |
| + | |
| + // The map should eventually clean up the garbage collected entries | |
| + // Note: This test is not deterministic due to GC behavior, but it should work most of the time | |
| + int finalSize = softMap.size(); | |
| + assertTrue( | |
| + finalSize <= initialSize + 2, | |
| + "Map should have cleaned up some entries after GC. Initial: " + initialSize + ", Final: " + finalSize); | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/ReferenceTypeStatisticsTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/ReferenceTypeStatisticsTest.java | |
| new file mode 100644 | |
| index 0000000000..1b53e3493c | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/cache/ReferenceTypeStatisticsTest.java | |
| @@ -0,0 +1,163 @@ | |
| +/* | |
| + * 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.impl.cache; | |
| + | |
| +import java.util.Map; | |
| + | |
| +import org.apache.maven.api.cache.CacheRetention; | |
| +import org.apache.maven.impl.cache.CacheStatistics.ReferenceTypeStatistics; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +class ReferenceTypeStatisticsTest { | |
| + | |
| + private final CacheStatistics statistics = new CacheStatistics(); | |
| + | |
| + @Test | |
| + void shouldTrackReferenceTypeStatistics() { | |
| + // Record cache creation with different reference types | |
| + statistics.recordCacheCreation("SOFT", "WEAK", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordCacheCreation("HARD", "SOFT", CacheRetention.REQUEST_SCOPED); | |
| + statistics.recordCacheCreation("SOFT", "WEAK", CacheRetention.SESSION_SCOPED); | |
| + | |
| + // Record cache accesses | |
| + statistics.recordCacheAccess("SOFT", "WEAK", true); // hit | |
| + statistics.recordCacheAccess("SOFT", "WEAK", false); // miss | |
| + statistics.recordCacheAccess("HARD", "SOFT", true); // hit | |
| + | |
| + Map<String, ReferenceTypeStatistics> refTypeStats = statistics.getReferenceTypeStatistics(); | |
| + | |
| + // Should have two reference type combinations | |
| + assertEquals(2, refTypeStats.size()); | |
| + | |
| + // Check SOFT/WEAK statistics | |
| + ReferenceTypeStatistics softWeakStats = refTypeStats.get("SOFT/WEAK"); | |
| + assertNotNull(softWeakStats); | |
| + assertEquals(2, softWeakStats.getCacheCreations()); | |
| + assertEquals(1, softWeakStats.getHits()); | |
| + assertEquals(1, softWeakStats.getMisses()); | |
| + assertEquals(2, softWeakStats.getTotal()); | |
| + assertEquals(50.0, softWeakStats.getHitRatio(), 0.1); | |
| + | |
| + // Check HARD/SOFT statistics | |
| + ReferenceTypeStatistics hardSoftStats = refTypeStats.get("HARD/SOFT"); | |
| + assertNotNull(hardSoftStats); | |
| + assertEquals(1, hardSoftStats.getCacheCreations()); | |
| + assertEquals(1, hardSoftStats.getHits()); | |
| + assertEquals(0, hardSoftStats.getMisses()); | |
| + assertEquals(1, hardSoftStats.getTotal()); | |
| + assertEquals(100.0, hardSoftStats.getHitRatio(), 0.1); | |
| + } | |
| + | |
| + @Test | |
| + void shouldTrackCreationsByRetention() { | |
| + statistics.recordCacheCreation("SOFT", "WEAK", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordCacheCreation("SOFT", "WEAK", CacheRetention.REQUEST_SCOPED); | |
| + statistics.recordCacheCreation("SOFT", "WEAK", CacheRetention.SESSION_SCOPED); | |
| + | |
| + var refTypeStats = statistics.getReferenceTypeStatistics(); | |
| + var softWeakStats = refTypeStats.get("SOFT/WEAK"); | |
| + | |
| + assertNotNull(softWeakStats); | |
| + assertEquals(3, softWeakStats.getCacheCreations()); | |
| + | |
| + var creationsByRetention = softWeakStats.getCreationsByRetention(); | |
| + assertEquals(2, creationsByRetention.get(CacheRetention.SESSION_SCOPED).longValue()); | |
| + assertEquals(1, creationsByRetention.get(CacheRetention.REQUEST_SCOPED).longValue()); | |
| + } | |
| + | |
| + @Test | |
| + void shouldHandleEmptyStatistics() { | |
| + var refTypeStats = statistics.getReferenceTypeStatistics(); | |
| + assertTrue(refTypeStats.isEmpty()); | |
| + } | |
| + | |
| + @Test | |
| + void shouldDisplayReferenceTypeStatisticsInOutput() { | |
| + CacheStatistics statistics = new CacheStatistics(); | |
| + | |
| + // Simulate cache usage with different reference types | |
| + statistics.recordCacheCreation("HARD", "HARD", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordCacheCreation("SOFT", "WEAK", CacheRetention.REQUEST_SCOPED); | |
| + statistics.recordCacheCreation("WEAK", "SOFT", CacheRetention.PERSISTENT); | |
| + | |
| + // Simulate cache accesses | |
| + statistics.recordCacheAccess("HARD", "HARD", true); | |
| + statistics.recordCacheAccess("HARD", "HARD", true); | |
| + statistics.recordCacheAccess("HARD", "HARD", false); | |
| + | |
| + statistics.recordCacheAccess("SOFT", "WEAK", true); | |
| + statistics.recordCacheAccess("SOFT", "WEAK", false); | |
| + statistics.recordCacheAccess("SOFT", "WEAK", false); | |
| + | |
| + statistics.recordCacheAccess("WEAK", "SOFT", false); | |
| + | |
| + // Simulate some regular cache statistics | |
| + statistics.recordHit("TestRequest", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordMiss("TestRequest", CacheRetention.SESSION_SCOPED); | |
| + | |
| + // Capture the formatted output (not used in this test, but could be useful for future enhancements) | |
| + | |
| + String output = DefaultRequestCache.formatCacheStatistics(statistics); | |
| + | |
| + // Verify that reference type information is included | |
| + assertTrue(output.contains("Reference type usage:"), "Should contain reference type section\n" + output); | |
| + assertTrue(output.contains("HARD/HARD:"), "Should show HARD/HARD reference type\n" + output); | |
| + assertTrue(output.contains("SOFT/WEAK:"), "Should show SOFT/WEAK reference type\n" + output); | |
| + assertTrue(output.contains("WEAK/SOFT:"), "Should show WEAK/SOFT reference type\n" + output); | |
| + assertTrue(output.contains("caches"), "Should show cache creation count\n" + output); | |
| + assertTrue(output.contains("accesses"), "Should show access count\n" + output); | |
| + assertTrue(output.contains("hit ratio"), "Should show hit ratio\n" + output); | |
| + | |
| + // Verify that different hit ratios are shown correctly | |
| + assertTrue( | |
| + output.contains("66.7%") || output.contains("66.6%"), | |
| + "Should show HARD/HARD hit ratio (~66.7%):\n" + output); | |
| + assertTrue(output.contains("33.3%"), "Should show SOFT/WEAK hit ratio (33.3%):\n" + output); | |
| + assertTrue(output.contains("0.0%"), "Should show WEAK/SOFT hit ratio (0.0%):\n" + output); | |
| + } | |
| + | |
| + @Test | |
| + void shouldShowMemoryPressureIndicators() { | |
| + CacheStatistics statistics = new CacheStatistics(); | |
| + | |
| + // Create scenario that might indicate memory pressure | |
| + statistics.recordCacheCreation("HARD", "HARD", CacheRetention.SESSION_SCOPED); | |
| + statistics.recordCacheCreation("SOFT", "SOFT", CacheRetention.SESSION_SCOPED); | |
| + | |
| + // Simulate many cache accesses with hard references (potential OOM risk) | |
| + for (int i = 0; i < 1000; i++) { | |
| + statistics.recordCacheAccess("HARD", "HARD", true); | |
| + } | |
| + | |
| + // Simulate some soft reference usage | |
| + for (int i = 0; i < 100; i++) { | |
| + statistics.recordCacheAccess("SOFT", "SOFT", i % 2 == 0); | |
| + } | |
| + | |
| + String output = DefaultRequestCache.formatCacheStatistics(statistics); | |
| + | |
| + // Should show high usage of hard references | |
| + assertTrue(output.contains("HARD/HARD:"), "Should show hard reference usage: \n" + output); | |
| + assertTrue(output.contains("1000 accesses"), "Should show high access count for hard references: \n" + output); | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultDependencyManagementImporterTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultDependencyManagementImporterTest.java | |
| index 8e30494bf7..94d8689ad7 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultDependencyManagementImporterTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultDependencyManagementImporterTest.java | |
| @@ -24,7 +24,8 @@ | |
| import org.apache.maven.api.model.InputSource; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.assertj.core.api.Assertions.assertThat; | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| class DefaultDependencyManagementImporterTest { | |
| @Test | |
| @@ -33,58 +34,66 @@ void testUpdateWithImportedFromDependencyLocationAndBomLocationAreNullDependency | |
| final DependencyManagement depMgmt = DependencyManagement.newBuilder().build(); | |
| final Dependency result = DefaultDependencyManagementImporter.updateWithImportedFrom(dependency, depMgmt); | |
| - assertThat(dependency).isEqualTo(result); | |
| + assertEquals(result, dependency); | |
| } | |
| @Test | |
| void testUpdateWithImportedFromDependencyManagementAndDependencyHaveSameSourceDependencyImportedFromSameSource() { | |
| - final InputSource source = new InputSource("SINGLE_SOURCE", ""); | |
| + final InputSource source = InputSource.of("SINGLE_SOURCE", ""); | |
| final Dependency dependency = Dependency.newBuilder() | |
| - .location("", new InputLocation(1, 1, source)) | |
| + .location("", InputLocation.of(1, 1, source)) | |
| .build(); | |
| final DependencyManagement bom = DependencyManagement.newBuilder() | |
| - .location("", new InputLocation(1, 1, source)) | |
| + .location("", InputLocation.of(1, 1, source)) | |
| .build(); | |
| final Dependency result = DefaultDependencyManagementImporter.updateWithImportedFrom(dependency, bom); | |
| - assertThat(result).isNotNull(); | |
| - assertThat(result.getImportedFrom().toString()) | |
| - .isEqualTo(bom.getLocation("").toString()); | |
| + assertNotNull(result); | |
| + String actualImportedFrom = result.getImportedFrom().toString(); | |
| + String expectedImportedFrom = bom.getLocation("").toString(); | |
| + assertEquals( | |
| + expectedImportedFrom, | |
| + actualImportedFrom, | |
| + "Expected importedFrom to be " + expectedImportedFrom + " but was " + actualImportedFrom); | |
| } | |
| @Test | |
| public void testUpdateWithImportedFromSingleLevelImportedFromSet() { | |
| // Arrange | |
| - final InputSource dependencySource = new InputSource("DEPENDENCY", "DEPENDENCY"); | |
| - final InputSource bomSource = new InputSource("BOM", "BOM"); | |
| + final InputSource dependencySource = InputSource.of("DEPENDENCY", "DEPENDENCY"); | |
| + final InputSource bomSource = InputSource.of("BOM", "BOM"); | |
| final Dependency dependency = Dependency.newBuilder() | |
| - .location("", new InputLocation(1, 1, dependencySource)) | |
| + .location("", InputLocation.of(1, 1, dependencySource)) | |
| .build(); | |
| final DependencyManagement bom = DependencyManagement.newBuilder() | |
| - .location("", new InputLocation(2, 2, bomSource)) | |
| + .location("", InputLocation.of(2, 2, bomSource)) | |
| .build(); | |
| // Act | |
| final Dependency result = DefaultDependencyManagementImporter.updateWithImportedFrom(dependency, bom); | |
| // Assert | |
| - assertThat(result).isNotNull(); | |
| - assertThat(result.getImportedFrom().toString()) | |
| - .isEqualTo(bom.getLocation("").toString()); | |
| + assertNotNull(result); | |
| + String actualImportedFrom = result.getImportedFrom().toString(); | |
| + String expectedImportedFrom = bom.getLocation("").toString(); | |
| + assertEquals( | |
| + expectedImportedFrom, | |
| + actualImportedFrom, | |
| + "Expected importedFrom to be " + expectedImportedFrom + " but was " + actualImportedFrom); | |
| } | |
| @Test | |
| public void testUpdateWithImportedFromMultiLevelImportedFromSetChanged() { | |
| // Arrange | |
| - final InputSource bomSource = new InputSource("BOM", "BOM"); | |
| + final InputSource bomSource = InputSource.of("BOM", "BOM"); | |
| final InputSource intermediateSource = | |
| - new InputSource("INTERMEDIATE", "INTERMEDIATE", new InputLocation(bomSource)); | |
| + InputSource.of("INTERMEDIATE", "INTERMEDIATE", InputLocation.of(bomSource)); | |
| final InputSource dependencySource = | |
| - new InputSource("DEPENDENCY", "DEPENDENCY", new InputLocation(intermediateSource)); | |
| - final InputLocation bomLocation = new InputLocation(2, 2, bomSource); | |
| + InputSource.of("DEPENDENCY", "DEPENDENCY", InputLocation.of(intermediateSource)); | |
| + final InputLocation bomLocation = InputLocation.of(2, 2, bomSource); | |
| final Dependency dependency = Dependency.newBuilder() | |
| - .location("", new InputLocation(1, 1, dependencySource)) | |
| + .location("", InputLocation.of(1, 1, dependencySource)) | |
| .importedFrom(bomLocation) | |
| .build(); | |
| final DependencyManagement bom = | |
| @@ -94,23 +103,27 @@ public void testUpdateWithImportedFromMultiLevelImportedFromSetChanged() { | |
| final Dependency result = DefaultDependencyManagementImporter.updateWithImportedFrom(dependency, bom); | |
| // Assert | |
| - assertThat(result.getImportedFrom().toString()) | |
| - .isEqualTo(bom.getLocation("").toString()); | |
| + String actualImportedFrom = result.getImportedFrom().toString(); | |
| + String expectedImportedFrom = bom.getLocation("").toString(); | |
| + assertEquals( | |
| + expectedImportedFrom, | |
| + actualImportedFrom, | |
| + "Expected importedFrom to be " + expectedImportedFrom + " but was " + actualImportedFrom); | |
| } | |
| @Test | |
| public void testUpdateWithImportedFromMultiLevelAlreadyFoundInDifferentSourceImportedFromSetMaintained() { | |
| // Arrange | |
| - final InputSource bomSource = new InputSource("BOM", "BOM"); | |
| + final InputSource bomSource = InputSource.of("BOM", "BOM"); | |
| final InputSource intermediateSource = | |
| - new InputSource("INTERMEDIATE", "INTERMEDIATE", new InputLocation(bomSource)); | |
| + InputSource.of("INTERMEDIATE", "INTERMEDIATE", InputLocation.of(bomSource)); | |
| final InputSource dependencySource = | |
| - new InputSource("DEPENDENCY", "DEPENDENCY", new InputLocation(intermediateSource)); | |
| + InputSource.of("DEPENDENCY", "DEPENDENCY", InputLocation.of(intermediateSource)); | |
| final Dependency dependency = Dependency.newBuilder() | |
| - .location("", new InputLocation(1, 1, dependencySource)) | |
| + .location("", InputLocation.of(1, 1, dependencySource)) | |
| .build(); | |
| final DependencyManagement differentSource = DependencyManagement.newBuilder() | |
| - .location("", new InputLocation(2, 2, new InputSource("BOM2", "BOM2"))) | |
| + .location("", InputLocation.of(2, 2, InputSource.of("BOM2", "BOM2"))) | |
| .build(); | |
| // Act | |
| @@ -118,7 +131,11 @@ public void testUpdateWithImportedFromMultiLevelAlreadyFoundInDifferentSourceImp | |
| DefaultDependencyManagementImporter.updateWithImportedFrom(dependency, differentSource); | |
| // Assert | |
| - assertThat(result.getImportedFrom().toString()) | |
| - .isEqualTo(differentSource.getLocation("").toString()); | |
| + String actualImportedFrom = result.getImportedFrom().toString(); | |
| + String expectedImportedFrom = differentSource.getLocation("").toString(); | |
| + assertEquals( | |
| + expectedImportedFrom, | |
| + actualImportedFrom, | |
| + "Expected importedFrom to be " + expectedImportedFrom + " but was " + actualImportedFrom); | |
| } | |
| } | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelObjectPoolTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelObjectPoolTest.java | |
| new file mode 100644 | |
| index 0000000000..50c9e6704a | |
| --- /dev/null | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelObjectPoolTest.java | |
| @@ -0,0 +1,237 @@ | |
| +/* | |
| + * 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.impl.model; | |
| + | |
| +import java.util.Map; | |
| +import java.util.Objects; | |
| + | |
| +import org.apache.maven.api.Constants; | |
| +import org.apache.maven.api.model.Dependency; | |
| +import org.apache.maven.api.model.ModelObjectProcessor; | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +import static org.junit.jupiter.api.Assertions.assertEquals; | |
| +import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| +import static org.junit.jupiter.api.Assertions.assertSame; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| + | |
| +/** | |
| + * Test for DefaultModelObjectPool. | |
| + */ | |
| +class DefaultModelObjectPoolTest { | |
| + | |
| + @Test | |
| + void testServiceLoading() { | |
| + // Test that the static method works | |
| + String testString = "test"; | |
| + String result = ModelObjectProcessor.processObject(testString); | |
| + assertNotNull(result); | |
| + assertEquals(testString, result); | |
| + } | |
| + | |
| + @Test | |
| + void testDependencyPooling() { | |
| + ModelObjectProcessor processor = new DefaultModelObjectPool(); | |
| + | |
| + // Create two identical dependencies | |
| + // Note: Due to the static processor being active, these may already be pooled | |
| + Dependency dep1 = Dependency.newBuilder() | |
| + .groupId("org.apache.maven") | |
| + .artifactId("maven-core") | |
| + .version("4.0.0") | |
| + .build(); | |
| + | |
| + Dependency dep2 = Dependency.newBuilder() | |
| + .groupId("org.apache.maven") | |
| + .artifactId("maven-core") | |
| + .version("4.0.0") | |
| + .build(); | |
| + | |
| + // Due to static processing, they may already be the same instance | |
| + // This is actually the expected behavior - pooling is working! | |
| + | |
| + // Process them through our specific processor instance | |
| + Dependency pooled1 = processor.process(dep1); | |
| + Dependency pooled2 = processor.process(dep2); | |
| + | |
| + // They should be the same instance after processing | |
| + assertSame(pooled1, pooled2); | |
| + | |
| + // The pooled instances should be semantically equal to the originals | |
| + assertTrue(dependenciesEqual(dep1, pooled1)); | |
| + assertTrue(dependenciesEqual(dep2, pooled2)); | |
| + } | |
| + | |
| + /** | |
| + * Helper method to check complete equality of dependencies. | |
| + */ | |
| + private boolean dependenciesEqual(Dependency dep1, Dependency dep2) { | |
| + return Objects.equals(dep1.getGroupId(), dep2.getGroupId()) | |
| + && Objects.equals(dep1.getArtifactId(), dep2.getArtifactId()) | |
| + && Objects.equals(dep1.getVersion(), dep2.getVersion()) | |
| + && Objects.equals(dep1.getType(), dep2.getType()) | |
| + && Objects.equals(dep1.getClassifier(), dep2.getClassifier()) | |
| + && Objects.equals(dep1.getScope(), dep2.getScope()) | |
| + && Objects.equals(dep1.getSystemPath(), dep2.getSystemPath()) | |
| + && Objects.equals(dep1.getExclusions(), dep2.getExclusions()) | |
| + && Objects.equals(dep1.getOptional(), dep2.getOptional()) | |
| + && Objects.equals(dep1.getLocationKeys(), dep2.getLocationKeys()) | |
| + && locationsEqual(dep1, dep2) | |
| + && Objects.equals(dep1.getImportedFrom(), dep2.getImportedFrom()); | |
| + } | |
| + | |
| + /** | |
| + * Helper method to check locations equality. | |
| + */ | |
| + private boolean locationsEqual(Dependency dep1, Dependency dep2) { | |
| + var keys1 = dep1.getLocationKeys(); | |
| + var keys2 = dep2.getLocationKeys(); | |
| + | |
| + if (!Objects.equals(keys1, keys2)) { | |
| + return false; | |
| + } | |
| + | |
| + for (Object key : keys1) { | |
| + if (!Objects.equals(dep1.getLocation(key), dep2.getLocation(key))) { | |
| + return false; | |
| + } | |
| + } | |
| + return true; | |
| + } | |
| + | |
| + @Test | |
| + void testNonDependencyObjects() { | |
| + ModelObjectProcessor processor = new DefaultModelObjectPool(); | |
| + | |
| + String testString = "test"; | |
| + String result = processor.process(testString); | |
| + | |
| + // Non-dependency objects should be returned as-is | |
| + assertSame(testString, result); | |
| + } | |
| + | |
| + @Test | |
| + void testConfigurableReferenceType() { | |
| + // Test that the reference type can be configured via system property | |
| + String originalValue = System.getProperty(Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE); | |
| + | |
| + try { | |
| + // Set a different reference type | |
| + System.setProperty(Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE, "SOFT"); | |
| + | |
| + // Create a new processor (this would use the new setting in a real scenario) | |
| + ModelObjectProcessor processor = new DefaultModelObjectPool(); | |
| + | |
| + // Test that it still works (the actual reference type is used internally) | |
| + Dependency dep = Dependency.newBuilder() | |
| + .groupId("test") | |
| + .artifactId("test") | |
| + .version("1.0") | |
| + .build(); | |
| + | |
| + Dependency result = processor.process(dep); | |
| + assertNotNull(result); | |
| + assertEquals(dep, result); | |
| + | |
| + } finally { | |
| + // Restore original value | |
| + if (originalValue != null) { | |
| + System.setProperty(Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE, originalValue); | |
| + } else { | |
| + System.clearProperty(Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE); | |
| + } | |
| + } | |
| + } | |
| + | |
| + @Test | |
| + void testConfigurablePooledTypes() { | |
| + // Configure to only pool Dependencies | |
| + ModelObjectProcessor processor = | |
| + new DefaultModelObjectPool(Map.of(Constants.MAVEN_MODEL_PROCESSOR_POOLED_TYPES, "Dependency")); | |
| + | |
| + // Dependencies should be pooled | |
| + Dependency dep1 = Dependency.newBuilder() | |
| + .groupId("test") | |
| + .artifactId("test") | |
| + .version("1.0") | |
| + .build(); | |
| + | |
| + Dependency dep2 = Dependency.newBuilder() | |
| + .groupId("test") | |
| + .artifactId("test") | |
| + .version("1.0") | |
| + .build(); | |
| + | |
| + Dependency result1 = processor.process(dep1); | |
| + Dependency result2 = processor.process(dep2); | |
| + | |
| + // Should be the same instance due to pooling | |
| + assertSame(result1, result2); | |
| + | |
| + // Non-dependency objects should not be pooled (pass through) | |
| + String str1 = "test"; | |
| + String str2 = processor.process(str1); | |
| + assertSame(str1, str2); // Same instance because it's not pooled | |
| + } | |
| + | |
| + @Test | |
| + void testPerTypeReferenceType() { | |
| + // Set default to WEAK and Dependency-specific to HARD | |
| + ModelObjectProcessor processor = new DefaultModelObjectPool(Map.of( | |
| + Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE, | |
| + "WEAK", | |
| + Constants.MAVEN_MODEL_PROCESSOR_REFERENCE_TYPE_PREFIX + "Dependency", | |
| + "HARD")); | |
| + | |
| + // Test that dependencies still work with per-type configuration | |
| + Dependency dep = Dependency.newBuilder() | |
| + .groupId("test") | |
| + .artifactId("test") | |
| + .version("1.0") | |
| + .build(); | |
| + | |
| + Dependency result = processor.process(dep); | |
| + assertNotNull(result); | |
| + assertEquals(dep, result); | |
| + } | |
| + | |
| + @Test | |
| + void testStatistics() { | |
| + ModelObjectProcessor processor = new DefaultModelObjectPool(); | |
| + | |
| + // Process some dependencies | |
| + for (int i = 0; i < 5; i++) { | |
| + Dependency dep = Dependency.newBuilder() | |
| + .groupId("test") | |
| + .artifactId("test-" + (i % 2)) // Create some duplicates | |
| + .version("1.0") | |
| + .build(); | |
| + processor.process(dep); | |
| + } | |
| + | |
| + // Check that statistics are available | |
| + String stats = DefaultModelObjectPool.getStatistics(Dependency.class); | |
| + assertNotNull(stats); | |
| + assertTrue(stats.contains("Dependency")); | |
| + | |
| + String allStats = DefaultModelObjectPool.getAllStatistics(); | |
| + assertNotNull(allStats); | |
| + assertTrue(allStats.contains("ModelObjectPool Statistics")); | |
| + } | |
| +} | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java | |
| index d7fe81c2ca..6e8397d708 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java | |
| @@ -23,6 +23,7 @@ | |
| import java.util.Collection; | |
| import java.util.List; | |
| +import org.apache.maven.api.Version; | |
| import org.apache.maven.api.model.Model; | |
| import org.apache.maven.api.services.model.ModelValidator; | |
| import org.apache.maven.impl.InternalSession; | |
| @@ -41,8 +42,6 @@ | |
| import static org.mockito.Mockito.mock; | |
| import static org.mockito.Mockito.when; | |
| -/** | |
| - */ | |
| class DefaultModelValidatorTest { | |
| private ModelValidator validator; | |
| @@ -130,6 +129,11 @@ void setUp() throws Exception { | |
| when(repoSession.getScopeManager()).thenReturn(scopeManager); | |
| session = mock(InternalSession.class); | |
| when(session.getSession()).thenReturn(repoSession); | |
| + | |
| + // Mock Maven version for error message testing | |
| + Version mavenVersion = mock(Version.class); | |
| + when(mavenVersion.toString()).thenReturn("4.0.0-test"); | |
| + when(session.getMavenVersion()).thenReturn(mavenVersion); | |
| } | |
| @AfterEach | |
| @@ -170,6 +174,23 @@ void testModelVersionMessage() throws Exception { | |
| assertTrue(result.getErrors().get(0).contains("'modelVersion' must be one of")); | |
| } | |
| + @Test | |
| + void testModelVersionMessageIncludesMavenVersion() throws Exception { | |
| + SimpleProblemCollector result = validateFile("bad-modelVersion.xml"); | |
| + | |
| + assertViolations(result, 1, 0, 0); | |
| + | |
| + String errorMessage = result.getFatals().get(0); | |
| + assertTrue(errorMessage.contains("modelVersion"), "Expected " + errorMessage + " to contain " + "modelVersion"); | |
| + // Should include Maven version (either "4.0.0-test" from mock or "unknown" as fallback) | |
| + assertTrue( | |
| + errorMessage.contains("4.0.0-test") || errorMessage.contains("unknown"), | |
| + "Error message should include Maven version: " + errorMessage); | |
| + assertTrue( | |
| + errorMessage.contains("is not supported by this Maven version"), | |
| + "Expected " + errorMessage + " to contain " + "is not supported by this Maven version"); | |
| + } | |
| + | |
| @Test | |
| void testMissingArtifactId() throws Exception { | |
| SimpleProblemCollector result = validate("missing-artifactId-pom.xml"); | |
| @@ -303,10 +324,18 @@ void testMissingAll() throws Exception { | |
| List<String> messages = result.getErrors(); | |
| - assertTrue(messages.contains("'modelVersion' is missing.")); | |
| - assertTrue(messages.contains("'groupId' is missing.")); | |
| - assertTrue(messages.contains("'artifactId' is missing.")); | |
| - assertTrue(messages.contains("'version' is missing.")); | |
| + assertTrue( | |
| + messages.contains("'modelVersion' is missing."), | |
| + "Expected " + messages + " to contain " + "'modelVersion' is missing."); | |
| + assertTrue( | |
| + messages.contains("'groupId' is missing."), | |
| + "Expected " + messages + " to contain " + "'groupId' is missing."); | |
| + assertTrue( | |
| + messages.contains("'artifactId' is missing."), | |
| + "Expected " + messages + " to contain " + "'artifactId' is missing."); | |
| + assertTrue( | |
| + messages.contains("'version' is missing."), | |
| + "Expected " + messages + " to contain " + "'version' is missing."); | |
| // type is inherited from the super pom | |
| } | |
| @@ -391,6 +420,10 @@ void testBadDependencyScope() throws Exception { | |
| assertViolations(result, 0, 0, 2); | |
| assertTrue(result.getWarnings().get(0).contains("groupId='test', artifactId='f'")); | |
| + // Check that the import scope error message is more helpful | |
| + assertTrue(result.getWarnings() | |
| + .get(0) | |
| + .contains("has scope 'import'. The 'import' scope is only valid in <dependencyManagement> sections")); | |
| assertTrue(result.getWarnings().get(1).contains("groupId='test', artifactId='g'")); | |
| } | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java | |
| index 03e8f9018c..5316352658 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/InterningTransformerTest.java | |
| @@ -91,33 +91,92 @@ void testTransformerInternsCorrectContexts() { | |
| @Test | |
| void testTransformerContainsExpectedContexts() { | |
| // Verify that the DEFAULT_CONTEXTS set contains all the expected fields | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("groupId")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("artifactId")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("version")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("packaging")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("scope")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("type")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("classifier")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("goal")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("execution")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("phase")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("modelVersion")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("name")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("url")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("system")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("distribution")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("status")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("connection")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("developerConnection")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("tag")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("id")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("inherited")); | |
| - assertTrue(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("optional")); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("groupId"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "groupId"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("artifactId"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " | |
| + + "artifactId"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("version"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "version"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("packaging"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "packaging"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("scope"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "scope"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("type"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "type"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("classifier"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " | |
| + + "classifier"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("goal"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "goal"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("execution"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "execution"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("phase"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "phase"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("modelVersion"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " | |
| + + "modelVersion"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("name"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "name"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("url"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "url"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("system"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "system"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("distribution"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " | |
| + + "distribution"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("status"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "status"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("connection"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " | |
| + + "connection"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("developerConnection"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " | |
| + + "developerConnection"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("tag"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "tag"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("id"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "id"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("inherited"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "inherited"); | |
| + assertTrue( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("optional"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to contain " + "optional"); | |
| // Verify that non-interned contexts are not in the set | |
| - assertFalse(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("nonInterned")); | |
| - assertFalse(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("description")); | |
| - assertFalse(DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("randomField")); | |
| + assertFalse( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("nonInterned"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to not contain " | |
| + + "nonInterned"); | |
| + assertFalse( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("description"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to not contain " | |
| + + "description"); | |
| + assertFalse( | |
| + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS.contains("randomField"), | |
| + "Expected " + DefaultModelBuilder.InterningTransformer.DEFAULT_CONTEXTS + " to not contain " | |
| + + "randomField"); | |
| } | |
| @Test | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java | |
| index 7b097a51b8..3fa6eb88ce 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/ParentCycleDetectionTest.java | |
| @@ -206,9 +206,9 @@ void testMultipleModulesWithSameParentDoNotCauseCycle(@TempDir Path tempDir) thr | |
| Files.writeString( | |
| parentPom, | |
| """ | |
| - <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| - <modelVersion>4.0.0</modelVersion> | |
| + <project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"> | |
| + <modelVersion>4.1.0</modelVersion> | |
| <groupId>test</groupId> | |
| <artifactId>parent</artifactId> | |
| <version>1.0</version> | |
| @@ -221,9 +221,9 @@ void testMultipleModulesWithSameParentDoNotCauseCycle(@TempDir Path tempDir) thr | |
| Files.writeString( | |
| moduleA, | |
| """ | |
| - <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| - <modelVersion>4.0.0</modelVersion> | |
| + <project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"> | |
| + <modelVersion>4.1.0</modelVersion> | |
| <parent> | |
| <groupId>test</groupId> | |
| <artifactId>parent</artifactId> | |
| @@ -239,9 +239,9 @@ void testMultipleModulesWithSameParentDoNotCauseCycle(@TempDir Path tempDir) thr | |
| Files.writeString( | |
| moduleB, | |
| """ | |
| - <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
| - <modelVersion>4.0.0</modelVersion> | |
| + <project xmlns="http://maven.apache.org/POM/4.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| + xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 https://maven.apache.org/xsd/maven-4.1.0.xsd"> | |
| + <modelVersion>4.1.0</modelVersion> | |
| <parent> | |
| <groupId>test</groupId> | |
| <artifactId>parent</artifactId> | |
| diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/resolver/DefaultModelResolverTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/resolver/DefaultModelResolverTest.java | |
| index e680267895..1b5826938b 100644 | |
| --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/resolver/DefaultModelResolverTest.java | |
| +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/resolver/DefaultModelResolverTest.java | |
| @@ -39,11 +39,10 @@ | |
| import org.junit.jupiter.api.BeforeEach; | |
| import org.junit.jupiter.api.Test; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.containsString; | |
| import static org.junit.jupiter.api.Assertions.assertEquals; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertThrows; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| /** | |
| * Test cases for the project {@code ModelResolver} implementation. | |
| @@ -80,7 +79,10 @@ void testResolveParentThrowsModelResolverExceptionWhenNotFound() throws Exceptio | |
| () -> newModelResolver().resolveModel(session, null, parent, new AtomicReference<>()), | |
| "Expected 'ModelResolverException' not thrown."); | |
| assertNotNull(e.getMessage()); | |
| - assertThat(e.getMessage(), containsString("Could not find artifact org.apache:apache:pom:0 in central")); | |
| + String message = e.getMessage(); | |
| + assertTrue( | |
| + message.contains("Could not find artifact org.apache:apache:pom:0 in central"), | |
| + "Expected exception message to contain artifact not found text but was: " + message); | |
| } | |
| @Test | |
| @@ -151,7 +153,10 @@ void testResolveDependencyThrowsModelResolverExceptionWhenNotFound() throws Exce | |
| () -> newModelResolver().resolveModel(session, null, dependency, new AtomicReference<>()), | |
| "Expected 'ModelResolverException' not thrown."); | |
| assertNotNull(e.getMessage()); | |
| - assertThat(e.getMessage(), containsString("Could not find artifact org.apache:apache:pom:0 in central")); | |
| + String message = e.getMessage(); | |
| + assertTrue( | |
| + message.contains("Could not find artifact org.apache:apache:pom:0 in central"), | |
| + "Expected exception message to contain artifact not found text but was: " + message); | |
| } | |
| @Test | |
| diff --git a/impl/maven-impl/src/test/resources/settings-simple.xml b/impl/maven-impl/src/test/resources/settings-simple.xml | |
| index ea664bacdf..abcda726c7 100644 | |
| --- a/impl/maven-impl/src/test/resources/settings-simple.xml | |
| +++ b/impl/maven-impl/src/test/resources/settings-simple.xml | |
| @@ -19,6 +19,6 @@ specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| -<settings> | |
| +<settings xmlns='http://maven.apache.org/SETTINGS/1.0.0'> | |
| <localRepository>${user.home}/.m2/repository</localRepository> | |
| </settings> | |
| diff --git a/impl/maven-jline/pom.xml b/impl/maven-jline/pom.xml | |
| index 74d93c0553..9ec3e9ae0c 100644 | |
| --- a/impl/maven-jline/pom.xml | |
| +++ b/impl/maven-jline/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-jline</artifactId> | |
| diff --git a/impl/maven-logging/pom.xml b/impl/maven-logging/pom.xml | |
| index 84c68710ce..6f89f2bdbd 100644 | |
| --- a/impl/maven-logging/pom.xml | |
| +++ b/impl/maven-logging/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-logging</artifactId> | |
| @@ -54,10 +54,5 @@ under the License. | |
| <artifactId>junit-jupiter-params</artifactId> | |
| <scope>test</scope> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <scope>test</scope> | |
| - </dependency> | |
| </dependencies> | |
| </project> | |
| diff --git a/impl/maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java b/impl/maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java | |
| index c2573b066c..77f008e67f 100644 | |
| --- a/impl/maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java | |
| +++ b/impl/maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java | |
| @@ -31,6 +31,6 @@ void createsLogLevelRecorder() { | |
| logLevelRecorder.setMaxLevelAllowed(LogLevelRecorder.Level.WARN); | |
| logLevelRecorder.record(Level.ERROR); | |
| - assertTrue(logLevelRecorder.metThreshold()); | |
| + assertTrue(logLevelRecorder.metThreshold(), "Expected " + logLevelRecorder + ".metThreshold() to return true"); | |
| } | |
| } | |
| diff --git a/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenBaseLoggerTimestampTest.java b/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenBaseLoggerTimestampTest.java | |
| index 862726fbe2..986e138b56 100644 | |
| --- a/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenBaseLoggerTimestampTest.java | |
| +++ b/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenBaseLoggerTimestampTest.java | |
| @@ -28,8 +28,7 @@ | |
| import org.junit.jupiter.params.ParameterizedTest; | |
| import org.junit.jupiter.params.provider.ValueSource; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| -import static org.hamcrest.Matchers.matchesPattern; | |
| +import static org.junit.jupiter.api.Assertions.assertTrue; | |
| class MavenBaseLoggerTimestampTest { | |
| private ByteArrayOutputStream logOutput; | |
| @@ -71,8 +70,9 @@ void whenShowDateTimeIsFalseShouldNotIncludeTimestamp() { | |
| String output = getLastLine(logOutput.toString()); | |
| // Then | |
| - assertThat( | |
| - "Should not include timestamp", output, matchesPattern("^\\[main\\] INFO test.logger - Test message$")); | |
| + assertTrue( | |
| + output.matches("^\\[main\\] INFO test.logger - Test message$"), | |
| + "Should not include timestamp but was: " + output); | |
| } | |
| @Test | |
| @@ -86,10 +86,9 @@ void whenShowDateTimeIsTrueWithoutFormatShouldShowElapsedTime() { // Changed tes | |
| String output = getLastLine(logOutput.toString()); | |
| // Then | |
| - assertThat( | |
| - "Should show elapsed time when no format specified", | |
| - output, | |
| - matchesPattern("^\\d+ \\[main\\] INFO test.logger - Test message$")); | |
| + assertTrue( | |
| + output.matches("^\\d+ \\[main\\] INFO test.logger - Test message$"), | |
| + "Should show elapsed time when no format specified but was: " + output); | |
| } | |
| @ParameterizedTest | |
| @@ -116,10 +115,9 @@ void whenCustomDateFormatShouldFormatCorrectly(String dateFormat) { | |
| .replace("/", "\\/") | |
| .replace(".", "\\."); | |
| - assertThat( | |
| - "Should match custom date format", | |
| - output, | |
| - matchesPattern("^" + patternStr + " \\[main\\] INFO test.logger - Test message$")); | |
| + assertTrue( | |
| + output.matches("^" + patternStr + " \\[main\\] INFO test.logger - Test message$"), | |
| + "Should match custom date format but was: " + output); | |
| } | |
| @Test | |
| @@ -134,10 +132,9 @@ void whenInvalidDateFormatShouldUseElapsedMillis() { | |
| String output = getLastLine(logOutput.toString()); | |
| // Then | |
| - assertThat( | |
| - "Should show elapsed milliseconds when format is invalid", | |
| - output, | |
| - matchesPattern("^\\d+ \\[main\\] INFO test.logger - Test message$")); | |
| + assertTrue( | |
| + output.matches("^\\d+ \\[main\\] INFO test.logger - Test message$"), | |
| + "Should show elapsed milliseconds when format is invalid but was: " + output); | |
| } | |
| private void initializeLogger() { | |
| diff --git a/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java b/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java | |
| index a6fb8cbc96..20150855f0 100644 | |
| --- a/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java | |
| +++ b/impl/maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java | |
| @@ -22,9 +22,8 @@ | |
| import org.junit.jupiter.api.Test; | |
| import org.slf4j.Logger; | |
| -import static org.hamcrest.CoreMatchers.instanceOf; | |
| -import static org.hamcrest.MatcherAssert.assertThat; | |
| import static org.junit.jupiter.api.Assertions.assertFalse; | |
| +import static org.junit.jupiter.api.Assertions.assertInstanceOf; | |
| import static org.junit.jupiter.api.Assertions.assertNotNull; | |
| import static org.junit.jupiter.api.Assertions.assertNotSame; | |
| import static org.junit.jupiter.api.Assertions.assertSame; | |
| @@ -37,7 +36,7 @@ void createsSimpleLogger() { | |
| Logger logger = mavenLoggerFactory.getLogger("Test"); | |
| - assertThat(logger, instanceOf(MavenSimpleLogger.class)); | |
| + assertInstanceOf(MavenSimpleLogger.class, logger, "Expected logger to be instance of MavenSimpleLogger"); | |
| } | |
| @Test | |
| @@ -60,15 +59,23 @@ void reportsWhenFailOnSeverityThresholdHasBeenHit() { | |
| mavenLoggerFactory.logLevelRecorder.setMaxLevelAllowed(LogLevelRecorder.Level.ERROR); | |
| MavenFailOnSeverityLogger logger = (MavenFailOnSeverityLogger) mavenLoggerFactory.getLogger("Test"); | |
| - assertFalse(mavenLoggerFactory.logLevelRecorder.metThreshold()); | |
| + assertFalse( | |
| + mavenLoggerFactory.logLevelRecorder.metThreshold(), | |
| + "Expected " + mavenLoggerFactory.logLevelRecorder + ".metThreshold() to return false"); | |
| logger.warn("This should not hit the fail threshold"); | |
| - assertFalse(mavenLoggerFactory.logLevelRecorder.metThreshold()); | |
| + assertFalse( | |
| + mavenLoggerFactory.logLevelRecorder.metThreshold(), | |
| + "Expected " + mavenLoggerFactory.logLevelRecorder + ".metThreshold() to return false"); | |
| logger.error("This should hit the fail threshold"); | |
| - assertTrue(mavenLoggerFactory.logLevelRecorder.metThreshold()); | |
| + assertTrue( | |
| + mavenLoggerFactory.logLevelRecorder.metThreshold(), | |
| + "Expected " + mavenLoggerFactory.logLevelRecorder + ".metThreshold() to return true"); | |
| logger.warn("This should not reset the fail threshold"); | |
| - assertTrue(mavenLoggerFactory.logLevelRecorder.metThreshold()); | |
| + assertTrue( | |
| + mavenLoggerFactory.logLevelRecorder.metThreshold(), | |
| + "Expected " + mavenLoggerFactory.logLevelRecorder + ".metThreshold() to return true"); | |
| } | |
| } | |
| diff --git a/impl/maven-support/pom.xml b/impl/maven-support/pom.xml | |
| index 87f45ef518..234d50eb9f 100644 | |
| --- a/impl/maven-support/pom.xml | |
| +++ b/impl/maven-support/pom.xml | |
| @@ -5,7 +5,7 @@ | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-support</artifactId> | |
| @@ -185,7 +185,7 @@ | |
| </goals> | |
| <phase>generate-sources</phase> | |
| <configuration> | |
| - <version>4.1.0</version> | |
| + <version>4.2.0</version> | |
| <basedir>${project.basedir}/../../api/maven-api-model</basedir> | |
| <velocityBasedir>${project.basedir}/../../src/mdo</velocityBasedir> | |
| <models> | |
| diff --git a/impl/maven-testing/pom.xml b/impl/maven-testing/pom.xml | |
| index 6bef6bb4b0..4683a05adf 100644 | |
| --- a/impl/maven-testing/pom.xml | |
| +++ b/impl/maven-testing/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-testing</artifactId> | |
| diff --git a/impl/maven-testing/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java b/impl/maven-testing/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java | |
| index 12de0178d1..67e2c71486 100644 | |
| --- a/impl/maven-testing/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java | |
| +++ b/impl/maven-testing/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java | |
| @@ -124,8 +124,7 @@ protected void setupContainer() { | |
| @Override | |
| public void afterEach(ExtensionContext context) throws Exception { | |
| if (injector != null) { | |
| - // TODO: implement | |
| - // injector.dispose(); | |
| + injector.dispose(); | |
| injector = null; | |
| } | |
| } | |
| diff --git a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java | |
| index 1a9bfff9c5..9a9d229031 100644 | |
| --- a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java | |
| +++ b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java | |
| @@ -265,8 +265,8 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte | |
| XmlNode pluginConfiguration = model.getBuild().getPlugins().stream() | |
| .filter(p -> | |
| Objects.equals(p.getGroupId(), coord[0]) && Objects.equals(p.getArtifactId(), coord[1])) | |
| - .map(ConfigurationContainer::getConfiguration) | |
| .findFirst() | |
| + .map(ConfigurationContainer::getConfiguration) | |
| .orElseGet(() -> XmlNode.newInstance("config")); | |
| List<XmlNode> children = mojoParameters.stream() | |
| .map(mp -> XmlNode.newInstance(mp.name(), mp.value())) | |
| diff --git a/impl/maven-xml/pom.xml b/impl/maven-xml/pom.xml | |
| index f8e819de09..fce318be3d 100644 | |
| --- a/impl/maven-xml/pom.xml | |
| +++ b/impl/maven-xml/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven-impl-modules</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-xml</artifactId> | |
| diff --git a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java | |
| index bb99243ccf..e97a221380 100644 | |
| --- a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java | |
| +++ b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/DefaultXmlService.java | |
| @@ -314,8 +314,8 @@ public XmlNode doMerge(XmlNode dominant, XmlNode recessive, Boolean childMergeOv | |
| .filter(n2 -> n2.name().equals(n1)) | |
| .collect(Collectors.toList())) | |
| .filter(l -> !l.isEmpty()) | |
| - .map(List::iterator) | |
| .findFirst() | |
| + .map(List::iterator) | |
| .orElse(null)); | |
| if (it == null) { | |
| if (children == null) { | |
| diff --git a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/ImmutableCollections.java b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/ImmutableCollections.java | |
| index dc6651da94..55775136eb 100644 | |
| --- a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/ImmutableCollections.java | |
| +++ b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/ImmutableCollections.java | |
| @@ -19,38 +19,24 @@ | |
| package org.apache.maven.internal.xml; | |
| import java.io.Serializable; | |
| -import java.util.AbstractList; | |
| import java.util.AbstractMap; | |
| import java.util.AbstractSet; | |
| import java.util.Collection; | |
| -import java.util.Comparator; | |
| import java.util.Iterator; | |
| import java.util.List; | |
| -import java.util.ListIterator; | |
| import java.util.Map; | |
| import java.util.NoSuchElementException; | |
| -import java.util.Properties; | |
| -import java.util.RandomAccess; | |
| import java.util.Set; | |
| import java.util.function.BiFunction; | |
| import java.util.function.Function; | |
| import java.util.function.Predicate; | |
| -import java.util.function.UnaryOperator; | |
| +/** | |
| + * This should be removed when https://bugs.openjdk.org/browse/JDK-8323729 | |
| + * is released in our minimum JDK. | |
| + */ | |
| class ImmutableCollections { | |
| - private static final List<?> EMPTY_LIST = new AbstractImmutableList<Object>() { | |
| - @Override | |
| - public Object get(int index) { | |
| - throw new IndexOutOfBoundsException(); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return 0; | |
| - } | |
| - }; | |
| - | |
| private static final Map<?, ?> EMPTY_MAP = new AbstractImmutableMap<Object, Object>() { | |
| @Override | |
| public Set<Entry<Object, Object>> entrySet() { | |
| @@ -78,31 +64,8 @@ public int size() { | |
| } | |
| }; | |
| - static <E> List<E> copy(Collection<E> collection) { | |
| - if (collection == null) { | |
| - return emptyList(); | |
| - } else if (collection instanceof AbstractImmutableList) { | |
| - return (List<E>) collection; | |
| - } else { | |
| - return switch (collection.size()) { | |
| - case 0 -> emptyList(); | |
| - case 1 -> singletonList(collection.iterator().next()); | |
| - case 2 -> { | |
| - Iterator<E> it = collection.iterator(); | |
| - yield new List2<>(it.next(), it.next()); | |
| - } | |
| - default -> new ListN<>(collection); | |
| - }; | |
| - } | |
| - } | |
| - | |
| - @SuppressWarnings("unchecked") | |
| - static <E> List<E> emptyList() { | |
| - return (List<E>) EMPTY_LIST; | |
| - } | |
| - | |
| - static <E> List<E> singletonList(E element) { | |
| - return new List1<>(element); | |
| + static <E1, E2 extends E1> List<E1> copy(Collection<E2> collection) { | |
| + return collection == null ? List.of() : List.copyOf(collection); | |
| } | |
| static <K, V> Map<K, V> copy(Map<K, V> map) { | |
| @@ -111,14 +74,15 @@ static <K, V> Map<K, V> copy(Map<K, V> map) { | |
| } else if (map instanceof AbstractImmutableMap) { | |
| return map; | |
| } else { | |
| - return switch (map.size()) { | |
| - case 0 -> emptyMap(); | |
| - case 1 -> { | |
| + switch (map.size()) { | |
| + case 0: | |
| + return emptyMap(); | |
| + case 1: | |
| Map.Entry<K, V> entry = map.entrySet().iterator().next(); | |
| - yield singletonMap(entry.getKey(), entry.getValue()); | |
| - } | |
| - default -> new MapN<>(map); | |
| - }; | |
| + return singletonMap(entry.getKey(), entry.getValue()); | |
| + default: | |
| + return new MapN<>(map); | |
| + } | |
| } | |
| } | |
| @@ -131,315 +95,6 @@ static <K, V> Map<K, V> singletonMap(K key, V value) { | |
| return new Map1<>(key, value); | |
| } | |
| - static Properties copy(Properties properties) { | |
| - if (properties instanceof ROProperties) { | |
| - return properties; | |
| - } | |
| - return new ROProperties(properties); | |
| - } | |
| - | |
| - private static class List1<E> extends AbstractImmutableList<E> { | |
| - private final E element; | |
| - | |
| - private List1(E element) { | |
| - this.element = element; | |
| - } | |
| - | |
| - @Override | |
| - public E get(int index) { | |
| - if (index == 0) { | |
| - return element; | |
| - } | |
| - throw outOfBounds(index); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return 1; | |
| - } | |
| - } | |
| - | |
| - private static class List2<E> extends AbstractImmutableList<E> { | |
| - private final E element1; | |
| - private final E element2; | |
| - | |
| - private List2(E element1, E element2) { | |
| - this.element1 = element1; | |
| - this.element2 = element2; | |
| - } | |
| - | |
| - @Override | |
| - public E get(int index) { | |
| - if (index == 0) { | |
| - return element1; | |
| - } else if (index == 1) { | |
| - return element2; | |
| - } | |
| - throw outOfBounds(index); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return 2; | |
| - } | |
| - } | |
| - | |
| - private static class ListN<E> extends AbstractImmutableList<E> { | |
| - private final Object[] elements; | |
| - | |
| - private ListN(Collection<E> elements) { | |
| - this.elements = elements.toArray(); | |
| - } | |
| - | |
| - @SuppressWarnings("unchecked") | |
| - @Override | |
| - public E get(int index) { | |
| - return (E) elements[index]; | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return elements.length; | |
| - } | |
| - } | |
| - | |
| - private abstract static class AbstractImmutableList<E> extends AbstractList<E> | |
| - implements RandomAccess, Serializable { | |
| - @Override | |
| - public boolean add(E e) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean remove(Object o) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean addAll(Collection<? extends E> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean removeAll(Collection<?> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean retainAll(Collection<?> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void clear() { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean removeIf(Predicate<? super E> filter) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void replaceAll(UnaryOperator<E> operator) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void sort(Comparator<? super E> c) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Iterator<E> iterator() { | |
| - return new Itr(0); | |
| - } | |
| - | |
| - @Override | |
| - public ListIterator<E> listIterator() { | |
| - return new Itr(0); | |
| - } | |
| - | |
| - @Override | |
| - public ListIterator<E> listIterator(int index) { | |
| - if (index < 0 || index > size()) { | |
| - throw outOfBounds(index); | |
| - } | |
| - return new Itr(index); | |
| - } | |
| - | |
| - @Override | |
| - public List<E> subList(int fromIndex, int toIndex) { | |
| - if (fromIndex < 0) { | |
| - throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); | |
| - } | |
| - if (toIndex > size()) { | |
| - throw new IndexOutOfBoundsException("toIndex = " + toIndex); | |
| - } | |
| - if (fromIndex > toIndex) { | |
| - throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); | |
| - } | |
| - return new SubList(fromIndex, toIndex); | |
| - } | |
| - | |
| - protected IndexOutOfBoundsException outOfBounds(int index) { | |
| - return new IndexOutOfBoundsException("Index: " + index + ", Size: " + size()); | |
| - } | |
| - | |
| - private class SubList extends AbstractImmutableList<E> { | |
| - private final int fromIndex; | |
| - private final int toIndex; | |
| - | |
| - private SubList(int fromIndex, int toIndex) { | |
| - this.fromIndex = fromIndex; | |
| - this.toIndex = toIndex; | |
| - } | |
| - | |
| - @Override | |
| - public E get(int index) { | |
| - if (index < 0 || index > size()) { | |
| - throw outOfBounds(index); | |
| - } | |
| - return AbstractImmutableList.this.get(fromIndex + index); | |
| - } | |
| - | |
| - @Override | |
| - public int size() { | |
| - return toIndex - fromIndex; | |
| - } | |
| - } | |
| - | |
| - private class Itr implements ListIterator<E> { | |
| - int index; | |
| - | |
| - private Itr(int index) { | |
| - this.index = index; | |
| - } | |
| - | |
| - @Override | |
| - public boolean hasNext() { | |
| - return index < size(); | |
| - } | |
| - | |
| - @Override | |
| - public E next() { | |
| - return get(index++); | |
| - } | |
| - | |
| - @Override | |
| - public boolean hasPrevious() { | |
| - return index > 0; | |
| - } | |
| - | |
| - @Override | |
| - public E previous() { | |
| - return get(--index); | |
| - } | |
| - | |
| - @Override | |
| - public int nextIndex() { | |
| - return index; | |
| - } | |
| - | |
| - @Override | |
| - public int previousIndex() { | |
| - return index - 1; | |
| - } | |
| - | |
| - @Override | |
| - public void remove() { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void set(E e) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void add(E e) { | |
| - throw uoe(); | |
| - } | |
| - } | |
| - } | |
| - | |
| - private static class ROProperties extends Properties { | |
| - private ROProperties(Properties props) { | |
| - super(); | |
| - if (props != null) { | |
| - // Do not use super.putAll, as it may delegate to put which throws an UnsupportedOperationException | |
| - for (Map.Entry<Object, Object> e : props.entrySet()) { | |
| - super.put(e.getKey(), e.getValue()); | |
| - } | |
| - } | |
| - } | |
| - | |
| - @Override | |
| - public Object put(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object remove(Object key) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void putAll(Map<?, ?> t) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void clear() { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public void replaceAll(BiFunction<? super Object, ? super Object, ?> function) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object putIfAbsent(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean remove(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public boolean replace(Object key, Object oldValue, Object newValue) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object replace(Object key, Object value) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object computeIfAbsent(Object key, Function<? super Object, ?> mappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object compute(Object key, BiFunction<? super Object, ? super Object, ?> remappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - | |
| - @Override | |
| - public Object merge(Object key, Object value, BiFunction<? super Object, ? super Object, ?> remappingFunction) { | |
| - throw uoe(); | |
| - } | |
| - } | |
| - | |
| private static class Map1<K, V> extends AbstractImmutableMap<K, V> { | |
| private final Entry<K, V> entry; | |
| diff --git a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeImpl.java b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeImpl.java | |
| index 8ae8569b14..c57eb6e937 100644 | |
| --- a/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeImpl.java | |
| +++ b/impl/maven-xml/src/main/java/org/apache/maven/internal/xml/XmlNodeImpl.java | |
| @@ -19,7 +19,6 @@ | |
| package org.apache.maven.internal.xml; | |
| import java.io.IOException; | |
| -import java.io.Serial; | |
| import java.io.Serializable; | |
| import java.io.StringWriter; | |
| import java.util.List; | |
| @@ -38,8 +37,6 @@ | |
| @Deprecated | |
| @SuppressWarnings("removal") | |
| public class XmlNodeImpl implements Serializable, XmlNode { | |
| - @Serial | |
| - private static final long serialVersionUID = 2567894443061173996L; | |
| @Nonnull | |
| protected final String prefix; | |
| diff --git a/impl/maven-xml/src/test/java/org/apache/maven/internal/xml/XmlNodeImplTest.java b/impl/maven-xml/src/test/java/org/apache/maven/internal/xml/XmlNodeImplTest.java | |
| index 5b6f11caa0..541b99c451 100644 | |
| --- a/impl/maven-xml/src/test/java/org/apache/maven/internal/xml/XmlNodeImplTest.java | |
| +++ b/impl/maven-xml/src/test/java/org/apache/maven/internal/xml/XmlNodeImplTest.java | |
| @@ -491,19 +491,19 @@ void testEqualsComplex() throws XMLStreamException, XmlPullParserException, IOEx | |
| } | |
| /** | |
| - * <p>testEqualsIsNullSafe.</p> | |
| + * <p>testEqualsWithDifferentStructures.</p> | |
| */ | |
| @Test | |
| - void testEqualsIsNullSafe() throws XMLStreamException, IOException { | |
| + void testEqualsWithDifferentStructures() throws XMLStreamException, IOException { | |
| String testDom = "<configuration><items thing='blah'><item>one</item><item>two</item></items></configuration>"; | |
| XmlNode dom = toXmlNode(testDom); | |
| + // Create a different DOM structure with different attributes and children | |
| Map<String, String> attributes = new HashMap<>(); | |
| - attributes.put("nullValue", null); | |
| - attributes.put(null, "nullKey"); | |
| + attributes.put("differentAttribute", "differentValue"); | |
| List<XmlNode> childList = new ArrayList<>(); | |
| - childList.add(null); | |
| - Xpp3Dom dom2 = new Xpp3Dom(XmlNode.newInstance(dom.name(), null, attributes, childList, null)); | |
| + childList.add(XmlNode.newInstance("differentChild", "differentValue", null, null, null)); | |
| + Xpp3Dom dom2 = new Xpp3Dom(XmlNode.newInstance(dom.name(), "differentValue", attributes, childList, null)); | |
| assertNotEquals(dom, dom2); | |
| assertNotEquals(dom2, dom); | |
| diff --git a/impl/pom.xml b/impl/pom.xml | |
| index a7cc8f02bd..93b8fbc7ba 100644 | |
| --- a/impl/pom.xml | |
| +++ b/impl/pom.xml | |
| @@ -22,7 +22,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>maven</artifactId> | |
| - <version>4.0.0-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>maven-impl-modules</artifactId> | |
| diff --git a/its/core-it-suite/pom.xml b/its/core-it-suite/pom.xml | |
| index 5fd7927b81..0f40b37b4f 100644 | |
| --- a/its/core-it-suite/pom.xml | |
| +++ b/its/core-it-suite/pom.xml | |
| @@ -23,7 +23,7 @@ under the License. | |
| <parent> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>core-its</artifactId> | |
| - <version>2.1-SNAPSHOT</version> | |
| + <version>4.1.0-SNAPSHOT</version> | |
| </parent> | |
| <artifactId>core-it-suite</artifactId> | |
| @@ -34,21 +34,31 @@ under the License. | |
| <!-- | |
| By default, the project just packages the tests in an artifact. To actually run them, activate the profile "run-its": | |
| - mvn clean test -Prun-its | |
| + mvn clean verify -Prun-its | |
| - This will subject the Maven version running the build to the integration tests. If you would like to test a different | |
| - Maven distribution, you can use the system property "mavenHome" to specify the path of the Maven distribution to test: | |
| + This will test the Maven distribution generated by the current build (apache-maven/target/apache-maven-xxx.zip). | |
| - mvn clean test -Prun-its -DmavenHome=<maven-under-test> | |
| + If you would like to test the Maven version running the build instead, use the "maven-from-build" profile: | |
| - Alternatively, you can just specify the version of a previously installed/deployed Maven distribution which will be | |
| + mvn clean verify -Prun-its,maven-from-build | |
| + | |
| + The "maven-from-build" profile restores the old default behavior where the integration tests run against the | |
| + Maven installation that is executing the build (i.e., the value of ${maven.home}). This is useful when you want | |
| + to test against a specific Maven installation without having to build the distribution first. | |
| + | |
| + You can also specify the path of a specific Maven distribution to test using the "mavenHome" property (this | |
| + automatically activates the "maven-from-build" profile): | |
| + | |
| + mvn clean verify -Prun-its -DmavenHome=<maven-under-test> | |
| + | |
| + Alternatively, you can specify the version of a previously installed/deployed Maven distribution which will be | |
| downloaded, unpacked and tested: | |
| - mvn clean test -Prun-its -DmavenVersion=2.2.1 | |
| + mvn clean verify -Prun-its -DmavenVersion=2.2.1 | |
| It's also possible to point the ITs at an already downloaded Maven distribution: | |
| - mvn clean test -Prun-its -DmavenDistro=<path-to-bin-archive> | |
| + mvn clean verify -Prun-its -DmavenDistro=<path-to-bin-archive> | |
| To run all the ITs using forked Maven, additionally activate the profile "forked". | |
| @@ -62,8 +72,8 @@ under the License. | |
| --> | |
| <properties> | |
| - <!-- The original Maven distribution to test. --> | |
| - <mavenHome>${maven.home}</mavenHome> | |
| + <!-- The original Maven distribution to test. By default, use the distribution built by the current build. --> | |
| + <mavenHome>${project.build.directory}/apache-maven</mavenHome> | |
| <!-- The (possibly instrumented copy of the) Maven distribution we actually | |
| use for the tests. --> | |
| <preparedMavenHome>${mavenHome}</preparedMavenHome> | |
| @@ -80,6 +90,12 @@ under the License. | |
| <jetty9Version>9.4.57.v20241219</jetty9Version> | |
| <stubPluginVersion>0.1-stub-SNAPSHOT</stubPluginVersion> | |
| + <!-- Support artifacts version - kept separate from Maven version --> | |
| + <core-it-support-version>2.1-SNAPSHOT</core-it-support-version> | |
| + | |
| + <its.forkCount>0.75C</its.forkCount> | |
| + <its.test /> | |
| + | |
| <version.toolbox>0.7.4</version.toolbox> | |
| </properties> | |
| @@ -89,11 +105,6 @@ under the License. | |
| <artifactId>junit-jupiter</artifactId> | |
| <!-- NOTE: Use compile scope for transitivity. --> | |
| </dependency> | |
| - <dependency> | |
| - <groupId>org.hamcrest</groupId> | |
| - <artifactId>hamcrest</artifactId> | |
| - <version>3.0</version> | |
| - </dependency> | |
| <dependency> | |
| <groupId>org.codehaus.plexus</groupId> | |
| <artifactId>plexus-utils</artifactId> | |
| @@ -126,37 +137,37 @@ under the License. | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>maven-it-plugin-bootstrap</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>core-it-component</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>core-it-toolchain</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>core-it-wagon</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>maven-it-helper</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>core-it-extension</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its</groupId> | |
| <artifactId>core-it-javaagent</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.plugins</groupId> | |
| @@ -236,227 +247,115 @@ under the License. | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-active-collection</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-artifact</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-class-loader</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-configuration</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-context-passing</artifactId> | |
| - <version>${project.version}</version> | |
| + <!-- dep-c is an optional dependency of maven-it-plugin-class-loader, so it should not be transitively included --> | |
| + <groupId>org.apache.maven.its.plugins.class-loader</groupId> | |
| + <artifactId>dep-c</artifactId> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-dependency-collection</artifactId> | |
| - <version>${project.version}</version> | |
| + <artifactId>maven-it-plugin-configuration</artifactId> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-dependency-resolution</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-expression</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-error</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-extension-consumer</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-extension-provider</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-fork</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-invalid-descriptor</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| + | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-log-file</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-model-interpolation</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-no-default-comp</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-no-project</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-online</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-optional-mojos</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-packaging</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-plugin-dependency</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-project</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-project-interpolation</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-setter</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-singleton-component</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-site</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-toolchain</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-touch</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-uses-properties</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-uses-wagon</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-all</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-plexus-utils-11</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-plexus-utils-new</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-plexus-component-api</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-log4j</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| + | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-extension1</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-extension2</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.maven.its.plugins</groupId> | |
| <artifactId>maven-it-plugin-plexus-lifecycle</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>maven-it-plugin-settings</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>mng-5805-pkg-type-mojo-configuration-extension</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>mng-5805-pkg-type-mojo-configuration-extension2</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>mng-5805-pkg-type-mojo-configuration-plugin</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>mng-5805-pkg-type-mojo-configuration-plugin-dep</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>mng-5958-pkg-type-extension</artifactId> | |
| - <version>${project.version}</version> | |
| - </dependency> | |
| - <dependency> | |
| - <groupId>org.apache.maven.its.plugins</groupId> | |
| - <artifactId>mng-6759-resolves-project-dependencies-plugin</artifactId> | |
| - <version>${project.version}</version> | |
| + <version>${core-it-support-version}</version> | |
| </dependency> | |
| </dependencies> | |
| @@ -500,21 +399,48 @@ under the License. | |
| </pom> | |
| </configuration> | |
| </plugin> | |
| - | |
| </plugins> | |
| </pluginManagement> | |
| + | |
| <plugins> | |
| + <plugin> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-invoker-plugin</artifactId> | |
| + <configuration> | |
| + <skipInstallation>true</skipInstallation> | |
| + <localRepositoryPath>${preparedUserHome}/.m2/repository</localRepositoryPath> | |
| + <extraArtifacts> | |
| + <extraArtifact>eu.maveniverse.maven.plugins:toolbox:${version.toolbox}:maven-plugin</extraArtifact> | |
| + </extraArtifacts> | |
| + </configuration> | |
| + <executions> | |
| + <execution> | |
| + <id>install</id> | |
| + <goals> | |
| + <goal>install</goal> | |
| + </goals> | |
| + </execution> | |
| + </executions> | |
| + </plugin> | |
| <plugin> | |
| <groupId>org.apache.maven.plugins</groupId> | |
| <artifactId>maven-surefire-plugin</artifactId> | |
| <configuration> | |
| + <skipTests>true</skipTests> | |
| + </configuration> | |
| + </plugin> | |
| + <plugin> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-failsafe-plugin</artifactId> | |
| + <configuration> | |
| + <test>${its.test}</test> | |
| <includes> | |
| <include>**/MavenIT*.java</include> | |
| </includes> | |
| <!-- test annotated by @Tag("disabled") will be skipped from executions --> | |
| <excludedGroups>disabled</excludedGroups> | |
| - <skip>true</skip> | |
| - <forkCount>1</forkCount> | |
| + <skipTests>true</skipTests> | |
| + <forkCount>${its.forkCount}</forkCount> | |
| <reuseForks>true</reuseForks> | |
| <!-- NOTE: Maven plugins have access to the system class path, keep it clean --> | |
| <useSystemClassLoader>false</useSystemClassLoader> | |
| @@ -525,7 +451,6 @@ under the License. | |
| <maven.test.repo.outer>${settings.localRepository}</maven.test.repo.outer> | |
| <maven.test.repo.local>${preparedUserHome}/.m2/repository</maven.test.repo.local> | |
| <maven.test.tmpdir>${project.build.testOutputDirectory}</maven.test.tmpdir> | |
| - <maven.version>${maven.version}</maven.version> | |
| <maven.home>${preparedMavenHome}</maven.home> | |
| <maven.it.global-settings.dir>${project.build.testOutputDirectory}</maven.it.global-settings.dir> | |
| </systemPropertyVariables> | |
| @@ -534,10 +459,68 @@ under the License. | |
| <excludedEnvironmentVariable>MAVEN_OPTS</excludedEnvironmentVariable> | |
| </excludedEnvironmentVariables> | |
| </configuration> | |
| + <executions> | |
| + <execution> | |
| + <goals> | |
| + <goal>integration-test</goal> | |
| + <goal>verify</goal> | |
| + </goals> | |
| + </execution> | |
| + </executions> | |
| + </plugin> | |
| + <!-- Default behavior: extract and use the Maven distribution built by the current build --> | |
| + <plugin> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-antrun-plugin</artifactId> | |
| + <executions> | |
| + <execution> | |
| + <id>unpack-built-maven-distro</id> | |
| + <goals> | |
| + <goal>run</goal> | |
| + </goals> | |
| + <phase>process-test-resources</phase> | |
| + <configuration> | |
| + <target> | |
| + <delete dir="${mavenHome}" /> | |
| + <unzip dest="${mavenHome}" src="${sessionRootDirectory}/apache-maven/target/apache-maven-${project.version}-bin.zip"> | |
| + <regexpmapper from="^([^/]+)/(.*)$" handledirsep="true" to="\2" /> | |
| + </unzip> | |
| + <chmod dir="${mavenHome}/bin" includes="mvn,mvnDebug,mvnenc" perm="ugo+rx" /> | |
| + </target> | |
| + </configuration> | |
| + </execution> | |
| + </executions> | |
| </plugin> | |
| </plugins> | |
| </build> | |
| <profiles> | |
| + <profile> | |
| + <id>maven-from-build</id> | |
| + <activation> | |
| + <property> | |
| + <name>mavenHome</name> | |
| + </property> | |
| + </activation> | |
| + <properties> | |
| + <!-- Use the Maven installation running the build (old default behavior) --> | |
| + <mavenHome>${maven.home}</mavenHome> | |
| + </properties> | |
| + <build> | |
| + <plugins> | |
| + <!-- Disable the default distribution extraction when using mavenHome --> | |
| + <plugin> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-antrun-plugin</artifactId> | |
| + <executions> | |
| + <execution> | |
| + <id>unpack-built-maven-distro</id> | |
| + <phase>none</phase> | |
| + </execution> | |
| + </executions> | |
| + </plugin> | |
| + </plugins> | |
| + </build> | |
| + </profile> | |
| <profile> | |
| <id>mimir</id> | |
| <build> | |
| @@ -565,30 +548,22 @@ under the License. | |
| </plugins> | |
| </build> | |
| </profile> | |
| - <profile> | |
| - <id>parallel</id> | |
| - <build> | |
| - <plugins> | |
| - <plugin> | |
| - <groupId>org.apache.maven.plugins</groupId> | |
| - <artifactId>maven-surefire-plugin</artifactId> | |
| - <configuration> | |
| - <parallel>suitesAndClasses</parallel> | |
| - <threadCount>6</threadCount> | |
| - </configuration> | |
| - </plugin> | |
| - </plugins> | |
| - </build> | |
| - </profile> | |
| <profile> | |
| <id>run-its</id> | |
| <dependencies> | |
| <dependency> | |
| <groupId>org.apache.maven</groupId> | |
| <artifactId>apache-maven</artifactId> | |
| - <version>${maven-version}</version> | |
| + <version>${project.version}</version> | |
| <classifier>bin</classifier> | |
| <type>zip</type> | |
| + <exclusions> | |
| + <!-- dependency only on Maven distribution --> | |
| + <exclusion> | |
| + <groupId>*</groupId> | |
| + <artifactId>*</artifactId> | |
| + </exclusion> | |
| + </exclusions> | |
| </dependency> | |
| <!-- not really used but will force download in the local repo used --> | |
| <dependency> | |
| @@ -607,9 +582,17 @@ under the License. | |
| <plugins> | |
| <plugin> | |
| <groupId>org.apache.maven.plugins</groupId> | |
| - <artifactId>maven-surefire-plugin</artifactId> | |
| + <artifactId>maven-invoker-plugin</artifactId> | |
| + <configuration> | |
| + <skipInstallation>false</skipInstallation> | |
| + </configuration> | |
| + </plugin> | |
| + <plugin> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-failsafe-plugin</artifactId> | |
| <configuration> | |
| - <skip>false</skip> | |
| + <skipTests>false</skipTests> | |
| + <redirectTestOutputToFile>false</redirectTestOutputToFile> | |
| </configuration> | |
| </plugin> | |
| </plugins> | |
| @@ -625,7 +608,7 @@ under the License. | |
| <build> | |
| <plugins> | |
| <plugin> | |
| - <artifactId>maven-surefire-plugin</artifactId> | |
| + <artifactId>maven-failsafe-plugin</artifactId> | |
| <configuration> | |
| <systemPropertyVariables> | |
| <!-- Pass this through to the tests (if set!) to have them | |
| @@ -642,9 +625,16 @@ under the License. | |
| <build> | |
| <plugins> | |
| <plugin> | |
| - <artifactId>maven-surefire-plugin</artifactId> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-invoker-plugin</artifactId> | |
| + <configuration> | |
| + <skipInstallation>false</skipInstallation> | |
| + </configuration> | |
| + </plugin> | |
| + <plugin> | |
| + <artifactId>maven-failsafe-plugin</artifactId> | |
| <configuration> | |
| - <skip>false</skip> | |
| + <skipTests>false</skipTests> | |
| <systemPropertyVariables> | |
| <verifier.forkMode>forked</verifier.forkMode> | |
| </systemPropertyVariables> | |
| @@ -666,9 +656,16 @@ under the License. | |
| <build> | |
| <plugins> | |
| <plugin> | |
| - <artifactId>maven-surefire-plugin</artifactId> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-invoker-plugin</artifactId> | |
| + <configuration> | |
| + <skipInstallation>false</skipInstallation> | |
| + </configuration> | |
| + </plugin> | |
| + <plugin> | |
| + <artifactId>maven-failsafe-plugin</artifactId> | |
| <configuration> | |
| - <skip>false</skip> | |
| + <skipTests>false</skipTests> | |
| </configuration> | |
| </plugin> | |
| <plugin> | |
| @@ -701,6 +698,11 @@ under the License. | |
| <groupId>org.apache.maven.plugins</groupId> | |
| <artifactId>maven-antrun-plugin</artifactId> | |
| <executions> | |
| + <!-- Disable the default distribution extraction --> | |
| + <execution> | |
| + <id>unpack-built-maven-distro</id> | |
| + <phase>none</phase> | |
| + </execution> | |
| <execution> | |
| <id>unpack-maven-distro</id> | |
| <goals> | |
| @@ -735,15 +737,27 @@ under the License. | |
| <build> | |
| <plugins> | |
| <plugin> | |
| - <artifactId>maven-surefire-plugin</artifactId> | |
| + <groupId>org.apache.maven.plugins</groupId> | |
| + <artifactId>maven-invoker-plugin</artifactId> | |
| <configuration> | |
| - <skip>false</skip> | |
| + <skipInstallation>false</skipInstallation> | |
| + </configuration> | |
| + </plugin> | |
| + <plugin> | |
| + <artifactId>maven-failsafe-plugin</artifactId> | |
| + <configuration> | |
| + <skipTests>false</skipTests> | |
| </configuration> | |
| </plugin> | |
| <plugin> | |
| <groupId>org.apache.maven.plugins</groupId> | |
| <artifactId>maven-antrun-plugin</artifactId> | |
| <executions> | |
| + <!-- Disable the default distribution extraction --> | |
| + <execution> | |
| + <id>unpack-built-maven-distro</id> | |
| + <phase>none</phase> | |
| + </execution> | |
| <execution> | |
| <id>unpack-maven-distro</id> | |
| <goals> | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenIT0064MojoConfigViaSettersTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenIT0064MojoConfigViaSettersTest.java | |
| index 529fce1bdf..568e2ae73b 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenIT0064MojoConfigViaSettersTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenIT0064MojoConfigViaSettersTest.java | |
| @@ -37,7 +37,16 @@ public MavenIT0064MojoConfigViaSettersTest() { | |
| public void testit0064() throws Exception { | |
| File testDir = extractResources("/it0064"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-setter").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.addCliArgument("org.apache.maven.its.plugins:maven-it-plugin-setter:setter-touch"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITMissingNamespaceTest b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITMissingNamespaceTest | |
| new file mode 100644 | |
| index 0000000000..64229470a8 | |
| --- /dev/null | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITMissingNamespaceTest | |
| @@ -0,0 +1,47 @@ | |
| +/* | |
| + * 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.it; | |
| + | |
| +import java.io.File; | |
| + | |
| +import org.junit.jupiter.api.Test; | |
| + | |
| +public class MavenITMissingNamespaceTest extends AbstractMavenIntegrationTestCase { | |
| + | |
| + public MavenITMissingNamespaceTest() { | |
| + super(ALL_MAVEN_VERSIONS); | |
| + } | |
| + | |
| + /** | |
| + * Test when project element does not have an xmlns attribute. | |
| + */ | |
| + @Test | |
| + public void testMissingNamespace() throws Exception { | |
| + | |
| + boolean supportSpaceInXml = matchesVersionRange("[3.1.0,)"); | |
| + | |
| + File testDir = extractResources("/missing-namespace"); | |
| + Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("validate"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + } | |
| +} | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0282NonReactorExecWhenProjectIndependentTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0282NonReactorExecWhenProjectIndependentTest.java | |
| index 926c403168..ebcafdfc7c 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0282NonReactorExecWhenProjectIndependentTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0282NonReactorExecWhenProjectIndependentTest.java | |
| @@ -42,7 +42,16 @@ public MavenITmng0282NonReactorExecWhenProjectIndependentTest() { | |
| public void testitMNG282() throws Exception { | |
| File testDir = extractResources("/mng-0282"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-no-project").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.deleteDirectory("subproject/target"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0768OfflineModeTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0768OfflineModeTest.java | |
| index 01c4578736..338162c502 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0768OfflineModeTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0768OfflineModeTest.java | |
| @@ -74,7 +74,7 @@ public void handle( | |
| response.setStatus(HttpServletResponse.SC_OK); | |
| if (request.getRequestURI().endsWith(".pom")) { | |
| - writer.println("<project>"); | |
| + writer.println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| writer.println(" <modelVersion>4.0.0</modelVersion>"); | |
| writer.println(" <groupId>org.apache.maven.its.mng0768</groupId>"); | |
| writer.println(" <artifactId>dep</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0823MojoContextPassingTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0823MojoContextPassingTest.java | |
| index 1c740fe53b..ddd4af389a 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0823MojoContextPassingTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng0823MojoContextPassingTest.java | |
| @@ -42,7 +42,16 @@ public MavenITmng0823MojoContextPassingTest() { | |
| public void testitMNG0823() throws Exception { | |
| File testDir = extractResources("/mng-0823"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-context-passing").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.addCliArguments( | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2305MultipleProxiesTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2305MultipleProxiesTest.java | |
| index adc45a1bcd..35fefebd63 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2305MultipleProxiesTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2305MultipleProxiesTest.java | |
| @@ -147,7 +147,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques | |
| // HTTP connector serves only http-0.1.jar and HTTPS connector serves only https-0.1.jar | |
| response.setStatus(HttpServletResponse.SC_NOT_FOUND); | |
| } else if (uri.endsWith(".pom")) { | |
| - writer.println("<project>"); | |
| + writer.println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| writer.println(" <modelVersion>4.0.0</modelVersion>"); | |
| writer.println(" <groupId>org.apache.maven.its.mng2305</groupId>"); | |
| writer.println(" <artifactId>" + request.getScheme() + "</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2843PluginConfigPropertiesInjectionTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2843PluginConfigPropertiesInjectionTest.java | |
| index b2afcebdef..8f980e45f1 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2843PluginConfigPropertiesInjectionTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng2843PluginConfigPropertiesInjectionTest.java | |
| @@ -46,7 +46,16 @@ public MavenITmng2843PluginConfigPropertiesInjectionTest() { | |
| public void testitMNG2843() throws Exception { | |
| File testDir = extractResources("/mng-2843"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-uses-properties").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.addCliArgument("validate"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3379ParallelArtifactDownloadsTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3379ParallelArtifactDownloadsTest.java | |
| index 5589f853ed..6fcccfdd4a 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3379ParallelArtifactDownloadsTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3379ParallelArtifactDownloadsTest.java | |
| @@ -63,7 +63,7 @@ public void testitMNG3379() throws Exception { | |
| String gid = "org.apache.maven.its.mng3379."; | |
| assertArtifact(verifier, gid + "a", "x", "0.2-SNAPSHOT", "", "jar", "69c041c12f35894230c7c23c49cd245886c6fb6f"); | |
| - assertArtifact(verifier, gid + "a", "x", "0.2-SNAPSHOT", "", "pom", "04a8ecb6dc279585b6d17552a4518805f0ff33b9"); | |
| + assertArtifact(verifier, gid + "a", "x", "0.2-SNAPSHOT", "", "pom", "f9bde7791bf97bac962281abc8471d84b35937f1"); | |
| assertArtifact( | |
| verifier, gid + "a", "x", "0.2-SNAPSHOT", "tests", "jar", "69c041c12f35894230c7c23c49cd245886c6fb6f"); | |
| assertArtifact( | |
| @@ -73,7 +73,7 @@ public void testitMNG3379() throws Exception { | |
| assertMetadata(verifier, gid + "a", "x", "0.2-SNAPSHOT", "e1cfc3a77657fc46bb624dee25c61b290e5b4dd7"); | |
| assertArtifact(verifier, gid + "b", "x", "0.2-SNAPSHOT", "", "jar", "efb7c4046565774cd7e44645e02f06ecdf91098d"); | |
| - assertArtifact(verifier, gid + "b", "x", "0.2-SNAPSHOT", "", "pom", "834b45a91af07702a59855bf99614c099979c065"); | |
| + assertArtifact(verifier, gid + "b", "x", "0.2-SNAPSHOT", "", "pom", "0356f3576a4244c23f8ad5574065619bf0230c23"); | |
| assertArtifact( | |
| verifier, gid + "b", "x", "0.2-SNAPSHOT", "tests", "jar", "efb7c4046565774cd7e44645e02f06ecdf91098d"); | |
| assertArtifact( | |
| @@ -84,7 +84,7 @@ public void testitMNG3379() throws Exception { | |
| assertMetadata(verifier, gid + "b", "x", "8f38b1041871f22dcb031544d8a3436c335bfcdb"); | |
| assertArtifact(verifier, gid + "c", "x", "0.2-SNAPSHOT", "", "jar", "1eb0d5a421b3074e8a69b0dcca7e325c0636a932"); | |
| - assertArtifact(verifier, gid + "c", "x", "0.2-SNAPSHOT", "", "pom", "f25d7907d7bd9807e823d15f49363de7826204b0"); | |
| + assertArtifact(verifier, gid + "c", "x", "0.2-SNAPSHOT", "", "pom", "1854c48bff9f2118a85b87ec722dd6431fbd7ca6"); | |
| assertArtifact( | |
| verifier, gid + "c", "x", "0.2-SNAPSHOT", "tests", "jar", "1eb0d5a421b3074e8a69b0dcca7e325c0636a932"); | |
| assertArtifact( | |
| @@ -95,7 +95,7 @@ public void testitMNG3379() throws Exception { | |
| assertMetadata(verifier, gid + "c", "x", "c4848e60d226ec6304df3abd9eba8fdb301b3660"); | |
| assertArtifact(verifier, gid + "d", "x", "0.2-SNAPSHOT", "", "jar", "3d606c564625a594165bcbbe4a24c8f11b18b5a0"); | |
| - assertArtifact(verifier, gid + "d", "x", "0.2-SNAPSHOT", "", "pom", "4255f7a5781e1be7564a09c86eee140fad042de8"); | |
| + assertArtifact(verifier, gid + "d", "x", "0.2-SNAPSHOT", "", "pom", "dac99e0f617b8c2bb178e8d0a9b6e1ca04261dff"); | |
| assertArtifact( | |
| verifier, gid + "d", "x", "0.2-SNAPSHOT", "tests", "jar", "3d606c564625a594165bcbbe4a24c8f11b18b5a0"); | |
| assertArtifact( | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3461MirrorMatchingTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3461MirrorMatchingTest.java | |
| index ec316ddc14..6db4b7eacd 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3461MirrorMatchingTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3461MirrorMatchingTest.java | |
| @@ -91,7 +91,7 @@ public void handle( | |
| response.getWriter().println(request.getRequestURI()); | |
| } else if (request.getRequestURI().endsWith("/b-0.1.pom")) { | |
| response.setStatus(HttpServletResponse.SC_OK); | |
| - response.getWriter().println("<project>"); | |
| + response.getWriter().println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| response.getWriter().println(" <modelVersion>4.0.0</modelVersion>"); | |
| response.getWriter().println(" <groupId>org.apache.maven.its.mng3461</groupId>"); | |
| response.getWriter().println(" <artifactId>b</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3503Xpp3ShadingTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3503Xpp3ShadingTest.java | |
| index f7714ffcab..caba91311a 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3503Xpp3ShadingTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3503Xpp3ShadingTest.java | |
| @@ -39,8 +39,15 @@ public MavenITmng3503Xpp3ShadingTest() { | |
| public void testitMNG3503NoLinkageErrors() throws Exception { | |
| File dir = extractResources("/mng-3503/mng-3503-xpp3Shading-pu11"); | |
| - Verifier verifier; | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(dir, "maven-it-plugin-plexus-utils-11").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + // Then, run the test project that uses the plugin | |
| verifier = newVerifier(dir.getAbsolutePath()); | |
| verifier.addCliArgument("validate"); | |
| @@ -54,7 +61,17 @@ public void testitMNG3503NoLinkageErrors() throws Exception { | |
| @Test | |
| public void testitMNG3503Xpp3Shading() throws Exception { | |
| File dir = extractResources("/mng-3503/mng-3503-xpp3Shading-pu-new"); | |
| - Verifier verifier = newVerifier(dir.getAbsolutePath()); | |
| + | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(dir, "maven-it-plugin-plexus-utils-new").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(dir.getAbsolutePath()); | |
| verifier.addCliArgument("validate"); | |
| verifier.execute(); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3506ArtifactHandlersFromPluginsTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3506ArtifactHandlersFromPluginsTest.java | |
| index 834f8cbed8..f59fe3e948 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3506ArtifactHandlersFromPluginsTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3506ArtifactHandlersFromPluginsTest.java | |
| @@ -49,7 +49,16 @@ public MavenITmng3506ArtifactHandlersFromPluginsTest() { | |
| public void testProjectPackagingUsage() throws IOException, VerificationException { | |
| File testDir = extractResources("/" + AID); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "mng-3506.2/maven-it-plugin-extension2").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.deleteArtifacts(GID); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3991ValidDependencyScopeTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3991ValidDependencyScopeTest.java | |
| index 91aecf650e..f143938862 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3991ValidDependencyScopeTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3991ValidDependencyScopeTest.java | |
| @@ -31,6 +31,7 @@ public class MavenITmng3991ValidDependencyScopeTest extends AbstractMavenIntegra | |
| public MavenITmng3991ValidDependencyScopeTest() { | |
| // TODO: One day, we should be able to error out but this requires to consider extensions and their use cases | |
| + // Disabled for Maven 4.x due to behavior change - see GitHub issue #2510 | |
| super("[5.0,)"); | |
| } | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4091BadPluginDescriptorTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4091BadPluginDescriptorTest.java | |
| index 3a44d10f8a..6c4c4bdec7 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4091BadPluginDescriptorTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4091BadPluginDescriptorTest.java | |
| @@ -41,7 +41,16 @@ public MavenITmng4091BadPluginDescriptorTest() { | |
| public void testitMNG4091InvalidDescriptor() throws Exception { | |
| File testDir = extractResources("/mng-4091/invalid"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-invalid-descriptor").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin (should fail) | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.addCliArgument("validate"); | |
| @@ -66,7 +75,16 @@ public void testitMNG4091InvalidDescriptor() throws Exception { | |
| public void testitMNG4091PluginDependency() throws Exception { | |
| File testDir = extractResources("/mng-4091/plugin-dependency"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-plugin-dependency").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.addCliArgument("validate"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4207PluginWithLog4JTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4207PluginWithLog4JTest.java | |
| index 8a36d684e5..cfac444798 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4207PluginWithLog4JTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4207PluginWithLog4JTest.java | |
| @@ -43,7 +43,16 @@ public MavenITmng4207PluginWithLog4JTest() { | |
| public void testit() throws Exception { | |
| File testDir = extractResources("/mng-4207"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-log4j").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteArtifacts("org.apache.maven.its.mng4207"); | |
| verifier.addCliArgument("-s"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4291MojoRequiresOnlineModeTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4291MojoRequiresOnlineModeTest.java | |
| index 6e4692cc6b..c547820ef6 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4291MojoRequiresOnlineModeTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4291MojoRequiresOnlineModeTest.java | |
| @@ -43,7 +43,16 @@ public MavenITmng4291MojoRequiresOnlineModeTest() { | |
| public void testitDirectInvocation() throws Exception { | |
| File testDir = extractResources("/mng-4291"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-online").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.setLogFileName("log-direct.txt"); | |
| @@ -68,7 +77,16 @@ public void testitDirectInvocation() throws Exception { | |
| public void testitLifecycleInvocation() throws Exception { | |
| File testDir = extractResources("/mng-4291"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-online").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.setLogFileName("log-lifecycle.txt"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4326LocalSnapshotSuppressesRemoteCheckTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4326LocalSnapshotSuppressesRemoteCheckTest.java | |
| index 1ea9298444..b360b745c0 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4326LocalSnapshotSuppressesRemoteCheckTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4326LocalSnapshotSuppressesRemoteCheckTest.java | |
| @@ -107,7 +107,7 @@ public void handle( | |
| writer.println("</metadata>"); | |
| } else if (uri.endsWith(".pom")) { | |
| response.setStatus(HttpServletResponse.SC_OK); | |
| - writer.println("<project>"); | |
| + writer.println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| writer.println(" <modelVersion>4.0.0</modelVersion>"); | |
| writer.println(" <groupId>org.apache.maven.its.mng4326</groupId>"); | |
| writer.println(" <artifactId>dep</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4331DependencyCollectionTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4331DependencyCollectionTest.java | |
| index 367acb14d8..e0ad016b8f 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4331DependencyCollectionTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4331DependencyCollectionTest.java | |
| @@ -48,7 +48,16 @@ public MavenITmng4331DependencyCollectionTest() { | |
| public void testitEarlyLifecyclePhase() throws Exception { | |
| File testDir = extractResources("/mng-4331"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-dependency-collection").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteArtifacts("org.apache.maven.its.mng4331"); | |
| verifier.deleteDirectory("sub-2/target"); | |
| @@ -72,7 +81,16 @@ public void testitEarlyLifecyclePhase() throws Exception { | |
| public void testitCliAggregator() throws Exception { | |
| File testDir = extractResources("/mng-4331"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-dependency-collection").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.deleteArtifacts("org.apache.maven.its.mng4331"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4338OptionalMojosTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4338OptionalMojosTest.java | |
| index a567196295..53d557cbfc 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4338OptionalMojosTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4338OptionalMojosTest.java | |
| @@ -43,7 +43,16 @@ public MavenITmng4338OptionalMojosTest() { | |
| public void testit() throws Exception { | |
| File testDir = extractResources("/mng-4338"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-optional-mojos").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.addCliArgument("validate"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4343MissingReleaseUpdatePolicyTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4343MissingReleaseUpdatePolicyTest.java | |
| index 5b7466ea53..cf97715abc 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4343MissingReleaseUpdatePolicyTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4343MissingReleaseUpdatePolicyTest.java | |
| @@ -79,7 +79,7 @@ public void handle( | |
| response.setStatus(HttpServletResponse.SC_OK); | |
| if (request.getRequestURI().endsWith(".pom")) { | |
| - writer.println("<project>"); | |
| + writer.println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| writer.println(" <modelVersion>4.0.0</modelVersion>"); | |
| writer.println(" <groupId>org.apache.maven.its.mng4343</groupId>"); | |
| writer.println(" <artifactId>dep</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4360WebDavSupportTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4360WebDavSupportTest.java | |
| index 15a4d1c2be..ec26469207 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4360WebDavSupportTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4360WebDavSupportTest.java | |
| @@ -89,7 +89,7 @@ public void handle( | |
| response.setStatus(HttpServletResponse.SC_OK); | |
| if (request.getRequestURI().endsWith(".pom")) { | |
| - writer.println("<project>"); | |
| + writer.println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| writer.println(" <modelVersion>4.0.0</modelVersion>"); | |
| writer.println(" <groupId>org.apache.maven.its.mng4360</groupId>"); | |
| writer.println(" <artifactId>dep</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4381ExtensionSingletonComponentTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4381ExtensionSingletonComponentTest.java | |
| index b70aed5f00..99a5452e5f 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4381ExtensionSingletonComponentTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4381ExtensionSingletonComponentTest.java | |
| @@ -46,7 +46,17 @@ public MavenITmng4381ExtensionSingletonComponentTest() { | |
| public void testit() throws Exception { | |
| File testDir = extractResources("/mng-4381"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = | |
| + newVerifier(new File(testDir, "sub-a/maven-it-plugin-extension-consumer").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("sub-a/target"); | |
| verifier.deleteDirectory("sub-b/target"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4428FollowHttpRedirectTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4428FollowHttpRedirectTest.java | |
| index a6416cf4dc..541a017807 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4428FollowHttpRedirectTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4428FollowHttpRedirectTest.java | |
| @@ -186,7 +186,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques | |
| } | |
| response.setHeader("Location", location); | |
| } else if (uri.endsWith(".pom")) { | |
| - writer.println("<project>"); | |
| + writer.println("<project xmlns=\"http://maven.apache.org/POM/4.0.0\">"); | |
| writer.println(" <modelVersion>4.0.0</modelVersion>"); | |
| writer.println(" <groupId>org.apache.maven.its.mng4428</groupId>"); | |
| writer.println(" <artifactId>dep</artifactId>"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4429CompRequirementOnNonDefaultImplTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4429CompRequirementOnNonDefaultImplTest.java | |
| index 13ff593829..9387de0406 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4429CompRequirementOnNonDefaultImplTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4429CompRequirementOnNonDefaultImplTest.java | |
| @@ -45,7 +45,16 @@ public MavenITmng4429CompRequirementOnNonDefaultImplTest() { | |
| public void testit() throws Exception { | |
| File testDir = extractResources("/mng-4429"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-no-default-comp").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.addCliArgument("validate"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4436SingletonComponentLookupTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4436SingletonComponentLookupTest.java | |
| index d121f277ae..70e7c90569 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4436SingletonComponentLookupTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4436SingletonComponentLookupTest.java | |
| @@ -46,7 +46,16 @@ public MavenITmng4436SingletonComponentLookupTest() { | |
| public void testit() throws Exception { | |
| File testDir = extractResources("/mng-4436"); | |
| - Verifier verifier = newVerifier(testDir.getAbsolutePath()); | |
| + // First, build the test plugin | |
| + Verifier verifier = newVerifier(new File(testDir, "maven-it-plugin-singleton-component").getAbsolutePath()); | |
| + verifier.setAutoclean(false); | |
| + verifier.deleteDirectory("target"); | |
| + verifier.addCliArgument("install"); | |
| + verifier.execute(); | |
| + verifier.verifyErrorFreeLog(); | |
| + | |
| + // Then, run the test project that uses the plugin | |
| + verifier = newVerifier(testDir.getAbsolutePath()); | |
| verifier.setAutoclean(false); | |
| verifier.deleteDirectory("target"); | |
| verifier.addCliArgument("validate"); | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4644StrictPomParsingRejectsMisplacedTextTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4644StrictPomParsingRejectsMisplacedTextTest.java | |
| index c73198525e..938f712942 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4644StrictPomParsingRejectsMisplacedTextTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4644StrictPomParsingRejectsMisplacedTextTest.java | |
| @@ -49,7 +49,7 @@ public void testit() throws Exception { | |
| verifier.execute(); | |
| verifier.verifyErrorFreeLog(); | |
| - fail("Should fail to validate the POM syntax due to misplaced text in <project> element."); | |
| + fail("Should fail to validate the POM syntax due to misplaced text in project element."); | |
| } catch (VerificationException e) { | |
| // expected | |
| } | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4679SnapshotUpdateInPluginTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4679SnapshotUpdateInPluginTest.java | |
| index 4a3efd1e00..34bb359fc7 100644 | |
| --- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4679SnapshotUpdateInPluginTest.java | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4679SnapshotUpdateInPluginTest.java | |
| @@ -62,7 +62,7 @@ public void testit() throws Exception { | |
| verifier.verifyErrorFreeLog(); | |
| assertChecksum(verifier, "jar", "2ea5c3d713bbaba7b87746449b91cd00e876703d"); | |
| - assertChecksum(verifier, "pom", "0b58dbbc61f81b85a70692ffdce88cf1892a8da4"); | |
| + assertChecksum(verifier, "pom", "d6883b610a0e087464ece92ac1e7f2b8e742e71f"); | |
| filterProps.put("@repo@", "repo-2"); | |
| verifier.filterFile("settings-template.xml", "settings.xml", filterProps); | |
| @@ -74,7 +74,7 @@ public void testit() throws Exception { | |
| verifier.verifyErrorFreeLog(); | |
| assertChecksum(verifier, "jar", "f3d46277c2ab45ff9bbd97605c942bed7fc27f97"); | |
| - assertChecksum(verifier, "pom", "127f0dc26035352bb54890315ad7d2ada067756a"); | |
| + assertChecksum(verifier, "pom", "ddfa2de1fd5765bbd72829841abfa7a1fde7ff21"); | |
| } | |
| private void assertChecksum(Verifier verifier, String ext, String checksum) throws Exception { | |
| diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng5102MixinsTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng5102MixinsTest.java | |
| new file mode 100644 | |
| index 0000000000..3e65254837 | |
| --- /dev/null | |
| +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng5102MixinsTest.java | |
| @@ -0,0 +1,147 @@ | |
| +/* | |
| + * 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment