aQute.bnd.repository.p2.provider.TargetIndexer Maven / Gradle / Ivy
package aQute.bnd.repository.p2.provider;
import static aQute.bnd.osgi.repository.ResourcesRepository.toResourcesRepository;
import static aQute.bnd.osgi.resource.ResourceUtils.toVersion;
import static aQute.lib.promise.PromiseCollectors.toPromise;
import static java.util.Collections.singleton;
import static java.util.stream.Collectors.toMap;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.TimeUnit;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.service.repository.Repository;
import org.osgi.util.promise.Promise;
import org.osgi.util.promise.PromiseFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aQute.bnd.http.HttpClient;
import aQute.bnd.osgi.repository.BridgeRepository;
import aQute.bnd.osgi.repository.ResourcesRepository;
import aQute.bnd.osgi.repository.XMLResourceGenerator;
import aQute.bnd.osgi.repository.XMLResourceParser;
import aQute.bnd.osgi.resource.CapabilityBuilder;
import aQute.bnd.osgi.resource.FilterBuilder;
import aQute.bnd.osgi.resource.RequirementBuilder;
import aQute.bnd.osgi.resource.ResourceBuilder;
import aQute.bnd.osgi.resource.ResourceUtils;
import aQute.bnd.osgi.resource.ResourceUtils.ContentCapability;
import aQute.bnd.osgi.resource.ResourceUtils.IdentityCapability;
import aQute.bnd.service.RepositoryPlugin.DownloadListener;
import aQute.bnd.util.repository.DownloadListenerPromise;
import aQute.bnd.version.Version;
import aQute.lib.io.IO;
import aQute.p2.api.Artifact;
import aQute.p2.provider.P2Impl;
import aQute.service.reporter.Reporter;
class TargetIndexer implements Closeable {
private final static Logger logger = LoggerFactory.getLogger(TargetIndexer.class);
private static final long MAX_STALE = TimeUnit.DAYS.toMillis(100);
private final Reporter reporter;
final File location;
private final HttpClient client;
private final PromiseFactory promiseFactory;
private final URI url;
private final String name;
private final String urlHash;
private final File indexFile;
private volatile BridgeRepository bridge;
private static final Resource RECOVERY = new ResourceBuilder().build();
private static final String P2_CAPABILITY_NAMESPACE = "bnd.p2";
private static final String MD5_ATTRIBUTE = "md5";
private static final Requirement MD5_REQUIREMENT = new RequirementBuilder(P2_CAPABILITY_NAMESPACE)
.addFilter(new FilterBuilder().isPresent(MD5_ATTRIBUTE))
.buildSyntheticRequirement();
TargetIndexer(Reporter reporter, File location, HttpClient client, URI url, String name) throws Exception {
this.reporter = reporter;
this.location = location;
this.indexFile = new File(location, "index.xml.gz");
this.client = client;
this.promiseFactory = client.promiseFactory();
this.url = url;
this.name = name;
this.urlHash = client.toName(url);
IO.mkdirs(this.location);
validate();
bridge = new BridgeRepository(readRepository(indexFile));
}
private void validate() {
if (!this.location.isDirectory())
throw new IllegalArgumentException("%s cannot be made a directory" + this.location);
}
File get(String bsn, Version version, Map properties, DownloadListener... listeners)
throws Exception {
Resource resource = getBridge().get(bsn, version);
if (resource == null)
return null;
ContentCapability contentCapability = ResourceUtils.getContentCapability(resource);
if (contentCapability == null)
return null;
URI url = contentCapability.url();
final File source = client.getCacheFileFor(url);
final File link = new File(location, bsn + "-" + version + ".jar");
IO.createSymbolicLinkOrCopy(link, source);
Promise go = client.build()
.useCache(MAX_STALE)
.async(url.toURL())
.map(file -> link);
if (listeners.length == 0)
return go.getValue();
new DownloadListenerPromise(reporter, name + ": get " + bsn + ";" + version + " " + url, go, listeners);
return link;
}
List list(String pattern) throws Exception {
return getBridge().list(pattern);
}
SortedSet versions(String bsn) throws Exception {
return getBridge().versions(bsn);
}
private Repository readRepository(File index) throws Exception {
if (index.isFile()) {
try (XMLResourceParser xp = new XMLResourceParser(index.toURI())) {
List resources = xp.parse();
if (urlHash.equals(xp.name())) {
return new ResourcesRepository(resources);
}
}
}
return save(readRepository());
}
private Repository readRepository() throws Exception {
Map knownResources = (getBridge() != null) ? getBridge().getRepository()
.findProviders(singleton(MD5_REQUIREMENT))
.get(MD5_REQUIREMENT)
.stream()
.collect(toMap(capability -> {
IdentityCapability identity = ResourceUtils.getIdentityCapability(capability.getResource());
return new ArtifactID(identity.osgi_identity(), identity.version(), (String) capability.getAttributes()
.get(MD5_ATTRIBUTE));
}, Capability::getResource, (u, v) -> v)) : new HashMap<>();
P2Impl p2 = new P2Impl(client, this.url, promiseFactory);
List artifacts = p2.getArtifacts();
Set visitedArtifacts = new HashSet<>(artifacts.size());
Set visitedURIs = new HashSet<>(artifacts.size());
Promise> all = artifacts.stream()
.map(a -> {
if (!visitedURIs.add(a.uri))
return null;
if (a.md5 != null) {
ArtifactID id = new ArtifactID(a.id, toVersion(a.version), a.md5);
if (!visitedArtifacts.add(id))
return null;
if (knownResources.containsKey(id)) {
return promiseFactory.resolved(knownResources.get(id));
}
}
return client.build()
.useCache(MAX_STALE)
.async(a.uri)
.map(file -> {
ResourceBuilder rb = new ResourceBuilder();
rb.addFile(file, a.uri);
if (a.md5 != null) {
rb.addCapability(
new CapabilityBuilder(P2_CAPABILITY_NAMESPACE).addAttribute(MD5_ATTRIBUTE, a.md5));
}
return rb.build();
})
.recover(failed -> {
logger.debug("{}: Failed to create resource for {}", name, a, failed.getFailure());
return RECOVERY;
});
})
.filter(Objects::nonNull)
.collect(toPromise(promiseFactory));
return all.map(resources -> resources.stream()
.filter(resource -> resource != RECOVERY)
.collect(toResourcesRepository()))
.getValue();
}
private Repository save(Repository repository) throws IOException, Exception {
XMLResourceGenerator xrg = new XMLResourceGenerator();
xrg.repository(repository)
.name(urlHash)
.save(indexFile);
return repository;
}
Map> findProviders(Collection extends Requirement> requirements) {
return getBridge().getRepository()
.findProviders(requirements);
}
public void refresh() throws Exception {
bridge = new BridgeRepository(save(readRepository()));
}
@Override
public void close() throws IOException {}
BridgeRepository getBridge() {
return bridge;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy