
org.mule.maven.client.internal.AetherMavenClient Maven / Gradle / Ivy
/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.maven.client.internal;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.tuple.Pair.of;
import static org.eclipse.aether.util.StringUtils.isEmpty;
import static org.eclipse.aether.util.artifact.ArtifactIdUtils.toId;
import static org.eclipse.aether.util.artifact.JavaScopes.COMPILE;
import static org.eclipse.aether.util.artifact.JavaScopes.PROVIDED;
import static org.eclipse.aether.util.artifact.JavaScopes.TEST;
import static org.mule.maven.client.internal.util.MavenUtils.getPomModel;
import static org.mule.maven.client.internal.util.Preconditions.checkState;
import static org.mule.maven.client.internal.util.VersionChecker.areCompatibleVersions;
import static org.slf4j.LoggerFactory.getLogger;
import org.mule.maven.client.api.BundleDependenciesResolutionException;
import org.mule.maven.client.api.MavenClient;
import org.mule.maven.client.api.MavenReactorResolver;
import org.mule.maven.client.api.PomFileSupplierFactory;
import org.mule.maven.client.api.VersionRangeResult;
import org.mule.maven.client.api.model.BundleDependency;
import org.mule.maven.client.api.model.BundleDescriptor;
import org.mule.maven.client.api.model.BundleScope;
import org.mule.maven.client.api.model.MavenConfiguration;
import org.mule.maven.client.internal.util.VersionChecker;
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Model;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositoryException;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
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.repository.WorkspaceReader;
import org.eclipse.aether.repository.WorkspaceRepository;
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.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.util.filter.PatternInclusionsDependencyFilter;
import org.eclipse.aether.util.graph.visitor.PathRecordingDependencyVisitor;
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
import org.slf4j.Logger;
public class AetherMavenClient implements MavenClient {
public static final String MULE_PLUGIN_CLASSIFIER = "mule-plugin";
public static final String MULE_DOMAIN_CLASSIFIER = "mule-domain";
private static final Logger LOGGER = getLogger(AetherMavenClient.class);
private static final String POM = "pom";
private static final Consumer NO_OP = _x -> {
};
private MavenConfiguration mavenConfiguration;
private AetherResolutionContext aetherResolutionContext;
private PomFileSupplierFactory pomFileSupplierFactory = new DefaultPomFileSupplierFactory();
private Optional> sessionConfigurator = empty();
public AetherMavenClient(MavenConfiguration mavenConfiguration) {
this.mavenConfiguration = mavenConfiguration;
this.aetherResolutionContext = new AetherResolutionContext(mavenConfiguration);
}
private AetherRepositoryState getRepositoryState(Optional workspaceReader) {
return getRepositoryState(aetherResolutionContext.getLocalRepositoryLocation(), workspaceReader);
}
private AetherRepositoryState getRepositoryState(File localRepositoryLocation,
Optional workspaceReader) {
AetherRepositoryState repositoryState = new AetherRepositoryState(localRepositoryLocation, workspaceReader,
aetherResolutionContext.getAuthenticatorSelector(),
aetherResolutionContext.getProxySelector(),
aetherResolutionContext.getMirrorSelector(),
mavenConfiguration.getForcePolicyUpdateNever(),
mavenConfiguration.getOfflineMode(),
mavenConfiguration
.getIgnoreArtifactDescriptorRepositories(),
sessionConfigurator.orElse(NO_OP));
return repositoryState;
}
@Override
public MavenConfiguration getMavenConfiguration() {
return this.mavenConfiguration;
}
public void setSessionConfigurator(Consumer sessionConfigurator) {
this.sessionConfigurator = of(sessionConfigurator);
}
@Override
public List resolveArtifactDependencies(File artifactFile,
boolean includeTestDependencies,
boolean includeProvidedDependencies,
Optional localRepositoryLocationSupplier,
Optional temporaryFolder) {
return resolveArtifactDependencies(artifactFile, includeTestDependencies, includeProvidedDependencies,
localRepositoryLocationSupplier, empty(), temporaryFolder);
}
@Override
public List resolveArtifactDependencies(File artifactFile,
boolean includeTestDependencies,
boolean includeProvidedDependencies,
Optional localRepositoryLocationSupplier,
Optional mavenReactorResolver,
Optional temporaryFolder) {
Model pomModel = getPomModel(artifactFile);
BundleDescriptor bundleDescriptor = getBundleDescriptor(pomModel);
Supplier pomFileSupplier;
if (artifactFile.isDirectory()) {
pomFileSupplier = pomFileSupplierFactory.uncompressPomArtifactSupplier(artifactFile, bundleDescriptor);
} else {
checkState(temporaryFolder.isPresent(), "temporary folder not provided but the artifact is compressed");
pomFileSupplier =
pomFileSupplierFactory.compressedArtifactSupplier(artifactFile, bundleDescriptor, temporaryFolder.get());
}
File localRepositoryLocation =
localRepositoryLocationSupplier.orElse(this.aetherResolutionContext.getLocalRepositoryLocation());
WorkspaceReader workspaceReader = new PomWorkspaceReader(bundleDescriptor, artifactFile, pomFileSupplier);
if (mavenReactorResolver.isPresent()) {
workspaceReader = new ChainedWorkspaceReader(workspaceReader, new MavenWorkspaceReaderAdapter(mavenReactorResolver.get()));
}
AetherRepositoryState repositoryState =
getRepositoryState(localRepositoryLocation, of(workspaceReader));
Artifact artifact = new DefaultArtifact(bundleDescriptor.getGroupId(), bundleDescriptor.getArtifactId(),
null,
POM,
pomModel.getVersion() != null ? pomModel.getVersion()
: pomModel.getParent().getVersion());
try {
return resolveArtifactDependencies(includeTestDependencies, includeProvidedDependencies, repositoryState, artifact);
} catch (DependencyResolutionException e) {
DependencyNode node = e.getResult().getRoot();
logUnresolvedArtifacts(node, e);
throw new RuntimeException(format("There was an issue solving the dependencies for the artifact [%s]",
artifactFile.getAbsolutePath()),
e);
} catch (DependencyCollectionException e) {
throw new RuntimeException(format("There was an issue resolving the dependency tree for the artifact [%s]",
artifactFile.getAbsolutePath()),
e);
} catch (ArtifactDescriptorException e) {
throw new RuntimeException(format("There was an issue resolving the artifact descriptor for the artifact [%s]",
artifactFile.getAbsolutePath()),
e);
} catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
private List resolveArtifactDependencies(boolean includeTestDependencies,
boolean includeProvidedDependencies,
AetherRepositoryState repositoryState, Artifact artifact)
throws RepositoryException {
ArtifactResult artifactResult = readArtifactDescriptor(artifact, repositoryState);
return assemblyDependenciesFromPom(repositoryState,
artifactResult.getArtifact(),
includeTestDependencies,
includeProvidedDependencies);
}
@Override
public List resolveBundleDescriptorDependencies(boolean includeTestDependencies,
BundleDescriptor bundleDescriptor) {
return resolveBundleDescriptorDependencies(includeTestDependencies, false, bundleDescriptor);
}
@Override
public List resolveBundleDescriptorDependencies(boolean includeTestDependencies,
boolean includeProvidedDependencies,
BundleDescriptor bundleDescriptor) {
try {
AetherRepositoryState repositoryState =
getRepositoryState(this.aetherResolutionContext.getLocalRepositoryLocation(), empty());
Artifact artifact = createArtifactFromBundleDescriptor(bundleDescriptor);
ArtifactResult artifactResult = readArtifactDescriptor(artifact, repositoryState);
return assemblyDependenciesFromPom(repositoryState,
artifactResult.getArtifact(),
includeTestDependencies,
includeProvidedDependencies);
} catch (RepositoryException e) {
throw new BundleDependenciesResolutionException(e);
}
}
public List resolveBundleDescriptorDependenciesWithWorkspaceReader(File artifactFile,
boolean includeTestDependencies,
boolean includeProvidedDependencies,
BundleDescriptor bundleDescriptor) {
Supplier pomFileSupplier;
pomFileSupplier = pomFileSupplierFactory.uncompressPomArtifactSupplier(artifactFile, bundleDescriptor);
try {
AetherRepositoryState repositoryState =
getRepositoryState(of(new PomWorkspaceReader(bundleDescriptor, artifactFile, pomFileSupplier)));
Artifact artifact = createArtifactFromBundleDescriptor(bundleDescriptor);
ArtifactResult artifactResult = readArtifactDescriptor(artifact, repositoryState);
return assemblyDependenciesFromPom(repositoryState,
artifactResult.getArtifact(),
includeTestDependencies,
includeProvidedDependencies);
} catch (RepositoryException e) {
throw new BundleDependenciesResolutionException(e);
}
}
private Artifact createArtifactFromBundleDescriptor(BundleDescriptor bundleDescriptor) {
return new DefaultArtifact(bundleDescriptor.getGroupId(), bundleDescriptor.getArtifactId(),
bundleDescriptor.getClassifier().orElse(null),
bundleDescriptor.getType(),
bundleDescriptor.getVersion());
}
@Override
public BundleDependency resolveBundleDescriptor(BundleDescriptor bundleDescriptor) {
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Resolving artifact with resolution context: " + aetherResolutionContext.toString());
}
AetherRepositoryState repositoryState = getRepositoryState(empty());
Artifact artifact = createArtifactFromBundleDescriptor(bundleDescriptor);
ArtifactRequest artifactRequest =
new ArtifactRequest(artifact, this.aetherResolutionContext.getRemoteRepositories(), null);
ArtifactResult artifactResult =
repositoryState.getSystem().resolveArtifact(repositoryState.getSession(), artifactRequest);
Artifact resolvedArtifact = artifactResult.getArtifact();
return artifactToBundleDependency(resolvedArtifact, "compile");
} catch (ArtifactResolutionException e) {
throw new BundleDependenciesResolutionException(e);
}
}
@Override
public Model getRawPomModel(File artifactFile) {
return getPomModel(artifactFile);
}
@Override
public List resolvePluginBundleDescriptorsDependencies(List bundleDescriptors) {
Map, BundleDependency> plugins = new HashMap<>();
bundleDescriptors.stream().map(this::resolveBundleDescriptor).forEach(directBundleDependency -> {
checkAndUpdatePluginVersion(directBundleDependency, plugins);
List bundleDependencies =
resolveBundleDescriptorDependencies(false, directBundleDependency.getDescriptor());
bundleDependencies.stream().filter(bundleDependency -> MULE_PLUGIN_CLASSIFIER
.equals(bundleDependency.getDescriptor().getClassifier().orElse(null)))
.forEach(bundleDependency -> {
checkAndUpdatePluginVersion(bundleDependency, plugins);
});
});
return plugins.values().stream().collect(toList());
}
private void checkAndUpdatePluginVersion(BundleDependency bundleDependency,
Map, BundleDependency> plugins) {
Pair key = descriptorToPair(bundleDependency);
if (plugins.containsKey(key)) {
BundleDependency existentDependency = plugins.get(key);
String existentBundleDescriptorVersion = existentDependency.getDescriptor().getVersion();
String bundleDescriptorVersion = bundleDependency.getDescriptor().getVersion();
if (areCompatibleVersions(existentBundleDescriptorVersion, bundleDescriptorVersion)) {
String highestVersion =
VersionChecker.getHighestVersion(existentBundleDescriptorVersion, bundleDescriptorVersion);
if (highestVersion.equals(bundleDescriptorVersion)) {
plugins.put(key, bundleDependency);
}
} else {
throw new RuntimeException(String.format("Incompatible versions between plugin %s and %s",
existentDependency.getDescriptor(),
bundleDependency.getDescriptor()));
}
} else {
plugins.put(key, bundleDependency);
}
}
private Pair descriptorToPair(BundleDependency bundleDependency) {
return of(bundleDependency.getDescriptor().getGroupId(), bundleDependency.getDescriptor().getArtifactId());
}
static BundleDependency artifactToBundleDependency(Artifact artifact, String scope) {
final BundleDescriptor.Builder bundleDescriptorBuilder = new BundleDescriptor.Builder()
.setArtifactId(artifact.getArtifactId())
.setGroupId(artifact.getGroupId())
.setVersion(artifact.getVersion())
.setBaseVersion(artifact.getBaseVersion())
.setType(artifact.getExtension());
String classifier = artifact.getClassifier();
if (!isEmpty(classifier)) {
bundleDescriptorBuilder.setClassifier(classifier);
}
BundleDependency.Builder builder = new BundleDependency.Builder()
.setDescriptor(bundleDescriptorBuilder.build())
.setScope(BundleScope.valueOf(scope.toUpperCase()));
if (!scope.equalsIgnoreCase("provided")) {
builder.setBundleUri(artifact.getFile().toURI());
}
return builder.build();
}
private ArtifactResult readArtifactDescriptor(Artifact artifact, AetherRepositoryState repositoryState)
throws ArtifactResolutionException {
return repositoryState.getSystem().resolveArtifact(repositoryState.getSession(),
new ArtifactRequest(artifact,
aetherResolutionContext.getRemoteRepositories(),
null));
}
@Override
public VersionRangeResult resolveVersionRange(BundleDescriptor bundleDescriptor) {
AetherRepositoryState repositoryState =
getRepositoryState(this.aetherResolutionContext.getLocalRepositoryLocation(), empty());
try {
VersionRange.createFromVersionSpec(bundleDescriptor.getVersion());
final org.eclipse.aether.resolution.VersionRangeResult versionRangeResult = repositoryState
.getSystem().resolveVersionRange(repositoryState.getSession(),
new VersionRangeRequest(createArtifactFromBundleDescriptor(bundleDescriptor),
aetherResolutionContext
.getRemoteRepositories(),
null));
return new DefaultVersionRangeResult(versionRangeResult);
} catch (InvalidVersionSpecificationException | VersionRangeResolutionException e) {
throw new RuntimeException(String.format("Couldn't resolve version range for bundleDescriptor: '%s'",
bundleDescriptor.toString(), e));
}
}
private List assemblyDependenciesFromPom(AetherRepositoryState repositoryState, Artifact artifact,
boolean enableTestDependencies, boolean enableProvidedDependencies)
throws RepositoryException {
Artifact defaultArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(),
null,
"pom",
artifact.getVersion() != null ? artifact.getVersion()
: artifact.getVersion());
LOGGER.info("About to fetch required dependencies for artifact: {}. This may take a while...", toId(defaultArtifact));
final CollectRequest collectRequest = new CollectRequest();
final ArtifactDescriptorResult artifactDescriptorResult =
repositoryState.getSystem().readArtifactDescriptor(repositoryState.getSession(),
new ArtifactDescriptorRequest(defaultArtifact, null, null)
.setRepositories(
aetherResolutionContext.getRemoteRepositories()));
List dependencies = artifactDescriptorResult.getDependencies();
List requestedDependencies = emptyList();
if (enableTestDependencies) {
requestedDependencies = dependencies.stream()
.filter(dependency -> dependency.getScope().equalsIgnoreCase(TEST))
.map(dependency -> dependency.setScope(COMPILE))
.collect(toList());
}
LOGGER.debug("Resolving dependencies for: {} from list of dependencies: {}", toId(defaultArtifact),
requestedDependencies);
// Include root dependency in order to get remote repositories from pom and allow aether to use selectors (mirrors, proxies, authentication).
collectRequest.setRoot(new Dependency(defaultArtifact, COMPILE));
collectRequest.setRootArtifact(defaultArtifact);
// Direct dependencies modified as merged and take precedence over the ones in pom
collectRequest.setDependencies(requestedDependencies);
collectRequest.setManagedDependencies(artifactDescriptorResult.getManagedDependencies());
collectRequest.setRepositories(ImmutableList.builder()
.addAll(aetherResolutionContext.getRemoteRepositories())
.build());
LOGGER
.debug("Collecting transitive dependencies of artifact: {} using request: {}", toId(defaultArtifact), collectRequest);
final CollectResult collectResult =
repositoryState.getSystem().collectDependencies(repositoryState.getSession(), collectRequest);
final DependencyRequest dependencyRequest = new DependencyRequest();
dependencyRequest.setRoot(collectResult.getRoot());
dependencyRequest.setCollectRequest(collectRequest);
LOGGER.debug("Collecting and resolving transitive dependencies of artifact: {}", toId(defaultArtifact));
final DependencyResult dependencyResult =
repositoryState.getSystem().resolveDependencies(repositoryState.getSession(), dependencyRequest);
PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
dependencyResult.getRoot().accept(nlg);
final List bundleDependencies = nlg.getNodes().stream()
.map(node -> artifactToBundleDependency(node.getArtifact(), node.getDependency().getScope()))
// root dependency is not wanted as result (always)
.skip(1)
.collect(toList());
if (enableProvidedDependencies) {
// Append direct dependencies declared as provided, due to Aether excludes them during the resolution
bundleDependencies.addAll(dependencies.stream()
.filter(dependency -> dependency.getScope().equalsIgnoreCase(PROVIDED))
.map(
dependency -> artifactToBundleDependency(dependency.getArtifact(), dependency.getScope()))
.collect(toList()));
}
return bundleDependencies;
}
private void logUnresolvedArtifacts(DependencyNode node, DependencyResolutionException e) {
List artifactResults = e.getResult().getArtifactResults().stream()
.filter(artifactResult -> !artifactResult.getExceptions().isEmpty()).collect(toList());
final List patternInclusion =
artifactResults.stream().map(artifactResult -> toId(artifactResult.getRequest().getArtifact()))
.collect(toList());
PathRecordingDependencyVisitor visitor =
new PathRecordingDependencyVisitor(new PatternInclusionsDependencyFilter(patternInclusion),
node.getArtifact() != null);
node.accept(visitor);
visitor.getPaths().stream().forEach(path -> {
List unresolvedArtifactPath =
path.stream().filter(dependencyNode -> dependencyNode.getArtifact() != null).collect(toList());
if (!unresolvedArtifactPath.isEmpty()) {
LOGGER.warn("Dependency path to not resolved artifacts -> " + unresolvedArtifactPath.toString());
}
});
}
private BundleDescriptor getBundleDescriptor(Model pomModel) {
final String version =
StringUtils.isNotBlank(pomModel.getVersion()) ? pomModel.getVersion() : pomModel.getParent().getVersion();
return new BundleDescriptor.Builder()
.setGroupId(StringUtils.isNotBlank(pomModel.getGroupId()) ? pomModel.getGroupId() : pomModel.getParent().getGroupId())
.setArtifactId(pomModel.getArtifactId())
.setVersion(version)
.setBaseVersion(version)
.setType(POM)
.build();
}
/**
* Custom implementation of a {@link WorkspaceReader} meant to be tightly used with the mule artifacts, where the POM file is
* inside the exploded artifact or the packaged artifact. For any other {@link Artifact} it will return values that will force
* the dependency mechanism to look for in a different {@link WorkspaceReader}.
*
* @since 1.0
*/
private class PomWorkspaceReader implements WorkspaceReader {
final WorkspaceRepository workspaceRepository;
private final BundleDescriptor bundleDescriptor;
private final Supplier pomFileSupplier;
/**
* @param bundleDescriptor artifact's descriptor to compare, so that resolves the file in {@link #findArtifact(Artifact)} when
* it matches with the {@link #bundleDescriptor}
* @param artifactFile the artifact file for which the reader is created
* @param pomFileSupplier supplier for the File of the {@link #bundleDescriptor}
*/
PomWorkspaceReader(BundleDescriptor bundleDescriptor, File artifactFile, Supplier pomFileSupplier) {
this.bundleDescriptor = bundleDescriptor;
this.workspaceRepository = new WorkspaceRepository(format("worskpace-repository-%s", artifactFile.getName()));
this.pomFileSupplier = pomFileSupplier;
}
@Override
public WorkspaceRepository getRepository() {
return workspaceRepository;
}
@Override
public File findArtifact(Artifact artifact) {
if (checkArtifact(artifact)) {
return pomFileSupplier.get();
}
return null;
}
@Override
public List findVersions(Artifact artifact) {
if (checkArtifact(artifact)) {
return singletonList(artifact.getVersion());
}
return emptyList();
}
private boolean checkArtifact(Artifact artifact) {
return this.bundleDescriptor.getGroupId().equals(artifact.getGroupId())
&& this.bundleDescriptor.getArtifactId().equals(artifact.getArtifactId())
&& this.bundleDescriptor.getVersion().equals(artifact.getVersion())
&& this.bundleDescriptor.getType().equals(artifact.getExtension());
}
}
/**
* Custom implementation of a {@link WorkspaceReader} meant to be adapt {@link MavenReactorResolver} to {@link WorkspaceReader}.
*
* @since 1.1
*/
private class MavenWorkspaceReaderAdapter implements WorkspaceReader {
private final WorkspaceRepository workspaceRepository;
private final MavenReactorResolver mavenReactorResolver;
MavenWorkspaceReaderAdapter(MavenReactorResolver mavenReactorResolver) {
this.workspaceRepository = new WorkspaceRepository(format("worskpace-repository-reactor"));
this.mavenReactorResolver = mavenReactorResolver;
}
@Override
public WorkspaceRepository getRepository() {
return workspaceRepository;
}
@Override
public File findArtifact(Artifact artifact) {
return mavenReactorResolver.findArtifact(toBundleDescriptor(artifact));
}
public BundleDescriptor toBundleDescriptor(Artifact artifact) {
return new BundleDescriptor.Builder()
.setGroupId(artifact.getGroupId())
.setArtifactId(artifact.getArtifactId())
.setVersion(artifact.getVersion())
.setBaseVersion(artifact.getBaseVersion())
.setType(artifact.getExtension())
.build();
}
@Override
public List findVersions(Artifact artifact) {
return mavenReactorResolver.findVersions(toBundleDescriptor(artifact));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy