You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2020/03/02 11:51:16 UTC

[felix-dev] 01/01: Add the inital connect implementation - this changes the framework mvn/bsn to be org.apache.felix.framework.connect-0.1.0 for the time being.

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch connect
in repository https://gitbox.apache.org/repos/asf/felix-dev.git

commit d26f9ad4c0159a7ff06c5c4765d694b9cda1e7fe
Author: Karl Pauls <kp...@adobe.com>
AuthorDate: Mon Mar 2 12:50:58 2020 +0100

    Add the inital connect implementation - this changes the framework mvn/bsn to be org.apache.felix.framework.connect-0.1.0 for the time being.
---
 framework/pom.xml                                  |  43 +--
 .../org/apache/felix/framework/BundleImpl.java     |  26 +-
 .../felix/framework/BundleProtectionDomain.java    |   2 +-
 .../apache/felix/framework/BundleRevisionImpl.java |  34 ++
 .../apache/felix/framework/BundleWiringImpl.java   |  89 +++--
 .../apache/felix/framework/ExtensionManager.java   |  91 +++--
 .../java/org/apache/felix/framework/Felix.java     |  36 +-
 .../apache/felix/framework/FrameworkFactory.java   |  10 +-
 .../apache/felix/framework/ServiceRegistry.java    |  18 +-
 .../org/apache/felix/framework/URLHandlers.java    |  60 +++-
 .../framework/URLHandlersBundleURLConnection.java  |  14 +-
 .../framework/URLHandlersStreamHandlerProxy.java   |   2 +-
 .../felix/framework/cache/BundleArchive.java       |  35 +-
 .../apache/felix/framework/cache/BundleCache.java  |  13 +-
 .../framework/cache/ConnectContentContent.java     | 215 ++++++++++++
 .../felix/framework/cache/ConnectRevision.java     |  88 +++++
 .../framework/cache/ContentDirectoryContent.java   |  15 +-
 .../apache/felix/framework/cache/JarContent.java   |   2 +-
 .../framework/capabilityset/CapabilitySet.java     |   2 +-
 .../framework/ext/ClassPathExtenderFactory.java    |   6 +-
 .../felix/framework/util/FelixConstants.java       |   2 +-
 .../apache/felix/framework/util/SecureAction.java  |  98 +++++-
 .../java/org/apache/felix/framework/util/Util.java | 116 ++++---
 .../util/manifestparser/ManifestParser.java        |  72 +++-
 framework/src/main/java/org/osgi/dto/DTO.java      |   2 +-
 .../src/main/java/org/osgi/dto/package-info.java   |   2 +-
 .../java/org/osgi/framework/FrameworkUtil.java     |  75 ++++-
 .../osgi/framework/PrototypeServiceFactory.java    |   2 +-
 .../java/org/osgi/framework/ServiceObjects.java    |   2 +-
 .../org/osgi/framework/connect/ConnectContent.java | 200 +++++++++++
 .../framework/connect/ConnectFrameworkFactory.java |  80 +++++
 .../org/osgi/framework/connect/ConnectModule.java  |  45 +++
 .../framework/connect/FrameworkUtilHelper.java     |  43 +++
 .../osgi/framework/connect/ModuleConnector.java    |  95 ++++++
 .../url => framework/connect}/package-info.java    |  12 +-
 .../java/org/osgi/framework/dto/BundleDTO.java     |   2 +-
 .../java/org/osgi/framework/dto/FrameworkDTO.java  |   2 +-
 .../osgi/framework/dto/ServiceReferenceDTO.java    |   2 +-
 .../java/org/osgi/framework/dto/package-info.java  |   2 +-
 .../osgi/framework/hooks/bundle/package-info.java  |   2 +-
 .../framework/hooks/resolver/package-info.java     |   2 +-
 .../osgi/framework/hooks/service/package-info.java |   2 +-
 .../hooks/weaving/WovenClassListener.java          |   2 +-
 .../osgi/framework/hooks/weaving/package-info.java |   2 +-
 .../org/osgi/framework/launch/package-info.java    |   2 +-
 .../framework/namespace/IdentityNamespace.java     |  14 +-
 .../osgi/framework/namespace/NativeNamespace.java  |   2 +-
 .../org/osgi/framework/namespace/package-info.java |   2 +-
 .../main/java/org/osgi/framework/package-info.java |   4 +-
 .../startlevel/dto/BundleStartLevelDTO.java        |   2 +-
 .../startlevel/dto/FrameworkStartLevelDTO.java     |   2 +-
 .../framework/startlevel/dto/package-info.java     |   2 +-
 .../osgi/framework/startlevel/package-info.java    |   2 +-
 .../framework/wiring/dto/BundleRevisionDTO.java    |   2 +-
 .../osgi/framework/wiring/dto/BundleWireDTO.java   |   2 +-
 .../osgi/framework/wiring/dto/BundleWiringDTO.java |   2 +-
 .../osgi/framework/wiring/dto/package-info.java    |   2 +-
 .../org/osgi/framework/wiring/package-info.java    |   2 +-
 .../java/org/osgi/resource/dto/CapabilityDTO.java  |   2 +-
 .../org/osgi/resource/dto/CapabilityRefDTO.java    |   2 +-
 .../java/org/osgi/resource/dto/RequirementDTO.java |   2 +-
 .../org/osgi/resource/dto/RequirementRefDTO.java   |   2 +-
 .../java/org/osgi/resource/dto/ResourceDTO.java    |   2 +-
 .../main/java/org/osgi/resource/dto/WireDTO.java   |   2 +-
 .../main/java/org/osgi/resource/dto/WiringDTO.java |   2 +-
 .../java/org/osgi/resource/dto/package-info.java   |   2 +-
 .../main/java/org/osgi/resource/package-info.java  |   2 +-
 .../osgi/service/packageadmin/package-info.java    |   2 +-
 .../org/osgi/service/startlevel/package-info.java  |   2 +-
 .../java/org/osgi/service/url/package-info.java    |   2 +-
 .../java/org/osgi/util/tracker/package-info.java   |   2 +-
 ....osgi.framework.connect.ConnectFrameworkFactory |   1 +
 framework/src/main/resources/default.properties    |  14 +-
 .../org/apache/felix/framework/util/accessor.bytes | Bin 0 -> 816 bytes
 .../org/apache/felix/framework/util/accessor.src}  |  19 +-
 .../apache/felix/framework/CollisionHookTest.java  |   4 +-
 .../org/apache/felix/framework/ConnectTest.java    | 368 +++++++++++++++++++++
 .../felix/framework/MultiReleaseVersionTest.java   |   2 +-
 .../felix/framework/ServiceRegistryTest.java       |  41 +--
 .../felix/framework/cache/BundleCacheTest.java     |  10 +-
 .../util/manifestparser/ManifestParserTest.java    |  30 +-
 81 files changed, 1861 insertions(+), 361 deletions(-)

diff --git a/framework/pom.xml b/framework/pom.xml
index d83ac02..e12b847 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -26,16 +26,16 @@
   <modelVersion>4.0.0</modelVersion>
   <packaging>bundle</packaging>
   <name>Apache Felix Framework</name>
-  <artifactId>org.apache.felix.framework</artifactId>
-  <version>6.1.0-SNAPSHOT</version>
+  <artifactId>org.apache.felix.framework.connect</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
   <properties>
     <dollar>$</dollar>
-    <felix.java.version>6</felix.java.version>
+    <felix.java.version>8</felix.java.version>
   </properties>
   <scm>
-      <connection>scm:git:https://github.com/apache/felix-dev.git</connection>
-      <developerConnection>scm:git:https://github.com/apache/felix-dev.git</developerConnection>
-      <url>https://gitbox.apache.org/repos/asf?p=felix-dev.git</url>
+      <connection>scm:svn:http://svn.apache.org/repos/asf/felix/trunk/framework</connection>
+      <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/trunk/framework</developerConnection>
+      <url>http://svn.apache.org/repos/asf/felix/framework</url>
   </scm>
 
   <build>
@@ -47,9 +47,9 @@
         <extensions>true</extensions>
         <configuration>
           <instructions>
-            <Bundle-SymbolicName>org.apache.felix.framework</Bundle-SymbolicName>
-            <Bundle-Name>Apache Felix Framework</Bundle-Name>
-            <Bundle-Description>OSGi R7 framework implementation.</Bundle-Description>
+            <Bundle-SymbolicName>org.apache.felix.framework.connect</Bundle-SymbolicName>
+            <Bundle-Name>Apache Felix Framework Connect</Bundle-Name>
+            <Bundle-Description>OSGi R7 framework connect implementation.</Bundle-Description>
             <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
             <Export-Package>
                 org.osgi.framework.*;-split-package:=first,
@@ -89,7 +89,7 @@
               <excludes>
                   <exclude>src/main/appended-resources/**</exclude>
                   <exclude>src/**/packageinfo</exclude>
