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:15 UTC

[felix-dev] branch connect created (now d26f9ad)

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

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


      at d26f9ad  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 branch includes the following new commits:

     new d26f9ad  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.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[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.

Posted by pa...@apache.org.
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);