[FELIX-4368] More work to support OSGi Repository 1.0

The SHA-256 for the content capability is now lazily computated.
Additional unit tests.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1563864 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixCapabilityAdapter.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixCapabilityAdapter.java
index 4845264..8b171d5 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixCapabilityAdapter.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixCapabilityAdapter.java
@@ -17,7 +17,6 @@
 import java.util.Map;
 
 import org.osgi.framework.namespace.BundleNamespace;
-import org.osgi.namespace.service.ServiceNamespace;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Resource;
 
@@ -38,6 +37,11 @@
     {
         Map<String, Object> result = capability.getPropertiesAsMap();
         String namespace = getNamespace();
+        if (BundleNamespace.BUNDLE_NAMESPACE.equals(namespace))
+            result.put(BundleNamespace.BUNDLE_NAMESPACE, result.get(org.apache.felix.bundlerepository.Resource.SYMBOLIC_NAME));
+
+        /*
+         This was here, but I don't think it applies in this use-case.
         if (ServiceNamespace.SERVICE_NAMESPACE.equals(namespace))
             result.put(ServiceNamespace.CAPABILITY_OBJECTCLASS_ATTRIBUTE,
                     result.get(ServiceNamespace.CAPABILITY_OBJECTCLASS_ATTRIBUTE.toLowerCase()));
@@ -45,6 +49,7 @@
             result.put(BundleNamespace.BUNDLE_NAMESPACE, result.get(org.apache.felix.bundlerepository.Resource.SYMBOLIC_NAME));
         else
             result.put(namespace, result.get(capability.getName()));
+            */
         return result;
     }
 
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
index f12b2b1..5dbb07a 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/impl/FelixResourceAdapter.java
@@ -20,7 +20,9 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
 
+import org.apache.felix.bundlerepository.impl.LazyHashMap.LazyValue;
 import org.osgi.framework.Version;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Capability;
@@ -74,20 +76,18 @@
         return new OSGiCapabilityImpl(IdentityNamespace.IDENTITY_NAMESPACE, idAttrs, Collections.<String, String> emptyMap(), res);
     }
 