-                  <exclude>src/main/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory</exclude>
+                  <exclude>src/main/resources/META-INF/services/*</exclude>
                   <exclude>src/main/resources/org/apache/felix/framework/Felix.properties</exclude>
               </excludes>
             </configuration>
@@ -118,18 +118,19 @@
       <resource>
         <directory>src/main/resources</directory>
         <filtering>true</filtering>
+        <excludes>
+        <exclude>**/*.bytes</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>false</filtering>
+        <includes>
+          <include>**/*.bytes</include>
+        </includes>
       </resource>
     </resources>
   </build>
-  <dependencyManagement>
-      <dependencies>
-          <dependency>
-              <groupId>org.hamcrest</groupId>
-              <artifactId>hamcrest-core</artifactId>
-              <version>1.3</version>
-          </dependency>
-      </dependencies>
-  </dependencyManagement>
   <dependencies>
     <dependency>
        <groupId>org.osgi</groupId>
@@ -152,7 +153,7 @@
     <dependency>
         <groupId>org.ow2.asm</groupId>
         <artifactId>asm-all</artifactId>
-        <version>4.2</version>
+        <version>5.2</version>
         <scope>test</scope>
     </dependency>
     <dependency>
@@ -163,7 +164,7 @@
     </dependency>
     <dependency>
         <groupId>org.mockito</groupId>
-        <artifactId>mockito-core</artifactId>
+        <artifactId>mockito-all</artifactId>
         <version>1.10.19</version>
         <scope>test</scope>
     </dependency>
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 d3b6724..f8f0591 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -34,6 +34,7 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.ServicePermission;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
+import org.osgi.framework.connect.ConnectModule;
 import org.osgi.framework.hooks.bundle.CollisionHook;
 import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.wiring.BundleRevision;
@@ -188,16 +189,23 @@ class BundleImpl implements Bundle, BundleRevisions
         {
             // Get current revision, since we can reuse it.
             BundleRevisionImpl current = adapt(BundleRevisionImpl.class);
-            // Close all existing revisions.
-            closeRevisions();
-            // Clear all revisions.
-            m_revisions.clear();
 
-            // Purge all old archive revisions, only keeping the newest one.
-            m_archive.purge();
+            if (isRemovalPending()) {
+                closeRevisions();
+                // Purge all old archive revisions, only keeping the newest one.
+                m_archive.purge();
+
+                current.resetContent(m_archive.getCurrentRevision().getContent());
+            }
+            else {
+                // Remove the revision from the resolver state.
+                getFramework().getResolver().removeRevision(current);
+                current.resolve(null);
+                current.disposeContentPath();
+            }
+
+            m_revisions.clear();
 
-            // Reset the content of the current bundle revision.
-            current.resetContent(m_archive.getCurrentRevision().getContent());
             // Re-add the revision to the bundle.
             addRevision(current);
 
@@ -1332,7 +1340,7 @@ class BundleImpl implements Bundle, BundleRevisions
                     }
                 }
             }
-            if (!collisionCanditates.isEmpty())
+            if (!collisionCanditates.isEmpty() && m_installingBundle != null)
             {
                 throw new BundleException(
                     "Bundle symbolic name and version are not unique: "
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java b/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
index ccd4807..15d7145 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
@@ -76,7 +76,7 @@ public class BundleProtectionDomain extends ProtectionDomain
 
             int count = 0;
             String manifest = null;
-            for (Enumeration e = m_root.getEntries(); e.hasMoreElements();)
+            for (Enumeration e = m_root.getEntries(); e != null && e.hasMoreElements();)
             {
                 String entry = (String) e.nextElement();
                 if (entry.endsWith("/"))
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
index a6d47ab..c6fcaa4 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.framework;
 
+import org.apache.felix.framework.cache.ConnectContentContent;
 import org.apache.felix.framework.cache.Content;
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.MultiReleaseContent;
@@ -28,6 +29,7 @@ import org.apache.felix.framework.util.manifestparser.NativeLibrary;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
+import org.osgi.framework.connect.ConnectModule;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
@@ -320,6 +322,15 @@ public class BundleRevisionImpl implements BundleRevision, Resource
         }
     }
 
+    synchronized void disposeContentPath()
+    {
+        for (int i = 0; (m_contentPath != null) && (i < m_contentPath.size()); i++)
+        {
+            m_contentPath.get(i).close();
+        }
+        m_contentPath = null;
+    }
+
     public void setProtectionDomain(ProtectionDomain pd)
     {
         m_protectionDomain = pd;
@@ -617,6 +628,29 @@ public class BundleRevisionImpl implements BundleRevision, Resource
         return getContentPath().get(index - 1).getEntryAsStream(urlPath);
     }
 
+
+    public long getContentTime(int index, String urlPath)
+    {
+        if (urlPath.startsWith("/"))
+        {
+            urlPath = urlPath.substring(1);
+        }
+        Content content;
+        if (index == 0)
+        {
+            content = getContent();
+        }
+        else {
+            content = getContentPath().get(index - 1);
+        }
+        if (content instanceof ConnectContentContent) {
+            return ((ConnectContentContent) content).getContentTime(urlPath);
+        }
+        else {
+            return m_bundle.getLastModified();
+        }
+    }
+
     public URL getLocalURL(int index, String urlPath)
     {
         if (urlPath.startsWith("/"))
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index fa1f729..6d22ec7 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.framework;
 
+import org.apache.felix.framework.cache.ConnectContentContent;
 import org.apache.felix.framework.cache.Content;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
 import org.apache.felix.framework.resolver.ResourceNotFoundException;
@@ -37,6 +38,8 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.framework.PackagePermission;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ConnectModule;
 import org.osgi.framework.hooks.weaving.WeavingException;
 import org.osgi.framework.hooks.weaving.WeavingHook;
 import org.osgi.framework.hooks.weaving.WovenClass;
@@ -113,7 +116,7 @@ public class BundleWiringImpl implements BundleWiring
 
     private volatile List<BundleRequirement> m_wovenReqs = null;
 
-    private volatile BundleClassLoader m_classLoader;
+    private volatile ClassLoader m_classLoader;
 
     // Bundle-specific class loader for boot delegation.
     private final ClassLoader m_bootClassLoader;
@@ -727,16 +730,24 @@ public class BundleWiringImpl implements BundleWiring
         // is not disposed.
         if (!m_isDisposed && (m_classLoader == null))
         {
-            m_classLoader = BundleRevisionImpl.getSecureAction().run(
-                new PrivilegedAction<BundleClassLoader>()
-                {
-                    @Override
-                    public BundleClassLoader run()
+            if (m_revision.getContent() instanceof ConnectContentContent)
+            {
+                m_classLoader = ((ConnectContentContent) m_revision.getContent()).getClassLoader();
+            }
+
+            if (m_classLoader == null)
+            {
+                m_classLoader = BundleRevisionImpl.getSecureAction().run(
+                    new PrivilegedAction<BundleClassLoader>()
                     {
-                        return new BundleClassLoader(BundleWiringImpl.this, determineParentClassLoader(), m_logger);
+                        @Override
+                        public BundleClassLoader run()
+                        {
+                            return new BundleClassLoader(BundleWiringImpl.this, determineParentClassLoader(), m_logger);
+                        }
                     }
-                }
-            );
+                );
+            }
         }
         return m_classLoader;
     }
@@ -1522,24 +1533,21 @@ public class BundleWiringImpl implements BundleWiring
 
                     try
                     {
-                        result = tryImplicitBootDelegation(name, isClass);
-                    }
-                    catch (Exception ex)
-                    {
-                        // Ignore, will throw using CNFE_CLASS_LOADER
-                    }
+                        // Get the appropriate class loader for delegation.
+                        ClassLoader bdcl = getBootDelegationClassLoader();
+                        result = (isClass) ? (Object) bdcl.loadClass(name) : (Object) bdcl.getResource(name);
 
-                    if (result != null)
-                    {
-                        m_accessorLookupCache.put(name, BundleRevisionImpl.getSecureAction()
-                                .getClassLoader(this.getClass()));
-                        return result;
+                        if (result != null)
+                        {
+                            m_accessorLookupCache.put(name, bdcl);
+                            return result;
+                        }
                     }
-                    else
+                    catch (ClassNotFoundException ex)
                     {
-                        m_accessorLookupCache.put(name, CNFE_CLASS_LOADER);
-                        CNFE_CLASS_LOADER.loadClass(name);
                     }
+                    m_accessorLookupCache.put(name, CNFE_CLASS_LOADER);
+                    CNFE_CLASS_LOADER.loadClass(name);
                 }
 
                 // Look in the revision's imports. Note that the search may
@@ -1550,25 +1558,38 @@ public class BundleWiringImpl implements BundleWiring
                 // If not found, try the revision's own class path.
                 if (result == null)
                 {
-                    if (isClass)
+                    ClassLoader cl = getClassLoaderInternal();
+                    if (cl == null)
                     {
-                        ClassLoader cl = getClassLoaderInternal();
-                        if (cl == null)
+                        if (isClass)
                         {
                             throw new ClassNotFoundException(
-                                    "Unable to load class '"
-                                            + name
-                                            + "' because the bundle wiring for "
-                                            + m_revision.getSymbolicName()
-                                            + " is no longer valid.");
+                                "Unable to load class '"
+                                    + name
+                                    + "' because the bundle wiring for "
+                                    + m_revision.getSymbolicName()
+                                    + " is no longer valid.");
+                        }
+                         else
+                        {
+                            throw new ResourceNotFoundException("Unable to load resource '"
+                                + name
+                                + "' because the bundle wiring for "
+                                + m_revision.getSymbolicName()
+                                + " is no longer valid.");
                         }
-                        result = ((BundleClassLoader) cl).findClass(name);
                     }
-                    else
+                    if (cl instanceof BundleClassLoader)
                     {
-                        result = m_revision.getResourceLocal(name);
+                        result = isClass ? ((BundleClassLoader) cl).findClass(name) :
+                            ((BundleClassLoader) cl).findResource(name);
+                    }
+                    else {
+                        result = isClass ? cl.loadClass(name) : !name.startsWith("/") ? cl.getResource(name) :
+                        cl.getResource(name.substring(1));
                     }
 
+
                     // If still not found, then try the revision's dynamic imports.
                     if (result == null)
                     {
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index a5c8664..21613e2 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.framework;
 
+import org.apache.felix.framework.cache.ConnectContentContent;
 import org.apache.felix.framework.cache.Content;
 import org.apache.felix.framework.cache.DirectoryContent;
 import org.apache.felix.framework.cache.JarContent;
@@ -245,11 +246,21 @@ class ExtensionManager implements Content
 
         String sysprops = felix._getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES);
 
+
+        boolean subst = "true".equalsIgnoreCase(felix._getProperty(FelixConstants.USE_PROPERTY_SUBSTITUTION_IN_SYSTEMPACKAGES));
+
+        if (sysprops != null && sysprops.isEmpty()) {
+            if (felix.hasConnectFramework()) {
+                subst = true;
+                sysprops = "${osgi-exports}";
+                config.put(Constants.FRAMEWORK_SYSTEMPACKAGES, sysprops);
+            }
+        }
+
         final Map<String, Set<String>> exports = Util.initializeJPMS(defaultProperties);
 
         if (exports != null && (sysprops == null || "true".equalsIgnoreCase(felix._getProperty(FelixConstants.USE_PROPERTY_SUBSTITUTION_IN_SYSTEMPACKAGES))))
         {
-            java.nio.file.FileSystem fs = java.nio.file.FileSystems.getFileSystem(URI.create("jrt:/"));
             final ClassParser classParser = new ClassParser();
             final Set<String> imports = new HashSet<String>();
             for (Set<String> moduleImport : exports.values())
@@ -271,6 +282,7 @@ class ExtensionManager implements Content
                     final SortedMap<String, SortedSet<String>> referred = new TreeMap<String, SortedSet<String>>();
                     if ("true".equalsIgnoreCase(felix._getProperty(FelixConstants.CALCULATE_SYSTEMPACKAGES_USES)))
                     {
+                        java.nio.file.FileSystem fs = java.nio.file.FileSystems.getFileSystem(URI.create("jrt:/"));
                         try
                         {
                             Properties cachedProps = new Properties();
@@ -344,7 +356,7 @@ class ExtensionManager implements Content
             }
         }
 
-        if(sysprops != null && "true".equalsIgnoreCase(felix._getProperty(FelixConstants.USE_PROPERTY_SUBSTITUTION_IN_SYSTEMPACKAGES)))
+        if(sysprops != null && subst)
         {
             config.put(Constants.FRAMEWORK_SYSTEMPACKAGES, Util.getPropertyWithSubs(Util.toProperties(config), Constants.FRAMEWORK_SYSTEMPACKAGES));
         }
@@ -414,23 +426,6 @@ class ExtensionManager implements Content
             ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
                 .getHeaders().get(Constants.FRAGMENT_HOST));
 
-        if (!Constants.EXTENSION_FRAMEWORK.equals(directive))
-        {
-           throw new BundleException("Unsupported Extension Bundle type: " +
-                directive, new UnsupportedOperationException(
-                "Unsupported Extension Bundle type!"));
-        }
-        else if (m_extenderFramework == null)
-        {
-            // We don't support extensions
-            m_logger.log(bundle, Logger.LOG_WARNING,
-                "Unable to add extension bundle - Maybe ClassLoader is not supported " +
-                        "(on java9, try --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED)?");
-
-            throw new UnsupportedOperationException(
-                "Unable to add extension bundle.");
-        }
-
         Content content = bundle.adapt(BundleRevisionImpl.class).getContent();
         final File file;
         if (content instanceof JarContent)
@@ -445,14 +440,31 @@ class ExtensionManager implements Content
         {
             file = null;
         }
-        if (file == null)
+        if (file == null && !(content instanceof ConnectContentContent))
         {
             // We don't support revision type for extension
             m_logger.log(bundle, Logger.LOG_WARNING,
-                    "Unable to add extension bundle - wrong revision type?");
+                "Unable to add extension bundle - wrong revision type?");
 
             throw new UnsupportedOperationException(
-                    "Unable to add extension bundle.");
+                "Unable to add extension bundle.");
+        }
+
+        if (!Constants.EXTENSION_FRAMEWORK.equals(directive))
+        {
+           throw new BundleException("Unsupported Extension Bundle type: " +
+                directive, new UnsupportedOperationException(
+                "Unsupported Extension Bundle type!"));
+        }
+        else if (m_extenderFramework == null && file != null)
+        {
+            // We don't support extensions
+            m_logger.log(bundle, Logger.LOG_WARNING,
+                "Unable to add extension bundle - Maybe ClassLoader is not supported " +
+                        "(on java9, try --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED)?");
+
+            throw new UnsupportedOperationException(
+                "Unable to add extension bundle.");
         }
 
         BundleRevisionImpl bri = bundle.adapt(BundleRevisionImpl.class);
@@ -559,25 +571,32 @@ class ExtensionManager implements Content
             {
                 f = ((JarContent) revisionContent).getFile();
             }
-            else
+            else if (revisionContent instanceof DirectoryContent)
             {
                 f = ((DirectoryContent) revisionContent).getFile();
             }
-            try
-            {
-                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>()
-                {
-                    @Override
-                    public Void run() throws Exception {
-                        m_extenderFramework.add(f);
-                        return null;
-                    }
-                });
+            else {
+                f = null;
             }
-            catch (Exception ex)
+            if (f != null)
             {
-                m_logger.log(revision.getBundle(), Logger.LOG_ERROR,
-                    "Error adding extension bundle to framework classloader: " + revision.getBundle(), ex);
+                try
+                {
+                    AccessController.doPrivileged(new PrivilegedExceptionAction<Void>()
+                    {
+                        @Override
+                        public Void run() throws Exception
+                        {
+                            m_extenderFramework.add(f);
+                            return null;
+                        }
+                    });
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(revision.getBundle(), Logger.LOG_ERROR,
+                        "Error adding extension bundle to framework classloader: " + revision.getBundle(), ex);
+                }
             }
 
             felix.setBundleStateAndNotify(revision.getBundle(), Bundle.RESOLVED);
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 46792c5..0b31b20 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -58,6 +58,7 @@ import org.osgi.framework.ServicePermission;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.Version;
+import org.osgi.framework.connect.ModuleConnector;
 import org.osgi.framework.launch.Framework;
 import org.osgi.framework.namespace.HostNamespace;
 import org.osgi.framework.startlevel.FrameworkStartLevel;
@@ -213,6 +214,8 @@ public class Felix extends BundleImpl implements Framework
     // Do we need to consult the default java security policy if no security provider is present?
     private volatile boolean m_securityDefaultPolicy;
 
+    private final ModuleConnector m_connectFramework;
+
     /**
      * <p>
      * This constructor creates a framework instance with a specified <tt>Map</tt>
@@ -351,6 +354,11 @@ public class Felix extends BundleImpl implements Framework
     **/
     public Felix(Map configMap)
     {
+        this(configMap, null);
+    }
+
+    public Felix(Map configMap, ModuleConnector connectFramework)
+    {
         super();
         // Copy the configuration properties; convert keys to strings.
         m_configMutableMap = new StringMap();
@@ -460,6 +468,8 @@ public class Felix extends BundleImpl implements Framework
         m_fwkWiring = new FrameworkWiringImpl(this, m_registry);
         // Create framework start level object.
         m_fwkStartLevel = new FrameworkStartLevelImpl(this, m_registry);
+
+        m_connectFramework = connectFramework;
     }
 
     Logger getLogger()
@@ -634,12 +644,12 @@ public class Felix extends BundleImpl implements Framework
         return true;
     }
 
-
     @Override
     public void init() throws BundleException
     {
-        init((FrameworkListener[]) null);
+        init(null);
     }
+
     /**
      * @see org.osgi.framework.launch.Framework#init(org.osgi.framework.FrameworkListener[])
      */
@@ -726,6 +736,10 @@ public class Felix extends BundleImpl implements Framework
                             throw new BundleException("Unable to flush bundle cache.", ex);
                         }
                     }
+                    if (m_connectFramework != null)
+                    {
+                        m_connectFramework.initialize(m_cache.getCacheDir(), (Map) m_configMap);
+                    }
                 }
 
                 // Initialize installed bundle data structures.
@@ -774,7 +788,6 @@ public class Felix extends BundleImpl implements Framework
                         "Unresolved constraint in System Bundle:"
                         + ex.getUnresolvedRequirements());
                 }
-
                 // Reload the cached bundles before creating and starting the
                 // system bundle, since we want all cached bundles to be reloaded
                 // when we activate the system bundle and any subsequent system
@@ -784,7 +797,7 @@ public class Felix extends BundleImpl implements Framework
                 // First get cached bundle identifiers.
                 try
                 {
-                    archives = m_cache.getArchives();
+                    archives = m_cache.getArchives(m_connectFramework);
                 }
                 catch (Exception ex)
                 {
@@ -850,6 +863,12 @@ public class Felix extends BundleImpl implements Framework
                     m_extensionManager.startExtensionBundle(this, (BundleImpl) extension);
                 }
 
+
+                if (m_connectFramework != null)
+                {
+                    m_connectFramework.createBundleActivator().ifPresent(m_activatorList::add);
+                }
+
                 // Now that we have loaded all cached bundles and have determined the
                 // max bundle ID of cached bundles, we need to try to load the next
                 // bundle ID from persistent storage. In case of failure, we should
@@ -3224,7 +3243,7 @@ public class Felix extends BundleImpl implements Framework
                 try
                 {
                     // Add the bundle to the cache.
-                    ba = m_cache.create(id, getInitialBundleStartLevel(), location, is);
+                    ba = m_cache.create(id, getInitialBundleStartLevel(), location, is, m_connectFramework);
                 }
                 catch (Exception ex)
                 {
@@ -5110,6 +5129,11 @@ public class Felix extends BundleImpl implements Framework
         }
     }
 
+    public boolean hasConnectFramework()
+    {
+        return m_connectFramework != null;
+    }
+
     //
     // Miscellaneous inner classes.
     //
@@ -5132,6 +5156,7 @@ public class Felix extends BundleImpl implements Framework
                 }
                 catch (Throwable throwable)
                 {
+                    throwable.printStackTrace();
                     iter.remove();
                     fireFrameworkEvent(FrameworkEvent.ERROR, context.getBundle(),
                             new BundleException("Unable to start Bundle", throwable));
@@ -5234,6 +5259,7 @@ public class Felix extends BundleImpl implements Framework
                         throwable);
                 }
             }
+            m_activatorList.clear();
             if (m_securityManager != null)
             {
                 System.setSecurityManager(null);
diff --git a/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java b/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
index ce377ad..938845f 100644
--- a/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
+++ b/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
@@ -19,12 +19,20 @@
 package org.apache.felix.framework;
 
 import java.util.Map;
+
+import org.osgi.framework.connect.ModuleConnector;
 import org.osgi.framework.launch.Framework;
 
-public class FrameworkFactory implements org.osgi.framework.launch.FrameworkFactory
+public class FrameworkFactory implements org.osgi.framework.launch.FrameworkFactory, org.osgi.framework.connect.ConnectFrameworkFactory
 {
     public Framework newFramework(Map configuration)
     {
         return new Felix(configuration);
     }
+
+    @Override
+    public Framework newFramework(Map<String, String> configuration, ModuleConnector connectFramework)
+    {
+        return new Felix(configuration, connectFramework);
+    }
 }
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
index 68bab2c..8c94ca8 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -280,23 +280,11 @@ public class ServiceRegistry
         if (usages != null)
         {
             final ServiceReference<?>[] refs = new ServiceReference[usages.length];
-            int count = 0;
-            for (int i = 0; i < usages.length; i++)
+            for (int i = 0; i < refs.length; i++)
             {
-                if (usages[i].m_count.get() > 0) {
-                    refs[count++] = usages[i].m_ref;
-                }
+                refs[i] = usages[i].m_ref;
             }
-
-            if (count == usages.length) {
-                return refs;
-            } else if (count == 0) {
-                return null;
-            }
-
-            ServiceReference<?>[] nrefs = new ServiceReference[count];
-            System.arraycopy(refs, 0, nrefs, 0, count);
-            return nrefs;
+            return refs;
         }
         return null;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlers.java b/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
index 79b13ce..88b65e9 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
@@ -117,23 +117,34 @@ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
             ? DEFAULT_STREAM_HANDLER_PACKAGE
             : pkgs + "|" + DEFAULT_STREAM_HANDLER_PACKAGE;
         m_loaded = (null != URLHandlersStreamHandlerProxy.class) &&
-            (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class);
+            (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class) && new URLHandlersStreamHandlerProxy(null, null) != null;
     }
 
     private void init(String protocol, URLStreamHandlerFactory factory)
     {
         try
         {
-            URLStreamHandler handler = getBuiltInStreamHandler(protocol, factory);
-            if (handler != null)
-            {
-                URL url = new URL(protocol, null, -1, "", handler);
-                addToCache(m_protocolToURL, protocol, url);
-            }
+            // Try to get it directly from the URL class to if possible
+            Method getURLStreamHandler = m_secureAction.getDeclaredMethod(URL.class,"getURLStreamHandler", new Class[]{String.class});
+            URLStreamHandler handler = (URLStreamHandler) m_secureAction.invoke(getURLStreamHandler, null, new Object[]{protocol});
+            addToCache(m_builtIn, protocol, handler);
         }
         catch (Throwable ex)
         {
-            // Ignore, this is a best effort (maybe log it or something).
+            // Ignore, this is a best effort
+            try
+            {
+                URLStreamHandler handler = getBuiltInStreamHandler(protocol, factory);
+                if (handler != null)
+                {
+                    URL url = new URL(protocol, null, -1, "", handler);
+                    addToCache(m_protocolToURL, protocol, url);
+                }
+            }
+            catch (Throwable ex2)
+            {
+                // Ignore, this is a best effort (maybe log it or something).
+            }
         }
     }
 
@@ -165,14 +176,7 @@ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
             init("ftp", currentFactory);
             init("http", currentFactory);
             init("https", currentFactory);
-            try
-            {
-                getBuiltInStreamHandler("jar", currentFactory);
-            }
-            catch (Throwable ex)
-            {
-                // Ignore, this is a best effort (maybe log it or something)
-            }
+
 
             // Try to preload the jrt handler as we need it from the jvm on java > 8
             if (getFromCache(m_builtIn, "jrt") == null)
@@ -198,6 +202,30 @@ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
                 }
             }
 
