Index: java/org/apache/catalina/loader/LockAwareURLClassLoader.java
===================================================================
--- java/org/apache/catalina/loader/LockAwareURLClassLoader.java (revision 0)
+++ java/org/apache/catalina/loader/LockAwareURLClassLoader.java (revision 0)
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.loader;
+
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+
+/**
+ * ClassLoader that is aware of JDK 7 improvements in synchronization policy in
+ * class loaders, and provides access to the relevant API through reflection.
+ */
+public class LockAwareURLClassLoader extends URLClassLoader {
+
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(LockAwareURLClassLoader.class);
+
+ /**
+ * If JDK 7 API is available and its use is enabled, this field will contain
+ * a reference to the ClassLoader.registerAsParallelCapable
+ * method. To make use of this feature, each derived class has to call this
+ * method in its static initialization block. If the API is not available,
+ * or its use is not enabled, this field will be null
.
+ */
+ protected static final Method registerAsParallelCapableMethod;
+
+ private static final Method getClassLoadingLockMethod;
+
+ private static final boolean useClassNameAsLock;
+
+ static {
+ final String propertyName = "org.apache.catalina.loader.WebappClassLoader.CLASSLOADER_LOCK_POLICY";
+ final String defaultValue = "strict";
+ String propertyValue = System.getProperty(propertyName);
+ if (!"strict".equals(propertyValue) && !"jdk7".equals(propertyValue)
+ && !"name".equals(propertyValue)) {
+ propertyValue = defaultValue;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(propertyName + ": " + propertyValue);
+ }
+ Method registerMethod = null;
+ Method getLockMethod = null;
+ boolean success = false;
+ if ("name".equals(propertyValue)) {
+ useClassNameAsLock = true;
+ } else {
+ useClassNameAsLock = false;
+ }
+ if ("jdk7".equals(propertyValue)) {
+ final Class> classLoaderClass = ClassLoader.class;
+ try {
+ registerMethod = classLoaderClass
+ .getDeclaredMethod("registerAsParallelCapable");
+ getLockMethod = classLoaderClass.getDeclaredMethod(
+ "getClassLoadingLock", String.class);
+ registerMethod.setAccessible(true);
+ if (Boolean.TRUE.equals(registerMethod.invoke(null))) {
+ success = true;
+ if (log.isDebugEnabled()) {
+ log.debug("Registered the class loader as parallelCapable");
+ }
+ }
+ } catch (NoSuchMethodException ex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Cannot register the class loader as parallelCapable");
+ }
+ } catch (Exception ex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to register the class loader as parallelCapable",
+ ex);
+ }
+ }
+ }
+ if (success) {
+ registerAsParallelCapableMethod = registerMethod;
+ getClassLoadingLockMethod = getLockMethod;
+ } else {
+ registerAsParallelCapableMethod = null;
+ getClassLoadingLockMethod = null;
+ }
+ }
+
+ // ----------------------------------------------------------- Constructors
+
+ /**
+ * Calls
+ * URLClassLoader(URL[], ClassLoader, URLStreamHandlerFactory)
.
+ *
+ * @param urls
+ * the URLs
+ * @param parent
+ * the parent ClassLoader in the delegation chain
+ * @param factory
+ * the URLStreamHandlerFactory
+ */
+ public LockAwareURLClassLoader(URL[] urls, ClassLoader parent,
+ URLStreamHandlerFactory factory) {
+ super(urls, parent, factory);
+ }
+
+ /**
+ * Calls URLClassLoader(URL[], ClassLoader)
.
+ *
+ * @param urls
+ * the URLs
+ * @param parent
+ * the parent ClassLoader in the delegation chain
+ */
+ public LockAwareURLClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ }
+
+ /**
+ * Calls URLClassLoader(URL[])
.
+ *
+ * @param urls
+ * the URLs
+ */
+ public LockAwareURLClassLoader(URL[] urls) {
+ super(urls);
+ }
+
+ // -------------------------------------------------------
+
+ /**
+ * Returns the lock object that has to be used when loading a particular
+ * class. By default returns a reference to this ClassLoader. This method
+ * may call JDK 7 method
+ * ClassLoader.getClassLoadingLock(String)
if running with JDK
+ * 7 or later and Tomcat is configured to do so.
+ *
+ * @param className
+ * The name of the class to be loaded
+ * @return the lock
+ */
+ protected Object getTomcatClassLoadingLock(String className) {
+ Object lock = this;
+ if (getClassLoadingLockMethod != null) {
+ try {
+ lock = getClassLoadingLockMethod.invoke(this, className);
+ } catch (Exception ex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to call " + getClassLoadingLockMethod,
+ ex);
+ }
+ }
+ } else if (useClassNameAsLock) {
+ lock = className.intern();
+ }
+ return lock;
+ }
+}
Index: java/org/apache/catalina/loader/ResourceEntry.java
===================================================================
--- java/org/apache/catalina/loader/ResourceEntry.java (revision 922264)
+++ java/org/apache/catalina/loader/ResourceEntry.java (working copy)
@@ -47,7 +47,7 @@
/**
* Loaded class.
*/
- public Class loadedClass = null;
+ public volatile Class loadedClass = null;
/**
Index: java/org/apache/catalina/loader/WebappClassLoader.java
===================================================================
--- java/org/apache/catalina/loader/WebappClassLoader.java (revision 922264)
+++ java/org/apache/catalina/loader/WebappClassLoader.java (working copy)
@@ -32,7 +32,6 @@
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
-import java.net.URLClassLoader;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.CodeSource;
@@ -112,7 +111,7 @@
* @version $Revision$ $Date$
*/
public class WebappClassLoader
- extends URLClassLoader
+ extends LockAwareURLClassLoader
implements Reloader, Lifecycle
{
@@ -129,6 +128,23 @@
public static final boolean ENABLE_CLEAR_REFERENCES =
Boolean.valueOf(System.getProperty("org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES", "true")).booleanValue();
+ static {
+ if (LockAwareURLClassLoader.registerAsParallelCapableMethod != null) {
+ try {
+ Object success = LockAwareURLClassLoader.registerAsParallelCapableMethod
+ .invoke(null);
+ if (log.isDebugEnabled() && Boolean.TRUE.equals(success)) {
+ log.debug("Registered the class loader as parallelCapable");
+ }
+ } catch (Exception ex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to register the class loader as parallelCapable",
+ ex);
+ }
+ }
+ }
+ }
+
/**
* @deprecated Not used
*/
@@ -1391,7 +1407,7 @@
public Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
- synchronized (name.intern()) {
+ synchronized (getTomcatClassLoadingLock(name)) {
if (log.isDebugEnabled())
log.debug("loadClass(" + name + ", " + resolve + ")");
Class clazz = null;
@@ -2469,7 +2485,7 @@
if (clazz != null)
return clazz;
- synchronized (name.intern()) {
+ synchronized (getTomcatClassLoadingLock(name)) {
clazz = entry.loadedClass;
if (clazz != null)
return clazz;
Index: java/org/apache/jasper/servlet/JasperLoader.java
===================================================================
--- java/org/apache/jasper/servlet/JasperLoader.java (revision 922264)
+++ java/org/apache/jasper/servlet/JasperLoader.java (working copy)
@@ -20,11 +20,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
-import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.PermissionCollection;
import org.apache.jasper.Constants;
+import org.apache.catalina.loader.LockAwareURLClassLoader;
/**
* Class loader for loading servlet class files (corresponding to JSP files)
@@ -34,14 +34,34 @@
* @author Harish Prabandham
* @author Jean-Francois Arcand
*/
-public class JasperLoader extends URLClassLoader {
+public class JasperLoader extends LockAwareURLClassLoader {
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(JasperLoader.class);
+
private PermissionCollection permissionCollection;
private CodeSource codeSource;
private String className;
private ClassLoader parent;
private SecurityManager securityManager;
+ static {
+ if (LockAwareURLClassLoader.registerAsParallelCapableMethod != null) {
+ try {
+ Object success = LockAwareURLClassLoader.registerAsParallelCapableMethod
+ .invoke(null);
+ if (log.isDebugEnabled() && Boolean.TRUE.equals(success)) {
+ log.debug("Registered the class loader as parallelCapable");
+ }
+ } catch (Exception ex) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to register the class loader as parallelCapable",
+ ex);
+ }
+ }
+ }
+ }
+
public JasperLoader(URL[] urls, ClassLoader parent,
PermissionCollection permissionCollection,
CodeSource codeSource) {
@@ -131,7 +151,16 @@
return clazz;
}
- return findClass(name);
+ synchronized(getTomcatClassLoadingLock(name)) {
+ clazz = findLoadedClass(name);
+ if (clazz == null) {
+ clazz = findClass(name);
+ }
+ if( resolve ) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ }
}
Index: webapps/docs/config/systemprops.xml
===================================================================
--- webapps/docs/config/systemprops.xml (revision 922264)
+++ webapps/docs/config/systemprops.xml (working copy)
@@ -333,6 +333,25 @@
If equals to jdk7
, Tomcat will try to use JDK 7
+ ClassLoader class API to register WebappClassLoader
and
+ JasperLoader
as parallelCapable. If the API is not
+ available, Tomcat will fallback to the strict
policy.
+
If equals to strict
, the default locking policy will be
+ used, which is to obtain a lock over the entire ClassLoader instance.
+
If equals to name
, the name of the class being loaded
+ will be used as the lock token. This option is experimental, and is
+ known to cause deadlocks (see bug
If not specified, the default value of strict
will be
+ used.
+
The URL for the catalina.properties configuration file.