org.eclipse.aether.collection.VersionFilter instances out of input expression string.
+ * "s" - contextual snapshot filter (project version decides are snapshots allowed or not)"nosnapshot" - unconditional snapshot filter (no snapshot versions selected from ranges)"norelease" - unconditional release filter (no release versions selected from ranges)"nopreview" - unconditional preview filter (no preview versions selected from ranges)"noprerelease" - unconditional pre-release filter (no preview and rc/cr versions selected from ranges)"noqualifier" - unconditional any-qualifier filter (no version with any qualifier selected from ranges)"h" (shorthand of h(1)) or "h(num)" - highest N version (based on version ordering)"l" (shorthand of l(1)) or "l(num)" - lowest N version (based on version ordering)"e(V)" - exclusion filter (excludes versions matching V version constraint)"i(V)" - inclusion filter (includes versions matching V version constraint)@G[:A]. Presence of "scope" narrows the
+ * application of filter to given G or G:A.
+ * "h(5);s;e(1)@org.foo:bar" will cause:
+ * org.foo:bar is being processed, version 1 is omitted"h(5);s;e(org.foo:bar:1) will cause: ranges are filtered for "top 5" (instead
- * full range), snapshots are banned if root project is not a snapshot, and if range for org.foo:bar is
- * being processed, version 1 is omitted. Value in this property builds
- * org.eclipse.aether.collection.VersionFilter instance.
+ * Values in this property builds org.eclipse.aether.collection.VersionFilter instance.
*
- * @since 4.0.0
+ * @since 3.10.0
*/
@Config
public static final String MAVEN_VERSION_FILTER = "maven.session.versionFilter";
@@ -409,9 +424,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.
* "true".
*
@@ -430,6 +445,17 @@ public final class Constants {
@Config(defaultValue = "default")
public static final String MAVEN_RESOLVER_TRANSPORT = "maven.resolver.transport";
+ /**
+ * Resolver validation control.
+ * Can be default (full validation), mild (only uninterpolated placeholders) or
+ * off (no validation, as in Maven 3.9.x).
+ * This configuration provides "escape hatch" for those projects, that are forced to use non-conformant solutions.
+ *
+ * @since 3.10.0
+ */
+ @Config(defaultValue = "default")
+ public static final String MAVEN_RESOLVER_VALIDATION = "maven.resolver.validation";
+
/**
* Plugin validation level.
*
@@ -462,9 +488,36 @@ public final class Constants {
@Config(type = "java.lang.Boolean", defaultValue = "true")
public static final String MAVEN_CONSUMER_POM = "maven.consumer.pom";
+ /**
+ * User property for controlling consumer POM flattening behavior.
+ * When set to true, consumer POMs are flattened by removing
+ * dependency management and keeping only direct dependencies with transitive scopes.
+ * When set to false (default), consumer POMs preserve dependency management
+ * like parent POMs, allowing dependency management to be inherited by consumers.
+ *
+ * @since 4.1.0
+ */
+ @Config(type = "java.lang.Boolean", defaultValue = "false")
+ public static final String MAVEN_CONSUMER_POM_FLATTEN = "maven.consumer.pom.flatten";
+
+ /**
+ * User property for controlling removal of unused managed dependencies during consumer POM flattening.
+ * When set to {@code true} (default), managed dependencies that do not appear in the resolved
+ * dependency tree are removed from the consumer POM to keep it lean. This is important when using
+ * BOMs like Spring Boot or Quarkus that contain hundreds of managed dependency entries.
+ * When set to {@code false}, all managed dependencies are preserved in the consumer POM,
+ * which may be needed in rare cases where downstream consumers override transitive dependency
+ * versions and rely on the original managed dependencies for alignment.
+ *
+ * @since 4.1.0
+ */
+ @Config(type = "java.lang.Boolean", defaultValue = "true")
+ public static final String MAVEN_CONSUMER_POM_REMOVE_UNUSED_MANAGED_DEPENDENCIES =
+ "maven.consumer.pom.removeUnusedManagedDependencies";
+
/**
* 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 +547,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 false, 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.
+ * "true".
+ *
+ * @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.
*
@@ -510,6 +576,35 @@ public final class Constants {
@Config(type = "java.lang.Integer", defaultValue = "100")
public static final String MAVEN_BUILDER_MAX_PROBLEMS = "maven.builder.maxProblems";
+ /**
+ * Configuration property for version range resolution used metadata "nature".
+ * It may contain following string values:
+ * + * Purpose: This method provides the base output directory for a given scope, + * which serves as the destination for compiled classes, processed resources, and other generated files. + * The returned path is always absolute. + *
+ *+ * Scope-based Directory Resolution: + *
+ *| Scope Parameter | + *Build Configuration | + *Typical Path | + *Contents | + *
|---|---|---|---|
| {@link ProjectScope#MAIN} | + *{@code build.getOutputDirectory()} | + *{@code target/classes} | + *Compiled application classes and processed main resources | + *
| {@link ProjectScope#TEST} | + *{@code build.getTestOutputDirectory()} | + *{@code target/test-classes} | + *Compiled test classes and processed test resources | + *
| {@code null} or other | + *{@code build.getDirectory()} | + *{@code target} | + *Parent directory for all build outputs | + *
+ * Role in {@link SourceRoot} Path Resolution: + *
+ *+ * This method is the foundation for {@link SourceRoot#targetPath(Project)} path resolution. + * When a {@link SourceRoot} has a relative {@code targetPath}, it is resolved against the + * output directory returned by this method for the source root's scope. This ensures that: + *
+ *+ * Maven 3 Compatibility: + *
+ *+ * This behavior maintains the Maven 3.x semantic where resource {@code targetPath} elements + * are resolved relative to the appropriate output directory ({@code project.build.outputDirectory} + * or {@code project.build.testOutputDirectory}), not the project base directory. + *
+ *+ * In Maven 3, when a resource configuration specifies: + *
+ *{@code
+ *
+ * src/main/resources
+ * META-INF/resources
+ *
+ * }
+ * + * The maven-resources-plugin resolves {@code targetPath} as: + * {@code project.build.outputDirectory + "/" + targetPath}, which results in + * {@code target/classes/META-INF/resources}. This method provides the same base directory + * ({@code target/classes}) for Maven 4 API consumers. + *
+ *+ * Example: + *
+ *{@code
+ * Project project = ...; // project at /home/user/myproject
+ *
+ * // Get main output directory
+ * Path mainOutput = project.getOutputDirectory(ProjectScope.MAIN);
+ * // Result: /home/user/myproject/target/classes
+ *
+ * // Get test output directory
+ * Path testOutput = project.getOutputDirectory(ProjectScope.TEST);
+ * // Result: /home/user/myproject/target/test-classes
+ *
+ * // Get build directory
+ * Path buildDir = project.getOutputDirectory(null);
+ * // Result: /home/user/myproject/target
+ * }
+ *
+ * @param scope the scope of the generated files for which to get the directory, or {@code null} for the build directory
+ * @return the absolute path to the output directory for the given scope
+ *
+ * @see SourceRoot#targetPath(Project)
+ * @see SourceRoot#targetPath()
+ * @see Build#getOutputDirectory()
+ * @see Build#getTestOutputDirectory()
+ * @see Build#getDirectory()
+ */
+ @Nonnull
+ default Path getOutputDirectory(@Nullable ProjectScope scope) {
+ String dir;
+ Build build = getBuild();
+ if (scope == ProjectScope.MAIN) {
+ dir = build.getOutputDirectory();
+ } else if (scope == ProjectScope.TEST) {
+ dir = build.getTestOutputDirectory();
+ } else {
+ dir = build.getDirectory();
+ }
+ return getBasedir().resolve(dir);
+ }
+
/**
* {@return the project direct dependencies (directly specified or inherited)}.
*/
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java
index 4b986da8d356..41300d074e63 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java
@@ -58,6 +58,12 @@ public interface ProtoSession {
@Nonnull
Mapdiff --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 c60f6befda85..c8451e66dd23 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 @@ -24,6 +24,8 @@ import java.util.List; import java.util.Optional; +import org.apache.maven.api.annotations.Nonnull; + /** * A root directory of source files. * The sources may be Java main classes, test classes, resources or anything else identified by the scope. @@ -35,7 +37,7 @@ */ public interface SourceRoot { /** - * {@return the root directory where the sources are stored}. + * {@return the root directory where the sources are stored} * The path is relative to the POM file. * *
- * 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. + *
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.
* * @see java.nio.file.FileSystem#getPathMatcher(String) */ @@ -79,7 +80,7 @@ default List"**/*.java".
@@ -104,7 +105,7 @@ default List+ * Important: This method returns the target path as specified in the configuration, + * which may be relative or absolute. It does not perform any path resolution. + * For the fully resolved absolute path, use {@link #targetPath(Project)} instead. + *
+ *+ * Return Value Semantics: + *
+ *+ * Maven 3 Compatibility: This behavior maintains compatibility with Maven 3.x, + * where resource {@code targetPath} elements were always interpreted as relative to the output directory + * ({@code project.build.outputDirectory} or {@code project.build.testOutputDirectory}), + * not the project base directory. Maven 3 plugins (like maven-resources-plugin) expect to receive + * the relative path and perform the resolution themselves. + *
+ *+ * Effect on Module and Target Version: + * When a target path is explicitly specified, the values of {@link #module()} and {@link #targetVersion()} + * are not used for inferring the output path (they are still used as compiler options however). + * This means that for scripts and resources, the files below the path specified by {@link #directory()} * are copied to the path specified by {@code targetPath()} with the exact same directory structure. + *
+ *+ * Usage Guidance: + *
+ *+ * Purpose: This method performs the complete path resolution logic, converting + * the potentially relative {@link #targetPath()} into an absolute filesystem path. This is the + * method that Maven 4 API consumers should use when they need to know the actual destination + * directory for copying files. + *
+ *+ * Resolution Algorithm: + *
+ *+ * Concrete Examples: + *
+ *+ * Given a project at {@code /home/user/myproject} with {@link ProjectScope#MAIN}: + *
+ *| Configuration ({@code targetPath()}) | + *Output Directory | + *Result ({@code targetPath(project)}) | + *Explanation | + *
|---|---|---|---|
| {@code Optional.empty()} | + *{@code /home/user/myproject/target/classes} | + *{@code /home/user/myproject/target/classes} | + *No explicit path → use output directory | + *
| {@code Optional.of(Path.of("META-INF"))} | + *{@code /home/user/myproject/target/classes} | + *{@code /home/user/myproject/target/classes/META-INF} | + *Relative path → resolve against output directory | + *
| {@code Optional.of(Path.of("WEB-INF/classes"))} | + *{@code /home/user/myproject/target/classes} | + *{@code /home/user/myproject/target/classes/WEB-INF/classes} | + *Relative path with subdirectories | + *
| {@code Optional.of(Path.of("/tmp/custom"))} | + *{@code /home/user/myproject/target/classes} | + *{@code /tmp/custom} | + *Absolute path → use as-is (no resolution) | + *
+ * Relationship to {@link #targetPath()}: + *
+ *+ * This method is the resolution counterpart to {@link #targetPath()}, which is the + * storage method. While {@code targetPath()} returns the path as configured (potentially relative), + * this method returns the absolute path where files will actually be written. The separation allows: + *
+ *+ * Implementation Note: The default implementation is equivalent to: + *
+ *{@code
+ * Optional configured = targetPath();
+ * if (configured.isPresent() && configured.get().isAbsolute()) {
+ * return configured.get();
+ * }
+ * Path outputDir = project.getOutputDirectory(scope());
+ * return configured.map(outputDir::resolve).orElse(outputDir);
+ * }
+ *
+ * @param project the project to use for obtaining the output directory
+ * @return the absolute path where files from {@link #directory()} should be copied
+ *
+ * @see #targetPath()
+ * @see Project#getOutputDirectory(ProjectScope)
+ */
+ @Nonnull
+ default Path targetPath(@Nonnull Project project) {
+ OptionalProject aggregation allows building several projects together. This is only * for projects that are built, hence available on the file system. One project, - * called the aggregator project lists one or more modules + * called the aggregator project lists one or more sub-projects * which are relative pointers on the file system to other projects. This is done using - * the {@code /project/modules/module} elements of the POM in the aggregator project. + * the {@code /project/subprojects/subproject} elements of the POM in the aggregator project. * Note that the aggregator project is required to have a {@code pom} packaging.
* *Project inheritance defines a parent-child relationship between projects.
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java
index f2968295e456..50627efd86e3 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java
@@ -49,7 +49,7 @@ public interface Log {
void debug(CharSequence content);
/**
- * Sends a message (and accompanying exception) to the user in the debug error level.
+ * Sends a message (and accompanying exception) to the user at the debug error level.
* The error's stacktrace will be output when this error level is enabled.
*
* @param content the message to log
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java
index 697b9d480206..3e86bfa2c02c 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/After.java
@@ -21,6 +21,7 @@
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -37,6 +38,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
+@Repeatable(Afters.class)
public @interface After {
/**
@@ -57,7 +59,7 @@ enum Type {
/**
* The type of this pointer.
*/
- Type type();
+ Type type() default Type.PROJECT;
/**
* The scope for dependencies, only if {@code type() == Type.Dependencies}.
diff --git a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Afters.java
similarity index 56%
rename from api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java
rename to api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Afters.java
index 8b2958a35cc6..c00ee72de848 100644
--- a/api/maven-api-model/src/main/java/org/apache/maven/api/model/InputLocationTracker.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Afters.java
@@ -16,17 +16,31 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.maven.api.model;
+package org.apache.maven.api.plugin.annotations;
-public interface InputLocationTracker {
- InputLocation getLocation(Object field);
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.maven.api.annotations.Experimental;
+
+/**
+ * Container annotation for repeatable {@link After} annotations.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface Afters {
/**
- * Gets the parent InputLocation where this InputLocation may have been imported from.
- * Can return {@code null}.
- *
- * @return InputLocation
- * @since 4.0.0
+ * The contained {@link After} annotations.
*/
- InputLocation getImportedFrom();
+ After[] value();
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java
index 14d2c7a2ef1c..28a7936fed4c 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/package-info.java
@@ -18,6 +18,10 @@
*/
/**
- * Maven Plugin Annotations.
+ * Provides annotations for Maven plugin development, including mojo configuration,
+ * parameter definitions, and lifecycle bindings. These annotations are used to
+ * generate plugin descriptors and configure plugin behavior.
+ *
+ * @since 4.0.0
*/
package org.apache.maven.api.plugin.annotations;
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 6604c0392a11..c17884fd9792 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 8405fc301766..c68392881018 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 22f398c06575..22d5af16f387 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/ArtifactResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
index fb012fab30df..7e832a95e41f 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java
@@ -40,14 +40,11 @@
*/
@Experimental
@Immutable
-public interface ArtifactResolverRequest extends Request If this value is not provided, then the default value in the context of Java projects
+ * is the Java version on which Maven is running, as given by {@link Runtime#version()}.
+ * 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/PathMatcherFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathMatcherFactory.java
new file mode 100644
index 000000000000..9f83e2e0f8bf
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathMatcherFactory.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.api.services;
+
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.util.Collection;
+import java.util.Objects;
+
+import org.apache.maven.api.Service;
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Service for creating {@link PathMatcher} objects that can be used to filter files
+ * based on include/exclude patterns. This service provides a clean API for plugins
+ * to create path matchers without directly depending on implementation classes.
+ *
+ * The path matchers created by this service support Maven's traditional include/exclude
+ * pattern syntax, which is compatible with the behavior of Maven 3 plugins like
+ * maven-compiler-plugin and maven-clean-plugin.
+ *
+ * Pattern syntax supports:
+ *
+ * The pathnames used for matching will be relative to the specified base directory
+ * and use {@code '/'} as separator, regardless of the hosting operating system.
+ *
+ * @param baseDirectory the base directory for relativizing paths during matching
+ * @param includes the patterns of files to include, or null/empty for including all files
+ * @param excludes the patterns of files to exclude, or null/empty for no exclusion
+ * @param useDefaultExcludes whether to augment excludes with default SCM exclusion patterns
+ * @return a PathMatcher that can be used to test if paths should be included
+ * @throws NullPointerException if baseDirectory is null
+ */
+ @Nonnull
+ PathMatcher createPathMatcher(
+ @Nonnull Path baseDirectory,
+ Collection
+ * This is equivalent to calling {@link #createPathMatcher(Path, Collection, Collection, boolean)}
+ * with {@code useDefaultExcludes = false}.
+ *
+ * @param baseDirectory the base directory for relativizing paths during matching
+ * @param includes the patterns of files to include, or null/empty for including all files
+ * @param excludes the patterns of files to exclude, or null/empty for no exclusion
+ * @return a PathMatcher that can be used to test if paths should be included
+ * @throws NullPointerException if baseDirectory is null
+ */
+ @Nonnull
+ default PathMatcher createPathMatcher(
+ @Nonnull Path baseDirectory, Collection
+ * This is equivalent to calling {@link #createPathMatcher(Path, Collection, Collection, boolean)}
+ * with {@code includes = null}.
+ *
+ * @param baseDirectory the base directory for relativizing paths during matching
+ * @param excludes the patterns of files to exclude, or null/empty for no exclusion
+ * @param useDefaultExcludes whether to augment excludes with default SCM exclusion patterns
+ * @return a PathMatcher that can be used to test if paths should be included
+ * @throws NullPointerException if baseDirectory is null
+ */
+ @Nonnull
+ default PathMatcher createExcludeOnlyMatcher(
+ @Nonnull Path baseDirectory, Collection
+ * This is equivalent to calling {@link #createPathMatcher(Path, Collection, Collection, boolean)}
+ * with {@code excludes = null} and {@code useDefaultExcludes = false}.
+ *
+ * @param baseDirectory the base directory for relativizing paths during matching
+ * @param includes the patterns of files to include, or null/empty for including all files
+ * @return a PathMatcher that can be used to test if paths should be included
+ * @throws NullPointerException if baseDirectory is null
+ */
+ @Nonnull
+ default PathMatcher createIncludeOnlyMatcher(@Nonnull Path baseDirectory, Collection problems(BuilderProblem.Severity severity) {
static ProblemCollector 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 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 ProblemCollector 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 ProblemCollector create(@Nullable ProtoSess
*/
@Nonnull
static ProblemCollector create(int maxCountLimit) {
- return new Impl<>(maxCountLimit);
+ return create(maxCountLimit, p -> true);
}
/**
@@ -212,13 +227,14 @@ class Impl implements ProblemCollector {
private final AtomicInteger totalCount;
private final ConcurrentMap Implementations of this interface can specify a list of remote repositories
+ * to be used during the operation. If no repositories are specified (null),
+ * the session's default remote repositories will be used. The repositories
+ * are validated to ensure they don't contain duplicates or null entries.
+ *
+ * Remote repositories are used for:
+ * Repository validation ensures data integrity by:
+ * If this method returns {@code null}, the session's default remote repositories
+ * will be used. If a non-null list is returned, it will be used instead of the
+ * session's repositories, allowing for request-specific repository configuration.
+ *
+ * The returned list should not contain duplicate repositories (based on their
+ * equality) or null entries, as these will cause validation failures when the
+ * request is processed.
+ *
+ * @return the list of remote repositories to use, or {@code null} to use session defaults
+ * @see Session#getRemoteRepositories()
+ */
+ @Nullable
+ List This method performs the following validations:
+ * Duplicate detection is based on the {@code RemoteRepository#equals(Object)}
+ * method, which typically compares repository IDs and URLs.
+ *
+ * @param repositories the list of repositories to validate, may be {@code null}
+ * @return the same list if validation passes, or {@code null} if input was {@code null}
+ * @throws IllegalArgumentException if the list contains duplicate repositories
+ * @throws IllegalArgumentException if the list contains null repository entries
+ */
+ default List
- * 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 MapApplication to Java
+ * In the context of a Java project, this is the value given to the {@code --release} compiler option.
+ * This value can determine whether a dependency will be placed on the class-path or on the module-path.
+ * For example, if the {@code module-info.class} entry of a JAR file exists only in the
+ * {@code META-INF/versions/17/} sub-directory, then the default location of that dependency will be
+ * the module-path only if the {@code --release} option is equal or greater than 17.
+ *
+ *
+ *
+ *
+ * @since 4.0.0
+ * @see PathMatcher
+ */
+@Experimental
+public interface PathMatcherFactory extends Service {
+
+ /**
+ * Creates a path matcher for filtering files based on include and exclude patterns.
+ *
+ *
+ *
+ *
+ *
+ *
+ * @since 4.0.0
+ * @see RemoteRepository
+ * @see Session#getRemoteRepositories()
+ */
+@Experimental
+@Immutable
+public interface RepositoryAwareRequest extends Request
+ *
+ *
+ *
If this property is specified but is a relative path, + then the path is resolved against the above-cited default value.
When a target path is explicitly specified, the values of the {@code module} and {@code targetVersion}
elements are not used for inferring the path (they are still used as compiler options however).
@@ -2363,12 +2403,12 @@
${token} with their effective values. Effective values are basically
* calculated from the elements of the model itself and the execution properties from the building request.
*
+ * @since 4.0.0
*/
public interface ModelInterpolator {
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelNormalizer.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelNormalizer.java
similarity index 99%
rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelNormalizer.java
rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelNormalizer.java
index 40891f041c02..50225fd417a5 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelNormalizer.java
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelNormalizer.java
@@ -26,6 +26,7 @@
* Handles normalization of a model. In this context, normalization is the process of producing a canonical
* representation for models that physically look different but are semantically equivalent.
*
+ * @since 4.0.0
*/
public interface ModelNormalizer {
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelPathTranslator.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelPathTranslator.java
similarity index 99%
rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelPathTranslator.java
rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelPathTranslator.java
index 07a36b95498c..c2ec4ce522ac 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelPathTranslator.java
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelPathTranslator.java
@@ -26,6 +26,7 @@
/**
* Resolves relative paths of a model against a specific base directory.
*
+ * @since 4.0.0
*/
public interface ModelPathTranslator {
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelProcessor.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelProcessor.java
similarity index 96%
rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelProcessor.java
rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelProcessor.java
index eb1c46e27adf..091e8f45fd67 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelProcessor.java
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelProcessor.java
@@ -28,7 +28,9 @@
import org.apache.maven.api.services.xml.XmlReaderRequest;
/**
- * ModelProcessor
+ * Locates and reads POM files from the filesystem.
+ *
+ * @since 4.0.0
*/
public interface ModelProcessor {
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelResolver.java
similarity index 90%
rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java
rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelResolver.java
index 0a7cbb621c7c..58cd0aef0f44 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ModelResolver.java
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ModelResolver.java
@@ -36,6 +36,8 @@
/**
* Resolves a POM from its coordinates.
+ *
+ * @since 4.0.0
*/
public interface ModelResolver extends Service {
@@ -85,8 +87,16 @@ record ModelResolverRequest(
@Nonnull String groupId,
@Nonnull String artifactId,
@Nonnull String version,
- @Nullable String classifier)
+ @Nullable String classifier,
+ @Nullable String extension)
implements Request* The {@link Model} is available from the activation context, but only static parts of it * are allowed to be used, i.e. those that do not change between file model and effective model. * + * @since 4.0.0 */ public interface ProfileActivationContext { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivator.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileActivator.java similarity index 99% rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivator.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileActivator.java index 99153fa5578b..5184a20aad4a 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileActivator.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileActivator.java @@ -24,6 +24,7 @@ /** * Determines whether a profile should be activated. * + * @since 4.0.0 */ public interface ProfileActivator { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileInjector.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileInjector.java similarity index 99% rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileInjector.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileInjector.java index 87eb50abe374..7361318f9801 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileInjector.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileInjector.java @@ -28,6 +28,7 @@ /** * Handles profile injection into the model. * + * @since 4.0.0 */ public interface ProfileInjector { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java similarity index 99% rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java index b85cdd43eb5c..6bf6e2251d28 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/ProfileSelector.java @@ -27,6 +27,7 @@ /** * Calculates the active profiles among a given collection of profiles. * + * @since 4.0.0 */ public interface ProfileSelector { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/RootDetector.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/RootDetector.java similarity index 92% rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/RootDetector.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/RootDetector.java index 7c1f1b301511..2c341d27097e 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/RootDetector.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/RootDetector.java @@ -23,7 +23,9 @@ import org.apache.maven.api.Service; /** - * Interface used to detect is a given directory "root directory". + * Detects whether a given directory is a root directory. + * + * @since 4.0.0 */ public interface RootDetector extends Service { boolean isRootDirectory(Path dir); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/RootLocator.java similarity index 96% rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/RootLocator.java index 5cb8bd841f95..739c279db808 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/RootLocator.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/RootLocator.java @@ -25,15 +25,17 @@ import org.apache.maven.api.annotations.Nullable; /** - * Interface used to locate the root directory for a given project. - * + * Locates the root directory for a given project. + *
* The root locator is usually looked up from the DI container. * One notable exception is the computation of the early {@code session.rootDirectory} * property which happens very early. The implementation used in this case * will be discovered using the JDK service mechanism. - * + *
* The default implementation will look for a {@code .mvn} child directory * or a {@code pom.xml} containing the {@code root="true"} attribute. + * + * @since 4.0.0 */ public interface RootLocator extends Service { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java similarity index 67% rename from impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java rename to api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java index 29dab47f0589..52eb87e0f141 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java +++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java @@ -18,6 +18,18 @@ */ package org.apache.maven.api.services.model; +/** + * Simplifies URLs by removing parent directory references ("/../") and collapsing path segments. + * This performs purely string-based normalization without full URL parsing or validation. + * + *
The normalization process iteratively removes "/../" segments by eliminating the preceding path segment, + * effectively resolving relative path traversals. + * + *
This does not guarantee that the resulting URL is valid or reachable; it simply
+ * produces a more canonical representation of the input string.
+ *
+ * @since 4.0.0
+ */
public interface UrlNormalizer {
/**
diff --git a/api/maven-api-toolchain/pom.xml b/api/maven-api-toolchain/pom.xml
index 568bd10f7261..64d53566d3ec 100644
--- a/api/maven-api-toolchain/pom.xml
+++ b/api/maven-api-toolchain/pom.xml
@@ -23,7 +23,7 @@ under the License.
+ * This is used by the write side to properly resolve prefixed attributes.
+ * For example, if an attribute {@code mvn:combine.children} exists on a child element
+ * but {@code xmlns:mvn} was declared on the root element, this map will contain
+ * the {@code mvn → http://maven.apache.org/POM/4.0.0} binding.
+ *
+ * @return map of namespace prefix to URI, never {@code null}
+ * @since 4.1.0
+ */
+ @Nonnull
+ default Map
+ * This map contains all namespace prefix to URI bindings in scope,
+ * including inherited ones from ancestor elements.
+ *
+ * @param namespaces the map of namespace prefix to URI
+ * @return this builder instance
+ * @since 4.1.0
+ */
+ public Builder namespaces(Map
@@ -450,7 +540,7 @@ public Builder inputLocation(Object inputLocation) {
* @throws NullPointerException if name has not been set
*/
public XmlNode build() {
- return new Impl(prefix, namespaceUri, name, value, attributes, children, inputLocation);
+ return new Impl(prefix, namespaceUri, name, value, attributes, namespaces, children, inputLocation);
}
private record Impl(
@@ -459,6 +549,7 @@ private record Impl(
@Nonnull String name,
String value,
@Nonnull Map This test addresses GitHub issue #11618 where concurrent access to a shared
+ * {@code HashSet} in {@code DefaultModelValidator} could cause {@code ClassCastException}.
+ * The underlying issue occurs when multiple threads access a non-thread-safe {@code HashSet}
+ * (backed by {@code HashMap}) during internal restructuring operations.
+ *
+ * The fix replaces {@code HashSet} with {@code ConcurrentHashMap.newKeySet()} to provide
+ * thread-safe concurrent access without external synchronization.
+ *
+ * @see GitHub #11618
+ */
+ @Test
+ void testConcurrentValidation() throws Exception {
+ int threadCount = 10;
+ int iterationsPerThread = 100;
+ CountDownLatch startLatch = new CountDownLatch(1);
+ CountDownLatch doneLatch = new CountDownLatch(threadCount);
+ AtomicReference
+ *
+ */
+ private boolean addDefaultEntities = true;
+
+ /**
+ * Field contentTransformer.
+ */
+ public final ContentTransformer contentTransformer;
+
+ // ----------------/
+ // - Constructors -/
+ // ----------------/
+
+ public CoreExtensionsXpp3Reader() {
+ this(new ContentTransformer() {
+ public String transform(String source, String fieldName) {
+ return source;
+ }
+ });
+ } // -- org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader()
+
+ public CoreExtensionsXpp3Reader(ContentTransformer contentTransformer) {
+ this.contentTransformer = contentTransformer;
+ } // -- org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader(ContentTransformer)
+
+ // -----------/
+ // - Methods -/
+ // -----------/
+
+ /**
+ * Method checkFieldWithDuplicate.
+ *
+ * @param parser a parser object.
+ * @param parsed a parsed object.
+ * @param alias a alias object.
+ * @param tagName a tagName object.
+ * @throws XmlPullParserException XmlPullParserException if
+ * any.
+ * @return boolean
+ */
+ private boolean checkFieldWithDuplicate(
+ XmlPullParser parser, String tagName, String alias, java.util.Set> goals() {
@@ -143,10 +125,8 @@ public void displayHelp(ParserRequest request, Consumer
+ *