+            // Try to preload the jrt handler as we need it from the jvm on java > 8
+            if (getFromCache(m_builtIn, "jar") == null)
+            {
+                try
+                {
+                    // Try to get it directly from the URL class to if possible
+                    Method getURLStreamHandler = m_secureAction.getDeclaredMethod(URL.class,"getURLStreamHandler", new Class[]{String.class});
+                    URLStreamHandler handler = (URLStreamHandler) m_secureAction.invoke(getURLStreamHandler, null, new Object[]{"jar"});
+                    addToCache(m_builtIn, "jar", handler);
+                }
+                catch (Throwable ex)
+                {
+                    // Ignore, this is a best effort
+                    try
+                    {
+                        getBuiltInStreamHandler("jar", currentFactory);
+                    }
+                    catch (Throwable ex2)
+                    {
+                        // Ignore, this is a best effort (maybe log it or something)
+                    }
+                }
+            }
+
             if (currentFactory != null)
             {
                 try
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
index 2cb7ee7..5a06e1b 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
@@ -35,7 +35,7 @@ class URLHandlersBundleURLConnection extends URLConnection
     private Felix m_framework;
     private BundleRevision m_targetRevision;
     private int m_classPathIdx = -1;
-    private int m_contentLength;
+    private long m_contentLength;
     private long m_contentTime;
     private String m_contentType;
     private InputStream m_is;
@@ -83,7 +83,6 @@ class URLHandlersBundleURLConnection extends URLConnection
         {
             throw new IOException("No bundle associated with resource: " + url);
         }
-        m_contentTime = bundle.getLastModified();
 
         // Get the bundle's revisions to find the target revision.
         BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
@@ -145,6 +144,7 @@ class URLHandlersBundleURLConnection extends URLConnection
             m_is = ((BundleRevisionImpl)
                 m_targetRevision).getInputStream(m_classPathIdx, url.getPath());
             m_contentLength = (m_is == null) ? 0 : m_is.available();
+            m_contentTime = ((BundleRevisionImpl) m_targetRevision).getContentTime(m_classPathIdx, url.getPath());
             m_contentType = URLConnection.guessContentTypeFromName(url.getFile());
             connected = true;
         }
@@ -160,6 +160,11 @@ class URLHandlersBundleURLConnection extends URLConnection
 
     public int getContentLength()
     {
+        return (int) getContentLengthLong();
+    }
+
+    public long getContentLengthLong()
+    {
         try
         {
             connect();
@@ -172,11 +177,6 @@ class URLHandlersBundleURLConnection extends URLConnection
         return m_contentLength;
     }
 
-    public long getContentLengthLong()
-    {
-        return getContentLength();
-    }
-
     public long getLastModified()
     {
         try
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
index ef577c6..5d9efa3 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -138,7 +138,7 @@ public class URLHandlersStreamHandlerProxy extends URLStreamHandler
         m_builtInURL = builtInURL;
     }
 
-    private URLHandlersStreamHandlerProxy(Object service, SecureAction action)
+    URLHandlersStreamHandlerProxy(Object service, SecureAction action)
     {
         m_protocol = null;
         m_service = service;
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
index fc527bc..35026f1 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
@@ -21,12 +21,16 @@ package org.apache.felix.framework.cache;
 import java.io.*;
 
 import java.util.Map;
+import java.util.Optional;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.WeakZipFileFactory;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
+import org.osgi.framework.connect.ModuleConnector;
+import org.osgi.framework.connect.ConnectModule;
 
 /**
  * <p>
@@ -100,6 +104,8 @@ public class BundleArchive
     **/
     private long m_refreshCount = -1;
 
+    private final ModuleConnector m_connector;
+
     // Maps a Long revision number to a BundleRevision.
     private final SortedMap<Long, BundleArchiveRevision> m_revisions
         = new TreeMap<Long, BundleArchiveRevision>();
@@ -121,7 +127,8 @@ public class BundleArchive
      * @param is input stream from which to read the bundle content.
      * @throws Exception if any error occurs.
     **/
-    public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+    public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory, ModuleConnector
+        connectFactory,
         File archiveRootDir, long id, int startLevel, String location, InputStream is)
         throws Exception
     {
@@ -141,6 +148,8 @@ public class BundleArchive
         m_lastModified = System.currentTimeMillis();
         m_refreshCount = 0;
 
+        m_connector = connectFactory;
+
         // Save state.
         initialize();
 
@@ -160,7 +169,7 @@ public class BundleArchive
      * @param configMap configMap for BundleArchive
      * @throws Exception if any error occurs.
     **/
-    public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+    public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory, ModuleConnector connectFactory,
         File archiveRootDir)
         throws Exception
     {
@@ -211,8 +220,12 @@ public class BundleArchive
         Long currentRevNum = m_revisions.lastKey();
         m_revisions.remove(currentRevNum);
 
+        String location = getRevisionLocation(currentRevNum);
+
+        m_connector = connectFactory;
+
         // Add the revision object for the most recent revision.
-        reviseInternal(true, currentRevNum, getRevisionLocation(currentRevNum), null);
+        reviseInternal(true, currentRevNum, location, null);
     }
 
     /**
@@ -795,9 +808,19 @@ public class BundleArchive
             }
             else
             {
-                // Anything else is assumed to be a URL to a JAR file.
-                result = new JarRevision(m_logger, m_configMap,
-                    m_zipFactory, revisionRootDir, location, false, null);
+                ConnectModule module = m_connector != null ?
+                    m_connector.connect(location).orElse(null) : null;
+
+                if (module != null)
+                {
+                    result = new ConnectRevision(m_logger, m_configMap, m_zipFactory, revisionRootDir, location, module);
+                }
+                else
+                {
+                    // Anything else is assumed to be a URL to a JAR file.
+                    result = new JarRevision(m_logger, m_configMap,
+                        m_zipFactory, revisionRootDir, location, false, null);
+                }
             }
         }
         catch (Exception ex)
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index 8e63af9..022cdb4 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -22,6 +22,7 @@ import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.SecureAction;
 import org.apache.felix.framework.util.WeakZipFileFactory;
 import org.osgi.framework.Constants;
+import org.osgi.framework.connect.ModuleConnector;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -386,6 +387,10 @@ public class BundleCache
         }
     }
 
+    public File getCacheDir() {
+        return determineCacheDir(m_configMap);
+    }
+
     /* package */ static SecureAction getSecureAction()
     {
         return m_secureAction;
@@ -398,7 +403,7 @@ public class BundleCache
         deleteDirectoryTree(cacheDir);
     }
 
-    public BundleArchive[] getArchives()
+    public BundleArchive[] getArchives(ModuleConnector connectFactory)
         throws Exception
     {
         // Get buffer size value.
@@ -431,7 +436,7 @@ public class BundleCache
                 {
                     archiveList.add(
                         new BundleArchive(
-                            m_logger, m_configMap, m_zipFactory, children[i]));
+                            m_logger, m_configMap, m_zipFactory, connectFactory, children[i]));
                 }
                 catch (Exception ex)
                 {
@@ -447,7 +452,7 @@ public class BundleCache
             archiveList.toArray(new BundleArchive[archiveList.size()]);
     }
 
-    public BundleArchive create(long id, int startLevel, String location, InputStream is)
+    public BundleArchive create(long id, int startLevel, String location, InputStream is, ModuleConnector connectFactory)
         throws Exception
     {
         File cacheDir = determineCacheDir(m_configMap);
@@ -461,7 +466,7 @@ public class BundleCache
             // Create the archive and add it to the list of archives.
             BundleArchive ba =
                 new BundleArchive(
-                    m_logger, m_configMap, m_zipFactory, archiveRootDir,
+                    m_logger, m_configMap, m_zipFactory, connectFactory, archiveRootDir,
                     id, startLevel, location, is);
             return ba;
         }
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java b/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java
new file mode 100644
index 0000000..7a985ac
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java
@@ -0,0 +1,215 @@
+/*
+ * 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.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.connect.ConnectContent;
+
+public class ConnectContentContent implements Content
+{
+    private static final transient String EMBEDDED_DIRECTORY = "-embedded";
+    private static final transient String LIBRARY_DIRECTORY = "-lib";
+
+    private final Logger m_logger;
+    private final WeakZipFileFactory m_zipFactory;
+    private final Map m_configMap;
+    private final String m_name;
+    private final File m_rootDir;
+    private final Object m_revisionLock;
+    private final ConnectContent m_content;
+
+    public ConnectContentContent(Logger logger, WeakZipFileFactory zipFactory, Map configMap, String name, File rootDir, Object revisionLock, ConnectContent content) throws IOException
+    {
+        m_logger = logger;
+        m_zipFactory = zipFactory;
+        m_configMap = configMap;
+        m_name = name;
+        m_rootDir = rootDir;
+        m_revisionLock = revisionLock;
+        m_content = content;
+    }
+
+    @Override
+    public void close()
+    {
+        // TODO: Connect
+    }
+
+    @Override
+    public boolean hasEntry(String name)
+    {
+        return m_content.getEntry(name).isPresent();
+    }
+
+    @Override
+    public Enumeration<String> getEntries()
+    {
+        try
+        {
+            Iterator<String> entries = m_content.getEntries().iterator();
+            return new Enumeration<String>()
+            {
+                @Override
+                public boolean hasMoreElements()
+                {
+                    return entries.hasNext();
+                }
+
+                @Override
+                public String nextElement()
+                {
+                    return entries.next();
+                }
+            };
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    @Override
+    public byte[] getEntryAsBytes(String name)
+    {
+        return m_content.getEntry(name).flatMap(entry ->
+        {
+            try
+            {
+                return Optional.of(entry.getBytes());
+            }
+            catch (IOException e)
+            {
+                e.printStackTrace();
+                return null;
+            }
+        }).orElse(null);
+    }
+
+    @Override
+    public InputStream getEntryAsStream(String name) throws IOException
+    {
+        return m_content.getEntry(name).flatMap(entry ->
+        {
+            try
+            {
+                return Optional.of(entry.getInputStream());
+            }
+            catch (IOException e)
+            {
+                e.printStackTrace();
+                return null;
+            }
+        }).orElse(null);
+    }
+
+    public ClassLoader getClassLoader() {
+        return m_content.getClassLoader().orElse(null);
+    }
+
+    @Override
+    public Content getEntryAsContent(String name)
+    {
+        if (".".equals(name) || "".equals(name)) {
+            return this;
+        }
+        String dir = name.endsWith("/") ? name : name + "/";
+
+        if (hasEntry(dir)) {
+            return new ContentDirectoryContent(this, name);
+        }
+
+        if (hasEntry(name) && name.endsWith(".jar"))
+        {
+            // Any embedded JAR files will be extracted to the embedded directory.
+            // Since embedded JAR file names may clash when extracting from multiple
+            // embedded JAR files, the embedded directory is per embedded JAR file.
+            File embedDir = new File(m_rootDir, m_name + EMBEDDED_DIRECTORY);
+
+            File extractJar = new File(embedDir, name);
+
+            try
+            {
+                if (!BundleCache.getSecureAction().fileExists(extractJar))
+                {
+                    // Extracting the embedded JAR file impacts all other existing
+                    // contents for this revision, so we have to grab the revision
+                    // lock first before trying to extract the embedded JAR file
+                    // to avoid a race condition.
+                    synchronized (m_revisionLock)
+                    {
+                        if (!BundleCache.getSecureAction().fileExists(extractJar))
+                        {
+                            // Make sure that the embedded JAR's parent directory exists;
+                            // it may be in a sub-directory.
+                            File jarDir = extractJar.getParentFile();
+                            if (!BundleCache.getSecureAction().fileExists(jarDir) && !BundleCache.getSecureAction().mkdirs(jarDir))
+                            {
+                                throw new IOException("Unable to create embedded JAR directory.");
+                            }
+
+                            // Extract embedded JAR into its directory.
+                            BundleCache.copyStreamToFile(m_content.getEntry(name).get().getInputStream(), extractJar);
+                        }
+                    }
+                }
+                return new JarContent(
+                    m_logger, m_configMap, m_zipFactory, m_revisionLock,
+                    extractJar.getParentFile(), extractJar, null);
+            }
+            catch (Exception ex)
+            {
+                m_logger.log(
+                    Logger.LOG_ERROR,
+                    "Unable to extract embedded JAR file.", ex);
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String getEntryAsNativeLibrary(String entryName)
+    {
+        // TODO: Connect
+        return null;
+    }
+
+    @Override
+    public URL getEntryAsURL(String name)
+    {
+        // TODO: Connect
+        return null;
+    }
+
+    public long getContentTime(String urlPath)
+    {
+        return m_content.getEntry(urlPath).flatMap(entry -> Optional.of(entry.getLastModified())).orElse(-1L);
+    }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java
new file mode 100644
index 0000000..9b165cf
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java
@@ -0,0 +1,88 @@
+/*
+ * 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.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ConnectModule;
+
+public class ConnectRevision extends BundleArchiveRevision
+{
+    private final WeakZipFileFactory m_zipFactory;
+    private final ConnectContent m_module;
+
+    public ConnectRevision(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+        File revisionRootDir, String location, ConnectModule module) throws Exception
+    {
+        super(logger, configMap, revisionRootDir, location);
+        m_zipFactory = zipFactory;
+        m_module = module.getContent();
+        m_module.open();
+        // If the revision directory exists, then we don't
+        // need to initialize since it has already been done.
+        if (BundleCache.getSecureAction().fileExists(getRevisionRootDir()))
+        {
+            return;
+        }
+        // Create revision directory, we only need this to store the
+        // revision location, since nothing else needs to be extracted
+        // since we are referencing a read directory already.
+        if (!BundleCache.getSecureAction().mkdir(getRevisionRootDir()))
+        {
+            getLogger().log(
+                Logger.LOG_ERROR,
+                getClass().getName() + ": Unable to create revision directory.");
+            throw new IOException("Unable to create archive directory.");
+        }
+    }
+
+    @Override
+    public Map<String, Object> getManifestHeader() throws Exception
+    {
+        return (Map) m_module.getHeaders().orElseGet(() -> m_module.getEntry("META-INF/MANIFEST.MF").flatMap(entry -> {
+                try
+                {
+                    return Optional.of((Map<String, String>) (Map) BundleCache.getMainAttributes(new StringMap(), entry.getInputStream(), entry.getContentLength()));
+                }
+                catch (Exception e)
+                {
+                    throw new IllegalStateException(e);
+                }
+            }).orElse(null));
+    }
+
+    @Override
+    public Content getContent() throws Exception
+    {
+        return new ConnectContentContent(getLogger(), m_zipFactory, getConfig(), "connect", getRevisionRootDir(), this, m_module);
+    }
+
+    @Override
+    protected void close() throws Exception
+    {
+        m_module.close();
+    }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
index d268f32..a1b3367 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
@@ -143,14 +143,17 @@ public class ContentDirectoryContent implements Content
 
         private String findNextEntry()
         {
-            // Find next entry that is inside the root directory.
-            while (m_enumeration.hasMoreElements())
+            if (m_enumeration != null)
             {
-                String next = (String) m_enumeration.nextElement();
-                if (next.startsWith(m_rootPath) && !next.equals(m_rootPath))
+                // Find next entry that is inside the root directory.
+                while (m_enumeration.hasMoreElements())
                 {
-                    // Strip off the root directory.
-                    return next.substring(m_rootPath.length());
+                    String next = (String) m_enumeration.nextElement();
+                    if (next.startsWith(m_rootPath) && !next.equals(m_rootPath))
+                    {
+                        // Strip off the root directory.
+                        return next.substring(m_rootPath.length());
+                    }
                 }
             }
             return null;
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
index 35afc9c..885137a 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
@@ -398,7 +398,7 @@ public class JarContent implements Content
         return m_file;
     }
 
-    private static class DevNullRunnable implements Runnable
+    static class DevNullRunnable implements Runnable
     {
         private final InputStream m_in;
 
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
index 7116751..e887c3a 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -577,7 +577,7 @@ public class CapabilitySet
     {
         // If the LHS expects a string, then we can just return
         // the RHS since it is a string.
-        if (lhs.getClass() == rhsString.getClass())
+        if (lhs instanceof String)
         {
             return rhsString;
         }
diff --git a/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java b/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
index 26103b4..ae43b73 100644
--- a/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
+++ b/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
@@ -23,6 +23,8 @@ import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLClassLoader;
 
+import org.apache.felix.framework.util.SecureAction;
+
 public interface ClassPathExtenderFactory
 {
     interface ClassPathExtender
@@ -50,7 +52,7 @@ public interface ClassPathExtenderFactory
                 try
                 {
                     append = app.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
-                    append.setAccessible(true);
+                    new SecureAction().setAccesssible(append);
                     break;
                 }
                 catch (Exception e)
@@ -74,7 +76,7 @@ public interface ClassPathExtenderFactory
             try
             {
                 addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
-                addURL.setAccessible(true);
+                new SecureAction().setAccesssible(addURL);
             }
             catch (Exception e)
             {
diff --git a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
index 71a1f75..0c5e1ca 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -20,7 +20,7 @@ package org.apache.felix.framework.util;
 
 public interface FelixConstants extends org.osgi.framework.Constants
 {
-    String SYSTEM_BUNDLE_SYMBOLICNAME = "org.apache.felix.framework";
+    String SYSTEM_BUNDLE_SYMBOLICNAME = "org.apache.felix.framework.connect";
     // Framework constants and values.
     String FRAMEWORK_VERSION_VALUE = "1.9";
     String FRAMEWORK_VENDOR_VALUE = "Apache Software Foundation";
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index 4a202a7..559075e 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -23,11 +23,15 @@ import java.lang.reflect.*;
 import java.lang.reflect.Proxy;
 import java.net.*;
 import java.security.*;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 import java.util.jar.JarFile;
+import java.util.stream.Stream;
 import java.util.zip.ZipFile;
 
 import org.osgi.framework.Bundle;
@@ -56,6 +60,30 @@ import org.osgi.framework.wiring.BundleRevision;
 **/
 public class SecureAction
 {
+    private static final byte[] accessor;
+
+    static {
+        byte[] result;
+
+        try (ByteArrayOutputStream output = new ByteArrayOutputStream();
+             InputStream input = SecureAction.class.getResourceAsStream("accessor.bytes"))
+        {
+
+            byte[] buffer = new byte[input.available() > 0 ? input.available() : 1024];
+            for (int i = input.read(buffer); i != -1; i = input.read(buffer))
+            {
+                output.write(buffer, 0, i);
+            }
+            result = output.toByteArray();
+        }
+        catch (Throwable t) {
+            t.printStackTrace();
+            result = new byte[0];
+        }
+        accessor = result;
+        getAccessor(URL.class);
+    }
+
     private static final ThreadLocal m_actions = new ThreadLocal()
     {
         public Object initialValue()
@@ -762,7 +790,7 @@ public class SecureAction
             Method addURL =
                 URLClassLoader.class.getDeclaredMethod("addURL",
                 new Class[] {URL.class});
-            addURL.setAccessible(true);
+            getAccessor(URLClassLoader.class).accept(new AccessibleObject[]{addURL});
             addURL.invoke(loader, new Object[]{extension});
         }
     }
