Bug 449484 - Add configuration property to allow bundles to provide
osgi.ee and osgi.native capabilities

Change-Id: I87d57a61056188943a04f135bf8d95878c80363b
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.osgi.tests/.classpath b/bundles/org.eclipse.osgi.tests/.classpath
index e10e448..8769c8c 100644
--- a/bundles/org.eclipse.osgi.tests/.classpath
+++ b/bundles/org.eclipse.osgi.tests/.classpath
@@ -128,5 +128,6 @@
 	<classpathentry kind="src" output="bundle_tests/test.bug438904.frag" path="bundles_src/test.bug438904.frag"/>
 	<classpathentry kind="src" output="bundle_tests/test.bug438904.global" path="bundles_src/test.bug438904.global"/>
 	<classpathentry kind="src" output="bundle_tests/test.system.nls"  path="bundles_src/test.system.nls"/>
+	<classpathentry kind="src" output="bundle_tests/test.bug449484" path="bundles_src/test.bug449484"/>	
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/bundles/org.eclipse.osgi.tests/build.properties b/bundles/org.eclipse.osgi.tests/build.properties
index 5959b10..1aecd09 100644
--- a/bundles/org.eclipse.osgi.tests/build.properties
+++ b/bundles/org.eclipse.osgi.tests/build.properties
@@ -260,6 +260,8 @@
 manifest.bundle_tests/test.bug438904.global.jar = META-INF/MANIFEST.MF
 source.bundle_tests/test.system.nls.jar = bundles_src/test.system.nls/
 manifest.bundle_tests/test.system.nls.jar = META-INF/MANIFEST.MF
+source.bundle_tests/test.bug449484.jar = bundles_src/test.bug449484/
+manifest.bundle_tests/test.bug449484.jar = META-INF/MANIFEST.MF
 
 jars.compile.order = bundle_tests/ext.framework.b.jar,\
                      osgitests.jar,\
@@ -385,4 +387,5 @@
                      bundle_tests/test.bug438904.host.jar,\
                      bundle_tests/test.bug438904.frag.jar,\
                      bundle_tests/test.bug438904.global.jar,\
-                     bundle_tests/test.system.nls.jar
+                     bundle_tests/test.system.nls.jar,\
+                     bundle_tests/test.bug449484.jar
diff --git a/bundles/org.eclipse.osgi.tests/bundles_src/test.bug449484/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.tests/bundles_src/test.bug449484/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..2e78829
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/bundles_src/test.bug449484/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: test.bug449484
+Bundle-Version: 1.0
+Provide-Capability: osgi.ee; osgi.ee=test, osgi.native; osgi.native=test
+
+
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java
index 6334103..d367c4f 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java
@@ -2347,6 +2347,48 @@
 		}
 	}
 
+	public void testProvideOSGiEEandNative() throws BundleException {
+		File config = OSGiTestsActivator.getContext().getDataFile(getName()); //$NON-NLS-1$
+		Map<String, Object> configuration = new HashMap<String, Object>();
+		configuration.put(Constants.FRAMEWORK_STORAGE, config.getAbsolutePath());
+		configuration.put("osgi.equinox.allow.restricted.provides", "true");
+
+		Equinox equinox = new Equinox(configuration);
+		equinox.start();
+		BundleContext systemContext = equinox.getBundleContext();
+		Bundle testBundle = systemContext.installBundle(installer.getBundleLocation("test.bug449484"));
+		equinox.adapt(FrameworkWiring.class).resolveBundles(Collections.singleton(testBundle));
+		assertEquals("Wrong bundle state", Bundle.RESOLVED, testBundle.getState());
+		testBundle.uninstall();
+		equinox.stop();
+
+		configuration.remove("osgi.equinox.allow.restricted.provides");
+		equinox = new Equinox(configuration);
+		equinox.start();
+		systemContext = equinox.getBundleContext();
+		try {
+			testBundle = systemContext.installBundle(installer.getBundleLocation("test.bug449484"));
+			testBundle.uninstall();
+			fail("Expected to fail to install bundle with restricted provide capabilities.");
+		} catch (BundleException e) {
+			// expected
+		}
+		equinox.stop();
+
+		configuration.put("osgi.equinox.allow.restricted.provides", "false");
+		equinox = new Equinox(configuration);
+		equinox.start();
+		systemContext = equinox.getBundleContext();
+		try {
+			testBundle = systemContext.installBundle(installer.getBundleLocation("test.bug449484"));
+			testBundle.uninstall();
+			fail("Expected to fail to install bundle with restricted provide capabilities.");
+		} catch (BundleException e) {
+			// expected
+		}
+		equinox.stop();
+	}
+
 	private static File[] createBundles(File outputDir, int bundleCount) throws IOException {
 		outputDir.mkdirs();
 
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/builders/OSGiManifestBuilderFactory.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/builders/OSGiManifestBuilderFactory.java
index 3ea7ff3..846681b 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/builders/OSGiManifestBuilderFactory.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/builders/OSGiManifestBuilderFactory.java
@@ -66,7 +66,7 @@
 	 * @param manifest the bundle manifest
 	 * @param symbolicNameAlias the symbolic name alias.  A <code>null</code> value is allowed.
 	 * @param extraExports the extra package exports.  A <code>null</code> value is allowed.
-	 * @param extraCapabilities the extra proided capabilities.   A <code>null</code> value is allowed.
+	 * @param extraCapabilities the extra provided capabilities.   A <code>null</code> value is allowed.
 	 * @return a builder for the specified bundle manifest
 	 * @throws BundleException if the bundle manifest is invalid
 	 */
@@ -83,7 +83,7 @@
 		Collection<Map<String, Object>> exportedPackages = new ArrayList<Map<String, Object>>();
 		getPackageExports(builder, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, manifest.get(Constants.EXPORT_PACKAGE)), symbolicName, exportedPackages);
 		getPackageExports(builder, ManifestElement.parseHeader(HEADER_OLD_PROVIDE_PACKAGE, manifest.get(HEADER_OLD_PROVIDE_PACKAGE)), symbolicName, exportedPackages);