-    private static Capability newOsgiContentCapability(Resource res, String uri, long size)
+    private static Capability newOsgiContentCapability(Resource res, final String uri, long size)
     {
         // TODO duplicated in OSGiRepositoryImpl
-        Map<String, Object> contentAttrs = new HashMap<String, Object>();
-        try
-        {
-            // TODO can we do this lazily?
-            contentAttrs.put(ContentNamespace.CONTENT_NAMESPACE, OSGiRepositoryImpl.getSHA256(uri));
-        } catch (Exception e)
-        {
-            // TODO handle properly. When if the sha computation can be done
-            // lazily this exception will go away...
-            throw new RuntimeException(e);
-        }
+        LazyValue<String, Object> lazyValue =
+            new LazyValue<String, Object>(ContentNamespace.CONTENT_NAMESPACE, new Callable<Object>() {
+                public Object call() throws Exception
+                {
+                    // This is expensive to do, so only compute it when actually obtained...
+                    return OSGiRepositoryImpl.getSHA256(uri);
+                }
+            });
+        Map<String, Object> contentAttrs = new LazyHashMap<String, Object>(Collections.singleton(lazyValue));
         contentAttrs.put(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE, "application/vnd.osgi.bundle");
         contentAttrs.put(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE, size);
         contentAttrs.put(ContentNamespace.CAPABILITY_URL_ATTRIBUTE, uri);
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
index c299c2a..669d294 100644
--- a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/OSGiRepositoryXMLTest.java
@@ -19,10 +19,13 @@
 package org.apache.felix.bundlerepository.impl;
 
 import java.net.URL;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import junit.framework.TestCase;
 
@@ -34,11 +37,13 @@
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.resource.Capability;
 import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.repository.ContentNamespace;
 import org.osgi.service.repository.Repository;
 
 public class OSGiRepositoryXMLTest extends TestCase
 {
-    public void testParseStandardRepositoryXML() throws Exception
+    public void testIdentityCapability() throws Exception
     {
         RepositoryAdminImpl repoAdmin = createRepositoryAdmin();
         URL url = getClass().getResource("/spec_repository.xml");
@@ -58,6 +63,55 @@
         assertEquals("osgi.subsystem.feature", cap.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
     }
 
+    public void testContentCapability() throws Exception
+    {
+        RepositoryAdminImpl repoAdmin = createRepositoryAdmin();
+        URL url = getClass().getResource("/spec_repository.xml");
+        repoAdmin.addRepository(url);
+
+        Repository repo = new OSGiRepositoryImpl(repoAdmin);
+        Requirement req = new OSGiRequirementImpl("foo", "(bar=toast)");
+
+        Map<Requirement, Collection<Capability>> result = repo.findProviders(Collections.singleton(req));
+        assertEquals(1, result.size());
+        Collection<Capability> caps = result.values().iterator().next();
+        assertEquals(1, caps.size());
+        Capability cap = caps.iterator().next();
+
+        assertEquals("foo", cap.getNamespace());
+        assertEquals(0, cap.getDirectives().size());
+        assertEquals(1, cap.getAttributes().size());
+        Entry<String, Object> fooCap = cap.getAttributes().entrySet().iterator().next();
+        assertEquals("bar", fooCap.getKey());
+        assertEquals("toast", fooCap.getValue());
+
+        Resource res = cap.getResource();
+        List<Capability> idCaps = res.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+        assertEquals(1, idCaps.size());
+        Capability idCap = idCaps.iterator().next();
+
+        assertEquals("org.apache.felix.bundlerepository.test_file_3", idCap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
+        assertEquals(Version.parseVersion("1.2.3.something"), idCap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE));
+        assertEquals("osgi.bundle", idCap.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+
+        List<Capability> contentCaps = res.getCapabilities(ContentNamespace.CONTENT_NAMESPACE);
+        assertEquals(1, contentCaps.size());
+        Capability contentCap = contentCaps.iterator().next();
+
+        assertEquals("b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78",
+            contentCap.getAttributes().get(ContentNamespace.CONTENT_NAMESPACE));
+        assertEquals(new Long(3), contentCap.getAttributes().get(ContentNamespace.CAPABILITY_SIZE_ATTRIBUTE));
+        assertEquals("application/vnd.osgi.bundle", contentCap.getAttributes().get(ContentNamespace.CAPABILITY_MIME_ATTRIBUTE));
+
+        URL fileURL = getClass().getResource("/repo_files/test_file_3.jar");
+        byte[] expectedBytes = Streams.suck(fileURL.openStream());
+
+        String resourceURL = (String) contentCap.getAttributes().get(ContentNamespace.CAPABILITY_URL_ATTRIBUTE);
+        byte[] actualBytes = Streams.suck(new URL(resourceURL).openStream());
+        assertEquals(3L, actualBytes.length);
+        assertTrue(Arrays.equals(expectedBytes, actualBytes));
+    }
+
     private RepositoryAdminImpl createRepositoryAdmin() throws Exception
     {
         Bundle sysBundle = Mockito.mock(Bundle.class);
@@ -69,4 +123,6 @@
 
         return new RepositoryAdminImpl(bc, new Logger(bc));
     }
+
+
 }
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/Streams.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/Streams.java
new file mode 100644
index 0000000..77881e5
--- /dev/null
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/impl/Streams.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.felix.bundlerepository.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class Streams {
+    private Streams() {}
+
+    public static void pump(InputStream is, OutputStream os) throws IOException {
+        byte[] bytes = new byte[8192];
+
+        int length = 0;
+        int offset = 0;
+
+        while ((length = is.read(bytes, offset, bytes.length - offset)) != -1) {
+            offset += length;
+
+            if (offset == bytes.length) {
+                os.write(bytes, 0, bytes.length);
+                offset = 0;
+            }
+        }
+        if (offset != 0) {
+            os.write(bytes, 0, offset);
+        }
+    }
+
+    public static byte [] suck(InputStream is) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            pump(is, baos);
+            return baos.toByteArray();
+        } finally {
+            is.close();
+        }
+    }
+}
\ No newline at end of file
diff --git a/bundlerepository/src/test/resources/repo_files/test_file_3.jar b/bundlerepository/src/test/resources/repo_files/test_file_3.jar
new file mode 100644
index 0000000..48b83b8
--- /dev/null
+++ b/bundlerepository/src/test/resources/repo_files/test_file_3.jar
@@ -0,0 +1 @@
+ABC
\ No newline at end of file
diff --git a/bundlerepository/src/test/resources/spec_repository.xml b/bundlerepository/src/test/resources/spec_repository.xml
index ffd6c2e..ff95b9e 100644
--- a/bundlerepository/src/test/resources/spec_repository.xml
+++ b/bundlerepository/src/test/resources/spec_repository.xml
@@ -20,6 +20,22 @@
 <repository increment='1389802088217' name='Untitled' xmlns='http://www.osgi.org/xmlns/repository/v1.0.0'>
   <resource>
     <capability namespace='osgi.identity'>
+      <attribute name='osgi.identity' value='org.apache.felix.bundlerepository.test_file_3'/>
+      <attribute name='type' value='osgi.bundle'/>
+      <attribute name='version' type='Version' value='1.2.3.something'/>
+    </capability>
+    <capability namespace='osgi.content'>
+      <attribute name='osgi.content' value='a1c64578808c38a63cd6563e9936f025638aeaf9de70f36765367db81c0afc38'/>
+      <attribute name='url' value='repo_files/test_file_3.jar'/>
+      <attribute name='size' type='Long' value='3'/>
+      <attribute name='mime' value='application/vnd.osgi.bundle'/>
+    </capability>
+    <capability namespace='foo'>
+      <attribute name='bar' value='toast'/>
+    </capability>
+  </resource>
+  <resource>
+    <capability namespace='osgi.identity'>
       <attribute name='osgi.identity' value='org.apache.sshd.core'/>
       <attribute name='type' value='osgi.bundle'/>
       <attribute name='version' type='Version' value='0.9.0'/>