Bug 201489 [osgi R5] multiple versions of a fragment should not attach to the same host
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/StateResolverTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/StateResolverTest.java
index 7b1da6f..563b5a7 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/StateResolverTest.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/StateResolverTest.java
@@ -1795,6 +1795,56 @@
 		assertTrue("3.4", aExports[1] == bImports[1]);
 	}
 
+	public void testFragmentsMultipleVersion() throws BundleException {
+		State state = buildEmptyState();
+		int bundleID = 0;
+		// test the selection algorithm of the resolver to pick the bundles which
+		// resolve the largest set of bundles; with fragments using Import-Package
+		Hashtable manifest = new Hashtable();
+
+		manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+		manifest.put(Constants.BUNDLE_SYMBOLICNAME, "A");
+		manifest.put(Constants.BUNDLE_VERSION, "1.0");
+		BundleDescription a1 = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME) + '_' + (String) manifest.get(Constants.BUNDLE_VERSION), bundleID++);
+
+		manifest.clear();
+		manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+		manifest.put(Constants.BUNDLE_SYMBOLICNAME, "AFrag");
+		manifest.put(Constants.BUNDLE_VERSION, "1.0");
+		manifest.put(Constants.FRAGMENT_HOST, "A");
+		BundleDescription aFrag1 = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME) + '_' + (String) manifest.get(Constants.BUNDLE_VERSION), bundleID++);
+
+		manifest.clear();
+		manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+		manifest.put(Constants.BUNDLE_SYMBOLICNAME, "A");
+		manifest.put(Constants.BUNDLE_VERSION, "2.0");
+		BundleDescription a2 = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME) + '_' + (String) manifest.get(Constants.BUNDLE_VERSION), bundleID++);
+
+		manifest.clear();
+		manifest.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+		manifest.put(Constants.BUNDLE_SYMBOLICNAME, "AFrag");
+		manifest.put(Constants.BUNDLE_VERSION, "2.0");
+		manifest.put(Constants.FRAGMENT_HOST, "A");
+		BundleDescription aFrag2 = state.getFactory().createBundleDescription(state, manifest, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME) + '_' + (String) manifest.get(Constants.BUNDLE_VERSION), bundleID++);
+
+		state.addBundle(a1);
+		state.addBundle(aFrag1);
+		state.addBundle(a2);
+		state.addBundle(aFrag2);
+		state.resolve();
+		assertTrue("0.1", a1.isResolved());
+		assertTrue("0.2", aFrag1.isResolved());
+		assertTrue("0.3", a2.isResolved());
+		assertTrue("0.4", aFrag2.isResolved());
+
+		state.removeBundle(a2);
+		state.resolve(false);
+		assertTrue("1.1", a1.isResolved());
+		assertTrue("1.2", aFrag2.isResolved());
+		assertFalse("1.3", aFrag1.isResolved());
+		assertEquals("1.4", a1, aFrag2.getHost().getSupplier());
+	}
+
 	public void testReexportPackage() throws BundleException {
 		State state = buildEmptyState();
 		Hashtable manifest = new Hashtable();
diff --git a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
index 726a00a..3a902ee 100644
--- a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
@@ -54,7 +54,7 @@
 Bundle-Description: %systemBundle
 Bundle-Copyright: %copyright
 Bundle-Vendor: %eclipse.org
-Bundle-Version: 3.3.2.qualifier
+Bundle-Version: 3.3.3.qualifier
 Bundle-Localization: systembundle
 Bundle-DocUrl: http://www.eclipse.org
 Eclipse-ExtensibleAPI: true
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java
index 063f159..f3da5c8 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java
@@ -322,6 +322,14 @@
 			fragment.setNewFragmentExports(true);
 
 		initFragments();
+		// need to make sure there is not already another version of this fragment 
+		// already attached to this host
+		for (Iterator iFragments = fragments.iterator(); iFragments.hasNext();) {
+			ResolverBundle existingFragment = (ResolverBundle) iFragments.next();
+			String bsn = existingFragment.getName();
+			if (bsn != null && bsn.equals(fragment.getName()))
+				return new ResolverExport[0];
+		}
 		if (fragments.contains(fragment))
 			return new ResolverExport[0];
 		fragments.add(fragment);
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
index fb62a6c..763a8f0 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -285,7 +285,22 @@
 	}
 
 	// Attach fragment to its host
-	private void attachFragment(ResolverBundle bundle, ArrayList rejectedSingletons) {
+	private void attachFragment(ResolverBundle bundle, ArrayList rejectedSingletons, ArrayList processedFragments) {
+		if (processedFragments.contains(bundle.getName()))
+			return;
+		processedFragments.add(bundle.getName());
+		// we want to attach multiple versions of the same fragment
+		// from highest version to lowest to give the higher versions first pick
+		// of the available host bundles.
+		Object[] fragments = resolverBundles.get(bundle.getName());
+		for (int i = 0; i < fragments.length; i++) {
+			ResolverBundle fragment = (ResolverBundle) fragments[i];
+			if (!fragment.isResolved())
+				attachFragment0(fragment, rejectedSingletons);
+		}
+	}
+
+	private void attachFragment0(ResolverBundle bundle, ArrayList rejectedSingletons) {
 		if (!bundle.isFragment() || !bundle.isResolvable() || rejectedSingletons.contains(bundle.getBundle()))
 			return;
 		// no need to select singletons now; it will be done when we select the rest of the singleton bundles (bug 152042)
@@ -462,8 +477,9 @@
 			// need to sort bundles to keep consistent order for fragment attachment (bug 174930)
 			Arrays.sort(bundles);
 		// First attach all fragments to the matching hosts
+		ArrayList processedFragments = new ArrayList(bundles.length);
 		for (int i = 0; i < bundles.length; i++)
-			attachFragment(bundles[i], rejectedSingletons);
+			attachFragment(bundles[i], rejectedSingletons, processedFragments);
 
 		// Lists of cyclic dependencies recording during resolving
 		ArrayList cycle = new ArrayList(1); // start small