}.
+ *
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
- * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String)
+ * @param coordString the string having "standard" coordinate.
+ * @return coordinate used to point to the artifact
+ *
+ * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String)
*/
- ArtifactCoordinate createArtifactCoordinate(String groupId, String artifactId, String version, String extension);
+ @Nonnull
+ ArtifactCoordinate createArtifactCoordinate(@Nonnull String coordString);
/**
- * Creates a coordinate out of string that is formatted like:
- * {@code :[:[:]]:}
- *
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
- * @param coordString the string having "standard" coordinate.
- * @return an {@code ArtifactCoordinate}, never {@code null}
- * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String)
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @return coordinate used to point to the artifact
+ *
+ * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String)
*/
- ArtifactCoordinate createArtifactCoordinate(String coordString);
+ @Nonnull
+ ArtifactCoordinate createArtifactCoordinate(String groupId, String artifactId, String version, String extension);
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param classifier the artifact classifier, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @param type the artifact type, or {@code null} is unspecified
+ * @return coordinate used to point to the artifact
+ *
* @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String, String, String)
*/
+ @Nonnull
ArtifactCoordinate createArtifactCoordinate(
String groupId, String artifactId, String version, String classifier, String extension, String type);
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param artifact artifact from which to get coordinates
+ * @return coordinate used to point to the artifact
+ *
* @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String, String, String)
*/
- ArtifactCoordinate createArtifactCoordinate(Artifact artifact);
+ @Nonnull
+ ArtifactCoordinate createArtifactCoordinate(@Nonnull Artifact artifact);
/**
* Shortcut for {@code getService(DependencyFactory.class).create(...)}.
*
+ * @param coordinate artifact coordinate to get as a dependency coordinate
+ * @return dependency coordinate for the given artifact
+ *
* @see DependencyCoordinateFactory#create(Session, ArtifactCoordinate)
*/
@Nonnull
@@ -260,6 +296,9 @@ ArtifactCoordinate createArtifactCoordinate(
/**
* Shortcut for {@code getService(DependencyFactory.class).create(...)}.
*
+ * @param dependency dependency for which to get the coordinate
+ * @return coordinate for the given dependency
+ *
* @see DependencyCoordinateFactory#create(Session, Dependency)
*/
@Nonnull
@@ -268,85 +307,131 @@ ArtifactCoordinate createArtifactCoordinate(
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @return artifact with the given coordinates
+ *
* @see org.apache.maven.api.services.ArtifactFactory#create(Session, String, String, String, String)
*/
+ @Nonnull
Artifact createArtifact(String groupId, String artifactId, String version, String extension);
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param classifier the artifact classifier, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @param type the artifact type, or {@code null} is unspecified
+ * @return artifact with the given coordinates
+ *
* @see org.apache.maven.api.services.ArtifactFactory#create(Session, String, String, String, String, String, String)
*/
+ @Nonnull
Artifact createArtifact(
String groupId, String artifactId, String version, String classifier, String extension, String type);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param coordinate coordinates of the artifact to resolve
+ * @return requested artifact together with the path to its file
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map.Entry resolveArtifact(ArtifactCoordinate coordinate);
+ @Nonnull
+ Map.Entry resolveArtifact(@Nonnull ArtifactCoordinate coordinate);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param coordinates coordinates of all artifacts to resolve
+ * @return requested artifacts together with the paths to their files
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map resolveArtifacts(ArtifactCoordinate... coordinates);
+ @Nonnull
+ Map resolveArtifacts(@Nonnull ArtifactCoordinate... coordinates);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param coordinates coordinates of all artifacts to resolve
+ * @return requested artifacts together with the paths to their files
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map resolveArtifacts(Collection extends ArtifactCoordinate> coordinates);
+ @Nonnull
+ Map resolveArtifacts(@Nonnull Collection extends ArtifactCoordinate> coordinates);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param artifact the artifact to resolve
+ * @return requested artifact together with the path to its file
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map.Entry resolveArtifact(Artifact artifact);
+ @Nonnull
+ Map.Entry resolveArtifact(@Nonnull Artifact artifact);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param artifacts all artifacts to resolve
+ * @return requested artifacts together with the paths to their files
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map resolveArtifacts(Artifact... artifacts);
+ @Nonnull
+ Map resolveArtifacts(@Nonnull Artifact... artifacts);
/**
* Shortcut for {@code getService(ArtifactInstaller.class).install(...)}.
*
- * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
+ * @param artifacts the artifacts to install
* @throws org.apache.maven.api.services.ArtifactInstallerException if the artifacts installation failed
+ *
+ * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
*/
- void installArtifacts(Artifact... artifacts);
+ void installArtifacts(@Nonnull Artifact... artifacts);
/**
* Shortcut for {@code getService(ArtifactInstaller.class).install(...)}.
*
- * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
+ * @param artifacts the artifacts to install
* @throws org.apache.maven.api.services.ArtifactInstallerException if the artifacts installation failed
+ *
+ * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
*/
- void installArtifacts(Collection artifacts);
+ void installArtifacts(@Nonnull Collection artifacts);
/**
* Shortcut for {@code getService(ArtifactDeployer.class).deploy(...)}.
*
- * @see org.apache.maven.api.services.ArtifactDeployer#deploy(Session, RemoteRepository, Collection)
+ * @param repository the repository where to deploy artifacts
+ * @param artifacts the artifacts to deploy
* @throws org.apache.maven.api.services.ArtifactDeployerException if the artifacts deployment failed
+ *
+ * @see org.apache.maven.api.services.ArtifactDeployer#deploy(Session, RemoteRepository, Collection)
*/
- void deployArtifact(RemoteRepository repository, Artifact... artifacts);
+ void deployArtifact(@Nonnull RemoteRepository repository, @Nonnull Artifact... artifacts);
/**
* Shortcut for {@code getService(ArtifactManager.class).setPath(...)}.
*
+ * @param artifact the artifact for which to associate a path
+ * @param path path to associate to the given artifact
+ *
* @see org.apache.maven.api.services.ArtifactManager#setPath(Artifact, Path)
*/
void setArtifactPath(@Nonnull Artifact artifact, @Nonnull Path path);
@@ -354,6 +439,9 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(ArtifactManager.class).getPath(...)}.
*
+ * @param artifact the artifact for which to get a path
+ * @return path associated to the given artifact
+ *
* @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact)
*/
@Nonnull
@@ -365,6 +453,9 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(LocalArtifactManager.class).getPathForLocalArtitact(...)}.
*
+ * @param artifact the artifact for which to get a local path
+ * @return local path associated to the given artifact, or {@code null} if none
+ *
* @see org.apache.maven.api.services.LocalRepositoryManager#getPathForLocalArtifact(Session, LocalRepository, Artifact)
*/
Path getPathForLocalArtifact(@Nonnull Artifact artifact);
@@ -376,6 +467,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(LocalArtifactManager.class).getPathForRemoteArtifact(...)}.
*
+ * @param remote the repository from where artifacts are downloaded
+ * @param artifact the artifact for which to get a path
+ * @return path associated to the given artifact
+ *
* @see org.apache.maven.api.services.LocalRepositoryManager#getPathForRemoteArtifact(Session, LocalRepository, RemoteRepository, Artifact)
*/
@Nonnull
@@ -389,6 +484,9 @@ Artifact createArtifact(
* In case there is {@link Artifact} in scope, the recommended way to perform this check is
* use of {@link Artifact#isSnapshot()} instead.
*
+ * @param version artifact version
+ * @return whether the given version is a snapshot
+ *
* @see org.apache.maven.api.services.VersionParser#isSnapshot(String)
*/
boolean isVersionSnapshot(@Nonnull String version);
@@ -396,6 +494,9 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(DependencyCollector.class).collect(...)}
*
+ * @param artifact artifact for which to get the dependencies, including transitive ones
+ * @return root node of the dependency graph for the given artifact
+ *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, Artifact)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed
*/
@@ -405,6 +506,9 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(DependencyCollector.class).collect(...)}
*
+ * @param project project for which to get the dependencies, including transitive ones
+ * @return root node of the dependency graph for the given project
+ *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, Project)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed
*/
@@ -418,6 +522,9 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(DependencyCollector.class).resolve(...)}
*
+ * @param dependency dependency for which to get transitive dependencies
+ * @return root node of the dependency graph for the given artifact
+ *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, DependencyCoordinate)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed
*/
@@ -427,29 +534,91 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(DependencyResolver.class).flatten(...)}.
*
- * @see org.apache.maven.api.services.DependencyResolver#flatten(Session, Node, PathScope)
+ * @param node node for which to get a flattened list
+ * @param scope build path scope (main compile, test compile, etc.) of desired nodes
+ * @return flattened list of node with the given build path scope
* @throws org.apache.maven.api.services.DependencyResolverException if the dependency flattening failed
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#flatten(Session, Node, PathScope)
*/
@Nonnull
List flattenDependencies(@Nonnull Node node, @Nonnull PathScope scope);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}.
+ *
+ * @param dependencyCoordinate coordinate of the dependency for which to get the paths
+ * @return paths to the transitive dependencies of the given dependency
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, DependencyCoordinate)
+ */
@Nonnull
List resolveDependencies(@Nonnull DependencyCoordinate dependencyCoordinate);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}.
+ *
+ * @param dependencyCoordinates coordinates of all dependency for which to get the paths
+ * @return paths to the transitive dependencies of the given dependencies
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, List)
+ */
@Nonnull
List resolveDependencies(@Nonnull List dependencyCoordinates);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}.
+ *
+ * @param project the project for which to get dependencies
+ * @param scope build path scope (main compile, test compile, etc.) of desired paths
+ * @return paths to the transitive dependencies of the given project
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope)
+ */
@Nonnull
List resolveDependencies(@Nonnull Project project, @Nonnull PathScope scope);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getDispatchedPaths()}.
+ *
+ * @param dependencyCoordinate coordinate of the dependency for which to get the paths
+ * @param scope build path scope (main compile, test compile, etc.) of desired paths
+ * @param desiredTypes the type of paths to include in the result
+ * @return paths to the transitive dependencies of the given project
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope)
+ */
+ @Nonnull
+ Map> resolveDependencies(
+ @Nonnull DependencyCoordinate dependencyCoordinate,
+ @Nonnull PathScope scope,
+ @Nonnull Collection desiredTypes);
+
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getDispatchedPaths()}.
+ *
+ * @param project the project for which to get dependencies
+ * @param scope build path scope (main compile, test compile, etc.) of desired paths
+ * @param desiredTypes the type of paths to include in the result
+ * @return paths to the transitive dependencies of the given project
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope)
+ */
+ @Nonnull
+ Map> resolveDependencies(
+ @Nonnull Project project, @Nonnull PathScope scope, @Nonnull Collection desiredTypes);
+
/**
* Resolves an artifact's meta version (if any) to a concrete version. For example, resolves "1.0-SNAPSHOT"
* to "1.0-20090208.132618-23" or "RELEASE"/"LATEST" to "2.0".
*
* Shortcut for {@code getService(VersionResolver.class).resolve(...)}
*
- * @see org.apache.maven.api.services.VersionResolver#resolve(Session, ArtifactCoordinate) (String)
+ * @param artifact the artifact for which to resolve the version
+ * @return resolved version of the given artifact
* @throws org.apache.maven.api.services.VersionResolverException if the resolution failed
+ *
+ * @see org.apache.maven.api.services.VersionResolver#resolve(Session, ArtifactCoordinate) (String)
*/
@Nonnull
Version resolveVersion(@Nonnull ArtifactCoordinate artifact);
@@ -462,9 +631,10 @@ Artifact createArtifact(
* In this case though, the result contains simply the (parsed) input version, regardless of the
* repositories and their contents.
*
+ * @param artifact the artifact for which to resolve the versions
* @return a list of resolved {@code Version}s.
- * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinate) (String)
* @throws org.apache.maven.api.services.VersionRangeResolverException if the resolution failed
+ * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinate) (String)
*/
@Nonnull
List resolveVersionRange(@Nonnull ArtifactCoordinate artifact);
@@ -474,8 +644,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionParser.class).parseVersion(...)}.
*
- * @see org.apache.maven.api.services.VersionParser#parseVersion(String)
+ * @param version the version string to parse
+ * @return the version parsed from the given string
* @throws org.apache.maven.api.services.VersionParserException if the parsing failed
+ * @see org.apache.maven.api.services.VersionParser#parseVersion(String)
*/
@Nonnull
Version parseVersion(@Nonnull String version);
@@ -485,8 +657,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionParser.class).parseVersionRange(...)}.
*
- * @see org.apache.maven.api.services.VersionParser#parseVersionRange(String)
+ * @param versionRange the version string to parse
+ * @return the version range parsed from the given string
* @throws org.apache.maven.api.services.VersionParserException if the parsing failed
+ * @see org.apache.maven.api.services.VersionParser#parseVersionRange(String)
*/
@Nonnull
VersionRange parseVersionRange(@Nonnull String versionRange);
@@ -496,8 +670,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionParser.class).parseVersionConstraint(...)}.
*
- * @see org.apache.maven.api.services.VersionParser#parseVersionConstraint(String)
+ * @param versionConstraint the version string to parse
+ * @return the version constraint parsed from the given string
* @throws org.apache.maven.api.services.VersionParserException if the parsing failed
+ * @see org.apache.maven.api.services.VersionParser#parseVersionConstraint(String)
*/
@Nonnull
VersionConstraint parseVersionConstraint(@Nonnull String versionConstraint);
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java
index 45a46fd1a1ca..5f702b5ed70b 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.api;
+import java.util.Set;
+
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Immutable;
import org.apache.maven.api.annotations.Nonnull;
@@ -30,7 +32,7 @@
*
* It provides information about the file type (or extension) of the associated artifact,
* its default classifier, and how the artifact will be used in the build when creating
- * various build paths.
+ * class-paths or module-paths.
*
* For example, the type {@code java-source} has a {@code jar} extension and a
* {@code sources} classifier. The artifact and its dependencies should be added
@@ -41,6 +43,60 @@
@Experimental
@Immutable
public interface Type extends ExtensibleEnum {
+ /**
+ * Artifact type name for a POM file.
+ */
+ String POM = "pom";
+
+ /**
+ * Artifact type name for a BOM file.
+ */
+ String BOM = "bom";
+
+ /**
+ * Artifact type name for a JAR file that can be placed either on the class-path or on the module-path.
+ * The path (classes or modules) is chosen by the plugin, possibly using heuristic rules.
+ * This is the behavior of Maven 3.
+ */
+ String JAR = "jar";
+
+ /**
+ * Artifact type name for a JAR file to unconditionally place on the class-path.
+ * If the JAR is modular, its module information are ignored.
+ * This type is new in Maven 4.
+ */
+ String CLASSPATH_JAR = "classpath-jar";
+
+ /**
+ * Artifact type name for a JAR file to unconditionally place on the module-path.
+ * If the JAR is not modular, then it is loaded by Java as an unnamed module.
+ * This type is new in Maven 4.
+ */
+ String MODULAR_JAR = "modular-jar";
+
+ /**
+ * Artifact type name for source code packaged in a JAR file.
+ */
+ String JAVA_SOURCE = "java-source";
+
+ /**
+ * Artifact type name for javadoc packaged in a JAR file.
+ */
+ String JAVADOC = "javadoc";
+
+ /**
+ * Artifact type name for a Maven plugin.
+ */
+ String MAVEN_PLUGIN = "maven-plugin";
+
+ /**
+ * Artifact type name for a JAR file containing test classes. If the main artifact is placed on the class-path
+ * ({@value #JAR} or {@value #CLASSPATH_JAR} types), then the test artifact will also be placed on the class-path.
+ * Otherwise, if the main artifact is placed on the module-path ({@value #JAR} or {@value #MODULAR_JAR} types),
+ * then the test artifact will be added using {@code --patch-module} option.
+ */
+ String TEST_JAR = "test-jar";
+
/**
* Returns the dependency type id.
* The id uniquely identifies this dependency type.
@@ -76,13 +132,6 @@ public interface Type extends ExtensibleEnum {
@Nullable
String getClassifier();
- /**
- * Specifies if the artifact should be added to the build path.
- *
- * @return if the artifact should be added to the build path
- */
- boolean isBuildPathConstituent();
-
/**
* Specifies if the artifact already embeds its own dependencies.
* This is the case for JEE packages or similar artifacts such as
@@ -91,4 +140,19 @@ public interface Type extends ExtensibleEnum {
* @return if the artifact's dependencies are included in the artifact
*/
boolean isIncludesDependencies();
+
+ /**
+ * Types of path (class-path, module-path, …) where the dependency can be placed.
+ * For most deterministic builds, the array length should be 1. In such case,
+ * the dependency will be unconditionally placed on the specified type of path
+ * and no heuristic rule will be involved.
+ *
+ *
It is nevertheless common to specify two or more types of path. For example,
+ * a Java library may be compatible with either the class-path or the module-path,
+ * and the user may have provided no instruction about which type to use. In such
+ * case, the plugin may apply rules for choosing a path. See for example
+ * {@link JavaPathType#CLASSES} and {@link JavaPathType#MODULES}.
+ */
+ @Nonnull
+ Set getPathTypes();
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
index 6d8f00935379..2f410a8a303f 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
@@ -20,10 +20,13 @@
import java.util.Collection;
import java.util.List;
+import java.util.function.Predicate;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.DependencyCoordinate;
+import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.PathScope;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Project;
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Experimental;
@@ -37,6 +40,16 @@ public interface DependencyResolverRequest extends DependencyCollectorRequest {
@Nonnull
PathScope getPathScope();
+ /**
+ * Returns a filter for the types of path (class-path, module-path, …) accepted by the tool.
+ * For example, if a Java tools accepts only class-path elements, then the filter should return
+ * {@code true} for {@link JavaPathType#CLASSES} and {@code false} for {@link JavaPathType#MODULES}.
+ * If no filter is explicitly set, then the default is a filter accepting everything.
+ *
+ * @return a filter for the types of path (class-path, module-path, …) accepted by the tool
+ */
+ Predicate getPathTypeFilter();
+
@Nonnull
static DependencyResolverRequestBuilder builder() {
return new DependencyResolverRequestBuilder();
@@ -88,6 +101,8 @@ static DependencyResolverRequest build(Session session, List pathTypeFilter;
+
@Nonnull
@Override
public DependencyResolverRequestBuilder session(@Nonnull Session session) {
@@ -158,16 +173,53 @@ public DependencyResolverRequestBuilder pathScope(@Nonnull PathScope pathScope)
return this;
}
+ /**
+ * Filters the types of paths to include in the result.
+ * The result will contain only the paths of types for which the predicate returned {@code true}.
+ * It is recommended to apply a filter for retaining only the types of paths of interest,
+ * because it can resolve ambiguities when a path could be of many types.
+ *
+ * @param pathTypeFilter predicate evaluating whether a path type should be included in the result
+ * @return {@code this} for method call chaining
+ */
+ @Nonnull
+ public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Predicate pathTypeFilter) {
+ this.pathTypeFilter = pathTypeFilter;
+ return this;
+ }
+
+ /**
+ * Specifies the type of paths to include in the result. This is a convenience method for
+ * {@link #pathTypeFilter(Predicate)} using {@link Collection#contains(Object)} as the filter.
+ *
+ * @param desiredTypes the type of paths to include in the result
+ * @return {@code this} for method call chaining
+ */
+ @Nonnull
+ public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Collection desiredTypes) {
+ return pathTypeFilter(desiredTypes::contains);
+ }
+
@Override
public DependencyResolverRequest build() {
return new DefaultDependencyResolverRequest(
- session, project, rootArtifact, root, dependencies, managedDependencies, verbose, pathScope);
+ session,
+ project,
+ rootArtifact,
+ root,
+ dependencies,
+ managedDependencies,
+ verbose,
+ pathScope,
+ pathTypeFilter);
}
static class DefaultDependencyResolverRequest extends DefaultDependencyCollectorRequest
implements DependencyResolverRequest {
private final PathScope pathScope;
+ private final Predicate pathTypeFilter;
+
DefaultDependencyResolverRequest(
Session session,
Project project,
@@ -176,9 +228,11 @@ static class DefaultDependencyResolverRequest extends DefaultDependencyCollector
Collection dependencies,
Collection managedDependencies,
boolean verbose,
- PathScope pathScope) {
+ PathScope pathScope,
+ Predicate pathTypeFilter) {
super(session, project, rootArtifact, root, dependencies, managedDependencies, verbose);
this.pathScope = nonNull(pathScope, "pathScope cannot be null");
+ this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : (t) -> true;
if (verbose) {
throw new IllegalArgumentException("verbose cannot be true for resolving dependencies");
}
@@ -189,6 +243,11 @@ static class DefaultDependencyResolverRequest extends DefaultDependencyCollector
public PathScope getPathScope() {
return pathScope;
}
+
+ @Override
+ public Predicate getPathTypeFilter() {
+ return pathTypeFilter;
+ }
}
}
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java
index 793243850068..60468046145e 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java
@@ -21,9 +21,11 @@
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import org.apache.maven.api.Dependency;
import org.apache.maven.api.Node;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
@@ -32,13 +34,57 @@ public interface DependencyResolverResult extends DependencyCollectorResult {
/**
* The ordered list of the flattened dependency nodes.
+ *
+ * @return the ordered list of the flattened dependency nodes
*/
@Nonnull
List getNodes();
+ /**
+ * Returns the file paths of all dependencies, regardless on which tool option those paths should be placed.
+ * The returned list may contain a mix of Java class-path, Java module-path, and other types of path elements.
+ *
+ * @return the paths of all dependencies
+ */
@Nonnull
List getPaths();
+ /**
+ * Returns the file paths of all dependencies, dispatched according the tool options where to place them.
+ * The {@link PathType} keys identify, for example, {@code --class-path} or {@code --module-path} options.
+ * In the case of Java tools, the map may also contain {@code --patch-module} options, which are
+ * {@linkplain org.apache.maven.api.JavaPathType#patchModule(String) handled in a special way}.
+ *
+ * Design note:
+ * All types of path are determined together because they are sometime mutually exclusive.
+ * For example, an artifact of type {@value org.apache.maven.api.Type#JAR} can be placed
+ * either on the class-path or on the module-path. The project needs to make a choice
+ * (possibly using heuristic rules), then to add the dependency in only one of the options
+ * identified by {@link PathType}.
+ *
+ * @return file paths to place on the different tool options
+ */
+ @Nonnull
+ Map> getDispatchedPaths();
+
@Nonnull
Map getDependencies();
+
+ /**
+ * Formats the command-line option for the path of the specified type.
+ * The option are documented in {@link org.apache.maven.api.JavaPathType} enumeration values.
+ *
+ * @param type the desired type of path (class-path, module-path, …)
+ * @return the option to pass to Java tools
+ */
+ default Optional formatOption(final PathType type) {
+ List paths = getDispatchedPaths().get(type);
+ if (paths != null) {
+ String option = type.option(paths);
+ if (!option.isEmpty()) {
+ return Optional.of(option);
+ }
+ }
+ return Optional.empty();
+ }
}
diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java b/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java
index 364e12425ee8..12f50ef31a76 100644
--- a/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java
+++ b/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java
@@ -54,8 +54,15 @@ public interface ArtifactHandler {
String getLanguage();
/**
- * IMPORTANT: this is WRONGLY NAMED method (and/or remnant for Maven2).
- * Its meaning is "is added to build path", that is used to create classpath/modulepath/etc.
+ * Specifies if the artifact contains java classes and can be added to the classpath.
+ * Whether the artifact should be added to the classpath depends on other
+ * dependency properties.
+ *
+ * @return if the artifact can be added to the class path
+ *
+ * @deprecated A value of {@code true} does not mean that the dependency should
+ * be placed on the classpath. See {@code JavaPathType} instead for better analysis.
*/
+ @Deprecated
boolean isAddedToClasspath();
}
diff --git a/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java b/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java
index 8f8dd820e0cf..f70db5bcbd32 100644
--- a/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java
+++ b/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java
@@ -77,11 +77,13 @@ public String getLanguage() {
return language;
}
+ @Deprecated
public void setAddedToClasspath(boolean addedToClasspath) {
this.addedToClasspath = addedToClasspath;
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return addedToClasspath;
}
diff --git a/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java b/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java
index 5e02488d0716..93c90bd889fa 100644
--- a/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java
+++ b/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java
@@ -65,6 +65,7 @@ public String getPackaging() {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return true;
}
diff --git a/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java b/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java
index 0a083732c05d..198fe6fb6aa3 100644
--- a/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java
+++ b/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java
@@ -37,6 +37,7 @@ public class DefaultArtifactHandler implements ArtifactHandler {
private String language;
+ @Deprecated
private boolean addedToClasspath;
/**
@@ -146,10 +147,12 @@ public void setLanguage(final String language) {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return addedToClasspath;
}
+ @Deprecated
public void setAddedToClasspath(final boolean addedToClasspath) {
this.addedToClasspath = addedToClasspath;
}
diff --git a/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java b/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java
index 1084d8617fad..2ad0541914dc 100644
--- a/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java
+++ b/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java
@@ -26,6 +26,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.Type;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.artifact.handler.ArtifactHandler;
@@ -72,7 +73,8 @@ public ArtifactHandler getArtifactHandler(String id) {
null,
type.isIncludesDependencies(),
type.getLanguage().id(),
- type.isBuildPathConstituent());
+ type.getPathTypes().contains(JavaPathType.CLASSES));
+ // TODO: watch out for module path
});
// Note: here, type decides is artifact added to "build path" (for example during resolution)
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java b/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java
index 89f00b4138e4..2f10720eb9df 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java
@@ -18,6 +18,7 @@
*/
package org.apache.maven.internal.aether;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Type;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.repository.internal.type.DefaultType;
@@ -45,8 +46,8 @@ public ArtifactType get(String typeId) {
type.getLanguage(),
type.getExtension(),
type.getClassifier(),
- type.isBuildPathConstituent(),
- type.isIncludesDependencies());
+ type.isIncludesDependencies(),
+ type.getPathTypes().toArray(new PathType[0]));
}
return null;
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java b/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
index f3bdb2be33dd..4db0a0c46b1e 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
@@ -29,6 +29,7 @@
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
import org.apache.maven.api.*;
import org.apache.maven.api.annotations.Nonnull;
@@ -353,7 +354,8 @@ public Map.Entry resolveArtifact(Artifact artifact) {
@Override
public Map resolveArtifacts(Artifact... artifacts) {
ArtifactCoordinateFactory acf = getService(ArtifactCoordinateFactory.class);
- List coords = map(Arrays.asList(artifacts), a -> acf.create(this, a));
+ List coords =
+ Arrays.stream(artifacts).map(a -> acf.create(this, a)).collect(Collectors.toList());
return resolveArtifacts(coords);
}
@@ -502,6 +504,34 @@ public List resolveDependencies(Project project, PathScope scope) {
.getPaths();
}
+ @Override
+ public Map> resolveDependencies(
+ @Nonnull DependencyCoordinate dependency,
+ @Nonnull PathScope scope,
+ @Nonnull Collection desiredTypes) {
+ return getService(DependencyResolver.class)
+ .resolve(DependencyResolverRequest.builder()
+ .session(this)
+ .dependency(dependency)
+ .pathScope(scope)
+ .pathTypeFilter(desiredTypes)
+ .build())
+ .getDispatchedPaths();
+ }
+
+ @Override
+ public Map> resolveDependencies(
+ @Nonnull Project project, @Nonnull PathScope scope, @Nonnull Collection desiredTypes) {
+ return getService(DependencyResolver.class)
+ .resolve(DependencyResolverRequest.builder()
+ .session(this)
+ .project(project)
+ .pathScope(scope)
+ .pathTypeFilter(desiredTypes)
+ .build())
+ .getDispatchedPaths();
+ }
+
@Override
public Path getPathForLocalArtifact(@Nonnull Artifact artifact) {
return getService(LocalRepositoryManager.class).getPathForLocalArtifact(this, getLocalRepository(), artifact);
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java
index 5a5b3f9b0515..8137a2e2c968 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java
@@ -34,6 +34,7 @@
import static org.apache.maven.internal.impl.Utils.nonNull;
public class DefaultDependency implements Dependency {
+
private final InternalSession session;
private final org.eclipse.aether.graph.Dependency dependency;
private final String key;
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
index 298576d5f4b6..65a1a18ea05f 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
@@ -21,19 +21,26 @@
import javax.inject.Named;
import javax.inject.Singleton;
+import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.api.*;
+import org.apache.maven.api.Artifact;
+import org.apache.maven.api.ArtifactCoordinate;
+import org.apache.maven.api.Dependency;
+import org.apache.maven.api.Node;
+import org.apache.maven.api.PathType;
+import org.apache.maven.api.Project;
+import org.apache.maven.api.Session;
import org.apache.maven.api.services.*;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.internal.LifecycleDependencyResolver;
@@ -69,59 +76,69 @@ private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
};
}
+ /**
+ * Collects, flattens and resolves the dependencies.
+ *
+ * @param request the request to resolve
+ * @return the result of the resolution
+ */
@Override
- public DependencyResolverResult resolve(DependencyResolverRequest request)
+ public DependencyResolverResult resolve(final DependencyResolverRequest request)
throws DependencyCollectorException, DependencyResolverException, ArtifactResolverException {
- nonNull(request, "request can not be null");
- InternalSession session = InternalSession.from(request.getSession());
-
+ nonNull(request, "request");
+ final InternalSession session = InternalSession.from(request.getSession());
+ final Predicate filter = request.getPathTypeFilter();
+ final PathModularizationCache cache = new PathModularizationCache(); // TODO: should be project-wide cache.
if (request.getProject().isPresent()) {
- DependencyResolutionResult result = resolveDependencies(
+ final DependencyResolutionResult resolved = resolveDependencies(
request.getSession(), request.getProject().get(), request.getPathScope());
- Map nodes = stream(
- result.getDependencyGraph())
+ final Map nodes = stream(
+ resolved.getDependencyGraph())
.filter(n -> n.getDependency() != null)
.collect(Collectors.toMap(DependencyNode::getDependency, n -> n));
- Node root = session.getNode(result.getDependencyGraph());
- List dependencies = new ArrayList<>();
- Map artifacts = new LinkedHashMap<>();
- List paths = new ArrayList<>();
- for (org.eclipse.aether.graph.Dependency dep : result.getResolvedDependencies()) {
- dependencies.add(session.getNode(nodes.get(dep)));
+ final Node root = session.getNode(resolved.getDependencyGraph());
+ final List deprendencies = resolved.getResolvedDependencies();
+ final DefaultDependencyResolverResult result =
+ new DefaultDependencyResolverResult(resolved.getCollectionErrors(), root, deprendencies.size());
+ for (org.eclipse.aether.graph.Dependency dep : deprendencies) {
+ Node node = session.getNode(nodes.get(dep));
Path path = dep.getArtifact().getFile().toPath();
- artifacts.put(session.getDependency(dep), path);
- paths.add(path);
+ try {
+ result.addDependency(node, session.getDependency(dep), filter, path, cache);
+ } catch (IOException e) {
+ throw cannotReadModuleInfo(path, e);
+ }
}
- return new DefaultDependencyResolverResult(
- result.getCollectionErrors(), root, dependencies, paths, artifacts);
+ return result;
}
- DependencyCollectorResult collectorResult =
+ final DependencyCollectorResult collectorResult =
session.getService(DependencyCollector.class).collect(request);
- List nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
- List deps =
- nodes.stream().map(Node::getDependency).filter(Objects::nonNull).collect(Collectors.toList());
- List coordinates =
- deps.stream().map(Artifact::toCoordinate).collect(Collectors.toList());
- Map artifacts = session.resolveArtifacts(coordinates);
- Map dependencies = new LinkedHashMap<>();
- List paths = new ArrayList<>();
- for (Dependency d : deps) {
- Path path = artifacts.get(d);
- if (dependencies.put(d, path) != null) {
- throw new IllegalStateException("Duplicate key");
+ final List nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
+ final List coordinates = nodes.stream()
+ .map(Node::getDependency)
+ .filter(Objects::nonNull)
+ .map(Artifact::toCoordinate)
+ .collect(Collectors.toList());
+ final Map artifacts = session.resolveArtifacts(coordinates);
+ final DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(
+ collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
+ for (Node node : nodes) {
+ Dependency d = node.getDependency();
+ Path path = (d != null) ? artifacts.get(d) : null;
+ try {
+ result.addDependency(node, d, filter, path, cache);
+ } catch (IOException e) {
+ throw cannotReadModuleInfo(path, e);
}
- paths.add(path);
}
-
- return new DefaultDependencyResolverResult(
- collectorResult.getExceptions(), collectorResult.getRoot(), nodes, paths, dependencies);
+ return result;
}
- private Stream stream(DependencyNode node) {
- return Stream.concat(Stream.of(node), node.getChildren().stream().flatMap(this::stream));
+ private static Stream stream(final DependencyNode node) {
+ return Stream.concat(Stream.of(node), node.getChildren().stream().flatMap(DefaultDependencyResolver::stream));
}
private DependencyResolutionResult resolveDependencies(Session session, Project project, PathScope scope) {
@@ -141,7 +158,7 @@ private DependencyResolutionResult resolveDependencies(Session session, Project
}
}
- private MavenProject getMavenProject(Project project) {
+ private static MavenProject getMavenProject(final Project project) {
return ((DefaultProject) project).getProject();
}
@@ -149,49 +166,7 @@ private Collection toScopes(PathScope scope) {
return map(scope.dependencyScopes(), DependencyScope::id);
}
- static class DefaultDependencyResolverResult implements DependencyResolverResult {
- private final List exceptions;
- private final Node root;
- private final List nodes;
- private final List paths;
- private final Map dependencies;
-
- DefaultDependencyResolverResult(
- List exceptions,
- Node root,
- List nodes,
- List paths,
- Map dependencies) {
- this.exceptions = exceptions;
- this.root = root;
- this.nodes = nodes;
- this.paths = paths;
- this.dependencies = dependencies;
- }
-
- @Override
- public List getExceptions() {
- return exceptions;
- }
-
- @Override
- public Node getRoot() {
- return root;
- }
-
- @Override
- public List getNodes() {
- return nodes;
- }
-
- @Override
- public List getPaths() {
- return paths;
- }
-
- @Override
- public Map getDependencies() {
- return dependencies;
- }
+ private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
+ return new DependencyResolverException("Cannot read module information of " + path, cause);
}
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolverResult.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolverResult.java
new file mode 100644
index 000000000000..55e82b4187c6
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolverResult.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.maven.api.Dependency;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Node;
+import org.apache.maven.api.PathType;
+import org.apache.maven.api.services.DependencyResolverRequest;
+import org.apache.maven.api.services.DependencyResolverResult;
+
+/**
+ * The result of collecting dependencies with a dependency resolver.
+ * New instances are initially empty. Callers must populate with calls
+ * to the following methods, in that order:
+ *
+ *
+ * - {@link #addOutputDirectory(Path, Path, PathModularizationCache)} (optional)
+ * - {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}
+ *
+ *
+ * @see DefaultDependencyResolver#resolve(DependencyResolverRequest)
+ */
+class DefaultDependencyResolverResult implements DependencyResolverResult {
+ /**
+ * The exceptions that occurred while building the dependency graph.
+ */
+ private final List exceptions;
+
+ /**
+ * The root node of the dependency graph.
+ */
+ private final Node root;
+
+ /**
+ * The ordered list of the flattened dependency nodes.
+ */
+ private final List nodes;
+
+ /**
+ * The file paths of all dependencies, regardless on which Java tool option those paths should be placed.
+ */
+ private final List paths;
+
+ /**
+ * The file paths of all dependencies, dispatched according the Java options where to place them.
+ */
+ private final Map> dispatchedPaths;
+
+ /**
+ * The dependencies together with the path to each dependency.
+ */
+ private final Map dependencies;
+
+ /**
+ * Information about modules in the main output. This field is initially null and is set to a non-null
+ * value when the output directories have been set, or when it is too late for setting them.
+ */
+ private PathModularization outputModules;
+
+ /**
+ * Creates an initially empty result. Callers should add path elements by calls
+ * to {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}.
+ *
+ * @param exceptions the exceptions that occurred while building the dependency graph
+ * @param root the root node of the dependency graph
+ * @param count estimated number of dependencies
+ */
+ DefaultDependencyResolverResult(List exceptions, Node root, int count) {
+ this.exceptions = exceptions;
+ this.root = root;
+ nodes = new ArrayList<>(count);
+ paths = new ArrayList<>(count);
+ dispatchedPaths = new LinkedHashMap<>();
+ dependencies = new LinkedHashMap<>(count + count / 3);
+ }
+
+ /**
+ * Adds the given path element to the specified type of path.
+ *
+ * @param type the type of path (class-path, module-path, …)
+ * @param path the path element to add
+ */
+ private void addPathElement(PathType type, Path path) {
+ dispatchedPaths.computeIfAbsent(type, (t) -> new ArrayList<>()).add(path);
+ }
+
+ /**
+ * Adds main and test output directories to the result. This method adds the main output directory
+ * to the module-path if it contains a {@code module-info.class}, or to the class-path otherwise.
+ * For the test output directory, the rules are more complex and are governed by the fact that
+ * Java does not accept the placement of two modules of the same name on the module-path.
+ * So the modular test output directory usually needs to be placed in a {@code --path-module} option.
+ *
+ *
+ * - If the test output directory is modular, then:
+ *
+ * - If a test module name is identical to a main module name,
+ * place the test directory in a {@code --patch-module} option.
+ * - Otherwise, place the test directory on the module-path. However, this case
+ * (a module existing only in test output, not in main output) should be uncommon.
+ *
+ *
+ * - Otherwise (test output contains no module information), then:
+ *
+ * - If the main output is on the module-path, place the test output
+ * on a {@code --patch-module} option.
+ * - Otherwise (main output on the class-path), place the test output on the class-path too.
+ *
+ *
+ *
+ *
+ * This method must be invoked before {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}
+ * if output directories are desired on the class-path or module-path.
+ * This method can be invoked at most once.
+ *
+ * @param main the main output directory, or {@code null} if none
+ * @param test the test output directory, or {@code null} if none
+ * @param cache cache of module information about each dependency
+ * @throws IOException if an error occurred while reading module information
+ *
+ * TODO: this is currently not called
+ */
+ void addOutputDirectory(Path main, Path test, PathModularizationCache cache) throws IOException {
+ if (outputModules != null) {
+ throw new IllegalStateException("Output directories must be set first and only once.");
+ }
+ if (main != null) {
+ outputModules = cache.getModuleInfo(main);
+ addPathElement(outputModules.getPathType(), main);
+ } else {
+ outputModules = PathModularization.NONE;
+ }
+ if (test != null) {
+ boolean addToClasspath = true;
+ PathModularization testModules = cache.getModuleInfo(test);
+ boolean isModuleHierarchy = outputModules.isModuleHierarchy() || testModules.isModuleHierarchy();
+ for (String moduleName : outputModules.getModuleNames().values()) {
+ Path subdir = test;
+ if (isModuleHierarchy) {
+ // If module hierarchy is used, the directory names shall be the module names.
+ Path path = test.resolve(moduleName);
+ if (!Files.isDirectory(path)) {
+ // Main module without tests. It is okay.
+ continue;
+ }
+ subdir = path;
+ }
+ // When the same module is found in main and test output, the latter is patching the former.
+ addPathElement(JavaPathType.patchModule(moduleName), subdir);
+ addToClasspath = false;
+ }
+ /*
+ * If the test output directory provides some modules of its own, add them.
+ * Except for this unusual case, tests should never be added to the module-path.
+ */
+ for (Map.Entry entry :
+ testModules.getModuleNames().entrySet()) {
+ if (!outputModules.containsModule(entry.getValue())) {
+ addPathElement(JavaPathType.MODULES, entry.getKey());
+ addToClasspath = false;
+ }
+ }
+ if (addToClasspath) {
+ addPathElement(JavaPathType.CLASSES, test);
+ }
+ }
+ }
+
+ /**
+ * Adds a dependency to the result. This method populates the {@link #nodes}, {@link #paths},
+ * {@link #dispatchedPaths} and {@link #dependencies} collections with the given arguments.
+ *
+ * @param node the dependency node
+ * @param dep the dependency for the given node, or {@code null} if none
+ * @param filter filter the paths accepted by the tool which will consume the path.
+ * @param path the path to the dependency, or {@code null} if the dependency was null
+ * @param cache cache of module information about each dependency
+ * @throws IOException if an error occurred while reading module information
+ */
+ void addDependency(
+ Node node,
+ Dependency dep,
+ Predicate filter,
+ Path path,
+ PathModularizationCache cache)
+ throws IOException {
+ nodes.add(node);
+ if (dep == null) {
+ return;
+ }
+ if (dependencies.put(dep, path) != null) {
+ throw new IllegalStateException("Duplicated key: " + dep);
+ }
+ if (path == null) {
+ return;
+ }
+ paths.add(path);
+ /*
+ * Dispatch the dependency to class-path, module-path, patch-module path, etc.
+ * according the dependency properties. We need to process patch-module first,
+ * because this type depends on whether a module of the same name has already
+ * been added on the module-type.
+ */
+ // DependencyProperties properties = dep.getDependencyProperties();
+ Set pathTypes = dep.getType().getPathTypes();
+ if (containsPatches(pathTypes)) {
+ if (outputModules == null) {
+ // For telling users that it is too late for setting the output directory.
+ outputModules = PathModularization.NONE;
+ }
+ PathType type = null;
+ for (Map.Entry info :
+ cache.getModuleInfo(path).getModuleNames().entrySet()) {
+ String moduleName = info.getValue();
+ type = JavaPathType.patchModule(moduleName);
+ if (!containsModule(moduleName, cache)) {
+ /*
+ * Not patching an existing module. This case should be unusual. If it nevertheless
+ * happens, add on class-path or module-path if allowed, or keep patching otherwise.
+ * The latter case (keep patching) is okay if the main module will be defined later.
+ */
+ type = cache.selectPathType(pathTypes, filter, path).orElse(type);
+ }
+ addPathElement(type, info.getKey());
+ // There is usually no more than one element, but nevertheless allow multi-modules.
+ }
+ /*
+ * If the dependency has no module information, search for an artifact of the same groupId
+ * and artifactId. If one is found, we are patching that module. If none is found, add the
+ * dependency as a normal dependency.
+ */
+ if (type == null) {
+ Path main = findArtifactPath(dep.getGroupId(), dep.getArtifactId());
+ if (main != null) {
+ for (Map.Entry info :
+ cache.getModuleInfo(main).getModuleNames().entrySet()) {
+ type = JavaPathType.patchModule(info.getValue());
+ addPathElement(type, info.getKey());
+ // There is usually no more than one element, but nevertheless allow multi-modules.
+ }
+ }
+ }
+ if (type != null) {
+ return; // Dependency added, we are done.
+ }
+ }
+ cache.selectPathType(pathTypes, filter, path).ifPresent((type) -> addPathElement(type, path));
+ }
+
+ /**
+ * Returns whether the given set of path types contains at least one patch for a module.
+ */
+ private boolean containsPatches(Set types) {
+ for (PathType type : types) {
+ if (type instanceof JavaPathType.Modular) {
+ type = ((JavaPathType.Modular) type).rawType();
+ }
+ if (JavaPathType.PATCH_MODULE.equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether at least one previously added modular dependency contains a module of the given name.
+ *
+ * @param moduleName name of the module to search
+ * @param cache cache of module information about each dependency
+ */
+ private boolean containsModule(String moduleName, PathModularizationCache cache) throws IOException {
+ for (Path path : dispatchedPaths.getOrDefault(JavaPathType.MODULES, Collections.emptyList())) {
+ if (cache.getModuleInfo(path).containsModule(moduleName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Searches an artifact of the given group and artifact identifiers, and returns its path
+ *
+ * @param group the group identifier to search
+ * @param artifact the artifact identifier to search
+ * @return path to the desired artifact, or {@code null} if not found
+ */
+ private Path findArtifactPath(String group, String artifact) throws IOException {
+ for (Map.Entry entry : dependencies.entrySet()) {
+ Dependency dep = entry.getKey();
+ if (group.equals(dep.getGroupId()) && artifact.equals(dep.getArtifactId())) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List getExceptions() {
+ return exceptions;
+ }
+
+ @Override
+ public Node getRoot() {
+ return root;
+ }
+
+ @Override
+ public List getNodes() {
+ return nodes;
+ }
+
+ @Override
+ public List getPaths() {
+ return paths;
+ }
+
+ @Override
+ public Map> getDispatchedPaths() {
+ return dispatchedPaths;
+ }
+
+ @Override
+ public Map getDependencies() {
+ return dependencies;
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java
index 5142f9f9c3bb..edc8c3010325 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java
@@ -28,6 +28,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
+import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.Type;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.services.LanguageRegistry;
@@ -93,8 +94,9 @@ public Type require(String id) {
languageRegistry.require(handler.getLanguage()),
handler.getExtension(),
handler.getClassifier(),
- handler.isAddedToClasspath(),
- handler.isIncludesDependencies());
+ handler.isIncludesDependencies(),
+ JavaPathType.CLASSES,
+ JavaPathType.MODULES);
}
return type;
});
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularization.java b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularization.java
new file mode 100644
index 000000000000..44d32a8762bf
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularization.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Information about the modules contained in a path element.
+ * The path element may be a JAR file or a directory. Directories may use either package hierarchy
+ * or module hierarchy, but not module source hierarchy. The latter is excluded because this class
+ * is for path elements of compiled codes.
+ */
+class PathModularization {
+ /**
+ * A unique constant for all non-modular dependencies.
+ */
+ public static final PathModularization NONE = new PathModularization();
+
+ /**
+ * Name of the file to use as a sentinel value for deciding if a directory or a JAR is modular.
+ */
+ private static final String MODULE_INFO = "module-info.class";
+
+ /**
+ * The attribute for automatic module name in {@code META-INF/MANIFEST.MF} files.
+ */
+ private static final Attributes.Name AUTO_MODULE_NAME = new Attributes.Name("Automatic-Module-Name");
+
+ /**
+ * Module information for the path specified at construction time.
+ * This map is usually either empty if no module was found, or a singleton map.
+ * It may however contain more than one entry if module hierarchy was detected,
+ * in which case there is one key per sub-directory.
+ *
+ * This map may contain null values if the constructor was invoked with {@code resolve}
+ * parameter set to false. This is more efficient when only the module existence needs to
+ * be tested, and module descriptors are not needed.
+ *
+ * @see #getModuleNames()
+ */
+ private final Map descriptors;
+
+ /**
+ * Whether module hierarchy was detected. If false, then package hierarchy is assumed.
+ * In a package hierarchy, the {@linkplain #descriptors} map has either zero or one entry.
+ * In a module hierarchy, the descriptors map may have an arbitrary number of entries,
+ * including one (so the map size cannot be used as a criterion).
+ *
+ * @see #isModuleHierarchy()
+ */
+ private final boolean isModuleHierarchy;
+
+ /**
+ * Constructs an empty instance for non-modular dependencies.
+ *
+ * @see #NONE
+ */
+ private PathModularization() {
+ descriptors = Collections.emptyMap();
+ isModuleHierarchy = false;
+ }
+
+ /**
+ * Finds module information in the given JAR file, output directory, or test output directory.
+ * If no module is found, or if module information cannot be extracted, then this constructor
+ * builds an empty map.
+ *
+ * If the {@code resolve} parameter value is {@code false}, then some or all map values may
+ * be null instead of the actual module name. This option can avoid the cost of reading module
+ * descriptors when only the modules existence needs to be verified.
+ *
+ * Algorithm:
+ * If the given path is a directory, then there is a choice:
+ *
+ *
+ * - Package hierarchy: if a {@code module-info.class} file is found at the root,
+ * then builds a singleton map with the module name declared in that descriptor.
+ * - Module hierarchy: if {@code module-info.class} files are found in sub-directories,
+ * at a deep intentionally restricted to one level, then builds a map of module names found
+ * in the descriptor of each sub-directory.
+ *
+ *
+ * Otherwise if the given path is a JAR file, then there is a choice:
+ *
+ * - If a {@code module-info.class} file is found in the root directory or in a
+ * {@code "META-INF/versions/{n}/"} subdirectory, builds a singleton map with
+ * the module name declared in that descriptor.
+ * - Otherwise if an {@code "Automatic-Module-Name"} attribute is declared in the
+ * {@code META-INF/MANIFEST.MF} file, builds a singleton map with the value of that attribute.
+ *
+ *
+ * Otherwise builds an empty map.
+ *
+ * @param path directory or JAR file to test
+ * @param resolve whether the module names are requested. If false, null values may be used instead
+ * @throws IOException if an error occurred while reading the JAR file or the module descriptor
+ */
+ PathModularization(Path path, boolean resolve) throws IOException {
+ if (Files.isDirectory(path)) {
+ /*
+ * Package hierarchy: only one module with descriptor at the root.
+ * This is the layout of output directories in projects using the
+ * classical (Java 8 and before) way to organize source files.
+ */
+ Path file = path.resolve(MODULE_INFO);
+ if (Files.isRegularFile(file)) {
+ String name = null;
+ if (resolve) {
+ try (InputStream in = Files.newInputStream(file)) {
+ name = getModuleName(in);
+ }
+ }
+ descriptors = Collections.singletonMap(file, name);
+ isModuleHierarchy = false;
+ return;
+ }
+ /*
+ * Module hierarchy: many modules, one per directory, with descriptor at the root of the sub-directory.
+ * This is the layout of output directories in projects using the new (Java 9 and later) way to organize
+ * source files.
+ */
+ if (Files.isDirectory(file)) {
+ Map names = new HashMap<>();
+ try (Stream subdirs = Files.list(file)) {
+ subdirs.filter(Files::isDirectory).forEach((subdir) -> {
+ Path mf = subdir.resolve(MODULE_INFO);
+ if (Files.isRegularFile(mf)) {
+ String name = null;
+ if (resolve) {
+ try (InputStream in = Files.newInputStream(mf)) {
+ name = getModuleName(in);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ names.put(mf, name);
+ }
+ });
+ } catch (UncheckedIOException e) {
+ throw e.getCause();
+ }
+ if (!names.isEmpty()) {
+ descriptors = Collections.unmodifiableMap(names);
+ isModuleHierarchy = true;
+ return;
+ }
+ }
+ } else if (Files.isRegularFile(path)) {
+ /*
+ * JAR file: can contain only one module, with descriptor at the root.
+ * If no descriptor, the "Automatic-Module-Name" manifest attribute is
+ * taken as a fallback.
+ */
+ try (JarFile jar = new JarFile(path.toFile())) {
+ ZipEntry entry = jar.getEntry(MODULE_INFO);
+ if (entry != null) {
+ String name = null;
+ if (resolve) {
+ try (InputStream in = jar.getInputStream(entry)) {
+ name = getModuleName(in);
+ }
+ }
+ descriptors = Collections.singletonMap(path, name);
+ isModuleHierarchy = false;
+ return;
+ }
+ // No module descriptor, check manifest file.
+ Manifest mf = jar.getManifest();
+ if (mf != null) {
+ Object name = mf.getMainAttributes().get(AUTO_MODULE_NAME);
+ if (name instanceof String) {
+ descriptors = Collections.singletonMap(path, (String) name);
+ isModuleHierarchy = false;
+ return;
+ }
+ }
+ }
+ }
+ descriptors = Collections.emptyMap();
+ isModuleHierarchy = false;
+ }
+
+ /**
+ * Returns the module name declared in the given {@code module-info} descriptor.
+ * The input stream may be for a file or for an entry in a JAR file.
+ */
+ @Nonnull
+ private static String getModuleName(InputStream in) throws IOException {
+ return ModuleDescriptor.read(in).name();
+ }
+
+ /**
+ * Returns the type of path detected. The return value is {@link JavaPathType#MODULES}
+ * if the dependency is a modular JAR file or a directory containing module descriptor(s),
+ * or {@link JavaPathType#CLASSES} otherwise. A JAR file without module descriptor but with
+ * an "Automatic-Module-Name" manifest attribute is considered modular.
+ */
+ public JavaPathType getPathType() {
+ return descriptors.isEmpty() ? JavaPathType.CLASSES : JavaPathType.MODULES;
+ }
+
+ /**
+ * Returns whether module hierarchy was detected. If false, then package hierarchy is assumed.
+ * In a package hierarchy, the {@linkplain #getModuleNames()} map of modules has either zero or one entry.
+ * In a module hierarchy, the descriptors map may have an arbitrary number of entries,
+ * including one (so the map size cannot be used as a criterion).
+ */
+ public boolean isModuleHierarchy() {
+ return isModuleHierarchy;
+ }
+
+ /**
+ * Returns the module names for the path specified at construction time.
+ * This map is usually either empty if no module was found, or a singleton map.
+ * It may however contain more than one entry if module hierarchy was detected,
+ * in which case there is one key per sub-directory.
+ *
+ * This map may contain null values if the constructor was invoked with {@code resolve}
+ * parameter set to false. This is more efficient when only the module existence needs to
+ * be tested, and module descriptors are not needed.
+ */
+ @Nonnull
+ public Map getModuleNames() {
+ return descriptors;
+ }
+
+ /**
+ * Returns whether the dependency contains a module of the given name.
+ */
+ public boolean containsModule(String name) {
+ return descriptors.containsValue(name);
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularizationCache.java b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularizationCache.java
new file mode 100644
index 000000000000..680e89f81225
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularizationCache.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.PathType;
+
+/**
+ * Cache of {@link PathModularization} instances computed for given {@link Path} elements.
+ * The cache is used for avoiding the need to reopen the same files many times when the
+ * same dependency is used for different scope. For example a path used for compilation
+ * is typically also used for tests.
+ */
+class PathModularizationCache {
+ /**
+ * Module information for each JAR file or output directories.
+ * Cached when first requested to avoid decoding the module descriptors multiple times.
+ *
+ * @see #getModuleInfo(Path)
+ */
+ private final Map moduleInfo;
+
+ /**
+ * Whether JAR files are modular. This map is redundant with {@link #moduleInfo},
+ * but cheaper to compute when the module names are not needed.
+ *
+ * @see #getPathType(Path)
+ */
+ private final Map pathTypes;
+
+ /**
+ * Creates an initially empty cache.
+ */
+ PathModularizationCache() {
+ moduleInfo = new HashMap<>();
+ pathTypes = new HashMap<>();
+ }
+
+ /**
+ * Gets module information for the given JAR file or output directory.
+ * Module descriptors are read when first requested, then cached.
+ */
+ PathModularization getModuleInfo(Path path) throws IOException {
+ PathModularization info = moduleInfo.get(path);
+ if (info == null) {
+ info = new PathModularization(path, true);
+ moduleInfo.put(path, info);
+ pathTypes.put(path, info.getPathType());
+ }
+ return info;
+ }
+
+ /**
+ * Returns {@link JavaPathType#MODULES} if the given JAR file or output directory is modular.
+ * This is used in heuristic rules for deciding whether to place a dependency on the class-path
+ * or on the module-path when the {@code "jar"} artifact type is used.
+ */
+ private PathType getPathType(Path path) throws IOException {
+ PathType type = pathTypes.get(path);
+ if (type == null) {
+ type = new PathModularization(path, false).getPathType();
+ pathTypes.put(path, type);
+ }
+ return type;
+ }
+
+ /**
+ * Selects the type of path where to place the given dependency.
+ * This method returns one of the values specified in the given array.
+ * This method does not handle the patch-module paths, because the patches
+ * depend on which modules have been previously added on the module-paths.
+ *
+ * If the dependency can be a constituent of both the class-path and the module-path,
+ * then the path type is determined by checking if the dependency is modular.
+ *
+ * @param types types of path where a dependency can be placed
+ * @param filter filter the paths accepted by the tool which will consume the path
+ * @param path path to the JAR file or output directory of the dependency
+ * @return where to place the dependency, or an empty value if the placement cannot be determined
+ * @throws IOException if an error occurred while reading module information
+ */
+ Optional selectPathType(Set types, Predicate filter, Path path)
+ throws IOException {
+ PathType selected = null;
+ boolean classes = false;
+ boolean modules = false;
+ boolean unknown = false;
+ for (PathType type : types) {
+ if (filter.test(type)) {
+ if (JavaPathType.CLASSES.equals(type)) {
+ classes = true;
+ } else if (JavaPathType.MODULES.equals(type)) {
+ modules = true;
+ } else {
+ unknown = true;
+ }
+ if (selected == null) {
+ selected = type;
+ } else if (unknown) {
+ // More than one filtered value, and we don't know how to handle at least one of them.
+ // TODO: add a plugin mechanism for allowing plugin to specify their selection algorithm.
+ return Optional.empty();
+ }
+ }
+ }
+ if (classes & modules) {
+ selected = getPathType(path);
+ }
+ return Optional.ofNullable(selected);
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
index b2a1f9c2742b..64087c262d47 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
@@ -62,6 +62,7 @@ public String getPackaging() {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return false;
}
diff --git a/maven-core/src/main/java/org/apache/maven/project/MavenProject.java b/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
index 0e165e04cb30..9b58e0212646 100644
--- a/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
+++ b/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
@@ -32,12 +32,14 @@
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
+import java.util.function.Predicate;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.lifecycle.internal.DefaultProjectArtifactFactory;
@@ -320,68 +322,97 @@ public List getTestCompileSourceRoots() {
return testCompileSourceRoots;
}
- public List getCompileClasspathElements() throws DependencyResolutionRequiredException {
- List list = new ArrayList<>(getArtifacts().size() + 1);
-
- String d = getBuild().getOutputDirectory();
- if (d != null) {
- list.add(d);
- }
-
- for (Artifact a : getArtifacts()) {
- if (a.getArtifactHandler().isAddedToClasspath()) {
- // TODO let the scope handler deal with this
- if (Artifact.SCOPE_COMPILE.equals(a.getScope())
- || Artifact.SCOPE_PROVIDED.equals(a.getScope())
- || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
- addArtifactPath(a, list);
- }
- }
- }
+ // TODO let the scope handler deal with this
+ private static boolean isCompilePathElement(final String scope) {
+ return Artifact.SCOPE_COMPILE.equals(scope)
+ || Artifact.SCOPE_PROVIDED.equals(scope)
+ || Artifact.SCOPE_SYSTEM.equals(scope);
+ }
- return list;
+ // TODO let the scope handler deal with this
+ private static boolean isRuntimePathElement(final String scope) {
+ return Artifact.SCOPE_COMPILE.equals(scope) || Artifact.SCOPE_RUNTIME.equals(scope);
}
- // TODO this checking for file == null happens because the resolver has been confused about the root
- // artifact or not. things like the stupid dummy artifact coming from surefire.
- public List getTestClasspathElements() throws DependencyResolutionRequiredException {
- List list = new ArrayList<>(getArtifacts().size() + 2);
+ // TODO let the scope handler deal with this
+ private static boolean isTestPathElement(final String scope) {
+ return true;
+ }
- String d = getBuild().getTestOutputDirectory();
- if (d != null) {
- list.add(d);
+ /**
+ * Returns a filtered list of classpath elements. This method is invoked when the caller
+ * requested that all dependencies are placed on the classpath, with no module-path element.
+ *
+ * @param scopeFilter a filter returning {@code true} for the artifact scopes to accept.
+ * @param includeTestDir whether to include the test directory in the classpath elements.
+ * @return paths of all artifacts placed on the classpath.
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ */
+ private List getClasspathElements(final Predicate scopeFilter, final boolean includeTestDir)
+ throws DependencyResolutionRequiredException {
+ final List list = new ArrayList<>(getArtifacts().size() + 2);
+ if (includeTestDir) {
+ String d = getBuild().getTestOutputDirectory();
+ if (d != null) {
+ list.add(d);
+ }
}
-
- d = getBuild().getOutputDirectory();
+ String d = getBuild().getOutputDirectory();
if (d != null) {
list.add(d);
}
-
for (Artifact a : getArtifacts()) {
- if (a.getArtifactHandler().isAddedToClasspath()) {
- addArtifactPath(a, list);
+ final File f = a.getFile();
+ if (f != null && scopeFilter.test(a.getScope())) {
+ final ArtifactHandler h = a.getArtifactHandler();
+ if (h.isAddedToClasspath()) {
+ list.add(f.getPath());
+ }
}
}
-
return list;
}
- public List getRuntimeClasspathElements() throws DependencyResolutionRequiredException {
- List list = new ArrayList<>(getArtifacts().size() + 1);
+ /**
+ * Returns the elements placed on the classpath for compilation.
+ * This method can be invoked when the caller does not support module-path.
+ *
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ *
+ * @deprecated This method is unreliable because it does not consider other dependency properties.
+ * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
+ */
+ @Deprecated
+ public List getCompileClasspathElements() throws DependencyResolutionRequiredException {
+ return getClasspathElements(MavenProject::isCompilePathElement, false);
+ }
- String d = getBuild().getOutputDirectory();
- if (d != null) {
- list.add(d);
- }
+ /**
+ * Returns the elements placed on the classpath for tests.
+ * This method can be invoked when the caller does not support module-path.
+ *
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ *
+ * @deprecated This method is unreliable because it does not consider other dependency properties.
+ * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
+ */
+ @Deprecated
+ public List getTestClasspathElements() throws DependencyResolutionRequiredException {
+ return getClasspathElements(MavenProject::isTestPathElement, true);
+ }
- for (Artifact a : getArtifacts()) {
- if (a.getArtifactHandler().isAddedToClasspath()
- // TODO let the scope handler deal with this
- && (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope()))) {
- addArtifactPath(a, list);
- }
- }
- return list;
+ /**
+ * Returns the elements placed on the classpath for runtime.
+ * This method can be invoked when the caller does not support module-path.
+ *
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ *
+ * @deprecated This method is unreliable because it does not consider other dependency properties.
+ * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
+ */
+ @Deprecated
+ public List getRuntimeClasspathElements() throws DependencyResolutionRequiredException {
+ return getClasspathElements(MavenProject::isRuntimePathElement, false);
}
// ----------------------------------------------------------------------
@@ -1111,13 +1142,6 @@ private void deepCopy(MavenProject project) {
lifecyclePhases.addAll(project.lifecyclePhases);
}
- private void addArtifactPath(Artifact artifact, List classpath) {
- File file = artifact.getFile();
- if (file != null) {
- classpath.add(file.getPath());
- }
- }
-
private static String getProjectReferenceId(String groupId, String artifactId, String version) {
StringBuilder buffer = new StringBuilder(128);
buffer.append(groupId).append(':').append(artifactId).append(':').append(version);
@@ -1347,9 +1371,7 @@ public List getCompileArtifacts() {
// TODO classpath check doesn't belong here - that's the other method
if (a.getArtifactHandler().isAddedToClasspath()) {
// TODO let the scope handler deal with this
- if (Artifact.SCOPE_COMPILE.equals(a.getScope())
- || Artifact.SCOPE_PROVIDED.equals(a.getScope())
- || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
+ if (isCompilePathElement(a.getScope())) {
list.add(a);
}
}
@@ -1369,9 +1391,7 @@ public List getCompileDependencies() {
for (Artifact a : getArtifacts()) {
// TODO let the scope handler deal with this
- if (Artifact.SCOPE_COMPILE.equals(a.getScope())
- || Artifact.SCOPE_PROVIDED.equals(a.getScope())
- || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
+ if (isCompilePathElement(a.getScope())) {
Dependency dependency = new Dependency();
dependency.setArtifactId(a.getArtifactId());
@@ -1437,7 +1457,7 @@ public List getRuntimeDependencies() {
for (Artifact a : getArtifacts()) {
// TODO let the scope handler deal with this
- if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
+ if (isRuntimePathElement(a.getScope())) {
Dependency dependency = new Dependency();
dependency.setArtifactId(a.getArtifactId());
@@ -1459,9 +1479,7 @@ public List getRuntimeArtifacts() {
for (Artifact a : getArtifacts()) {
// TODO classpath check doesn't belong here - that's the other method
- if (a.getArtifactHandler().isAddedToClasspath()
- // TODO let the scope handler deal with this
- && (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope()))) {
+ if (a.getArtifactHandler().isAddedToClasspath() && isRuntimePathElement(a.getScope())) {
list.add(a);
}
}
@@ -1481,7 +1499,10 @@ public List getSystemClasspathElements() throws DependencyResolutionRequ
if (a.getArtifactHandler().isAddedToClasspath()) {
// TODO let the scope handler deal with this
if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
- addArtifactPath(a, list);
+ File f = a.getFile();
+ if (f != null) {
+ list.add(f.getPath());
+ }
}
}
}
diff --git a/maven-core/src/main/java/org/apache/maven/project/artifact/PluginArtifact.java b/maven-core/src/main/java/org/apache/maven/project/artifact/PluginArtifact.java
index bf266e717fc0..18c3442d0635 100644
--- a/maven-core/src/main/java/org/apache/maven/project/artifact/PluginArtifact.java
+++ b/maven-core/src/main/java/org/apache/maven/project/artifact/PluginArtifact.java
@@ -83,6 +83,7 @@ public String getPackaging() {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return true;
}
diff --git a/maven-core/src/main/java/org/apache/maven/project/artifact/ProjectArtifact.java b/maven-core/src/main/java/org/apache/maven/project/artifact/ProjectArtifact.java
index 965b37097017..c040fc93446e 100644
--- a/maven-core/src/main/java/org/apache/maven/project/artifact/ProjectArtifact.java
+++ b/maven-core/src/main/java/org/apache/maven/project/artifact/ProjectArtifact.java
@@ -88,6 +88,7 @@ public String getPackaging() {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return false;
}
diff --git a/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java b/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java
index 18ce020307f5..ef654feca23b 100644
--- a/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java
+++ b/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java
@@ -23,6 +23,7 @@
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -58,6 +59,7 @@
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;
@PlexusTest
@@ -126,6 +128,17 @@ void setup() {
sessionScope.seed(InternalSession.class, InternalSession.from(this.session));
}
+ private Project project(Artifact artifact) {
+ return session.getService(ProjectBuilder.class)
+ .build(ProjectBuilderRequest.builder()
+ .session(session)
+ .path(session.getPathForLocalArtifact(artifact))
+ .processPlugins(false)
+ .build())
+ .getProject()
+ .get();
+ }
+
@Test
void testCreateAndResolveArtifact() {
ArtifactCoordinate coord =
@@ -144,14 +157,7 @@ void testCreateAndResolveArtifact() {
void testBuildProject() {
Artifact artifact = session.createArtifact("org.codehaus.plexus", "plexus-utils", "1.4.5", "pom");
- Project project = session.getService(ProjectBuilder.class)
- .build(ProjectBuilderRequest.builder()
- .session(session)
- .path(session.getPathForLocalArtifact(artifact))
- .processPlugins(false)
- .build())
- .getProject()
- .get();
+ Project project = project(artifact);
assertNotNull(project);
}
@@ -165,28 +171,43 @@ void testCollectArtifactDependencies() {
@Test
void testResolveArtifactCoordinateDependencies() {
- ArtifactCoordinate coord =
- session.createArtifactCoordinate("org.apache.maven.core.test", "test-extension", "1", "jar");
+ DependencyCoordinate coord = session.createDependencyCoordinate(
+ session.createArtifactCoordinate("org.apache.maven.core.test", "test-extension", "1", "jar"));
- List paths = session.resolveDependencies(session.createDependencyCoordinate(coord));
+ List paths = session.resolveDependencies(coord);
assertNotNull(paths);
assertEquals(10, paths.size());
- assertTrue(paths.get(0).getFileName().toString().equals("test-extension-1.jar"));
+ assertEquals("test-extension-1.jar", paths.get(0).getFileName().toString());
+
+ // JUnit has an "Automatic-Module-Name", so it appears on the module path.
+ Map> dispatched = session.resolveDependencies(
+ coord, PathScope.TEST_COMPILE, Arrays.asList(JavaPathType.CLASSES, JavaPathType.MODULES));
+ List classes = dispatched.get(JavaPathType.CLASSES);
+ List modules = dispatched.get(JavaPathType.MODULES);
+ assertEquals(2, dispatched.size());
+ assertEquals(8, classes.size()); // "pluxus.pom" and "junit.jar" are excluded.
+ assertEquals(1, modules.size());
+ assertEquals("test-extension-1.jar", classes.get(0).getFileName().toString());
+ assertEquals("junit-4.13.1.jar", modules.get(0).getFileName().toString());
+ assertTrue(paths.containsAll(classes));
+ assertTrue(paths.containsAll(modules));
+
+ // If caller wants only a classpath, JUnit shall move there.
+ dispatched = session.resolveDependencies(coord, PathScope.TEST_COMPILE, Arrays.asList(JavaPathType.CLASSES));
+ classes = dispatched.get(JavaPathType.CLASSES);
+ modules = dispatched.get(JavaPathType.MODULES);
+ assertEquals(1, dispatched.size());
+ assertEquals(9, classes.size());
+ assertNull(modules);
+ assertTrue(paths.containsAll(classes));
}
@Test
void testProjectDependencies() {
Artifact pom = session.createArtifact("org.codehaus.plexus", "plexus-container-default", "1.0-alpha-32", "pom");
- Project project = session.getService(ProjectBuilder.class)
- .build(ProjectBuilderRequest.builder()
- .session(session)
- .path(session.getPathForLocalArtifact(pom))
- .processPlugins(false)
- .build())
- .getProject()
- .get();
+ Project project = project(pom);
assertNotNull(project);
Artifact artifact = session.createArtifact("org.apache.maven.core.test", "test-extension", "1", "jar");
diff --git a/maven-core/src/test/java/org/apache/maven/internal/impl/TestArtifactHandler.java b/maven-core/src/test/java/org/apache/maven/internal/impl/TestArtifactHandler.java
index 68281e9169c9..30ebee3972d8 100644
--- a/maven-core/src/test/java/org/apache/maven/internal/impl/TestArtifactHandler.java
+++ b/maven-core/src/test/java/org/apache/maven/internal/impl/TestArtifactHandler.java
@@ -65,6 +65,7 @@ public String getPackaging() {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return true;
}
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultType.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultType.java
index d87836bfac3f..6096267ea1be 100644
--- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultType.java
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultType.java
@@ -18,11 +18,12 @@
*/
package org.apache.maven.repository.internal.type;
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.*;
import java.util.Map;
+import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.Language;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Type;
import org.apache.maven.repository.internal.artifact.MavenArtifactProperties;
import org.eclipse.aether.artifact.ArtifactProperties;
@@ -40,8 +41,8 @@ public class DefaultType implements Type, ArtifactType {
private final Language language;
private final String extension;
private final String classifier;
- private final boolean buildPathConstituent;
private final boolean includesDependencies;
+ private final Set pathTypes;
private final Map properties;
public DefaultType(
@@ -49,20 +50,22 @@ public DefaultType(
Language language,
String extension,
String classifier,
- boolean buildPathConstituent,
- boolean includesDependencies) {
+ boolean includesDependencies,
+ PathType... pathTypes) {
this.id = requireNonNull(id, "id");
this.language = requireNonNull(language, "language");
this.extension = requireNonNull(extension, "extension");
this.classifier = classifier;
- this.buildPathConstituent = buildPathConstituent;
this.includesDependencies = includesDependencies;
+ this.pathTypes = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(pathTypes)));
Map properties = new HashMap<>();
properties.put(ArtifactProperties.TYPE, id);
properties.put(ArtifactProperties.LANGUAGE, language.id());
properties.put(MavenArtifactProperties.INCLUDES_DEPENDENCIES, Boolean.toString(includesDependencies));
- properties.put(MavenArtifactProperties.CONSTITUTES_BUILD_PATH, Boolean.toString(buildPathConstituent));
+ properties.put(
+ MavenArtifactProperties.CONSTITUTES_BUILD_PATH,
+ String.valueOf(this.pathTypes.contains(JavaPathType.CLASSES)));
this.properties = Collections.unmodifiableMap(properties);
}
@@ -91,16 +94,15 @@ public String getClassifier() {
return classifier;
}
- @Override
- public boolean isBuildPathConstituent() {
- return this.buildPathConstituent;
- }
-
@Override
public boolean isIncludesDependencies() {
return this.includesDependencies;
}
+ public Set getPathTypes() {
+ return this.pathTypes;
+ }
+
@Override
public Map getProperties() {
return properties;
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultTypeProvider.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultTypeProvider.java
index 2172fce0bb1f..fb833e687d56 100644
--- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultTypeProvider.java
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/type/DefaultTypeProvider.java
@@ -23,6 +23,7 @@
import java.util.Arrays;
import java.util.Collection;
+import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
import org.apache.maven.api.spi.TypeProvider;
@@ -37,18 +38,31 @@ public Collection provides() {
public Collection types() {
return Arrays.asList(
- new DefaultType("bom", Language.NONE, "pom", null, false, false),
- new DefaultType("pom", Language.NONE, "pom", null, false, false),
- new DefaultType("maven-plugin", Language.JAVA_FAMILY, "jar", null, true, false),
- new DefaultType("jar", Language.JAVA_FAMILY, "jar", null, true, false),
- new DefaultType("ejb", Language.JAVA_FAMILY, "jar", null, true, false),
- new DefaultType("ejb-client", Language.JAVA_FAMILY, "jar", "client", true, false),
- new DefaultType("test-jar", Language.JAVA_FAMILY, "jar", "tests", true, false),
- new DefaultType("javadoc", Language.JAVA_FAMILY, "jar", "javadoc", true, false),
- new DefaultType("java-source", Language.JAVA_FAMILY, "jar", "sources", false, false),
- new DefaultType("war", Language.JAVA_FAMILY, "war", null, false, true),
- new DefaultType("ear", Language.JAVA_FAMILY, "ear", null, false, true),
- new DefaultType("rar", Language.JAVA_FAMILY, "rar", null, false, true),
- new DefaultType("par", Language.JAVA_FAMILY, "par", null, false, true));
+ // Maven types
+ new DefaultType(Type.POM, Language.NONE, "pom", null, false),
+ new DefaultType(Type.BOM, Language.NONE, "pom", null, false),
+ new DefaultType(Type.MAVEN_PLUGIN, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES),
+ // Java types
+ new DefaultType(
+ Type.JAR, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES, JavaPathType.MODULES),
+ new DefaultType(Type.JAVADOC, Language.JAVA_FAMILY, "jar", "javadoc", false, JavaPathType.CLASSES),
+ new DefaultType(Type.JAVA_SOURCE, Language.JAVA_FAMILY, "jar", "sources", false),
+ new DefaultType(
+ Type.TEST_JAR,
+ Language.JAVA_FAMILY,
+ "jar",
+ "tests",
+ false,
+ JavaPathType.CLASSES,
+ JavaPathType.PATCH_MODULE),
+ new DefaultType(Type.MODULAR_JAR, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.MODULES),
+ new DefaultType(Type.CLASSPATH_JAR, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES),
+ // j2ee types
+ new DefaultType("ejb", Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES),
+ new DefaultType("ejb-client", Language.JAVA_FAMILY, "jar", "client", false, JavaPathType.CLASSES),
+ new DefaultType("war", Language.JAVA_FAMILY, "war", null, true),
+ new DefaultType("ear", Language.JAVA_FAMILY, "ear", null, true),
+ new DefaultType("rar", Language.JAVA_FAMILY, "rar", null, true),
+ new DefaultType("par", Language.JAVA_FAMILY, "par", null, true));
}
}