org.metaeffekt.artifact.resolver.ArtifactResolverManager Maven / Gradle / Ivy
/*
* Copyright 2021-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.metaeffekt.artifact.resolver;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.metaeffekt.artifact.resolver.alpine.AlpinePackageResolver;
import org.metaeffekt.artifact.resolver.deb.DebArtifactResolver;
import org.metaeffekt.artifact.resolver.download.WebAccess;
import org.metaeffekt.artifact.resolver.generic.ArtifactIndex;
import org.metaeffekt.artifact.resolver.generic.IndexConfig;
import org.metaeffekt.artifact.resolver.manager.DownloadEnvironmentManager;
import org.metaeffekt.artifact.resolver.maven.MavenArtifactResolver;
import org.metaeffekt.artifact.resolver.model.ArtifactPartResolver;
import org.metaeffekt.artifact.resolver.model.ArtifactPartType;
import org.metaeffekt.artifact.resolver.model.DownloadLocation;
import org.metaeffekt.artifact.resolver.model.ResolvedArtifactPart;
import org.metaeffekt.artifact.resolver.npm.NpmArtifactResolver;
import org.metaeffekt.artifact.resolver.pypi.PyPIArtifactResolver;
import org.metaeffekt.artifact.resolver.rpm.RpmArtifactResolver;
import org.metaeffekt.artifact.resolver.url.UrlArtifactResolver;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import java.util.*;
import java.util.stream.Collectors;
/**
* Main ArtifactResolver. Manages a configuration of artifact resolvers and runs them.
*/
@Slf4j
public class ArtifactResolverManager {
private final DownloadLocation downloadLocation;
private final WebAccess webAccess;
/**
* Resolvers that will be iterated through when an artifact is resolved.
*
* The order in which resolvers are added here might affect resolver results:
*
* - insertion order is iteration order
* - for each type, only the first successful resolver's result is used
* (see {@link #getResolvedArtifactParts(Artifact, Collection)})
*
*/
private final List artifactResolverList = new ArrayList<>();
/**
* Central configuration of resolver manager and resolvers.
*/
private ArtifactResolverConfig artifactResolverConfig;
/**
* Abstraction for our container runtime. Manages execution environments for different configurations.
*
* Keeping this around for debugging and resource management purposes.
*/
private final DownloadEnvironmentManager environmentManager;
public ArtifactResolverManager(@NonNull DownloadLocation downloadLocation,
@NonNull WebAccess webAccess,
@NonNull ArtifactResolverConfig resolverConfig) {
this.downloadLocation = downloadLocation;
this.webAccess = webAccess;
this.artifactResolverConfig = resolverConfig;
final IndexConfig indexConfig = artifactResolverConfig.getIndexConfig();
final ArtifactIndex artifactIndex = new ArtifactIndex(indexConfig);
artifactResolverList.add(new MavenArtifactResolver(downloadLocation, webAccess, artifactIndex, artifactResolverConfig.getMavenConfig()));
artifactResolverList.add(new NpmArtifactResolver(downloadLocation, webAccess));
artifactResolverList.add(new UrlArtifactResolver(downloadLocation, webAccess));
artifactResolverList.add(new PyPIArtifactResolver(downloadLocation));
if (artifactResolverConfig.getRpmIndexConfig() != null) {
artifactResolverList.add(new RpmArtifactResolver(downloadLocation, webAccess, artifactResolverConfig.getRpmIndexConfig()));
}
if (artifactResolverConfig.getDebArtifactResolverConfig() != null) {
artifactResolverList.add(new DebArtifactResolver(downloadLocation, webAccess, artifactResolverConfig.getDebArtifactResolverConfig()));
}
// add environment-based resolvers
if (resolverConfig.getEnvironmentManagerConfig() != null &&
resolverConfig.getEnvironmentManagerConfig().isEnableEnvironmentManager()) {
this.environmentManager =
DownloadEnvironmentManager.createIfAvailable(resolverConfig.getEnvironmentManagerConfig());
if (this.environmentManager != null) {
artifactResolverList.add(
new AlpinePackageResolver(downloadLocation, webAccess, environmentManager, resolverConfig));
}
} else {
environmentManager = null;
}
}
/**
* Resolves the binary form for the given artifact.
*
* If you wish to resolve multiple types, consider using
* {@link #getResolvedArtifactParts(Artifact, Collection) getResolvedArtifactParts}
*
* @param artifact The artifact to resolve. Various attributes may be used to feature the resolution.
*
* @return a resolved part object for whatever was resolved
*/
public ResolvedArtifactPart resolveBinaryArtifact(Artifact artifact) {
return getResolvedArtifactPart(artifact, ArtifactPartType.BINARY_ARTIFACT);
}
public ResolvedArtifactPart resolveSourceArtifact(Artifact artifact) {
return getResolvedArtifactPart(artifact, ArtifactPartType.SOURCE_ARTIFACT);
}
public ResolvedArtifactPart resolveSourceArchive(Artifact artifact) {
return getResolvedArtifactPart(artifact, ArtifactPartType.SOURCE_ARCHIVE);
}
public ResolvedArtifactPart resolveDescriptor(Artifact artifact) {
return getResolvedArtifactPart(artifact, ArtifactPartType.DESCRIPTOR);
}
/**
* Resolves the first artifact resolver for the given artifact and type.
*
* Backwards-compatibly with the old getResolvedArtifactPart, this now calls
* {@link #getResolvedArtifactParts(Artifact, Collection) getResolvedArtifactParts}.
* @param artifact artifact to resolve
* @param artifactPartType type of resolver to execute
* @return result of the first resolver that worked for this artifact.
*/
private ResolvedArtifactPart getResolvedArtifactPart(@NonNull Artifact artifact,
@NonNull ArtifactPartType artifactPartType) {
return getResolvedArtifactParts(artifact, Collections.singleton(artifactPartType)).get(artifactPartType);
}
/**
* Get and resolve all available resolver parts (one per type).
* @see #getResolvedArtifactParts(Artifact, Collection)
* @param artifact artifact to resolve
* @return map of partType to resolved part
*/
public SortedMap getResolvedArtifactParts(
@NonNull final Artifact artifact) {
return getResolvedArtifactParts(artifact, null);
}
/**
* Get and resolve all available resolver parts (one per type).
* @param artifact artifact to resolve
* @param partTypes only resolve given partTypes. Null to resolve all.
* @return map of partType to resolved part
*/
public SortedMap getResolvedArtifactParts(
@NonNull final Artifact artifact,
Collection partTypes) {
return processPartResolvers(collectArtifactPartResolvers(artifact, partTypes));
}
/**
* Runs part resolvers and returns the result.
* @param partResolvers collection of part resolvers to run
* @return result of all resolve operations
*/
private SortedMap processPartResolvers(
@NonNull List partResolvers) {
final SortedMap typeToResolved = new TreeMap<>();
for (ArtifactPartResolver partResolver : partResolvers) {
// don't compute if the map already registered this part as resolved
if (typeToResolved.get(partResolver.getArtifactPartType()) != null) {
continue;
}
final ResolvedArtifactPart resolvedArtifactPart = partResolver.get();
if (resolvedArtifactPart == null) {
// skip if resolve was unsuccessful
continue;
}
// write successful resolve to map
typeToResolved.put(partResolver.getArtifactPartType(), resolvedArtifactPart);
}
return typeToResolved;
}
/**
* Runs resolution on all parts of an artifact and merges results into the original artifact.
*
* @param artifact artifact to resolve
*/
public void resolveAndMergeAll(@NonNull Artifact artifact) {
final Artifact clone = new Artifact(artifact);
final SortedMap resolvedParts = getResolvedArtifactParts(clone, null);
// match certain order for these types for historic reasons. we then remove used ones from the map.
merge(artifact, resolvedParts.remove(ArtifactPartType.DESCRIPTOR));
merge(artifact, resolvedParts.remove(ArtifactPartType.BINARY_ARTIFACT));
merge(artifact, resolvedParts.remove(ArtifactPartType.SOURCE_ARTIFACT));
merge(artifact, resolvedParts.remove(ArtifactPartType.SOURCE_ARCHIVE));
// merge types that were not removed yet (not merged yet) in enum-given order (sorted map)
for (ResolvedArtifactPart resolvedPart : resolvedParts.values()) {
merge(artifact, resolvedPart);
}
}
/**
* Cheaply merge enriched results back into the original artifact using {@link Artifact#merge(Artifact)}.
*
* @param artifact artifact to be modified (merged into)
* @param resolveArtifactPart part holding an enriched artifact to merge
*/
private void merge(@NonNull Artifact artifact, ResolvedArtifactPart resolveArtifactPart) {
if (resolveArtifactPart != null) {
final Artifact enrichedArtifact = resolveArtifactPart.getEnrichedArtifact();
if (enrichedArtifact != null) {
artifact.merge(enrichedArtifact);
}
}
}
/**
* Gets all part resolvers for an artifact, filtering for the given types.
* @param artifact artifact to resolve
* @param partTypes types to filter for. {@code null} disables filtering.
* @return a list of resolvers
*/
private List collectArtifactPartResolvers(@NonNull final Artifact artifact,
final Collection partTypes) {
EnumSet partTypeSet = partTypes == null ? null : EnumSet.copyOf(partTypes);
return artifactResolverList.stream()
.map(artifactResolver -> artifactResolver.collectResolvers(artifact))
.flatMap(resolvers -> resolvers.getArtifactPartResolvers().stream())
.filter(partResolver -> partTypeSet == null || partTypeSet.contains(partResolver.getArtifactPartType()))
.collect(Collectors.toList());
}
public boolean isEnvironmentEnabled() {
return environmentManager != null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy