You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2020/03/02 11:51:16 UTC
[felix-dev] 01/01: Add the inital connect implementation - this
changes the framework mvn/bsn to be
org.apache.felix.framework.connect-0.1.0 for the time being.
This is an automated email from the ASF dual-hosted git repository.
pauls pushed a commit to branch connect
in repository https://gitbox.apache.org/repos/asf/felix-dev.git
commit d26f9ad4c0159a7ff06c5c4765d694b9cda1e7fe
Author: Karl Pauls <kp...@adobe.com>
AuthorDate: Mon Mar 2 12:50:58 2020 +0100
Add the inital connect implementation - this changes the framework mvn/bsn to be org.apache.felix.framework.connect-0.1.0 for the time being.
---
framework/pom.xml | 43 +--
.../org/apache/felix/framework/BundleImpl.java | 26 +-
.../felix/framework/BundleProtectionDomain.java | 2 +-
.../apache/felix/framework/BundleRevisionImpl.java | 34 ++
.../apache/felix/framework/BundleWiringImpl.java | 89 +++--
.../apache/felix/framework/ExtensionManager.java | 91 +++--
.../java/org/apache/felix/framework/Felix.java | 36 +-
.../apache/felix/framework/FrameworkFactory.java | 10 +-
.../apache/felix/framework/ServiceRegistry.java | 18 +-
.../org/apache/felix/framework/URLHandlers.java | 60 +++-
.../framework/URLHandlersBundleURLConnection.java | 14 +-
.../framework/URLHandlersStreamHandlerProxy.java | 2 +-
.../felix/framework/cache/BundleArchive.java | 35 +-
.../apache/felix/framework/cache/BundleCache.java | 13 +-
.../framework/cache/ConnectContentContent.java | 215 ++++++++++++
.../felix/framework/cache/ConnectRevision.java | 88 +++++
.../framework/cache/ContentDirectoryContent.java | 15 +-
.../apache/felix/framework/cache/JarContent.java | 2 +-
.../framework/capabilityset/CapabilitySet.java | 2 +-
.../framework/ext/ClassPathExtenderFactory.java | 6 +-
.../felix/framework/util/FelixConstants.java | 2 +-
.../apache/felix/framework/util/SecureAction.java | 98 +++++-
.../java/org/apache/felix/framework/util/Util.java | 116 ++++---
.../util/manifestparser/ManifestParser.java | 72 +++-
framework/src/main/java/org/osgi/dto/DTO.java | 2 +-
.../src/main/java/org/osgi/dto/package-info.java | 2 +-
.../java/org/osgi/framework/FrameworkUtil.java | 75 ++++-
.../osgi/framework/PrototypeServiceFactory.java | 2 +-
.../java/org/osgi/framework/ServiceObjects.java | 2 +-
.../org/osgi/framework/connect/ConnectContent.java | 200 +++++++++++
.../framework/connect/ConnectFrameworkFactory.java | 80 +++++
.../org/osgi/framework/connect/ConnectModule.java | 45 +++
.../framework/connect/FrameworkUtilHelper.java | 43 +++
.../osgi/framework/connect/ModuleConnector.java | 95 ++++++
.../url => framework/connect}/package-info.java | 12 +-
.../java/org/osgi/framework/dto/BundleDTO.java | 2 +-
.../java/org/osgi/framework/dto/FrameworkDTO.java | 2 +-
.../osgi/framework/dto/ServiceReferenceDTO.java | 2 +-
.../java/org/osgi/framework/dto/package-info.java | 2 +-
.../osgi/framework/hooks/bundle/package-info.java | 2 +-
.../framework/hooks/resolver/package-info.java | 2 +-
.../osgi/framework/hooks/service/package-info.java | 2 +-
.../hooks/weaving/WovenClassListener.java | 2 +-
.../osgi/framework/hooks/weaving/package-info.java | 2 +-
.../org/osgi/framework/launch/package-info.java | 2 +-
.../framework/namespace/IdentityNamespace.java | 14 +-
.../osgi/framework/namespace/NativeNamespace.java | 2 +-
.../org/osgi/framework/namespace/package-info.java | 2 +-
.../main/java/org/osgi/framework/package-info.java | 4 +-
.../startlevel/dto/BundleStartLevelDTO.java | 2 +-
.../startlevel/dto/FrameworkStartLevelDTO.java | 2 +-
.../framework/startlevel/dto/package-info.java | 2 +-
.../osgi/framework/startlevel/package-info.java | 2 +-
.../framework/wiring/dto/BundleRevisionDTO.java | 2 +-
.../osgi/framework/wiring/dto/BundleWireDTO.java | 2 +-
.../osgi/framework/wiring/dto/BundleWiringDTO.java | 2 +-
.../osgi/framework/wiring/dto/package-info.java | 2 +-
.../org/osgi/framework/wiring/package-info.java | 2 +-
.../java/org/osgi/resource/dto/CapabilityDTO.java | 2 +-
.../org/osgi/resource/dto/CapabilityRefDTO.java | 2 +-
.../java/org/osgi/resource/dto/RequirementDTO.java | 2 +-
.../org/osgi/resource/dto/RequirementRefDTO.java | 2 +-
.../java/org/osgi/resource/dto/ResourceDTO.java | 2 +-
.../main/java/org/osgi/resource/dto/WireDTO.java | 2 +-
.../main/java/org/osgi/resource/dto/WiringDTO.java | 2 +-
.../java/org/osgi/resource/dto/package-info.java | 2 +-
.../main/java/org/osgi/resource/package-info.java | 2 +-
.../osgi/service/packageadmin/package-info.java | 2 +-
.../org/osgi/service/startlevel/package-info.java | 2 +-
.../java/org/osgi/service/url/package-info.java | 2 +-
.../java/org/osgi/util/tracker/package-info.java | 2 +-
....osgi.framework.connect.ConnectFrameworkFactory | 1 +
framework/src/main/resources/default.properties | 14 +-
.../org/apache/felix/framework/util/accessor.bytes | Bin 0 -> 816 bytes
.../org/apache/felix/framework/util/accessor.src} | 19 +-
.../apache/felix/framework/CollisionHookTest.java | 4 +-
.../org/apache/felix/framework/ConnectTest.java | 368 +++++++++++++++++++++
.../felix/framework/MultiReleaseVersionTest.java | 2 +-
.../felix/framework/ServiceRegistryTest.java | 41 +--
.../felix/framework/cache/BundleCacheTest.java | 10 +-
.../util/manifestparser/ManifestParserTest.java | 30 +-
81 files changed, 1861 insertions(+), 361 deletions(-)
diff --git a/framework/pom.xml b/framework/pom.xml
index d83ac02..e12b847 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -26,16 +26,16 @@
<modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
<name>Apache Felix Framework</name>
- <artifactId>org.apache.felix.framework</artifactId>
- <version>6.1.0-SNAPSHOT</version>
+ <artifactId>org.apache.felix.framework.connect</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
<properties>
<dollar>$</dollar>
- <felix.java.version>6</felix.java.version>
+ <felix.java.version>8</felix.java.version>
</properties>
<scm>
- <connection>scm:git:https://github.com/apache/felix-dev.git</connection>
- <developerConnection>scm:git:https://github.com/apache/felix-dev.git</developerConnection>
- <url>https://gitbox.apache.org/repos/asf?p=felix-dev.git</url>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/felix/trunk/framework</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/trunk/framework</developerConnection>
+ <url>http://svn.apache.org/repos/asf/felix/framework</url>
</scm>
<build>
@@ -47,9 +47,9 @@
<extensions>true</extensions>
<configuration>
<instructions>
- <Bundle-SymbolicName>org.apache.felix.framework</Bundle-SymbolicName>
- <Bundle-Name>Apache Felix Framework</Bundle-Name>
- <Bundle-Description>OSGi R7 framework implementation.</Bundle-Description>
+ <Bundle-SymbolicName>org.apache.felix.framework.connect</Bundle-SymbolicName>
+ <Bundle-Name>Apache Felix Framework Connect</Bundle-Name>
+ <Bundle-Description>OSGi R7 framework connect implementation.</Bundle-Description>
<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
<Export-Package>
org.osgi.framework.*;-split-package:=first,
@@ -89,7 +89,7 @@
<excludes>
<exclude>src/main/appended-resources/**</exclude>
<exclude>src/**/packageinfo</exclude>
- <exclude>src/main/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory</exclude>
+ <exclude>src/main/resources/META-INF/services/*</exclude>
<exclude>src/main/resources/org/apache/felix/framework/Felix.properties</exclude>
</excludes>
</configuration>
@@ -118,18 +118,19 @@
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
+ <excludes>
+ <exclude>**/*.bytes</exclude>
+ </excludes>
+ </resource>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>false</filtering>
+ <includes>
+ <include>**/*.bytes</include>
+ </includes>
</resource>
</resources>
</build>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-core</artifactId>
- <version>1.3</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
@@ -152,7 +153,7 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
- <version>4.2</version>
+ <version>5.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -163,7 +164,7 @@
</dependency>
<dependency>
<groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
+ <artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index d3b6724..f8f0591 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -34,6 +34,7 @@ import org.osgi.framework.Constants;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
+import org.osgi.framework.connect.ConnectModule;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.wiring.BundleRevision;
@@ -188,16 +189,23 @@ class BundleImpl implements Bundle, BundleRevisions
{
// Get current revision, since we can reuse it.
BundleRevisionImpl current = adapt(BundleRevisionImpl.class);
- // Close all existing revisions.
- closeRevisions();
- // Clear all revisions.
- m_revisions.clear();
- // Purge all old archive revisions, only keeping the newest one.
- m_archive.purge();
+ if (isRemovalPending()) {
+ closeRevisions();
+ // Purge all old archive revisions, only keeping the newest one.
+ m_archive.purge();
+
+ current.resetContent(m_archive.getCurrentRevision().getContent());
+ }
+ else {
+ // Remove the revision from the resolver state.
+ getFramework().getResolver().removeRevision(current);
+ current.resolve(null);
+ current.disposeContentPath();
+ }
+
+ m_revisions.clear();
- // Reset the content of the current bundle revision.
- current.resetContent(m_archive.getCurrentRevision().getContent());
// Re-add the revision to the bundle.
addRevision(current);
@@ -1332,7 +1340,7 @@ class BundleImpl implements Bundle, BundleRevisions
}
}
}
- if (!collisionCanditates.isEmpty())
+ if (!collisionCanditates.isEmpty() && m_installingBundle != null)
{
throw new BundleException(
"Bundle symbolic name and version are not unique: "
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java b/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
index ccd4807..15d7145 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
@@ -76,7 +76,7 @@ public class BundleProtectionDomain extends ProtectionDomain
int count = 0;
String manifest = null;
- for (Enumeration e = m_root.getEntries(); e.hasMoreElements();)
+ for (Enumeration e = m_root.getEntries(); e != null && e.hasMoreElements();)
{
String entry = (String) e.nextElement();
if (entry.endsWith("/"))
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
index a6d47ab..c6fcaa4 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -18,6 +18,7 @@
*/
package org.apache.felix.framework;
+import org.apache.felix.framework.cache.ConnectContentContent;
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.MultiReleaseContent;
@@ -28,6 +29,7 @@ import org.apache.felix.framework.util.manifestparser.NativeLibrary;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
+import org.osgi.framework.connect.ConnectModule;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
@@ -320,6 +322,15 @@ public class BundleRevisionImpl implements BundleRevision, Resource
}
}
+ synchronized void disposeContentPath()
+ {
+ for (int i = 0; (m_contentPath != null) && (i < m_contentPath.size()); i++)
+ {
+ m_contentPath.get(i).close();
+ }
+ m_contentPath = null;
+ }
+
public void setProtectionDomain(ProtectionDomain pd)
{
m_protectionDomain = pd;
@@ -617,6 +628,29 @@ public class BundleRevisionImpl implements BundleRevision, Resource
return getContentPath().get(index - 1).getEntryAsStream(urlPath);
}
+
+ public long getContentTime(int index, String urlPath)
+ {
+ if (urlPath.startsWith("/"))
+ {
+ urlPath = urlPath.substring(1);
+ }
+ Content content;
+ if (index == 0)
+ {
+ content = getContent();
+ }
+ else {
+ content = getContentPath().get(index - 1);
+ }
+ if (content instanceof ConnectContentContent) {
+ return ((ConnectContentContent) content).getContentTime(urlPath);
+ }
+ else {
+ return m_bundle.getLastModified();
+ }
+ }
+
public URL getLocalURL(int index, String urlPath)
{
if (urlPath.startsWith("/"))
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index fa1f729..6d22ec7 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -18,6 +18,7 @@
*/
package org.apache.felix.framework;
+import org.apache.felix.framework.cache.ConnectContentContent;
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.capabilityset.SimpleFilter;
import org.apache.felix.framework.resolver.ResourceNotFoundException;
@@ -37,6 +38,8 @@ import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.PackagePermission;
import org.osgi.framework.ServiceReference;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ConnectModule;
import org.osgi.framework.hooks.weaving.WeavingException;
import org.osgi.framework.hooks.weaving.WeavingHook;
import org.osgi.framework.hooks.weaving.WovenClass;
@@ -113,7 +116,7 @@ public class BundleWiringImpl implements BundleWiring
private volatile List<BundleRequirement> m_wovenReqs = null;
- private volatile BundleClassLoader m_classLoader;
+ private volatile ClassLoader m_classLoader;
// Bundle-specific class loader for boot delegation.
private final ClassLoader m_bootClassLoader;
@@ -727,16 +730,24 @@ public class BundleWiringImpl implements BundleWiring
// is not disposed.
if (!m_isDisposed && (m_classLoader == null))
{
- m_classLoader = BundleRevisionImpl.getSecureAction().run(
- new PrivilegedAction<BundleClassLoader>()
- {
- @Override
- public BundleClassLoader run()
+ if (m_revision.getContent() instanceof ConnectContentContent)
+ {
+ m_classLoader = ((ConnectContentContent) m_revision.getContent()).getClassLoader();
+ }
+
+ if (m_classLoader == null)
+ {
+ m_classLoader = BundleRevisionImpl.getSecureAction().run(
+ new PrivilegedAction<BundleClassLoader>()
{
- return new BundleClassLoader(BundleWiringImpl.this, determineParentClassLoader(), m_logger);
+ @Override
+ public BundleClassLoader run()
+ {
+ return new BundleClassLoader(BundleWiringImpl.this, determineParentClassLoader(), m_logger);
+ }
}
- }
- );
+ );
+ }
}
return m_classLoader;
}
@@ -1522,24 +1533,21 @@ public class BundleWiringImpl implements BundleWiring
try
{
- result = tryImplicitBootDelegation(name, isClass);
- }
- catch (Exception ex)
- {
- // Ignore, will throw using CNFE_CLASS_LOADER
- }
+ // Get the appropriate class loader for delegation.
+ ClassLoader bdcl = getBootDelegationClassLoader();
+ result = (isClass) ? (Object) bdcl.loadClass(name) : (Object) bdcl.getResource(name);
- if (result != null)
- {
- m_accessorLookupCache.put(name, BundleRevisionImpl.getSecureAction()
- .getClassLoader(this.getClass()));
- return result;
+ if (result != null)
+ {
+ m_accessorLookupCache.put(name, bdcl);
+ return result;
+ }
}
- else
+ catch (ClassNotFoundException ex)
{
- m_accessorLookupCache.put(name, CNFE_CLASS_LOADER);
- CNFE_CLASS_LOADER.loadClass(name);
}
+ m_accessorLookupCache.put(name, CNFE_CLASS_LOADER);
+ CNFE_CLASS_LOADER.loadClass(name);
}
// Look in the revision's imports. Note that the search may
@@ -1550,25 +1558,38 @@ public class BundleWiringImpl implements BundleWiring
// If not found, try the revision's own class path.
if (result == null)
{
- if (isClass)
+ ClassLoader cl = getClassLoaderInternal();
+ if (cl == null)
{
- ClassLoader cl = getClassLoaderInternal();
- if (cl == null)
+ if (isClass)
{
throw new ClassNotFoundException(
- "Unable to load class '"
- + name
- + "' because the bundle wiring for "
- + m_revision.getSymbolicName()
- + " is no longer valid.");
+ "Unable to load class '"
+ + name
+ + "' because the bundle wiring for "
+ + m_revision.getSymbolicName()
+ + " is no longer valid.");
+ }
+ else
+ {
+ throw new ResourceNotFoundException("Unable to load resource '"
+ + name
+ + "' because the bundle wiring for "
+ + m_revision.getSymbolicName()
+ + " is no longer valid.");
}
- result = ((BundleClassLoader) cl).findClass(name);
}
- else
+ if (cl instanceof BundleClassLoader)
{
- result = m_revision.getResourceLocal(name);
+ result = isClass ? ((BundleClassLoader) cl).findClass(name) :
+ ((BundleClassLoader) cl).findResource(name);
+ }
+ else {
+ result = isClass ? cl.loadClass(name) : !name.startsWith("/") ? cl.getResource(name) :
+ cl.getResource(name.substring(1));
}
+
// If still not found, then try the revision's dynamic imports.
if (result == null)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index a5c8664..21613e2 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -18,6 +18,7 @@
*/
package org.apache.felix.framework;
+import org.apache.felix.framework.cache.ConnectContentContent;
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.cache.DirectoryContent;
import org.apache.felix.framework.cache.JarContent;
@@ -245,11 +246,21 @@ class ExtensionManager implements Content
String sysprops = felix._getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES);
+
+ boolean subst = "true".equalsIgnoreCase(felix._getProperty(FelixConstants.USE_PROPERTY_SUBSTITUTION_IN_SYSTEMPACKAGES));
+
+ if (sysprops != null && sysprops.isEmpty()) {
+ if (felix.hasConnectFramework()) {
+ subst = true;
+ sysprops = "${osgi-exports}";
+ config.put(Constants.FRAMEWORK_SYSTEMPACKAGES, sysprops);
+ }
+ }
+
final Map<String, Set<String>> exports = Util.initializeJPMS(defaultProperties);
if (exports != null && (sysprops == null || "true".equalsIgnoreCase(felix._getProperty(FelixConstants.USE_PROPERTY_SUBSTITUTION_IN_SYSTEMPACKAGES))))
{
- java.nio.file.FileSystem fs = java.nio.file.FileSystems.getFileSystem(URI.create("jrt:/"));
final ClassParser classParser = new ClassParser();
final Set<String> imports = new HashSet<String>();
for (Set<String> moduleImport : exports.values())
@@ -271,6 +282,7 @@ class ExtensionManager implements Content
final SortedMap<String, SortedSet<String>> referred = new TreeMap<String, SortedSet<String>>();
if ("true".equalsIgnoreCase(felix._getProperty(FelixConstants.CALCULATE_SYSTEMPACKAGES_USES)))
{
+ java.nio.file.FileSystem fs = java.nio.file.FileSystems.getFileSystem(URI.create("jrt:/"));
try
{
Properties cachedProps = new Properties();
@@ -344,7 +356,7 @@ class ExtensionManager implements Content
}
}
- if(sysprops != null && "true".equalsIgnoreCase(felix._getProperty(FelixConstants.USE_PROPERTY_SUBSTITUTION_IN_SYSTEMPACKAGES)))
+ if(sysprops != null && subst)
{
config.put(Constants.FRAMEWORK_SYSTEMPACKAGES, Util.getPropertyWithSubs(Util.toProperties(config), Constants.FRAMEWORK_SYSTEMPACKAGES));
}
@@ -414,23 +426,6 @@ class ExtensionManager implements Content
((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
.getHeaders().get(Constants.FRAGMENT_HOST));
- if (!Constants.EXTENSION_FRAMEWORK.equals(directive))
- {
- throw new BundleException("Unsupported Extension Bundle type: " +
- directive, new UnsupportedOperationException(
- "Unsupported Extension Bundle type!"));
- }
- else if (m_extenderFramework == null)
- {
- // We don't support extensions
- m_logger.log(bundle, Logger.LOG_WARNING,
- "Unable to add extension bundle - Maybe ClassLoader is not supported " +
- "(on java9, try --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED)?");
-
- throw new UnsupportedOperationException(
- "Unable to add extension bundle.");
- }
-
Content content = bundle.adapt(BundleRevisionImpl.class).getContent();
final File file;
if (content instanceof JarContent)
@@ -445,14 +440,31 @@ class ExtensionManager implements Content
{
file = null;
}
- if (file == null)
+ if (file == null && !(content instanceof ConnectContentContent))
{
// We don't support revision type for extension
m_logger.log(bundle, Logger.LOG_WARNING,
- "Unable to add extension bundle - wrong revision type?");
+ "Unable to add extension bundle - wrong revision type?");
throw new UnsupportedOperationException(
- "Unable to add extension bundle.");
+ "Unable to add extension bundle.");
+ }
+
+ if (!Constants.EXTENSION_FRAMEWORK.equals(directive))
+ {
+ throw new BundleException("Unsupported Extension Bundle type: " +
+ directive, new UnsupportedOperationException(
+ "Unsupported Extension Bundle type!"));
+ }
+ else if (m_extenderFramework == null && file != null)
+ {
+ // We don't support extensions
+ m_logger.log(bundle, Logger.LOG_WARNING,
+ "Unable to add extension bundle - Maybe ClassLoader is not supported " +
+ "(on java9, try --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED)?");
+
+ throw new UnsupportedOperationException(
+ "Unable to add extension bundle.");
}
BundleRevisionImpl bri = bundle.adapt(BundleRevisionImpl.class);
@@ -559,25 +571,32 @@ class ExtensionManager implements Content
{
f = ((JarContent) revisionContent).getFile();
}
- else
+ else if (revisionContent instanceof DirectoryContent)
{
f = ((DirectoryContent) revisionContent).getFile();
}
- try
- {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>()
- {
- @Override
- public Void run() throws Exception {
- m_extenderFramework.add(f);
- return null;
- }
- });
+ else {
+ f = null;
}
- catch (Exception ex)
+ if (f != null)
{
- m_logger.log(revision.getBundle(), Logger.LOG_ERROR,
- "Error adding extension bundle to framework classloader: " + revision.getBundle(), ex);
+ try
+ {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ m_extenderFramework.add(f);
+ return null;
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(revision.getBundle(), Logger.LOG_ERROR,
+ "Error adding extension bundle to framework classloader: " + revision.getBundle(), ex);
+ }
}
felix.setBundleStateAndNotify(revision.getBundle(), Bundle.RESOLVED);
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 46792c5..0b31b20 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -58,6 +58,7 @@ import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
+import org.osgi.framework.connect.ModuleConnector;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.startlevel.FrameworkStartLevel;
@@ -213,6 +214,8 @@ public class Felix extends BundleImpl implements Framework
// Do we need to consult the default java security policy if no security provider is present?
private volatile boolean m_securityDefaultPolicy;
+ private final ModuleConnector m_connectFramework;
+
/**
* <p>
* This constructor creates a framework instance with a specified <tt>Map</tt>
@@ -351,6 +354,11 @@ public class Felix extends BundleImpl implements Framework
**/
public Felix(Map configMap)
{
+ this(configMap, null);
+ }
+
+ public Felix(Map configMap, ModuleConnector connectFramework)
+ {
super();
// Copy the configuration properties; convert keys to strings.
m_configMutableMap = new StringMap();
@@ -460,6 +468,8 @@ public class Felix extends BundleImpl implements Framework
m_fwkWiring = new FrameworkWiringImpl(this, m_registry);
// Create framework start level object.
m_fwkStartLevel = new FrameworkStartLevelImpl(this, m_registry);
+
+ m_connectFramework = connectFramework;
}
Logger getLogger()
@@ -634,12 +644,12 @@ public class Felix extends BundleImpl implements Framework
return true;
}
-
@Override
public void init() throws BundleException
{
- init((FrameworkListener[]) null);
+ init(null);
}
+
/**
* @see org.osgi.framework.launch.Framework#init(org.osgi.framework.FrameworkListener[])
*/
@@ -726,6 +736,10 @@ public class Felix extends BundleImpl implements Framework
throw new BundleException("Unable to flush bundle cache.", ex);
}
}
+ if (m_connectFramework != null)
+ {
+ m_connectFramework.initialize(m_cache.getCacheDir(), (Map) m_configMap);
+ }
}
// Initialize installed bundle data structures.
@@ -774,7 +788,6 @@ public class Felix extends BundleImpl implements Framework
"Unresolved constraint in System Bundle:"
+ ex.getUnresolvedRequirements());
}
-
// Reload the cached bundles before creating and starting the
// system bundle, since we want all cached bundles to be reloaded
// when we activate the system bundle and any subsequent system
@@ -784,7 +797,7 @@ public class Felix extends BundleImpl implements Framework
// First get cached bundle identifiers.
try
{
- archives = m_cache.getArchives();
+ archives = m_cache.getArchives(m_connectFramework);
}
catch (Exception ex)
{
@@ -850,6 +863,12 @@ public class Felix extends BundleImpl implements Framework
m_extensionManager.startExtensionBundle(this, (BundleImpl) extension);
}
+
+ if (m_connectFramework != null)
+ {
+ m_connectFramework.createBundleActivator().ifPresent(m_activatorList::add);
+ }
+
// Now that we have loaded all cached bundles and have determined the
// max bundle ID of cached bundles, we need to try to load the next
// bundle ID from persistent storage. In case of failure, we should
@@ -3224,7 +3243,7 @@ public class Felix extends BundleImpl implements Framework
try
{
// Add the bundle to the cache.
- ba = m_cache.create(id, getInitialBundleStartLevel(), location, is);
+ ba = m_cache.create(id, getInitialBundleStartLevel(), location, is, m_connectFramework);
}
catch (Exception ex)
{
@@ -5110,6 +5129,11 @@ public class Felix extends BundleImpl implements Framework
}
}
+ public boolean hasConnectFramework()
+ {
+ return m_connectFramework != null;
+ }
+
//
// Miscellaneous inner classes.
//
@@ -5132,6 +5156,7 @@ public class Felix extends BundleImpl implements Framework
}
catch (Throwable throwable)
{
+ throwable.printStackTrace();
iter.remove();
fireFrameworkEvent(FrameworkEvent.ERROR, context.getBundle(),
new BundleException("Unable to start Bundle", throwable));
@@ -5234,6 +5259,7 @@ public class Felix extends BundleImpl implements Framework
throwable);
}
}
+ m_activatorList.clear();
if (m_securityManager != null)
{
System.setSecurityManager(null);
diff --git a/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java b/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
index ce377ad..938845f 100644
--- a/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
+++ b/framework/src/main/java/org/apache/felix/framework/FrameworkFactory.java
@@ -19,12 +19,20 @@
package org.apache.felix.framework;
import java.util.Map;
+
+import org.osgi.framework.connect.ModuleConnector;
import org.osgi.framework.launch.Framework;
-public class FrameworkFactory implements org.osgi.framework.launch.FrameworkFactory
+public class FrameworkFactory implements org.osgi.framework.launch.FrameworkFactory, org.osgi.framework.connect.ConnectFrameworkFactory
{
public Framework newFramework(Map configuration)
{
return new Felix(configuration);
}
+
+ @Override
+ public Framework newFramework(Map<String, String> configuration, ModuleConnector connectFramework)
+ {
+ return new Felix(configuration, connectFramework);
+ }
}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
index 68bab2c..8c94ca8 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -280,23 +280,11 @@ public class ServiceRegistry
if (usages != null)
{
final ServiceReference<?>[] refs = new ServiceReference[usages.length];
- int count = 0;
- for (int i = 0; i < usages.length; i++)
+ for (int i = 0; i < refs.length; i++)
{
- if (usages[i].m_count.get() > 0) {
- refs[count++] = usages[i].m_ref;
- }
+ refs[i] = usages[i].m_ref;
}
-
- if (count == usages.length) {
- return refs;
- } else if (count == 0) {
- return null;
- }
-
- ServiceReference<?>[] nrefs = new ServiceReference[count];
- System.arraycopy(refs, 0, nrefs, 0, count);
- return nrefs;
+ return refs;
}
return null;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlers.java b/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
index 79b13ce..88b65e9 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
@@ -117,23 +117,34 @@ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
? DEFAULT_STREAM_HANDLER_PACKAGE
: pkgs + "|" + DEFAULT_STREAM_HANDLER_PACKAGE;
m_loaded = (null != URLHandlersStreamHandlerProxy.class) &&
- (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class);
+ (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class) && new URLHandlersStreamHandlerProxy(null, null) != null;
}
private void init(String protocol, URLStreamHandlerFactory factory)
{
try
{
- URLStreamHandler handler = getBuiltInStreamHandler(protocol, factory);
- if (handler != null)
- {
- URL url = new URL(protocol, null, -1, "", handler);
- addToCache(m_protocolToURL, protocol, url);
- }
+ // Try to get it directly from the URL class to if possible
+ Method getURLStreamHandler = m_secureAction.getDeclaredMethod(URL.class,"getURLStreamHandler", new Class[]{String.class});
+ URLStreamHandler handler = (URLStreamHandler) m_secureAction.invoke(getURLStreamHandler, null, new Object[]{protocol});
+ addToCache(m_builtIn, protocol, handler);
}
catch (Throwable ex)
{
- // Ignore, this is a best effort (maybe log it or something).
+ // Ignore, this is a best effort
+ try
+ {
+ URLStreamHandler handler = getBuiltInStreamHandler(protocol, factory);
+ if (handler != null)
+ {
+ URL url = new URL(protocol, null, -1, "", handler);
+ addToCache(m_protocolToURL, protocol, url);
+ }
+ }
+ catch (Throwable ex2)
+ {
+ // Ignore, this is a best effort (maybe log it or something).
+ }
}
}
@@ -165,14 +176,7 @@ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
init("ftp", currentFactory);
init("http", currentFactory);
init("https", currentFactory);
- try
- {
- getBuiltInStreamHandler("jar", currentFactory);
- }
- catch (Throwable ex)
- {
- // Ignore, this is a best effort (maybe log it or something)
- }
+
// Try to preload the jrt handler as we need it from the jvm on java > 8
if (getFromCache(m_builtIn, "jrt") == null)
@@ -198,6 +202,30 @@ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
}
}
+ // Try to preload the jrt handler as we need it from the jvm on java > 8
+ if (getFromCache(m_builtIn, "jar") == null)
+ {
+ try
+ {
+ // Try to get it directly from the URL class to if possible
+ Method getURLStreamHandler = m_secureAction.getDeclaredMethod(URL.class,"getURLStreamHandler", new Class[]{String.class});
+ URLStreamHandler handler = (URLStreamHandler) m_secureAction.invoke(getURLStreamHandler, null, new Object[]{"jar"});
+ addToCache(m_builtIn, "jar", handler);
+ }
+ catch (Throwable ex)
+ {
+ // Ignore, this is a best effort
+ try
+ {
+ getBuiltInStreamHandler("jar", currentFactory);
+ }
+ catch (Throwable ex2)
+ {
+ // Ignore, this is a best effort (maybe log it or something)
+ }
+ }
+ }
+
if (currentFactory != null)
{
try
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
index 2cb7ee7..5a06e1b 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
@@ -35,7 +35,7 @@ class URLHandlersBundleURLConnection extends URLConnection
private Felix m_framework;
private BundleRevision m_targetRevision;
private int m_classPathIdx = -1;
- private int m_contentLength;
+ private long m_contentLength;
private long m_contentTime;
private String m_contentType;
private InputStream m_is;
@@ -83,7 +83,6 @@ class URLHandlersBundleURLConnection extends URLConnection
{
throw new IOException("No bundle associated with resource: " + url);
}
- m_contentTime = bundle.getLastModified();
// Get the bundle's revisions to find the target revision.
BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
@@ -145,6 +144,7 @@ class URLHandlersBundleURLConnection extends URLConnection
m_is = ((BundleRevisionImpl)
m_targetRevision).getInputStream(m_classPathIdx, url.getPath());
m_contentLength = (m_is == null) ? 0 : m_is.available();
+ m_contentTime = ((BundleRevisionImpl) m_targetRevision).getContentTime(m_classPathIdx, url.getPath());
m_contentType = URLConnection.guessContentTypeFromName(url.getFile());
connected = true;
}
@@ -160,6 +160,11 @@ class URLHandlersBundleURLConnection extends URLConnection
public int getContentLength()
{
+ return (int) getContentLengthLong();
+ }
+
+ public long getContentLengthLong()
+ {
try
{
connect();
@@ -172,11 +177,6 @@ class URLHandlersBundleURLConnection extends URLConnection
return m_contentLength;
}
- public long getContentLengthLong()
- {
- return getContentLength();
- }
-
public long getLastModified()
{
try
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
index ef577c6..5d9efa3 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -138,7 +138,7 @@ public class URLHandlersStreamHandlerProxy extends URLStreamHandler
m_builtInURL = builtInURL;
}
- private URLHandlersStreamHandlerProxy(Object service, SecureAction action)
+ URLHandlersStreamHandlerProxy(Object service, SecureAction action)
{
m_protocol = null;
m_service = service;
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
index fc527bc..35026f1 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
@@ -21,12 +21,16 @@ package org.apache.felix.framework.cache;
import java.io.*;
import java.util.Map;
+import java.util.Optional;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.WeakZipFileFactory;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
+import org.osgi.framework.connect.ModuleConnector;
+import org.osgi.framework.connect.ConnectModule;
/**
* <p>
@@ -100,6 +104,8 @@ public class BundleArchive
**/
private long m_refreshCount = -1;
+ private final ModuleConnector m_connector;
+
// Maps a Long revision number to a BundleRevision.
private final SortedMap<Long, BundleArchiveRevision> m_revisions
= new TreeMap<Long, BundleArchiveRevision>();
@@ -121,7 +127,8 @@ public class BundleArchive
* @param is input stream from which to read the bundle content.
* @throws Exception if any error occurs.
**/
- public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+ public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory, ModuleConnector
+ connectFactory,
File archiveRootDir, long id, int startLevel, String location, InputStream is)
throws Exception
{
@@ -141,6 +148,8 @@ public class BundleArchive
m_lastModified = System.currentTimeMillis();
m_refreshCount = 0;
+ m_connector = connectFactory;
+
// Save state.
initialize();
@@ -160,7 +169,7 @@ public class BundleArchive
* @param configMap configMap for BundleArchive
* @throws Exception if any error occurs.
**/
- public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+ public BundleArchive(Logger logger, Map configMap, WeakZipFileFactory zipFactory, ModuleConnector connectFactory,
File archiveRootDir)
throws Exception
{
@@ -211,8 +220,12 @@ public class BundleArchive
Long currentRevNum = m_revisions.lastKey();
m_revisions.remove(currentRevNum);
+ String location = getRevisionLocation(currentRevNum);
+
+ m_connector = connectFactory;
+
// Add the revision object for the most recent revision.
- reviseInternal(true, currentRevNum, getRevisionLocation(currentRevNum), null);
+ reviseInternal(true, currentRevNum, location, null);
}
/**
@@ -795,9 +808,19 @@ public class BundleArchive
}
else
{
- // Anything else is assumed to be a URL to a JAR file.
- result = new JarRevision(m_logger, m_configMap,
- m_zipFactory, revisionRootDir, location, false, null);
+ ConnectModule module = m_connector != null ?
+ m_connector.connect(location).orElse(null) : null;
+
+ if (module != null)
+ {
+ result = new ConnectRevision(m_logger, m_configMap, m_zipFactory, revisionRootDir, location, module);
+ }
+ else
+ {
+ // Anything else is assumed to be a URL to a JAR file.
+ result = new JarRevision(m_logger, m_configMap,
+ m_zipFactory, revisionRootDir, location, false, null);
+ }
}
}
catch (Exception ex)
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index 8e63af9..022cdb4 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -22,6 +22,7 @@ import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.SecureAction;
import org.apache.felix.framework.util.WeakZipFileFactory;
import org.osgi.framework.Constants;
+import org.osgi.framework.connect.ModuleConnector;
import java.io.File;
import java.io.FileOutputStream;
@@ -386,6 +387,10 @@ public class BundleCache
}
}
+ public File getCacheDir() {
+ return determineCacheDir(m_configMap);
+ }
+
/* package */ static SecureAction getSecureAction()
{
return m_secureAction;
@@ -398,7 +403,7 @@ public class BundleCache
deleteDirectoryTree(cacheDir);
}
- public BundleArchive[] getArchives()
+ public BundleArchive[] getArchives(ModuleConnector connectFactory)
throws Exception
{
// Get buffer size value.
@@ -431,7 +436,7 @@ public class BundleCache
{
archiveList.add(
new BundleArchive(
- m_logger, m_configMap, m_zipFactory, children[i]));
+ m_logger, m_configMap, m_zipFactory, connectFactory, children[i]));
}
catch (Exception ex)
{
@@ -447,7 +452,7 @@ public class BundleCache
archiveList.toArray(new BundleArchive[archiveList.size()]);
}
- public BundleArchive create(long id, int startLevel, String location, InputStream is)
+ public BundleArchive create(long id, int startLevel, String location, InputStream is, ModuleConnector connectFactory)
throws Exception
{
File cacheDir = determineCacheDir(m_configMap);
@@ -461,7 +466,7 @@ public class BundleCache
// Create the archive and add it to the list of archives.
BundleArchive ba =
new BundleArchive(
- m_logger, m_configMap, m_zipFactory, archiveRootDir,
+ m_logger, m_configMap, m_zipFactory, connectFactory, archiveRootDir,
id, startLevel, location, is);
return ba;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java b/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java
new file mode 100644
index 0000000..7a985ac
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.connect.ConnectContent;
+
+public class ConnectContentContent implements Content
+{
+ private static final transient String EMBEDDED_DIRECTORY = "-embedded";
+ private static final transient String LIBRARY_DIRECTORY = "-lib";
+
+ private final Logger m_logger;
+ private final WeakZipFileFactory m_zipFactory;
+ private final Map m_configMap;
+ private final String m_name;
+ private final File m_rootDir;
+ private final Object m_revisionLock;
+ private final ConnectContent m_content;
+
+ public ConnectContentContent(Logger logger, WeakZipFileFactory zipFactory, Map configMap, String name, File rootDir, Object revisionLock, ConnectContent content) throws IOException
+ {
+ m_logger = logger;
+ m_zipFactory = zipFactory;
+ m_configMap = configMap;
+ m_name = name;
+ m_rootDir = rootDir;
+ m_revisionLock = revisionLock;
+ m_content = content;
+ }
+
+ @Override
+ public void close()
+ {
+ // TODO: Connect
+ }
+
+ @Override
+ public boolean hasEntry(String name)
+ {
+ return m_content.getEntry(name).isPresent();
+ }
+
+ @Override
+ public Enumeration<String> getEntries()
+ {
+ try
+ {
+ Iterator<String> entries = m_content.getEntries().iterator();
+ return new Enumeration<String>()
+ {
+ @Override
+ public boolean hasMoreElements()
+ {
+ return entries.hasNext();
+ }
+
+ @Override
+ public String nextElement()
+ {
+ return entries.next();
+ }
+ };
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] getEntryAsBytes(String name)
+ {
+ return m_content.getEntry(name).flatMap(entry ->
+ {
+ try
+ {
+ return Optional.of(entry.getBytes());
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }).orElse(null);
+ }
+
+ @Override
+ public InputStream getEntryAsStream(String name) throws IOException
+ {
+ return m_content.getEntry(name).flatMap(entry ->
+ {
+ try
+ {
+ return Optional.of(entry.getInputStream());
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }).orElse(null);
+ }
+
+ public ClassLoader getClassLoader() {
+ return m_content.getClassLoader().orElse(null);
+ }
+
+ @Override
+ public Content getEntryAsContent(String name)
+ {
+ if (".".equals(name) || "".equals(name)) {
+ return this;
+ }
+ String dir = name.endsWith("/") ? name : name + "/";
+
+ if (hasEntry(dir)) {
+ return new ContentDirectoryContent(this, name);
+ }
+
+ if (hasEntry(name) && name.endsWith(".jar"))
+ {
+ // Any embedded JAR files will be extracted to the embedded directory.
+ // Since embedded JAR file names may clash when extracting from multiple
+ // embedded JAR files, the embedded directory is per embedded JAR file.
+ File embedDir = new File(m_rootDir, m_name + EMBEDDED_DIRECTORY);
+
+ File extractJar = new File(embedDir, name);
+
+ try
+ {
+ if (!BundleCache.getSecureAction().fileExists(extractJar))
+ {
+ // Extracting the embedded JAR file impacts all other existing
+ // contents for this revision, so we have to grab the revision
+ // lock first before trying to extract the embedded JAR file
+ // to avoid a race condition.
+ synchronized (m_revisionLock)
+ {
+ if (!BundleCache.getSecureAction().fileExists(extractJar))
+ {
+ // Make sure that the embedded JAR's parent directory exists;
+ // it may be in a sub-directory.
+ File jarDir = extractJar.getParentFile();
+ if (!BundleCache.getSecureAction().fileExists(jarDir) && !BundleCache.getSecureAction().mkdirs(jarDir))
+ {
+ throw new IOException("Unable to create embedded JAR directory.");
+ }
+
+ // Extract embedded JAR into its directory.
+ BundleCache.copyStreamToFile(m_content.getEntry(name).get().getInputStream(), extractJar);
+ }
+ }
+ }
+ return new JarContent(
+ m_logger, m_configMap, m_zipFactory, m_revisionLock,
+ extractJar.getParentFile(), extractJar, null);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Unable to extract embedded JAR file.", ex);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getEntryAsNativeLibrary(String entryName)
+ {
+ // TODO: Connect
+ return null;
+ }
+
+ @Override
+ public URL getEntryAsURL(String name)
+ {
+ // TODO: Connect
+ return null;
+ }
+
+ public long getContentTime(String urlPath)
+ {
+ return m_content.getEntry(urlPath).flatMap(entry -> Optional.of(entry.getLastModified())).orElse(-1L);
+ }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java
new file mode 100644
index 0000000..9b165cf
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.osgi.framework.connect.ConnectContent;
+import org.osgi.framework.connect.ConnectModule;
+
+public class ConnectRevision extends BundleArchiveRevision
+{
+ private final WeakZipFileFactory m_zipFactory;
+ private final ConnectContent m_module;
+
+ public ConnectRevision(Logger logger, Map configMap, WeakZipFileFactory zipFactory,
+ File revisionRootDir, String location, ConnectModule module) throws Exception
+ {
+ super(logger, configMap, revisionRootDir, location);
+ m_zipFactory = zipFactory;
+ m_module = module.getContent();
+ m_module.open();
+ // If the revision directory exists, then we don't
+ // need to initialize since it has already been done.
+ if (BundleCache.getSecureAction().fileExists(getRevisionRootDir()))
+ {
+ return;
+ }
+ // Create revision directory, we only need this to store the
+ // revision location, since nothing else needs to be extracted
+ // since we are referencing a read directory already.
+ if (!BundleCache.getSecureAction().mkdir(getRevisionRootDir()))
+ {
+ getLogger().log(
+ Logger.LOG_ERROR,
+ getClass().getName() + ": Unable to create revision directory.");
+ throw new IOException("Unable to create archive directory.");
+ }
+ }
+
+ @Override
+ public Map<String, Object> getManifestHeader() throws Exception
+ {
+ return (Map) m_module.getHeaders().orElseGet(() -> m_module.getEntry("META-INF/MANIFEST.MF").flatMap(entry -> {
+ try
+ {
+ return Optional.of((Map<String, String>) (Map) BundleCache.getMainAttributes(new StringMap(), entry.getInputStream(), entry.getContentLength()));
+ }
+ catch (Exception e)
+ {
+ throw new IllegalStateException(e);
+ }
+ }).orElse(null));
+ }
+
+ @Override
+ public Content getContent() throws Exception
+ {
+ return new ConnectContentContent(getLogger(), m_zipFactory, getConfig(), "connect", getRevisionRootDir(), this, m_module);
+ }
+
+ @Override
+ protected void close() throws Exception
+ {
+ m_module.close();
+ }
+}
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
index d268f32..a1b3367 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
@@ -143,14 +143,17 @@ public class ContentDirectoryContent implements Content
private String findNextEntry()
{
- // Find next entry that is inside the root directory.
- while (m_enumeration.hasMoreElements())
+ if (m_enumeration != null)
{
- String next = (String) m_enumeration.nextElement();
- if (next.startsWith(m_rootPath) && !next.equals(m_rootPath))
+ // Find next entry that is inside the root directory.
+ while (m_enumeration.hasMoreElements())
{
- // Strip off the root directory.
- return next.substring(m_rootPath.length());
+ String next = (String) m_enumeration.nextElement();
+ if (next.startsWith(m_rootPath) && !next.equals(m_rootPath))
+ {
+ // Strip off the root directory.
+ return next.substring(m_rootPath.length());
+ }
}
}
return null;
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
index 35afc9c..885137a 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
@@ -398,7 +398,7 @@ public class JarContent implements Content
return m_file;
}
- private static class DevNullRunnable implements Runnable
+ static class DevNullRunnable implements Runnable
{
private final InputStream m_in;
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
index 7116751..e887c3a 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -577,7 +577,7 @@ public class CapabilitySet
{
// If the LHS expects a string, then we can just return
// the RHS since it is a string.
- if (lhs.getClass() == rhsString.getClass())
+ if (lhs instanceof String)
{
return rhsString;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java b/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
index 26103b4..ae43b73 100644
--- a/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
+++ b/framework/src/main/java/org/apache/felix/framework/ext/ClassPathExtenderFactory.java
@@ -23,6 +23,8 @@ import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
+import org.apache.felix.framework.util.SecureAction;
+
public interface ClassPathExtenderFactory
{
interface ClassPathExtender
@@ -50,7 +52,7 @@ public interface ClassPathExtenderFactory
try
{
append = app.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
- append.setAccessible(true);
+ new SecureAction().setAccesssible(append);
break;
}
catch (Exception e)
@@ -74,7 +76,7 @@ public interface ClassPathExtenderFactory
try
{
addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
- addURL.setAccessible(true);
+ new SecureAction().setAccesssible(addURL);
}
catch (Exception e)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
index 71a1f75..0c5e1ca 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -20,7 +20,7 @@ package org.apache.felix.framework.util;
public interface FelixConstants extends org.osgi.framework.Constants
{
- String SYSTEM_BUNDLE_SYMBOLICNAME = "org.apache.felix.framework";
+ String SYSTEM_BUNDLE_SYMBOLICNAME = "org.apache.felix.framework.connect";
// Framework constants and values.
String FRAMEWORK_VERSION_VALUE = "1.9";
String FRAMEWORK_VENDOR_VALUE = "Apache Software Foundation";
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index 4a202a7..559075e 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -23,11 +23,15 @@ import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
import java.security.*;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
import java.util.jar.JarFile;
+import java.util.stream.Stream;
import java.util.zip.ZipFile;
import org.osgi.framework.Bundle;
@@ -56,6 +60,30 @@ import org.osgi.framework.wiring.BundleRevision;
**/
public class SecureAction
{
+ private static final byte[] accessor;
+
+ static {
+ byte[] result;
+
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream();
+ InputStream input = SecureAction.class.getResourceAsStream("accessor.bytes"))
+ {
+
+ byte[] buffer = new byte[input.available() > 0 ? input.available() : 1024];
+ for (int i = input.read(buffer); i != -1; i = input.read(buffer))
+ {
+ output.write(buffer, 0, i);
+ }
+ result = output.toByteArray();
+ }
+ catch (Throwable t) {
+ t.printStackTrace();
+ result = new byte[0];
+ }
+ accessor = result;
+ getAccessor(URL.class);
+ }
+
private static final ThreadLocal m_actions = new ThreadLocal()
{
public Object initialValue()
@@ -762,7 +790,7 @@ public class SecureAction
Method addURL =
URLClassLoader.class.getDeclaredMethod("addURL",
new Class[] {URL.class});
- addURL.setAccessible(true);
+ getAccessor(URLClassLoader.class).accept(new AccessibleObject[]{addURL});
addURL.invoke(loader, new Object[]{extension});
}
}
@@ -851,7 +879,7 @@ public class SecureAction
}
}
- public void setAccesssible(AccessibleObject ao)
+ public void setAccesssible(Executable ao)
{
if (System.getSecurityManager() != null)
{
@@ -868,7 +896,7 @@ public class SecureAction
}
else
{
- ao.setAccessible(true);
+ getAccessor(ao.getDeclaringClass()).accept(new AccessibleObject[]{ao});
}
}
@@ -889,7 +917,8 @@ public class SecureAction
}
else
{
- method.setAccessible(true);
+ getAccessor(method.getDeclaringClass()).accept(new AccessibleObject[]{method});
+
return method.invoke(target, params);
}
}
@@ -955,8 +984,7 @@ public class SecureAction
else
{
Field field = targetClass.getDeclaredField(name);
- field.setAccessible(true);
-
+ getAccessor(targetClass).accept(new AccessibleObject[]{field});
return field.get(target);
}
}
@@ -985,9 +1013,50 @@ public class SecureAction
}
}
+ private static volatile Consumer<AccessibleObject[]> m_accessorCache = null;
+
+ @SuppressWarnings("unchecked")
+ private static Consumer<AccessibleObject[]> getAccessor(Class clazz) {
+ String packageName = clazz.getPackage().getName();
+ if ("java.net".equals(packageName) || "jdk.internal.loader".equals(packageName))
+ {
+ if (m_accessorCache == null)
+ {
+ try
+ {
+ // Use reflection on Unsafe to avoid having to compile against it
+ Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); //$NON-NLS-1$
+ Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe"); //$NON-NLS-1$
+ // NOTE: deep reflection is allowed on sun.misc package for java 9.
+ theUnsafe.setAccessible(true);
+ Object unsafe = theUnsafe.get(null);
+ // using defineAnonymousClass here because it seems more simple to get what we need
+ Method defineAnonymousClass = unsafeClass.getMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class); //$NON-NLS-1$
+ // The bytes stored in a resource to avoid real loading of it (see accessible.src for source).
+
+ Class<Consumer<AccessibleObject[]>> result =
+ (Class<Consumer<AccessibleObject[]>>)
+ defineAnonymousClass.invoke(unsafe, URL.class, accessor , null);
+ m_accessorCache = result.getConstructor().newInstance();
+
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ m_accessorCache = objects -> AccessibleObject.setAccessible(objects, true);
+ }
+ }
+ return m_accessorCache;
+ }
+ else {
+ return objects -> AccessibleObject.setAccessible(objects, true);
+ }
+ }
+
private static Object _swapStaticFieldIfNotClass(Class targetClazz,
Class targetType, Class condition, String lockName) throws Exception
{
+
Object lock = null;
if (lockName != null)
{
@@ -995,7 +1064,7 @@ public class SecureAction
{
Field lockField =
targetClazz.getDeclaredField(lockName);
- lockField.setAccessible(true);
+ getAccessor(targetClazz).accept(new AccessibleObject[]{lockField});
lock = lockField.get(null);
}
catch (NoSuchFieldException ex)
@@ -1010,14 +1079,14 @@ public class SecureAction
{
Field[] fields = targetClazz.getDeclaredFields();
+ getAccessor(targetClazz).accept(fields);
+
Object result = null;
for (int i = 0; (i < fields.length) && (result == null); i++)
{
if (Modifier.isStatic(fields[i].getModifiers()) &&
(fields[i].getType() == targetType))
{
- fields[i].setAccessible(true);
-
result = fields[i].get(null);
if (result != null)
@@ -1040,7 +1109,6 @@ public class SecureAction
if (Modifier.isStatic(fields[i].getModifiers()) &&
(fields[i].getType() == Hashtable.class))
{
- fields[i].setAccessible(true);
Hashtable cache = (Hashtable) fields[i].get(null);
if (cache != null)
{
@@ -1081,13 +1149,13 @@ public class SecureAction
synchronized (lock)
{
Field[] fields = targetClazz.getDeclaredFields();
+ getAccessor(targetClazz).accept(fields);
// reset cache
for (int i = 0; i < fields.length; i++)
{
if (Modifier.isStatic(fields[i].getModifiers()) &&
((fields[i].getType() == Hashtable.class) || (fields[i].getType() == HashMap.class)))
{
- fields[i].setAccessible(true);
if (fields[i].getType() == Hashtable.class)
{
Hashtable cache = (Hashtable) fields[i].get(null);
@@ -1708,7 +1776,7 @@ public class SecureAction
Method addURL =
URLClassLoader.class.getDeclaredMethod("addURL",
new Class[] {URL.class});
- addURL.setAccessible(true);
+ getAccessor(URLClassLoader.class).accept(new AccessibleObject[]{addURL});
addURL.invoke(arg2, new Object[]{arg1});
return null;
case CREATE_TMPFILE_ACTION:
@@ -1740,7 +1808,7 @@ public class SecureAction
return ((Class) arg1).getDeclaredMethod((String) arg2, (Class[]) arg3);
case GET_FIELD_ACTION:
Field field = ((Class) arg1).getDeclaredField((String) arg2);
- field.setAccessible(true);
+ getAccessor((Class) arg1).accept(new AccessibleObject[]{field});
return field.get(arg3);
case GET_FILE_INPUT_ACTION:
return new FileInputStream((File) arg1);
@@ -1765,7 +1833,7 @@ public class SecureAction
case INVOKE_DIRECTMETHOD_ACTION:
return ((Method) arg1).invoke(arg2, (Object[]) arg3);
case INVOKE_METHOD_ACTION:
- ((Method) arg1).setAccessible(true);
+ getAccessor(((Method) arg1).getDeclaringClass()).accept(new AccessibleObject[]{(Method) arg1});
return ((Method) arg1).invoke(arg2, (Object[]) arg3);
case LIST_DIRECTORY_ACTION:
return ((File) arg1).listFiles();
@@ -1780,7 +1848,7 @@ public class SecureAction
case RENAME_FILE_ACTION:
return ((File) arg1).renameTo((File) arg2) ? Boolean.TRUE : Boolean.FALSE;
case SET_ACCESSIBLE_ACTION:
- ((AccessibleObject) arg1).setAccessible(true);
+ getAccessor(((Executable) arg1).getDeclaringClass()).accept(new AccessibleObject[]{(Executable) arg1});
return null;
case START_ACTIVATOR_ACTION:
((BundleActivator) arg1).start((BundleContext) arg2);
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index f6acd36..d92bb1b 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -47,12 +47,15 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
+import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
@@ -60,37 +63,46 @@ public class Util
{
/**
* The default name used for the default configuration properties file.
- **/
- private static final String DEFAULT_PROPERTIES_FILE = "default.properties";
+ **/
+ private static final String DEFAULT_PROPERTIES_FILE = "/default.properties";
+
+ private static final Properties DEFAULTS;
+
+ private static final IOException DEFAULT_EX;
+
+ private static final Map<String, Set<String>> MODULES_MAP;
+
+ static
+ {
+ Properties defaults = null;
+ IOException defaultEX = null;
+ try
+ {
+ defaults = loadDefaultProperties();
+ } catch (IOException ex) {
+ defaultEX = ex;
+ }
+ DEFAULTS = defaults;
+ DEFAULT_EX = defaultEX;
+
+ MODULES_MAP = calculateModulesMap();
+ }
+
+ public static Properties loadDefaultProperties(Logger logger) {
+ if (DEFAULTS.isEmpty()) {
+ logger.log(Logger.LOG_ERROR, "Unable to load any configuration properties.", DEFAULT_EX);
+ }
+ return DEFAULTS;
+ }
- public static Properties loadDefaultProperties(Logger logger)
+ private static Properties loadDefaultProperties() throws IOException
{
Properties defaultProperties = new Properties();
- URL propURL = Util.class.getClassLoader().getResource(DEFAULT_PROPERTIES_FILE);
+ URL propURL = Util.class.getResource(DEFAULT_PROPERTIES_FILE);
if (propURL != null)
{
- InputStream is = null;
- try
- {
- // Load properties from URL.
- is = propURL.openConnection().getInputStream();
+ try (InputStream is = propURL.openConnection().getInputStream()) {
defaultProperties.load(is);
- is.close();
- }
- catch (Exception ex)
- {
- // Try to close input stream if we have one.
- try
- {
- if (is != null) is.close();
- }
- catch (IOException ex2)
- {
- // Nothing we can do.
- }
-
- logger.log(
- Logger.LOG_ERROR, "Unable to load any configuration properties.", ex);
}
}
return defaultProperties;
@@ -138,6 +150,8 @@ public class Util
properties.put("ee-jpms", ee.toString());
properties.put("eecap-jpms", eecap.toString());
+
+ properties.put("felix.detect.jpms", "jpms");
}
properties.put("felix.detect.java.specification.version", version.getMajor() < 9 ? ("1." + (version.getMinor() > 6 ? version.getMinor() : 6)) : Integer.toString(version.getMajor()));
@@ -157,9 +171,9 @@ public class Util
}
}
- public static Map<String, Set<String>> initializeJPMS(Properties properties)
+ private static Map<String, Set<String>> calculateModulesMap()
{
- Map<String,Set<String>> exports = null;
+ Map<String, Set<String>> result = new LinkedHashMap<>();
try
{
Class<?> c_ModuleLayer = Felix.class.getClassLoader().loadClass("java.lang.ModuleLayer");
@@ -179,16 +193,13 @@ public class Util
moduleLayer = c_ModuleLayer.getMethod("boot").invoke(null);
}
- Set<String> modules = new TreeSet<String>();
- exports = new HashMap<String, Set<String>>();
for (Object module : ((Iterable) c_ModuleLayer.getMethod("modules").invoke(moduleLayer)))
{
if ((Boolean) m_canRead.invoke(self, module))
{
- Object name = m_getName.invoke(module);
- properties.put("felix.detect.jpms." + name, name);
- modules.add("felix.jpms." + name);
- Set<String> pkgs = new HashSet<String>();
+ String name = (String) m_getName.invoke(module);
+
+ Set<String> pkgs = new LinkedHashSet<>();
Object descriptor = c_Module.getMethod("getDescriptor").invoke(module);
@@ -199,24 +210,45 @@ public class Util
pkgs.add((String) c_Exports.getMethod("source").invoke(export));
}
}
- if (!pkgs.isEmpty())
- {
- exports.put("felix.jpms." + c_Descriptor.getMethod("toNameAndVersion").invoke(descriptor), pkgs);
- }
+ result.put(name, pkgs);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ //ex.printStackTrace();
+ // Not much we can do - probably not on java9
+ }
+ return result;
+ }
+
+ public static Map<String, Set<String>> initializeJPMS(Properties properties)
+ {
+ Map<String,Set<String>> exports = null;
+ if (!MODULES_MAP.isEmpty())
+ {
+ Set<String> modules = new TreeSet<String>();
+ exports = new HashMap<String, Set<String>>();
+ for (Map.Entry<String, Set<String>> module : MODULES_MAP.entrySet())
+ {
+ Object name = module.getKey();
+ properties.put("felix.detect.jpms." + name, name);
+ modules.add("felix.jpms." + name);
+ Set<String> pkgs = module.getValue();
+
+ if (!pkgs.isEmpty())
+ {
+ exports.put("felix.jpms." + name, pkgs);
}
}
- properties.put("felix.detect.jpms", "jpms");
String modulesString = "";
for (String module : modules) {
modulesString += "${" + module + "}";
}
properties.put("jre-jpms", modulesString);
}
- catch (Exception ex)
- {
- // Not much we can do - probably not on java9
- }
+
return exports;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index 9158c6a..60a0fe6 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -20,6 +20,7 @@ package org.apache.felix.framework.util.manifestparser;
import org.apache.felix.framework.BundleRevisionImpl;
import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.cache.ConnectContentContent;
import org.apache.felix.framework.capabilityset.SimpleFilter;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.wiring.BundleCapabilityImpl;
@@ -28,6 +29,7 @@ import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.framework.VersionRange;
+import org.osgi.framework.connect.ConnectContent;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
@@ -37,10 +39,13 @@ import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -109,7 +114,7 @@ public class ManifestParser
// Parse bundle symbolic name.
//
- BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap);
+ BundleCapabilityImpl bundleCap = parseBundleSymbolicName(logger, owner, m_headerMap);
if (bundleCap != null)
{
m_bundleSymbolicName = (String)
@@ -215,7 +220,7 @@ public class ManifestParser
List<ParsedHeaderClause> exportClauses =
parseStandardHeader((String) headerMap.get(Constants.EXPORT_PACKAGE));
exportClauses = normalizeExportClauses(logger, exportClauses,
- getManifestVersion(), m_bundleSymbolicName, m_bundleVersion);
+ getManifestVersion(), m_bundleSymbolicName, m_bundleVersion, owner instanceof BundleRevisionImpl && ((BundleRevisionImpl) owner).getContent() instanceof ConnectContentContent);
List<BundleCapability> exportCaps = convertExports(exportClauses, owner);
//
@@ -696,7 +701,7 @@ public class ManifestParser
throws BundleException
{
- if (!mv.equals("2") && !clauses.isEmpty())
+ if (mv != null && !mv.equals("2") && !clauses.isEmpty())
{
// Should we error here if we are not an R4 bundle?
}
@@ -836,7 +841,7 @@ public class ManifestParser
private static List<ParsedHeaderClause> normalizeExportClauses(
Logger logger, List<ParsedHeaderClause> clauses,
- String mv, String bsn, Version bv)
+ String mv, String bsn, Version bv, boolean connectModule)
throws BundleException
{
for (ParsedHeaderClause clause : clauses)
@@ -845,7 +850,7 @@ public class ManifestParser
for (String pkgName : clause.m_paths)
{
// Verify that java.* packages are not exported (except from the system bundle).
- if (!FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(bsn) && pkgName.startsWith("java."))
+ if ((!FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(bsn) && !connectModule) && pkgName.startsWith("java."))
{
throw new BundleException(
"Exporting java.* packages not allowed: "
@@ -1358,12 +1363,12 @@ public class ManifestParser
return false;
}
- private static BundleCapabilityImpl parseBundleSymbolicName(
+ private static BundleCapabilityImpl parseBundleSymbolicName(Logger logger,
BundleRevision owner, Map<String, Object> headerMap)
throws BundleException
{
- List<ParsedHeaderClause> clauses = parseStandardHeader(
- (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ List<ParsedHeaderClause> clauses = normalizeCapabilityClauses(logger, parseStandardHeader(
+ (String) headerMap.get(Constants.BUNDLE_SYMBOLICNAME)), getManifestVersion(headerMap));
if (clauses.size() > 0)
{
if (clauses.size() > 1)
@@ -1378,6 +1383,11 @@ public class ManifestParser
"Cannot have multiple symbolic names: "
+ headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
}
+ else if (clauses.get(0).m_attrs.containsKey(Constants.BUNDLE_VERSION))
+ {
+ throw new BundleException(
+ "Cannot have a bundle version: " + headerMap.get(Constants.BUNDLE_VERSION));
+ }
// Get bundle version.
Version bundleVersion = Version.emptyVersion;
@@ -1400,6 +1410,48 @@ public class ManifestParser
}
}
+ Object tagList = clauses.get(0).m_attrs.get(IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE);
+ LinkedHashSet<String> tags = new LinkedHashSet<>();
+ if (tagList != null)
+ {
+ if (tagList instanceof List)
+ {
+ for (Object member : ((List) tagList))
+ {
+ if (member instanceof String)
+ {
+ tags.add((String) member);
+ }
+ else
+ {
+ throw new BundleException("Invalid tags list: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ }
+ }
+ }
+ else if (tagList instanceof String)
+ {
+ tags.add((String) tagList);
+ }
+ else
+ {
+ throw new BundleException("Invalid tags list: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ }
+ }
+
+ if (tags.contains(ConnectContent.TAG_OSGI_CONNECT))
+ {
+ throw new BundleException("Invalid tags list: " + headerMap.get(Constants.BUNDLE_SYMBOLICNAME));
+ }
+ if (owner != null && ((BundleRevisionImpl) owner).getContent() instanceof ConnectContentContent)
+ {
+ tags.add(ConnectContent.TAG_OSGI_CONNECT);
+ }
+
+ if (!tags.isEmpty())
+ {
+ clauses.get(0).m_attrs.put(IdentityNamespace.CAPABILITY_TAGS_ATTRIBUTE, new ArrayList<>(tags));
+ }
+
// Create a require capability and return it.
String symName = (String) clauses.get(0).m_paths.get(0);
clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
@@ -1415,9 +1467,9 @@ public class ManifestParser
}
private static BundleCapabilityImpl addIdentityCapability(BundleRevision owner,
- Map<String, Object> headerMap, BundleCapabilityImpl bundleCap)
+ Map<String, Object> headerMap, BundleCapabilityImpl bundleCap) throws BundleException
{
- Map<String, Object> attrs = new HashMap<String, Object>();
+ Map<String, Object> attrs = new HashMap<String, Object>(bundleCap.getAttributes());
attrs.put(IdentityNamespace.IDENTITY_NAMESPACE,
bundleCap.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
diff --git a/framework/src/main/java/org/osgi/dto/DTO.java b/framework/src/main/java/org/osgi/dto/DTO.java
index 53074a6..f109341 100644
--- a/framework/src/main/java/org/osgi/dto/DTO.java
+++ b/framework/src/main/java/org/osgi/dto/DTO.java
@@ -35,7 +35,7 @@ import java.util.Set;
* The object graph from a Data Transfer Object must be a tree to simplify
* serialization and deserialization.
*
- * @author $Id$
+ * @author $Id: DTO.java 1825132 2018-02-23 15:11:00Z pauls $
* @NotThreadSafe
*/
public abstract class DTO {
diff --git a/framework/src/main/java/org/osgi/dto/package-info.java b/framework/src/main/java/org/osgi/dto/package-info.java
index d0d0ad1..0a1637e 100644
--- a/framework/src/main/java/org/osgi/dto/package-info.java
+++ b/framework/src/main/java/org/osgi/dto/package-info.java
@@ -30,7 +30,7 @@
* <p>
* {@code Import-Package: org.osgi.dto; version="[1.1,1.2)"}
*
- * @author $Id$
+ * @author $Id: package-info.java 1825132 2018-02-23 15:11:00Z pauls $
*/
@Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/FrameworkUtil.java b/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
index 9f72092..d50bea5 100644
--- a/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
+++ b/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
@@ -26,15 +26,20 @@ import java.security.PrivilegedAction;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.ServiceLoader;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
+import org.osgi.framework.connect.FrameworkUtilHelper;
+
/**
* Framework Utility class.
*
@@ -194,29 +199,67 @@ public class FrameworkUtil {
}
/**
- * Return a {@code Bundle} for the specified bundle class. The returned
- * {@code Bundle} is the bundle associated with the bundle class loader
- * which defined the specified class.
- *
- * @param classFromBundle A class defined by a bundle class loader.
+ * Return a {@code Bundle} for the specified bundle class loader.
+ *
+ * @param bundleClassLoader A bundle class loader.
+ * @return An Optional containing {@code Bundle} for the specified bundle
+ * class loader or an empty Optional if the specified class loader
+ * is not associated with a specific bundle.
+ * @since 1.10
+ */
+ public static Optional<Bundle> getBundle(ClassLoader bundleClassLoader) {
+ return Optional
+ .ofNullable((bundleClassLoader instanceof BundleReference)
+ ? ((BundleReference) bundleClassLoader).getBundle()
+ : null);
+ }
+
+ /**
+ * Return a {@code Bundle} for the specified bundle class.
+ *
+ * @param classFromBundle A class defined by a bundle.
* @return A {@code Bundle} for the specified bundle class or {@code null}
- * if the specified class was not defined by a bundle class loader.
+ * if the specified class was not defined by a bundle.
* @since 1.5
*/
- public static Bundle getBundle(final Class<?> classFromBundle) {
+ public static Bundle getBundle(Class< ? > classFromBundle) {
// We use doPriv since the caller may not have permission
// to call getClassLoader.
- Object cl = AccessController.doPrivileged(new PrivilegedAction<Object>() {
- @Override
- public Object run() {
- return classFromBundle.getClassLoader();
- }
- });
+ Optional<ClassLoader> cl = Optional
+ .ofNullable(AccessController.doPrivileged(
+ (PrivilegedAction<ClassLoader>) () -> classFromBundle
+ .getClassLoader()));
+
+ return cl.flatMap(FrameworkUtil::getBundle)
+ .orElseGet(() -> helpers.stream()
+ .map(helper -> helper.getBundle(classFromBundle))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .orElse(null));
+ }
- if (cl instanceof BundleReference) {
- return ((BundleReference) cl).getBundle();
+ private final static List<FrameworkUtilHelper> helpers;
+ static {
+ List<FrameworkUtilHelper> l = new ArrayList<>();
+ try {
+ ServiceLoader<FrameworkUtilHelper> helperLoader = AccessController
+ .doPrivileged(
+ (PrivilegedAction<ServiceLoader<FrameworkUtilHelper>>) () -> ServiceLoader
+ .load(FrameworkUtilHelper.class,
+ FrameworkUtilHelper.class
+ .getClassLoader()));
+ helperLoader.forEach(l::add);
+ } catch (Throwable error) {
+ // try hard not to fail static <clinit>
+ try {
+ Thread t = Thread.currentThread();
+ t.getUncaughtExceptionHandler().uncaughtException(t, error);
+ } catch (Throwable ignored) {
+ // we ignore this
+ }
}
- return null;
+ helpers = Collections.unmodifiableList(l);
}
/**
diff --git a/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java b/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
index 864506f..dbb9243 100644
--- a/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
+++ b/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
@@ -65,7 +65,7 @@ import org.osgi.annotation.versioning.ConsumerType;
* @see ServiceObjects
* @ThreadSafe
* @since 1.8
- * @author $Id$
+ * @author $Id: PrototypeServiceFactory.java 1825132 2018-02-23 15:11:00Z pauls $
*/
@ConsumerType
public interface PrototypeServiceFactory<S> extends ServiceFactory<S> {
diff --git a/framework/src/main/java/org/osgi/framework/ServiceObjects.java b/framework/src/main/java/org/osgi/framework/ServiceObjects.java
index 8490189..145ed77 100644
--- a/framework/src/main/java/org/osgi/framework/ServiceObjects.java
+++ b/framework/src/main/java/org/osgi/framework/ServiceObjects.java
@@ -41,7 +41,7 @@ import org.osgi.annotation.versioning.ProviderType;
* @see PrototypeServiceFactory
* @ThreadSafe
* @since 1.8
- * @author $Id$
+ * @author $Id: ServiceObjects.java 1825132 2018-02-23 15:11:00Z pauls $
*/
@ProviderType
public interface ServiceObjects<S> {
diff --git a/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java b/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java
new file mode 100644
index 0000000..eb80ae6
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) OSGi Alliance (2019). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.framework.connect;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Optional;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleRevisions;
+
+/**
+ * A connect content provides a {@link Framework framework} access to the
+ * content of a connect {@link ConnectModule module}. A framework may
+ * {@link #open() open} and {@link #close() close} the content for a connect
+ * module multiple times while the connect content is in use by the framework
+ * instance. The framework must close the connect content once the connect
+ * content is no longer used as the content of a current bundle revision or an
+ * in use bundle revision.
+ * <p>
+ * An entry in a connect content is identified by a path name that is a
+ * '{@code /}'-separated path. A connect content may treat directories as
+ * entries. A directory entry path name will end with a slash ('/'). A directory
+ * entry may be located using a path name that drops the trailing slash.
+ *
+ * @see BundleRevisions
+ * @ThreadSafe
+ * @author $Id: 44ec66031f9460c48453c7113e4871472a7c475c $
+ */
+public interface ConnectContent {
+ /**
+ * The {@code osgi.identity}
+ * {@link IdentityNamespace#CAPABILITY_TAGS_ATTRIBUTE tags} attribute value
+ * used by the framework to tag connect bundle revisions.
+ */
+ public static final String TAG_OSGI_CONNECT = "osgi.connect";
+
+ /**
+ * Returns this connect content Manifest headers and values. The
+ * {@link Optional#empty() empty} value is returned if the framework should
+ * handle parsing the Manifest of the content itself.
+ *
+ * @return This connect content Manifest headers and values.
+ * @throws IllegalStateException if the connect content has been closed
+ */
+ Optional<Map<String,String>> getHeaders();
+
+ /**
+ * Returns an iterable with all the entry names available in this
+ * ConnectContent
+ *
+ * @return the entry names
+ * @throws IOException if an error occurs reading the ConnectContent
+ * @throws IllegalStateException if the connect content has been closed
+ */
+ Iterable<String> getEntries() throws IOException;
+
+ /**
+ * Returns the connect entry for the specified path name in this content.
+ * The {@link Optional#empty() empty} value is returned if an entry with the
+ * specified path name does not exist. The path must not start with a
+ * "/" 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 '#'} \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™
+ * 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="[1.0,2.0)"
* </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="[1.2,2.0)"
* </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="[1.0,2.0)"
* </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);