package aQute.bnd.repository.fileset;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.jar.JarFile;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.service.repository.ContentNamespace;
import org.osgi.util.function.Function;
import org.osgi.util.promise.Deferred;
import org.osgi.util.promise.Promise;
import org.osgi.util.promise.Promises;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Domain;
import aQute.bnd.osgi.repository.BaseRepository;
import aQute.bnd.osgi.repository.BridgeRepository;
import aQute.bnd.osgi.repository.ResourcesRepository;
import aQute.bnd.osgi.resource.CapabilityBuilder;
import aQute.bnd.osgi.resource.ResourceBuilder;
import aQute.bnd.osgi.resource.ResourceUtils;
import aQute.bnd.osgi.resource.ResourceUtils.ContentCapability;
import aQute.bnd.service.Plugin;
import aQute.bnd.service.RepositoryPlugin;
import aQute.bnd.util.repository.DownloadListenerPromise;
import aQute.bnd.version.Version;
import aQute.lib.exceptions.Exceptions;
import aQute.lib.strings.Strings;
import aQute.libg.cryptography.SHA256;
import aQute.service.reporter.Reporter;
public class FileSetRepository extends BaseRepository implements Plugin, RepositoryPlugin {
private final static Logger logger = LoggerFactory.getLogger(FileSetRepository.class);
private final String name;
private final Collection<File> files;
private final Deferred<BridgeRepository> repository;
private Reporter reporter;
public FileSetRepository(String name, Collection<File> files) throws Exception {
this.name = name;
this.files = files;
this.repository = new Deferred<>();
}
private Collection<File> getFiles() {
return files;
}
private BridgeRepository getBridge() throws Exception {
Promise<BridgeRepository> promise = repository.getPromise();
if (!promise.isDone()) {
repository.resolveWith(readFiles());
}
return promise.getValue();
}
private Promise<BridgeRepository> readFiles() {
List<Promise<Resource>> promises = new ArrayList<>(getFiles().size());
for (File file : getFiles()) {
promises.add(parse(Promises.resolved(file)));
}
Promise<List<Resource>> all = Promises.all(promises);
return all.map(new Function<List<Resource>,BridgeRepository>() {
@Override
public BridgeRepository apply(List<Resource> resources) {
try {
ResourcesRepository rr = new ResourcesRepository();
for (Resource r : resources) {
if (r != null) {
logger.debug("{}: adding resource {}", getName(), r);
rr.add(r);
}
}
return new BridgeRepository(rr);
} catch (Exception e) {
throw Exceptions.duck(e);
}
}
});
}
private Promise<Resource> parse(Promise<File> file) {
return file.map(new Function<File,Resource>() {
@Override
public Resource apply(File file) {
try {
if (!file.isFile()) {
return null;
}
ResourceBuilder rb = new ResourceBuilder();
try (JarFile jar = new JarFile(file)) {
Domain manifest = Domain.domain(jar.getManifest());
boolean hasIdentity = rb.addManifest(manifest);
if (!hasIdentity) {
return null;
}
} catch (Exception f) {
return null;
}
logger.debug("{}: parsing {}", getName(), file);
Attrs attrs = new Attrs();
attrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, file.toURI().toString());
attrs.putTyped(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE, file.length());
attrs.put(ContentNamespace.CONTENT_NAMESPACE, SHA256.digest(file).asHex());
rb.addCapability(CapabilityBuilder.createCapReqBuilder(ContentNamespace.CONTENT_NAMESPACE, attrs));
return rb.build();
} catch (Exception e) {
logger.debug("{}: failed to parse {}", getName(), file, e);
throw Exceptions.duck(e);
}
}
});
}
@Override
public File get(String bsn, Version version, Map<String,String> properties, DownloadListener... listeners)
throws Exception {
Promise<File> promise = get(bsn, version);
if (promise == null) {
return null;
}
if (listeners.length != 0) {
new DownloadListenerPromise(reporter, "Get " + bsn + "-" + version + " for " + getName(), promise,
listeners);
}
return promise.getValue();
}
private Promise<File> get(final String bsn, final Version version) throws Exception {
logger.debug("{}: get {} {}", getName(), bsn, version);
Resource resource = getBridge().get(bsn, version);
if (resource == null) {
logger.debug("{}: resource not found {} {}", getName(), bsn, version);
return null;
}
ContentCapability content = ResourceUtils.getContentCapability(resource);
if (content == null) {
logger.warn("{}: No content capability for {}", getName(), resource);
return null;
}
URI uri = content.url();
if (uri == null) {
logger.warn("{}: No content URI for {}", getName(), resource);
return null;
}
logger.debug("{}: get returning {}", getName(), uri);
return Promises.resolved(new File(uri));
}
@Override
public boolean canWrite() {
return false;
}
@Override
public PutResult put(InputStream stream, PutOptions options) throws Exception {
throw new UnsupportedOperationException("Read only");
}
@Override
public List<String> list(String pattern) throws Exception {
List<String> bsns = getBridge().list(pattern);
logger.debug("{}: list({}) {}", getName(), pattern, bsns);
return bsns;
}
@Override
public SortedSet<Version> versions(String bsn) throws Exception {
SortedSet<Version> versions = getBridge().versions(bsn);
logger.debug("{}: versions({}) {}", getName(), bsn, versions);
return versions;
}
@Override
public String getName() {
return name;
}
@Override
public String getLocation() {
try {
return Strings.join(list(null));
} catch (Exception e) {
return Strings.join(getFiles());
}
}
@Override
public String toString() {
return String.format("%s [%-40s r/w=%s]", getName(), getLocation(), canWrite());
}
@Override
public Map<Requirement,Collection<Capability>> findProviders(Collection< ? extends Requirement> requirements) {
try {
return getBridge().getRepository().findProviders(requirements);
} catch (Exception e) {
throw Exceptions.duck(e);
}
}
@Override
public void setProperties(Map<String,String> map) throws Exception {
// ignored
}
@Override
public void setReporter(Reporter reporter) {
this.reporter = reporter;
}
}