Repository: karaf-cave Updated Branches: refs/heads/master 033a21dff -> e0913bc7f
[KARAF-3937] Karaf Cave produces incomplete indexes Project: http://git-wip-us.apache.org/repos/asf/karaf-cave/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf-cave/commit/e0913bc7 Tree: http://git-wip-us.apache.org/repos/asf/karaf-cave/tree/e0913bc7 Diff: http://git-wip-us.apache.org/repos/asf/karaf-cave/diff/e0913bc7 Branch: refs/heads/master Commit: e0913bc7fed4dce549f9f18ca17b8060075ae7f1 Parents: 033a21d Author: Guillaume Nodet <[email protected]> Authored: Mon Aug 24 14:27:48 2015 +0200 Committer: Guillaume Nodet <[email protected]> Committed: Mon Aug 24 14:27:48 2015 +0200 ---------------------------------------------------------------------- .../cave/server/storage/CaveRepositoryImpl.java | 146 +++++++++++++------ 1 file changed, 101 insertions(+), 45 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf-cave/blob/e0913bc7/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java index f78c515..ebc210c 100644 --- a/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java +++ b/server/storage/src/main/java/org/apache/karaf/cave/server/storage/CaveRepositoryImpl.java @@ -16,7 +16,9 @@ */ package org.apache.karaf.cave.server.storage; +import javax.xml.stream.XMLStreamException; import java.io.File; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Writer; @@ -28,6 +30,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -38,8 +42,6 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import javax.xml.stream.XMLStreamException; - import org.apache.karaf.cave.server.api.CaveRepository; import org.apache.karaf.features.internal.resolver.ResolverUtil; import org.apache.karaf.features.internal.resolver.ResourceBuilder; @@ -56,6 +58,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.jar.JarFile.MANIFEST_NAME; +import static org.osgi.service.repository.ContentNamespace.CAPABILITY_MIME_ATTRIBUTE; +import static org.osgi.service.repository.ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE; import static org.osgi.service.repository.ContentNamespace.CAPABILITY_URL_ATTRIBUTE; import static org.osgi.service.repository.ContentNamespace.CONTENT_NAMESPACE; @@ -289,37 +293,107 @@ public class CaveRepositoryImpl implements CaveRepository { } } - private ResourceImpl createResource(URL url) throws BundleException, IOException { - return createResource(url, url.toExternalForm()); + private ResourceImpl createResource(URL url) throws BundleException, IOException, NoSuchAlgorithmException { + return createResource(url, url.toExternalForm(), true); } - private ResourceImpl createResource(URL url, String uri) throws BundleException, IOException { - Map<String, String> headers = getHeaders(url); - try { - ResourceImpl resource = ResourceBuilder.build(uri, headers); - useResourceRelativeUri(resource); - return resource; - } catch (BundleException e) { - throw new BundleException("Unable to create resource from " + uri + ": " + e.getMessage(), e); - } - } - - Map<String, String> getHeaders(URL url) throws IOException, BundleException { - try (InputStream is = url.openStream()) { + private ResourceImpl createResource(URL url, String uri, boolean readFully) throws BundleException, IOException, NoSuchAlgorithmException { + Map<String, String> headers = null; + String digest = null; + long size = -1; + // Find headers, compute length and checksum + try (ContentInputStream is = new ContentInputStream(url.openStream())) { ZipInputStream zis = new ZipInputStream(is); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (MANIFEST_NAME.equals(entry.getName())) { Attributes attributes = new Manifest(zis).getMainAttributes(); - Map<String, String> headers = new HashMap<>(); + headers = new HashMap<>(); for (Map.Entry attr : attributes.entrySet()) { headers.put(attr.getKey().toString(), attr.getValue().toString()); } - return headers; + if (!readFully) { + break; + } } } + if (readFully) { + digest = is.getDigest(); + size = is.getSize(); + } + } + if (headers == null) { + throw new BundleException("Resource " + url + " does not contain a manifest"); + } + // Fix the content directive + try { + ResourceImpl resource = ResourceBuilder.build(uri, headers); + for (Capability cap : resource.getCapabilities(null)) { + if (cap.getNamespace().equals(CONTENT_NAMESPACE)) { + String resourceURI = cap.getAttributes().get(CAPABILITY_URL_ATTRIBUTE).toString(); + String locationURI = "file:" + getLocation(); + LOGGER.debug("Converting resource URI {} relatively to repository URI {}", resourceURI, locationURI); + if (resourceURI.startsWith(locationURI)) { + resourceURI = resourceURI.substring(locationURI.length() + 1); + LOGGER.debug("Resource URI converted to " + resourceURI); + // This is a bit hacky, but the map is not read only + cap.getAttributes().put(CAPABILITY_URL_ATTRIBUTE, resourceURI); + } + if (readFully) { + cap.getAttributes().put(CONTENT_NAMESPACE, digest); + cap.getAttributes().put(CAPABILITY_SIZE_ATTRIBUTE, size); + } + cap.getAttributes().put(CAPABILITY_MIME_ATTRIBUTE, "application/vnd.osgi.bundle"); + break; + } + } + return resource; + } catch (BundleException e) { + throw new BundleException("Unable to create resource from " + uri + ": " + e.getMessage(), e); + } + } + + private static class ContentInputStream extends FilterInputStream { + final MessageDigest md; + long size = 0; + + public ContentInputStream(InputStream in) throws NoSuchAlgorithmException { + super(in); + md = MessageDigest.getInstance("SHA-256"); + } + + @Override + public int read() throws IOException { + int b = super.read(); + if (b >= 0) { + md.update((byte) b); + size++; + } + return b; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int length = super.read(b, off, len); + if (length > 0) { + md.update(b, off, length); + this.size += length; + } + return length; + } + + public String getDigest() { + byte[] digest = md.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + public long getSize() { + return size; } - throw new BundleException("Resource " + url + " does not contain a manifest"); } /** @@ -335,7 +409,9 @@ public class CaveRepositoryImpl implements CaveRepository { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); try (InputStream is = conn.getInputStream()) { String type = conn.getContentType(); - if ("application/java-archive".equals(type) || "application/octet-stream".equals(type)) { + if ("application/java-archive".equals(type) + || "application/octet-stream".equals(type) + || "application/vnd.osgi.bundle".equals(type)) { // I have a jar/binary, potentially a resource try { if ((filter == null) || (url.matches(filter))) { @@ -448,9 +524,12 @@ public class CaveRepositoryImpl implements CaveRepository { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); try (InputStream is = conn.getInputStream()) { String type = conn.getContentType(); - if ("application/java-archive".equals(type) || "application/octet-stream".equals(type)) { + if ("application/java-archive".equals(type) + || "application/octet-stream".equals(type) + || "application/vnd.osgi.bundle".equals(type)) { try { if ((filter == null) || (url.matches(filter))) { + // Make sure this is a valid bundle ResourceImpl resource = createResource(new URL(url)); LOGGER.debug("Copy {} into the Cave repository storage", url); int index = url.lastIndexOf("/"); @@ -482,32 +561,9 @@ public class CaveRepositoryImpl implements CaveRepository { } /** - * Convert the Resource absolute URI to an URI relative to the repository one. - * - * @param resource the Resource to manipulate. - */ - private void useResourceRelativeUri(ResourceImpl resource) { - for (Capability cap : resource.getCapabilities(null)) { - if (cap.getNamespace().equals(CONTENT_NAMESPACE)) { - String resourceURI = cap.getAttributes().get(CAPABILITY_URL_ATTRIBUTE).toString(); - String locationURI = "file:" + getLocation(); - LOGGER.debug("Converting resource URI {} relatively to repository URI {}", resourceURI, locationURI); - if (resourceURI.startsWith(locationURI)) { - resourceURI = resourceURI.substring(locationURI.length() + 1); - LOGGER.debug("Resource URI converted to " + resourceURI); - // This is a bit hacky, but the map is not read only - cap.getAttributes().put(CAPABILITY_URL_ATTRIBUTE, resourceURI); - } - break; - } - } - } - - /** * Get the File object of the repository.xml file. * * @return the File corresponding to the repository.xml. - * @throws Exception */ private Path getRepositoryXmlFile() { return getLocationPath().resolve("repository.xml");
