Bug 503742 - Disable by default lazy start trigger on failed class load
from Bundle.loadClass

Change-Id: I4d9533461b4ed4db76dc96772fd4a50a9d28a1c0
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
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 fea52f9..20fbe43 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
@@ -508,17 +508,18 @@
 		Object[] actualEvents = simpleResults.getResults(0);
 		compareResults(expectedEvents, actualEvents);
 
-		// test that calling loadClass from a lazy start bundle activates a bundle
+		// test that calling loadClass from a lazy start bundle does not activates a bundle
+		// This is not disabled by default (bug 503742)
 		Bundle legacyA = installer.installBundle("legacy.lazystart.a"); //$NON-NLS-1$
 		try {
 			legacyA.loadClass("does.not.exist.Test"); //$NON-NLS-1$
 		} catch (ClassNotFoundException e) {
 			// expected
 		}
-		expectedEvents = new Object[1];
-		expectedEvents[0] = new BundleEvent(BundleEvent.STARTED, legacyA);
-		actualEvents = simpleResults.getResults(1);
+		expectedEvents = new Object[0];
+		actualEvents = simpleResults.getResults(0);
 		compareResults(expectedEvents, actualEvents);
+		assertEquals("Wrong state for lazy bundle.", Bundle.STARTING, legacyA.getState());
 	}
 
 	public void testOSGiLazyStart() throws Exception {
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 f58ab25..eaf9aa5 100755
--- 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
@@ -2933,6 +2933,55 @@
 		equinox.stop();
 	}
 
+	public void testLazyTriggerOnLoadError() throws InterruptedException, BundleException {
+		// create/start/stop/start/stop test
+		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(EquinoxConfiguration.PROP_COMPATIBILITY_START_LAZY_ON_FAIL_CLASSLOAD, "true");
+		Equinox equinox = new Equinox(configuration);
+
+		equinox.start();
+
+		BundleContext systemContext = equinox.getBundleContext();
+		assertNotNull("System context is null", systemContext); //$NON-NLS-1$
+		// install a lazy activated bundle
+		Bundle b = systemContext.installBundle(installer.getBundleLocation("chain.test.d")); //$NON-NLS-1$
+		b.start(Bundle.START_ACTIVATION_POLICY);
+		assertEquals("Wrong state of bundle.", Bundle.STARTING, b.getState());
+		try {
+			// trigger the start by loading non existing class
+			b.loadClass("does.not.exist.Clazz");
+			fail("Expected class load error");
+		} catch (ClassNotFoundException e) {
+			// expected
+		}
+		// should be active now
+		assertEquals("Wrong state of bundle.", Bundle.ACTIVE, b.getState());
+		// put the framework back to the RESOLVED state
+		equinox.stop();
+		equinox.waitForStop(10000);
+
+		// revert back to default behavior
+		configuration.remove(EquinoxConfiguration.PROP_COMPATIBILITY_START_LAZY_ON_FAIL_CLASSLOAD);
+		equinox = new Equinox(configuration);
+		equinox.start();
+		b = equinox.getBundleContext().getBundle(b.getBundleId());
+		assertEquals("Wrong state of bundle.", Bundle.STARTING, b.getState());
+
+		try {
+			// loading non-existing class should NOT trigger lazy activation now
+			b.loadClass("does.not.exist.Clazz");
+			fail("Expected class load error");
+		} catch (ClassNotFoundException e) {
+			// expected
+		}
+		// should still be in STARTING state.
+		assertEquals("Wrong state of bundle.", Bundle.STARTING, b.getState());
+
+		equinox.stop();
+	}
+
 	void checkActiveThreadType(Equinox equinox, boolean expectIsDeamon) {
 		String uuid = equinox.getBundleContext().getProperty(Constants.FRAMEWORK_UUID);
 		ThreadGroup topGroup = Thread.currentThread().getThreadGroup();
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java
index 75dc1a3..b881e90 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java
@@ -564,9 +564,10 @@
 				return classLoader.loadClass(name);
 			}
 		} catch (ClassNotFoundException e) {
-			// This is an equinox-ism.  Not sure it is worth it to offer an option to disable ...
+			// This is an equinox-ism, check compatibility flag
+			boolean compatibilityLazyTrigger = equinoxContainer.getConfiguration().compatibilityLazyTriggerOnFailLoad;
 			// On failure attempt to activate lazy activating bundles.
-			if (State.LAZY_STARTING.equals(module.getState())) {
+			if (compatibilityLazyTrigger && State.LAZY_STARTING.equals(module.getState())) {
 				try {
 					module.start(StartOptions.LAZY_TRIGGER);
 				} catch (BundleException e1) {
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 bf22b30..54327d4 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
@@ -92,6 +92,7 @@
 
 	public final boolean contextBootDelegation;
 	public final boolean compatibilityBootDelegation;
+	public final boolean compatibilityLazyTriggerOnFailLoad;
 
 	public final List<String> LIB_EXTENSIONS;
 	public final List<String> ECLIPSE_LIB_VARIANTS;
@@ -149,6 +150,7 @@
 	public static final String PROP_COMPATIBILITY_BOOTDELEGATION = "osgi.compatibility.bootdelegation"; //$NON-NLS-1$
 	public static final String PROP_COMPATIBILITY_ERROR_FAILED_START = "osgi.compatibility.errorOnFailedStart"; //$NON-NLS-1$
 	public static final String PROP_COMPATIBILITY_START_LAZY = "osgi.compatibility.eagerStart.LazyActivation"; //$NON-NLS-1$
+	public static final String PROP_COMPATIBILITY_START_LAZY_ON_FAIL_CLASSLOAD = "osgi.compatibility.trigger.lazyActivation.onFailedClassLoad"; //$NON-NLS-1$
 
 	public static final String PROP_OSGI_OS = "osgi.os"; //$NON-NLS-1$
 	public static final String PROP_OSGI_WS = "osgi.ws"; //$NON-NLS-1$
@@ -544,6 +546,7 @@
 
 		contextBootDelegation = "true".equals(getConfiguration(PROP_CONTEXT_BOOTDELEGATION, "true")); //$NON-NLS-1$ //$NON-NLS-2$
 		compatibilityBootDelegation = "true".equals(getConfiguration(PROP_COMPATIBILITY_BOOTDELEGATION)); //$NON-NLS-1$
+		compatibilityLazyTriggerOnFailLoad = "true".equals(getConfiguration(PROP_COMPATIBILITY_START_LAZY_ON_FAIL_CLASSLOAD)); //$NON-NLS-1$
 
 		COPY_NATIVES = Boolean.valueOf(getConfiguration(PROP_COPY_NATIVES)).booleanValue();
 		String[] libExtensions = ManifestElement.getArrayFromList(getConfiguration(EquinoxConfiguration.PROP_FRAMEWORK_LIBRARY_EXTENSIONS, getConfiguration(org.osgi.framework.Constants.FRAMEWORK_LIBRARY_EXTENSIONS, getOSLibraryExtDefaults())), ","); //$NON-NLS-1$