001/* 002 * Copyright 2017-2022 original authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.micronaut.maven.services; 017 018import io.micronaut.core.util.StringUtils; 019import io.micronaut.testresources.buildtools.MavenDependency; 020import org.apache.maven.execution.MavenSession; 021import org.apache.maven.model.DependencyManagement; 022import org.apache.maven.project.MavenProject; 023import org.eclipse.aether.RepositorySystem; 024import org.eclipse.aether.RepositorySystemSession; 025import org.eclipse.aether.artifact.Artifact; 026import org.eclipse.aether.artifact.DefaultArtifact; 027import org.eclipse.aether.collection.CollectRequest; 028import org.eclipse.aether.graph.Dependency; 029import org.eclipse.aether.graph.DependencyFilter; 030import org.eclipse.aether.resolution.ArtifactResult; 031import org.eclipse.aether.resolution.DependencyRequest; 032import org.eclipse.aether.resolution.DependencyResolutionException; 033import org.eclipse.aether.resolution.DependencyResult; 034import org.eclipse.aether.util.artifact.JavaScopes; 035import org.eclipse.aether.util.filter.DependencyFilterUtils; 036 037import javax.inject.Inject; 038import javax.inject.Singleton; 039import java.io.File; 040import java.util.ArrayList; 041import java.util.HashMap; 042import java.util.List; 043import java.util.stream.Stream; 044 045/** 046 * Utility methods for performing dependency resolution. 047 * 048 * @author Álvaro Sánchez-Mariscal 049 * @since 5.0.0 050 */ 051@Singleton 052public class DependencyResolutionService { 053 054 public static final String TEST_RESOURCES_GROUP = "io.micronaut.testresources"; 055 public static final String TEST_RESOURCES_ARTIFACT_ID_PREFIX = "micronaut-test-resources-"; 056 057 private static final String JAR_EXTENSION = "jar"; 058 059 private final MavenSession mavenSession; 060 private final MavenProject mavenProject; 061 private final RepositorySystem repositorySystem; 062 063 @Inject 064 public DependencyResolutionService(MavenSession mavenSession, 065 MavenProject mavenProject, 066 RepositorySystem repositorySystem) { 067 this.mavenSession = mavenSession; 068 this.mavenProject = mavenProject; 069 this.repositorySystem = repositorySystem; 070 } 071 072 private static Stream<File> streamClasspath(List<ArtifactResult> resolutionResult) { 073 return resolutionResult.stream() 074 .map(ArtifactResult::getArtifact) 075 .map(Artifact::getFile); 076 } 077 078 public static List<String> toClasspath(List<ArtifactResult> resolutionResult) { 079 return streamClasspath(resolutionResult) 080 .map(File::getAbsolutePath) 081 .toList(); 082 } 083 084 public static List<File> toClasspathFiles(List<ArtifactResult> resolutionResult) { 085 return streamClasspath(resolutionResult).toList(); 086 } 087 088 public static Dependency mavenDependencyToAetherDependency(org.apache.maven.model.Dependency dependency) { 089 Artifact artifact = mavenDependencyToAetherArtifact(dependency); 090 return new Dependency(artifact, dependency.getScope(), Boolean.valueOf(dependency.getOptional())); 091 } 092 093 public static Artifact mavenDependencyToAetherArtifact(org.apache.maven.model.Dependency dependency) { 094 return new DefaultArtifact( 095 dependency.getGroupId(), 096 dependency.getArtifactId(), 097 dependency.getClassifier(), 098 dependency.getType(), 099 dependency.getVersion() 100 ); 101 } 102 103 public static MavenDependency mavenDependencyToTestResourcesDependency(org.apache.maven.model.Dependency dependency) { 104 return new MavenDependency(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()); 105 } 106 107 public static Artifact testResourcesDependencyToAetherArtifact(MavenDependency dependency) { 108 return new DefaultArtifact(dependency.getGroup(), dependency.getArtifact(), JAR_EXTENSION, dependency.getVersion()); 109 } 110 111 /** 112 * Performs a dependency request to compute the transitive dependencies of the given artifacts. 113 * 114 * @param artifacts The artifacts to resolve 115 * @param applyManagedDependencies Whether to apply the managed dependencies of the project 116 * @return The resolution result 117 */ 118 public List<ArtifactResult> artifactResultsFor(Stream<Artifact> artifacts, boolean applyManagedDependencies) throws DependencyResolutionException { 119 RepositorySystemSession repositorySession = mavenSession.getRepositorySession(); 120 DependencyFilter classpathFilter = DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME); 121 CollectRequest collectRequest = new CollectRequest(); 122 collectRequest.setRepositories(mavenProject.getRemoteProjectRepositories()); 123 124 if (applyManagedDependencies) { 125 DependencyManagement dependencyManagement = mavenProject.getDependencyManagement(); 126 List<org.apache.maven.model.Dependency> dependencies = dependencyManagement == null || dependencyManagement.getDependencies() == null 127 ? List.of() 128 : dependencyManagement.getDependencies(); 129 HashMap<String, Dependency> dependencyMap = new HashMap<>(dependencies.size()); 130 for (org.apache.maven.model.Dependency dependency : dependencies) { 131 String ga = dependency.getGroupId() + ":" + dependency.getArtifactId(); 132 dependencyMap.putIfAbsent(ga, mavenDependencyToAetherDependency(dependency)); 133 } 134 collectRequest.setManagedDependencies(new ArrayList<>(dependencyMap.values())); 135 136 artifacts.forEach(artifact -> { 137 if (StringUtils.isEmpty(artifact.getVersion())) { 138 dependencyMap.computeIfPresent(artifact.getGroupId() + ":" + artifact.getArtifactId(), (coordinates, dependency) -> { 139 collectRequest.addDependency(new Dependency( 140 new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(), dependency.getArtifact().getVersion()), 141 JavaScopes.RUNTIME 142 )); 143 return dependency; 144 }); 145 } else { 146 collectRequest.addDependency(new Dependency(artifact, JavaScopes.RUNTIME)); 147 } 148 }); 149 } else { 150 artifacts.map(artifact -> new Dependency(artifact, JavaScopes.RUNTIME)).forEach(collectRequest::addDependency); 151 } 152 153 DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, classpathFilter); 154 DependencyResult dependencyResult = repositorySystem.resolveDependencies(repositorySession, dependencyRequest); 155 return dependencyResult.getArtifactResults(); 156 } 157}