diff options
author | Thomas Watson | 2016-01-13 14:43:26 +0000 |
---|---|---|
committer | Thomas Watson | 2016-01-13 20:29:15 +0000 |
commit | 0db881dd7d066c74a69841696476b17d034cfbec (patch) | |
tree | 58c1f852b96f456120b4cf4552c4111a3e5dc600 | |
parent | b6a649a8122e45c63568c00cb522a05a5afd1a32 (diff) | |
download | rt.equinox.bundles-0db881dd7d066c74a69841696476b17d034cfbec.tar.gz rt.equinox.bundles-0db881dd7d066c74a69841696476b17d034cfbec.tar.xz rt.equinox.bundles-0db881dd7d066c74a69841696476b17d034cfbec.zip |
Bug 479396 - deadlock in class loading when deploying at startup
Change-Id: I125f389a742a26dd18a03da736bb0de34a5b4871
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
2 files changed, 115 insertions, 9 deletions
diff --git a/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java b/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java index 03bd8f902..5360875a2 100644 --- a/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java +++ b/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2013 IBM Corporation and others. + * Copyright (c) 2008, 2015 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 @@ -25,6 +25,7 @@ package org.eclipse.equinox.servletbridge; import java.io.*; +import java.lang.reflect.Method; import java.net.*; import java.security.*; import java.util.*; @@ -32,6 +33,19 @@ import java.util.jar.*; import java.util.jar.Attributes.Name; public class CloseableURLClassLoader extends URLClassLoader { + private static final boolean CLOSEABLE_REGISTERED_AS_PARALLEL; + static { + boolean registeredAsParallel; + try { + Method parallelCapableMetod = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable", (Class[]) null); //$NON-NLS-1$ + parallelCapableMetod.setAccessible(true); + registeredAsParallel = ((Boolean) parallelCapableMetod.invoke(null, (Object[]) null)).booleanValue(); + } catch (Throwable e) { + // must do everything to avoid failing in clinit + registeredAsParallel = true; + } + CLOSEABLE_REGISTERED_AS_PARALLEL = registeredAsParallel; + } static final String DOT_CLASS = ".class"; //$NON-NLS-1$ static final String BANG_SLASH = "!/"; //$NON-NLS-1$ static final String JAR = "jar"; //$NON-NLS-1$ @@ -47,6 +61,7 @@ public class CloseableURLClassLoader extends URLClassLoader { private final AccessControlContext context; private final boolean verifyJars; + private final boolean registeredAsParallel; private static class CloseableJarURLConnection extends JarURLConnection { private final JarFile jarFile; @@ -165,6 +180,7 @@ public class CloseableURLClassLoader extends URLClassLoader { */ public CloseableURLClassLoader(URL[] urls, ClassLoader parent, boolean verifyJars) { super(excludeFileJarURLS(urls), parent); + this.registeredAsParallel = CLOSEABLE_REGISTERED_AS_PARALLEL && this.getClass() == CloseableURLClassLoader.class; this.context = AccessController.getContext(); this.verifyJars = verifyJars; for (int i = 0; i < urls.length; i++) { @@ -289,12 +305,14 @@ public class CloseableURLClassLoader extends URLClassLoader { JarURLConnection connection = (JarURLConnection) resourceURL.openConnection(); int lastDot = name.lastIndexOf('.'); if (lastDot != -1) { - String packageName = name.substring(0, lastDot + 1); - Package pkg = getPackage(packageName); - if (pkg != null) { - checkForSealedPackage(pkg, packageName, manifest, connection.getJarFileURL()); - } else { - definePackage(packageName, manifest, connection.getJarFileURL()); + String packageName = name.substring(0, lastDot); + synchronized (pkgLock) { + Package pkg = getPackage(packageName); + if (pkg != null) { + checkForSealedPackage(pkg, packageName, manifest, connection.getJarFileURL()); + } else { + definePackage(packageName, manifest, connection.getJarFileURL()); + } } } JarEntry entry = connection.getJarEntry(); @@ -304,6 +322,20 @@ public class CloseableURLClassLoader extends URLClassLoader { is = new DataInputStream(connection.getInputStream()); is.readFully(bytes, 0, bytes.length); CodeSource cs = new CodeSource(connection.getJarFileURL(), entry.getCertificates()); + if (isRegisteredAsParallel()) { + boolean initialLock = lockClassName(name); + try { + Class clazz = findLoadedClass(name); + if (clazz != null) { + return clazz; + } + return defineClass(name, bytes, 0, bytes.length, cs); + } finally { + if (initialLock) { + unlockClassName(name); + } + } + } return defineClass(name, bytes, 0, bytes.length, cs); } finally { if (is != null) @@ -428,4 +460,46 @@ public class CloseableURLClassLoader extends URLClassLoader { result.addAll(Arrays.asList(super.getURLs())); return (URL[]) result.toArray(new URL[result.size()]); } + + private final Map classNameLocks = new HashMap(5); + private final Object pkgLock = new Object(); + + private boolean lockClassName(String classname) { + synchronized (classNameLocks) { + Object lockingThread = classNameLocks.get(classname); + Thread current = Thread.currentThread(); + if (lockingThread == current) + return false; + boolean previousInterruption = Thread.interrupted(); + try { + while (true) { + if (lockingThread == null) { + classNameLocks.put(classname, current); + return true; + } + + classNameLocks.wait(); + lockingThread = classNameLocks.get(classname); + } + } catch (InterruptedException e) { + current.interrupt(); + throw (LinkageError) new LinkageError(classname).initCause(e); + } finally { + if (previousInterruption) { + current.interrupt(); + } + } + } + } + + private void unlockClassName(String classname) { + synchronized (classNameLocks) { + classNameLocks.remove(classname); + classNameLocks.notifyAll(); + } + } + + protected boolean isRegisteredAsParallel() { + return registeredAsParallel; + } } diff --git a/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/FrameworkLauncher.java b/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/FrameworkLauncher.java index 7cc531eac..424e91b6c 100644 --- a/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/FrameworkLauncher.java +++ b/bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/FrameworkLauncher.java @@ -1,5 +1,9 @@ /******************************************************************************* +<<<<<<< HEAD * Copyright (c) 2005, 2010 Cognos Incorporated, IBM Corporation and others. +======= + * Copyright (c) 2005, 2015 Cognos Incorporated, IBM Corporation and others. +>>>>>>> ef14261... Bug 479396 - deadlock in class loading when deploying at startup * 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 @@ -998,7 +1002,21 @@ public class FrameworkLauncher { * used in its initialization for matching classes before delegating to it's parent. * Sometimes also referred to as a ParentLastClassLoader */ - protected class ChildFirstURLClassLoader extends CloseableURLClassLoader { + protected static class ChildFirstURLClassLoader extends CloseableURLClassLoader { + private static final boolean CHILDFIRST_REGISTERED_AS_PARALLEL; + + static { + boolean registeredAsParallel; + try { + Method parallelCapableMetod = ClassLoader.class.getDeclaredMethod("registerAsParallelCapable", (Class[]) null); //$NON-NLS-1$ + parallelCapableMetod.setAccessible(true); + registeredAsParallel = ((Boolean) parallelCapableMetod.invoke(null, (Object[]) null)).booleanValue(); + } catch (Throwable e) { + // must do everything to avoid failing in clinit + registeredAsParallel = false; + } + CHILDFIRST_REGISTERED_AS_PARALLEL = registeredAsParallel; + } public ChildFirstURLClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent, false); @@ -1014,7 +1032,16 @@ public class FrameworkLauncher { return resource; } - protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (isRegisteredAsParallel()) { + return loadClass0(name, resolve); + } + synchronized (this) { + return loadClass0(name, resolve); + } + } + + private Class loadClass0(String name, boolean resolve) throws ClassNotFoundException { Class clazz = findLoadedClass(name); if (clazz == null) { try { @@ -1038,6 +1065,11 @@ public class FrameworkLauncher { protected PermissionCollection getPermissions(CodeSource codesource) { return allPermissions; } + + protected boolean isRegisteredAsParallel() { + return CHILDFIRST_REGISTERED_AS_PARALLEL; + } + } } |