Bug 229621 ClassLoader Deadlock Occuring with IBM JDK.
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java
index 89976c2..33c821c 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/loader/ClasspathManager.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2007 IBM Corporation and others.
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -23,6 +23,7 @@
 import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingStatsHook;
 import org.eclipse.osgi.framework.adaptor.BundleData;
 import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
 import org.eclipse.osgi.internal.baseadaptor.AdaptorMsg;
 import org.eclipse.osgi.util.NLS;
 import org.osgi.framework.BundleException;
@@ -31,7 +32,7 @@
 /**
  * A helper class for <code>BaseClassLoader</code> implementations.  This class will keep track of 
  * <code>ClasspathEntry</code> objects for the host bundle and any attached fragment bundles.  This 
- * class takes care of seaching the <code>ClasspathEntry</code> objects for a base class loader
+ * class takes care of searching the <code>ClasspathEntry</code> objects for a base class loader
  * implementation.  Additional behavior may be added to a classpath manager by configuring 
  * <code>ClassLoadingHook</code> and <code>ClassLoadingStatsHook</code>.
  * @see BaseClassLoader
@@ -41,14 +42,18 @@
  */
 public class ClasspathManager {
 	private static final FragmentClasspath[] emptyFragments = new FragmentClasspath[0];
+	private final static String PROP_CLASSLOADER_LOCK = "osgi.classloader.lock"; //$NON-NLS-1$
+	private final static String VALUE_CLASSNAME_LOCK = "classname"; //$NON-NLS-1$
+	private final static boolean LOCK_CLASSNAME = VALUE_CLASSNAME_LOCK.equals(FrameworkProperties.getProperty(PROP_CLASSLOADER_LOCK));
 
 	private BaseData data;
 	private String[] classpath;
 	private ClasspathEntry[] entries;
 	private BaseClassLoader classloader;
 	private FragmentClasspath[] fragments = emptyFragments;
-	// a colloction of String[2], each element is {"libname", "libpath"}
+	// a collection of String[2], each element is {"libname", "libpath"}
 	private Collection loadedLibraries = null;
+	private HashMap classNameLocks = new HashMap(5);
 
 	/**
 	 * Constructs a classpath manager for the given host base data, classpath and base class loader
@@ -412,7 +417,10 @@
 		try {
 			for (int i = 0; i < hooks.length; i++)
 				hooks[i].preFindLocalClass(classname, this);
-			result = findLocalClassImpl(classname, hooks);
+			if (LOCK_CLASSNAME)
+				result = findLocalClass_LockClassName(classname, hooks);
+			else
+				result = findLocalClass_LockClassLoader(classname, hooks);
 			return result;
 		} finally {
 			for (int i = 0; i < hooks.length; i++)
@@ -420,33 +428,76 @@
 		}
 	}
 
+	private Class findLocalClass_LockClassName(String classname, ClassLoadingStatsHook[] hooks) throws ClassNotFoundException {
+		boolean initialLock = lockClassName(classname);
+		try {
+			return findLocalClassImpl(classname, hooks);
+		} finally {
+			if (initialLock)
+				unlockClassName(classname);
+		}
+	}
+
+	private Class findLocalClass_LockClassLoader(String classname, ClassLoadingStatsHook[] hooks) throws ClassNotFoundException {
+		synchronized (classloader) {
+			return findLocalClassImpl(classname, hooks);
+		}
+	}
+
 	private Class findLocalClassImpl(String classname, ClassLoadingStatsHook[] hooks) throws ClassNotFoundException {
 		// must call findLoadedClass here even if it was called earlier,
 		// the findLoadedClass and defineClass calls must be atomic
-		synchronized (classloader) {
-			Class result = classloader.publicFindLoaded(classname);
-			if (result != null)
-				return result;
-			for (int i = 0; i < entries.length; i++) {
-				if (entries[i] != null) {
-					result = findClassImpl(classname, entries[i], hooks);
-					if (result != null)
-						return result;
-				}
+		Class result = classloader.publicFindLoaded(classname);
+		if (result != null)
+			return result;
+		for (int i = 0; i < entries.length; i++) {
+			if (entries[i] != null) {
+				result = findClassImpl(classname, entries[i], hooks);
+				if (result != null)
+					return result;
 			}
-			// look in fragments.
-			for (int i = 0; i < fragments.length; i++) {
-				ClasspathEntry[] fragEntries = fragments[i].getEntries();
-				for (int j = 0; j < fragEntries.length; j++) {
-					result = findClassImpl(classname, fragEntries[j], hooks);
-					if (result != null)
-						return result;
-				}
+		}
+		// look in fragments.
+		for (int i = 0; i < fragments.length; i++) {
+			ClasspathEntry[] fragEntries = fragments[i].getEntries();
+			for (int j = 0; j < fragEntries.length; j++) {
+				result = findClassImpl(classname, fragEntries[j], hooks);
+				if (result != null)
+					return result;
 			}
 		}
 		throw new ClassNotFoundException(classname);
 	}
 
+	private boolean lockClassName(String classname) throws ClassNotFoundException {
+		synchronized (classNameLocks) {
+			Object lockingThread = classNameLocks.get(classname);
+			Thread current = Thread.currentThread();
+			if (lockingThread == current)
+				return false;
+			while (true) {
+				if (lockingThread == null) {
+					classNameLocks.put(classname, current);
+					return true;
+				}
+				try {
+					classNameLocks.wait();
+					lockingThread = classNameLocks.get(classname);
+				} catch (InterruptedException e) {
+					current.interrupt();
+					throw new ClassNotFoundException(classname);
+				}
+			}
+		}
+	}
+
+	private void unlockClassName(String classname) {
+		synchronized (classNameLocks) {
+			classNameLocks.remove(classname);
+			classNameLocks.notifyAll();
+		}
+	}
+
 	private Class findClassImpl(String name, ClasspathEntry classpathEntry, ClassLoadingStatsHook[] hooks) {
 		if (Debug.DEBUG && Debug.DEBUG_LOADER)
 			Debug.println("BundleClassLoader[" + classpathEntry.getBundleFile() + "].findClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
diff --git a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java
index c2ce6ca..aad589f 100644
--- a/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java
+++ b/bundles/org.eclipse.osgi/eclipseAdaptor/src/org/eclipse/core/runtime/internal/adaptor/EclipseClassLoadingHook.java
@@ -30,6 +30,7 @@
 	private static String[] NL_JAR_VARIANTS = buildNLJarVariants(EclipseEnvironmentInfo.getDefault().getNL());
 	private static boolean DEFINE_PACKAGES;
 	private static String[] LIB_VARIANTS = buildLibraryVariants();
+	private Object pkgLock = new Object();
 
 	static {
 		try {
@@ -65,9 +66,12 @@
 		if (lastIndex < 0)
 			return null;
 		String packageName = name.substring(0, lastIndex);
-		Object pkg = manager.getBaseClassLoader().publicGetPackage(packageName);
-		if (pkg != null)
-			return null;
+		Object pkg;
+		synchronized (pkgLock) {
+			pkg = manager.getBaseClassLoader().publicGetPackage(packageName);
+			if (pkg != null)
+				return null;
+		}
 
 		// get info about the package from the classpath entry's manifest.
 		String specTitle = null, specVersion = null, specVendor = null, implTitle = null, implVersion = null, implVendor = null;
@@ -107,7 +111,12 @@
 		}
 		// The package is not defined yet define it before we define the class.
 		// TODO still need to seal packages.
-		manager.getBaseClassLoader().publicDefinePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, null);
+		synchronized (pkgLock) {
+			pkg = manager.getBaseClassLoader().publicGetPackage(packageName);
+			if (pkg != null)
+				return null;
+			manager.getBaseClassLoader().publicDefinePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, null);
+		}
 		// not doing any byte processing
 		return null;
 	}