FELIX-4504 Support Core R6 Framework DTOs

Some unit tests included. This code passes all the Core R6 DTO tests.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1640076 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 280bc85..b9ac3ab 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -42,6 +42,7 @@
 import org.apache.felix.framework.util.ShrinkableCollection;
 import org.apache.felix.framework.util.StringMap;
 import org.apache.felix.framework.util.Util;
+import org.osgi.dto.DTO;
 import org.osgi.framework.AdaptPermission;
 import org.osgi.framework.AdminPermission;
 import org.osgi.framework.Bundle;
@@ -1114,6 +1115,11 @@
             return (A) new AccessControlContext(new ProtectionDomain[] {pd});
 
         }
+        else if (DTO.class.isAssignableFrom(type) ||
+                DTO[].class.isAssignableFrom(type))
+        {
+            return DTOFactory.createDTO(this, type);
+        }
         return null;
     }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/DTOFactory.java b/framework/src/main/java/org/apache/felix/framework/DTOFactory.java
new file mode 100644
index 0000000..e3cb64f
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/DTOFactory.java
@@ -0,0 +1,480 @@
+/*
+ * 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.framework;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.dto.FrameworkDTO;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.startlevel.dto.BundleStartLevelDTO;
+import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.wiring.dto.BundleRevisionDTO;
+import org.osgi.framework.wiring.dto.BundleWireDTO;
+import org.osgi.framework.wiring.dto.BundleWiringDTO;
+import org.osgi.framework.wiring.dto.BundleWiringDTO.NodeDTO;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.resource.Wiring;
+import org.osgi.resource.dto.CapabilityDTO;
+import org.osgi.resource.dto.CapabilityRefDTO;
+import org.osgi.resource.dto.RequirementDTO;
+import org.osgi.resource.dto.RequirementRefDTO;
+import org.osgi.resource.dto.WireDTO;
+
+/**
+ * Creates various DTOs provided by the core framework.
+ */
+public class DTOFactory
+{
+    private DTOFactory()
+    {
+        // Only static methods
+    }
+
+    static <T> T createDTO(Bundle bundle, Class<T> type)
+    {
+        if (Bundle.UNINSTALLED == bundle.getState())
+            return null;
+
+        if (type == BundleDTO.class)
+        {
+            return type.cast(createBundleDTO(bundle));
+        }
+        else if (type == BundleStartLevelDTO.class)
+        {
+            return type.cast(createBundleStartLevelDTO(bundle));
+        }
+        else if (type == BundleRevisionDTO.class)
+        {
+            return type.cast(createBundleRevisionDTO(bundle));
+        }
+        else if (type == BundleRevisionDTO[].class)
+        {
+            return type.cast(createBundleRevisionDTOArray(bundle));
+        }
+        else if (type == BundleWiringDTO.class)
+        {
+            return type.cast(createBundleWiringDTO(bundle));
+        }
+        else if (type == BundleWiringDTO[].class)
+        {
+            return type.cast(createBundleWiringDTOArray(bundle));
+        }
+        else if (type == ServiceReferenceDTO[].class)
+        {
+            return type.cast(createServiceReferenceDTOArray(bundle));
+        }
+        else if (type == FrameworkDTO.class && bundle instanceof Felix)
+        {
+            return type.cast(createFrameworkDTO((Felix) bundle));
+        }
+        else if (type == FrameworkStartLevelDTO.class && bundle instanceof Framework)
+        {
+            return type.cast(createFrameworkStartLevelDTO((Framework) bundle));
+        }
+        return null;
+    }
+
+    private static BundleDTO createBundleDTO(Bundle bundle)
+    {
+        BundleDTO dto = new BundleDTO();
+        dto.id = bundle.getBundleId();
+        dto.lastModified = bundle.getLastModified();
+        dto.state = bundle.getState();
+        dto.symbolicName = bundle.getSymbolicName();
+        dto.version = "" + bundle.getVersion();
+        return dto;
+    }
+
+    private static BundleRevisionDTO createBundleRevisionDTO(Bundle bundle)
+    {
+        BundleRevision br = bundle.adapt(BundleRevision.class);
+        if (!(br instanceof BundleRevisionImpl))
+            return null;
+
+        return createBundleRevisionDTO(bundle, (BundleRevisionImpl) br, new HashSet<BundleRevisionDTO>());
+    }
+
+    private static BundleRevisionDTO[] createBundleRevisionDTOArray(Bundle bundle)
+    {
+        BundleRevisions brs = bundle.adapt(BundleRevisions.class);
+        if (brs == null || brs.getRevisions() == null)
+            return null;
+
+        List<BundleRevision> revisions = brs.getRevisions();
+        BundleRevisionDTO[] dtos = new BundleRevisionDTO[revisions.size()];
+        for (int i=0; i < revisions.size(); i++)
+        {
+            if (revisions.get(i) instanceof BundleRevisionImpl)
+                dtos[i] = createBundleRevisionDTO(bundle, (BundleRevisionImpl) revisions.get(i), new HashSet<BundleRevisionDTO>());
+        }
+        return dtos;
+    }
+
+    private static BundleRevisionDTO createBundleRevisionDTO(BundleRevision revision, Set<BundleRevisionDTO> resources)
+    {
+        if (revision instanceof BundleRevisionImpl)
+            return createBundleRevisionDTO(revision.getBundle(), (BundleRevisionImpl) revision, resources);
+        else
+            return null;
+    }
+
+    private static BundleRevisionDTO createBundleRevisionDTO(Bundle bundle, BundleRevisionImpl revision, Set<BundleRevisionDTO> resources)
+    {
+        BundleRevisionDTO dto = new BundleRevisionDTO();
+        dto.id = getRevisionID(revision);
+        addBundleRevisionDTO(dto, resources);
+
+        dto.bundle = bundle.getBundleId();
+        dto.symbolicName = revision.getSymbolicName();
+        dto.type = revision.getTypes();
+        dto.version = revision.getVersion().toString();
+
+        dto.capabilities = new ArrayList<CapabilityDTO>();
+        for (Capability cap : revision.getCapabilities(null))
+        {
+            CapabilityDTO cdto = new CapabilityDTO();
+            cdto.id = getCapabilityID(cap);
+            cdto.namespace = cap.getNamespace();
+            cdto.attributes = convertAttrsToDTO(cap.getAttributes());
+            cdto.directives = new HashMap<String, String>(cap.getDirectives());
+            cdto.resource = getResourceIDAndAdd(cap.getResource(), resources);
+
+            dto.capabilities.add(cdto);
+        }
+
+        dto.requirements = new ArrayList<RequirementDTO>();
+        for (Requirement req : revision.getRequirements(null))
+        {
+            RequirementDTO rdto = new RequirementDTO();
+            rdto.id = getRequirementID(req);
+            rdto.namespace = req.getNamespace();
+            rdto.attributes = convertAttrsToDTO(req.getAttributes());
+            rdto.directives = new HashMap<String, String>(req.getDirectives());
+            rdto.resource = getResourceIDAndAdd(req.getResource(), resources);
+
+            dto.requirements.add(rdto);
+        }
+        return dto;
+    }
+
+    private static BundleWiringDTO createBundleWiringDTO(Bundle bundle)
+    {
+        BundleWiring bw = bundle.adapt(BundleWiring.class);
+        return createBundleWiringDTO(bw);
+    }
+
+    private static BundleWiringDTO createBundleWiringDTO(BundleWiring wiring)
+    {
+        BundleWiringDTO dto = new BundleWiringDTO();
+        dto.bundle = wiring.getBundle().getBundleId();
+        dto.root = getWiringID(wiring);
+        dto.nodes = new HashSet<BundleWiringDTO.NodeDTO>();
+        dto.resources = new HashSet<BundleRevisionDTO>();
+
+        createBundleRevisionDTO(wiring.getRevision(), dto.resources);
+        createBundleWiringNodeDTO(wiring, dto.resources, dto.nodes);
+
+        return dto;
+    }
+
+    private static BundleWiringDTO[] createBundleWiringDTOArray(Bundle bundle)
+    {
+        BundleRevisions brs = bundle.adapt(BundleRevisions.class);
+        if (brs == null || brs.getRevisions() == null)
+            return null;
+
+        List<BundleRevision> revisions = brs.getRevisions();
+        BundleWiringDTO[] dtos = new BundleWiringDTO[revisions.size()];
+        for (int i=0; i < revisions.size(); i++)
+        {
+            BundleWiring wiring = revisions.get(i).getWiring();
+            dtos[i] = createBundleWiringDTO(wiring);
+        }
+        return dtos;
+    }
+
+    private static void createBundleWiringNodeDTO(BundleWiring bw, Set<BundleRevisionDTO> resources, Set<NodeDTO> nodes)
+    {
+        NodeDTO node = new BundleWiringDTO.NodeDTO();
+        node.id = getWiringID(bw);
+        nodes.add(node);
+
+        node.current = bw.isCurrent();
+        node.inUse = bw.isInUse();
+        node.resource = getResourceIDAndAdd(bw.getResource(), resources);
+
+        node.capabilities = new ArrayList<CapabilityRefDTO>();
+        for (Capability cap : bw.getCapabilities(null))
+        {
+            CapabilityRefDTO cdto = new CapabilityRefDTO();
+            cdto.capability = getCapabilityID(cap);
+            cdto.resource = getResourceIDAndAdd(cap.getResource(), resources);
+            node.capabilities.add(cdto);
+        }
+
+        node.requirements = new ArrayList<RequirementRefDTO>();
+        for (Requirement req : bw.getRequirements(null))
+        {
+            RequirementRefDTO rdto = new RequirementRefDTO();
+            rdto.requirement = getRequirementID(req);
+            rdto.resource = getResourceIDAndAdd(req.getResource(), resources);
+            node.requirements.add(rdto);
+        }
+
+        node.providedWires = new ArrayList<WireDTO>();
+        for (Wire pw : bw.getProvidedWires(null))
+        {
+            node.providedWires.add(createBundleWireDTO(pw, resources, nodes));
+        }
+
+        node.requiredWires = new ArrayList<WireDTO>();
+        for (Wire rw : bw.getRequiredWires(null))
+        {
+            node.requiredWires.add(createBundleWireDTO(rw, resources, nodes));
+        }
+    }
+
+    private static BundleWireDTO createBundleWireDTO(Wire wire, Set<BundleRevisionDTO> resources, Set<NodeDTO> nodes)
+    {
+        BundleWireDTO wdto = new BundleWireDTO();
+        if (wire instanceof BundleWire)
+        {
+            BundleWire w = (BundleWire) wire;
+
+            BundleWiring pw = w.getProviderWiring();
+            addWiringNodeIfNotPresent(pw, resources, nodes);
+            wdto.providerWiring = getWiringID(pw);
+
+            BundleWiring rw = w.getRequirerWiring();
+            addWiringNodeIfNotPresent(rw, resources, nodes);
+            wdto.requirerWiring = getWiringID(rw);
+        }
+        wdto.provider = getResourceIDAndAdd(wire.getProvider(), resources);
+        wdto.requirer = getResourceIDAndAdd(wire.getRequirer(), resources);
+        wdto.capability = new CapabilityRefDTO();
+        wdto.capability.capability = getCapabilityID(wire.getCapability());
+        wdto.capability.resource = getResourceIDAndAdd(wire.getCapability().getResource(), resources);
+        wdto.requirement = new RequirementRefDTO();
+        wdto.requirement.requirement = getRequirementID(wire.getRequirement());
+        wdto.requirement.resource = getResourceIDAndAdd(wire.getRequirement().getResource(), resources);
+        return wdto;
+    }
+
+    private static BundleStartLevelDTO createBundleStartLevelDTO(Bundle bundle)
+    {
+        BundleStartLevelDTO dto = new BundleStartLevelDTO();
+        dto.bundle = bundle.getBundleId();
+
+        BundleStartLevel sl = bundle.adapt(BundleStartLevel.class);
+        dto.activationPolicyUsed = sl.isActivationPolicyUsed();
+        dto.persistentlyStarted = sl.isPersistentlyStarted();
+        dto.startLevel = sl.getStartLevel();
+
+        return dto;
+    }
+
+    private static ServiceReferenceDTO[] createServiceReferenceDTOArray(Bundle bundle)
+    {
+        BundleContext ctx = bundle.getBundleContext();
+        if (ctx == null)
+            return null;
+
+        ServiceReference<?>[] svcs = bundle.getRegisteredServices();
+        if (svcs == null)
+            return new ServiceReferenceDTO[0];
+
+        ServiceReferenceDTO[] dtos = new ServiceReferenceDTO[svcs.length];
+        for (int i=0; i < svcs.length; i++)
+        {
+            dtos[i] = createServiceReferenceDTO(svcs[i]);
+        }
+        return dtos;
+    }
+
+    private static ServiceReferenceDTO createServiceReferenceDTO(ServiceReference<?> svc)
+    {
+        ServiceReferenceDTO dto = new ServiceReferenceDTO();
+        dto.bundle = svc.getBundle().getBundleId();
+        dto.id = (Long) svc.getProperty(Constants.SERVICE_ID);
+        Map<String, Object> props = new HashMap<String, Object>();
+        for (String key : svc.getPropertyKeys())
+        {
+            props.put(key, svc.getProperty(key));
+        }
+        dto.properties = new HashMap<String, Object>(props);
+
+        Bundle[] ubs = svc.getUsingBundles();
+        if (ubs == null)
+        {
+            dto.usingBundles = new long[0];
+        }
+        else
+        {
+            dto.usingBundles = new long[ubs.length];
+            for (int j=0; j < ubs.length; j++)
+            {
+                dto.usingBundles[j] = ubs[j].getBundleId();
+            }
+        }
+        return dto;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static FrameworkDTO createFrameworkDTO(Felix framework)
+    {
+        FrameworkDTO dto = new FrameworkDTO();
+        dto.properties = framework.getConfig(); // This map is immutable, so it's fine to share
+
+        dto.bundles = new ArrayList<BundleDTO>();
+        for (Bundle b : framework.getBundleContext().getBundles())
+        {
+            dto.bundles.add(DTOFactory.createDTO(b, BundleDTO.class));
+        }
+
+        dto.services = new ArrayList<ServiceReferenceDTO>();
+
+        ServiceReference<?>[] refs = null;
+        try
+        {
+            refs = framework.getBundleContext().getAllServiceReferences(null, null);
+        }
+        catch (InvalidSyntaxException e)
+        {
+            // No filter, should never happen
+        }
+
+        for (ServiceReference<?> sr : refs)
+        {
+            dto.services.add(createServiceReferenceDTO(sr));
+        }
+
+        return dto;
+    }
+
+    private static FrameworkStartLevelDTO createFrameworkStartLevelDTO(Framework framework)
+    {
+        FrameworkStartLevel fsl = framework.adapt(FrameworkStartLevel.class);
+
+        FrameworkStartLevelDTO dto = new FrameworkStartLevelDTO();
+        dto.initialBundleStartLevel = fsl.getInitialBundleStartLevel();
+        dto.startLevel = fsl.getStartLevel();
+
+        return dto;
+    }
+
+    private static void addBundleRevisionDTO(BundleRevisionDTO dto, Set<BundleRevisionDTO> resources)
+    {
+        for (BundleRevisionDTO r : resources)
+        {
+            if (r.id == dto.id)
+                return;
+        }
+        resources.add(dto);
+    }
+
+    private static void addWiringNodeIfNotPresent(BundleWiring bw, Set<BundleRevisionDTO> resources, Set<NodeDTO> nodes)
+    {
+        int wiringID = getWiringID(bw);
+        for (NodeDTO n : nodes)
+        {
+            if (n.id == wiringID)
+                return;
+        }
+        createBundleWiringNodeDTO(bw, resources, nodes);
+    }
+
+    // Attributes contain Version values which are not supported for DTOs, so if
+    // these are found they need to be converted to String values.
+    private static Map<String, Object> convertAttrsToDTO(Map<String, Object> map)
+    {
+        Map<String, Object> m = new HashMap<String, Object>();
+        for (Map.Entry<String, Object> entry : map.entrySet())
+        {
+            if (entry.getValue() instanceof Version)
+                // DTOs don't support Version objects
+                m.put(entry.getKey(), entry.getValue().toString());
+            else
+                m.put(entry.getKey(), entry.getValue());
+        }
+        return m;
+    }
+
+    private static int getWiringID(Wiring bw)
+    {
+        return bw.hashCode();
+    }
+
+    private static int getCapabilityID(Capability capability)
+    {
+        return capability.hashCode();
+    }
+
+    private static int getRequirementID(Requirement requirement)
+    {
+        return requirement.hashCode();
+    }
+
+    private static int getResourceIDAndAdd(Resource res, Set<BundleRevisionDTO> resources)
+    {
+        if (res instanceof BundleRevisionImpl)
+        {
+            BundleRevisionImpl bres = (BundleRevisionImpl) res;
+            int id = bres.getId().hashCode();
+
+            if (resources == null)
+                return id;
+
+            for (BundleRevisionDTO rdto : resources)
+            {
+                if (rdto.id == id)
+                    return id;
+            }
+            createBundleRevisionDTO(bres, resources);
+            return id;
+        }
+        return res.hashCode();
+    }
+
+    private static int getRevisionID(BundleRevisionImpl revision)
+    {
+        return revision.getId().hashCode();
+    }
+}
diff --git a/framework/src/test/java/org/apache/felix/framework/DTOFactoryTest.java b/framework/src/test/java/org/apache/felix/framework/DTOFactoryTest.java
new file mode 100644
index 0000000..fc897bf
--- /dev/null
+++ b/framework/src/test/java/org/apache/felix/framework/DTOFactoryTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.framework;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.apache.felix.framework.Felix;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.dto.BundleStartLevelDTO;
+import org.osgi.framework.wiring.dto.BundleRevisionDTO;
+import org.osgi.framework.wiring.dto.BundleWiringDTO;
+import org.osgi.resource.dto.CapabilityDTO;
+
+public class DTOFactoryTest
+{
+    private int counter;
+    private Framework framework;
+    private File testDir;
+
+    @Before
+    public void setUp() throws Exception
+    {
+        String path = "/" + getClass().getName().replace('.', '/') + ".class";
+        String url = getClass().getResource(path).getFile();
+        String baseDir = url.substring(0, url.length() - path.length());
+        String rndStr = Long.toString(System.nanoTime(), Character.MAX_RADIX);
+        rndStr = rndStr.substring(rndStr.length() - 6, rndStr.length() - 1);
+        testDir = new File(baseDir, getClass().getSimpleName() + "_" + rndStr);
+
+        File cacheDir = new File(testDir, "cache");
+        cacheDir.mkdirs();
+        String cache = cacheDir.getAbsolutePath();
+
+        Map<String, Object> params = new HashMap<String, Object>();
+        params.put("felix.cache.profiledir", cache);
+        params.put("felix.cache.dir", cache);
+        params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+        framework = new Felix(params);
+        framework.init();
+        framework.start();
+    }
+
+    @After
+    public void tearDown() throws Exception
+    {
+        framework.stop();
+    }
+
+    @Test
+    public void testBundleStartLevelDTO() throws Exception
+    {
+        String mf = "Bundle-SymbolicName: tb1\n"
+                + "Bundle-Version: 1.0.0\n"
+                + "Bundle-ManifestVersion: 2\n";
+        File bf = createBundle(mf);
+        Bundle bundle = framework.getBundleContext().installBundle(bf.toURI().toURL().toExternalForm());
+
+        BundleStartLevel sl = bundle.adapt(BundleStartLevel.class);
+        sl.setStartLevel(7);
+
+        BundleStartLevelDTO dto = bundle.adapt(BundleStartLevelDTO.class);
+        assertEquals(bundle.getBundleId(), dto.bundle);
+        assertEquals(7, dto.startLevel);
+    }
+
+    @Test
+    public void testServiceReferenceDTOArray() throws Exception
+    {
+        ServiceRegistration<String> reg = framework.getBundleContext().registerService(String.class, "hi", null);
+        Long sid = (Long) reg.getReference().getProperty(Constants.SERVICE_ID);
+
+        ServiceReferenceDTO[] dtos = framework.adapt(ServiceReferenceDTO[].class);
+        assertTrue(dtos.length > 0);
+
+        boolean found = false;
+        for (ServiceReferenceDTO dto : dtos)
+        {
+            if (dto.id == sid)
+            {
+                found = true;
+                assertEquals(0L, dto.bundle);
+                assertEquals(sid, dto.properties.get(Constants.SERVICE_ID));
+                assertTrue(Arrays.equals(new String [] {String.class.getName()},
+                        (String []) dto.properties.get(Constants.OBJECTCLASS)));
+                assertEquals(0L, dto.properties.get(Constants.SERVICE_BUNDLEID));
+                assertEquals(Constants.SCOPE_SINGLETON, dto.properties.get(Constants.SERVICE_SCOPE));
+                assertEquals(0, dto.usingBundles.length);
+            }
+        }
+        assertTrue(found);
+    }
+
+    @Test
+    public void testBundleRevisionDTO() throws Exception
+    {
+        String mf = "Bundle-SymbolicName: tb2\n"
+                + "Bundle-Version: 1.2.3\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Import-Package: org.osgi.framework;version=\"[1.1,2)\"";
+        File bf = createBundle(mf);
+        Bundle bundle = framework.getBundleContext().installBundle(bf.toURI().toURL().toExternalForm());
+        bundle.start();
+        assertEquals("Precondition", Bundle.ACTIVE, bundle.getState());
+
+        BundleRevisionDTO dto = bundle.adapt(BundleRevisionDTO.class);
+        assertEquals(bundle.getBundleId(), dto.bundle);
+        assertTrue(dto.id != 0);
+        assertEquals("tb2", dto.symbolicName);
+        assertEquals("1.2.3", dto.version);
+        assertEquals(0, dto.type);
+
+        boolean foundBundle = false;
+        boolean foundHost = false;
+        boolean foundIdentity = false;
+        int resource = 0;
+        for (CapabilityDTO cap : dto.capabilities)
+        {
+            assertTrue(cap.id != 0);
+            if (resource == 0)
+                resource = cap.resource;
+            else
+                assertEquals(resource, cap.resource);
+
+            if (BundleNamespace.BUNDLE_NAMESPACE.equals(cap.namespace))
+            {
+                foundBundle = true;
+                assertEquals("tb2", cap.attributes.get(BundleNamespace.BUNDLE_NAMESPACE));
+                assertEquals("1.2.3", cap.attributes.get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE));
+            }
+            else if (HostNamespace.HOST_NAMESPACE.equals(cap.namespace))
+            {
+                foundHost = true;
+                assertEquals("tb2", cap.attributes.get(HostNamespace.HOST_NAMESPACE));
+                assertEquals("1.2.3", cap.attributes.get(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE));
+            }
+            else if (IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.namespace))
+            {
+                foundIdentity = true;
+                assertEquals("tb2", cap.attributes.get(IdentityNamespace.IDENTITY_NAMESPACE));
+                assertEquals("1.2.3", cap.attributes.get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE));
+                assertEquals(IdentityNamespace.TYPE_BUNDLE, cap.attributes.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+            }
+        }
+        assertTrue(foundBundle);
+        assertTrue(foundHost);
+        assertTrue(foundIdentity);
+    }
+
+    @Test
+    public void testBundleRevisionDTOArray() throws Exception {
+        String mf = "Bundle-SymbolicName: tb2\n"
+                + "Bundle-Version: 1.2.3\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Import-Package: org.osgi.framework;version=\"[1.1,2)\"";
+        File bf = createBundle(mf);
+        Bundle bundle = framework.getBundleContext().installBundle(bf.toURI().toURL().toExternalForm());
+        bundle.start();
+        assertEquals("Precondition", Bundle.ACTIVE, bundle.getState());
+
+        BundleRevisionDTO[] dtos = bundle.adapt(BundleRevisionDTO[].class);
+        assertEquals(1, dtos.length);
+        BundleRevisionDTO dto = dtos[0];
+
+        assertEquals(bundle.getBundleId(), dto.bundle);
+        assertTrue(dto.id != 0);
+        assertEquals("tb2", dto.symbolicName);
+        assertEquals("1.2.3", dto.version);
+        assertEquals(0, dto.type);
+
+        boolean foundBundle = false;
+        boolean foundHost = false;
+        boolean foundIdentity = false;
+        int resource = 0;
+        for (CapabilityDTO cap : dto.capabilities)
+        {
+            assertTrue(cap.id != 0);
+            if (resource == 0)
+                resource = cap.resource;
+            else
+                assertEquals(resource, cap.resource);
+
+            if (BundleNamespace.BUNDLE_NAMESPACE.equals(cap.namespace))
+            {
+                foundBundle = true;
+                assertEquals("tb2", cap.attributes.get(BundleNamespace.BUNDLE_NAMESPACE));
+                assertEquals("1.2.3", cap.attributes.get(BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE));
+            }
+            else if (HostNamespace.HOST_NAMESPACE.equals(cap.namespace))
+            {
+                foundHost = true;
+                assertEquals("tb2", cap.attributes.get(HostNamespace.HOST_NAMESPACE));
+                assertEquals("1.2.3", cap.attributes.get(HostNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE));
+            }
+            else if (IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.namespace))
+            {
+                foundIdentity = true;
+                assertEquals("tb2", cap.attributes.get(IdentityNamespace.IDENTITY_NAMESPACE));
+                assertEquals("1.2.3", cap.attributes.get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE));
+                assertEquals(IdentityNamespace.TYPE_BUNDLE, cap.attributes.get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE));
+            }
+        }
+        assertTrue(foundBundle);
+        assertTrue(foundHost);
+        assertTrue(foundIdentity);
+    }
+
+    @Test
+    public void testBundleWiringDTO() throws Exception {
+        String mf = "Bundle-SymbolicName: tb2\n"
+                + "Bundle-Version: 1.2.3\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Import-Package: org.osgi.framework;version=\"[1.1,2)\"";
+        File bf = createBundle(mf);
+        Bundle bundle = framework.getBundleContext().installBundle(bf.toURI().toURL().toExternalForm());
+        bundle.start();
+        assertEquals("Precondition", Bundle.ACTIVE, bundle.getState());
+
+        BundleWiringDTO dto = bundle.adapt(BundleWiringDTO.class);
+        assertEquals(bundle.getBundleId(), dto.bundle);
+    }
+
+    private File createBundle(String manifest) throws IOException
+    {
+        File f = File.createTempFile("felix-bundle" + counter++, ".jar", testDir);
+
+        Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+        mf.getMainAttributes().putValue("Manifest-Version", "1.0");
+        JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+
+        os.close();
+        return f;
+    }
+}