dev.jbang.dependencies.ArtifactResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbang-cli Show documentation
Show all versions of jbang-cli Show documentation
JBang Command Line Interface
The newest version!
package dev.jbang.dependencies;
import static dev.jbang.util.Util.infoMsg;
import java.io.Closeable;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.RepositoryEvent;
import org.eclipse.aether.RepositoryListener;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.ArtifactType;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.artifact.DefaultArtifactType;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.metadata.Metadata;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.artifact.SubArtifact;
import dev.jbang.Settings;
import dev.jbang.cli.ExitException;
import dev.jbang.util.Util;
import eu.maveniverse.maven.mima.context.Context;
import eu.maveniverse.maven.mima.context.ContextOverrides;
import eu.maveniverse.maven.mima.context.Runtimes;
public class ArtifactResolver implements Closeable {
private final Context context;
private final boolean downloadSources;
public static class Builder {
private List repositories;
private int timeout;
private boolean offline;
private boolean ignoreTransitiveRepositories;
private boolean withUserSettings;
private Path localFolder;
private Path settingsXml;
private boolean updateCache;
private boolean loggingEnabled;
private boolean downloadSources = false;
public static Builder create() {
return new Builder();
}
private Builder() {
}
public Builder repositories(List repositories) {
this.repositories = repositories;
return this;
}
public Builder timeout(int timeout) {
this.timeout = timeout;
return this;
}
public Builder withUserSettings(boolean withUserSettings) {
this.withUserSettings = withUserSettings;
return this;
}
public Builder localFolder(Path localFolder) {
this.localFolder = localFolder;
return this;
}
public Builder settingsXml(Path settingsXml) {
this.settingsXml = settingsXml;
return this;
}
public Builder forceCacheUpdate(boolean updateCache) {
this.updateCache = updateCache;
return this;
}
public Builder offline(boolean offline) {
this.offline = offline;
return this;
}
public Builder ignoreTransitiveRepositories(boolean ignoreTransitiveRepositories) {
this.ignoreTransitiveRepositories = ignoreTransitiveRepositories;
return this;
}
public Builder logging(boolean logging) {
this.loggingEnabled = logging;
return this;
}
public Builder downloadSources(boolean downloadSources) {
this.downloadSources = downloadSources;
return this;
}
public ArtifactResolver build() {
return new ArtifactResolver(this);
}
}
private ArtifactResolver(Builder builder) {
HashMap userProperties = new HashMap<>();
if (builder.timeout > 0) {
userProperties.put(ConfigurationProperties.CONNECT_TIMEOUT, String.valueOf(builder.timeout));
}
final List partialRepos; // always have reposes, at least Central if no user defined ones
if (builder.repositories != null) {
partialRepos = builder.repositories.stream().map(this::toRemoteRepo).collect(Collectors.toList());
} else {
partialRepos = Collections.singletonList(ContextOverrides.CENTRAL);
}
this.downloadSources = builder.downloadSources;
final RepositoryListener listener = builder.loggingEnabled ? setupSessionLogging() : null;
ContextOverrides.Builder overridesBuilder = ContextOverrides.create()
.userProperties(userProperties)
.offline(builder.offline)
.ignoreArtifactDescriptorRepositories(
builder.ignoreTransitiveRepositories)
.withUserSettings(builder.withUserSettings)
.withUserSettingsXmlOverride(builder.settingsXml)
.withLocalRepositoryOverride(builder.localFolder)
.repositories(partialRepos) // set reposes
// explicitly, jbang
// drives
.addRepositoriesOp(
ContextOverrides.AddRepositoriesOp.REPLACE) // ignore
// settings.xml
// reposes
.snapshotUpdatePolicy(builder.updateCache
? ContextOverrides.SnapshotUpdatePolicy.ALWAYS
: null)
.repositoryListener(listener);
overridesBuilder.extraArtifactTypes(
Collections.singletonList(new DefaultArtifactType("fatjar", "jar", null, "java", true, true)));
this.context = Runtimes.INSTANCE.getRuntime().create(overridesBuilder.build());
}
@Override
public void close() {
context.close();
}
public void downloadSources(Artifact artifact) {
try {
context .repositorySystem()
.resolveArtifact(context.repositorySystemSession(), new ArtifactRequest()
.setArtifact(
new SubArtifact(
artifact,
"sources",
"jar"))
.setRepositories(
context.remoteRepositories()));
} catch (ArtifactResolutionException e) {
Util.verboseMsg("Could not resolve sources for " + artifact.toString());
}
}
public List resolve(List depIds) {
context.repositorySystemSession().getData().set("depIds", depIds);
try {
Map> scopeDeps = depIds.stream()
.map(coord -> toDependency(toArtifact(coord)))
.collect(Collectors.groupingBy(Dependency::getScope));
List deps = scopeDeps.get(JavaScopes.COMPILE);
List managedDeps = deps .stream()
.flatMap(d -> getManagedDependencies(d).stream())
.collect(Collectors.toList());
if (scopeDeps.containsKey("import")) {
// If there are any @pom artifacts we'll apply their
// managed dependencies to the given dependencies BEFORE ordinary deps
List boms = scopeDeps.get("import");
List mdeps = boms .stream()
.flatMap(d -> getManagedDependencies(d).stream())
.collect(Collectors.toList());
deps = deps.stream().map(d -> applyManagedDependencies(d, mdeps)).collect(Collectors.toList());
managedDeps.addAll(0, mdeps);
}
CollectRequest collectRequest = new CollectRequest()
.setManagedDependencies(managedDeps)
.setDependencies(deps)
.setRepositories(context.remoteRepositories());
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null);
DependencyResult dependencyResult = context .repositorySystem()
.resolveDependencies(context.repositorySystemSession(),
dependencyRequest);
List artifacts = dependencyResult.getArtifactResults();
if (downloadSources) {
Util.infoMsg("Resolving sources for dependencies...");
}
return artifacts.stream()
.map(ar -> {
if (downloadSources) {
downloadSources(ar.getArtifact());
}
return ar.getArtifact();
})
.map(ArtifactResolver::toArtifactInfo)
.collect(Collectors.toList());
} catch (DependencyResolutionException ex) {
throw new ExitException(1, "Could not resolve dependencies: " + ex.getMessage(), ex);
}
}
private AbstractRepositoryListener setupSessionLogging() {
return new AbstractRepositoryListener() {
@Override
public void metadataResolving(RepositoryEvent event) {
Metadata md = event.getMetadata();
printEvent(md.getGroupId(), md.getArtifactId(), md.getVersion(), md.getType(), null);
}
@Override
public void metadataDownloading(RepositoryEvent event) {
Metadata md = event.getMetadata();
printEvent(md.getGroupId(), md.getArtifactId(), md.getVersion(), md.getType(), null);
}
@Override
public void artifactResolving(RepositoryEvent event) {
Artifact art = event.getArtifact();
printEvent(art.getGroupId(), art.getArtifactId(), art.getVersion(), art.getExtension(),
art.getClassifier());
}
@Override
public void artifactResolved(RepositoryEvent event) {
Artifact art = event.getArtifact();
printEvent(art.getGroupId(), art.getArtifactId(), art.getVersion(), art.getExtension(),
art.getClassifier());
}
@Override
public void artifactDownloading(RepositoryEvent event) {
Artifact art = event.getArtifact();
printEvent(art.getGroupId(), art.getArtifactId(), art.getVersion(), art.getExtension(),
art.getClassifier());
}
@SuppressWarnings("unchecked")
private void printEvent(String groupId, String artId, String version, String type, String classifier) {
RepositorySystemSession session = context.repositorySystemSession();
List depIds = (List) session.getData().get("depIds");
if (depIds == null) {
return;
}
Set ids = (Set) session .getData()
.computeIfAbsent("ids", () -> new HashSet<>(depIds));
Set printed = (Set) session .getData()
.computeIfAbsent("printed", () -> new HashSet<>());
String id = coord(groupId, artId, null, null, classifier);
if (!printed.contains(id)) {
String coord = coord(groupId, artId, version, null, classifier);
String pomcoord = coord(groupId, artId, version, "pom", null);
if (ids.contains(id) || ids.contains(coord) || ids.contains(pomcoord) || Util.isVerbose()) {
if (ids.contains(pomcoord)) {
infoMsg(" " + pomcoord);
} else {
infoMsg(" " + coord);
}
printed.add(id);
}
}
}
private String coord(String groupId, String artId, String version, String type, String classifier) {
String res = groupId + ":" + artId;
if (version != null && !version.isEmpty()) {
res += ":" + version;
}
if (classifier != null && classifier.length() > 0) {
res += "-" + classifier;
}
if ("pom".equals(type)) {
res += "@" + type;
}
return res;
}
};
}
private Dependency applyManagedDependencies(Dependency d, List managedDeps) {
Artifact art = d.getArtifact();
if (art.getVersion().isEmpty()) {
Optional ma = managedDeps .stream()
.map(Dependency::getArtifact)
.filter(a -> a.getGroupId().equals(art.getGroupId())
&& a.getArtifactId().equals(art.getArtifactId()))
.findFirst();
if (ma.isPresent()) {
return new Dependency(ma.get(), d.getScope(), d.getOptional(), d.getExclusions());
}
}
return d;
}
private List getManagedDependencies(Dependency dependency) {
return resolveDescriptor(dependency.getArtifact()).getManagedDependencies();
}
private ArtifactDescriptorResult resolveDescriptor(Artifact artifact) {
try {
ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest()
.setArtifact(artifact)
.setRepositories(
context.remoteRepositories());
return context .repositorySystem()
.readArtifactDescriptor(context.repositorySystemSession(), descriptorRequest);
} catch (ArtifactDescriptorException ex) {
throw new ExitException(1, "Could not read artifact descriptor for " + artifact, ex);
}
}
private RemoteRepository toRemoteRepo(MavenRepo repo) {
return new RemoteRepository.Builder(repo.getId(), "default", repo.getUrl())
.build();
}
private static Dependency toDependency(Artifact artifact) {
return new Dependency(artifact,
"pom".equalsIgnoreCase(artifact.getExtension()) ? "import" : JavaScopes.COMPILE);
}
private Artifact toArtifact(String coord) {
return toArtifact(MavenCoordinate.fromString(coord));
}
private Artifact toArtifact(MavenCoordinate coord) {
String cls = coord.getClassifier();
String ext = coord.getType();
if (coord.getType() != null) {
ArtifactType type = context.repositorySystemSession().getArtifactTypeRegistry().get(coord.getType());
if (type != null) {
ext = type.getExtension();
cls = Optional.ofNullable(cls).orElse(type.getClassifier());
return new DefaultArtifact(coord.getGroupId(), coord.getArtifactId(), cls, ext, coord.getVersion(),
type);
}
}
return new DefaultArtifact(coord.getGroupId(), coord.getArtifactId(), cls, ext, coord.getVersion());
}
private static ArtifactInfo toArtifactInfo(Artifact artifact) {
MavenCoordinate coord = new MavenCoordinate(artifact.getGroupId(), artifact.getArtifactId(),
artifact.getVersion(), artifact.getClassifier(), artifact.getExtension());
return new ArtifactInfo(coord, artifact.getFile().toPath());
}
public static Path getLocalMavenRepo() {
try (ArtifactResolver ar = Builder.create().localFolder(Settings.getJBangLocalMavenRepoOverride()).build()) {
return ar.context
.repositorySystemSession()
.getLocalRepository()
.getBasedir()
.toPath();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy