Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2016-01-11 16:28:38 +0000
committerThomas Watson2016-01-12 14:15:15 +0000
commit219ee5019313bcc58ae855ae56aa88b7b3b0622a (patch)
tree2bddd74d1afe9597ce0097991dbbb17e2e28fb2f /bundles
parent0bc9249ac4efc3d8626fc05afee0ee9931f0f1c9 (diff)
downloadrt.equinox.bundles-219ee5019313bcc58ae855ae56aa88b7b3b0622a.tar.gz
rt.equinox.bundles-219ee5019313bcc58ae855ae56aa88b7b3b0622a.tar.xz
rt.equinox.bundles-219ee5019313bcc58ae855ae56aa88b7b3b0622a.zip
Bug 479396 - deadlock in class loading when deploying at startupI20160112-1800
Change-Id: I125f389a742a26dd18a03da736bb0de34a5b4871 Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
Diffstat (limited to 'bundles')
-rw-r--r--bundles/org.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF2
-rw-r--r--bundles/org.eclipse.equinox.servletbridge/pom.xml2
-rw-r--r--bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/CloseableURLClassLoader.java87
-rw-r--r--bundles/org.eclipse.equinox.servletbridge/src/org/eclipse/equinox/servletbridge/FrameworkLauncher.java34
4 files changed, 114 insertions, 11 deletions
diff --git a/bundles/org.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF
index b316ed8d..291474ef 100644
--- a/bundles/org.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.servletbridge/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2
Bundle-Name: %bundleName
Bundle-Vendor: %providerName
Bundle-SymbolicName: org.eclipse.equinox.servletbridge;singleton:=true
-Bundle-Version: 1.3.0.qualifier
+Bundle-Version: 1.3.100.qualifier
Bundle-Localization: plugin
Import-Package: javax.servlet;version="2.3.0",
javax.servlet.http;version="2.3.0"
diff --git a/bundles/org.eclipse.equinox.servletbridge/pom.xml b/bundles/org.eclipse.equinox.servletbridge/pom.xml
index 501c90a6..45507386 100644
--- a/bundles/org.eclipse.equinox.servletbridge/pom.xml
+++ b/bundles/org.eclipse.equinox.servletbridge/pom.xml
@@ -19,6 +19,6 @@
</parent>
<groupId>org.eclipse.equinox</groupId>
<artifactId>org.eclipse.equinox.servletbridge</artifactId>
- <version>1.3.0-SNAPSHOT</version>
+ <version>1.3.100-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
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 3d560933..beffba6a 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++) {
@@ -290,12 +306,15 @@ public class CloseableURLClassLoader extends URLClassLoader {
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
String packageName = name.substring(0, lastDot);
- Package pkg = getPackage(packageName);
- if (pkg != null) {
- checkForSealedPackage(pkg, packageName, manifest, connection.getJarFileURL());
- } else {
- definePackage(packageName, manifest, connection.getJarFileURL());
+ 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();
byte[] bytes = new byte[(int) entry.getSize()];
@@ -304,6 +323,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)
@@ -436,4 +469,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 76ce07c1..64bac668 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,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2013 Cognos Incorporated, IBM Corporation and others.
+ * Copyright (c) 2005, 2015 Cognos Incorporated, 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
@@ -1001,7 +1001,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);
@@ -1017,7 +1031,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 {
@@ -1041,6 +1064,11 @@ public class FrameworkLauncher {
protected PermissionCollection getPermissions(CodeSource codesource) {
return allPermissions;
}
+
+ protected boolean isRegisteredAsParallel() {
+ return CHILDFIRST_REGISTERED_AS_PARALLEL;
+ }
+
}
}

Back to the top