Skip to content

Instantly share code, notes, and snippets.

@gnodet
Last active October 10, 2025 09:28
Show Gist options
  • Select an option

  • Save gnodet/f8b97e2929ff3ce4e9645dbb30b88492 to your computer and use it in GitHub Desktop.

Select an option

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.
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