package net.bytebuddy.build.maven; import net.bytebuddy.ByteBuddy; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.collection.CollectRequest; import org.eclipse.aether.collection.DependencyCollectionException; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.DependencyRequest; import org.eclipse.aether.resolution.DependencyResolutionException; import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator; import java.io.Closeable; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A resolver that transforms a Maven coordinate into a class loader that can view the dependencies implied by this coordinate. */ public class ClassLoaderResolver implements Closeable { /** * The Maven log dispatcher. */ private final Log log; /** * The repository system to use. */ private final RepositorySystem repositorySystem; /** * The repository system session to use. */ private final RepositorySystemSession repositorySystemSession; /** * A list of remote repositories available. */ private final List<RemoteRepository> remoteRepositories; /** * A mapping of Maven coordinates to already existing class loaders. */ private final Map<MavenCoordinate, ClassLoader> classLoaders; /** * Creates a new class loader resolver. * * @param log The Maven log dispatcher. * @param repositorySystem The repository system to use. * @param repositorySystemSession The repository system session to use. * @param remoteRepositories A list of remote repositories available. */ public ClassLoaderResolver(Log log, RepositorySystem repositorySystem, RepositorySystemSession repositorySystemSession, List<RemoteRepository> remoteRepositories) { this.log = log; this.repositorySystem = repositorySystem; this.repositorySystemSession = repositorySystemSession; this.remoteRepositories = remoteRepositories; classLoaders = new HashMap<MavenCoordinate, ClassLoader>(); } /** * Resolves a Maven coordinate to a class loader that can load all of the coordinates classes. If a Maven coordinate was resolved previously, * the previously created class loader is returned. * * @param mavenCoordinate The Maven coordinate to resolve. * @return A class loader that references all of the class loader's dependencies and which is a child of this class's class loader. * @throws MojoExecutionException If the user configuration results in an error. * @throws MojoFailureException If the plugin application raises an error. */ public ClassLoader resolve(MavenCoordinate mavenCoordinate) throws MojoFailureException, MojoExecutionException { ClassLoader classLoader = classLoaders.get(mavenCoordinate); if (classLoader == null) { classLoader = doResolve(mavenCoordinate); classLoaders.put(mavenCoordinate, classLoader); } return classLoader; } /** * Resolves a Maven coordinate to a class loader that can load all of the coordinates classes. * * @param mavenCoordinate The Maven coordinate to resolve. * @return A class loader that references all of the class loader's dependencies and which is a child of this class's class loader. * @throws MojoExecutionException If the user configuration results in an error. * @throws MojoFailureException If the plugin application raises an error. */ private ClassLoader doResolve(MavenCoordinate mavenCoordinate) throws MojoExecutionException, MojoFailureException { List<URL> urls = new ArrayList<URL>(); log.info("Resolving transformer dependency: " + mavenCoordinate); try { DependencyNode root = repositorySystem.collectDependencies(repositorySystemSession, new CollectRequest(new Dependency(mavenCoordinate.asArtifact(), "runtime"), remoteRepositories)).getRoot(); repositorySystem.resolveDependencies(repositorySystemSession, new DependencyRequest().setRoot(root)); PreorderNodeListGenerator preorderNodeListGenerator = new PreorderNodeListGenerator(); root.accept(preorderNodeListGenerator); for (Artifact artifact : preorderNodeListGenerator.getArtifacts(false)) { urls.add(artifact.getFile().toURI().toURL()); } } catch (DependencyCollectionException exception) { throw new MojoExecutionException("Could not collect dependencies for " + mavenCoordinate, exception); } catch (DependencyResolutionException exception) { throw new MojoExecutionException("Could not resolve dependencies for " + mavenCoordinate, exception); } catch (MalformedURLException exception) { throw new MojoFailureException("Could not resolve file as URL for " + mavenCoordinate, exception); } return new URLClassLoader(urls.toArray(new URL[urls.size()]), ByteBuddy.class.getClassLoader()); } @Override public void close() throws IOException { for (ClassLoader classLoader : classLoaders.values()) { if (classLoader instanceof Closeable) { // URLClassLoaders are only closeable since Java 1.7. ((Closeable) classLoader).close(); } } } }