-		if (extraExports != null) {
+		if (extraExports != null && !extraExports.isEmpty()) {
 			getPackageExports(builder, ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, extraExports), symbolicName, exportedPackages);
 		}
 		getPackageImports(builder, manifest, exportedPackages, manifestVersion);
@@ -91,7 +91,7 @@
 		getRequireBundle(builder, ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, manifest.get(Constants.REQUIRE_BUNDLE)));
 
 		getProvideCapabilities(builder, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, manifest.get(Constants.PROVIDE_CAPABILITY)), extraCapabilities == null);
-		if (extraCapabilities != null) {
+		if (extraCapabilities != null && !extraCapabilities.isEmpty()) {
 			getProvideCapabilities(builder, ManifestElement.parseHeader(Constants.PROVIDE_CAPABILITY, extraCapabilities), false);
 		}
 		getRequireCapabilities(builder, ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, manifest.get(Constants.REQUIRE_CAPABILITY)));
@@ -503,7 +503,7 @@
 		builder.addCapability(EquinoxFragmentNamespace.FRAGMENT_NAMESPACE, directives, Collections.<String, Object> singletonMap(EquinoxFragmentNamespace.FRAGMENT_NAMESPACE, hostName));
 	}
 
-	private static void getProvideCapabilities(ModuleRevisionBuilder builder, ManifestElement[] provideElements, boolean chechSystemCapabilities) throws BundleException {
+	private static void getProvideCapabilities(ModuleRevisionBuilder builder, ManifestElement[] provideElements, boolean checkSystemCapabilities) throws BundleException {
 		if (provideElements == null)
 			return;
 		for (ManifestElement provideElement : provideElements) {
@@ -511,7 +511,7 @@
 			Map<String, Object> attributes = getAttributes(provideElement);
 			Map<String, String> directives = getDirectives(provideElement);
 			for (String namespace : namespaces) {
-				if (PROHIBITED_CAPABILITIES.contains(namespace) || (chechSystemCapabilities && SYSTEM_CAPABILITIES.contains(namespace))) {
+				if (PROHIBITED_CAPABILITIES.contains(namespace) || (checkSystemCapabilities && SYSTEM_CAPABILITIES.contains(namespace))) {
 					throw new BundleException("A bundle is not allowed to define a capability in the " + namespace + " name space.", BundleException.MANIFEST_ERROR); //$NON-NLS-1$ //$NON-NLS-2$
 				}
 
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java
index 1a8613f..e71ca9d 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxConfiguration.java
@@ -189,6 +189,7 @@
 	public static final String PROP_STATE_SAVE_DELAY_INTERVAL = "eclipse.stateSaveDelayInterval"; //$NON-NLS-1$
 
 	public static final String PROP_MODULE_LOCK_TIMEOUT = "osgi.module.lock.timeout"; //$NON-NLS-1$
+	public static final String PROP_ALLOW_RESTRICTED_PROVIDES = "osgi.equinox.allow.restricted.provides"; //$NON-NLS-1$
 
 	private final static Collection<String> populateInitConfig = Arrays.asList(PROP_OSGI_ARCH, PROP_OSGI_OS, PROP_OSGI_WS, PROP_OSGI_NL, FRAMEWORK_OS_NAME, FRAMEWORK_OS_VERSION, FRAMEWORK_PROCESSOR, FRAMEWORK_LANGUAGE);
 
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java
index 680862d..2ac2574 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java
@@ -79,6 +79,7 @@
 	private final MRUBundleFileList mruList;
 	private final FrameworkExtensionInstaller extensionInstaller;
 	private final List<String> cachedHeaderKeys = Arrays.asList(Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_ACTIVATIONPOLICY, "Service-Component"); //$NON-NLS-1$
+	private final boolean allowRestrictedProvides;
 
 	public static Storage createStorage(EquinoxContainer container) throws IOException, BundleException {
 		Storage storage = new Storage(container);
@@ -95,6 +96,7 @@
 		mruList = new MRUBundleFileList(getBundleFileLimit(container.getConfiguration()));
 		equinoxContainer = container;
 		extensionInstaller = new FrameworkExtensionInstaller(container.getConfiguration());
+		allowRestrictedProvides = Boolean.parseBoolean(container.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_ALLOW_RESTRICTED_PROVIDES));
 
 		// we need to set the install path as soon as possible so we can determine
 		// the absolute location of install relative URLs
@@ -582,7 +584,7 @@
 			}
 		}
 		if (generation.getBundleInfo().getBundleId() != 0) {
-			ModuleRevisionBuilder builder = OSGiManifestBuilderFactory.createBuilder(mapHeaders);
+			ModuleRevisionBuilder builder = allowRestrictedProvides ? OSGiManifestBuilderFactory.createBuilder(mapHeaders, null, null, "") : OSGiManifestBuilderFactory.createBuilder(mapHeaders); //$NON-NLS-1$
 			if ((builder.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
 				for (ModuleRevisionBuilder.GenericInfo reqInfo : builder.getRequirements()) {
 					if (HostNamespace.HOST_NAMESPACE.equals(reqInfo.getNamespace())) {