Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2021-02-08 16:57:49 +0000
committerThomas Watson2021-02-08 17:12:04 +0000
commitfd10234f845386210e0d9da7b8b29fb68c20f2a6 (patch)
tree1d859f26dd1c61a3775c4e91d72dc101fc696105
parent56c82b5a86633c14b71f18e34a505b1cb9c81ad4 (diff)
downloadrt.equinox.framework-fd10234f845386210e0d9da7b8b29fb68c20f2a6.tar.gz
rt.equinox.framework-fd10234f845386210e0d9da7b8b29fb68c20f2a6.tar.xz
rt.equinox.framework-fd10234f845386210e0d9da7b8b29fb68c20f2a6.zip
Bug 570984 - StackOverflowError when Import-Package fails in fragment toI20210208-1800
circularly dependent plug-in Using development mode in the old resolver can lead to endless recursion when trying to set the suppliers involved in a cycle. This is because the cycle code is trying to drop bundles involved in the cycle that have unsatisified requirements. The problem is that when in development mode the resolver allows requirements to be wired to capabilities from unresolvable suppliers. This means the cycle code is not properly dropping the supplier so the supplier gets pulled into the resolve again after dropping it which leads back to the cycle check again. The fix is to avoid dropping the bundles involved in a cycle when in development mode. Change-Id: Ib3ef6b1796cd582e30a441799ed965b23cce973d Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
-rw-r--r--bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java18
-rw-r--r--bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java44
-rw-r--r--bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/services/resolver/StateResolverTest.java48
3 files changed, 107 insertions, 3 deletions
diff --git a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java
index 25af89e8e..7cd4e670c 100644
--- a/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java
+++ b/bundles/org.eclipse.osgi.compatibility.state/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -1320,7 +1320,8 @@ public class ResolverImpl implements Resolver {
return;
cycleLoop: for (Iterator<ResolverBundle> iCycle = cycle.iterator(); iCycle.hasNext();) {
ResolverBundle cycleBundle = iCycle.next();
- if (!cycleBundle.isResolvable()) {
+ // only clear cycles when not in dev mode
+ if (!developmentMode && !cycleBundle.isResolvable()) {
iCycle.remove(); // remove this bundle from the list of bundles that need re-resolved
continue cycleLoop;
}
@@ -1337,13 +1338,24 @@ public class ResolverImpl implements Resolver {
}
}
if (!resolverImport.isDynamic() && !resolverImport.isOptional() && resolverImport.getSelectedSupplier() == null) {
- cycleBundle.setResolvable(false);
+ if (resolverImport.isFromFragment()) {
+ resolverImport.getBundle().setResolvable(false);
+ } else {
+ cycleBundle.setResolvable(false);
+ }
state.addResolverError(resolverImport.getVersionConstraint().getBundle(), ResolverError.MISSING_IMPORT_PACKAGE, resolverImport.getVersionConstraint().toString(), resolverImport.getVersionConstraint());
- iCycle.remove();
continue cycleLoop;
}
}
}
+ // only clear cycles when not in dev mode
+ if (!developmentMode) {
+ for (Iterator<ResolverBundle> iCycle = cycle.iterator(); iCycle.hasNext();) {
+ if (!iCycle.next().isResolvable()) {
+ iCycle.remove();
+ }
+ }
+ }
if (cycle.size() != cycleSize) {
//we removed an un-resolvable bundle; must re-resolve remaining cycle
for (int i = 0; i < cycle.size(); i++) {
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java
index d1ce33195..53888f606 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/TestModuleContainer.java
@@ -3762,6 +3762,50 @@ public class TestModuleContainer extends AbstractTest {
Assert.assertNull("Failed to resolve", report.getResolutionException());
}
+ @Test
+ public void testCycleBug570984() throws BundleException, IOException {
+ DummyContainerAdaptor adaptor = createDummyAdaptor();
+ ModuleContainer container = adaptor.getContainer();
+
+ // install the system.bundle
+ Module systemBundle = installDummyModule("system.bundle.MF", Constants.SYSTEM_BUNDLE_LOCATION,
+ Constants.SYSTEM_BUNDLE_SYMBOLICNAME, null, null, container);
+ ResolutionReport report = container.resolve(Arrays.asList(systemBundle), true);
+ Assert.assertNull("Failed to resolve system.bundle.", report.getResolutionException());
+
+ Map<String, String> manifestA = new HashMap<>();
+ manifestA.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifestA.put(Constants.BUNDLE_SYMBOLICNAME, "a");
+ manifestA.put(Constants.BUNDLE_VERSION, "1");
+ manifestA.put(Constants.REQUIRE_BUNDLE, "b");
+ manifestA.put(Constants.PROVIDE_CAPABILITY, "ca");
+ manifestA.put(Constants.REQUIRE_CAPABILITY, "cb");
+ Module moduleA = installDummyModule(manifestA, "a", container);
+
+ Map<String, String> manifestB = new HashMap<>();
+ manifestB.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifestB.put(Constants.BUNDLE_SYMBOLICNAME, "b");
+ manifestB.put(Constants.BUNDLE_VERSION, "1");
+ manifestB.put(Constants.REQUIRE_BUNDLE, "a");
+ manifestB.put(Constants.PROVIDE_CAPABILITY, "cb");
+ manifestB.put(Constants.REQUIRE_CAPABILITY, "ca");
+ Module moduleB = installDummyModule(manifestB, "b", container);
+
+ Map<String, String> manifestBF = new HashMap<>();
+ manifestBF.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifestBF.put(Constants.BUNDLE_SYMBOLICNAME, "bf");
+ manifestBF.put(Constants.BUNDLE_VERSION, "1");
+ manifestBF.put(Constants.FRAGMENT_HOST, "b");
+ manifestBF.put(Constants.IMPORT_PACKAGE, "e");
+ Module moduleBF = installDummyModule(manifestBF, "bf", container);
+
+ report = container.resolve(Arrays.asList(moduleA, moduleB, moduleBF), false);
+ Assert.assertNull("Failed to resolve", report.getResolutionException());
+ assertEquals("Wrong state for moduleA", State.RESOLVED, moduleA.getState());
+ assertEquals("Wrong state for moduleB", State.RESOLVED, moduleB.getState());
+ assertEquals("Wrong state for moduleBF", State.INSTALLED, moduleBF.getState());
+ }
+
private static void assertWires(List<ModuleWire> required, List<ModuleWire>... provided) {
for (ModuleWire requiredWire : required) {
for (List<ModuleWire> providedList : provided) {
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 4c3fc82cc..8bd3e5b2d 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
@@ -4410,6 +4410,54 @@ public class StateResolverTest extends AbstractStateTest {
assertEquals("Wrong number of visible", 2, visible.length);
}
+ public void testCycleBug570984() throws BundleException {
+ State state = buildEmptyState();
+ int bundleID = 0;
+
+ Hashtable<String, String> manifestA = new Hashtable<>();
+ manifestA.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifestA.put(Constants.BUNDLE_SYMBOLICNAME, "a");
+ manifestA.put(Constants.BUNDLE_VERSION, "1");
+ manifestA.put(Constants.REQUIRE_BUNDLE, "b");
+ manifestA.put(Constants.PROVIDE_CAPABILITY, "ca");
+ manifestA.put(Constants.REQUIRE_CAPABILITY, "cb");
+ BundleDescription moduleA = state.getFactory().createBundleDescription(state, manifestA,
+ manifestA.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++);
+
+ Hashtable<String, String> manifestB = new Hashtable<>();
+ manifestB.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifestB.put(Constants.BUNDLE_SYMBOLICNAME, "b");
+ manifestB.put(Constants.BUNDLE_VERSION, "1");
+ manifestB.put(Constants.REQUIRE_BUNDLE, "a");
+ manifestB.put(Constants.PROVIDE_CAPABILITY, "cb");
+ manifestB.put(Constants.REQUIRE_CAPABILITY, "ca");
+ BundleDescription moduleB = state.getFactory().createBundleDescription(state, manifestB,
+ manifestB.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++);
+
+ Hashtable<String, String> manifestBF = new Hashtable<>();
+ manifestBF.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifestBF.put(Constants.BUNDLE_SYMBOLICNAME, "bf");
+ manifestBF.put(Constants.BUNDLE_VERSION, "1");
+ manifestBF.put(Constants.FRAGMENT_HOST, "b");
+ manifestBF.put(Constants.IMPORT_PACKAGE, "e");
+ BundleDescription moduleBF = state.getFactory().createBundleDescription(state, manifestBF,
+ manifestBF.get(Constants.BUNDLE_SYMBOLICNAME), bundleID++);
+
+ state.addBundle(moduleA);
+ state.addBundle(moduleB);
+ state.addBundle(moduleBF);
+ state.resolve();
+ assertTrue("A is not resolved", moduleA.isResolved()); //$NON-NLS-1$
+ assertTrue("B is not resolved", moduleB.isResolved()); //$NON-NLS-1$
+ assertFalse("BF not resolved", moduleBF.isResolved()); //$NON-NLS-1$
+
+ // now try dev mode - this will trigger a StackOverflowError
+ Dictionary[] props = new Dictionary[] { new Hashtable() };
+ props[0].put("osgi.resolverMode", "development"); //$NON-NLS-1$ //$NON-NLS-2$
+ state.setPlatformProperties(props);
+ state.resolve(new BundleDescription[] { moduleA, moduleB, moduleBF }, true);
+ }
+
public static class CatchAllValue {
public CatchAllValue(String s) {
//do nothing

Back to the top