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();
}
}
}
}