@@ -851,7 +879,7 @@ public class SecureAction
         }
     }
 
-    public void setAccesssible(AccessibleObject ao)
+    public void setAccesssible(Executable ao)
     {
         if (System.getSecurityManager() != null)
         {
@@ -868,7 +896,7 @@ public class SecureAction
         }
         else
         {
-            ao.setAccessible(true);
+            getAccessor(ao.getDeclaringClass()).accept(new AccessibleObject[]{ao});
         }
     }
 
@@ -889,7 +917,8 @@ public class SecureAction
         }
         else
         {
-            method.setAccessible(true);
+            getAccessor(method.getDeclaringClass()).accept(new AccessibleObject[]{method});
+
             return method.invoke(target, params);
         }
     }
@@ -955,8 +984,7 @@ public class SecureAction
         else
         {
             Field field = targetClass.getDeclaredField(name);
-            field.setAccessible(true);
-
+            getAccessor(targetClass).accept(new AccessibleObject[]{field});
             return field.get(target);
         }
     }
@@ -985,9 +1013,50 @@ public class SecureAction
         }
     }
 
+    private static volatile Consumer<AccessibleObject[]> m_accessorCache = null;
+
+    @SuppressWarnings("unchecked")
+    private static Consumer<AccessibleObject[]> getAccessor(Class clazz) {
+        String packageName = clazz.getPackage().getName();
+        if ("java.net".equals(packageName) || "jdk.internal.loader".equals(packageName))
+        {
+            if (m_accessorCache == null)
+            {
+                try
+                {
+                    // Use reflection on Unsafe to avoid having to compile against it
+                    Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); //$NON-NLS-1$
+                    Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe"); //$NON-NLS-1$
+                    // NOTE: deep reflection is allowed on sun.misc package for java 9.
+                    theUnsafe.setAccessible(true);
+                    Object unsafe = theUnsafe.get(null);
+                    // using defineAnonymousClass here because it seems more simple to get what we need
+                    Method defineAnonymousClass = unsafeClass.getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class); //$NON-NLS-1$
+                    // The bytes stored in a resource to avoid real loading of it (see accessible.src for source).
+
+                    Class<Consumer<AccessibleObject[]>> result =
+                        (Class<Consumer<AccessibleObject[]>>)
+                            defineAnonymousClass.invoke(unsafe, URL.class, accessor , null);
+                    m_accessorCache = result.getConstructor().newInstance();
+
+                }
+                catch (Throwable t)
+                {
+                    t.printStackTrace();
+                    m_accessorCache = objects -> AccessibleObject.setAccessible(objects, true);
+                }
+            }
+            return m_accessorCache;
+        }
+        else {
+            return objects -> AccessibleObject.setAccessible(objects, true);
+        }
+    }
+
     private static Object _swapStaticFieldIfNotClass(Class targetClazz,
         Class targetType, Class condition, String lockName) throws Exception
     {
+
         Object lock = null;
         if (lockName != null)
         {
@@ -995,7 +1064,7 @@ public class SecureAction
             {
                 Field lockField =
                     targetClazz.getDeclaredField(lockName);
-                lockField.setAccessible(true);
+                getAccessor(targetClazz).accept(new AccessibleObject[]{lockField});
                 lock = lockField.get(null);
             }
             catch (NoSuchFieldException ex)
@@ -1010,14 +1079,14 @@ public class SecureAction
         {
             Field[] fields = targetClazz.getDeclaredFields();
 
+            getAccessor(targetClazz).accept(fields);
+
             Object result = null;
             for (int i = 0; (i < fields.length) && (result == null); i++)
             {
                 if (Modifier.isStatic(fields[i].getModifiers()) &&
                     (fields[i].getType() == targetType))
                 {
-                    fields[i].setAccessible(true);
-
                     result = fields[i].get(null);
 
                     if (result != null)
@@ -1040,7 +1109,6 @@ public class SecureAction
                         if (Modifier.isStatic(fields[i].getModifiers()) &&
                             (fields[i].getType() == Hashtable.class))
                         {
-                            fields[i].setAccessible(true);
                             Hashtable cache = (Hashtable) fields[i].get(null);
                             if (cache != null)
                             {
@@ -1081,13 +1149,13 @@ public class SecureAction
         synchronized (lock)
         {
             Field[] fields = targetClazz.getDeclaredFields();
+            getAccessor(targetClazz).accept(fields);
             // reset cache
             for (int i = 0; i < fields.length; i++)
             {
                 if (Modifier.isStatic(fields[i].getModifiers()) &&
                     ((fields[i].getType() == Hashtable.class) || (fields[i].getType() == HashMap.class)))
                 {
-                    fields[i].setAccessible(true);
                     if (fields[i].getType() == Hashtable.class)
                     {
                         Hashtable cache = (Hashtable) fields[i].get(null);
@@ -1708,7 +1776,7 @@ public class SecureAction
                     Method addURL =
                         URLClassLoader.class.getDeclaredMethod("addURL",
                         new Class[] {URL.class});
-                    addURL.setAccessible(true);
+                    getAccessor(URLClassLoader.class).accept(new AccessibleObject[]{addURL});
                     addURL.invoke(arg2, new Object[]{arg1});
                     return null;
                 case CREATE_TMPFILE_ACTION:
@@ -1740,7 +1808,7 @@ public class SecureAction
                     return ((Class) arg1).getDeclaredMethod((String) arg2, (Class[]) arg3);
                 case GET_FIELD_ACTION:
                     Field field = ((Class) arg1).getDeclaredField((String) arg2);
-                    field.setAccessible(true);
+                    getAccessor((Class) arg1).accept(new AccessibleObject[]{field});
                     return field.get(arg3);
                 case GET_FILE_INPUT_ACTION:
                     return new FileInputStream((File) arg1);
@@ -1765,7 +1833,7 @@ public class SecureAction
                 case INVOKE_DIRECTMETHOD_ACTION:
                     return ((Method) arg1).invoke(arg2, (Object[]) arg3);
                 case INVOKE_METHOD_ACTION:
-                    ((Method) arg1).setAccessible(true);
+                    getAccessor(((Method) arg1).getDeclaringClass()).accept(new AccessibleObject[]{(Method) arg1});
                     return ((Method) arg1).invoke(arg2, (Object[]) arg3);
                 case LIST_DIRECTORY_ACTION:
                     return ((File) arg1).listFiles();
@@ -1780,7 +1848,7 @@ public class SecureAction
                 case RENAME_FILE_ACTION:
                     return ((File) arg1).renameTo((File) arg2) ? Boolean.TRUE : Boolean.FALSE;
                 case SET_ACCESSIBLE_ACTION:
-                    ((AccessibleObject) arg1).setAccessible(true);
+                    getAccessor(((Executable) arg1).getDeclaringClass()).accept(new AccessibleObject[]{(Executable) arg1});
                     return null;
                 case START_ACTIVATOR_ACTION:
                     ((BundleActivator) arg1).start((BundleContext) arg2);
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index f6acd36..d92bb1b 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -47,12 +47,15 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Random;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -60,37 +63,46 @@ public class Util
 {
     /**
      * The default name used for the default configuration properties file.
-    **/
-    private static final String DEFAULT_PROPERTIES_FILE = "default.properties";
+     **/
+    private static final String DEFAULT_PROPERTIES_FILE = "/default.properties";
+
+    private static final Properties DEFAULTS;
+
+    private static final IOException DEFAULT_EX;
+
+    private static final Map<String, Set<String>> MODULES_MAP;
+
+    static
+    {
+        Properties defaults = null;
+        IOException defaultEX = null;
+        try
+        {
+            defaults = loadDefaultProperties();
+        } catch (IOException ex) {
+            defaultEX = ex;
+        }
+        DEFAULTS = defaults;
+        DEFAULT_EX = defaultEX;
+
+        MODULES_MAP = calculateModulesMap();
+    }
+
+    public static Properties loadDefaultProperties(Logger logger) {
+        if (DEFAULTS.isEmpty()) {
+            logger.log(Logger.LOG_ERROR, "Unable to load any configuration properties.", DEFAULT_EX);
+        }
+        return DEFAULTS;
+    }
 
-    public static Properties loadDefaultProperties(Logger logger)
+    private static Properties loadDefaultProperties() throws IOException
     {
         Properties defaultProperties = new Properties();
-        URL propURL = Util.class.getClassLoader().getResource(DEFAULT_PROPERTIES_FILE);
+        URL propURL = Util.class.getResource(DEFAULT_PROPERTIES_FILE);
         if (propURL != null)
         {
-            InputStream is = null;
-            try
-            {
-                // Load properties from URL.
-                is = propURL.openConnection().getInputStream();
+            try (InputStream is = propURL.openConnection().getInputStream()) {
                 defaultProperties.load(is);
-                is.close();
-            }
-            catch (Exception ex)
-            {
-                // Try to close input stream if we have one.
-                try
-                {
-                    if (is != null) is.close();
-                }
-                catch (IOException ex2)
-                {
-                    // Nothing we can do.
-                }
-
-                logger.log(
-                    Logger.LOG_ERROR, "Unable to load any configuration properties.", ex);
             }
         }
         return defaultProperties;
@@ -138,6 +150,8 @@ public class Util
                 properties.put("ee-jpms", ee.toString());
 
                 properties.put("eecap-jpms", eecap.toString());
+
+                properties.put("felix.detect.jpms", "jpms");
             }
 
             properties.put("felix.detect.java.specification.version", version.getMajor() < 9 ? ("1." + (version.getMinor() > 6 ? version.getMinor() : 6)) : Integer.toString(version.getMajor()));
@@ -157,9 +171,9 @@ public class Util
         }
     }
 
-    public static Map<String, Set<String>> initializeJPMS(Properties properties)
+    private static Map<String, Set<String>> calculateModulesMap()
     {
-        Map<String,Set<String>> exports = null;
+        Map<String, Set<String>> result = new LinkedHashMap<>();
         try
         {
             Class<?> c_ModuleLayer = Felix.class.getClassLoader().loadClass("java.lang.ModuleLayer");
@@ -179,16 +193,13 @@ public class Util
                 moduleLayer = c_ModuleLayer.getMethod("boot").invoke(null);
             }
 
-            Set<String> modules = new TreeSet<String>();
-            exports = new HashMap<String, Set<String>>();
             for (Object module : ((Iterable) c_ModuleLayer.getMethod("modules").invoke(moduleLayer)))
             {
                 if ((Boolean) m_canRead.invoke(self, module))
                 {
-                    Object name = m_getName.invoke(module);
-                    properties.put("felix.detect.jpms." + name, name);
-                    modules.add("felix.jpms." + name);
-                    Set<String> pkgs = new HashSet<String>();
+                    String name = (String) m_getName.invoke(module);
+
+                    Set<String> pkgs = new LinkedHashSet<>();
 
                     Object descriptor = c_Module.getMethod("getDescriptor").invoke(module);
 
@@ -199,24 +210,45 @@ public class Util
                             pkgs.add((String) c_Exports.getMethod("source").invoke(export));
                         }
                     }
-                    if (!pkgs.isEmpty())
-                    {
-                        exports.put("felix.jpms." + c_Descriptor.getMethod("toNameAndVersion").invoke(descriptor), pkgs);
-                    }
+                    result.put(name, pkgs);
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+            //ex.printStackTrace();
+            // Not much we can do - probably not on java9
+        }
+        return result;
+    }
+
+    public static Map<String, Set<String>> initializeJPMS(Properties properties)
+    {
+        Map<String,Set<String>> exports = null;
+        if (!MODULES_MAP.isEmpty())
+        {
+            Set<String> modules = new TreeSet<String>();
+            exports = new HashMap<String, Set<String>>();
+            for (Map.Entry<String, Set<String>> module : MODULES_MAP.entrySet())
+            {
+                Object name = module.getKey();
+                properties.put("felix.detect.jpms." + name, name);
+                modules.add("felix.jpms." + name);
+                Set<String> pkgs = module.getValue();
+
+                if (!pkgs.isEmpty())
+                {
+                    exports.put("felix.jpms." + name, pkgs);
                 }
             }
 
-            properties.put("felix.detect.jpms", "jpms");
             String modulesString = "";
             for (String module : modules) {
                 modulesString += "${" + module + "}";
             }
             properties.put("jre-jpms", modulesString);
         }
-        catch (Exception ex)
-        {
-            // Not much we can do - probably not on java9
-        }
+
         return exports;
     }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index 9158c6a..60a0fe6 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -20,6 +20,7 @@ package org.apache.felix.framework.util.manifestparser;
 
 import org.apache.felix.framework.BundleRevisionImpl;
 import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.cache.ConnectContentContent;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.wiring.BundleCapabilityImpl;
@@ -28,6 +29,7 @@ import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
 import org.osgi.framework.VersionRange;
+import org.osgi.framework.connect.ConnectContent;
 import org.osgi.framework.namespace.BundleNamespace;
 import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
 import org.osgi.framework.namespace.IdentityNamespace;
@@ -37,10 +39,13 @@ import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -109,7 +114,7 @@ public class ManifestParser
         // Parse bundle symbolic name.
         //
 
-        BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap);
+        BundleCapabilityImpl bundleCap = parseBundleSymbolicName(logger, owner, m_headerMap);
         if (bundleCap != null)
         {
             m_bundleSymbolicName = (String)
@@ -215,7 +220,7 @@ public class ManifestParser
         List<ParsedHeaderClause> exportClauses =
             parseStandardHeader((String) headerMap.get(Constants.EXPORT_PACKAGE));
         exportClauses = normalizeExportClauses(logger, exportClauses,
-            getManifestVersion(), m_bundleSymbolicName, m_bundleVersion);
+            getManifestVersion(), m_bundleSymbolicName, m_bundleVersion, owner instanceof BundleRevisionImpl && ((BundleRevisionImpl) owner).getContent() instanceof ConnectContentContent);
         List<BundleCapability> exportCaps = convertExports(exportClauses, owner);
 
         //
@@ -696,7 +701,7 @@ public class ManifestParser
         throws BundleException
     {
 
-        if (!mv.equals("2") && !clauses.isEmpty())
+        if (mv != null && !mv.equals("2") && !clauses.isEmpty())
         {
             // Should we error here if we are not an R4 bundle?
         }
@@ -836,7 +841,7 @@ public class ManifestParser
 
     private static List<ParsedHeaderClause> normalizeExportClauses(
         Logger logger, List<ParsedHeaderClause> clauses,
-        String mv, String bsn, Version bv)
+        String mv, String bsn, Version bv, boolean connectModule)
         throws BundleException
     {
         for (ParsedHeaderClause clause : clauses)
@@ -845,7 +850,7 @@ public class ManifestParser
             for (String pkgName : clause.m_paths)
             {
                 // Verify that java.* packages are not exported (except from the system bundle).
-                if (!FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(bsn) && pkgName.startsWith("java."))
+                if ((!FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(bsn) && !connectModule) && pkgName.startsWith("java."))
                 {
                     throw new BundleException(
                         "Exporting java.* packages not allowed: "
@@ -1358,12 +1363,12 @@ public class ManifestParser
         return false;
     }
 
-    private static BundleCapabilityImpl parseBundleSymbolicName(
+    private static BundleCapabilityImpl parseBundleSymbolicName(Logger logger,
         BundleRevision owner, Map<String, Object> headerMap)
         throws BundleException
     {
-        List<ParsedHeaderClause> clauses = parseStandardHeader(
-            (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+        List<ParsedHeaderClause> clauses = normalizeCapabilityClauses(logger, parseStandardHeader(
+            (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME)), getManifestVersion(headerMap));
         if (clauses.size() > 0)
         {
             if (clauses.size() > 1)
@@ -1378,6 +1383,11 @@ public class ManifestParser
                     "Cannot have multiple symbolic names: "
                         + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
             }
+            else if (clauses.get(0).m_attrs.containsKey(Constants.BUNDLE_VERSION))
+            {
+                throw new BundleException(
+                    "Cannot have a bundle version: " + headerMap.get(Constants.BUNDLE_VERSION));
+            }
 
             // Get bundle version.
             Version bundleVersion = Version.emptyVersion;
@@ -1400,6 +1410,48 @@ public class ManifestParser
                 }
             }
 
+            Object tagList = clauses.get(0).m_attrs.get(IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE);
+            LinkedHashSet<String> tags = new LinkedHashSet<>();
+            if (tagList != null)
+            {
+                if (tagList instanceof List)
+                {
+                    for (Object member : ((List) tagList))
+                    {
+                        if (member instanceof String)
+                        {
+                            tags.add((String) member);
+                        }
+                        else
+                        {
+                            throw new BundleException("Invalid tags list: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+                        }
+                    }
+                }
+                else if (tagList instanceof String)
+                {
+                    tags.add((String) tagList);
+                }
+                else
+                {
+                    throw new BundleException("Invalid tags list: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+                }
+            }
+
+            if (tags.contains(ConnectContent.TAG_OSGI_CONNECT))
+            {
+                throw new BundleException("Invalid tags list: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+            }
+            if (owner != null && ((BundleRevisionImpl) owner).getContent() instanceof ConnectContentContent)
+            {
+                tags.add(ConnectContent.TAG_OSGI_CONNECT);
+            }
+
+            if (!tags.isEmpty())
+            {
+                clauses.get(0).m_attrs.put(IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE, new ArrayList<>(tags));
+            }
+
             // Create a require capability and return it.
             String symName = (String) clauses.get(0).m_paths.get(0);
             clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
@@ -1415,9 +1467,9 @@ public class ManifestParser
     }
 
     private static BundleCapabilityImpl addIdentityCapability(BundleRevision owner,
-        Map<String, Object> headerMap, BundleCapabilityImpl bundleCap)
+        Map<String, Object> headerMap, BundleCapabilityImpl bundleCap) throws BundleException
     {
-        Map<String, Object> attrs = new HashMap<String, Object>();
+        Map<String, Object> attrs = new HashMap<String, Object>(bundleCap.getAttributes());
 
         attrs.put(IdentityNamespace.IDENTITY_NAMESPACE,
             bundleCap.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
diff --git a/framework/src/main/java/org/osgi/dto/DTO.java b/framework/src/main/java/org/osgi/dto/DTO.java
index 53074a6..f109341 100644
--- a/framework/src/main/java/org/osgi/dto/DTO.java
+++ b/framework/src/main/java/org/osgi/dto/DTO.java
@@ -35,7 +35,7 @@ import java.util.Set;
  * The object graph from a Data Transfer Object must be a tree to simplify
  * serialization and deserialization.
  * 
- * @author $Id$
+ * @author $Id: DTO.java 1825132 2018-02-23 15:11:00Z pauls $
  * @NotThreadSafe
  */
 public abstract class DTO {
diff --git a/framework/src/main/java/org/osgi/dto/package-info.java b/framework/src/main/java/org/osgi/dto/package-info.java
index d0d0ad1..0a1637e 100644
--- a/framework/src/main/java/org/osgi/dto/package-info.java
+++ b/framework/src/main/java/org/osgi/dto/package-info.java
@@ -30,7 +30,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.dto; version="[1.1,1.2)"}
  *
- * @author $Id$
+ * @author $Id: package-info.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 
 @Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/FrameworkUtil.java b/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
index 9f72092..d50bea5 100644
--- a/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
+++ b/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
@@ -26,15 +26,20 @@ import java.security.PrivilegedAction;
 import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+import java.util.ServiceLoader;
 import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
+import org.osgi.framework.connect.FrameworkUtilHelper;
+
 /**
  * Framework Utility class.
  * 
@@ -194,29 +199,67 @@ public class FrameworkUtil {
 	}
 
 	/**
-	 * Return a {@code Bundle} for the specified bundle class. The returned
-	 * {@code Bundle} is the bundle associated with the bundle class loader
-	 * which defined the specified class.
-	 * 
-	 * @param classFromBundle A class defined by a bundle class loader.
+	 * Return a {@code Bundle} for the specified bundle class loader.
+	 *
+	 * @param bundleClassLoader A bundle class loader.
+	 * @return An Optional containing {@code Bundle} for the specified bundle
+	 *         class loader or an empty Optional if the specified class loader
+	 *         is not associated with a specific bundle.
+	 * @since 1.10
+	 */
+	public static Optional<Bundle> getBundle(ClassLoader bundleClassLoader) {
+		return Optional
+			.ofNullable((bundleClassLoader instanceof BundleReference)
+				? ((BundleReference) bundleClassLoader).getBundle()
+				: null);
+	}
+
+	/**
+	 * Return a {@code Bundle} for the specified bundle class.
+	 *
+	 * @param classFromBundle A class defined by a bundle.
 	 * @return A {@code Bundle} for the specified bundle class or {@code null}
-	 *         if the specified class was not defined by a bundle class loader.
+	 *         if the specified class was not defined by a bundle.
 	 * @since 1.5
 	 */
-	public static Bundle getBundle(final Class<?> classFromBundle) {
+	public static Bundle getBundle(Class< ? > classFromBundle) {
 		// We use doPriv since the caller may not have permission
 		// to call getClassLoader.
-		Object cl = AccessController.doPrivileged(new PrivilegedAction<Object>() {
-			@Override
-			public Object run() {
-				return classFromBundle.getClassLoader();
-			}
-		});
+		Optional<ClassLoader> cl = Optional
+			.ofNullable(AccessController.doPrivileged(
+				(PrivilegedAction<ClassLoader>) () -> classFromBundle
+					.getClassLoader()));
+
+		return cl.flatMap(FrameworkUtil::getBundle)
+			.orElseGet(() -> helpers.stream()
+				.map(helper -> helper.getBundle(classFromBundle))
+				.filter(Optional::isPresent)
+				.map(Optional::get)
+				.findFirst()
+				.orElse(null));
+	}
 
-		if (cl instanceof BundleReference) {
-			return ((BundleReference) cl).getBundle();
+	private final static List<FrameworkUtilHelper> helpers;
+	static {
+		List<FrameworkUtilHelper> l = new ArrayList<>();
+		try {
+			ServiceLoader<FrameworkUtilHelper> helperLoader = AccessController
+				.doPrivileged(
+					(PrivilegedAction<ServiceLoader<FrameworkUtilHelper>>) () -> ServiceLoader
+						.load(FrameworkUtilHelper.class,
+							FrameworkUtilHelper.class
+								.getClassLoader()));
+			helperLoader.forEach(l::add);
+		} catch (Throwable error) {
+			// try hard not to fail static <clinit>
+			try {
+				Thread t = Thread.currentThread();
+				t.getUncaughtExceptionHandler().uncaughtException(t, error);
+			} catch (Throwable ignored) {
+				// we ignore this
+			}
 		}
-		return null;
+		helpers = Collections.unmodifiableList(l);
 	}
 
 	/**
diff --git a/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java b/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
index 864506f..dbb9243 100644
--- a/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
+++ b/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
@@ -65,7 +65,7 @@ import org.osgi.annotation.versioning.ConsumerType;
  * @see ServiceObjects
  * @ThreadSafe
  * @since 1.8
- * @author $Id$
+ * @author $Id: PrototypeServiceFactory.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 @ConsumerType
 public interface PrototypeServiceFactory<S> extends ServiceFactory<S> {
diff --git a/framework/src/main/java/org/osgi/framework/ServiceObjects.java b/framework/src/main/java/org/osgi/framework/ServiceObjects.java
index 8490189..145ed77 100644
--- a/framework/src/main/java/org/osgi/framework/ServiceObjects.java
+++ b/framework/src/main/java/org/osgi/framework/ServiceObjects.java
@@ -41,7 +41,7 @@ import org.osgi.annotation.versioning.ProviderType;
  * @see PrototypeServiceFactory
  * @ThreadSafe
  * @since 1.8
- * @author $Id$
+ * @author $Id: ServiceObjects.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 @ProviderType
 public interface ServiceObjects<S> {
diff --git a/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java b/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java
new file mode 100644
index 0000000..eb80ae6
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) OSGi Alliance (2019). All Rights Reserved.
+ *
+ * Licensed 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.osgi.framework.connect;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Optional;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleRevisions;
+
+/**
+ * A connect content provides a {@link Framework framework} access to the
+ * content of a connect {@link ConnectModule module}. A framework may
+ * {@link #open() open} and {@link #close() close} the content for a connect
+ * module multiple times while the connect content is in use by the framework
+ * instance. The framework must close the connect content once the connect
+ * content is no longer used as the content of a current bundle revision or an
+ * in use bundle revision.
+ * <p>
+ * An entry in a connect content is identified by a path name that is a
+ * '{@code /}'-separated path. A connect content may treat directories as
+ * entries. A directory entry path name will end with a slash ('/'). A directory
+ * entry may be located using a path name that drops the trailing slash.
+ * 
+ * @see BundleRevisions
+ * @ThreadSafe
+ * @author $Id: 44ec66031f9460c48453c7113e4871472a7c475c $
+ */
+public interface ConnectContent {
+	/**
+	 * The {@code osgi.identity}
+	 * {@link IdentityNamespace#CAPABILITY_TAGS_ATTRIBUTE tags} attribute value
+	 * used by the framework to tag connect bundle revisions.
+	 */
+	public static final String TAG_OSGI_CONNECT = "osgi.connect";
+
+	/**
+	 * Returns this connect content Manifest headers and values. The
+	 * {@link Optional#empty() empty} value is returned if the framework should
+	 * handle parsing the Manifest of the content itself.
+	 * 
+	 * @return This connect content Manifest headers and values.
+	 * @throws IllegalStateException if the connect content has been closed
+	 */
+	Optional<Map<String,String>> getHeaders();
+
+	/**
+	 * Returns an iterable with all the entry names available in this
+	 * ConnectContent
+	 * 
+	 * @return the entry names
+	 * @throws IOException if an error occurs reading the ConnectContent
+	 * @throws IllegalStateException if the connect content has been closed
+	 */
+	Iterable<String> getEntries() throws IOException;
+
+	/**
+	 * Returns the connect entry for the specified path name in this content.
+	 * The {@link Optional#empty() empty} value is returned if an entry with the
+	 * specified path name does not exist. The path must not start with a
+	 * &quot;/&quot; and is relative to the root of this content. A connect
+	 * entry for a directory will have a path name that ends with a slash ('/').
+	 * 
+	 * @param path the path name of the entry
+	 * @return the connect entry, or {@link Optional#empty() empty} if not
+	 *         found.
+	 * @throws IllegalStateException if the connect content has been closed
+	 */
+	Optional<ConnectEntry> getEntry(String path);
+
+	/**
+	 * Returns a class loader for this connect content. The
+	 * {@link Optional#empty() empty} value is returned if the framework should
+	 * handle creating a class loader for the bundle revision associated with
+	 * this connect content.
+	 * <p>
+	 * This method is called by the framework for {@link Bundle#RESOLVED
+	 * resolved} bundles only and will be called at most once while a bundle is
+	 * resolved. If a bundle associated with a connect module is refreshed and
+	 * resolved again the framework will ask the content for the class loader
+	 * again. This allows for a connect content to reuse or create a new class
+	 * loader each time the bundle revision is resolved.
+	 * 
+	 * @return a class loader for the module.
+	 */
+	Optional<ClassLoader> getClassLoader();
+
+	/**
+	 * Opens this connect content. The framework will open the content when it
+	 * needs to access the content for a bundle revision associated with the
+	 * connect content. The framework may lazily postpone to open the content
+	 * until right before requests to access the bundle revision content are
+	 * made.
+	 * 
+	 * @throws IOException if an error occurred opening the content
+	 */
+	void open() throws IOException;
+
+	/**
+	 * Closes this connect content.
+	 * 
+	 * @throws IOException if an error occurred closing the connect content
+	 */
+	void close() throws IOException;
+
+	/**
+	 * Represents the entry of a connect module
+	 */
+	public interface ConnectEntry {
+		/**
+		 * Returns the path name of the entry
+		 * 
+		 * @return the path name of the entry
+		 */
+		String getName();
+
+		/**
+		 * Returns the size of the entry. The value {@code -1} is returned if
+		 * the content length is not known.
+		 * 
+		 * @return the size of the entry, or {@code -1} if the content length is
+		 *         not known.
+		 */
+		public long getContentLength();
+
+		/**
+		 * Returns the last modification time of the entry
+		 * 
+		 * @return the last modification time of the entry
+		 */
+		public long getLastModified();
+
+		/**
+		 * Returns the content of the entry as a byte array.
+		 * 
+		 * @return the content bytes
+		 * @throws IOException if an error occurs reading the content
+		 */
+		default byte[] getBytes() throws IOException {
+			long longLength = getContentLength();
+			if (longLength > Integer.MAX_VALUE - 8) {
+				throw new IOException(
+						"Entry is to big to fit into a byte[]: " + getName());
+			}
+
+			try (InputStream in = getInputStream()) {
+				int length = (int) longLength;
+				if (length > 0) {
+					int bytesread = 0;
+					byte[] result = new byte[length];
+					int readcount = 0;
+					while (bytesread < length) {
+						readcount = in.read(result, bytesread,
+								length - bytesread);
+						bytesread += readcount;
+						if (readcount <= 0) {
+							break;
+						}
+					}
+					return result;
+				} else {
+					ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+					int nRead;
+					byte[] data = new byte[1024];
+					while ((nRead = in.read(data, 0, data.length)) > 0) {
+						buffer.write(data, 0, nRead);
+					}
+					buffer.flush();
+					return buffer.toByteArray();
+				}
+			}
+		}
+
+		/**
+		 * Returns the content of the entry as an input stream.
+		 * 
+		 * @return the content input stream
+		 * @throws IOException if an error occurs reading the content
+		 */
+		InputStream getInputStream() throws IOException;
+	}
+}
diff --git a/framework/src/main/java/org/osgi/framework/connect/ConnectFrameworkFactory.java b/framework/src/main/java/org/osgi/framework/connect/ConnectFrameworkFactory.java
new file mode 100644
index 0000000..fe0a641
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectFrameworkFactory.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) OSGi Alliance (2019). All Rights Reserved.
+ *
+ * Licensed 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.osgi.framework.connect;
+
+import java.util.Map;
+
+import org.osgi.annotation.versioning.ProviderType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * A factory for creating {@link Framework} instances.
+ * <p>
+ * If a framework supports {@link ModuleConnector} then the implementation jar
+ * must contain the following resource:
+ * 
+ * <pre>
+ * /META-INF/services/org.osgi.framework.connect.ConnectFrameworkFactory
+ * </pre>
+ * 
+ * This UTF-8 encoded resource must contain the name of the framework
+ * implementation's ConnectFrameworkFactory implementation class. Space and tab
+ * characters, including blank lines, in the resource must be ignored. The
+ * number sign ({@code '#'} &#92;u0023) and all characters following it on each
+ * line are a comment and must be ignored.
+ * <p>
+ * Launchers can find the name of the ConnectFrameworkFactory implementation
+ * class in the resource and then load and construct a ConnectFrameworkFactory
+ * object for the framework implementation. The ConnectFrameworkFactory
+ * implementation class must have a public, no-argument constructor. Java&#8482;
+ * SE 6 introduced the {@code ServiceLoader} class which can create a
+ * ConnectFrameworkFactory instance from the resource.
+ * 
+ * @ThreadSafe
+ * @author $Id: c1193dbc989c5cc0840f0b6a66a229b95d6fbc4e $
+ */
+@ProviderType
+public interface ConnectFrameworkFactory {
+	/**
+	 * Create a new {@link Framework} instance using the specified
+	 * {@link ModuleConnector module connector}.
+	 * 
+	 * @param configuration The framework properties to configure the new
+	 *            framework instance. If framework properties are not provided
+	 *            by the configuration argument, the created framework instance
+	 *            must use some reasonable default configuration appropriate for
+	 *            the current VM. For example, the system packages for the
+	 *            current execution environment should be properly exported. The
+	 *            specified configuration argument may be {@code null}. The
+	 *            created framework instance must copy any information needed
+	 *            from the specified configuration argument since the
+	 *            configuration argument can be changed after the framework
+	 *            instance has been created.
+	 * @param moduleConnector The module connector that the new framework
+	 *            instance will use. The specified module connector argument may
+	 *            be {@code null}.
+	 * @return A new, configured {@link Framework} instance. The framework
+	 *         instance must be in the {@link Bundle#INSTALLED} state.
+	 * @throws SecurityException If the caller does not have
+	 *             {@code AllPermission}, and the Java Runtime Environment
+	 *             supports permissions.
+	 * @see ModuleConnector
+	 * @since 1.3
+	 */
+	Framework newFramework(Map<String,String> configuration,
+			ModuleConnector moduleConnector);
+}
diff --git a/framework/src/main/java/org/osgi/framework/connect/ConnectModule.java b/framework/src/main/java/org/osgi/framework/connect/ConnectModule.java
new file mode 100644
index 0000000..6f48a00
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectModule.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) OSGi Alliance (2019). All Rights Reserved.
+ *
+ * Licensed 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.osgi.framework.connect;
+
+import java.io.IOException;
+
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleRevision;
+
+/**
+ * A connect module instance is used by a {@link Framework framework} when a
+ * bundle location is connected to connect module. The connected bundle must use
+ * the connect module to load content for the bundle revisions installed in the
+ * framework for the connected bundle.
+ * 
+ * @ThreadSafe
+ * @author $Id: 421e5c4762caa3798def32c52c3c1347648a5606 $
+ */
+public interface ConnectModule {
+	/**
+	 * Returns the current content of this connect module. The framework will
+	 * call this method when it needs to access the content for the current
+	 * {@link BundleRevision bundle revision} that is connected to this connect
+	 * module. The framework may lazily postpone to open the content until right
+	 * before requests to access the bundle revision content are made.
+	 * 
+	 * @return the current content of this connect module
+	 * @throws IOException if an error occurred getting the content
+	 * @see ModuleConnector#connect(String)
+	 */
+	ConnectContent getContent() throws IOException;
+}
diff --git a/framework/src/main/java/org/osgi/framework/connect/FrameworkUtilHelper.java b/framework/src/main/java/org/osgi/framework/connect/FrameworkUtilHelper.java
new file mode 100644
index 0000000..d418a6b
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/FrameworkUtilHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) OSGi Alliance (2019). All Rights Reserved.
+ *
+ * Licensed 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.osgi.framework.connect;
+
+import java.util.Optional;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * A helper for the {@link FrameworkUtil} class. This helper provides
+ * alternative implementations for methods on {@link FrameworkUtil}.
+ */
+public interface FrameworkUtilHelper {
+	/**
+	 * Return a {@code Bundle} associated with the specified class.
+	 * <p>
+	 * This helper method is called by {@link FrameworkUtil#getBundle(Class)} if
+	 * the standard implementation of {@code FrameworkUtil} cannot find the
+	 * bundle.
+	 * 
+	 * @param classFromBundle A class associated with a bundle
+	 * @return An Optional containing a {@code Bundle} for the specified class
+	 *         or an empty Optional if the specified class is not from a bundle.
+	 */
+	default Optional<Bundle> getBundle(Class< ? > classFromBundle) {
+		return Optional.empty();
+	}
+}
diff --git a/framework/src/main/java/org/osgi/framework/connect/ModuleConnector.java b/framework/src/main/java/org/osgi/framework/connect/ModuleConnector.java
new file mode 100644
index 0000000..f2a9af8
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ModuleConnector.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) OSGi Alliance (2019). All Rights Reserved.
+ *
+ * Licensed 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.osgi.framework.connect;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Optional;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * A <code>ModuleConnector</code> provides connections to instances of
+ * {@link ConnectModule} that are used by a {@link Framework} instance to
+ * connect installed bundles locations with content provided by the
+ * <code>ModuleConnector</code>. This allows a <code>ModuleConnector</code> to
+ * provide content and classes for a connected bundle installed in the
+ * <code>Framework</code>. A <code>ModuleConnector</code> is provided when
+ * {@link ConnectFrameworkFactory#newFramework(java.util.Map, ModuleConnector)
+ * creating} a framework instance. Because a <code>ModuleConnector</code>
+ * instance can participate in the initialization of the <code>Framework</code>
+ * and the life cycle of a <code>Framework</code> instance the
+ * <code>ModuleConnector</code> instance should only be used with a single
+ * <code>Framework</code> instance at a time.
+ * 
+ * @ThreadSafe
+ * @author $Id: 3f9a112c56a213eb81baf1fe568ce4f0ecdd1321 $
+ */
+public interface ModuleConnector {
+
+	/**
+	 * Initializes this module connector with the
+	 * {@link Constants#FRAMEWORK_STORAGE framework persistent storage} file and
+	 * framework properties configured for a {@link Framework} instance. This
+	 * method is called once by a {@link Framework} instance and is called
+	 * before any other methods on this module connector are called.
+	 * 
+	 * @param configuration The framework properties used configure the new
+	 *            framework instance. An unmodifiable map of framework
+	 *            configuration properties that were used to create a new
+	 *            framework instance.
+	 * @param storage the persistent storage area used by the {@link Framework}
+	 *            or {@code null} if the platform does not have file system
+	 *            support.
+	 */
+	void initialize(File storage, Map<String,String> configuration);
+
+	/**
+	 * Connects a bundle location with a {@link ConnectModule}. If an
+	 * {@link Optional#empty() empty} optional is returned the the framework
+	 * must handle reading the content of the bundle itself. If a value is
+	 * {@link Optional#isPresent() present} in the returned optional then the
+	 * <code>ConnectModule</code> {@link Optional#get() value} from the optional
+	 * must be used to connect the bundle to the returned {@link ConnectModule}.
+	 * The returned connect module is used by the framework to access the
+	 * content of the bundle.
+	 * 
+	 * @param location the bundle location used to install a bundle
+	 * @return the connect module for the specified bundle location
+	 * @throws BundleException if the location cannot be handled
+	 */
+	Optional<ConnectModule> connect(String location) throws BundleException;
+
+	/**
+	 * Creates a new activator for this module connector. A new activator is
+	 * created by the framework each time the framework is
+	 * {@link Framework#init() initialized}. An activator allows the module
+	 * connector to participate in the framework life cycle. When the framework
+	 * is {@link Framework#init() initialized} the activator
+	 * {@link BundleActivator#start(org.osgi.framework.BundleContext) start}
+	 * method is called. When the framework is {@link Framework#stop() stopped}
+	 * the activator
+	 * {@link BundleActivator#stop(org.osgi.framework.BundleContext) stop}
+	 * method is called
+	 * 
+	 * @return a new activator for this module connector or
+	 *         {@link Optional#empty() empty} if no activator is available
+	 */
+	Optional<BundleActivator> createBundleActivator();
+}
diff --git a/framework/src/main/java/org/osgi/service/url/package-info.java b/framework/src/main/java/org/osgi/framework/connect/package-info.java
similarity index 75%
copy from framework/src/main/java/org/osgi/service/url/package-info.java
copy to framework/src/main/java/org/osgi/framework/connect/package-info.java
index 7053546..3ca3ca6 100644
--- a/framework/src/main/java/org/osgi/service/url/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/connect/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) OSGi Alliance (2010, 2013). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2010, 2019). All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,22 +15,20 @@
  */
 
 /**
- * URL Stream and Content Handlers Package Version 1.0.
- * 
+ * Framework Connect Package Version 1.0.
  * <p>
  * Bundles wishing to use this package must list the package in the
  * Import-Package header of the bundle's manifest.
- * 
  * <p>
  * Example import for consumers using the API in this package:
  * <p>
- * {@code  Import-Package: org.osgi.service.url; version="[1.0,2.0)"}
+ * {@code  Import-Package: org.osgi.framework.connect; version="[1.0,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: a196dc76c5b8d5acb472bd10a4ab382ed58d92d1 $
  */
 
 @Version("1.0")
-package org.osgi.service.url;
+package org.osgi.framework.connect;
 
 import org.osgi.annotation.versioning.Version;
 
diff --git a/framework/src/main/java/org/osgi/framework/dto/BundleDTO.java b/framework/src/main/java/org/osgi/framework/dto/BundleDTO.java
index aa30709..58429b4 100644
--- a/framework/src/main/java/org/osgi/framework/dto/BundleDTO.java
+++ b/framework/src/main/java/org/osgi/framework/dto/BundleDTO.java
@@ -25,7 +25,7 @@ import org.osgi.framework.Bundle;
  * <p>
  * A Bundle can be adapted to provide a {@code BundleDTO} for the Bundle.
  * 
- * @author $Id$
+ * @author $Id: BundleDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class BundleDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/framework/dto/FrameworkDTO.java b/framework/src/main/java/org/osgi/framework/dto/FrameworkDTO.java
index 7c52572..cd1c4e0 100644
--- a/framework/src/main/java/org/osgi/framework/dto/FrameworkDTO.java
+++ b/framework/src/main/java/org/osgi/framework/dto/FrameworkDTO.java
@@ -30,7 +30,7 @@ import org.osgi.framework.BundleContext;
  * framework will contain only the launch properties of the framework. These
  * properties will not include the System properties.
  * 
- * @author $Id$
+ * @author $Id: FrameworkDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class FrameworkDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/framework/dto/ServiceReferenceDTO.java b/framework/src/main/java/org/osgi/framework/dto/ServiceReferenceDTO.java
index 87265de..0ee46b7 100644
--- a/framework/src/main/java/org/osgi/framework/dto/ServiceReferenceDTO.java
+++ b/framework/src/main/java/org/osgi/framework/dto/ServiceReferenceDTO.java
@@ -32,7 +32,7 @@ import org.osgi.framework.ServiceReference;
  * property values which are not valid value types for DTOs to type
  * {@code String} using {@code String.valueOf(Object)}.
  * 
- * @author $Id$
+ * @author $Id: ServiceReferenceDTO.java 1825132 2018-02-23 15:11:00Z pauls $
  * @NotThreadSafe
  */
 public class ServiceReferenceDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/framework/dto/package-info.java b/framework/src/main/java/org/osgi/framework/dto/package-info.java
index 2acfb6f..286362b 100644
--- a/framework/src/main/java/org/osgi/framework/dto/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/dto/package-info.java
@@ -32,7 +32,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.dto; version="[1.8,1.9)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.8")
diff --git a/framework/src/main/java/org/osgi/framework/hooks/bundle/package-info.java b/framework/src/main/java/org/osgi/framework/hooks/bundle/package-info.java
index 08c20ca..f701a5f 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/bundle/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/bundle/package-info.java
@@ -26,7 +26,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.hooks.bundle; version="[1.1,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/hooks/resolver/package-info.java b/framework/src/main/java/org/osgi/framework/hooks/resolver/package-info.java
index de05003..2f447ff 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/resolver/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/resolver/package-info.java
@@ -26,7 +26,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.hooks.resolver; version="[1.0,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.0")
diff --git a/framework/src/main/java/org/osgi/framework/hooks/service/package-info.java b/framework/src/main/java/org/osgi/framework/hooks/service/package-info.java
index 74f0c41..1cdd0e9 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/service/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/service/package-info.java
@@ -26,7 +26,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.hooks.service; version="[1.1,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/hooks/weaving/WovenClassListener.java b/framework/src/main/java/org/osgi/framework/hooks/weaving/WovenClassListener.java
index 4a7a699..915b249 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/weaving/WovenClassListener.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/weaving/WovenClassListener.java
@@ -47,7 +47,7 @@ import org.osgi.annotation.versioning.ConsumerType;
  * 
  * @ThreadSafe
  * @since 1.1
- * @author $Id$
+ * @author $Id: WovenClassListener.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 @ConsumerType
 public interface WovenClassListener {
diff --git a/framework/src/main/java/org/osgi/framework/hooks/weaving/package-info.java b/framework/src/main/java/org/osgi/framework/hooks/weaving/package-info.java
index 5b48f04..05810dd 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/weaving/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/weaving/package-info.java
@@ -28,7 +28,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.hooks.weaving; version="[1.1,2.0)"}
  * </p>
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/launch/package-info.java b/framework/src/main/java/org/osgi/framework/launch/package-info.java
index db5e926..ca81049 100644
--- a/framework/src/main/java/org/osgi/framework/launch/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/launch/package-info.java
@@ -26,7 +26,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.launch; version="[1.2,2.0)"}
  *
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.2")
diff --git a/framework/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java b/framework/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java
index cfbb843..ffb9703 100644
--- a/framework/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java
+++ b/framework/src/main/java/org/osgi/framework/namespace/IdentityNamespace.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) OSGi Alliance (2012, 2013). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2012, 2019). All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ import org.osgi.resource.Namespace;
  * capability.
  * 
  * @Immutable
- * @author $Id: 7bc7a11c45b30538ffbb7572c4539f6160557684 $
+ * @author $Id: 27ae5d1c50d9fffcd7089d0fb225d8e793c4677a $
  */
 public final class IdentityNamespace extends Namespace {
 
@@ -105,6 +105,16 @@ public final class IdentityNamespace extends Namespace {
 	public static final String	TYPE_UNKNOWN						= "unknown";
 
 	/**
+	 * The attribute value that contains tags for the resource. A tag is used to
+	 * identify an aspect of the resource that is not otherwise expressed by the
+	 * capabilities of the resource. The value of this attribute must be of type
+	 * {@code List<String>}.
+	 * 
+	 * @since 1.2
+	 */
+	public static final String	CAPABILITY_TAGS_ATTRIBUTE			= "tags";
+
+	/**
 	 * The capability attribute that contains a human readable copyright notice
 	 * for the resource. See the {@code Bundle-Copyright} manifest header.
 	 */
diff --git a/framework/src/main/java/org/osgi/framework/namespace/NativeNamespace.java b/framework/src/main/java/org/osgi/framework/namespace/NativeNamespace.java
index fc91420..eb30c71 100644
--- a/framework/src/main/java/org/osgi/framework/namespace/NativeNamespace.java
+++ b/framework/src/main/java/org/osgi/framework/namespace/NativeNamespace.java
@@ -31,7 +31,7 @@ import org.osgi.resource.Namespace;
  * 
  * @Immutable
  * @since 1.1
- * @author $Id$
+ * @author $Id: NativeNamespace.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 public final class NativeNamespace extends Namespace {
 
diff --git a/framework/src/main/java/org/osgi/framework/namespace/package-info.java b/framework/src/main/java/org/osgi/framework/namespace/package-info.java
index e89f34a..7d661e3 100644
--- a/framework/src/main/java/org/osgi/framework/namespace/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/namespace/package-info.java
@@ -22,7 +22,7 @@
  * the types in this package just contain constants for capability and 
  * requirement namespaces specified by the OSGi Alliance.
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/package-info.java b/framework/src/main/java/org/osgi/framework/package-info.java
index 818d4ae..a4283d3 100644
--- a/framework/src/main/java/org/osgi/framework/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/package-info.java
@@ -24,10 +24,10 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework; version="[1.9,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1874504 2020-02-25 15:53:01Z pauls $
  */
 
-@Version("1.9")
+@Version("1.10")
 package org.osgi.framework;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/framework/src/main/java/org/osgi/framework/startlevel/dto/BundleStartLevelDTO.java b/framework/src/main/java/org/osgi/framework/startlevel/dto/BundleStartLevelDTO.java
index 81430e2..f430124 100644
--- a/framework/src/main/java/org/osgi/framework/startlevel/dto/BundleStartLevelDTO.java
+++ b/framework/src/main/java/org/osgi/framework/startlevel/dto/BundleStartLevelDTO.java
@@ -26,7 +26,7 @@ import org.osgi.framework.startlevel.BundleStartLevel;
  * An installed Bundle can be adapted to provide a {@code BundleStartLevelDTO}
  * for the Bundle.
  * 
- * @author $Id$
+ * @author $Id: BundleStartLevelDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class BundleStartLevelDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/framework/startlevel/dto/FrameworkStartLevelDTO.java b/framework/src/main/java/org/osgi/framework/startlevel/dto/FrameworkStartLevelDTO.java
index 2d1de40..0ea44fe 100644
--- a/framework/src/main/java/org/osgi/framework/startlevel/dto/FrameworkStartLevelDTO.java
+++ b/framework/src/main/java/org/osgi/framework/startlevel/dto/FrameworkStartLevelDTO.java
@@ -26,7 +26,7 @@ import org.osgi.framework.startlevel.FrameworkStartLevel;
  * The System Bundle can be adapted to provide a {@code FrameworkStartLevelDTO}
  * for the framework of the Bundle.
  * 
- * @author $Id$
+ * @author $Id: FrameworkStartLevelDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class FrameworkStartLevelDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/framework/startlevel/dto/package-info.java b/framework/src/main/java/org/osgi/framework/startlevel/dto/package-info.java
index 62262c5..2ef56ec 100644
--- a/framework/src/main/java/org/osgi/framework/startlevel/dto/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/startlevel/dto/package-info.java
@@ -32,7 +32,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.startlevel.dto; version="[1.0,1.1)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.0")
diff --git a/framework/src/main/java/org/osgi/framework/startlevel/package-info.java b/framework/src/main/java/org/osgi/framework/startlevel/package-info.java
index 27d775b..14618ed 100644
--- a/framework/src/main/java/org/osgi/framework/startlevel/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/startlevel/package-info.java
@@ -73,7 +73,7 @@
  * Import-Package: org.osgi.framework.startlevel; version=&quot;[1.0,2.0)&quot;
  * </pre>
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.0")
diff --git a/framework/src/main/java/org/osgi/framework/wiring/dto/BundleRevisionDTO.java b/framework/src/main/java/org/osgi/framework/wiring/dto/BundleRevisionDTO.java
index 43d7416..b9d4580 100644
--- a/framework/src/main/java/org/osgi/framework/wiring/dto/BundleRevisionDTO.java
+++ b/framework/src/main/java/org/osgi/framework/wiring/dto/BundleRevisionDTO.java
@@ -28,7 +28,7 @@ import org.osgi.resource.dto.ResourceDTO;
  * in use revisions of the Bundle can be obtained by adapting the bundle to
  * {@code BundleRevisionDTO[]}.
  * 
- * @author $Id$
+ * @author $Id: BundleRevisionDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class BundleRevisionDTO extends ResourceDTO {
diff --git a/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWireDTO.java b/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWireDTO.java
index ef923b4..967ece4 100644
--- a/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWireDTO.java
+++ b/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWireDTO.java
@@ -26,7 +26,7 @@ import org.osgi.resource.dto.WiringDTO;
  * <p>
  * {@code BundleWireDTO}s are referenced {@link BundleWiringDTO.NodeDTO}s.
  * 
- * @author $Id$
+ * @author $Id: BundleWireDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class BundleWireDTO extends WireDTO {
diff --git a/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWiringDTO.java b/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWiringDTO.java
index e23e80f..74f0b35 100644
--- a/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWiringDTO.java
+++ b/framework/src/main/java/org/osgi/framework/wiring/dto/BundleWiringDTO.java
@@ -30,7 +30,7 @@ import org.osgi.resource.dto.WiringDTO;
  * wirings of the Bundle can be obtained by adapting the bundle to
  * {@code BundleWiringDTO[]}.
  * 
- * @author $Id$
+ * @author $Id: BundleWiringDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class BundleWiringDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/framework/wiring/dto/package-info.java b/framework/src/main/java/org/osgi/framework/wiring/dto/package-info.java
index 47eb1ee..8db1a91 100644
--- a/framework/src/main/java/org/osgi/framework/wiring/dto/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/wiring/dto/package-info.java
@@ -30,7 +30,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.framework.wiring.dto; version="[1.3,1.4)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 
 @Version("1.3")
diff --git a/framework/src/main/java/org/osgi/framework/wiring/package-info.java b/framework/src/main/java/org/osgi/framework/wiring/package-info.java
index 6fb7232..27c3f94 100644
--- a/framework/src/main/java/org/osgi/framework/wiring/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/wiring/package-info.java
@@ -25,7 +25,7 @@
  * Import-Package: org.osgi.framework.wiring; version=&quot;[1.2,2.0)&quot;
  * </pre>
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.2")
diff --git a/framework/src/main/java/org/osgi/resource/dto/CapabilityDTO.java b/framework/src/main/java/org/osgi/resource/dto/CapabilityDTO.java
index 1f7fad1..941ec8f 100644
--- a/framework/src/main/java/org/osgi/resource/dto/CapabilityDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/CapabilityDTO.java
@@ -23,7 +23,7 @@ import org.osgi.resource.Capability;
 /**
  * Data Transfer Object for a Capability.
  * 
- * @author $Id$
+ * @author $Id: CapabilityDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class CapabilityDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/CapabilityRefDTO.java b/framework/src/main/java/org/osgi/resource/dto/CapabilityRefDTO.java
index 81d5b85..4a94459 100644
--- a/framework/src/main/java/org/osgi/resource/dto/CapabilityRefDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/CapabilityRefDTO.java
@@ -21,7 +21,7 @@ import org.osgi.dto.DTO;
 /**
  * Data Transfer Object for a reference to a Capability.
  * 
- * @author $Id$
+ * @author $Id: CapabilityRefDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class CapabilityRefDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/RequirementDTO.java b/framework/src/main/java/org/osgi/resource/dto/RequirementDTO.java
index dfa21db..d8e02a0 100644
--- a/framework/src/main/java/org/osgi/resource/dto/RequirementDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/RequirementDTO.java
@@ -23,7 +23,7 @@ import org.osgi.resource.Requirement;
 /**
  * Data Transfer Object for a Requirement.
  * 
- * @author $Id$
+ * @author $Id: RequirementDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class RequirementDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/RequirementRefDTO.java b/framework/src/main/java/org/osgi/resource/dto/RequirementRefDTO.java
index 8f913a7..9f8c316 100644
--- a/framework/src/main/java/org/osgi/resource/dto/RequirementRefDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/RequirementRefDTO.java
@@ -21,7 +21,7 @@ import org.osgi.dto.DTO;
 /**
  * Data Transfer Object for a reference to a Requirement.
  * 
- * @author $Id$
+ * @author $Id: RequirementRefDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class RequirementRefDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/ResourceDTO.java b/framework/src/main/java/org/osgi/resource/dto/ResourceDTO.java
index 377b7af..5e35165 100644
--- a/framework/src/main/java/org/osgi/resource/dto/ResourceDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/ResourceDTO.java
@@ -23,7 +23,7 @@ import org.osgi.resource.Resource;
 /**
  * Data Transfer Object for a Resource.
  * 
- * @author $Id$
+ * @author $Id: ResourceDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class ResourceDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/WireDTO.java b/framework/src/main/java/org/osgi/resource/dto/WireDTO.java
index 017ea7c..00b61d8 100644
--- a/framework/src/main/java/org/osgi/resource/dto/WireDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/WireDTO.java
@@ -22,7 +22,7 @@ import org.osgi.resource.Wire;
 /**
  * Data Transfer Object for a Wire.
  * 
- * @author $Id$
+ * @author $Id: WireDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class WireDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/WiringDTO.java b/framework/src/main/java/org/osgi/resource/dto/WiringDTO.java
index 8dc3844..4484658 100644
--- a/framework/src/main/java/org/osgi/resource/dto/WiringDTO.java
+++ b/framework/src/main/java/org/osgi/resource/dto/WiringDTO.java
@@ -23,7 +23,7 @@ import org.osgi.resource.Wiring;
 /**
  * Data Transfer Object for a Wiring node.
  * 
- * @author $Id$
+ * @author $Id: WiringDTO.java 1614569 2014-07-30 07:22:32Z cziegeler $
  * @NotThreadSafe
  */
 public class WiringDTO extends DTO {
diff --git a/framework/src/main/java/org/osgi/resource/dto/package-info.java b/framework/src/main/java/org/osgi/resource/dto/package-info.java
index 6e316cd..de4e2c9 100644
--- a/framework/src/main/java/org/osgi/resource/dto/package-info.java
+++ b/framework/src/main/java/org/osgi/resource/dto/package-info.java
@@ -32,7 +32,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.resource.dto; version="[1.0,1.1)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.0")
diff --git a/framework/src/main/java/org/osgi/resource/package-info.java b/framework/src/main/java/org/osgi/resource/package-info.java
index c537ca9..c0349ff 100644
--- a/framework/src/main/java/org/osgi/resource/package-info.java
+++ b/framework/src/main/java/org/osgi/resource/package-info.java
@@ -25,7 +25,7 @@
  * Import-Package: org.osgi.resource; version=&quot;[1.0,2.0)&quot;
  * </pre>
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.0")
diff --git a/framework/src/main/java/org/osgi/service/packageadmin/package-info.java b/framework/src/main/java/org/osgi/service/packageadmin/package-info.java
index 41bb8d3..1baf3bd 100644
--- a/framework/src/main/java/org/osgi/service/packageadmin/package-info.java
+++ b/framework/src/main/java/org/osgi/service/packageadmin/package-info.java
@@ -31,7 +31,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.service.packageadmin; version="[1.2,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 
 @Version("1.2")
diff --git a/framework/src/main/java/org/osgi/service/startlevel/package-info.java b/framework/src/main/java/org/osgi/service/startlevel/package-info.java
index 70b62bd..c0954f1 100644
--- a/framework/src/main/java/org/osgi/service/startlevel/package-info.java
+++ b/framework/src/main/java/org/osgi/service/startlevel/package-info.java
@@ -31,7 +31,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.service.startlevel; version="[1.1,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 
 @Version("1.1")
diff --git a/framework/src/main/java/org/osgi/service/url/package-info.java b/framework/src/main/java/org/osgi/service/url/package-info.java
index 7053546..3e73e2b 100644
--- a/framework/src/main/java/org/osgi/service/url/package-info.java
+++ b/framework/src/main/java/org/osgi/service/url/package-info.java
@@ -26,7 +26,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.service.url; version="[1.0,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1614569 2014-07-30 07:22:32Z cziegeler $
  */
 
 @Version("1.0")
diff --git a/framework/src/main/java/org/osgi/util/tracker/package-info.java b/framework/src/main/java/org/osgi/util/tracker/package-info.java
index b72e066..b0007b8 100644
--- a/framework/src/main/java/org/osgi/util/tracker/package-info.java
+++ b/framework/src/main/java/org/osgi/util/tracker/package-info.java
@@ -26,7 +26,7 @@
  * <p>
  * {@code  Import-Package: org.osgi.util.tracker; version="[1.5,2.0)"}
  * 
- * @author $Id$
+ * @author $Id: package-info.java 1825132 2018-02-23 15:11:00Z pauls $
  */
 
 @Version("1.5.2")
diff --git a/framework/src/main/resources/META-INF/services/org.osgi.framework.connect.ConnectFrameworkFactory b/framework/src/main/resources/META-INF/services/org.osgi.framework.connect.ConnectFrameworkFactory
new file mode 100644
index 0000000..3708ae5
--- /dev/null
+++ b/framework/src/main/resources/META-INF/services/org.osgi.framework.connect.ConnectFrameworkFactory
@@ -0,0 +1 @@
+org.apache.felix.framework.FrameworkFactory
\ No newline at end of file
diff --git a/framework/src/main/resources/default.properties b/framework/src/main/resources/default.properties
index f6b4821..fc7163e 100644
--- a/framework/src/main/resources/default.properties
+++ b/framework/src/main/resources/default.properties
@@ -100,7 +100,12 @@ ee-1.6=JavaSE-1.6,J2SE-1.5,J2SE-1.4,J2SE-1.3,J2SE-1.2, \
 
 # Default packages exported by system bundle.
 org.osgi.framework.system.packages=\
- org.osgi.framework;version="1.9", \
+ ${dollar}{osgi-exports} \
+ ${dollar}{jre-${dollar}{felix.detect.java.specification.version}} \
+ ${dollar}{jre-${dollar}{felix.detect.jpms}}
+
+osgi-exports= \
+ org.osgi.framework;version="1.10", \
  org.osgi.framework.dto;version="1.8";uses:="org.osgi.dto", \
  org.osgi.framework.hooks.bundle;version="1.1";uses:="org.osgi.framework", \
  org.osgi.framework.hooks.resolver;version="1.0";uses:="org.osgi.framework.wiring", \
@@ -119,9 +124,8 @@ org.osgi.framework.system.packages=\
  org.osgi.service.url;version="1.0", \
  org.osgi.service.resolver;version="1.1";uses:="org.osgi.resource", \
  org.osgi.util.tracker;version="1.5.2";uses:="org.osgi.framework", \
- org.osgi.dto;version="1.1" \
- ${dollar}{jre-${dollar}{felix.detect.java.specification.version}} \
- ${dollar}{jre-${dollar}{felix.detect.jpms}}
+ org.osgi.dto;version="1.1", \
+ org.osgi.framework.connect;version="1.0.0"
 
 #
 # Java platform package export properties.
@@ -374,4 +378,4 @@ jre-1.8= \
  java.util.function;version="${dollar}{felix.detect.java.version}", \
  java.util.stream;version="${dollar}{felix.detect.java.version}", \
  javax.crypto;uses:="javax.crypto.spec,javax.security.auth";version="${dollar}{felix.detect.java.version}", \
- javax.lang.model;uses:="javax.lang.model.element";version="${dollar}{felix.detect.java.version}"
\ No newline at end of file
+ javax.lang.model;uses:="javax.lang.model.element";version="${dollar}{felix.detect.java.version}"
diff --git a/framework/src/main/resources/org/apache/felix/framework/util/accessor.bytes b/framework/src/main/resources/org/apache/felix/framework/util/accessor.bytes
new file mode 100644
index 0000000..f395846
Binary files /dev/null and b/framework/src/main/resources/org/apache/felix/framework/util/accessor.bytes differ
diff --git a/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java b/framework/src/main/resources/org/apache/felix/framework/util/accessor.src
similarity index 72%
copy from framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
copy to framework/src/main/resources/org/apache/felix/framework/util/accessor.src
index ce377ad..2fe3e07 100644
--- a/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
+++ b/framework/src/main/resources/org/apache/felix/framework/util/accessor.src
@@ -6,9 +6,9 @@
  * 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
@@ -16,15 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.felix.framework;
+package java.net;
 
-import java.util.Map;
-import org.osgi.framework.launch.Framework;
+import java.lang.reflect.AccessibleObject;
+import java.util.function.Consumer;
 
-public class FrameworkFactory implements org.osgi.framework.launch.FrameworkFactory
+public class Accessor implements Consumer<AccessibleObject[]>
 {
-    public Framework newFramework(Map configuration)
+    @Override
+    public void accept(AccessibleObject[] accessibleObjectStream)
     {
-        return new Felix(configuration);
+        AccessibleObject.setAccessible(accessibleObjectStream, true);
     }
-}
\ No newline at end of file
+}
diff --git a/framework/src/test/java/org/apache/felix/framework/CollisionHookTest.java b/framework/src/test/java/org/apache/felix/framework/CollisionHookTest.java
index a04fe4a..0861de8 100644
--- a/framework/src/test/java/org/apache/felix/framework/CollisionHookTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/CollisionHookTest.java
@@ -214,7 +214,7 @@ public class CollisionHookTest extends TestCase
 
         try
         {
-            new BundleImpl(felixMock, null, archive);
+            new BundleImpl(felixMock, new BundleImpl(), archive);
             fail("Should have thrown a BundleException because the collision hook is not enabled");
         }
         catch (BundleException be)
@@ -290,7 +290,7 @@ public class CollisionHookTest extends TestCase
 
         try
         {
-            new BundleImpl(felixMock, null, archive);
+            new BundleImpl(felixMock, new BundleImpl(), archive);
             fail("Should have thrown a BundleException because the installed bundle is not unique");
         }
         catch (BundleException be)
diff --git a/framework/src/test/java/org/apache/felix/framework/ConnectTest.java b/framework/src/test/java/org/apache/felix/framework/ConnectTest.java
new file mode 100644
index 0000000..bcaeb7c
--- /dev/null
+++ b/framework/src/test/java/org/apache/felix/framework/ConnectTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+
+import junit.framework.TestCase;
+import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.Version;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ModuleConnector;
+import org.osgi.framework.connect.ConnectModule;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+public class ConnectTest extends TestCase
+{
+    public void testSimpleConnect() throws Exception {
+        File cacheDir = File.createTempFile("felix-cache", ".dir");
+        cacheDir.delete();
+        cacheDir.mkdirs();
+        String cache = cacheDir.getPath();
+
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("felix.cache.profiledir", cache);
+        params.put("felix.cache.dir", cache);
+        params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+
+        FrameworkFactory factory = new FrameworkFactory();
+        Framework framework = null;
+        try
+        {
+            final AtomicReference<String> version = new AtomicReference<String>("1.0.0");
+            ModuleConnector connectFactory = new ModuleConnector()
+            {
+                @Override
+                public void initialize(File storage, Map<String, String> configuration)
+                {
+
+                }
+
+                @Override
+                public Optional<ConnectModule> connect(String location) throws IllegalStateException
+                {
+                    return location.startsWith("connect:foo") ? Optional.of(new ConnectModule()
+                    {
+                        @Override
+                        public ConnectContent getContent() throws IOException
+                        {
+                            return new ConnectContent()
+                            {
+                                @Override
+                                public Optional<ConnectEntry> getEntry(String name)
+                                {
+
+                                    return "foo.txt".equals(name) ? Optional.of(
+                                        new ConnectEntry()
+                                        {
+                                            @Override
+                                            public String getName()
+                                            {
+                                                return name;
+                                            }
+
+                                            @Override
+                                            public long getContentLength()
+                                            {
+                                                return 0;
+                                            }
+
+                                            @Override
+                                            public long getLastModified()
+                                            {
+                                                return 0;
+                                            }
+
+                                            @Override
+                                            public InputStream getInputStream() throws IOException
+                                            {
+                                                return null;
+                                            }
+                                        }
+                                    ) : Optional.empty();
+                                }
+
+                                @Override
+                                public Iterable<String> getEntries()
+                                {
+                                    return Arrays.asList("foo.txt");
+                                }
+
+                                @Override
+                                public Optional<ClassLoader> getClassLoader()
+                                {
+                                    return Optional.of(getClass().getClassLoader());
+                                }
+
+                                @Override
+                                public void open() throws IOException
+                                {
+                                }
+
+                                @Override
+                                public void close() throws IOException
+                                {
+                                }
+
+                                @Override
+                                public Optional<Map<String, String>> getHeaders()
+                                {
+                                    Map<String, String> headers = new HashMap<String, String>();
+                                    headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+                                    headers.put(Constants.BUNDLE_SYMBOLICNAME, "connect.foo");
+                                    headers.put(Constants.BUNDLE_VERSION, version.get());
+                                    headers.put(Constants.EXPORT_PACKAGE, ConnectTest.class.getPackage().getName());
+                                    return Optional.of(headers);
+                                }
+                            };
+                        }
+
+
+                    }) : location.startsWith("connect:extension") ? Optional.of(new ConnectModule()
+                    {
+                        @Override
+                        public ConnectContent getContent() throws IOException
+                        {
+                            return new ConnectContent()
+                            {
+                                @Override
+                                public Optional<ConnectEntry> getEntry(String name)
+                                {
+
+                                    return "foo.txt".equals(name) ? Optional.of(
+                                        new ConnectEntry()
+                                        {
+                                            @Override
+                                            public String getName()
+                                            {
+                                                return name;
+                                            }
+
+                                            @Override
+                                            public long getContentLength()
+                                            {
+                                                return 0;
+                                            }
+
+                                            @Override
+                                            public long getLastModified()
+                                            {
+                                                return 0;
+                                            }
+
+                                            @Override
+                                            public InputStream getInputStream() throws IOException
+                                            {
+                                                return null;
+                                            }
+                                        }
+                                    ) : Optional.empty();
+                                }
+
+                                @Override
+                                public Iterable<String> getEntries()
+                                {
+                                    return Arrays.asList("foo.txt");
+                                }
+
+                                @Override
+                                public Optional<ClassLoader> getClassLoader()
+                                {
+                                    return Optional.of(getClass().getClassLoader());
+                                }
+
+                                @Override
+                                public void open() throws IOException
+                                {
+
+                                }
+
+                                @Override
+                                public void close() throws IOException
+                                {
+
+                                }
+
+                                @Override
+                                public Optional<Map<String, String>> getHeaders()
+                                {
+                                    Map<String, String> headers = new HashMap<String, String>();
+                                    headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+                                    headers.put(Constants.BUNDLE_SYMBOLICNAME, "connect.extension");
+                                    headers.put(Constants.BUNDLE_VERSION, "1.0.0");
+                                    headers.put(Constants.FRAGMENT_HOST, "system.bundle;extension:=framework");
+                                    return Optional.of(headers);
+                                }
+                            };
+                        }
+
+
+                    })
+                        : Optional.empty();
+                }
+
+                @Override
+                public Optional<BundleActivator> createBundleActivator()
+                {
+                    return Optional.empty();
+                }
+            };
+
+            framework = factory.newFramework(params, connectFactory);
+
+            framework.start();
+            Bundle b = framework.getBundleContext().installBundle("connect:foo");
+
+            TestCase.assertNotNull(b);
+            TestCase.assertEquals("connect.foo", b.getSymbolicName());
+            TestCase.assertEquals(b, framework.getBundleContext().getBundle("connect:foo"));
+
+            TestCase.assertNotNull(b.getEntry("foo.txt"));
+            TestCase.assertNull(b.getEntry("bar.txt"));
+
+            Bundle extension = framework.getBundleContext().installBundle("connect:extension");
+
+            TestCase.assertEquals(Bundle.RESOLVED, extension.getState());
+
+            framework.stop();
+            framework.waitForStop(1000);
+            framework = factory.newFramework(params, connectFactory);
+            framework.start();
+
+            b = framework.getBundleContext().getBundle("connect:foo");
+            assertNotNull(b);
+            TestCase.assertEquals("connect.foo", b.getSymbolicName());
+            TestCase.assertNotNull(b.getEntry("foo.txt"));
+            TestCase.assertNull(b.getEntry("bar.txt"));
+
+            TestCase.assertEquals(ConnectTest.class, b.loadClass(ConnectTest.class.getName()));
+
+            String mf = "Bundle-SymbolicName: connect.test\n"
+                + "Bundle-Version: 1.0.0\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Import-Package: " + ConnectTest.class.getPackage().getName()
+                + "\n";
+
+            File bundleFile = createBundle(mf, cacheDir, StringMap.class);
+
+            Bundle b2 = framework.getBundleContext().installBundle(bundleFile.toURI().toURL().toString());
+            b2.start();
+            TestCase.assertEquals(Bundle.ACTIVE, b2.getState());
+            TestCase.assertEquals(ConnectTest.class, b2.loadClass(ConnectTest.class.getName()));
+            TestCase.assertNotSame(StringMap.class, b2.loadClass(StringMap.class.getName()));
+            TestCase.assertEquals(Version.parseVersion("1.0.0"), b.getVersion());
+            Version revVersion = b.adapt(BundleRevision.class).getVersion();
+
+            TestCase.assertEquals(b.adapt(BundleRevision.class),
+                b2.adapt(BundleWiring.class).getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE).get(0).getProvider());
+
+            version.set("2.0.0");
+
+            b.update();
+
+            final CountDownLatch latch = new CountDownLatch(1);
+
+            framework.adapt(FrameworkWiring.class).refreshBundles(Arrays.asList(b), new FrameworkListener()
+            {
+                @Override
+                public void frameworkEvent(FrameworkEvent event)
+                {
+                    latch.countDown();
+                }
+            });
+
+            latch.await(1, TimeUnit.SECONDS);
+
+
+            TestCase.assertEquals(Version.parseVersion("2.0.0"), b.getVersion());
+
+            TestCase.assertEquals(Bundle.ACTIVE, b2.getState());
+            TestCase.assertEquals(ConnectTest.class, b2.loadClass(ConnectTest.class.getName()));
+            TestCase.assertNotSame(StringMap.class, b2.loadClass(StringMap.class.getName()));
+            TestCase.assertEquals(b.adapt(BundleRevision.class),
+                b2.adapt(BundleWiring.class).getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE).get(0).getProvider());
+            TestCase.assertNotSame(revVersion, b2.adapt(BundleWiring.class).getRequiredWires(PackageNamespace.PACKAGE_NAMESPACE).get(0).getProvider());
+
+        }
+        finally
+        {
+            try
+            {
+                if (framework != null)
+                {
+                    framework.stop();
+                    framework.waitForStop(1000);
+                }
+            }
+            finally {
+                MultiReleaseVersionTest.deleteDir(cacheDir);
+            }
+        }
+    }
+
+    private static File createBundle(String manifest, File tempDir, Class... classes) throws IOException
+    {
+        File f = File.createTempFile("felix-bundle", ".jar", tempDir);
+
+        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);
+
+        for (Class c : classes)
+        {
+            String path = c.getName().replace('.', '/') + ".class";
+            os.putNextEntry(new ZipEntry(path));
+
+            InputStream is = c.getClassLoader().getResourceAsStream(path);
+            byte[] b = new byte[is.available()];
+            is.read(b);
+            is.close();
+            os.write(b);
+        }
+
+        os.close();
+        return f;
+    }
+}
diff --git a/framework/src/test/java/org/apache/felix/framework/MultiReleaseVersionTest.java b/framework/src/test/java/org/apache/felix/framework/MultiReleaseVersionTest.java
index 27ba50f..be40789 100644
--- a/framework/src/test/java/org/apache/felix/framework/MultiReleaseVersionTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/MultiReleaseVersionTest.java
@@ -122,7 +122,7 @@ public class MultiReleaseVersionTest extends TestCase
         return f;
     }
 
-    private static void deleteDir(File root) throws IOException
+    public static void deleteDir(File root) throws IOException
     {
         if (root.isDirectory())
         {
diff --git a/framework/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java b/framework/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java
index 3c8e478..b83eef4 100644
--- a/framework/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/ServiceRegistryTest.java
@@ -1237,7 +1237,10 @@ public class ServiceRegistryTest extends TestCase
         ServiceRegistry sr = new ServiceRegistry(null, null);
         Bundle regBundle = Mockito.mock(Bundle.class);
 
-        ServiceReference<String> ref = registerService(sr, regBundle, "hi");
+        ServiceRegistration reg = sr.registerService(
+                regBundle, new String [] {String.class.getName()}, "hi", null);
+        @SuppressWarnings("unchecked")
+        ServiceReference<String> ref = reg.getReference();
 
         final Bundle clientBundle = Mockito.mock(Bundle.class);
         Mockito.when(clientBundle.getBundleId()).thenReturn(42L);
@@ -1247,41 +1250,9 @@ public class ServiceRegistryTest extends TestCase
         Mockito.when(clientBundle.getBundleId()).thenReturn(327L);
         assertThat(sr.getService(clientBundle2, ref, false), is("hi"));
 
-        assertThat(sr.ungetService(clientBundle, ref, null), is(true));
-
-        assertThat(sr.getUsingBundles(ref), is(new Bundle[]{clientBundle2}));
-    }
-
-    public void testServicesInUseWithoutZeroCounts() throws Exception
-    {
-        ServiceRegistry sr = new ServiceRegistry(null, null);
-        Bundle regBundle = Mockito.mock(Bundle.class);
-
-        ServiceReference<String> refHi = registerService(sr, regBundle, "hi");
-        ServiceReference<String> refBye = registerService(sr, regBundle, "bye");
-
-        final Bundle clientBundle = Mockito.mock(Bundle.class);
-        Mockito.when(clientBundle.getBundleId()).thenReturn(42L);
-
-        sr.getService(clientBundle, refHi, false);
-        sr.getService(clientBundle, refBye, false);
-        assertThat(sr.getServicesInUse(clientBundle).length, is(2));
-
-        sr.ungetService(clientBundle, refBye, null);
-        assertThat(sr.getServicesInUse(clientBundle), is(new ServiceReference[]{refHi}));
-
-        sr.ungetService(clientBundle, refHi, null);
-        assertThat(sr.getServicesInUse(clientBundle), nullValue());
-    }
-
-    private ServiceReference<String> registerService(ServiceRegistry sr, Bundle regBundle, String svcObj) {
-        ServiceRegistration reg = sr.registerService(
-                regBundle, new String [] {String.class.getName()}, svcObj, null);
-
-        @SuppressWarnings("unchecked")
-        ServiceReference<String> ref =  reg.getReference();
+        assertThat(sr.ungetService(clientBundle, reg.getReference(), null), is(true));
 
-        return ref;
+        assertThat(sr.getUsingBundles(reg.getReference()), is(new Bundle[]{clientBundle2}));
     }
 
     private Object getPrivateField(Object obj, String fieldName) throws NoSuchFieldException,
diff --git a/framework/src/test/java/org/apache/felix/framework/cache/BundleCacheTest.java b/framework/src/test/java/org/apache/felix/framework/cache/BundleCacheTest.java
index fc83cb1..5dbfb74 100644
--- a/framework/src/test/java/org/apache/felix/framework/cache/BundleCacheTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/cache/BundleCacheTest.java
@@ -114,15 +114,15 @@ public class BundleCacheTest extends TestCase
 
         output.close();
 
-        BundleArchive archive = cache.create(1, 1, "slip", new FileInputStream(bundle));
+        BundleArchive archive = cache.create(1, 1, "slip", new FileInputStream(bundle), null);
 
         testNoZipSlip(archive);
 
-        archive = cache.create(1, 1, bundle.toURI().toURL().toString(), null);
+        archive = cache.create(1, 1, bundle.toURI().toURL().toString(), null, null);
 
         testNoZipSlip(archive);
 
-        archive = cache.create(1, 1, "reference:" + bundle.toURI().toURL().toString(), null);
+        archive = cache.create(1, 1, "reference:" + bundle.toURI().toURL().toString(), null, null);
 
         testNoZipSlip(archive);
 
@@ -134,7 +134,7 @@ public class BundleCacheTest extends TestCase
         test.createNewFile();
         test.deleteOnExit();
 
-        archive = cache.create(1, 1, "reference:" + dir.toURI().toURL().toString(), null);
+        archive = cache.create(1, 1, "reference:" + dir.toURI().toURL().toString(), null, null);
 
         testNoZipSlip(archive);
     }
@@ -172,7 +172,7 @@ public class BundleCacheTest extends TestCase
 
     private void testBundle(String location, File file) throws Exception
     {
-        BundleArchive archive = cache.create(1, 1, location, file != null ? new FileInputStream(file) : null);
+        BundleArchive archive = cache.create(1, 1, location, file != null ? new FileInputStream(file) : null, null);
 
         assertNotNull(archive);
 
diff --git a/framework/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java b/framework/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java
index c95e021..93db1ce 100644
--- a/framework/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/util/manifestparser/ManifestParserTest.java
@@ -28,6 +28,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.felix.framework.BundleRevisionImpl;
+import org.apache.felix.framework.cache.ConnectContentContent;
+import org.apache.felix.framework.cache.Content;
 import org.apache.felix.framework.util.FelixConstants;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
@@ -35,6 +38,7 @@ import org.osgi.framework.Filter;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
+import org.osgi.framework.connect.ConnectContent;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.framework.namespace.NativeNamespace;
 import org.osgi.framework.wiring.BundleCapability;
@@ -62,7 +66,7 @@ public class ManifestParserTest extends TestCase
     {
         Map<String, Object> headers = new HashMap<String, Object>();
         headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
-        headers.put(Constants.BUNDLE_SYMBOLICNAME, "abc;singleton:=true");
+        headers.put(Constants.BUNDLE_SYMBOLICNAME, "abc;singleton:=true;foo=bar;" + IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE + "=test");
         headers.put(Constants.BUNDLE_VERSION, "1.2.3.something");
         String copyright = "(c) 2013 Apache Software Foundation";
         headers.put(Constants.BUNDLE_COPYRIGHT, copyright);
@@ -72,7 +76,13 @@ public class ManifestParserTest extends TestCase
         headers.put(Constants.BUNDLE_DOCURL, docurl);
         String license = "http://www.apache.org/licenses/LICENSE-2.0";
         headers.put("Bundle-License", license);
-        ManifestParser mp = new ManifestParser(null, null, null, headers);
+
+        BundleRevisionImpl mockBundleRevision = mock(BundleRevisionImpl.class);
+
+        Content connectContent = mock(ConnectContentContent.class);
+        when(mockBundleRevision.getContent()).thenReturn(connectContent);
+
+        ManifestParser mp = new ManifestParser(null, null, mockBundleRevision, headers);
 
         BundleCapability ic = findCapability(mp.getCapabilities(), IdentityNamespace.IDENTITY_NAMESPACE);
         assertEquals("abc", ic.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE));
@@ -82,6 +92,8 @@ public class ManifestParserTest extends TestCase
         assertEquals(description, ic.getAttributes().get(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE));
         assertEquals(docurl, ic.getAttributes().get(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE));
         assertEquals(license, ic.getAttributes().get(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE));
+        assertEquals(Arrays.asList("test", ConnectContent.TAG_OSGI_CONNECT), ic.getAttributes().get(IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE));
+        assertEquals("bar", ic.getAttributes().get("foo"));
 
         assertEquals(1, ic.getDirectives().size());
         assertEquals("true", ic.getDirectives().get(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE));
@@ -97,7 +109,10 @@ public class ManifestParserTest extends TestCase
         		"osgi.native.osversion:Version=\"7.0\";"+
         		"osgi.native.processor:List<String>=\"x86-64,amd64,em64t,x86_64\";"+
         		"osgi.native.language=\"en\"");
-        BundleRevision mockBundleRevision = mock(BundleRevision.class);
+
+        BundleRevisionImpl mockBundleRevision = mock(BundleRevisionImpl.class);
+
+        when(mockBundleRevision.getContent()).thenReturn(null);
         when(mockBundleRevision.getSymbolicName()).thenReturn(FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME);
     	
         ManifestParser mp = new ManifestParser(null, null, mockBundleRevision, headers);
@@ -123,7 +138,9 @@ public class ManifestParserTest extends TestCase
         headers.put(Constants.REQUIRE_CAPABILITY,
                 "com.example.other;theList:List<String>=\"one,two,three\";theLong:Long=999");
 
-        BundleRevision mockBundleRevision = mock(BundleRevision.class);
+        BundleRevisionImpl mockBundleRevision = mock(BundleRevisionImpl.class);
+
+        when(mockBundleRevision.getContent()).thenReturn(null);
 
         when(mockBundleRevision.getSymbolicName()).thenReturn("com.example.test.sample");
 
@@ -154,7 +171,10 @@ public class ManifestParserTest extends TestCase
         String[] languages = {"en", "se"};
         String selectionFilter = "(com.acme.windowing=win32)";
         NativeLibraryClause clause = new NativeLibraryClause(libraryFiles, osNames, processors, osVersions, languages, selectionFilter);
-        BundleRevision owner = mock(BundleRevision.class);
+
+        BundleRevisionImpl owner = mock(BundleRevisionImpl.class);
+
+        when(owner.getContent()).thenReturn(null);
         nativeLibraryClauses.add(clause);
         
         List<BundleRequirement> nativeBundleReq = ManifestParser.convertNativeCode(owner, nativeLibraryClauses, false);