diff options
3 files changed, 98 insertions, 64 deletions
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java index 84860b719..5102ae3c9 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/AbstractBundleTests.java @@ -54,12 +54,30 @@ public class AbstractBundleTests extends CoreTest { } static public void compareResults(Object[] expectedEvents, Object[] actualEvents) { - assertEquals("compareResults length", expectedEvents.length, actualEvents.length); + String expectedActual = " -- EXPECTED:" + toStringEventArray(expectedEvents) + " ACTUAL:" + toStringEventArray(actualEvents); + assertEquals("compareResults length" + expectedActual, expectedEvents.length, actualEvents.length); for (int i = 0; i < expectedEvents.length; i++) { - assertEquals("compareResults " + i, expectedEvents[i], actualEvents[i]); + String assertMsg = "compareResults: " + i + expectedActual; + assertEquals(assertMsg, expectedEvents[i], actualEvents[i]); } } + static public String toStringEventArray(Object[] events) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + sb.append('['); + for (Object event : events) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(toString(event)); + } + sb.append(']'); + return sb.toString(); + } + static public void assertEquals(String message, Object expected, Object actual) { if (expected == null && actual == null) return; diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java index 434e7f983..fea52f993 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java @@ -19,8 +19,7 @@ import javax.jws.WebService; import javax.xml.namespace.QName; import javax.xml.ws.Endpoint; import javax.xml.ws.Service; -import junit.framework.Test; -import junit.framework.TestSuite; +import junit.framework.*; import org.eclipse.osgi.internal.loader.ModuleClassLoader; import org.eclipse.osgi.tests.OSGiTestsActivator; import org.osgi.framework.*; @@ -222,14 +221,14 @@ public class ClassLoadingBundleTests extends AbstractBundleTests { expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestC); expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestD); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTest); + expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestD); + expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestD); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestB); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestB); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestC); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestC); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestA); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestA); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestD); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestD); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTest); actualFrameworkEvents = syncListenerResults.getResults(14); @@ -289,14 +288,14 @@ public class ClassLoadingBundleTests extends AbstractBundleTests { expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestC); expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestD); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTest); + expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestD); + expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestD); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestB); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestB); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestC); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestC); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestA); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestA); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestD); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestD); expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTest); actualFrameworkEvents = syncListenerResults.getResults(14); @@ -312,7 +311,7 @@ public class ClassLoadingBundleTests extends AbstractBundleTests { } } - public void testBug405918() throws BundleException { + public void testBug408629() throws BundleException { Bundle chainTest = installer.installBundle("chain.test"); //$NON-NLS-1$ Bundle chainTestA = installer.installBundle("chain.test.a"); //$NON-NLS-1$ Bundle chainTestB = installer.installBundle("chain.test.b"); //$NON-NLS-1$ @@ -333,15 +332,15 @@ public class ClassLoadingBundleTests extends AbstractBundleTests { // eager start chainTestD chainTestD.start(); - Object[] expectedEvents = new Object[5]; - expectedEvents[0] = new BundleEvent(BundleEvent.RESOLVED, chainTestD); - expectedEvents[1] = new BundleEvent(BundleEvent.RESOLVED, chainTestC); - expectedEvents[2] = new BundleEvent(BundleEvent.RESOLVED, chainTestB); - expectedEvents[3] = new BundleEvent(BundleEvent.RESOLVED, chainTestA); - expectedEvents[4] = new BundleEvent(BundleEvent.RESOLVED, chainTest); + Object[] expectedEvents1 = new Object[5]; + expectedEvents1[0] = new BundleEvent(BundleEvent.RESOLVED, chainTestD); + expectedEvents1[1] = new BundleEvent(BundleEvent.RESOLVED, chainTestC); + expectedEvents1[2] = new BundleEvent(BundleEvent.RESOLVED, chainTestB); + expectedEvents1[3] = new BundleEvent(BundleEvent.RESOLVED, chainTestA); + expectedEvents1[4] = new BundleEvent(BundleEvent.RESOLVED, chainTest); Object[] actualEvents = syncListenerResults.getResults(5); - compareResults(expectedEvents, actualEvents); + compareResults(expectedEvents1, actualEvents); try { System.setProperty("test.bug300692", "true"); System.setProperty("test.bug300692.listener", "true"); @@ -352,26 +351,47 @@ public class ClassLoadingBundleTests extends AbstractBundleTests { Object[] actualFrameworkEvents = frameworkListenerResults.getResults(1); compareResults(expectedFrameworkEvents, actualFrameworkEvents); - expectedEvents = new Object[14]; - int i = 0; - - expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestA); - expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestB); - expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestC); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTest); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestB); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestB); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestC); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestC); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestA); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestA); - expectedEvents[i++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestD); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTING, chainTestD); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTestD); - expectedEvents[i++] = new BundleEvent(BundleEvent.STARTED, chainTest); + expectedEvents1 = new Object[14]; + Object[] expectedEvents2 = new Object[14]; + int i1 = 0; + int i2 = 0; + + expectedEvents1[i1++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestA); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestA); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestB); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestB); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestC); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestC); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTING, chainTest); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTING, chainTest); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestD); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTING, chainTestB); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTING, chainTestB); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTED, chainTestB); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTED, chainTestB); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.LAZY_ACTIVATION, chainTestD); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTING, chainTestD); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTING, chainTestD); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTED, chainTestD); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTED, chainTestD); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTING, chainTestC); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTING, chainTestC); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTED, chainTestC); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTED, chainTestC); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTING, chainTestA); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTING, chainTestA); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTED, chainTestA); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTED, chainTestA); + expectedEvents1[i1++] = new BundleEvent(BundleEvent.STARTED, chainTest); + expectedEvents2[i2++] = new BundleEvent(BundleEvent.STARTED, chainTest); actualFrameworkEvents = syncListenerResults.getResults(14); - compareResults(expectedEvents, actualFrameworkEvents); + try { + compareResults(expectedEvents1, actualFrameworkEvents); + } catch (AssertionFailedError e) { + // try the second alternative + compareResults(expectedEvents2, actualFrameworkEvents); + } } finally { System.getProperties().remove("test.bug300692"); System.getProperties().remove("test.bug300692.listener"); diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hooks/EclipseLazyStarter.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hooks/EclipseLazyStarter.java index 3f419d07c..db636e962 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hooks/EclipseLazyStarter.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hooks/EclipseLazyStarter.java @@ -30,8 +30,10 @@ public class EclipseLazyStarter extends ClassLoaderHook { private static final EnumSet<State> alreadyActive = EnumSet.of(State.ACTIVE, State.STOPPING, State.UNINSTALLED); private static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction()); - // holds the current activation trigger class and the ClasspathManagers that need to be activated - private final ThreadLocal<List<Object>> activationStack = new ThreadLocal<List<Object>>(); + // holds the initiating class name + private final ThreadLocal<String> initiatingClassName = new ThreadLocal<String>(); + // holds the ClasspathManagers that need to be activated + private final ThreadLocal<Deque<ClasspathManager>> activationStack = new ThreadLocal<Deque<ClasspathManager>>(); // used to store exceptions that occurred while activating a bundle // keyed by ClasspathManager->Exception // WeakHashMap is used to prevent pinning the ClasspathManager objects. @@ -45,6 +47,9 @@ public class EclipseLazyStarter extends ClassLoaderHook { @Override public void preFindLocalClass(String name, ClasspathManager manager) throws ClassNotFoundException { + if (initiatingClassName.get() == null) { + initiatingClassName.set(name); + } ModuleRevision revision = manager.getGeneration().getRevision(); Module module = revision.getRevisions().getModule(); // If the bundle is active, uninstalled or stopping then the bundle has already @@ -54,46 +59,37 @@ public class EclipseLazyStarter extends ClassLoaderHook { // The bundle is not active and does not require activation, just return the class if (!shouldActivateFor(name, module, revision, manager)) return; - List<Object> stack = activationStack.get(); + Deque<ClasspathManager> stack = activationStack.get(); if (stack == null) { - stack = new ArrayList<Object>(6); + stack = new ArrayDeque<ClasspathManager>(6); activationStack.set(stack); } - // the first element in the stack is the name of the trigger class, - // each element after the trigger class is a classpath manager - // that must be activated after the trigger class has been defined (see postFindLocalClass) - int size = stack.size(); - if (size > 1) { - for (int i = size - 1; i >= 1; i--) - if (manager == stack.get(i)) - // the manager is already on the stack in which case we are already in the process of loading the trigger class - return; + // each element is a classpath manager that must be activated after + // the initiating class has been defined (see postFindLocalClass) + if (!stack.contains(manager)) { + // only add the manager if it has not been added already + stack.addFirst(manager); } - if (size == 0) - stack.add(name); - stack.add(manager); } @Override public void postFindLocalClass(String name, Class<?> clazz, ClasspathManager manager) throws ClassNotFoundException { - List<Object> stack = activationStack.get(); - if (stack == null) + if (initiatingClassName.get() != name) return; - int size = stack.size(); - if (size <= 1 || stack.get(0) != name) + initiatingClassName.set(null); + Deque<ClasspathManager> stack = activationStack.get(); + if (stack == null || stack.isEmpty()) return; + // if we have a stack we must clear it even if (clazz == null) - ClasspathManager[] managers = null; - managers = new ClasspathManager[size - 1]; - for (int i = 1; i < size; i++) - managers[i - 1] = (ClasspathManager) stack.get(i); + List<ClasspathManager> managers = new ArrayList<ClasspathManager>(stack); stack.clear(); if (clazz == null) return; - for (int i = managers.length - 1; i >= 0; i--) { - if (errors.get(managers[i]) != null) { + for (ClasspathManager managerElement : managers) { + if (errors.get(managerElement) != null) { if (container.getConfiguration().throwErrorOnFailedStart) - throw errors.get(managers[i]); + throw errors.get(managerElement); continue; } @@ -101,12 +97,12 @@ public class EclipseLazyStarter extends ClassLoaderHook { // Note that another thread may already be starting this bundle; // In this case we will timeout after a default of 5 seconds and record the BundleException long startTime = System.currentTimeMillis(); - Module m = managers[i].getGeneration().getRevision().getRevisions().getModule(); + Module m = managerElement.getGeneration().getRevision().getRevisions().getModule(); try { // do not persist the start of this bundle secureAction.start(m, StartOptions.LAZY_TRIGGER); } catch (BundleException e) { - Bundle bundle = managers[i].getGeneration().getRevision().getBundle(); + Bundle bundle = managerElement.getGeneration().getRevision().getBundle(); if (e.getType() == BundleException.STATECHANGE_ERROR) { String message = NLS.bind(Msg.ECLIPSE_CLASSLOADER_CONCURRENT_STARTUP, new Object[] {Thread.currentThread(), name, m.getStateChangeOwner(), bundle, new Long(System.currentTimeMillis() - startTime)}); container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, message, e); @@ -114,7 +110,7 @@ public class EclipseLazyStarter extends ClassLoaderHook { } String message = NLS.bind(Msg.ECLIPSE_CLASSLOADER_ACTIVATION, bundle.getSymbolicName(), Long.toString(bundle.getBundleId())); ClassNotFoundException error = new ClassNotFoundException(message, e); - errors.put(managers[i], error); + errors.put(managerElement, error); if (container.getConfiguration().throwErrorOnFailedStart) { container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, message, e, null); throw error; |