DependencyBuilder.java

  1. /*
  2.  * Licensed under the Apache License, Version 2.0 (the "License");
  3.  * you may not use this file except in compliance with the License.
  4.  * You may obtain a copy of the License at
  5.  *
  6.  * http://www.apache.org/licenses/LICENSE-2.0
  7.  *
  8.  * Unless required by applicable law or agreed to in writing, software
  9.  * distributed under the License is distributed on an "AS IS" BASIS,
  10.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11.  * See the License for the specific language governing permissions and
  12.  * limitations under the License.
  13.  */
  14. package org.basepom.inline.mojo;

  15. import static com.google.common.base.Preconditions.checkNotNull;
  16. import static com.google.common.collect.ImmutableSet.toImmutableSet;

  17. import java.util.List;
  18. import java.util.Objects;

  19. import com.google.common.base.Throwables;
  20. import com.google.common.collect.ImmutableList;
  21. import com.google.common.collect.ImmutableSet;
  22. import org.apache.maven.RepositoryUtils;
  23. import org.apache.maven.execution.MavenSession;
  24. import org.apache.maven.project.DefaultDependencyResolutionRequest;
  25. import org.apache.maven.project.DefaultProjectBuildingRequest;
  26. import org.apache.maven.project.DependencyResolutionException;
  27. import org.apache.maven.project.DependencyResolutionRequest;
  28. import org.apache.maven.project.DependencyResolutionResult;
  29. import org.apache.maven.project.MavenProject;
  30. import org.apache.maven.project.ProjectBuilder;
  31. import org.apache.maven.project.ProjectBuildingException;
  32. import org.apache.maven.project.ProjectBuildingRequest;
  33. import org.apache.maven.project.ProjectBuildingResult;
  34. import org.apache.maven.project.ProjectDependenciesResolver;
  35. import org.eclipse.aether.artifact.Artifact;
  36. import org.eclipse.aether.artifact.DefaultArtifact;
  37. import org.eclipse.aether.graph.Dependency;
  38. import org.eclipse.aether.graph.DependencyFilter;
  39. import org.eclipse.aether.repository.RemoteRepository;
  40. import org.eclipse.aether.transfer.ArtifactTransferException;
  41. import org.eclipse.aether.transfer.NoRepositoryLayoutException;
  42. import org.eclipse.aether.util.artifact.JavaScopes;

  43. /**
  44.  * Builds a map of dependencies required by a specific project or another dependency.
  45.  */
  46. final class DependencyBuilder {

  47.     private static final PluginLog LOG = new PluginLog(DependencyBuilder.class);

  48.     private final MavenProject rootProject;
  49.     private final MavenSession mavenSession;
  50.     private final ProjectBuilder projectBuilder;
  51.     private final ProjectDependenciesResolver projectDependenciesResolver;
  52.     private final List<MavenProject> reactorProjects;

  53.     DependencyBuilder(MavenProject rootProject, MavenSession mavenSession, ProjectBuilder projectBuilder,
  54.             ProjectDependenciesResolver projectDependenciesResolver, List<MavenProject> reactorProjects) {
  55.         this.rootProject = rootProject;
  56.         this.mavenSession = mavenSession;
  57.         this.projectBuilder = projectBuilder;
  58.         this.projectDependenciesResolver = projectDependenciesResolver;
  59.         this.reactorProjects = ImmutableList.copyOf(reactorProjects);
  60.     }

  61.     /**
  62.      * Create a map of dependencies for a given dependency node (representing an element on the dependency tree).
  63.      *
  64.      * @param dependency         The dependency node to use.
  65.      * @param projectScopeFilter A scope limiting filter to mask out dependencies out of scope.
  66.      * @return A map of dependencies for this given dependency node.
  67.      * @throws DependencyResolutionException Dependency resolution failed.
  68.      * @throws ProjectBuildingException      Maven project could not be built.
  69.      */
  70.     ImmutableList<Dependency> mapDependency(final Dependency dependency,
  71.             final DependencyFilter projectScopeFilter)
  72.             throws DependencyResolutionException, ProjectBuildingException {
  73.         checkNotNull(dependency, "dependency is null");

  74.         // build the project
  75.         final ProjectBuildingResult result = projectBuilder.build(convertFromAetherDependency(dependency), false, createProjectBuildingRequest());

  76.         // now resolve the project representing the dependency.
  77.         final MavenProject project = result.getProject();
  78.         return mapProject(project, projectScopeFilter);
  79.     }

  80.     /**
  81.      * Create a map of names to dependencies for a given project.
  82.      *
  83.      * @param project     The current maven project.
  84.      * @param scopeFilter A scope limiting filter to mask out dependencies out of scope.
  85.      * @return A map of dependencies for this given dependency node.
  86.      * @throws DependencyResolutionException Dependency resolution failed.
  87.      */
  88.     ImmutableList<Dependency> mapProject(final MavenProject project,
  89.             final DependencyFilter scopeFilter)
  90.             throws DependencyResolutionException {
  91.         checkNotNull(project, "project is null");

  92.         final DependencyResolutionRequest request = new DefaultDependencyResolutionRequest();
  93.         request.setRepositorySession(createProjectBuildingRequest().getRepositorySession());
  94.         request.setMavenProject(project);
  95.         request.setResolutionFilter(scopeFilter);

  96.         DependencyResolutionResult result;

  97.         try {
  98.             result = projectDependenciesResolver.resolve(request);
  99.         } catch (DependencyResolutionException e) {
  100.             result = e.getResult();
  101.             // try to resolve using the reactor projects
  102.             final ImmutableSet<ProjectKey> reactorProjects = this.reactorProjects.stream()
  103.                     .map(ProjectKey::fromProject).collect(toImmutableSet());

  104.             // resolve all dependencies that are matched by the reactor.
  105.             final ImmutableSet<Dependency> reactorDependencies = result.getUnresolvedDependencies().stream()
  106.                     .filter(d -> reactorProjects.contains(ProjectKey.fromDependency(d)))
  107.                     .collect(toImmutableSet());

  108.             result.getUnresolvedDependencies().removeAll(reactorDependencies);
  109.             result.getResolvedDependencies().addAll(reactorDependencies);

  110.             // remove all unresolved system dependencies
  111.             final ImmutableSet<Dependency> systemDependencies = result.getUnresolvedDependencies().stream()
  112.                     .filter(d -> JavaScopes.SYSTEM.equals(d.getScope()))
  113.                     .collect(toImmutableSet());

  114.             result.getUnresolvedDependencies().removeAll(systemDependencies);
  115.             result.getResolvedDependencies().addAll(systemDependencies);

  116.             // remove all unresolved optional dependencies
  117.             final ImmutableSet<Dependency> optionalDependencies = result.getUnresolvedDependencies().stream()
  118.                     .filter(Dependency::isOptional)
  119.                     .collect(toImmutableSet());

  120.             result.getUnresolvedDependencies().removeAll(optionalDependencies);
  121.             result.getResolvedDependencies().addAll(optionalDependencies);

  122.             if (!result.getUnresolvedDependencies().isEmpty()) {
  123.                 final Throwable t = Throwables.getRootCause(e);
  124.                 RemoteRepository repository = null;

  125.                 if (t instanceof NoRepositoryLayoutException) {
  126.                     repository = ((NoRepositoryLayoutException) t).getRepository();
  127.                 } else if (t instanceof ArtifactTransferException) {
  128.                     repository = ((ArtifactTransferException) t).getRepository();
  129.                 }

  130.                 if (repository != null && "legacy".equals(repository.getContentType())) {
  131.                     LOG.warn("Could not access a legacy repository for artifacts:  %s; Reason: %s", result.getUnresolvedDependencies(), t.getMessage());
  132.                 } else {
  133.                     throw e;
  134.                 }
  135.             }
  136.         }

  137.         return ImmutableList.copyOf(result.getResolvedDependencies());
  138.     }

  139.     static org.apache.maven.artifact.Artifact convertFromAetherDependency(final Dependency dependency) {
  140.         final var mavenArtifact = RepositoryUtils.toArtifact(convertToPomArtifact(dependency.getArtifact()));
  141.         mavenArtifact.setScope(dependency.getScope());
  142.         mavenArtifact.setOptional(dependency.isOptional());

  143.         return mavenArtifact;
  144.     }

  145.     private ProjectBuildingRequest createProjectBuildingRequest() {
  146.         DefaultProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(mavenSession.getProjectBuildingRequest());
  147.         buildingRequest.setRemoteRepositories(rootProject.getRemoteArtifactRepositories());
  148.         return buildingRequest;
  149.     }


  150.     static Artifact convertToPomArtifact(final Artifact artifact) {
  151.         // pom artifact has no classifier. If this is already a pom artifact, don't touch it.
  152.         if (artifact.getClassifier().isEmpty() && "pom".equals(artifact.getExtension())) {
  153.             return artifact;
  154.         }

  155.         // create a POM artifact.
  156.         return new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), "pom", artifact.getVersion());
  157.     }

  158.     private static final class ProjectKey {

  159.         private final String groupId;
  160.         private final String artifactId;
  161.         private final String version;

  162.         public static ProjectKey fromProject(final MavenProject project) {
  163.             checkNotNull(project, "project; is null");
  164.             return new ProjectKey(project.getGroupId(), project.getArtifactId(), project.getVersion());
  165.         }

  166.         public static ProjectKey fromDependency(final Dependency dependency) {
  167.             checkNotNull(dependency, "artifact; is null");
  168.             return new ProjectKey(dependency.getArtifact().getGroupId(),
  169.                     dependency.getArtifact().getArtifactId(),
  170.                     dependency.getArtifact().getVersion());
  171.         }

  172.         private ProjectKey(final String groupId, final String artifactId, final String version) {
  173.             this.groupId = checkNotNull(groupId, "groupId is null");
  174.             this.artifactId = checkNotNull(artifactId, "artifactId is null");
  175.             this.version = checkNotNull(version, "version is null");
  176.         }

  177.         @Override
  178.         public boolean equals(Object o) {
  179.             if (this == o) {
  180.                 return true;
  181.             }
  182.             if (o == null || getClass() != o.getClass()) {
  183.                 return false;
  184.             }
  185.             ProjectKey that = (ProjectKey) o;
  186.             return groupId.equals(that.groupId) &&
  187.                     artifactId.equals(that.artifactId) &&
  188.                     version.equals(that.version);
  189.         }

  190.         @Override
  191.         public int hashCode() {
  192.             return Objects.hash(groupId, artifactId, version);
  193.         }
  194.     }
  195. }