Merge pull request #65 from apache/frameworkR8
FELIX-6369: Implement R8 core spec
diff --git a/framework.security/src/main/java/org/apache/felix/framework/FakeBundle.java b/framework.security/src/main/java/org/apache/felix/framework/FakeBundle.java
index f7b3d9b..19a0506 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/FakeBundle.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/FakeBundle.java
@@ -185,11 +185,6 @@
return System.identityHashCode(this);
}
- public int compareTo(Bundle o) {
- // TODO Auto-generated method stub
- return 0;
- }
-
public Object adapt(Class arg0) {
// TODO Auto-generated method stub
return null;
@@ -199,9 +194,4 @@
// TODO Auto-generated method stub
return null;
}
-
- public int compareTo(Object t)
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java
index 4df53f2..801182e 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java
@@ -34,12 +34,16 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.PropertyPermission;
import org.apache.felix.framework.util.SecureAction;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.CapabilityPermission;
import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
@@ -107,12 +111,15 @@
{
return new PermissionInfo[] {
IMPLICIT[0],
- new PermissionInfo(AdminPermission.class.getName(), "(id="
- + bundle.getBundleId() + ")", AdminPermission.METADATA),
- new PermissionInfo(AdminPermission.class.getName(), "(id="
- + bundle.getBundleId() + ")", AdminPermission.RESOURCE),
- new PermissionInfo(AdminPermission.class.getName(), "(id="
- + bundle.getBundleId() + ")", AdminPermission.CONTEXT) };
+ new PermissionInfo(PropertyPermission.class.getName(), "org.osgi.framework.*", "read"),
+ new PermissionInfo(
+ AdminPermission.class.getName(),
+ "(id=" + bundle.getBundleId() + ")",
+ AdminPermission.CLASS + "," + AdminPermission.METADATA + "," + AdminPermission.RESOURCE + "," + AdminPermission.CONTEXT),
+ new PermissionInfo(CapabilityPermission.class.getName(), "(|(capability.namespace=osgi.ee)(capability.namespace=osgi.native))", CapabilityPermission.REQUIRE),
+ new PermissionInfo(PackagePermission.class.getName(),"(package.name=java.*)",PackagePermission.IMPORT),
+ new PermissionInfo(ServicePermission.class.getName(),"org.osgi.service.condition.Condition", ServicePermission.GET)
+ };
}
public Permissions getPermissions(PermissionInfo[] permissionInfos)
diff --git a/framework/pom.xml b/framework/pom.xml
index 89ab25c..ed9f6f6 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -21,7 +21,7 @@
<groupId>org.apache.felix</groupId>
<artifactId>felix-parent</artifactId>
<version>6</version>
- <relativePath>../../pom/pom.xml</relativePath>
+ <relativePath>../pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
@@ -30,12 +30,12 @@
<version>6.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: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>
<tag>HEAD</tag>
</scm>
@@ -44,13 +44,13 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>4.1.0</version>
+ <version>5.1.1</version>
<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-Description>OSGi R8 framework implementation.</Bundle-Description>
<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
<Export-Package>
org.osgi.framework.*;-split-package:=first,
@@ -61,7 +61,8 @@
org.osgi.service.url;-split-package:=first,
org.osgi.service.resolver,
org.osgi.util.tracker;-split-package:=first,
- org.osgi.dto;-split-package:=first
+ org.osgi.dto;-split-package:=first,
+ org.osgi.service.condition;-split-package:=first
</Export-Package>
<Private-Package>org.apache.felix.framework.*, org.apache.felix.resolver.*</Private-Package>
<Import-Package>!*</Import-Package>
@@ -90,7 +91,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/**</exclude>
<exclude>src/main/resources/org/apache/felix/framework/Felix.properties</exclude>
</excludes>
</configuration>
@@ -119,18 +120,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>
@@ -153,7 +155,7 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
- <version>4.2</version>
+ <version>5.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -164,7 +166,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..7dd59d0 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -67,6 +67,7 @@
private final BundleArchive m_archive;
private final List<BundleRevisionImpl> m_revisions = new ArrayList<BundleRevisionImpl>(0);
+ private volatile BundleRevisionImpl m_currentRevision = null;
private volatile int m_state;
private boolean m_useDeclaredActivationPolicy;
private BundleActivator m_activator = null;
@@ -188,16 +189,24 @@
{
// Get current revision, since we can reuse it.
BundleRevisionImpl current = adapt(BundleRevisionImpl.class);
- // Close all existing revisions.
- closeRevisions();
- // Clear all revisions.
+
+ 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();
+ m_currentRevision = null;
- // Purge all old archive revisions, only keeping the newest one.
- m_archive.purge();
-
- // Reset the content of the current bundle revision.
- current.resetContent(m_archive.getCurrentRevision().getContent());
// Re-add the revision to the bundle.
addRevision(current);
@@ -1091,7 +1100,7 @@
}
@Override
- public synchronized <A> A adapt(Class<A> type)
+ public <A> A adapt(Class<A> type)
{
checkAdapt(type);
if (type == BundleContext.class)
@@ -1109,14 +1118,14 @@
{
return null;
}
- return (A) m_revisions.get(0);
+ return (A) m_currentRevision;
}
// We need some way to get the current revision even if
// the associated bundle is uninstalled, so we use the
// impl revision class for this purpose.
else if (type == BundleRevisionImpl.class)
{
- return (A) m_revisions.get(0);
+ return (A) m_currentRevision;
}
else if (type == BundleRevisions.class)
{
@@ -1128,7 +1137,9 @@
{
return null;
}
- return (A) m_revisions.get(0).getWiring();
+ BundleRevisionImpl revision = m_currentRevision;
+
+ return (A) (revision != null ? revision.getWiring() : null);
}
else if ( type == AccessControlContext.class)
{
@@ -1229,6 +1240,8 @@
synchronized boolean rollbackRevise() throws Exception
{
BundleRevision br = m_revisions.remove(0);
+ m_currentRevision = !m_revisions.isEmpty() ? m_revisions.get(0) : null;
+
// Since revising a bundle adds a revision to the global
// state, we must remove it from the global state on rollback.
getFramework().getResolver().removeRevision(br);
@@ -1242,7 +1255,9 @@
// which is the normal case.
synchronized void addRevision(BundleRevisionImpl revision) throws Exception
{
+ BundleRevisionImpl previous = m_currentRevision;
m_revisions.add(0, revision);
+ m_currentRevision = revision;
try
{
@@ -1251,6 +1266,7 @@
catch (Exception ex)
{
m_revisions.remove(0);
+ m_currentRevision = previous;
throw ex;
}
@@ -1332,7 +1348,7 @@
}
}
}
- if (!collisionCanditates.isEmpty())
+ if (!collisionCanditates.isEmpty() && m_installingBundle != null)
{
throw new BundleException(
"Bundle symbolic name and version are not unique: "
@@ -1347,7 +1363,7 @@
{
ProtectionDomain pd = null;
- for (int i = m_revisions.size() - 1; (i >= 0) && (pd == null); i--)
+ for (int i = 0; (i < m_revisions.size()) && (pd == null); i++)
{
pd = m_revisions.get(i).getProtectionDomain();
}
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..9d51085 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
@@ -21,13 +21,10 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
-import java.lang.reflect.InvocationTargetException;
-import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
@@ -45,16 +42,12 @@
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.framework.util.FelixConstants;
-import org.osgi.framework.Bundle;
import org.osgi.framework.PackagePermission;
-import org.osgi.framework.wiring.BundleRevision;
-
public class BundleProtectionDomain extends ProtectionDomain
{
private static final class BundleInputStream extends InputStream
@@ -76,7 +69,7 @@
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("/"))
@@ -264,12 +257,12 @@
{
target = Felix.m_secureAction.createTempFile("jar", null, null);
Felix.m_secureAction.deleteFileOnExit(target);
- FileOutputStream output = null;
+ OutputStream output = null;
InputStream input = null;
IOException rethrow = null;
try
{
- output = new FileOutputStream(target);
+ output = Felix.m_secureAction.getOutputStream(target);
input = new BundleInputStream(content);
byte[] buffer = new byte[64 * 1024];
for (int i = input.read(buffer);i != -1; i = input.read(buffer))
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 036732b..5aa9daa 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -319,6 +319,15 @@
}
}
+ 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;
@@ -616,6 +625,25 @@
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);
+ }
+ long result = content.getContentTime(urlPath);
+ return result > 0 ? result : 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 437d962..0e970bc 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;
@@ -112,7 +113,7 @@
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;
@@ -726,16 +727,24 @@
// 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;
}
@@ -1490,24 +1499,21 @@
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
@@ -1518,25 +1524,39 @@
// 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.");
}
- result = ((BundleClassLoader) cl).findClass(name);
+ else
+ {
+ throw new ResourceNotFoundException("Unable to load resource '"
+ + name
+ + "' because the bundle wiring for "
+ + m_revision.getSymbolicName()
+ + " is no longer valid.");
+ }
+ }
+ if (cl instanceof BundleClassLoader)
+ {
+ result = isClass ? ((BundleClassLoader) cl).findClass(name) :
+ ((BundleClassLoader) cl).findResource(name);
}
else
{
- result = m_revision.getResourceLocal(name);
+ 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)
{
@@ -2052,7 +2072,7 @@
catch (Error e)
{
// Mark the woven class as incomplete.
- wci.complete(null, null, null);
+ wci.complete();
wci.setState(WovenClass.TRANSFORMING_FAILED);
callWovenClassListeners(felix, wovenClassListeners, wci);
throw e;
diff --git a/framework/src/main/java/org/apache/felix/framework/DTOFactory.java b/framework/src/main/java/org/apache/felix/framework/DTOFactory.java
index 0abcf9c..7d38e40 100644
--- a/framework/src/main/java/org/apache/felix/framework/DTOFactory.java
+++ b/framework/src/main/java/org/apache/felix/framework/DTOFactory.java
@@ -120,6 +120,11 @@
return null;
}
+ static ServiceReferenceDTO createDTO(ServiceReference ref)
+ {
+ return createServiceReferenceDTO(ref);
+ }
+
private static BundleDTO createBundleDTO(Bundle bundle)
{
BundleDTO dto = new BundleDTO();
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..5ed9955 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;
@@ -53,8 +54,6 @@
import org.osgi.framework.wiring.BundleWiring;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -245,11 +244,23 @@
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,15 +282,16 @@
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();
File modulesDir = felix.getDataFile(felix, "modules");
- modulesDir.mkdirs();
+ Felix.m_secureAction.mkdirs(modulesDir);
File cached = new File(modulesDir, moduleKey + ".properties");
- if (cached.isFile())
+ if (Felix.m_secureAction.isFile(cached))
{
- FileInputStream input = new FileInputStream(cached);
+ InputStream input = Felix.m_secureAction.getInputStream(cached);
cachedProps.load(new InputStreamReader(input, "UTF-8"));
input.close();
for (Enumeration<?> keys = cachedProps.propertyNames(); keys.hasMoreElements();)
@@ -301,7 +313,7 @@
cachedProps.setProperty(pkg, String.join(",", uses));
}
}
- OutputStream output = new FileOutputStream(cached);
+ OutputStream output = Felix.m_secureAction.getOutputStream(cached);
cachedProps.store(new OutputStreamWriter(output, "UTF-8"), null);
output.close();
}
@@ -344,7 +356,7 @@
}
}
- 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 @@
((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 @@
{
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,33 @@
{
f = ((JarContent) revisionContent).getFile();
}
- else
+ else if (revisionContent instanceof DirectoryContent)
{
f = ((DirectoryContent) revisionContent).getFile();
}
- try
+ else
{
- AccessController.doPrivileged(new PrivilegedExceptionAction<Void>()
- {
- @Override
- public Void run() throws Exception {
- m_extenderFramework.add(f);
- return null;
- }
- });
+ 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);
@@ -878,6 +898,12 @@
return null;
}
+ @Override
+ public long getContentTime(String name)
+ {
+ return -1L;
+ }
+
//
// Utility methods.
//
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..ade90ee 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.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;
@@ -74,9 +75,7 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -213,6 +212,8 @@
// 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 +352,11 @@
**/
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 +466,8 @@
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 +642,12 @@
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 +734,10 @@
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 +786,6 @@
"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 +795,7 @@
// First get cached bundle identifiers.
try
{
- archives = m_cache.getArchives();
+ archives = m_cache.getArchives(m_connectFramework);
}
catch (Exception ex)
{
@@ -850,6 +861,12 @@
m_extensionManager.startExtensionBundle(this, (BundleImpl) extension);
}
+
+ if (m_connectFramework != null)
+ {
+ m_connectFramework.newBundleActivator().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
@@ -884,7 +901,6 @@
}
catch (Throwable ex)
{
- m_dispatcher.stopDispatching();
m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
throw new RuntimeException("Unable to start system bundle.");
}
@@ -948,6 +964,17 @@
}
}
}
+ catch (Throwable t)
+ {
+ stopBundle(this, false);
+ if (m_cache != null)
+ {
+ m_cache.release();
+ m_cache = null;
+ }
+ __setState(Bundle.INSTALLED);
+ throw t;
+ }
finally
{
releaseBundleLock(this);
@@ -987,7 +1014,7 @@
BufferedReader input = null;
try
{
- input = new BufferedReader(new InputStreamReader(new FileInputStream(dataFile), "UTF-8"));
+ input = new BufferedReader(new InputStreamReader(m_secureAction.getInputStream(dataFile), "UTF-8"));
lastVersion = Version.parseVersion(input.readLine()).getMajor();
}
catch (Exception ignore)
@@ -1020,7 +1047,7 @@
try
{
output = new PrintWriter(new OutputStreamWriter(
- new FileOutputStream(getDataFile(this, "last.java.version")), "UTF-8"));
+ m_secureAction.getOutputStream(getDataFile(this, "last.java.version")), "UTF-8"));
output.println(Integer.toString(currentVersion));
output.flush();
}
@@ -3224,7 +3251,7 @@
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)
{
@@ -3760,8 +3787,11 @@
Class clazz = Util.loadClassUsingClass(svcObj.getClass(), classNames[i], m_secureAction);
if (clazz == null)
{
- throw new IllegalArgumentException(
- "Cannot cast service: " + classNames[i]);
+ if (!Util.checkImplementsWithName(svcObj.getClass(), classNames[i]))
+ {
+ throw new IllegalArgumentException(
+ "Cannot cast service: " + classNames[i]);
+ }
}
else if (!clazz.isAssignableFrom(svcObj.getClass()))
{
@@ -5025,9 +5055,12 @@
try
{
File file = m_cache.getSystemBundleDataFile("bundle.id");
- is = m_secureAction.getFileInputStream(file);
- br = new BufferedReader(new InputStreamReader(is));
- return Long.parseLong(br.readLine());
+ if (m_secureAction.isFile(file))
+ {
+ is = m_secureAction.getInputStream(file);
+ br = new BufferedReader(new InputStreamReader(is));
+ return Long.parseLong(br.readLine());
+ }
}
catch (FileNotFoundException ex)
{
@@ -5078,7 +5111,7 @@
try
{
File file = m_cache.getSystemBundleDataFile("bundle.id");
- os = m_secureAction.getFileOutputStream(file);
+ os = m_secureAction.getOutputStream(file);
bw = new BufferedWriter(new OutputStreamWriter(os));
String s = Long.toString(m_nextId);
bw.write(s, 0, s.length());
@@ -5110,18 +5143,28 @@
}
}
+ public boolean hasConnectFramework()
+ {
+ return m_connectFramework != null;
+ }
+
//
// Miscellaneous inner classes.
//
class SystemBundleActivator implements BundleActivator
{
+ private volatile ServiceRegistration<org.osgi.service.condition.Condition> m_reg;
@Override
public void start(BundleContext context) throws Exception
{
// Add the bundle activator for the url handler service.
m_activatorList.add(0, new URLHandlersActivator(m_configMap, Felix.this));
+
+ m_reg = context.registerService(org.osgi.service.condition.Condition.class, org.osgi.service.condition.Condition.INSTANCE,
+ FrameworkUtil.asDictionary(Collections.singletonMap(org.osgi.service.condition.Condition.CONDITION_ID, org.osgi.service.condition.Condition.CONDITION_ID_TRUE)));
+
// Start all activators.
for (Iterator<BundleActivator> iter = m_activatorList.iterator(); iter.hasNext(); )
{
@@ -5132,6 +5175,7 @@
}
catch (Throwable throwable)
{
+ throwable.printStackTrace();
iter.remove();
fireFrameworkEvent(FrameworkEvent.ERROR, context.getBundle(),
new BundleException("Unable to start Bundle", throwable));
@@ -5141,6 +5185,7 @@
throwable);
}
}
+
}
@Override
@@ -5163,6 +5208,8 @@
// Stop framework start level thread.
m_fwkStartLevel.stop();
+ m_resolver.stop();
+
// Shutdown event dispatching queue.
m_dispatcher.stopDispatching();
@@ -5234,6 +5281,8 @@
throwable);
}
}
+ m_reg.unregister();
+ 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/FrameworkWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/FrameworkWiringImpl.java
index 7bc42ce..f051ac0 100644
--- a/framework/src/main/java/org/apache/felix/framework/FrameworkWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/FrameworkWiringImpl.java
@@ -63,9 +63,6 @@
* thread explicitly is required in the embedded case, where Felix may be
* stopped without the Java VM being stopped. In this case the
* FelixFrameworkWiring thread must be stopped explicitly.
- * <p>
- * This method is called by the
- * {@link PackageAdminActivator#stop(BundleContext)} method.
*/
void stop()
{
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
index 9169900..614797a 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
@@ -42,6 +42,7 @@
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.dto.ServiceReferenceDTO;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
@@ -363,9 +364,12 @@
{
if (clazz == null)
{
- throw new ServiceException(
- "Service cannot be cast due to missing class: " + m_classes[i],
- ServiceException.FACTORY_ERROR);
+ if (!Util.checkImplementsWithName(svcObj.getClass(), m_classes[i]))
+ {
+ throw new ServiceException(
+ "Service cannot be cast due to missing class: " + m_classes[i],
+ ServiceException.FACTORY_ERROR);
+ }
}
else
{
@@ -696,9 +700,20 @@
}
@Override
- public Dictionary<String, Object> getProperties() {
+ public Dictionary<String, Object> getProperties()
+ {
return new Hashtable<String, Object>(ServiceRegistrationImpl.this.m_propMap);
}
+
+ @Override
+ public Object adapt(Class type)
+ {
+ if (type == ServiceReferenceDTO.class)
+ {
+ return DTOFactory.createDTO(this);
+ }
+ return null;
+ }
}
private class ServiceReferenceMap implements Map
diff --git a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
index 2d71eae..cca8544 100644
--- a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
@@ -31,7 +31,6 @@
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
@@ -45,7 +44,6 @@
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.ShrinkableCollection;
import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.NativeLibrary;
import org.apache.felix.framework.wiring.BundleRequirementImpl;
import org.apache.felix.framework.wiring.BundleWireImpl;
import org.apache.felix.resolver.ResolverImpl;
@@ -57,6 +55,7 @@
import org.osgi.framework.Constants;
import org.osgi.framework.PackagePermission;
import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.wiring.BundleCapability;
@@ -91,6 +90,7 @@
private final Map<String, List<BundleRevision>> m_singletons;
// Selected singleton bundle revisions.
private final Set<BundleRevision> m_selectedSingletons;
+ private volatile ServiceRegistration<?> m_serviceRegistration;
StatefulResolver(Felix felix, ServiceRegistry registry)
{
@@ -168,12 +168,22 @@
void start()
{
- m_registry.registerService(m_felix,
- new String[] { Resolver.class.getName() },
+ m_serviceRegistration = m_registry.registerService(m_felix,
+ new String[]{Resolver.class.getName()},
new ResolverImpl(m_logger, 1),
null);
}
+ void stop()
+ {
+ ServiceRegistration reg = m_serviceRegistration;
+ if (reg != null)
+ {
+ reg.unregister();
+ m_serviceRegistration = null;
+ }
+ }
+
synchronized void addRevision(BundleRevision br)
{
// Always attempt to remove the revision, since
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 @@
? 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 @@
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 @@
}
}
+ // 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 794af28..bbde321 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 @@
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;
@@ -87,7 +87,6 @@
{
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);
@@ -149,6 +148,7 @@
m_is = ((BundleRevisionImpl)
m_targetRevision).getInputStream(m_classPathIdx, m_path);
m_contentLength = (m_is == null) ? 0 : m_is.available();
+ m_contentTime = ((BundleRevisionImpl) m_targetRevision).getContentTime(m_classPathIdx, m_path);
m_contentType = URLConnection.guessContentTypeFromName(m_path);
connected = true;
}
@@ -164,6 +164,11 @@
public int getContentLength()
{
+ return (int) getContentLengthLong();
+ }
+
+ public long getContentLengthLong()
+ {
try
{
connect();
@@ -176,11 +181,6 @@
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..7ad09da 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -22,7 +22,6 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
@@ -138,7 +137,7 @@
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/WovenClassImpl.java b/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
index 9459f2f..4e3a8c7 100644
--- a/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/WovenClassImpl.java
@@ -30,7 +30,6 @@
import org.osgi.framework.AdminPermission;
import org.osgi.framework.PackagePermission;
import org.osgi.framework.hooks.weaving.WovenClass;
-import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleWiring;
class WovenClassImpl implements WovenClass, List<String>
@@ -52,12 +51,10 @@
m_state = TRANSFORMING;
}
- synchronized void complete(Class definedClass, byte[] bytes,
- List<String> imports)
+ synchronized void complete()
{
- completeDefine(definedClass);
- m_bytes = (bytes == null) ? m_bytes : bytes;
- completeImports(imports);
+ completeDefine(null);
+ completeImports(null);
}
synchronized void completeImports(List<String> imports)
@@ -66,7 +63,7 @@
: Util.newImmutableList(imports);
}
- synchronized void completeDefine(Class definedClass)
+ synchronized void completeDefine(Class<?> definedClass)
{
m_definedClass = definedClass;
}
@@ -191,14 +188,12 @@
{
try
{
- List<BundleRequirement> reqs = ManifestParser
+ ManifestParser
.parseDynamicImportHeader(null, null, s);
} catch (Exception ex)
{
- RuntimeException re = new IllegalArgumentException(
- "Unable to parse dynamic import.");
- re.initCause(ex);
- throw re;
+ throw new IllegalArgumentException(
+ "Unable to parse dynamic import.", ex);
}
checkImport(s);
return m_imports.add(s);
@@ -244,14 +239,12 @@
{
try
{
- List<BundleRequirement> reqs = ManifestParser
+ ManifestParser
.parseDynamicImportHeader(null, null, s);
} catch (Exception ex)
{
- RuntimeException re = new IllegalArgumentException(
- "Unable to parse dynamic import.");
- re.initCause(ex);
- throw re;
+ throw new IllegalArgumentException(
+ "Unable to parse dynamic import.", ex);
}
checkImport(s);
}
@@ -271,14 +264,12 @@
{
try
{
- List<BundleRequirement> reqs = ManifestParser
+ ManifestParser
.parseDynamicImportHeader(null, null, s);
} catch (Exception ex)
{
- RuntimeException re = new IllegalArgumentException(
- "Unable to parse dynamic import.");
- re.initCause(ex);
- throw re;
+ throw new IllegalArgumentException(
+ "Unable to parse dynamic import.", ex);
}
checkImport(s);
}
@@ -333,14 +324,12 @@
}
try
{
- List<BundleRequirement> reqs = ManifestParser
+ ManifestParser
.parseDynamicImportHeader(null, null, s);
} catch (Exception ex)
{
- RuntimeException re = new IllegalArgumentException(
- "Unable to parse dynamic import.");
- re.initCause(ex);
- throw re;
+ throw new IllegalArgumentException(
+ "Unable to parse dynamic import.", ex);
}
checkImport(s);
return m_imports.set(i, s);
@@ -356,14 +345,13 @@
}
try
{
- List<BundleRequirement> reqs = ManifestParser
+ ManifestParser
.parseDynamicImportHeader(null, null, s);
- } catch (Exception ex)
+ }
+ catch (Exception ex)
{
- RuntimeException re = new IllegalArgumentException(
- "Unable to parse dynamic import.");
- re.initCause(ex);
- throw re;
+ throw new IllegalArgumentException(
+ "Unable to parse dynamic import.", ex);
}
checkImport(s);
m_imports.add(i, s);
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..50e4d31 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
@@ -27,6 +27,8 @@
import org.apache.felix.framework.util.WeakZipFileFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
+import org.osgi.framework.connect.ModuleConnector;
+import org.osgi.framework.connect.ConnectModule;
/**
* <p>
@@ -100,6 +102,8 @@
**/
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 +125,8 @@
* @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 +146,8 @@
m_lastModified = System.currentTimeMillis();
m_refreshCount = 0;
+ m_connector = connectFactory;
+
// Save state.
initialize();
@@ -160,7 +167,7 @@
* @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
{
@@ -184,7 +191,7 @@
for (File child : children)
{
if (child.getName().startsWith(REVISION_DIRECTORY)
- && child.isDirectory())
+ && BundleCache.getSecureAction().isFileDirectory(child))
{
// Determine the revision number and add it to the revision map.
int idx = child.getName().lastIndexOf('.');
@@ -211,8 +218,12 @@
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);
}
/**
@@ -542,7 +553,7 @@
BufferedReader br = null;
try
{
- is = BundleCache.getSecureAction().getFileInputStream(new File(
+ is = BundleCache.getSecureAction().getInputStream(new File(
new File(m_archiveRootDir, REVISION_DIRECTORY +
getRefreshCount() + "." + revNum.toString()), REVISION_LOCATION_FILE));
@@ -565,7 +576,7 @@
try
{
os = BundleCache.getSecureAction()
- .getFileOutputStream(new File(
+ .getOutputStream(new File(
new File(m_archiveRootDir, REVISION_DIRECTORY +
getRefreshCount() + "." + revNum.toString()), REVISION_LOCATION_FILE));
bw = new BufferedWriter(new OutputStreamWriter(os));
@@ -795,9 +806,19 @@
}
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)
@@ -869,7 +890,7 @@
try
{
is = BundleCache.getSecureAction()
- .getFileInputStream(infoFile);
+ .getInputStream(infoFile);
br = new BufferedReader(new InputStreamReader(is));
// Read id.
@@ -900,7 +921,7 @@
try
{
os = BundleCache.getSecureAction()
- .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_INFO_FILE));
+ .getOutputStream(new File(m_archiveRootDir, BUNDLE_INFO_FILE));
bw = new BufferedWriter(new OutputStreamWriter(os));
// Write id.
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..ef544d0 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,9 +22,9 @@
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;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -144,25 +144,14 @@
{
File lockFile = new File(cacheDir, CACHE_LOCK_NAME);
FileChannel fc = null;
- FileOutputStream fos = null;
try
{
- if (!getSecureAction().fileExists(lockFile))
- {
- fos = getSecureAction().getFileOutputStream(lockFile);
- fc = fos.getChannel();
- }
- else
- {
- fos = getSecureAction().getFileOutputStream(lockFile);
- fc = fos.getChannel();
- }
+ fc = getSecureAction().getFileChannel(lockFile);
}
catch (Exception ex)
{
try
{
- if (fos != null) fos.close();
if (fc != null) fc.close();
}
catch (Exception ex2)
@@ -386,6 +375,11 @@
}
}
+ public File getCacheDir()
+ {
+ return determineCacheDir(m_configMap);
+ }
+
/* package */ static SecureAction getSecureAction()
{
return m_secureAction;
@@ -398,7 +392,7 @@
deleteDirectoryTree(cacheDir);
}
- public BundleArchive[] getArchives()
+ public BundleArchive[] getArchives(ModuleConnector connectFactory)
throws Exception
{
// Get buffer size value.
@@ -431,7 +425,7 @@
{
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 +441,7 @@
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 +455,7 @@
// 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;
}
@@ -548,7 +542,7 @@
try
{
- os = getSecureAction().getFileOutputStream(outputFile);
+ os = getSecureAction().getOutputStream(outputFile);
for (int i = is.read(bytes);i != -1; i = is.read(bytes))
{
os.write(bytes, 0, i);
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..88e6dec
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ConnectContentContent.java
@@ -0,0 +1,218 @@
+/*
+ * 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;
+ }
+
+ @Override
+ 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..94b3284
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ConnectRevision.java
@@ -0,0 +1,90 @@
+/*
+ * 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
+ {
+ byte[] manifest = entry.getBytes();
+
+ return Optional.of((Map<String, String>) (Map) BundleCache.getMainAttributes(new StringMap(), new java.io.ByteArrayInputStream(manifest), manifest.length));
+ }
+ 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/Content.java b/framework/src/main/java/org/apache/felix/framework/cache/Content.java
index 09ace44..79580c9 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/Content.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/Content.java
@@ -121,4 +121,6 @@
* or null if not possible.
*/
URL getEntryAsURL(String name);
+
+ long getContentTime(String urlPath);
}
\ No newline at end of file
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..e1f04ac 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
@@ -47,10 +47,7 @@
public boolean hasEntry(String name) throws IllegalStateException
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
return m_content.hasEntry(m_rootPath + name);
}
@@ -63,10 +60,7 @@
public byte[] getEntryAsBytes(String name) throws IllegalStateException
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
return m_content.getEntryAsBytes(m_rootPath + name);
}
@@ -74,35 +68,42 @@
public InputStream getEntryAsStream(String name)
throws IllegalStateException, IOException
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
return m_content.getEntryAsStream(m_rootPath + name);
}
+ private String getName(String name)
+ {
+ if ((name.length() > 0) && (name.charAt(0) == '/')) {
+ name = name.substring(1);
+ }
+ return name;
+ }
+
public URL getEntryAsURL(String name)
{
return m_content.getEntryAsURL(m_rootPath + name);
}
+ @Override
+ public long getContentTime(String name)
+ {
+ name = getName(name);
+
+ return m_content.getContentTime(m_rootPath + name);
+ }
+
public Content getEntryAsContent(String name)
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
return m_content.getEntryAsContent(m_rootPath + name);
}
public String getEntryAsNativeLibrary(String name)
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
return m_content.getEntryAsNativeLibrary(m_rootPath + name);
}
@@ -143,14 +144,17 @@
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/DirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
index 50da934..0240933 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
@@ -72,10 +72,7 @@
public boolean hasEntry(String name) throws IllegalStateException
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
// Return true if the file associated with the entry exists,
// unless the entry name ends with "/", in which case only
@@ -97,10 +94,7 @@
public byte[] getEntryAsBytes(String name) throws IllegalStateException
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
// Get the embedded resource.
@@ -108,7 +102,7 @@
try
{
- return BundleCache.getSecureAction().fileExists(file) ? BundleCache.read(BundleCache.getSecureAction().getFileInputStream(file), file.length()) : null;
+ return BundleCache.getSecureAction().fileExists(file) ? BundleCache.read(BundleCache.getSecureAction().getInputStream(file), file.length()) : null;
}
catch (Exception ex)
{
@@ -122,15 +116,12 @@
public InputStream getEntryAsStream(String name)
throws IllegalStateException, IOException
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
- name = name.substring(1);
- }
+ name = getName(name);
File file = new File(m_dir, name);
try
{
- return BundleCache.getSecureAction().fileExists(file) ? BundleCache.getSecureAction().getFileInputStream(file) : null;
+ return BundleCache.getSecureAction().fileExists(file) ? BundleCache.getSecureAction().getInputStream(file) : null;
}
catch (Exception ex)
{
@@ -141,12 +132,17 @@
}
}
- public URL getEntryAsURL(String name)
+ private String getName(String name)
{
- if ((name.length() > 0) && (name.charAt(0) == '/'))
- {
+ if ((name.length() > 0) && (name.charAt(0) == '/')) {
name = name.substring(1);
}
+ return name;
+ }
+
+ public URL getEntryAsURL(String name)
+ {
+ name = getName(name);
if (hasEntry(name))
{
@@ -165,6 +161,16 @@
}
}
+ @Override
+ public long getContentTime(String name)
+ {
+ name = getName(name);
+
+ File file = new File(m_dir, name);
+
+ return BundleCache.getSecureAction().getLastModified(file);
+ }
+
public Content getEntryAsContent(String entryName)
{
// If the entry name refers to the content itself, then
@@ -278,7 +284,7 @@
try
{
- is = BundleCache.getSecureAction().getFileInputStream(entryFile);
+ is = BundleCache.getSecureAction().getInputStream(entryFile);
// Create the file.
BundleCache.copyStreamToFile(is, libFile);
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
index a1cf608..f6d01a9 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
@@ -71,7 +71,7 @@
throws Exception
{
File manifest = new File(m_refDir, "META-INF/MANIFEST.MF");
- return manifest.isFile() ? BundleCache.getMainAttributes(new StringMap(), BundleCache.getSecureAction().getFileInputStream(manifest), manifest.length()) : null;
+ return BundleCache.getSecureAction().isFile(manifest) ? BundleCache.getMainAttributes(new StringMap(), BundleCache.getSecureAction().getInputStream(manifest), manifest.length()) : null;
}
public Content getContent() throws Exception
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..79c2249 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
@@ -112,9 +112,6 @@
{
return false;
}
- finally
- {
- }
}
public Enumeration<String> getEntries()
@@ -195,6 +192,20 @@
}
}
+ @Override
+ public long getContentTime(String urlPath)
+ {
+ try
+ {
+ ZipEntry ze = m_zipFile.getEntry(urlPath);
+ return ze.getTime();
+ }
+ catch (Exception ex)
+ {
+ return -1L;
+ }
+ }
+
public Content getEntryAsContent(String entryName)
{
// If the entry name refers to the content itself, then
@@ -398,7 +409,7 @@
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/cache/JarRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
index ac5c1e8..4ff4e84 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
@@ -147,7 +147,7 @@
// set request properties such as proxy auth.
URL url = BundleCache.getSecureAction().createURL(
null, getLocation(), null);
- conn = url.openConnection();
+ conn = BundleCache.getSecureAction().openURLConnection(url);
// Support for http proxy authentication.
String auth = BundleCache.getSecureAction()
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 @@
{
// 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/capabilityset/SimpleFilter.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
index 253c70d..6e56485 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
@@ -18,7 +18,6 @@
*/
package org.apache.felix.framework.capabilityset;
-import org.osgi.framework.Version;
import org.osgi.framework.VersionRange;
import java.util.ArrayList;
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..f83663c 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.net.URL;
import java.net.URLClassLoader;
+import org.apache.felix.framework.util.SecureAction;
+
public interface ClassPathExtenderFactory
{
interface ClassPathExtender
@@ -50,10 +52,10 @@
try
{
append = app.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
- append.setAccessible(true);
+ new SecureAction().setAccesssible(append);
break;
}
- catch (Exception e)
+ catch (Throwable e)
{
append = null;
try
@@ -74,9 +76,9 @@
try
{
addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
- addURL.setAccessible(true);
+ new SecureAction().setAccesssible(addURL);
}
- catch (Exception e)
+ catch (Throwable e)
{
addURL = null;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java b/framework/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java
index 53996dd..29a5e30 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/MultiReleaseContent.java
@@ -18,23 +18,15 @@
*/
package org.apache.felix.framework.util;
+import org.apache.felix.framework.cache.BundleCache;
import org.apache.felix.framework.cache.Content;
import org.osgi.framework.Version;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.Manifest;
+import java.util.*;
public class MultiReleaseContent implements Content
{
@@ -61,14 +53,16 @@
}
if (javaVersion > 8)
{
- InputStream manifestStream = null;
-
try
{
- manifestStream = content.getEntryAsStream("META-INF/MANIFEST.MF");
- if (manifestStream != null)
+ byte[] versionManifestInput = content.getEntryAsBytes("META-INF/MANIFEST.MF");
+
+ if (versionManifestInput != null)
{
- if ("true".equals(new Manifest(manifestStream).getMainAttributes().getValue("Multi-Release")))
+ Map<String, Object> versionManifest = BundleCache.getMainAttributes(
+ new StringMap(), new ByteArrayInputStream(versionManifestInput), versionManifestInput.length);
+
+ if ("true".equals(versionManifest.get("Multi-Release")))
{
content = new MultiReleaseContent(javaVersion, content);
}
@@ -77,17 +71,6 @@
catch (Exception ex)
{
}
- finally
- {
- if (manifestStream != null)
- {
- try
- {
- manifestStream.close();
- }
- catch (Exception ex){}
- }
- }
}
return content;
}
@@ -175,6 +158,12 @@
return m_content.getEntryAsURL(findPath(name));
}
+ @Override
+ public long getContentTime(String name)
+ {
+ return m_content.getContentTime(findPath(name));
+ }
+
private String findPath(String path)
{
String internalPath = path;
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..1e8024e 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
@@ -22,11 +22,15 @@
import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
import java.security.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.zip.ZipFile;
@@ -56,6 +60,32 @@
**/
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()
@@ -321,6 +351,28 @@
}
}
+ public boolean isFile(File file)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ Actions actions = (Actions) m_actions.get();
+ actions.set(Actions.FILE_IS_FILE_ACTION, file);
+ return ((Boolean) AccessController.doPrivileged(actions, m_acc))
+ .booleanValue();
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw (RuntimeException) ex.getException();
+ }
+ }
+ else
+ {
+ return file.isFile();
+ }
+ }
+
public boolean isFileDirectory(File file)
{
if (System.getSecurityManager() != null)
@@ -430,6 +482,56 @@
}
}
+ public InputStream getInputStream(File file) throws IOException
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ Actions actions = (Actions) m_actions.get();
+ actions.set(Actions.GET_INPUT_ACTION, file);
+ return (InputStream) AccessController.doPrivileged(actions, m_acc);
+ }
+ catch (PrivilegedActionException ex)
+ {
+ if (ex.getException() instanceof IOException)
+ {
+ throw (IOException) ex.getException();
+ }
+ throw (RuntimeException) ex.getException();
+ }
+ }
+ else
+ {
+ return Files.newInputStream(file.toPath());
+ }
+ }
+
+ public OutputStream getOutputStream(File file) throws IOException
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ Actions actions = (Actions) m_actions.get();
+ actions.set(Actions.GET_OUTPUT_ACTION, file);
+ return (OutputStream) AccessController.doPrivileged(actions, m_acc);
+ }
+ catch (PrivilegedActionException ex)
+ {
+ if (ex.getException() instanceof IOException)
+ {
+ throw (IOException) ex.getException();
+ }
+ throw (RuntimeException) ex.getException();
+ }
+ }
+ else
+ {
+ return Files.newOutputStream(file.toPath());
+ }
+ }
+
public FileInputStream getFileInputStream(File file) throws IOException
{
if (System.getSecurityManager() != null)
@@ -480,6 +582,31 @@
}
}
+ public FileChannel getFileChannel(File file) throws IOException
+ {
+ if (System.getSecurityManager() != null)
+ {
+ try
+ {
+ Actions actions = (Actions) m_actions.get();
+ actions.set(Actions.GET_FILE_CHANNEL_ACTION, file);
+ return (FileChannel) AccessController.doPrivileged(actions, m_acc);
+ }
+ catch (PrivilegedActionException ex)
+ {
+ if (ex.getException() instanceof IOException)
+ {
+ throw (IOException) ex.getException();
+ }
+ throw (RuntimeException) ex.getException();
+ }
+ }
+ else
+ {
+ return FileChannel.open(file.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+ }
+ }
+
public URI toURI(File file)
{
if (System.getSecurityManager() != null)
@@ -762,7 +889,7 @@
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 +978,7 @@
}
}
- public void setAccesssible(AccessibleObject ao)
+ public void setAccesssible(Executable ao)
{
if (System.getSecurityManager() != null)
{
@@ -868,7 +995,7 @@
}
else
{
- ao.setAccessible(true);
+ getAccessor(ao.getDeclaringClass()).accept(new AccessibleObject[]{ao});
}
}
@@ -889,7 +1016,8 @@
}
else
{
- method.setAccessible(true);
+ getAccessor(method.getDeclaringClass()).accept(new AccessibleObject[]{method});
+
return method.invoke(target, params);
}
}
@@ -955,8 +1083,7 @@
else
{
Field field = targetClass.getDeclaredField(name);
- field.setAccessible(true);
-
+ getAccessor(targetClass).accept(new AccessibleObject[]{field});
return field.get(target);
}
}
@@ -985,9 +1112,51 @@
}
}
+ 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 +1164,7 @@
{
Field lockField =
targetClazz.getDeclaredField(lockName);
- lockField.setAccessible(true);
+ getAccessor(targetClazz).accept(new AccessibleObject[]{lockField});
lock = lockField.get(null);
}
catch (NoSuchFieldException ex)
@@ -1010,14 +1179,14 @@
{
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 +1209,6 @@
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 +1249,13 @@
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);
@@ -1548,6 +1716,27 @@
}
}
+ public long getLastModified(File file)
+ {
+ if (System.getSecurityManager() != null)
+ {
+ Actions actions = (Actions) m_actions.get();
+ actions.set(Actions.LAST_MODIFIED, file);
+ try
+ {
+ return (Long) AccessController.doPrivileged(actions, m_acc);
+ }
+ catch (PrivilegedActionException e)
+ {
+ throw (RuntimeException) e.getException();
+ }
+ }
+ else
+ {
+ return file.lastModified();
+ }
+ }
+
private static class Actions implements PrivilegedExceptionAction
{
public static final int INITIALIZE_CONTEXT_ACTION = 0;
@@ -1609,6 +1798,11 @@
public static final int INVOKE_WOVEN_CLASS_LISTENER = 56;
public static final int GET_CANONICAL_PATH = 57;
public static final int CREATE_PROXY = 58;
+ public static final int LAST_MODIFIED = 59;
+ public static final int FILE_IS_FILE_ACTION = 60;
+ public static final int GET_FILE_CHANNEL_ACTION = 61;
+ private static final int GET_INPUT_ACTION = 62;
+ private static final int GET_OUTPUT_ACTION = 63;
private int m_action = -1;
private Object m_arg1 = null;
@@ -1708,7 +1902,7 @@
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 +1934,7 @@
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 +1959,7 @@
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 +1974,7 @@
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);
@@ -1872,6 +2066,16 @@
case CREATE_PROXY:
return Proxy.newProxyInstance((ClassLoader)arg1, (Class<?>[])arg2,
(InvocationHandler) arg3);
+ case LAST_MODIFIED:
+ return ((File) arg1).lastModified();
+ case FILE_IS_FILE_ACTION:
+ return ((File) arg1).isFile() ? Boolean.TRUE : Boolean.FALSE;
+ case GET_FILE_CHANNEL_ACTION:
+ return FileChannel.open(((File) arg1).toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+ case GET_INPUT_ACTION:
+ return Files.newInputStream(((File) arg1).toPath());
+ case GET_OUTPUT_ACTION:
+ return Files.newOutputStream(((File) arg1).toPath());
}
return null;
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..b8618bb 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
@@ -45,8 +45,9 @@
import java.util.ArrayList;
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;
@@ -60,37 +61,51 @@
{
/**
* 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;
+ }
+
+ 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
+ try (InputStream is = propURL.openConnection().getInputStream())
{
- // Load properties from URL.
- 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,13 +153,15 @@
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()));
+ properties.put("felix.detect.java.specification.version", version.getMajor() < 9 ? ("1." + (version.getMinor() > 6 ? version.getMinor() < 8 ? version.getMinor() : 8 : 6)) : Integer.toString(version.getMajor()));
if (version.getMajor() < 9)
{
- properties.put("felix.detect.java.version", String.format("0.0.0.JavaSE_001_%03d", version.getMinor() > 6 ? version.getMinor() : 6));
+ properties.put("felix.detect.java.version", String.format("0.0.0.JavaSE_001_%03d", version.getMinor() > 6 ? version.getMinor() < 8 ? version.getMinor() : 8 : 6));
}
else
{
@@ -157,9 +174,9 @@
}
}
- 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 +196,13 @@
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 +213,45 @@
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;
}
@@ -427,6 +462,31 @@
return null;
}
+ public static boolean checkImplementsWithName(Class<?> clazz, String name)
+ {
+ while (clazz != null)
+ {
+ if (clazz.getName().equals(name))
+ {
+ return true;
+ }
+ // Try to see if
+ // one of the class's implemented interface
+ // is the name.
+ Class[] ifcs = clazz.getInterfaces();
+ for (int i = 0; i < ifcs.length; i++)
+ {
+ if (checkImplementsWithName(ifcs[i], name)) {
+ return true;
+ }
+ }
+
+ // Try the super class class loader.
+ clazz = clazz.getSuperclass();
+ }
+ return false;
+ }
+
/**
* This method determines if the requesting bundle is able to cast
* the specified service reference based on class visibility rules
@@ -1034,4 +1094,4 @@
{
return map == null || map.isEmpty() ? EMPTY_MAP : Collections.unmodifiableMap(map);
}
-}
\ No newline at end of file
+}
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..09e0572 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 @@
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.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;
@@ -41,6 +43,7 @@
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 +112,7 @@
// 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 +218,7 @@
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 +699,7 @@
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 +839,7 @@
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 +848,7 @@
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 +1361,12 @@
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 +1381,11 @@
"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 +1408,48 @@
}
}
+ 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 +1465,9 @@
}
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/apache/felix/framework/wiring/BundleCapabilityImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
index 9fa3806..f426ad1 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -25,7 +25,6 @@
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
-import org.apache.felix.framework.capabilityset.SimpleFilter;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.manifestparser.ManifestParser;
import org.osgi.framework.Constants;
@@ -41,8 +40,6 @@
private final Map<String, String> m_dirs;
private final Map<String, Object> m_attrs;
private final List<String> m_uses;
- private final List<List<String>> m_includeFilter;
- private final List<List<String>> m_excludeFilter;
private final Set<String> m_mandatory;
public BundleCapabilityImpl(BundleRevision revision, String namespace,
@@ -55,13 +52,13 @@
// Find all export directives: uses, mandatory, include, and exclude.
- List<String> uses = Collections.EMPTY_LIST;
+ List<String> uses = Collections.emptyList();
String value = m_dirs.get(Constants.USES_DIRECTIVE);
if (value != null)
{
// Parse these uses directive.
StringTokenizer tok = new StringTokenizer(value, ",");
- uses = new ArrayList(tok.countTokens());
+ uses = new ArrayList<>(tok.countTokens());
while (tok.hasMoreTokens())
{
uses.add(tok.nextToken().trim());
@@ -69,44 +66,12 @@
}
m_uses = uses;
- value = m_dirs.get(Constants.INCLUDE_DIRECTIVE);
- if (value != null)
- {
- List<String> filters = ManifestParser.parseDelimitedString(value, ",");
- m_includeFilter = new ArrayList<List<String>>(filters.size());
- for (int filterIdx = 0; filterIdx < filters.size(); filterIdx++)
- {
- List<String> substrings = SimpleFilter.parseSubstring(filters.get(filterIdx));
- m_includeFilter.add(substrings);
- }
- }
- else
- {
- m_includeFilter = null;
- }
-
- value = m_dirs.get(Constants.EXCLUDE_DIRECTIVE);
- if (value != null)
- {
- List<String> filters = ManifestParser.parseDelimitedString(value, ",");
- m_excludeFilter = new ArrayList<List<String>>(filters.size());
- for (int filterIdx = 0; filterIdx < filters.size(); filterIdx++)
- {
- List<String> substrings = SimpleFilter.parseSubstring(filters.get(filterIdx));
- m_excludeFilter.add(substrings);
- }
- }
- else
- {
- m_excludeFilter = null;
- }
-
- Set<String> mandatory = Collections.EMPTY_SET;
+ Set<String> mandatory = Collections.emptySet();
value = m_dirs.get(Constants.MANDATORY_DIRECTIVE);
if (value != null)
{
List<String> names = ManifestParser.parseDelimitedString(value, ",");
- mandatory = new HashSet<String>(names.size());
+ mandatory = new HashSet<>(names.size());
for (String name : names)
{
// If attribute exists, then record it as mandatory.
@@ -160,38 +125,6 @@
return m_uses;
}
- public boolean isIncluded(String name)
- {
- if ((m_includeFilter == null) && (m_excludeFilter == null))
- {
- return true;
- }
-
- // Get the class name portion of the target class.
- String className = Util.getClassName(name);
-
- // If there are no include filters then all classes are included
- // by default, otherwise try to find one match.
- boolean included = (m_includeFilter == null);
- for (int i = 0;
- (!included) && (m_includeFilter != null) && (i < m_includeFilter.size());
- i++)
- {
- included = SimpleFilter.compareSubstring(m_includeFilter.get(i), className);
- }
-
- // If there are no exclude filters then no classes are excluded
- // by default, otherwise try to find one match.
- boolean excluded = false;
- for (int i = 0;
- (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.size());
- i++)
- {
- excluded = SimpleFilter.compareSubstring(m_excludeFilter.get(i), className);
- }
- return included && !excluded;
- }
-
@Override
public String toString()
{
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
index 3e3b685..881d366 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleRequirementImpl.java
@@ -61,7 +61,7 @@
BundleRevision revision, String namespace,
Map<String, String> dirs, Map<String, Object> attrs)
{
- this(revision, namespace, dirs, Collections.EMPTY_MAP, SimpleFilter.convert(attrs));
+ this(revision, namespace, dirs, Collections.emptyMap(), SimpleFilter.convert(attrs));
}
public String getNamespace()
@@ -91,7 +91,7 @@
public boolean matches(BundleCapability cap)
{
- return CapabilitySet.matches((BundleCapabilityImpl) cap, getFilter());
+ return CapabilitySet.matches(cap, getFilter());
}
public boolean isOptional()
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 @@
* 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/BundlePermission.java b/framework/src/main/java/org/osgi/framework/BundlePermission.java
index 2371599..d4de480 100644
--- a/framework/src/main/java/org/osgi/framework/BundlePermission.java
+++ b/framework/src/main/java/org/osgi/framework/BundlePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2004, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2004, 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.
@@ -58,7 +58,7 @@
*
* @since 1.3
* @ThreadSafe
- * @author $Id: 7b0816059dc9b3e37f0375039bebbe5f0b18d998 $
+ * @author $Id: 7a45b1cb579ff9cfd1582f82436dd755ff805fba $
*/
public final class BundlePermission extends BasicPermission {
@@ -543,7 +543,7 @@
// work our way up the tree...
int last;
int offset = requestedName.length() - 1;
- while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+ while ((last = requestedName.lastIndexOf('.', offset)) != -1) {
requestedName = requestedName.substring(0, last + 1) + "*";
bp = pc.get(requestedName);
if (bp != null) {
diff --git a/framework/src/main/java/org/osgi/framework/CapabilityPermission.java b/framework/src/main/java/org/osgi/framework/CapabilityPermission.java
index 177ff97..60a5202 100644
--- a/framework/src/main/java/org/osgi/framework/CapabilityPermission.java
+++ b/framework/src/main/java/org/osgi/framework/CapabilityPermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 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.
@@ -47,7 +47,7 @@
* </ul>
*
* @ThreadSafe
- * @author $Id: 8a38df04e56e9dcab7ea413ba69d4c4f05487c25 $
+ * @author $Id: c7bae50cab3a92ba810fcf983081c008f22a6bbd $
* @since 1.6
*/
@@ -714,7 +714,7 @@
/* work our way up the tree... */
int last;
int offset = requestedName.length() - 1;
- while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+ while ((last = requestedName.lastIndexOf('.', offset)) != -1) {
requestedName = requestedName.substring(0, last + 1) + "*";
cp = pc.get(requestedName);
if (cp != null) {
diff --git a/framework/src/main/java/org/osgi/framework/Constants.java b/framework/src/main/java/org/osgi/framework/Constants.java
index 68dfb53..19c68b8 100644
--- a/framework/src/main/java/org/osgi/framework/Constants.java
+++ b/framework/src/main/java/org/osgi/framework/Constants.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2018). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 2020). 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.
@@ -29,7 +29,7 @@
* otherwise indicated.
*
* @since 1.1
- * @author $Id: 41e648afb56767a610f279a9c7effef47dfcbf2e $
+ * @author $Id: 57af8721d5f7e08f9b8a1d9f109a5ebb94bf9646 $
*/
@ProviderType
public interface Constants {
@@ -1025,7 +1025,6 @@
/**
* Framework environment property identifying whether the Framework supports
* bootclasspath extension bundles.
- *
* <p>
* If the value of this property is {@code true}, then the Framework
* supports bootclasspath extension bundles. The default value is
@@ -1035,6 +1034,7 @@
* {@code BundleContext.getProperty} method.
*
* @since 1.3
+ * @deprecated As of 1.10.
*/
String SUPPORTS_BOOTCLASSPATH_EXTENSION = "org.osgi.supports.bootclasspath.extension";
@@ -1574,7 +1574,7 @@
String SERVICE_IMPORTED_CONFIGS = "service.imported.configs";
/**
- * Service property identifying the intents that this service implement.
+ * Service property identifying the intents that this service implements.
* This property has a dual purpose:
* <ul>
* <li>A bundle can use this service property to notify the distribution
@@ -1833,8 +1833,8 @@
* Service property identifying the monotonically increasing change count of
* a service.
* <p>
- * A service may optional provide this property to indicate there has been a
- * change in some data provided by the service. The change count must be
+ * A service may provide this property to indicate there has been a change
+ * in some data provided by the service. The change count must be
* incremented with a positive value every time the data provided by the
* service is changed. The service announces the modified change count by
* updating its service properties with the new value for this service
diff --git a/framework/src/main/java/org/osgi/framework/FilterImpl.java b/framework/src/main/java/org/osgi/framework/FilterImpl.java
new file mode 100755
index 0000000..5d88a58
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/FilterImpl.java
@@ -0,0 +1,1385 @@
+/*
+ * Copyright (c) OSGi Alliance (2005, 2020). 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;
+
+import static java.util.Objects.requireNonNull;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+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.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * RFC 1960-based Filter. Filter objects can be created by calling the
+ * constructor with the desired filter string. A Filter object can be called
+ * numerous times to determine if the match argument matches the filter string
+ * that was used to create the Filter object.
+ * <p>
+ * The syntax of a filter string is the string representation of LDAP search
+ * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
+ * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should be
+ * noted that RFC 2254: <i>A String Representation of LDAP Search Filters</i>
+ * (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes RFC 1960 but
+ * only adds extensible matching and is not applicable for this API.
+ * <p>
+ * The string representation of an LDAP search filter is defined by the
+ * following grammar. It uses a prefix format.
+ *
+ * <pre>
+ * <filter> ::= '(' <filtercomp> ')'
+ * <filtercomp> ::= <and> | <or> | <not> | <item>
+ * <and> ::= '&' <filterlist>
+ * <or> ::= '|' <filterlist>
+ * <not> ::= '!' <filter>
+ * <filterlist> ::= <filter> | <filter> <filterlist>
+ * <item> ::= <simple> | <present> | <substring>
+ * <simple> ::= <attr> <filtertype> <value>
+ * <filtertype> ::= <equal> | <approx> | <greater> | <less>
+ * <equal> ::= '='
+ * <approx> ::= '˜='
+ * <greater> ::= '>='
+ * <less> ::= '<='
+ * <present> ::= <attr> '=*'
+ * <substring> ::= <attr> '=' <initial> <any> <final>
+ * <initial> ::= NULL | <value>
+ * <any> ::= '*' <starval>
+ * <starval> ::= NULL | <value> '*' <starval>
+ * <final> ::= NULL | <value>
+ * </pre>
+ *
+ * {@code <attr>} is a string representing an attribute, or key, in the
+ * properties objects of the registered services. Attribute names are not case
+ * sensitive; that is cn and CN both refer to the same attribute.
+ * {@code <value>} is a string representing the value, or part of one, of
+ * a key in the properties objects of the registered services. If a
+ * {@code <value>} must contain one of the characters ' {@code *}' or
+ * '{@code (}' or '{@code )}', these characters should be escaped by preceding
+ * them with the backslash '{@code \}' character. Note that although both the
+ * {@code <substring>} and {@code <present>} productions can produce
+ * the {@code 'attr=*'} construct, this construct is used only to denote a
+ * presence filter.
+ * <p>
+ * Examples of LDAP filters are:
+ *
+ * <pre>
+ * "(cn=Babs Jensen)"
+ * "(!(cn=Tim Howes))"
+ * "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))"
+ * "(o=univ*of*mich*)"
+ * </pre>
+ * <p>
+ * The approximate match ({@code ~=}) is implementation specific but should at
+ * least ignore case and white space differences. Optional are codes like
+ * soundex or other smart "closeness" comparisons.
+ * <p>
+ * Comparison of values is not straightforward. Strings are compared differently
+ * than numbers and it is possible for a key to have multiple values. Note that
+ * that keys in the match argument must always be strings. The comparison is
+ * defined by the object type of the key's value. The following rules apply for
+ * comparison: <blockquote>
+ * <TABLE BORDER=0>
+ * <TR>
+ * <TD><b>Property Value Type </b></TD>
+ * <TD><b>Comparison Type</b></TD>
+ * </TR>
+ * <TR>
+ * <TD>String</TD>
+ * <TD>String comparison</TD>
+ * </TR>
+ * <TR valign=top>
+ * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
+ * <TD>numerical comparison</TD>
+ * </TR>
+ * <TR>
+ * <TD>Character</TD>
+ * <TD>character comparison</TD>
+ * </TR>
+ * <TR>
+ * <TD>Boolean</TD>
+ * <TD>equality comparisons only</TD>
+ * </TR>
+ * <TR>
+ * <TD>[] (array)</TD>
+ * <TD>recursively applied to values</TD>
+ * </TR>
+ * <TR>
+ * <TD>Collection</TD>
+ * <TD>recursively applied to values</TD>
+ * </TR>
+ * </TABLE>
+ * Note: arrays of primitives are also supported. </blockquote> A filter matches
+ * a key that has multiple values if it matches at least one of those values.
+ * For example,
+ *
+ * <pre>
+ * Dictionary d = new Hashtable();
+ * d.put("cn", new String[] {
+ * "a", "b", "c"
+ * });
+ * </pre>
+ *
+ * d will match {@code (cn=a)} and also {@code (cn=b)}
+ * <p>
+ * A filter component that references a key having an unrecognizable data type
+ * will evaluate to {@code false} .
+ */
+abstract class FilterImpl implements Filter {
+ /* normalized filter string for Filter object */
+ private transient String filterString;
+
+ /**
+ * Creates a {@link FilterImpl} object. This filter object may be used to
+ * match a {@link ServiceReference} or a Dictionary.
+ * <p>
+ * If the filter cannot be parsed, an {@link InvalidSyntaxException} will be
+ * thrown with a human readable message where the filter became unparsable.
+ *
+ * @param filterString the filter string.
+ * @throws InvalidSyntaxException If the filter parameter contains an
+ * invalid filter string that cannot be parsed.
+ */
+ static FilterImpl createFilter(String filterString)
+ throws InvalidSyntaxException {
+ return new Parser(filterString).parse();
+ }
+
+ FilterImpl() {
+ // empty constructor for subclasses
+ }
+
+ /**
+ * Filter using a service's properties.
+ * <p>
+ * This {@code Filter} is executed using the keys and values of the
+ * referenced service's properties. The keys are looked up in a case
+ * insensitive manner.
+ *
+ * @param reference The reference to the service whose properties are used
+ * in the match.
+ * @return {@code true} if the service's properties match this
+ * {@code Filter}; {@code false} otherwise.
+ */
+ @Override
+ public boolean match(ServiceReference< ? > reference) {
+ return matches0((reference != null) ? new ServiceReferenceMap(reference)
+ : Collections.emptyMap());
+ }
+
+ /**
+ * Filter using a {@code Dictionary} with case insensitive key lookup. This
+ * {@code Filter} is executed using the specified {@code Dictionary}'s keys
+ * and values. The keys are looked up in a case insensitive manner.
+ *
+ * @param dictionary The {@code Dictionary} whose key/value pairs are used
+ * in the match.
+ * @return {@code true} if the {@code Dictionary}'s values match this
+ * filter; {@code false} otherwise.
+ * @throws IllegalArgumentException If {@code dictionary} contains case
+ * variants of the same key name.
+ */
+ @Override
+ public boolean match(Dictionary<String, ? > dictionary) {
+ return matches0(
+ (dictionary != null) ? new CaseInsensitiveMap(dictionary)
+ : Collections.emptyMap());
+ }
+
+ /**
+ * Filter using a {@code Dictionary}. This {@code Filter} is executed using
+ * the specified {@code Dictionary}'s keys and values. The keys are looked
+ * up in a normal manner respecting case.
+ *
+ * @param dictionary The {@code Dictionary} whose key/value pairs are used
+ * in the match.
+ * @return {@code true} if the {@code Dictionary}'s values match this
+ * filter; {@code false} otherwise.
+ * @since 1.3
+ */
+ @Override
+ public boolean matchCase(Dictionary<String, ? > dictionary) {
+ return matches0((dictionary != null) ? DictionaryMap.asMap(dictionary)
+ : Collections.emptyMap());
+ }
+
+ /**
+ * Filter using a {@code Map}. This {@code Filter} is executed using the
+ * specified {@code Map}'s keys and values. The keys are looked up in a
+ * normal manner respecting case.
+ *
+ * @param map The {@code Map} whose key/value pairs are used in the match.
+ * Maps with {@code null} key or values are not supported. A
+ * {@code null} value is considered not present to the filter.
+ * @return {@code true} if the {@code Map}'s values match this filter;
+ * {@code false} otherwise.
+ * @since 1.6
+ */
+ @Override
+ public boolean matches(Map<String, ? > map) {
+ return matches0((map != null) ? map : Collections.emptyMap());
+ }
+
+ abstract boolean matches0(Map<String, ? > map);
+
+ /**
+ * Returns this {@code Filter}'s filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This {@code Filter}'s filter string.
+ */
+ @Override
+ public String toString() {
+ String result = filterString;
+ if (result == null) {
+ filterString = result = normalize(new StringBuilder()).toString();
+ }
+ return result;
+ }
+
+ /**
+ * Returns this {@code Filter}'s normalized filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This {@code Filter}'s filter string.
+ */
+ abstract StringBuilder normalize(StringBuilder sb);
+
+ /**
+ * Compares this {@code Filter} to another {@code Filter}.
+ * <p>
+ * This implementation returns the result of calling
+ * {@code this.toString().equals(obj.toString()}.
+ *
+ * @param obj The object to compare against this {@code Filter}.
+ * @return If the other object is a {@code Filter} object, then returns the
+ * result of calling {@code this.toString().equals(obj.toString()};
+ * {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof Filter)) {
+ return false;
+ }
+
+ return this.toString().equals(obj.toString());
+ }
+
+ /**
+ * Returns the hashCode for this {@code Filter}.
+ * <p>
+ * This implementation returns the result of calling
+ * {@code this.toString().hashCode()}.
+ *
+ * @return The hashCode of this {@code Filter}.
+ */
+ @Override
+ public int hashCode() {
+ return this.toString().hashCode();
+ }
+
+ static final class And extends FilterImpl {
+ private final FilterImpl[] operands;
+
+ And(FilterImpl[] operands) {
+ this.operands = operands;
+ }
+
+ @Override
+ boolean matches0(Map<String, ? > map) {
+ for (FilterImpl operand : operands) {
+ if (!operand.matches0(map)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append('&');
+ for (FilterImpl operand : operands) {
+ operand.normalize(sb);
+ }
+ return sb.append(')');
+ }
+ }
+
+ static final class Or extends FilterImpl {
+ private final FilterImpl[] operands;
+
+ Or(FilterImpl[] operands) {
+ this.operands = operands;
+ }
+
+ @Override
+ boolean matches0(Map<String, ? > map) {
+ for (FilterImpl operand : operands) {
+ if (operand.matches0(map)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append('|');
+ for (FilterImpl operand : operands) {
+ operand.normalize(sb);
+ }
+ return sb.append(')');
+ }
+ }
+
+ static final class Not extends FilterImpl {
+ private final FilterImpl operand;
+
+ Not(FilterImpl operand) {
+ this.operand = operand;
+ }
+
+ @Override
+ boolean matches0(Map<String, ? > map) {
+ return !operand.matches0(map);
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append('!');
+ operand.normalize(sb);
+ return sb.append(')');
+ }
+ }
+
+ static abstract class Item extends FilterImpl {
+ final String attr;
+
+ Item(String attr) {
+ this.attr = attr;
+ }
+
+ @Override
+ boolean matches0(Map<String, ? > map) {
+ return compare(map.get(attr));
+ }
+
+ private boolean compare(Object value1) {
+ if (value1 == null) {
+ return false;
+ }
+ if (value1 instanceof String) {
+ return compare_String((String) value1);
+ }
+ if (value1 instanceof Version) {
+ return compare_Version((Version) value1);
+ }
+
+ Class< ? > clazz = value1.getClass();
+ if (clazz.isArray()) {
+ Class< ? > type = clazz.getComponentType();
+ if (type.isPrimitive()) {
+ return compare_PrimitiveArray(type, value1);
+ }
+ return compare_ObjectArray((Object[]) value1);
+ }
+ if (value1 instanceof Collection< ? >) {
+ return compare_Collection((Collection< ? >) value1);
+ }
+ if (value1 instanceof Integer || value1 instanceof Long
+ || value1 instanceof Byte || value1 instanceof Short) {
+ return compare_Long(((Number) value1).longValue());
+ }
+ if (value1 instanceof Character) {
+ return compare_Character(((Character) value1).charValue());
+ }
+ if (value1 instanceof Float) {
+ return compare_Float(((Float) value1).floatValue());
+ }
+ if (value1 instanceof Double) {
+ return compare_Double(((Double) value1).doubleValue());
+ }
+ if (value1 instanceof Boolean) {
+ return compare_Boolean(((Boolean) value1).booleanValue());
+ }
+ if (value1 instanceof Comparable< ? >) {
+ @SuppressWarnings("unchecked")
+ Comparable<Object> comparable = (Comparable<Object>) value1;
+ return compare_Comparable(comparable);
+ }
+ return compare_Unknown(value1);
+ }
+
+ private boolean compare_Collection(Collection< ? > collection) {
+ for (Object value1 : collection) {
+ if (compare(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_ObjectArray(Object[] array) {
+ for (Object value1 : array) {
+ if (compare(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean compare_PrimitiveArray(Class< ? > type,
+ Object primarray) {
+ if (Integer.TYPE.isAssignableFrom(type)) {
+ int[] array = (int[]) primarray;
+ for (int value1 : array) {
+ if (compare_Long(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Long.TYPE.isAssignableFrom(type)) {
+ long[] array = (long[]) primarray;
+ for (long value1 : array) {
+ if (compare_Long(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Byte.TYPE.isAssignableFrom(type)) {
+ byte[] array = (byte[]) primarray;
+ for (byte value1 : array) {
+ if (compare_Long(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Short.TYPE.isAssignableFrom(type)) {
+ short[] array = (short[]) primarray;
+ for (short value1 : array) {
+ if (compare_Long(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Character.TYPE.isAssignableFrom(type)) {
+ char[] array = (char[]) primarray;
+ for (char value1 : array) {
+ if (compare_Character(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Float.TYPE.isAssignableFrom(type)) {
+ float[] array = (float[]) primarray;
+ for (float value1 : array) {
+ if (compare_Float(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Double.TYPE.isAssignableFrom(type)) {
+ double[] array = (double[]) primarray;
+ for (double value1 : array) {
+ if (compare_Double(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (Boolean.TYPE.isAssignableFrom(type)) {
+ boolean[] array = (boolean[]) primarray;
+ for (boolean value1 : array) {
+ if (compare_Boolean(value1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+ }
+
+ boolean compare_String(String string) {
+ return false;
+ }
+
+ boolean compare_Version(Version value1) {
+ return false;
+ }
+
+ boolean compare_Comparable(Comparable<Object> value1) {
+ return false;
+ }
+
+ boolean compare_Unknown(Object value1) {
+ return false;
+ }
+
+ boolean compare_Boolean(boolean boolval) {
+ return false;
+ }
+
+ boolean compare_Character(char charval) {
+ return false;
+ }
+
+ boolean compare_Double(double doubleval) {
+ return false;
+ }
+
+ boolean compare_Float(float floatval) {
+ return false;
+ }
+
+ boolean compare_Long(long longval) {
+ return false;
+ }
+
+ /**
+ * Encode the value string such that '(', '*', ')' and '\' are escaped.
+ *
+ * @param value unencoded value string.
+ */
+ static StringBuilder encodeValue(StringBuilder sb, String value) {
+ for (int i = 0, len = value.length(); i < len; i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '(' :
+ case '*' :
+ case ')' :
+ case '\\' :
+ sb.append('\\');
+ // FALL-THROUGH
+ default :
+ sb.append(c);
+ break;
+ }
+ }
+ return sb;
+ }
+ }
+
+ static final class Present extends Item {
+ Present(String attr) {
+ super(attr);
+ }
+
+ @Override
+ boolean matches0(Map<String, ? > map) {
+ return map.get(attr) != null;
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ return sb.append('(')
+ .append(attr)
+ .append('=')
+ .append('*')
+ .append(')');
+ }
+ }
+
+ static final class Substring extends Item {
+ final String[] substrings;
+
+ Substring(String attr, String[] substrings) {
+ super(attr);
+ this.substrings = substrings;
+ }
+
+ @Override
+ boolean compare_String(String string) {
+ int pos = 0;
+ for (int i = 0, size = substrings.length; i < size; i++) {
+ String substr = substrings[i];
+ if (i + 1 < size) /* if this is not that last substr */ {
+ if (substr == null) /* * */ {
+ String substr2 = substrings[i + 1];
+ if (substr2 == null) /* ** */
+ continue; /* ignore first star */
+ /* xxx */
+ int index = string.indexOf(substr2, pos);
+ if (index == -1) {
+ return false;
+ }
+ pos = index + substr2.length();
+ if (i + 2 < size) // if there are more
+ // substrings, increment
+ // over the string we just
+ // matched; otherwise need
+ // to do the last substr
+ // check
+ i++;
+ } else /* xxx */ {
+ int len = substr.length();
+ if (string.regionMatches(pos, substr, 0, len)) {
+ pos += len;
+ } else {
+ return false;
+ }
+ }
+ } else /* last substr */ {
+ if (substr == null) /* * */ {
+ return true;
+ }
+ /* xxx */
+ return string.endsWith(substr);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append(attr).append('=');
+ for (String substr : substrings) {
+ if (substr == null) /* * */ {
+ sb.append('*');
+ } else /* xxx */ {
+ encodeValue(sb, substr);
+ }
+ }
+ return sb.append(')');
+ }
+ }
+
+ static class Equal extends Item {
+ final String value;
+ private Object cached;
+
+ Equal(String attr, String value) {
+ super(attr);
+ this.value = value;
+ }
+
+ private <T> T convert(Class<T> type, Function<String, ? extends T> converter) {
+ @SuppressWarnings("unchecked")
+ T converted = (T) cached;
+ if ((converted != null) && type.isInstance(converted)) {
+ return converted;
+ }
+ cached = converted = converter.apply(value.trim());
+ return converted;
+ }
+
+ boolean comparison(int compare) {
+ return compare == 0;
+ }
+
+ @Override
+ boolean compare_String(String string) {
+ return comparison((string == value) ? 0 : string.compareTo(value));
+ }
+
+ @Override
+ boolean compare_Version(Version value1) {
+ try {
+ Version version2 = convert(Version.class, Version::valueOf);
+ return comparison(value1.compareTo(version2));
+ } catch (Exception e) {
+ // if the valueOf or compareTo method throws an exception
+ return false;
+ }
+ }
+
+ @Override
+ boolean compare_Boolean(boolean boolval) {
+ boolean boolval2 = convert(Boolean.class, Boolean::valueOf).booleanValue();
+ return comparison(Boolean.compare(boolval, boolval2));
+ }
+
+ @Override
+ boolean compare_Character(char charval) {
+ char charval2;
+ try {
+ charval2 = value.charAt(0);
+ } catch (IndexOutOfBoundsException e) {
+ return false;
+ }
+ return comparison(Character.compare(charval, charval2));
+ }
+
+ @Override
+ boolean compare_Double(double doubleval) {
+ double doubleval2;
+ try {
+ doubleval2 = convert(Double.class, Double::valueOf).doubleValue();
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ return comparison(Double.compare(doubleval, doubleval2));
+ }
+
+ @Override
+ boolean compare_Float(float floatval) {
+ float floatval2;
+ try {
+ floatval2 = convert(Float.class, Float::valueOf).floatValue();
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ return comparison(Float.compare(floatval, floatval2));
+ }
+
+ @Override
+ boolean compare_Long(long longval) {
+ long longval2;
+ try {
+ longval2 = convert(Long.class, Long::valueOf).longValue();
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ return comparison(Long.compare(longval, longval2));
+ }
+
+ @Override
+ boolean compare_Comparable(Comparable<Object> value1) {
+ Object value2 = valueOf(value1.getClass());
+ if (value2 == null) {
+ return false;
+ }
+ try {
+ return comparison(value1.compareTo(value2));
+ } catch (Exception e) {
+ // if the compareTo method throws an exception; return false
+ return false;
+ }
+ }
+
+ @Override
+ boolean compare_Unknown(Object value1) {
+ Object value2 = valueOf(value1.getClass());
+ if (value2 == null) {
+ return false;
+ }
+ try {
+ return value1.equals(value2);
+ } catch (Exception e) {
+ // if the equals method throws an exception; return false
+ return false;
+ }
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append(attr).append('=');
+ return encodeValue(sb, value).append(')');
+ }
+
+ Object valueOf(Class< ? > target) {
+ do {
+ Method method;
+ try {
+ method = target.getMethod("valueOf", String.class);
+ } catch (NoSuchMethodException e) {
+ break;
+ }
+ if (Modifier.isStatic(method.getModifiers())
+ && target.isAssignableFrom(method.getReturnType())) {
+ setAccessible(method);
+ try {
+ return method.invoke(null, value.trim());
+ } catch (Error e) {
+ throw e;
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+ } while (false);
+
+ do {
+ Constructor< ? > constructor;
+ try {
+ constructor = target.getConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ break;
+ }
+ setAccessible(constructor);
+ try {
+ return constructor.newInstance(value.trim());
+ } catch (Error e) {
+ throw e;
+ } catch (Throwable e) {
+ return null;
+ }
+ } while (false);
+
+ return null;
+ }
+
+ private static void setAccessible(AccessibleObject accessible) {
+ if (!accessible.isAccessible()) {
+ AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+ accessible.setAccessible(true);
+ return null;
+ });
+ }
+ }
+ }
+
+ static final class LessEqual extends Equal {
+ LessEqual(String attr, String value) {
+ super(attr, value);
+ }
+
+ @Override
+ boolean comparison(int compare) {
+ return compare <= 0;
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append(attr).append('<').append('=');
+ return encodeValue(sb, value).append(')');
+ }
+ }
+
+ static final class GreaterEqual extends Equal {
+ GreaterEqual(String attr, String value) {
+ super(attr, value);
+ }
+
+ @Override
+ boolean comparison(int compare) {
+ return compare >= 0;
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append(attr).append('>').append('=');
+ return encodeValue(sb, value).append(')');
+ }
+ }
+
+ static final class Approx extends Equal {
+ final String approx;
+
+ Approx(String attr, String value) {
+ super(attr, value);
+ this.approx = approxString(value);
+ }
+
+ @Override
+ boolean compare_String(String string) {
+ string = approxString(string);
+ return string.equalsIgnoreCase(approx);
+ }
+
+ @Override
+ boolean compare_Character(char charval) {
+ char charval2;
+ try {
+ charval2 = approx.charAt(0);
+ } catch (IndexOutOfBoundsException e) {
+ return false;
+ }
+ return (charval == charval2)
+ || (Character.toUpperCase(charval) == Character
+ .toUpperCase(charval2))
+ || (Character.toLowerCase(charval) == Character
+ .toLowerCase(charval2));
+ }
+
+ @Override
+ StringBuilder normalize(StringBuilder sb) {
+ sb.append('(').append(attr).append('~').append('=');
+ return encodeValue(sb, approx).append(')');
+ }
+
+ /**
+ * Map a string for an APPROX (~=) comparison. This implementation
+ * removes white spaces. This is the minimum implementation allowed by
+ * the OSGi spec.
+ *
+ * @param input Input string.
+ * @return String ready for APPROX comparison.
+ */
+ static String approxString(String input) {
+ boolean changed = false;
+ char[] output = input.toCharArray();
+ int cursor = 0;
+ for (char c : output) {
+ if (Character.isWhitespace(c)) {
+ changed = true;
+ continue;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return changed ? new String(output, 0, cursor) : input;
+ }
+ }
+
+ /**
+ * Parser class for OSGi filter strings. This class parses the complete
+ * filter string and builds a tree of FilterImpl objects rooted at the
+ * parent.
+ */
+ static private final class Parser {
+ private final String filterstring;
+ private final char[] filterChars;
+ private int pos;
+
+ Parser(String filterstring) {
+ this.filterstring = filterstring;
+ filterChars = filterstring.toCharArray();
+ pos = 0;
+ }
+
+ FilterImpl parse() throws InvalidSyntaxException {
+ FilterImpl filter;
+ try {
+ filter = parse_filter();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new InvalidSyntaxException("Filter ended abruptly",
+ filterstring, e);
+ }
+
+ if (pos != filterChars.length) {
+ throw new InvalidSyntaxException(
+ "Extraneous trailing characters: "
+ + filterstring.substring(pos),
+ filterstring);
+ }
+ return filter;
+ }
+
+ private FilterImpl parse_filter() throws InvalidSyntaxException {
+ FilterImpl filter;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException(
+ "Missing '(': " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ pos++;
+
+ filter = parse_filtercomp();
+
+ skipWhiteSpace();
+
+ if (filterChars[pos] != ')') {
+ throw new InvalidSyntaxException(
+ "Missing ')': " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ pos++;
+
+ skipWhiteSpace();
+
+ return filter;
+ }
+
+ private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ char c = filterChars[pos];
+
+ switch (c) {
+ case '&' : {
+ pos++;
+ return parse_and();
+ }
+ case '|' : {
+ pos++;
+ return parse_or();
+ }
+ case '!' : {
+ pos++;
+ return parse_not();
+ }
+ }
+ return parse_item();
+ }
+
+ private FilterImpl parse_and() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ List<FilterImpl> operands = new ArrayList<>(10);
+
+ while (filterChars[pos] == '(') {
+ FilterImpl child = parse_filter();
+ operands.add(child);
+ }
+
+ return new FilterImpl.And(operands.toArray(new FilterImpl[0]));
+ }
+
+ private FilterImpl parse_or() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ List<FilterImpl> operands = new ArrayList<>(10);
+
+ while (filterChars[pos] == '(') {
+ FilterImpl child = parse_filter();
+ operands.add(child);
+ }
+
+ return new FilterImpl.Or(operands.toArray(new FilterImpl[0]));
+ }
+
+ private FilterImpl parse_not() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ FilterImpl child = parse_filter();
+
+ return new FilterImpl.Not(child);
+ }
+
+ private FilterImpl parse_item() throws InvalidSyntaxException {
+ String attr = parse_attr();
+
+ skipWhiteSpace();
+
+ switch (filterChars[pos]) {
+ case '~' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl.Approx(attr, parse_value());
+ }
+ break;
+ }
+ case '>' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl.GreaterEqual(attr, parse_value());
+ }
+ break;
+ }
+ case '<' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl.LessEqual(attr, parse_value());
+ }
+ break;
+ }
+ case '=' : {
+ if (filterChars[pos + 1] == '*') {
+ int oldpos = pos;
+ pos += 2;
+ skipWhiteSpace();
+ if (filterChars[pos] == ')') {
+ return new FilterImpl.Present(attr);
+ }
+ pos = oldpos;
+ }
+
+ pos++;
+ String[] substrings = parse_substring();
+
+ int length = substrings.length;
+ if (length == 0) {
+ return new FilterImpl.Equal(attr, "");
+ }
+ if (length == 1) {
+ String single = substrings[0];
+ if (single != null) {
+ return new FilterImpl.Equal(attr, single);
+ }
+ }
+ return new FilterImpl.Substring(attr, substrings);
+ }
+ }
+
+ throw new InvalidSyntaxException(
+ "Invalid operator: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ private String parse_attr() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ int begin = pos;
+ int end = pos;
+
+ char c = filterChars[pos];
+
+ while (c != '~' && c != '<' && c != '>' && c != '=' && c != '('
+ && c != ')') {
+ pos++;
+
+ if (!Character.isWhitespace(c)) {
+ end = pos;
+ }
+
+ c = filterChars[pos];
+ }
+
+ int length = end - begin;
+
+ if (length == 0) {
+ throw new InvalidSyntaxException(
+ "Missing attr: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ return new String(filterChars, begin, length);
+ }
+
+ private String parse_value() throws InvalidSyntaxException {
+ StringBuilder sb = new StringBuilder(filterChars.length - pos);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')' : {
+ break parseloop;
+ }
+
+ case '(' : {
+ throw new InvalidSyntaxException(
+ "Invalid value: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ case '\\' : {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ if (sb.length() == 0) {
+ throw new InvalidSyntaxException(
+ "Missing value: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ return sb.toString();
+ }
+
+ private String[] parse_substring() throws InvalidSyntaxException {
+ StringBuilder sb = new StringBuilder(filterChars.length - pos);
+
+ List<String> operands = new ArrayList<>(10);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')' : {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ break parseloop;
+ }
+
+ case '(' : {
+ throw new InvalidSyntaxException(
+ "Invalid value: " + filterstring.substring(pos),
+ filterstring);
+ }
+
+ case '*' : {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ sb.setLength(0);
+
+ operands.add(null);
+ pos++;
+
+ break;
+ }
+
+ case '\\' : {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ return operands.toArray(new String[0]);
+ }
+
+ private void skipWhiteSpace() {
+ for (int length = filterChars.length; (pos < length)
+ && Character.isWhitespace(filterChars[pos]);) {
+ pos++;
+ }
+ }
+ }
+
+ /**
+ * This Map is used for key lookup during filter evaluation. This Map
+ * implementation only supports the get operation using a String key as no
+ * other operations are used by the Filter implementation.
+ */
+ private static class DictionaryMap extends AbstractMap<String,Object>
+ implements Map<String,Object> {
+ static Map<String, ? > asMap(Dictionary<String, ? > dictionary) {
+ if (dictionary instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map<String, ? > coerced = (Map<String, ? >) dictionary;
+ return coerced;
+ }
+ return new DictionaryMap(dictionary);
+ }
+
+ private final Dictionary<String, ? > dictionary;
+
+ DictionaryMap(Dictionary<String, ? > dictionary) {
+ this.dictionary = requireNonNull(dictionary);
+ }
+
+ @Override
+ public Object get(Object key) {
+ return dictionary.get(key);
+ }
+
+ @Override
+ public Set<Entry<String,Object>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * This Map is used for case-insensitive key lookup during filter
+ * evaluation. This Map implementation only supports the get operation using
+ * a String key as no other operations are used by the Filter
+ * implementation.
+ */
+ private static final class CaseInsensitiveMap
+ extends DictionaryMap implements Map<String,Object> {
+ private final String[] keys;
+
+ /**
+ * Create a case insensitive map from the specified dictionary.
+ *
+ * @param dictionary
+ * @throws IllegalArgumentException If {@code dictionary} contains case
+ * variants of the same key name.
+ */
+ CaseInsensitiveMap(Dictionary<String, ? > dictionary) {
+ super(dictionary);
+ List<String> keyList = new ArrayList<>(dictionary.size());
+ for (Enumeration< ? > e = dictionary.keys(); e.hasMoreElements();) {
+ Object k = e.nextElement();
+ if (k instanceof String) {
+ String key = (String) k;
+ for (String i : keyList) {
+ if (key.equalsIgnoreCase(i)) {
+ throw new IllegalArgumentException();
+ }
+ }
+ keyList.add(key);
+ }
+ }
+ this.keys = keyList.toArray(new String[0]);
+ }
+
+ @Override
+ public Object get(Object o) {
+ String k = (String) o;
+ for (String key : keys) {
+ if (key.equalsIgnoreCase(k)) {
+ return super.get(key);
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * This Map is used for key lookup from a ServiceReference during filter
+ * evaluation. This Map implementation only supports the get operation using
+ * a String key as no other operations are used by the Filter
+ * implementation.
+ */
+ private static final class ServiceReferenceMap
+ extends AbstractMap<String,Object> implements Map<String,Object> {
+ private final ServiceReference< ? > reference;
+
+ ServiceReferenceMap(ServiceReference< ? > reference) {
+ this.reference = requireNonNull(reference);
+ }
+
+ @Override
+ public Object get(Object key) {
+ return reference.getProperty((String) key);
+ }
+
+ @Override
+ public Set<Entry<String,Object>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/framework/src/main/java/org/osgi/framework/FrameworkEvent.java b/framework/src/main/java/org/osgi/framework/FrameworkEvent.java
index 84c63f1..9156a79 100644
--- a/framework/src/main/java/org/osgi/framework/FrameworkEvent.java
+++ b/framework/src/main/java/org/osgi/framework/FrameworkEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2004, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2004, 2020). 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.
@@ -34,7 +34,7 @@
*
* @Immutable
* @see FrameworkListener
- * @author $Id: b3072b2d058e70389a52e342ed5f8647b930b8f1 $
+ * @author $Id: bcff0614c20b454723977355f99486ca01ee89ea $
*/
public class FrameworkEvent extends EventObject {
@@ -148,13 +148,13 @@
/**
* The Framework has stopped and the boot class path has changed.
- *
* <p>
* This event is fired when the Framework has been stopped because of a stop
* operation on the system bundle and a bootclasspath extension bundle has
* been installed or updated. The source of this event is the System Bundle.
*
* @since 1.5
+ * @deprecated As of 1.10.
*/
public final static int STOPPED_BOOTCLASSPATH_MODIFIED = 0x00000100;
@@ -173,9 +173,9 @@
* The Framework has stopped and the framework requires a new class loader
* to restart.
* <p>
- * This event is fired when the Framework has been stopped because of a stop
- * operation on the system bundle and the framework requires a new class
- * loader to be used to restart. For example, if a framework extension
+ * This event is fired when the Framework has been stopped because of a
+ * refresh operation on the system bundle and the framework requires a new
+ * class loader to be used to restart. For example, if a framework extension
* bundle has been refreshed. The source of this event is the System Bundle.
*
* @since 1.9
@@ -244,7 +244,6 @@
* <li>{@link #PACKAGES_REFRESHED}</li>
* <li>{@link #STARTLEVEL_CHANGED}</li>
* <li>{@link #STOPPED}</li>
- * <li>{@link #STOPPED_BOOTCLASSPATH_MODIFIED}</li>
* <li>{@link #STOPPED_UPDATE}</li>
* <li>{@link #WAIT_TIMEDOUT}</li>
* </ul>
diff --git a/framework/src/main/java/org/osgi/framework/FrameworkUtil.java b/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
index 9f72092..edc8008 100644
--- a/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
+++ b/framework/src/main/java/org/osgi/framework/FrameworkUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2005, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2005, 2020). 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.
@@ -16,25 +16,28 @@
package org.osgi.framework;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
+import static java.util.Objects.requireNonNull;
+
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.AbstractMap;
+import java.util.AbstractSet;
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.Objects;
+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.
*
@@ -44,7 +47,7 @@
*
* @since 1.3
* @ThreadSafe
- * @author $Id: 90d50e4d3f69b659bed23beedab6e54b31b96d76 $
+ * @author $Id: 71423feb17277b685e6d5d7864907e768da8cd0b $
*/
public class FrameworkUtil {
/**
@@ -77,7 +80,7 @@
* @see Filter
*/
public static Filter createFilter(String filter) throws InvalidSyntaxException {
- return FilterImpl.newInstance(filter);
+ return FilterImpl.createFilter(filter);
}
/**
@@ -194,1523 +197,68 @@
}
/**
- * 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.
+ * Return a {@code Bundle} for the specified bundle class loader.
*
- * @param classFromBundle A class defined by a 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) {
+ requireNonNull(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()));
- if (cl instanceof BundleReference) {
- return ((BundleReference) cl).getBundle();
- }
- return null;
+ return cl.flatMap(FrameworkUtil::getBundle)
+ .orElseGet(() -> helpers.stream()
+ .map(helper -> helper.getBundle(classFromBundle))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findFirst()
+ .orElse(null));
}
- /**
- * RFC 1960-based Filter. Filter objects can be created by calling the
- * constructor with the desired filter string. A Filter object can be called
- * numerous times to determine if the match argument matches the filter
- * string that was used to create the Filter object.
- *
- * <p>
- * The syntax of a filter string is the string representation of LDAP search
- * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
- * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should
- * be noted that RFC 2254: <i>A String Representation of LDAP Search
- * Filters</i> (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
- * RFC 1960 but only adds extensible matching and is not applicable for this
- * API.
- *
- * <p>
- * The string representation of an LDAP search filter is defined by the
- * following grammar. It uses a prefix format.
- *
- * <pre>
- * <filter> ::= '(' <filtercomp> ')'
- * <filtercomp> ::= <and> | <or> | <not> | <item>
- * <and> ::= '&' <filterlist>
- * <or> ::= '|' <filterlist>
- * <not> ::= '!' <filter>
- * <filterlist> ::= <filter> | <filter> <filterlist>
- * <item> ::= <simple> | <present> | <substring>
- * <simple> ::= <attr> <filtertype> <value>
- * <filtertype> ::= <equal> | <approx> | <greater> | <less>
- * <equal> ::= '='
- * <approx> ::= '˜='
- * <greater> ::= '>='
- * <less> ::= '<='
- * <present> ::= <attr> '=*'
- * <substring> ::= <attr> '=' <initial> <any> <final>
- * <initial> ::= NULL | <value>
- * <any> ::= '*' <starval>
- * <starval> ::= NULL | <value> '*' <starval>
- * <final> ::= NULL | <value>
- * </pre>
- *
- * {@code <attr>} is a string representing an attribute, or key, in
- * the properties objects of the registered services. Attribute names are
- * not case sensitive; that is cn and CN both refer to the same attribute.
- * {@code <value>} is a string representing the value, or part of one,
- * of a key in the properties objects of the registered services. If a
- * {@code <value>} must contain one of the characters ' {@code *}' or
- * '{@code (}' or '{@code )}', these characters should be escaped by
- * preceding them with the backslash '{@code \}' character. Note that
- * although both the {@code <substring>} and {@code <present>}
- * productions can produce the {@code 'attr=*'} construct, this construct is
- * used only to denote a presence filter.
- *
- * <p>
- * Examples of LDAP filters are:
- *
- * <pre>
- * "(cn=Babs Jensen)"
- * "(!(cn=Tim Howes))"
- * "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))"
- * "(o=univ*of*mich*)"
- * </pre>
- *
- * <p>
- * The approximate match ({@code ~=}) is implementation specific but should
- * at least ignore case and white space differences. Optional are codes like
- * soundex or other smart "closeness" comparisons.
- *
- * <p>
- * Comparison of values is not straightforward. Strings are compared
- * differently than numbers and it is possible for a key to have multiple
- * values. Note that that keys in the match argument must always be strings.
- * The comparison is defined by the object type of the key's value. The
- * following rules apply for comparison:
- *
- * <blockquote>
- * <TABLE BORDER=0>
- * <TR>
- * <TD><b>Property Value Type </b></TD>
- * <TD><b>Comparison Type</b></TD>
- * </TR>
- * <TR>
- * <TD>String</TD>
- * <TD>String comparison</TD>
- * </TR>
- * <TR valign=top>
- * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
- * <TD>numerical comparison</TD>
- * </TR>
- * <TR>
- * <TD>Character</TD>
- * <TD>character comparison</TD>
- * </TR>
- * <TR>
- * <TD>Boolean</TD>
- * <TD>equality comparisons only</TD>
- * </TR>
- * <TR>
- * <TD>[] (array)</TD>
- * <TD>recursively applied to values</TD>
- * </TR>
- * <TR>
- * <TD>Collection</TD>
- * <TD>recursively applied to values</TD>
- * </TR>
- * </TABLE>
- * Note: arrays of primitives are also supported. </blockquote>
- *
- * A filter matches a key that has multiple values if it matches at least
- * one of those values. For example,
- *
- * <pre>
- * Dictionary d = new Hashtable();
- * d.put("cn", new String[] {"a", "b", "c"});
- * </pre>
- *
- * d will match {@code (cn=a)} and also {@code (cn=b)}
- *
- * <p>
- * A filter component that references a key having an unrecognizable data
- * type will evaluate to {@code false} .
- */
- static private final class FilterImpl implements Filter {
- /* filter operators */
- private static final int EQUAL = 1;
- private static final int APPROX = 2;
- private static final int GREATER = 3;
- private static final int LESS = 4;
- private static final int PRESENT = 5;
- private static final int SUBSTRING = 6;
- private static final int AND = 7;
- private static final int OR = 8;
- private static final int NOT = 9;
-
- /** filter operation */
- private final int op;
- /** filter attribute or null if operation AND, OR or NOT */
- private final String attr;
- /** filter operands */
- private final Object value;
-
- /* normalized filter string for Filter object */
- private transient String filterString;
-
- /**
- * Constructs a {@link FilterImpl} object. This filter object may be
- * used to match a {@link ServiceReference} or a Dictionary.
- *
- * <p>
- * If the filter cannot be parsed, an {@link InvalidSyntaxException}
- * will be thrown with a human readable message where the filter became
- * unparsable.
- *
- * @param filterString the filter string.
- * @throws InvalidSyntaxException If the filter parameter contains an
- * invalid filter string that cannot be parsed.
- */
- static FilterImpl newInstance(String filterString) throws InvalidSyntaxException {
- return new Parser(filterString).parse();
- }
-
- FilterImpl(int operation, String attr, Object value) {
- this.op = operation;
- this.attr = attr;
- this.value = value;
- filterString = null;
- }
-
- /**
- * Filter using a service's properties.
- * <p>
- * This {@code Filter} is executed using the keys and values of the
- * referenced service's properties. The keys are looked up in a case
- * insensitive manner.
- *
- * @param reference The reference to the service whose properties are
- * used in the match.
- * @return {@code true} if the service's properties match this
- * {@code Filter}; {@code false} otherwise.
- */
- @Override
- public boolean match(ServiceReference<?> reference) {
- return matches(new ServiceReferenceMap(reference));
- }
-
- /**
- * Filter using a {@code Dictionary} with case insensitive key lookup.
- * This {@code Filter} is executed using the specified
- * {@code Dictionary}'s keys and values. The keys are looked up in a
- * case insensitive manner.
- *
- * @param dictionary The {@code Dictionary} whose key/value pairs are
- * used in the match.
- * @return {@code true} if the {@code Dictionary}'s values match this
- * filter; {@code false} otherwise.
- * @throws IllegalArgumentException If {@code dictionary} contains case
- * variants of the same key name.
- */
- @Override
- public boolean match(Dictionary<String, ?> dictionary) {
- return matches(new CaseInsensitiveMap(dictionary));
- }
-
- /**
- * Filter using a {@code Dictionary}. This {@code Filter} is executed
- * using the specified {@code Dictionary}'s keys and values. The keys
- * are looked up in a normal manner respecting case.
- *
- * @param dictionary The {@code Dictionary} whose key/value pairs are
- * used in the match.
- * @return {@code true} if the {@code Dictionary}'s values match this
- * filter; {@code false} otherwise.
- * @since 1.3
- */
- @Override
- public boolean matchCase(Dictionary<String, ?> dictionary) {
- switch (op) {
- case AND : {
- FilterImpl[] filters = (FilterImpl[]) value;
- for (FilterImpl f : filters) {
- if (!f.matchCase(dictionary)) {
- return false;
- }
- }
- return true;
- }
-
- case OR : {
- FilterImpl[] filters = (FilterImpl[]) value;
- for (FilterImpl f : filters) {
- if (f.matchCase(dictionary)) {
- return true;
- }
- }
- return false;
- }
-
- case NOT : {
- FilterImpl filter = (FilterImpl) value;
- return !filter.matchCase(dictionary);
- }
-
- case SUBSTRING :
- case EQUAL :
- case GREATER :
- case LESS :
- case APPROX : {
- Object prop = (dictionary == null) ? null : dictionary.get(attr);
- return compare(op, prop, value);
- }
-
- case PRESENT : {
- Object prop = (dictionary == null) ? null : dictionary.get(attr);
- return prop != null;
- }
- }
-
- return false;
- }
-
- /**
- * Filter using a {@code Map}. This {@code Filter} is executed using the
- * specified {@code Map}'s keys and values. The keys are looked up in a
- * normal manner respecting case.
- *
- * @param map The {@code Map} whose key/value pairs are used in the
- * match. Maps with {@code null} key or values are not supported.
- * A {@code null} value is considered not present to the filter.
- * @return {@code true} if the {@code Map}'s values match this filter;
- * {@code false} otherwise.
- * @since 1.6
- */
- @Override
- public boolean matches(Map<String, ?> map) {
- switch (op) {
- case AND : {
- FilterImpl[] filters = (FilterImpl[]) value;
- for (FilterImpl f : filters) {
- if (!f.matches(map)) {
- return false;
- }
- }
- return true;
- }
-
- case OR : {
- FilterImpl[] filters = (FilterImpl[]) value;
- for (FilterImpl f : filters) {
- if (f.matches(map)) {
- return true;
- }
- }
- return false;
- }
-
- case NOT : {
- FilterImpl filter = (FilterImpl) value;
- return !filter.matches(map);
- }
-
- case SUBSTRING :
- case EQUAL :
- case GREATER :
- case LESS :
- case APPROX : {
- Object prop = (map == null) ? null : map.get(attr);
- return compare(op, prop, value);
- }
-
- case PRESENT : {
- Object prop = (map == null) ? null : map.get(attr);
- return prop != null;
- }
- }
-
- return false;
- }
-
- /**
- * Returns this {@code Filter}'s filter string.
- * <p>
- * The filter string is normalized by removing whitespace which does not
- * affect the meaning of the filter.
- *
- * @return This {@code Filter}'s filter string.
- */
- @Override
- public String toString() {
- String result = filterString;
- if (result == null) {
- filterString = result = normalize().toString();
- }
- return result;
- }
-
- /**
- * Returns this {@code Filter}'s normalized filter string.
- * <p>
- * The filter string is normalized by removing whitespace which does not
- * affect the meaning of the filter.
- *
- * @return This {@code Filter}'s filter string.
- */
- private StringBuilder normalize() {
- StringBuilder sb = new StringBuilder();
- sb.append('(');
-
- switch (op) {
- case AND : {
- sb.append('&');
-
- FilterImpl[] filters = (FilterImpl[]) value;
- for (FilterImpl f : filters) {
- sb.append(f.normalize());
- }
-
- break;
- }
-
- case OR : {
- sb.append('|');
-
- FilterImpl[] filters = (FilterImpl[]) value;
- for (FilterImpl f : filters) {
- sb.append(f.normalize());
- }
-
- break;
- }
-
- case NOT : {
- sb.append('!');
- FilterImpl filter = (FilterImpl) value;
- sb.append(filter.normalize());
-
- break;
- }
-
- case SUBSTRING : {
- sb.append(attr);
- sb.append('=');
-
- String[] substrings = (String[]) value;
-
- for (String substr : substrings) {
- if (substr == null) /* * */{
- sb.append('*');
- } else /* xxx */{
- sb.append(encodeValue(substr));
- }
- }
-
- break;
- }
- case EQUAL : {
- sb.append(attr);
- sb.append('=');
- sb.append(encodeValue((String) value));
-
- break;
- }
- case GREATER : {
- sb.append(attr);
- sb.append(">=");
- sb.append(encodeValue((String) value));
-
- break;
- }
- case LESS : {
- sb.append(attr);
- sb.append("<=");
- sb.append(encodeValue((String) value));
-
- break;
- }
- case APPROX : {
- sb.append(attr);
- sb.append("~=");
- sb.append(encodeValue(approxString((String) value)));
-
- break;
- }
-
- case PRESENT : {
- sb.append(attr);
- sb.append("=*");
-
- break;
- }
- }
-
- sb.append(')');
-
- return sb;
- }
-
- /**
- * Compares this {@code Filter} to another {@code Filter}.
- *
- * <p>
- * This implementation returns the result of calling
- * {@code this.toString().equals(obj.toString()}.
- *
- * @param obj The object to compare against this {@code Filter}.
- * @return If the other object is a {@code Filter} object, then returns
- * the result of calling
- * {@code this.toString().equals(obj.toString()}; {@code false}
- * otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
-
- if (!(obj instanceof Filter)) {
- return false;
- }
-
- return this.toString().equals(obj.toString());
- }
-
- /**
- * Returns the hashCode for this {@code Filter}.
- *
- * <p>
- * This implementation returns the result of calling
- * {@code this.toString().hashCode()}.
- *
- * @return The hashCode of this {@code Filter}.
- */
- @Override
- public int hashCode() {
- return this.toString().hashCode();
- }
-
- /**
- * Encode the value string such that '(', '*', ')' and '\' are escaped.
- *
- * @param value unencoded value string.
- * @return encoded value string.
- */
- private static String encodeValue(String value) {
- boolean encoded = false;
- int inlen = value.length();
- int outlen = inlen << 1; /* inlen 2 */
-
- char[] output = new char[outlen];
- value.getChars(0, inlen, output, inlen);
-
- int cursor = 0;
- for (int i = inlen; i < outlen; i++) {
- char c = output[i];
-
- switch (c) {
- case '(' :
- case '*' :
- case ')' :
- case '\\' : {
- output[cursor] = '\\';
- cursor++;
- encoded = true;
-
- break;
- }
- }
-
- output[cursor] = c;
- cursor++;
- }
-
- return encoded ? new String(output, 0, cursor) : value;
- }
-
- private boolean compare(int operation, Object value1, Object value2) {
- if (value1 == null) {
- return false;
- }
- if (value1 instanceof String) {
- return compare_String(operation, (String) value1, value2);
- }
- if (value1 instanceof Version) {
- return compare_Version(operation, (Version) value1, value2);
- }
-
- Class<?> clazz = value1.getClass();
- if (clazz.isArray()) {
- Class<?> type = clazz.getComponentType();
- if (type.isPrimitive()) {
- return compare_PrimitiveArray(operation, type, value1, value2);
- }
- return compare_ObjectArray(operation, (Object[]) value1, value2);
- }
- if (value1 instanceof Collection<?>) {
- return compare_Collection(operation, (Collection<?>) value1, value2);
- }
- if (value1 instanceof Integer) {
- return compare_Integer(operation, ((Integer) value1).intValue(), value2);
- }
- if (value1 instanceof Long) {
- return compare_Long(operation, ((Long) value1).longValue(), value2);
- }
- if (value1 instanceof Byte) {
- return compare_Byte(operation, ((Byte) value1).byteValue(), value2);
- }
- if (value1 instanceof Short) {
- return compare_Short(operation, ((Short) value1).shortValue(), value2);
- }
- if (value1 instanceof Character) {
- return compare_Character(operation, ((Character) value1).charValue(), value2);
- }
- if (value1 instanceof Float) {
- return compare_Float(operation, ((Float) value1).floatValue(), value2);
- }
- if (value1 instanceof Double) {
- return compare_Double(operation, ((Double) value1).doubleValue(), value2);
- }
- if (value1 instanceof Boolean) {
- return compare_Boolean(operation, ((Boolean) value1).booleanValue(), value2);
- }
- if (value1 instanceof Comparable<?>) {
- @SuppressWarnings("unchecked")
- Comparable<Object> comparable = (Comparable<Object>) value1;
- return compare_Comparable(operation, comparable, value2);
- }
- return compare_Unknown(operation, value1, value2);
- }
-
- private boolean compare_Collection(int operation, Collection<?> collection, Object value2) {
- for (Object value1 : collection) {
- if (compare(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean compare_ObjectArray(int operation, Object[] array, Object value2) {
- for (Object value1 : array) {
- if (compare(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean compare_PrimitiveArray(int operation, Class<?> type, Object primarray, Object value2) {
- if (Integer.TYPE.isAssignableFrom(type)) {
- int[] array = (int[]) primarray;
- for (int value1 : array) {
- if (compare_Integer(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Long.TYPE.isAssignableFrom(type)) {
- long[] array = (long[]) primarray;
- for (long value1 : array) {
- if (compare_Long(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Byte.TYPE.isAssignableFrom(type)) {
- byte[] array = (byte[]) primarray;
- for (byte value1 : array) {
- if (compare_Byte(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Short.TYPE.isAssignableFrom(type)) {
- short[] array = (short[]) primarray;
- for (short value1 : array) {
- if (compare_Short(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Character.TYPE.isAssignableFrom(type)) {
- char[] array = (char[]) primarray;
- for (char value1 : array) {
- if (compare_Character(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Float.TYPE.isAssignableFrom(type)) {
- float[] array = (float[]) primarray;
- for (float value1 : array) {
- if (compare_Float(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Double.TYPE.isAssignableFrom(type)) {
- double[] array = (double[]) primarray;
- for (double value1 : array) {
- if (compare_Double(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- if (Boolean.TYPE.isAssignableFrom(type)) {
- boolean[] array = (boolean[]) primarray;
- for (boolean value1 : array) {
- if (compare_Boolean(operation, value1, value2)) {
- return true;
- }
- }
- return false;
- }
- return false;
- }
-
- private boolean compare_String(int operation, String string, Object value2) {
- switch (operation) {
- case SUBSTRING : {
- String[] substrings = (String[]) value2;
- int pos = 0;
- for (int i = 0, size = substrings.length; i < size; i++) {
- String substr = substrings[i];
-
- if (i + 1 < size) /* if this is not that last substr */{
- if (substr == null) /* * */{
- String substr2 = substrings[i + 1];
-
- if (substr2 == null) /* ** */
- continue; /* ignore first star */
- /* xxx */
- int index = string.indexOf(substr2, pos);
- if (index == -1) {
- return false;
- }
-
- pos = index + substr2.length();
- if (i + 2 < size) // if there are more
- // substrings, increment
- // over the string we just
- // matched; otherwise need
- // to do the last substr
- // check
- i++;
- } else /* xxx */{
- int len = substr.length();
- if (string.regionMatches(pos, substr, 0, len)) {
- pos += len;
- } else {
- return false;
- }
- }
- } else /* last substr */{
- if (substr == null) /* * */{
- return true;
- }
- /* xxx */
- return string.endsWith(substr);
- }
- }
-
- return true;
- }
- case EQUAL : {
- return string.equals(value2);
- }
- case APPROX : {
- string = approxString(string);
- String string2 = approxString((String) value2);
-
- return string.equalsIgnoreCase(string2);
- }
- case GREATER : {
- return string.compareTo((String) value2) >= 0;
- }
- case LESS : {
- return string.compareTo((String) value2) <= 0;
- }
- }
- return false;
- }
-
- private boolean compare_Integer(int operation, int intval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- int intval2;
+ 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 {
- intval2 = Integer.parseInt(((String) value2).trim());
- } catch (IllegalArgumentException e) {
- return false;
- }
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return intval == intval2;
- }
- case GREATER : {
- return intval >= intval2;
- }
- case LESS : {
- return intval <= intval2;
- }
- }
- return false;
- }
-
- private boolean compare_Long(int operation, long longval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- long longval2;
- try {
- longval2 = Long.parseLong(((String) value2).trim());
- } catch (IllegalArgumentException e) {
- return false;
- }
-
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return longval == longval2;
- }
- case GREATER : {
- return longval >= longval2;
- }
- case LESS : {
- return longval <= longval2;
- }
- }
- return false;
- }
-
- private boolean compare_Byte(int operation, byte byteval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- byte byteval2;
- try {
- byteval2 = Byte.parseByte(((String) value2).trim());
- } catch (IllegalArgumentException e) {
- return false;
- }
-
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return byteval == byteval2;
- }
- case GREATER : {
- return byteval >= byteval2;
- }
- case LESS : {
- return byteval <= byteval2;
- }
- }
- return false;
- }
-
- private boolean compare_Short(int operation, short shortval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- short shortval2;
- try {
- shortval2 = Short.parseShort(((String) value2).trim());
- } catch (IllegalArgumentException e) {
- return false;
- }
-
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return shortval == shortval2;
- }
- case GREATER : {
- return shortval >= shortval2;
- }
- case LESS : {
- return shortval <= shortval2;
- }
- }
- return false;
- }
-
- private boolean compare_Character(int operation, char charval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- char charval2;
- try {
- charval2 = ((String) value2).charAt(0);
- } catch (IndexOutOfBoundsException e) {
- return false;
- }
-
- switch (operation) {
- case EQUAL : {
- return charval == charval2;
- }
- case APPROX : {
- return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2)) || (Character.toLowerCase(charval) == Character.toLowerCase(charval2));
- }
- case GREATER : {
- return charval >= charval2;
- }
- case LESS : {
- return charval <= charval2;
- }
- }
- return false;
- }
-
- private boolean compare_Boolean(int operation, boolean boolval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- boolean boolval2 = Boolean.valueOf(((String) value2).trim()).booleanValue();
- switch (operation) {
- case APPROX :
- case EQUAL :
- case GREATER :
- case LESS : {
- return boolval == boolval2;
- }
- }
- return false;
- }
-
- private boolean compare_Float(int operation, float floatval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- float floatval2;
- try {
- floatval2 = Float.parseFloat(((String) value2).trim());
- } catch (IllegalArgumentException e) {
- return false;
- }
-
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return Float.compare(floatval, floatval2) == 0;
- }
- case GREATER : {
- return Float.compare(floatval, floatval2) >= 0;
- }
- case LESS : {
- return Float.compare(floatval, floatval2) <= 0;
- }
- }
- return false;
- }
-
- private boolean compare_Double(int operation, double doubleval, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- double doubleval2;
- try {
- doubleval2 = Double.parseDouble(((String) value2).trim());
- } catch (IllegalArgumentException e) {
- return false;
- }
-
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return Double.compare(doubleval, doubleval2) == 0;
- }
- case GREATER : {
- return Double.compare(doubleval, doubleval2) >= 0;
- }
- case LESS : {
- return Double.compare(doubleval, doubleval2) <= 0;
- }
- }
- return false;
- }
-
- private static Object valueOf(Class<?> target, String value2) {
- do {
- Method method;
- try {
- method = target.getMethod("valueOf", String.class);
- } catch (NoSuchMethodException e) {
- break;
- }
- if (Modifier.isStatic(method.getModifiers()) && target.isAssignableFrom(method.getReturnType())) {
- setAccessible(method);
- try {
- return method.invoke(null, value2.trim());
- } catch (IllegalAccessException e) {
- return null;
- } catch (InvocationTargetException e) {
- return null;
- }
- }
- } while (false);
-
- do {
- Constructor<?> constructor;
- try {
- constructor = target.getConstructor(String.class);
- } catch (NoSuchMethodException e) {
- break;
- }
- setAccessible(constructor);
- try {
- return constructor.newInstance(value2.trim());
- } catch (IllegalAccessException e) {
- return null;
- } catch (InvocationTargetException e) {
- return null;
- } catch (InstantiationException e) {
- return null;
- }
- } while (false);
-
- return null;
- }
-
- private static void setAccessible(AccessibleObject accessible) {
- if (!accessible.isAccessible()) {
- AccessController.doPrivileged(new SetAccessibleAction(accessible));
+ Thread t = Thread.currentThread();
+ t.getUncaughtExceptionHandler().uncaughtException(t, error);
+ } catch (Throwable ignored) {
+ // we ignore this
}
}
-
- private boolean compare_Comparable(int operation, Comparable<Object> value1, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- value2 = valueOf(value1.getClass(), (String) value2);
- if (value2 == null) {
- return false;
- }
- try {
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return value1.compareTo(value2) == 0;
- }
- case GREATER : {
- return value1.compareTo(value2) >= 0;
- }
- case LESS : {
- return value1.compareTo(value2) <= 0;
- }
- }
- } catch (Exception e) {
- // if the compareTo method throws an exception; return false
- return false;
- }
- return false;
- }
-
- private boolean compare_Version(int operation, Version value1, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- try {
- Version version2 = Version.valueOf((String) value2);
- switch (operation) {
- case APPROX :
- case EQUAL : {
- return value1.compareTo(version2) == 0;
- }
- case GREATER : {
- return value1.compareTo(version2) >= 0;
- }
- case LESS : {
- return value1.compareTo(version2) <= 0;
- }
- }
- } catch (Exception e) {
- // if the valueOf or compareTo method throws an exception
- return false;
- }
- return false;
- }
-
- private boolean compare_Unknown(int operation, Object value1, Object value2) {
- if (operation == SUBSTRING) {
- return false;
- }
- value2 = valueOf(value1.getClass(), (String) value2);
- if (value2 == null) {
- return false;
- }
- try {
- switch (operation) {
- case APPROX :
- case EQUAL :
- case GREATER :
- case LESS : {
- return value1.equals(value2);
- }
- }
- } catch (Exception e) {
- // if the equals method throws an exception; return false
- return false;
- }
- return false;
- }
-
- /**
- * Map a string for an APPROX (~=) comparison.
- *
- * This implementation removes white spaces. This is the minimum
- * implementation allowed by the OSGi spec.
- *
- * @param input Input string.
- * @return String ready for APPROX comparison.
- */
- private static String approxString(String input) {
- boolean changed = false;
- char[] output = input.toCharArray();
- int cursor = 0;
- for (char c : output) {
- if (Character.isWhitespace(c)) {
- changed = true;
- continue;
- }
-
- output[cursor] = c;
- cursor++;
- }
-
- return changed ? new String(output, 0, cursor) : input;
- }
-
- /**
- * Parser class for OSGi filter strings. This class parses the complete
- * filter string and builds a tree of Filter objects rooted at the
- * parent.
- */
- static private final class Parser {
- private final String filterstring;
- private final char[] filterChars;
- private int pos;
-
- Parser(String filterstring) {
- this.filterstring = filterstring;
- filterChars = filterstring.toCharArray();
- pos = 0;
- }
-
- FilterImpl parse() throws InvalidSyntaxException {
- FilterImpl filter;
- try {
- filter = parse_filter();
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new InvalidSyntaxException("Filter ended abruptly", filterstring, e);
- }
-
- if (pos != filterChars.length) {
- throw new InvalidSyntaxException("Extraneous trailing characters: " + filterstring.substring(pos), filterstring);
- }
- return filter;
- }
-
- private FilterImpl parse_filter() throws InvalidSyntaxException {
- FilterImpl filter;
- skipWhiteSpace();
-
- if (filterChars[pos] != '(') {
- throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
- }
-
- pos++;
-
- filter = parse_filtercomp();
-
- skipWhiteSpace();
-
- if (filterChars[pos] != ')') {
- throw new InvalidSyntaxException("Missing ')': " + filterstring.substring(pos), filterstring);
- }
-
- pos++;
-
- skipWhiteSpace();
-
- return filter;
- }
-
- private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
- skipWhiteSpace();
-
- char c = filterChars[pos];
-
- switch (c) {
- case '&' : {
- pos++;
- return parse_and();
- }
- case '|' : {
- pos++;
- return parse_or();
- }
- case '!' : {
- pos++;
- return parse_not();
- }
- }
- return parse_item();
- }
-
- private FilterImpl parse_and() throws InvalidSyntaxException {
- int lookahead = pos;
- skipWhiteSpace();
-
- if (filterChars[pos] != '(') {
- pos = lookahead - 1;
- return parse_item();
- }
-
- List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
-
- while (filterChars[pos] == '(') {
- FilterImpl child = parse_filter();
- operands.add(child);
- }
-
- return new FilterImpl(FilterImpl.AND, null,
- operands.toArray(new FilterImpl[0]));
- }
-
- private FilterImpl parse_or() throws InvalidSyntaxException {
- int lookahead = pos;
- skipWhiteSpace();
-
- if (filterChars[pos] != '(') {
- pos = lookahead - 1;
- return parse_item();
- }
-
- List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
-
- while (filterChars[pos] == '(') {
- FilterImpl child = parse_filter();
- operands.add(child);
- }
-
- return new FilterImpl(FilterImpl.OR, null,
- operands.toArray(new FilterImpl[0]));
- }
-
- private FilterImpl parse_not() throws InvalidSyntaxException {
- int lookahead = pos;
- skipWhiteSpace();
-
- if (filterChars[pos] != '(') {
- pos = lookahead - 1;
- return parse_item();
- }
-
- FilterImpl child = parse_filter();
-
- return new FilterImpl(FilterImpl.NOT, null, child);
- }
-
- private FilterImpl parse_item() throws InvalidSyntaxException {
- String attr = parse_attr();
-
- skipWhiteSpace();
-
- switch (filterChars[pos]) {
- case '~' : {
- if (filterChars[pos + 1] == '=') {
- pos += 2;
- return new FilterImpl(FilterImpl.APPROX, attr, parse_value());
- }
- break;
- }
- case '>' : {
- if (filterChars[pos + 1] == '=') {
- pos += 2;
- return new FilterImpl(FilterImpl.GREATER, attr, parse_value());
- }
- break;
- }
- case '<' : {
- if (filterChars[pos + 1] == '=') {
- pos += 2;
- return new FilterImpl(FilterImpl.LESS, attr, parse_value());
- }
- break;
- }
- case '=' : {
- if (filterChars[pos + 1] == '*') {
- int oldpos = pos;
- pos += 2;
- skipWhiteSpace();
- if (filterChars[pos] == ')') {
- return new FilterImpl(FilterImpl.PRESENT, attr, null);
- }
- pos = oldpos;
- }
-
- pos++;
- Object string = parse_substring();
-
- if (string instanceof String) {
- return new FilterImpl(FilterImpl.EQUAL, attr, string);
- }
- return new FilterImpl(FilterImpl.SUBSTRING, attr, string);
- }
- }
-
- throw new InvalidSyntaxException("Invalid operator: " + filterstring.substring(pos), filterstring);
- }
-
- private String parse_attr() throws InvalidSyntaxException {
- skipWhiteSpace();
-
- int begin = pos;
- int end = pos;
-
- char c = filterChars[pos];
-
- while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
- pos++;
-
- if (!Character.isWhitespace(c)) {
- end = pos;
- }
-
- c = filterChars[pos];
- }
-
- int length = end - begin;
-
- if (length == 0) {
- throw new InvalidSyntaxException("Missing attr: " + filterstring.substring(pos), filterstring);
- }
-
- return new String(filterChars, begin, length);
- }
-
- private String parse_value() throws InvalidSyntaxException {
- StringBuilder sb = new StringBuilder(filterChars.length - pos);
-
- parseloop: while (true) {
- char c = filterChars[pos];
-
- switch (c) {
- case ')' : {
- break parseloop;
- }
-
- case '(' : {
- throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
- }
-
- case '\\' : {
- pos++;
- c = filterChars[pos];
- /* fall through into default */
- }
-
- default : {
- sb.append(c);
- pos++;
- break;
- }
- }
- }
-
- if (sb.length() == 0) {
- throw new InvalidSyntaxException("Missing value: " + filterstring.substring(pos), filterstring);
- }
-
- return sb.toString();
- }
-
- private Object parse_substring() throws InvalidSyntaxException {
- StringBuilder sb = new StringBuilder(filterChars.length - pos);
-
- List<String> operands = new ArrayList<String>(10);
-
- parseloop: while (true) {
- char c = filterChars[pos];
-
- switch (c) {
- case ')' : {
- if (sb.length() > 0) {
- operands.add(sb.toString());
- }
-
- break parseloop;
- }
-
- case '(' : {
- throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
- }
-
- case '*' : {
- if (sb.length() > 0) {
- operands.add(sb.toString());
- }
-
- sb.setLength(0);
-
- operands.add(null);
- pos++;
-
- break;
- }
-
- case '\\' : {
- pos++;
- c = filterChars[pos];
- /* fall through into default */
- }
-
- default : {
- sb.append(c);
- pos++;
- break;
- }
- }
- }
-
- int size = operands.size();
-
- if (size == 0) {
- return "";
- }
-
- if (size == 1) {
- Object single = operands.get(0);
-
- if (single != null) {
- return single;
- }
- }
-
- return operands.toArray(new String[0]);
- }
-
- private void skipWhiteSpace() {
- for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
- pos++;
- }
- }
- }
- }
-
- /**
- * This Map is used for case-insensitive key lookup during filter
- * evaluation. This Map implementation only supports the get operation using
- * a String key as no other operations are used by the Filter
- * implementation.
- */
- static private final class CaseInsensitiveMap extends AbstractMap<String, Object> implements Map<String, Object> {
- private final Dictionary<String, ?> dictionary;
- private final String[] keys;
-
- /**
- * Create a case insensitive map from the specified dictionary.
- *
- * @param dictionary
- * @throws IllegalArgumentException If {@code dictionary} contains case
- * variants of the same key name.
- */
- CaseInsensitiveMap(Dictionary<String, ?> dictionary) {
- if (dictionary == null) {
- this.dictionary = null;
- this.keys = new String[0];
- return;
- }
- this.dictionary = dictionary;
- List<String> keyList = new ArrayList<String>(dictionary.size());
- for (Enumeration<?> e = dictionary.keys(); e.hasMoreElements();) {
- Object k = e.nextElement();
- if (k instanceof String) {
- String key = (String) k;
- for (String i : keyList) {
- if (key.equalsIgnoreCase(i)) {
- throw new IllegalArgumentException();
- }
- }
- keyList.add(key);
- }
- }
- this.keys = keyList.toArray(new String[0]);
- }
-
- @Override
- public Object get(Object o) {
- String k = (String) o;
- for (String key : keys) {
- if (key.equalsIgnoreCase(k)) {
- return dictionary.get(key);
- }
- }
- return null;
- }
-
- @Override
- public Set<java.util.Map.Entry<String, Object>> entrySet() {
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * This Map is used for key lookup from a ServiceReference during filter
- * evaluation. This Map implementation only supports the get operation using
- * a String key as no other operations are used by the Filter
- * implementation.
- */
- static private final class ServiceReferenceMap extends AbstractMap<String, Object> implements Map<String, Object> {
- private final ServiceReference<?> reference;
-
- ServiceReferenceMap(ServiceReference<?> reference) {
- this.reference = reference;
- }
-
- @Override
- public Object get(Object key) {
- if (reference == null) {
- return null;
- }
- return reference.getProperty((String) key);
- }
-
- @Override
- public Set<java.util.Map.Entry<String, Object>> entrySet() {
- throw new UnsupportedOperationException();
- }
- }
-
- static private final class SetAccessibleAction implements PrivilegedAction<Void> {
- private final AccessibleObject accessible;
-
- SetAccessibleAction(AccessibleObject accessible) {
- this.accessible = accessible;
- }
-
- @Override
- public Void run() {
- accessible.setAccessible(true);
- return null;
- }
+ helpers = Collections.unmodifiableList(l);
}
/**
@@ -2155,4 +703,357 @@
return sb.toString();
}
}
+
+ /**
+ * Return a Map wrapper around a Dictionary.
+ *
+ * @param <K> The type of the key.
+ * @param <V> The type of the value.
+ * @param dictionary The dictionary to wrap.
+ * @return A Map object which wraps the specified dictionary. If the
+ * specified dictionary can be cast to a Map, then the specified
+ * dictionary is returned.
+ * @since 1.10
+ */
+ public static <K, V> Map<K,V> asMap(
+ Dictionary< ? extends K, ? extends V> dictionary) {
+ if (dictionary instanceof Map) {
+ @SuppressWarnings("unchecked")
+ Map<K,V> coerced = (Map<K,V>) dictionary;
+ return coerced;
+ }
+ return new DictionaryAsMap<>(dictionary);
+ }
+
+ private static class DictionaryAsMap<K, V> extends AbstractMap<K,V> {
+ private final Dictionary<K,V> dict;
+
+ @SuppressWarnings("unchecked")
+ DictionaryAsMap(Dictionary< ? extends K, ? extends V> dict) {
+ this.dict = (Dictionary<K,V>) requireNonNull(dict);
+ }
+
+ Iterator<K> keys() {
+ List<K> keys = new ArrayList<>(dict.size());
+ for (Enumeration<K> e = dict.keys(); e.hasMoreElements();) {
+ keys.add(e.nextElement());
+ }
+ return keys.iterator();
+ }
+
+ @Override
+ public int size() {
+ return dict.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return dict.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ if (key == null) {
+ return false;
+ }
+ return dict.get(key) != null;
+ }
+
+ @Override
+ public V get(Object key) {
+ if (key == null) {
+ return null;
+ }
+ return dict.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return dict.put(
+ requireNonNull(key,
+ "a Dictionary cannot contain a null key"),
+ requireNonNull(value,
+ "a Dictionary cannot contain a null value"));
+ }
+
+ @Override
+ public V remove(Object key) {
+ if (key == null) {
+ return null;
+ }
+ return dict.remove(key);
+ }
+
+ @Override
+ public void clear() {
+ for (Iterator<K> iter = keys(); iter.hasNext();) {
+ dict.remove(iter.next());
+ }
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return new KeySet();
+ }
+
+ @Override
+ public Set<Map.Entry<K,V>> entrySet() {
+ return new EntrySet();
+ }
+
+ @Override
+ public String toString() {
+ return dict.toString();
+ }
+
+ final class KeySet extends AbstractSet<K> {
+ @Override
+ public Iterator<K> iterator() {
+ return new KeyIterator();
+ }
+
+ @Override
+ public int size() {
+ return DictionaryAsMap.this.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return DictionaryAsMap.this.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object key) {
+ return DictionaryAsMap.this.containsKey(key);
+ }
+
+ @Override
+ public boolean remove(Object key) {
+ return DictionaryAsMap.this.remove(key) != null;
+ }
+
+ @Override
+ public void clear() {
+ DictionaryAsMap.this.clear();
+ }
+ }
+
+ final class KeyIterator implements Iterator<K> {
+ private final Iterator<K> keys = DictionaryAsMap.this.keys();
+ private K key = null;
+
+ @Override
+ public boolean hasNext() {
+ return keys.hasNext();
+ }
+
+ @Override
+ public K next() {
+ return key = keys.next();
+ }
+
+ @Override
+ public void remove() {
+ if (key == null) {
+ throw new IllegalStateException();
+ }
+ DictionaryAsMap.this.remove(key);
+ key = null;
+ }
+ }
+
+ final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+ @Override
+ public Iterator<Map.Entry<K,V>> iterator() {
+ return new EntryIterator();
+ }
+
+ @Override
+ public int size() {
+ return DictionaryAsMap.this.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return DictionaryAsMap.this.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry< ? , ? > e = (Map.Entry< ? , ? >) o;
+ return containsEntry(e);
+ }
+ return false;
+ }
+
+ private boolean containsEntry(Map.Entry< ? , ? > e) {
+ Object key = e.getKey();
+ if (key == null) {
+ return false;
+ }
+ Object value = e.getValue();
+ if (value == null) {
+ return false;
+ }
+ return Objects.equals(DictionaryAsMap.this.get(key), value);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry< ? , ? > e = (Map.Entry< ? , ? >) o;
+ if (containsEntry(e)) {
+ DictionaryAsMap.this.remove(e.getKey());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void clear() {
+ DictionaryAsMap.this.clear();
+ }
+ }
+
+ final class EntryIterator implements Iterator<Map.Entry<K,V>> {
+ private final Iterator<K> keys = DictionaryAsMap.this.keys();
+ private K key = null;
+
+ @Override
+ public boolean hasNext() {
+ return keys.hasNext();
+ }
+
+ @Override
+ public Map.Entry<K,V> next() {
+ return new Entry(key = keys.next());
+ }
+
+ @Override
+ public void remove() {
+ if (key == null) {
+ throw new IllegalStateException();
+ }
+ DictionaryAsMap.this.remove(key);
+ key = null;
+ }
+ }
+
+ final class Entry extends SimpleEntry<K,V> {
+ private static final long serialVersionUID = 1L;
+
+ Entry(K key) {
+ super(key, DictionaryAsMap.this.get(key));
+ }
+
+ @Override
+ public V setValue(V value) {
+ DictionaryAsMap.this.put(getKey(), value);
+ return super.setValue(value);
+ }
+ }
+ }
+
+ /**
+ * Return a Dictionary wrapper around a Map.
+ *
+ * @param <K> The type of the key.
+ * @param <V> The type of the value.
+ * @param map The map to wrap.
+ * @return A Dictionary object which wraps the specified map. If the
+ * specified map can be cast to a Dictionary, then the specified map
+ * is returned.
+ * @since 1.10
+ */
+ public static <K, V> Dictionary<K,V> asDictionary(
+ Map< ? extends K, ? extends V> map) {
+ if (map instanceof Dictionary) {
+ @SuppressWarnings("unchecked")
+ Dictionary<K,V> coerced = (Dictionary<K,V>) map;
+ return coerced;
+ }
+ return new MapAsDictionary<>(map);
+ }
+
+ private static class MapAsDictionary<K, V> extends Dictionary<K,V> {
+ private final Map<K,V> map;
+
+ @SuppressWarnings("unchecked")
+ MapAsDictionary(Map< ? extends K, ? extends V> map) {
+ this.map = (Map<K,V>) requireNonNull(map);
+ boolean nullKey;
+ try {
+ nullKey = map.containsKey(null);
+ } catch (NullPointerException e) {
+ nullKey = false; // map does not allow null key
+ }
+ if (nullKey) {
+ throw new NullPointerException(
+ "a Dictionary cannot contain a null key");
+ }
+ boolean nullValue;
+ try {
+ nullValue = map.containsValue(null);
+ } catch (NullPointerException e) {
+ nullValue = false; // map does not allow null value
+ }
+ if (nullValue) {
+ throw new NullPointerException(
+ "a Dictionary cannot contain a null value");
+ }
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public Enumeration<K> keys() {
+ return Collections.enumeration(map.keySet());
+ }
+
+ @Override
+ public Enumeration<V> elements() {
+ return Collections.enumeration(map.values());
+ }
+
+ @Override
+ public V get(Object key) {
+ if (key == null) {
+ return null;
+ }
+ return map.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return map.put(
+ requireNonNull(key,
+ "a Dictionary cannot contain a null key"),
+ requireNonNull(value,
+ "a Dictionary cannot contain a null value"));
+ }
+
+ @Override
+ public V remove(Object key) {
+ if (key == null) {
+ return null;
+ }
+ return map.remove(key);
+ }
+
+ @Override
+ public String toString() {
+ return map.toString();
+ }
+ }
+
}
diff --git a/framework/src/main/java/org/osgi/framework/PackagePermission.java b/framework/src/main/java/org/osgi/framework/PackagePermission.java
index 1338daa..c10c211 100644
--- a/framework/src/main/java/org/osgi/framework/PackagePermission.java
+++ b/framework/src/main/java/org/osgi/framework/PackagePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 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.
@@ -54,7 +54,7 @@
* deprecated, implies the {@code import} action.
*
* @ThreadSafe
- * @author $Id: 264ccd683465cbe22d571b0cb7d0b19352d582f7 $
+ * @author $Id: cc8cd627f5ca1e77bd3420d6e64d07b1a9ba4684 $
*/
public final class PackagePermission extends BasicPermission {
@@ -713,7 +713,7 @@
/* work our way up the tree... */
int last;
int offset = requestedName.length() - 1;
- while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+ while ((last = requestedName.lastIndexOf('.', offset)) != -1) {
requestedName = requestedName.substring(0, last + 1) + "*";
pp = pc.get(requestedName);
if (pp != null) {
diff --git a/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java b/framework/src/main/java/org/osgi/framework/PrototypeServiceFactory.java
index 864506f..9a3a3c8 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 @@
* @see ServiceObjects
* @ThreadSafe
* @since 1.8
- * @author $Id$
+ * @author $Id: 864506fa15679676e52eee91982a6fd5c1e9768f $
*/
@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..9b8ca46 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 @@
* @see PrototypeServiceFactory
* @ThreadSafe
* @since 1.8
- * @author $Id$
+ * @author $Id: 84901895b763946d9f0e3819e47ecbf0ffa60f04 $
*/
@ProviderType
public interface ServiceObjects<S> {
diff --git a/framework/src/main/java/org/osgi/framework/ServicePermission.java b/framework/src/main/java/org/osgi/framework/ServicePermission.java
index e7d6c6f..693a787 100644
--- a/framework/src/main/java/org/osgi/framework/ServicePermission.java
+++ b/framework/src/main/java/org/osgi/framework/ServicePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 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.
@@ -51,7 +51,7 @@
* to get the specific service.
*
* @ThreadSafe
- * @author $Id: 8db61d0b1cadd57ab173cba677b6bfb353680800 $
+ * @author $Id: a6b52521c8ba68698c1e167d4596f3ac56aab8ca $
*/
public final class ServicePermission extends BasicPermission {
@@ -866,7 +866,7 @@
// work our way up the tree...
int last;
int offset = requestedName.length() - 1;
- while ((last = requestedName.lastIndexOf(".", offset)) != -1) {
+ while ((last = requestedName.lastIndexOf('.', offset)) != -1) {
requestedName = requestedName.substring(0, last + 1) + "*";
sp = pc.get(requestedName);
if (sp != null) {
diff --git a/framework/src/main/java/org/osgi/framework/ServiceReference.java b/framework/src/main/java/org/osgi/framework/ServiceReference.java
index 5d1b175..c512a44 100644
--- a/framework/src/main/java/org/osgi/framework/ServiceReference.java
+++ b/framework/src/main/java/org/osgi/framework/ServiceReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2000, 2017). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2000, 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.
@@ -50,10 +50,11 @@
* @see BundleContext#getService(ServiceReference)
* @see BundleContext#getServiceObjects(ServiceReference)
* @ThreadSafe
- * @author $Id: 1454244c30992b7a52ac3838b03bc584c3495816 $
+ * @author $Id: adb91d7f0922417180e901dc6ec447f467b34921 $
*/
@ProviderType
-public interface ServiceReference<S> extends Comparable<Object> {
+public interface ServiceReference<S>
+ extends Comparable<Object>, BundleReference {
/**
* Returns the property value to which the specified property key is mapped
* in the properties {@code Dictionary} object of the service referenced by
@@ -109,6 +110,7 @@
* already been unregistered.
* @see BundleContext#registerService(String[],Object,Dictionary)
*/
+ @Override
public Bundle getBundle();
/**
@@ -131,14 +133,24 @@
* <p>
* This method performs the following checks:
* <ol>
+ * <li>If the specified bundle is equal to the bundle that registered the
+ * service referenced by this {@code ServiceReference} (registrant bundle)
+ * return {@code true}.</li>
* <li>Get the package name from the specified class name.</li>
- * <li>For the bundle that registered the service referenced by this
- * {@code ServiceReference} (registrant bundle); find the source for the
- * package. If no source is found then return {@code true} if the registrant
- * bundle is equal to the specified bundle; otherwise return {@code false}.</li>
- * <li>If the package source of the registrant bundle is equal to the
- * package source of the specified bundle then return {@code true};
- * otherwise return {@code false}.</li>
+ * <li>For the specified bundle; find the source for the package. If no
+ * source is found then return {@code true} (use of reflection is assumed by
+ * the specified bundle).</li>
+ * <li>For the registrant bundle; find the source for the package. If the
+ * package source is found then return {@code true} if the package source
+ * equals the package source of the specified bundle; otherwise return
+ * {@code false}.</li>
+ * <li>If no package source is found for the registrant bundle then
+ * determine the package source based on the service object. If the service
+ * object is a {@code ServiceFactory} and the factory implementation is not
+ * from the registrant bundle return {@code true}; otherwise attempt to find
+ * the package source based on the service object class. If the package
+ * source is found and is equal to package source of the specified bundle
+ * return {@code true}; otherwise return {@code false}.</li>
* </ol>
*
* @param bundle The {@code Bundle} object to check.
@@ -148,8 +160,8 @@
* bundle use the same source for the package of the specified class
* name. Otherwise {@code false} is returned.
* @throws IllegalArgumentException If the specified {@code Bundle} was not
- * created by the same framework instance as this
- * {@code ServiceReference}.
+ * created by the same framework instance as this
+ * {@code ServiceReference}.
* @since 1.3
*/
public boolean isAssignableTo(Bundle bundle, String className);
@@ -212,4 +224,27 @@
* @since 1.9
*/
public Dictionary<String,Object> getProperties();
+
+ /**
+ * Adapt this {@code ServiceReference} object to the specified type.
+ * <p>
+ * Adapting this {@code ServiceReference} object to the specified type may
+ * require certain checks, including security checks, to succeed. If a check
+ * does not succeed, then this {@code ServiceReference} object cannot be
+ * adapted and {@code null} is returned.
+ *
+ * @param <A> The type to which this {@code ServiceReference} object is to
+ * be adapted.
+ * @param type Class object for the type to which this
+ * {@code ServiceReference} object is to be adapted.
+ * @return The object, of the specified type, to which this
+ * {@code ServiceReference} object has been adapted or {@code null}
+ * if this {@code ServiceReference} object cannot be adapted to the
+ * specified type.
+ * @throws SecurityException If the caller does not have the appropriate
+ * {@code AdaptPermission[type,this,ADAPT]}, and the Java
+ * Runtime Environment supports permissions.
+ * @since 1.10
+ */
+ <A> A adapt(Class<A> type);
}
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..3a90329
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectContent.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) OSGi Alliance (2019, 2020). 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.annotation.versioning.ConsumerType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleRevisions;
+
+/**
+ * A {@code ConnectContent} provides a {@link Framework} instance access to the
+ * content of a {@link ConnectModule}.
+ * <p>
+ * A framework may {@link #open() open} and {@link #close() close} the content
+ * for a {@link ConnectModule} multiple times while the {@code ConnectContent}
+ * is in use by the framework. The framework must close the
+ * {@code ConnectContent} once the {@code ConnectContent} is no longer used as
+ * the content of a current bundle revision or an in use bundle revision.
+ * <p>
+ * An entry in a {@code ConnectContent} is identified by a path name that is a
+ * solidus (<code>'/' \u002F</code>) separated path. A {@code ConnectContent}
+ * may treat directories as entries. A directory entry path name will end with a
+ * solidus. A directory entry may be located using a path name that omits the
+ * trailing solidus.
+ *
+ * @see BundleRevisions
+ * @ThreadSafe
+ * @author $Id: 9e455f9d467f0e38daea0ea52a59a5ccb8c81257 $
+ */
+@ConsumerType
+public interface ConnectContent {
+ /**
+ * The {@code osgi.identity}
+ * {@link IdentityNamespace#CAPABILITY_TAGS_ATTRIBUTE tags} attribute value
+ * used by the framework to tag connect bundle revisions.
+ */
+ String TAG_OSGI_CONNECT = "osgi.connect";
+
+ /**
+ * Returns the Manifest headers and values of this {@code ConnectContent}.
+ *
+ * @return An {@code Optional} containing the Manifest headers and values
+ * for this {@code ConnectContent}, or an empty {@code Optional} if
+ * the framework should handle parsing the Manifest of the content
+ * itself.
+ * @throws IllegalStateException If this {@code ConnectContent} has been
+ * closed.
+ */
+ Optional<Map<String,String>> getHeaders();
+
+ /**
+ * Returns the entry names available in this {@code ConnectContent}.
+ *
+ * @return An {@code Iterable} which can supply the available entry names.
+ * @throws IOException If an error occurs reading this
+ * {@code ConnectContent}.
+ * @throws IllegalStateException If this {@code ConnectContent} has been
+ * closed.
+ */
+ Iterable<String> getEntries() throws IOException;
+
+ /**
+ * Returns the {@link ConnectEntry} for the specified path name in this
+ * content.
+ * <p>
+ * 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 An {@code Optional} containing the {@link ConnectEntry} for the
+ * specified path, or an empty {@code Optional} if no entry for
+ * specified path can be found.
+ * @throws IllegalStateException If this {@code ConnectContent} has been
+ * closed.
+ */
+ Optional<ConnectEntry> getEntry(String path);
+
+ /**
+ * Returns a class loader for this {@code ConnectContent}.
+ * <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 {@link ConnectModule} is
+ * refreshed and resolved again, the framework will ask the
+ * {@code ConnectContent} for the class loader again. This allows for a
+ * {@code ConnectContent} to reuse or create a new class loader each time
+ * the bundle revision is resolved.
+ *
+ * @return An {@code Optional} containing the class loader for this
+ * {@code ConnectContent}, or an empty {@code Optional} if framework
+ * should handle creating a class loader for the bundle revision
+ * associated with this {@code ConnectContent}.
+ * @throws IllegalStateException If this {@code ConnectContent} has been
+ * closed.
+ */
+ Optional<ClassLoader> getClassLoader();
+
+ /**
+ * Opens this {@code ConnectContent}.
+ * <p>
+ * The framework will open the content when it needs to access the content
+ * for a bundle revision associated with this {@code ConnectContent}. The
+ * framework may defer calling this method until requests to access the
+ * bundle revision content are made.
+ *
+ * @throws IOException If an error occurred opening this
+ * {@code ConnectContent}.
+ */
+ void open() throws IOException;
+
+ /**
+ * Closes this {@code ConnectContent}.
+ *
+ * @throws IOException If an error occurred closing this
+ * {@code ConnectContent}.
+ */
+ void close() throws IOException;
+
+ /**
+ * Represents the entry of a {@code ConnectContent}.
+ */
+ @ConsumerType
+ public interface ConnectEntry {
+ /**
+ * Returns the path name of this entry.
+ *
+ * @return The path name of this entry.
+ */
+ String getName();
+
+ /**
+ * Returns the content length of this entry.
+ *
+ * @return The content length of the entry, or {@code -1} if the content
+ * length is not known.
+ */
+ public long getContentLength();
+
+ /**
+ * Returns the last modification time of this entry.
+ *
+ * @return The last modification time of this entry measured in
+ * milliseconds since the epoch (00:00:00 GMT, January 1, 1970).
+ */
+ public long getLastModified();
+
+ /**
+ * Returns the content of this entry.
+ *
+ * @return The content of this entry.
+ * @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 an input stream for the content of this entry.
+ *
+ * @return An input stream for the content of this entry.
+ * @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..307f419
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectFrameworkFactory.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) OSGi Alliance (2019, 2020). 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: fee4e88754bbaa4a88bcee0c0eaefa54893df6a1 $
+ */
+@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
+ */
+ 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..d3b8376
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ConnectModule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) OSGi Alliance (2019, 2020). 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.annotation.versioning.ConsumerType;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleRevision;
+
+/**
+ * A {@code ConnectModule} is used by a {@link Framework} instance to access the
+ * content of the connected bundle.
+ *
+ * @ThreadSafe
+ * @author $Id: d81245bffb9c6de8e3d2e9515f1443b0f6b47189 $
+ */
+@ConsumerType
+public interface ConnectModule {
+ /**
+ * Returns the current content of this connect module.
+ * <p>
+ * The framework must call this method when it needs to access the content
+ * for the current {@link BundleRevision bundle revision} of this
+ * {@code ConnectModule}. The framework may defer opening the returned
+ * {@link ConnectContent} until requests to access the bundle revision
+ * content are made.
+ *
+ * @return The current {@link ConnectContent} of this {@code ConnectModule}.
+ * @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..f7b54ab
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/FrameworkUtilHelper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) OSGi Alliance (2019, 2020). 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.annotation.versioning.ConsumerType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * A helper for the {@link FrameworkUtil} class.
+ * <p>
+ * This helper provides alternative implementations for methods on
+ * {@link FrameworkUtil}.
+ */
+@ConsumerType
+public interface FrameworkUtilHelper {
+ /**
+ * Returns the {@link Bundle} associated with the specified class.
+ * <p>
+ * This helper method is called by {@link FrameworkUtil#getBundle(Class)} if
+ * the standard implementation of {@link FrameworkUtil} is unable to find
+ * the bundle.
+ *
+ * @param classFromBundle A class associated with a bundle.
+ * @return An {@code Optional} containing the {@link Bundle} for the
+ * specified class, or an empty {@code 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..e62b0e0
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/ModuleConnector.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) OSGi Alliance (2019, 2020). 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.annotation.versioning.ConsumerType;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * A {@code ModuleConnector} 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}.
+ * <p>
+ * This allows a {@code ModuleConnector} to provide content and classes for a
+ * connected bundle installed in the {@code Framework}. A
+ * {@code ModuleConnector} is provided when
+ * {@link ConnectFrameworkFactory#newFramework(Map, ModuleConnector) creating} a
+ * framework instance. Because a {@code ModuleConnector} instance can
+ * participate in the initialization of the {@code Framework} and the life cycle
+ * of a {@code Framework} instance the {@code ModuleConnector} instance should
+ * only be used with a single {@code Framework} instance at a time.
+ *
+ * @ThreadSafe
+ * @author $Id: 5ee358acfec177e4bf92994fee8e61c9c841ec06 $
+ */
+@ConsumerType
+public interface ModuleConnector {
+
+ /**
+ * Initializes this {@code ModuleConnector} with the
+ * {@link Constants#FRAMEWORK_STORAGE framework persistent storage} file and
+ * framework properties configured for a {@link Framework} instance.
+ * <p>
+ * This method is called once by a {@link Framework} instance and is called
+ * before any other methods on this module connector are called.
+ *
+ * @param storage The persistent storage area used by the {@link Framework}
+ * or {@code null} if the platform does not have file system
+ * support.
+ * @param configuration An unmodifiable map of framework configuration
+ * properties that were used to configure the new framework
+ * instance.
+ */
+ void initialize(File storage, Map<String,String> configuration);
+
+ /**
+ * Connects a bundle location with a {@link ConnectModule}.
+ * <p>
+ * When the result is empty, then the framework must handle reading the
+ * content of the bundle itself. Otherwise, the returned
+ * {@link ConnectModule} must be used by the framework to access the content
+ * of the bundle.
+ *
+ * @param location The bundle location used to install a bundle.
+ * @return An {@code Optional} containing the {@link ConnectModule} for the
+ * specified bundle location, or an empty {@code Optional} if the
+ * framework must handle reading the content of the bundle itself.
+ * @throws BundleException If the location cannot be handled.
+ */
+ Optional<ConnectModule> connect(String location) throws BundleException;
+
+ /**
+ * Creates a new activator for this {@code ModuleConnector}.
+ * <p>
+ * This method is called by the framework during framework
+ * {@link Framework#init(FrameworkListener...) initialization}. Returning an
+ * activator allows this {@code ModuleConnector} to participate in the
+ * framework life cycle. If an activator is returned:
+ * <ul>
+ * <li>The framework will call the activator's
+ * {@link BundleActivator#start(BundleContext) start} method prior to
+ * activating any extension bundles.</li>
+ * <li>The framework will call the activator's
+ * {@link BundleActivator#stop(BundleContext) stop} method after
+ * deactivating any extension bundles.</li>
+ * </ul>
+ *
+ * @return An {@code Optional} containing a new {@link BundleActivator} for
+ * this {@code ModuleConnector}, or an empty {@code Optional} if no
+ * {@link BundleActivator} is necessary.
+ */
+ Optional<BundleActivator> newBundleActivator();
+}
diff --git a/framework/src/main/java/org/osgi/framework/connect/package-info.java b/framework/src/main/java/org/osgi/framework/connect/package-info.java
new file mode 100644
index 0000000..d4e22a4
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/connect/package-info.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) OSGi Alliance (2019, 2020). 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.
+ */
+
+/**
+ * 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.framework.connect; version="[1.0,2.0)"}
+ *
+ * @author $Id: b7344ca62c5cef84330e009ce4a0704e4d83b6c0 $
+ */
+
+@Version("1.0")
+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..6b83401 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 @@
* <p>
* A Bundle can be adapted to provide a {@code BundleDTO} for the Bundle.
*
- * @author $Id$
+ * @author $Id: aa30709351d8fe70b19c9ea99456ebd15ecab7c3 $
* @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..7b32f93 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 @@
* framework will contain only the launch properties of the framework. These
* properties will not include the System properties.
*
- * @author $Id$
+ * @author $Id: 7c525727cbe877e888b460cd14d8f9054f99ee0c $
* @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..2f92fb1 100644
--- a/framework/src/main/java/org/osgi/framework/dto/ServiceReferenceDTO.java
+++ b/framework/src/main/java/org/osgi/framework/dto/ServiceReferenceDTO.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2012, 2014). 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.
@@ -17,22 +17,23 @@
package org.osgi.framework.dto;
import java.util.Map;
+
import org.osgi.dto.DTO;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
/**
* Data Transfer Object for a ServiceReference.
- *
* <p>
* {@code ServiceReferenceDTO}s for all registered services can be obtained from
- * a {@link FrameworkDTO}. A started Bundle can be adapted to provide a
+ * a {@link FrameworkDTO}. A {@link ServiceReference} can be adapted to a
+ * {@code ServiceReferenceDTO}. A started Bundle can be adapted to provide a
* {@code ServiceReferenceDTO[]} of the services registered by the Bundle. A
* {@code ServiceReferenceDTO} obtained from a framework must convert service
* property values which are not valid value types for DTOs to type
* {@code String} using {@code String.valueOf(Object)}.
*
- * @author $Id$
+ * @author $Id: 8ac26a24b8adacdcd09f441dcdfc8d6a27bbaabd $
* @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..4499fc8 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: 2acfb6f1633e18f1ceedd27c04e70131cae4f293 $
*/
@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..ce89815 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: 08c20cab669f1850f585c5cd4b3b897ce587b2bd $
*/
@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..6333093 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: de050037c6b835045603f09b12ad58a6353d1229 $
*/
@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..5fd8020 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: 74f0c41d5ebf20181942ee393965d885deb3889c $
*/
@Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java b/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java
index 254dd9b..8768402 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2010, 2013). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2010, 2020). 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.
@@ -18,12 +18,11 @@
/**
* A weaving exception used to indicate that the class load should be failed but
- * the weaving hook must not be blacklisted by the framework.
- *
+ * the weaving hook must not be deny listed by the framework.
* <p>
* This exception conforms to the general purpose exception chaining mechanism.
*
- * @author $Id: 7575fc1b015fea7c77397391df6c8d2085513e76 $
+ * @author $Id: 29ebe6460fbe20ce8037f14a162c72f633b1de31 $
*/
public class WeavingException extends RuntimeException {
diff --git a/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java b/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java
index 760b00c..b3e7ac4 100644
--- a/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java
+++ b/framework/src/main/java/org/osgi/framework/hooks/weaving/WeavingHook.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2010, 2013). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2010, 2020). 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.
@@ -36,30 +36,29 @@
* bytes as modified by previously called weaving hooks.
*
* @ThreadSafe
- * @author $Id: 8d99df5b0f3e7ffa9573695923afe86de9835fde $
+ * @author $Id: e5b54121d2cab7caefbae3b718421b28e5a9ede7 $
*/
@ConsumerType
public interface WeavingHook {
/**
* Weaving hook method.
- *
+ * <p>
* This method can modify the specified woven class object to weave the
* class being defined.
- *
* <p>
* If this method throws any exception, the framework must log the exception
* and fail the class load in progress. This weaving hook service must be
- * blacklisted by the framework and must not be called again. The
- * blacklisting of this weaving hook service must expire when this weaving
- * hook service is unregistered. However, this method can throw a
+ * deny listed by the framework and must not be called again. The deny
+ * listing of this weaving hook service must expire when this weaving hook
+ * service is unregistered. However, this method can throw a
* {@link WeavingException} to deliberately fail the class load in progress
- * without being blacklisted by the framework.
+ * without being deny listed by the framework.
*
* @param wovenClass The {@link WovenClass} object that represents the data
- * that will be used to define the class.
+ * that will be used to define the class.
* @throws WeavingException If this weaving hook wants to deliberately fail
- * the class load in progress without being blacklisted by the
- * framework
+ * the class load in progress without being deny listed by the
+ * framework
*/
public void weave(WovenClass wovenClass);
}
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..36e7303 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 @@
*
* @ThreadSafe
* @since 1.1
- * @author $Id$
+ * @author $Id: 4a7a69943bffbc53738f050c51d49f11b67a13cb $
*/
@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..bffee3e 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: 5b48f041a1764e8b2979cdbbb528082054ab3e7f $
*/
@Version("1.1")
diff --git a/framework/src/main/java/org/osgi/framework/launch/Framework.java b/framework/src/main/java/org/osgi/framework/launch/Framework.java
index 37a519d..fd6d325 100644
--- a/framework/src/main/java/org/osgi/framework/launch/Framework.java
+++ b/framework/src/main/java/org/osgi/framework/launch/Framework.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2008, 2018). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2008, 2020). 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.
@@ -19,6 +19,7 @@
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
+
import org.osgi.annotation.versioning.ProviderType;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
@@ -35,7 +36,7 @@
* instance.
*
* @ThreadSafe
- * @author $Id: 7fa67978e59a43dfedd6e755ddfa5b1aa6ea9141 $
+ * @author $Id: bf960bdc39d19a780694a8cab5a555b3e0dc0fde $
*/
@ProviderType
public interface Framework extends Bundle {
@@ -117,37 +118,33 @@
* A Framework Event is returned to indicate why this Framework has stopped.
*
* @param timeout Maximum number of milliseconds to wait until this
- * Framework has completely stopped. A value of zero will wait
- * indefinitely.
+ * Framework has completely stopped. A value of zero will wait
+ * indefinitely.
* @return A Framework Event indicating the reason this method returned. The
* following {@code FrameworkEvent} types may be returned by this
* method.
* <ul>
* <li>{@link FrameworkEvent#STOPPED STOPPED} - This Framework has
- * been stopped. </li>
- *
+ * been stopped.</li>
* <li>{@link FrameworkEvent#STOPPED_UPDATE STOPPED_UPDATE} - This
* Framework has been updated which has shutdown and will now
* restart.</li>
- *
- * <li> {@link FrameworkEvent#STOPPED_BOOTCLASSPATH_MODIFIED
- * STOPPED_BOOTCLASSPATH_MODIFIED} - This Framework has been stopped
- * and a bootclasspath extension bundle has been installed or
- * updated. The VM must be restarted in order for the changed boot
- * class path to take effect. </li>
- *
+ * <li>{@link FrameworkEvent#STOPPED_SYSTEM_REFRESHED
+ * STOPPED_SYSTEM_REFRESHED} - The Framework has been stopped
+ * because of a refresh operation on the system bundle. A new class
+ * loader must be used to restart the Framework.</li>
* <li>{@link FrameworkEvent#ERROR ERROR} - The Framework
* encountered an error while shutting down or an error has occurred
- * which forced the framework to shutdown. </li>
- *
- * <li> {@link FrameworkEvent#WAIT_TIMEDOUT WAIT_TIMEDOUT} - This
+ * which forced the framework to shutdown.</li>
+ * <li>{@link FrameworkEvent#WAIT_TIMEDOUT WAIT_TIMEDOUT} - This
* method has timed out and returned before this Framework has
* stopped.</li>
* </ul>
* @throws InterruptedException If another thread interrupted the current
- * thread before or while the current thread was waiting for this
- * Framework to completely stop. The <i>interrupted status</i> of
- * the current thread is cleared when this exception is thrown.
+ * thread before or while the current thread was waiting for
+ * this Framework to completely stop. The <i>interrupted
+ * status</i> of the current thread is cleared when this
+ * exception is thrown.
* @throws IllegalArgumentException If the value of timeout is negative.
*/
FrameworkEvent waitForStop(long timeout) throws InterruptedException;
diff --git a/framework/src/main/java/org/osgi/framework/launch/FrameworkFactory.java b/framework/src/main/java/org/osgi/framework/launch/FrameworkFactory.java
index 6d656d3..f5e29a9 100644
--- a/framework/src/main/java/org/osgi/framework/launch/FrameworkFactory.java
+++ b/framework/src/main/java/org/osgi/framework/launch/FrameworkFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2009, 2013). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2009, 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.
@@ -17,6 +17,7 @@
package org.osgi.framework.launch;
import java.util.Map;
+
import org.osgi.annotation.versioning.ProviderType;
import org.osgi.framework.Bundle;
@@ -45,7 +46,7 @@
* the resource.
*
* @ThreadSafe
- * @author $Id: c1647bcb8416b6dfa9e37c6cc146bb54c7173526 $
+ * @author $Id: ecb53ee09a939a2c23a47ecad6f94fcfdf2f7c46 $
*/
@ProviderType
public interface FrameworkFactory {
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..77d416f 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2010, 2014). 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.
@@ -26,7 +26,7 @@
* <p>
* {@code Import-Package: org.osgi.framework.launch; version="[1.2,2.0)"}
*
- * @author $Id$
+ * @author $Id: 70417d372c5e8200dc743847b14aeb300870b839 $
*/
@Version("1.2")
diff --git a/framework/src/main/java/org/osgi/framework/namespace/HostNamespace.java b/framework/src/main/java/org/osgi/framework/namespace/HostNamespace.java
index 2f843a8..c6af9d8 100644
--- a/framework/src/main/java/org/osgi/framework/namespace/HostNamespace.java
+++ b/framework/src/main/java/org/osgi/framework/namespace/HostNamespace.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2012, 2014). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2012, 2020). 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.
@@ -54,19 +54,19 @@
* </ul>
*
* <p>
- * A non-fragment resource with the with the
- * {@link IdentityNamespace#TYPE_BUNDLE osgi.bundle} type
- * {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE identity} provides zero or
- * one<sup>†</sup> host capabilities. A fragment resource with the
- * {@link IdentityNamespace#TYPE_FRAGMENT osgi.fragment} type
- * {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE identity} must not declare
- * a host capability and must declare exactly one host requirement.
+ * A non-fragment resource with the {@link IdentityNamespace#TYPE_BUNDLE
+ * osgi.bundle} type {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE
+ * identity} provides zero or one<sup>†</sup> host capabilities. A
+ * fragment resource with the {@link IdentityNamespace#TYPE_FRAGMENT
+ * osgi.fragment} type {@link IdentityNamespace#CAPABILITY_TYPE_ATTRIBUTE
+ * identity} must not declare a host capability and must declare exactly one
+ * host requirement.
* <p>
* † A resource with no bundle symbolic name must not provide a host
* capability.
*
* @Immutable
- * @author $Id: 9f789ca25dafcf9d5e9a4f45d377f943d62b134a $
+ * @author $Id: 79484b4d0c372d8cebf7c767ec12e134b60b8411 $
*/
public final class HostNamespace extends AbstractWiringNamespace {
@@ -135,7 +135,6 @@
* fragment. The default value is {@link #EXTENSION_FRAMEWORK framework}.
*
* @see #EXTENSION_FRAMEWORK
- * @see #EXTENSION_BOOTCLASSPATH
*/
public final static String REQUIREMENT_EXTENSION_DIRECTIVE = "extension";
@@ -153,6 +152,7 @@
* loaded by the boot class loader.
*
* @see #REQUIREMENT_EXTENSION_DIRECTIVE
+ * @deprecated As of 1.2.
*/
public final static String EXTENSION_BOOTCLASSPATH = "bootclasspath";
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..3ef3f11 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, 2020). 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 @@
* capability.
*
* @Immutable
- * @author $Id: 7bc7a11c45b30538ffbb7572c4539f6160557684 $
+ * @author $Id: d2357fa05b1e68f952710e07a2fab46bf4ea3cec $
*/
public final class IdentityNamespace extends Namespace {
@@ -105,6 +105,16 @@
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..4376082 100644
--- a/framework/src/main/java/org/osgi/framework/namespace/NativeNamespace.java
+++ b/framework/src/main/java/org/osgi/framework/namespace/NativeNamespace.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2012, 2016). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2012, 2020). 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.
@@ -31,7 +31,7 @@
*
* @Immutable
* @since 1.1
- * @author $Id$
+ * @author $Id: f252fb2850001968fe0ee4e741a66847560dce33 $
*/
public final class NativeNamespace extends Namespace {
@@ -44,7 +44,7 @@
* The capability attribute contains alias values of the
* {@link Constants#FRAMEWORK_OS_NAME org.osgi.framework.os.name} launching
* property value according to the
- * <a href="https://www.osgi.org/developer/specifications/reference/">OSGi
+ * <a href="https://docs.osgi.org/reference/">OSGi
* Specification References</a>. The value of this attribute must be of type
* {@code List<String>}.
*/
@@ -62,7 +62,7 @@
* The capability attribute contains alias values of the
* {@link Constants#FRAMEWORK_PROCESSOR org.osgi.framework.processor}
* launching property value according to the
- * <a href="https://www.osgi.org/developer/specifications/reference/">OSGi
+ * <a href="https://docs.osgi.org/reference/">OSGi
* Specification References</a>. The value of this attribute must be of type
* {@code List<String>}.
*/
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..78fec59 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
@@ -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.
@@ -15,17 +15,17 @@
*/
/**
- * Namespace Package Version 1.1.
+ * Namespace Package Version 1.2.
*
* <p>
* Bundles should not need to import this package at runtime since all
* the types in this package just contain constants for capability and
* requirement namespaces specified by the OSGi Alliance.
*
- * @author $Id$
+ * @author $Id: 43f3804d39ad166dc082926b3a3679be67962fcc $
*/
-@Version("1.1")
+@Version("1.2")
package org.osgi.framework.namespace;
import org.osgi.annotation.versioning.Version;
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..44ad83a 100644
--- a/framework/src/main/java/org/osgi/framework/package-info.java
+++ b/framework/src/main/java/org/osgi/framework/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) OSGi Alliance (2010, 2016). 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,19 +15,19 @@
*/
/**
- * Framework Package Version 1.9.
+ * Framework Package Version 1.10.
* <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.framework; version="[1.9,2.0)"}
+ * {@code Import-Package: org.osgi.framework; version="[1.10,2.0)"}
*
- * @author $Id$
+ * @author $Id: 5b7956509a6fcf8b3f7ad23ca343dcae27cf37c6 $
*/
-@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..c36575b 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 @@
* An installed Bundle can be adapted to provide a {@code BundleStartLevelDTO}
* for the Bundle.
*
- * @author $Id$
+ * @author $Id: 81430e24483d75fd406e57214284ec43b4b3f6a7 $
* @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..cf9ff88 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 @@
* The System Bundle can be adapted to provide a {@code FrameworkStartLevelDTO}
* for the framework of the Bundle.
*
- * @author $Id$
+ * @author $Id: 2d1de40ba2b1d12832ebe0e5cdd4f789d439b6f6 $
* @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..cef11d9 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: 62262c52e6446fbe4a7b29725881f01791396c29 $
*/
@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..3265948 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: 27d775bbe53df5e1dd5d7fe6f5e1c95b294c2301 $
*/
@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..1358b2a 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 @@
* in use revisions of the Bundle can be obtained by adapting the bundle to
* {@code BundleRevisionDTO[]}.
*
- * @author $Id$
+ * @author $Id: 43d74167b8b59468b148ceb9dcde4509b68c2a90 $
* @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..b55e634 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 @@
* <p>
* {@code BundleWireDTO}s are referenced {@link BundleWiringDTO.NodeDTO}s.
*
- * @author $Id$
+ * @author $Id: ef923b41460de6d3e34fad73dc794a788533c1e9 $
* @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..a95c185 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 @@
* wirings of the Bundle can be obtained by adapting the bundle to
* {@code BundleWiringDTO[]}.
*
- * @author $Id$
+ * @author $Id: e23e80fcf8a14cbc9f565ec146821a4f22f2731b $
* @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..6a41ae5 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: 47eb1eed801ba77d58d8807aa85e9e000bca00d0 $
*/
@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..199d60c 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: 6fb7232ca6c7b389c20f75566a46c4022b8dd5d5 $
*/
@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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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/condition/Condition.java b/framework/src/main/java/org/osgi/service/condition/Condition.java
new file mode 100755
index 0000000..fcd6da8
--- /dev/null
+++ b/framework/src/main/java/org/osgi/service/condition/Condition.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) OSGi Alliance (2020). 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.service.condition;
+
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Condition Service interface.
+ * <p>
+ * In dynamic systems, such as OSGi, one of the more challenging problems can be
+ * to define when a system or part of it is ready to do work. The answer can
+ * change depending on the individual perspective. The developer of a web server
+ * might say, the system is ready when the server starts listening on port 80.
+ * An application developer however would define the system as ready when the
+ * database connection is up and all servlets are registered. Taking the
+ * application developers view, the web server should start listening on port 80
+ * when the application is ready and not beforehand.
+ * <p>
+ * The {@code Condition} service interface is a marker interface designed to
+ * address this issue. Its role is to provide a dependency that can be tracked.
+ * It acts as a defined signal to other services.
+ * <p>
+ * A {@code Condition} service must be registered with the
+ * {@link Condition#CONDITION_ID} service property.
+ *
+ * @ThreadSafe
+ * @author $Id: 9736e5e1c38c45254f733d73ed7ae2c0e253f544 $
+ */
+@ConsumerType
+public interface Condition {
+
+ /**
+ * Service property identifying a condition's unique identifier.
+ * <p>
+ * Since a {@code Condition} service can potentially describe more then one
+ * condition, the type of this service property is {@code String+}.
+ */
+ String CONDITION_ID = "osgi.condition.id";
+
+ /**
+ * The unique identifier for the default True condition.
+ * <p>
+ * The default True condition is registered by the framework during
+ * framework initialization and therefore can always be relied upon.
+ *
+ * @see Condition#CONDITION_ID
+ */
+ String CONDITION_ID_TRUE = "true";
+
+ /**
+ * A condition instance that can be used to register {@code Condition}
+ * services.
+ * <p>
+ * This can be helpful to avoid a bundle having to implement this interface
+ * to register a {@code Condition} service
+ */
+ Condition INSTANCE = new ConditionImpl();
+}
+
+final class ConditionImpl implements Condition {
+ ConditionImpl() {
+ }
+}
diff --git a/framework/src/main/java/org/osgi/service/condition/package-info.java b/framework/src/main/java/org/osgi/service/condition/package-info.java
new file mode 100755
index 0000000..a3ddce5
--- /dev/null
+++ b/framework/src/main/java/org/osgi/service/condition/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) OSGi Alliance (2020). 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.
+ */
+
+/**
+ * Condition Service 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. This package has two types of
+ * users: the consumers that use the API in this package and the providers that
+ * implement the API in this package.
+ * <p>
+ * Example import for consumers using the API in this package:
+ * <p>
+ * {@code Import-Package: org.osgi.service.condition; version="[1.0,2.0)"}
+ * <p>
+ * Example import for providers implementing the API in this package:
+ * <p>
+ * {@code Import-Package: org.osgi.service.condition; version="[1.0,1.1)"}
+ *
+ * @author $Id: 52df94eac922bd9d4f2f680cdd98e6d405844958 $
+ */
+
+@Version("1.0")
+package org.osgi.service.condition;
+
+import org.osgi.annotation.versioning.Version;
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/native-image/org.apache.felix/org.apache.felix.framework/native-image.properties b/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/native-image.properties
new file mode 100644
index 0000000..85eb4e1
--- /dev/null
+++ b/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/native-image.properties
@@ -0,0 +1,5 @@
+Args = --initialize-at-build-time=\
+org.apache.felix.framework.util.Util,\
+org.apache.felix.framework.util.SecureAction,\
+org.apache.felix.framework.BundleWiringImpl,\
+org.apache.felix.framework.BundleRevisionImpl
diff --git a/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/reflect-config.json b/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/reflect-config.json
new file mode 100644
index 0000000..b1bc8ad
--- /dev/null
+++ b/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/reflect-config.json
@@ -0,0 +1,27 @@
+[
+ {
+ "name":"org.osgi.framework.Version",
+ "allPublicConstructors" : true,
+ "allPublicMethods" : true
+ },
+ {
+ "name":"org.osgi.framework.VersionRange",
+ "allPublicConstructors" : true,
+ "allPublicMethods" : true
+ },
+ {
+ "name" : "java.net.URL",
+ "allPublicConstructors" : true,
+ "allPublicMethods" : true,
+ "allDeclaredMethods" : true,
+ "allPublicFields" : true,
+ "allDeclaredFields" : true
+ },
+ {
+ "name" : "java.net.URLStreamHandler",
+ "allPublicMethods" : true,
+ "allDeclaredMethods" : true,
+ "allPublicFields" : true,
+ "allDeclaredFields" : true
+ }
+]
diff --git a/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/resource-config.json b/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/resource-config.json
new file mode 100644
index 0000000..cfeeac1
--- /dev/null
+++ b/framework/src/main/resources/META-INF/native-image/org.apache.felix/org.apache.felix.framework/resource-config.json
@@ -0,0 +1,9 @@
+{
+ "resources":[
+ {"pattern":"META-INF/services/org.osgi.framework.connect.ConnectFrameworkFactory"},
+ {"pattern":"META-INF/services/org.osgi.framework.launch.FrameworkFactory"},
+ {"pattern":"default.properties"},
+ {"pattern":"org/apache/felix/framework/Felix.properties"},
+ {"pattern":"org/apache/felix/framework/util/accessor.bytes"}
+ ]
+}
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 d66362f..792bfdb 100644
--- a/framework/src/main/resources/default.properties
+++ b/framework/src/main/resources/default.properties
@@ -21,7 +21,7 @@
# New-style generic execution environment capabilities.
org.osgi.framework.system.capabilities= \
- ${dollar}{felix.service.caps} ${dollar}{eecap-${dollar}{java.specification.version}} ${dollar}{eecap-${dollar}{felix.detect.jpms}}
+ ${dollar}{felix.service.caps} ${dollar}{eecap-${dollar}{felix.detect.java.specification.version}} ${dollar}{eecap-${dollar}{felix.detect.jpms}}
# Native Processor Aliases. Format is felix.native.processor.alias.<normalized Processor Name>=alias1,alias2
felix.native.processor.alias.68k=
@@ -88,7 +88,7 @@
# Deprecated old-style execution environment properties.
org.osgi.framework.executionenvironment= \
- ${dollar}{ee-${dollar}{java.specification.version}} ${dollar}{ee-${dollar}{felix.detect.jpms}}
+ ${dollar}{ee-${dollar}{felix.detect.java.specification.version}} ${dollar}{ee-${dollar}{felix.detect.jpms}}
ee-1.8=JavaSE-1.8,JavaSE-1.7,JavaSE-1.6,J2SE-1.5,J2SE-1.4,J2SE-1.3, \
J2SE-1.2,JRE-1.1,JRE-1.0,OSGi/Minimum-1.2,OSGi/Minimum-1.1, \
@@ -101,14 +101,20 @@
# 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.connect;version="1.0.0", \
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", \
org.osgi.framework.hooks.service;version="1.1";uses:="org.osgi.framework", \
org.osgi.framework.hooks.weaving;version="1.1";uses:="org.osgi.framework.wiring", \
org.osgi.framework.launch;version="1.2";uses:="org.osgi.framework", \
- org.osgi.framework.namespace;version="1.1";uses:="org.osgi.resource", \
+ org.osgi.framework.namespace;version="1.2";uses:="org.osgi.resource", \
org.osgi.framework.startlevel;version="1.0";uses:="org.osgi.framework", \
org.osgi.framework.startlevel.dto;version="1.0";uses:="org.osgi.dto", \
org.osgi.framework.wiring;version="1.2";uses:="org.osgi.framework,org.osgi.resource", \
@@ -120,9 +126,8 @@
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.service.condition;version="1.0"
#
# Java platform package export properties.
@@ -375,4 +380,4 @@
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
--- /dev/null
+++ b/framework/src/main/resources/org/apache/felix/framework/util/accessor.bytes
Binary files differ
diff --git a/framework/src/main/resources/org/apache/felix/framework/util/accessor.src b/framework/src/main/resources/org/apache/felix/framework/util/accessor.src
new file mode 100644
index 0000000..2fe3e07
--- /dev/null
+++ b/framework/src/main/resources/org/apache/felix/framework/util/accessor.src
@@ -0,0 +1,31 @@
+/*
+ * 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 java.net;
+
+import java.lang.reflect.AccessibleObject;
+import java.util.function.Consumer;
+
+public class Accessor implements Consumer<AccessibleObject[]>
+{
+ @Override
+ public void accept(AccessibleObject[] accessibleObjectStream)
+ {
+ AccessibleObject.setAccessible(accessibleObjectStream, true);
+ }
+}
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 @@
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 @@
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..0a6c3f0
--- /dev/null
+++ b/framework/src/test/java/org/apache/felix/framework/ConnectTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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> newBundleActivator()
+ {
+ 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/LaunchTest.java b/framework/src/test/java/org/apache/felix/framework/LaunchTest.java
new file mode 100644
index 0000000..fcb6295
--- /dev/null
+++ b/framework/src/test/java/org/apache/felix/framework/LaunchTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.felix.framework.util.FelixConstants;
+import org.osgi.framework.*;
+import org.osgi.framework.connect.ConnectModule;
+import org.osgi.framework.connect.ModuleConnector;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.resolver.Resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class LaunchTest extends TestCase
+{
+ public void testInit() throws Exception
+ {
+ Map params = new HashMap();
+ File cacheDir = File.createTempFile("felix-cache", ".dir");
+ cacheDir.delete();
+ cacheDir.mkdirs();
+ String cache = cacheDir.getPath();
+ params.put("felix.cache.profiledir", cache);
+ params.put("felix.cache.dir", cache);
+ params.put(FelixConstants.LOG_LOGGER_PROP, new Logger()
+ {
+ @Override
+ protected void doLogOut(int level, String s, Throwable throwable) {
+
+ }
+ });
+ params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+ try
+ {
+ Framework f = new Felix(params, new ModuleConnector()
+ {
+ boolean first = true;
+ @Override
+ public void initialize(File storage, Map<String, String> configuration)
+ {
+ if (first) {
+ first = false;
+ throw new IllegalStateException("Test");
+ }
+ }
+
+ @Override
+ public Optional<ConnectModule> connect(String location)
+ {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<BundleActivator> newBundleActivator()
+ {
+ return Optional.empty();
+ }
+ }){
+ boolean first = true;
+ @Override
+ synchronized BundleActivator getActivator()
+ {
+ BundleActivator activator = super.getActivator();
+ if (first) {
+ first = false;
+ return new BundleActivator() {
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ activator.start(context);
+ throw new IllegalStateException("TEst");
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ activator.stop(context);
+ }
+ };
+ }
+ return super.getActivator();
+ }
+ };
+ try
+ {
+ f.init();
+ fail("Excepted init to fail");
+ }
+ catch (Exception ex)
+ {
+
+ }
+ assertEquals(Bundle.INSTALLED, f.getState());
+ try
+ {
+ f.init();
+ fail("Excepted init to fail");
+ }
+ catch (Exception ex)
+ {
+
+ }
+ assertEquals(Bundle.INSTALLED, f.getState());
+ f.init();
+ assertEquals(Bundle.STARTING, f.getState());
+ f.stop();
+ f.waitForStop(0);
+ assertEquals(Bundle.RESOLVED, f.getState());
+ f.init();
+ assertEquals(1,f.getBundleContext().getServiceReferences(Resolver.class, null).size());
+ assertEquals(Bundle.STARTING, f.getState());
+ f.start();
+ assertEquals(Bundle.ACTIVE, f.getState());
+ f.stop();
+ f.waitForStop(0);
+ }
+ finally
+ {
+ deleteDir(cacheDir);
+ }
+ }
+
+ private static void deleteDir(File root) throws IOException
+ {
+ if (root.isDirectory())
+ {
+ for (File file : root.listFiles())
+ {
+ deleteDir(file);
+ }
+ }
+ assertTrue(root.delete());
+ }
+}
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 @@
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/ResourceLoadingTest.java b/framework/src/test/java/org/apache/felix/framework/ResourceLoadingTest.java
index ce1167c..a89e430 100644
--- a/framework/src/test/java/org/apache/felix/framework/ResourceLoadingTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/ResourceLoadingTest.java
@@ -35,7 +35,6 @@
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
-import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
public class ResourceLoadingTest extends TestCase
@@ -104,24 +103,33 @@
assertNotNull(testBundle.getResource(name));
assertNotNull(testBundle.getEntry(name));
- BufferedReader reader = new BufferedReader(new InputStreamReader(testBundle.getResource(name).openStream()));
- assertEquals("This is a Test", reader.readLine());
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(testBundle.getResource(name).openStream())))
+ {
+ assertEquals("This is a Test", reader.readLine());
+ }
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(testBundle.getEntry(name).openStream())))
+ {
+ assertEquals("This is a Test", reader.readLine());
+ }
- reader = new BufferedReader(new InputStreamReader(testBundle.getEntry(name).openStream()));
- assertEquals("This is a Test", reader.readLine());
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(testBundle.adapt(BundleWiring.class).getClassLoader().getResourceAsStream(name))))
+ {
+ assertEquals("This is a Test", reader.readLine());
+ }
- reader = new BufferedReader(new InputStreamReader(testBundle.adapt(BundleWiring.class).getClassLoader().getResourceAsStream(name)));
- assertEquals("This is a Test", reader.readLine());
-
- reader = new BufferedReader(new InputStreamReader(testBundle.adapt(BundleWiring.class).getClassLoader().getResource(name).openStream()));
- assertEquals("This is a Test", reader.readLine());
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(testBundle.adapt(BundleWiring.class).getClassLoader().getResource(name).openStream())))
+ {
+ assertEquals("This is a Test", reader.readLine());
+ }
URL url = testBundle.adapt(BundleWiring.class).getClassLoader().getResource(name);
URL testURL = new URL(url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + "/" + name);
- reader = new BufferedReader(new InputStreamReader(testURL.openStream()));
- assertEquals("This is a Test", reader.readLine());
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(testURL.openStream())))
+ {
+ assertEquals("This is a Test", reader.readLine());
+ }
}
private static void deleteDir(File root) throws IOException
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 @@
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 @@
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 @@
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.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.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 @@
{
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 @@
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 @@
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 @@
"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 @@
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 @@
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);