Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2016-01-13 14:43:26 +0000
committerThomas Watson2016-01-13 20:29:15 +0000
commit0db881dd7d066c74a69841696476b17d034cfbec (patch)
tree58c1f852b96f456120b4cf4552c4111a3e5dc600
parentb6a649a8122e45c63568c00cb522a05a5afd1a32 (diff)
downloadrt.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>
-rw-r--r--bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java88
-rw-r--r--bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/FrameworkLauncher.java36
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;
+ }
+
}
}

Back to the top