View | Details | Raw Unified | Return to bug 48903
Collapse All | Expand All

(-)java/org/apache/catalina/loader/LockAwareURLClassLoader.java (+168 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package org.apache.catalina.loader;
18
19
import java.lang.reflect.Method;
20
import java.net.URL;
21
import java.net.URLClassLoader;
22
import java.net.URLStreamHandlerFactory;
23
24
/**
25
 * ClassLoader that is aware of JDK 7 improvements in synchronization policy in
26
 * class loaders, and provides access to the relevant API through reflection.
27
 */
28
public class LockAwareURLClassLoader extends URLClassLoader {
29
30
    private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
31
            .getLog(LockAwareURLClassLoader.class);
32
33
    /**
34
     * If JDK 7 API is available and its use is enabled, this field will contain
35
     * a reference to the <code>ClassLoader.registerAsParallelCapable</code>
36
     * method. To make use of this feature, each derived class has to call this
37
     * method in its static initialization block. If the API is not available,
38
     * or its use is not enabled, this field will be <code>null</code>.
39
     */
40
    protected static final Method registerAsParallelCapableMethod;
41
42
    private static final Method getClassLoadingLockMethod;
43
44
    private static final boolean useClassNameAsLock;
45
46
    static {
47
        final String propertyName = "org.apache.catalina.loader.WebappClassLoader.CLASSLOADER_LOCK_POLICY";
48
        final String defaultValue = "strict";
49
        String propertyValue = System.getProperty(propertyName);
50
        if (!"strict".equals(propertyValue) && !"jdk7".equals(propertyValue)
51
                && !"name".equals(propertyValue)) {
52
            propertyValue = defaultValue;
53
        }
54
        if (log.isDebugEnabled()) {
55
            log.debug(propertyName + ": " + propertyValue);
56
        }
57
        Method registerMethod = null;
58
        Method getLockMethod = null;
59
        boolean success = false;
60
        if ("name".equals(propertyValue)) {
61
            useClassNameAsLock = true;
62
        } else {
63
            useClassNameAsLock = false;
64
        }
65
        if ("jdk7".equals(propertyValue)) {
66
            final Class<?> classLoaderClass = ClassLoader.class;
67
            try {
68
                registerMethod = classLoaderClass
69
                        .getDeclaredMethod("registerAsParallelCapable");
70
                getLockMethod = classLoaderClass.getDeclaredMethod(
71
                        "getClassLoadingLock", String.class);
72
                registerMethod.setAccessible(true);
73
                if (Boolean.TRUE.equals(registerMethod.invoke(null))) {
74
                    success = true;
75
                    if (log.isDebugEnabled()) {
76
                        log.debug("Registered the class loader as parallelCapable");
77
                    }
78
                }
79
            } catch (NoSuchMethodException ex) {
80
                if (log.isDebugEnabled()) {
81
                    log.debug("Cannot register the class loader as parallelCapable");
82
                }
83
            } catch (Exception ex) {
84
                if (log.isDebugEnabled()) {
85
                    log.debug("Failed to register the class loader as parallelCapable",
86
                            ex);
87
                }
88
            }
89
        }
90
        if (success) {
91
            registerAsParallelCapableMethod = registerMethod;
92
            getClassLoadingLockMethod = getLockMethod;
93
        } else {
94
            registerAsParallelCapableMethod = null;
95
            getClassLoadingLockMethod = null;
96
        }
97
    }
98
99
    // ----------------------------------------------------------- Constructors
100
101
    /**
102
     * Calls
103
     * <code>URLClassLoader(URL[], ClassLoader, URLStreamHandlerFactory)</code>.
104
     * 
105
     * @param urls
106
     *            the URLs
107
     * @param parent
108
     *            the parent ClassLoader in the delegation chain
109
     * @param factory
110
     *            the URLStreamHandlerFactory
111
     */
112
    public LockAwareURLClassLoader(URL[] urls, ClassLoader parent,
113
            URLStreamHandlerFactory factory) {
114
        super(urls, parent, factory);
115
    }
116
117
    /**
118
     * Calls <code>URLClassLoader(URL[], ClassLoader)</code>.
119
     * 
120
     * @param urls
121
     *            the URLs
122
     * @param parent
123
     *            the parent ClassLoader in the delegation chain
124
     */
125
    public LockAwareURLClassLoader(URL[] urls, ClassLoader parent) {
126
        super(urls, parent);
127
    }
128
129
    /**
130
     * Calls <code>URLClassLoader(URL[])</code>.
131
     * 
132
     * @param urls
133
     *            the URLs
134
     */
135
    public LockAwareURLClassLoader(URL[] urls) {
136
        super(urls);
137
    }
138
139
    // -------------------------------------------------------
140
141
    /**
142
     * Returns the lock object that has to be used when loading a particular
143
     * class. By default returns a reference to this ClassLoader. This method
144
     * may call JDK 7 method
145
     * <code>ClassLoader.getClassLoadingLock(String)</code> if running with JDK
146
     * 7 or later and Tomcat is configured to do so.
147
     * 
148
     * @param className
149
     *            The name of the class to be loaded
150
     * @return the lock
151
     */
152
    protected Object getTomcatClassLoadingLock(String className) {
153
        Object lock = this;
154
        if (getClassLoadingLockMethod != null) {
155
            try {
156
                lock = getClassLoadingLockMethod.invoke(this, className);
157
            } catch (Exception ex) {
158
                if (log.isDebugEnabled()) {
159
                    log.debug("Failed to call " + getClassLoadingLockMethod,
160
                            ex);
161
                }
162
            }
163
        } else if (useClassNameAsLock) {
164
            lock = className.intern();
165
        }
166
        return lock;
167
    }
168
}
(-)java/org/apache/catalina/loader/ResourceEntry.java (-1 / +1 lines)
Lines 47-53 Link Here
47
    /**
47
    /**
48
     * Loaded class.
48
     * Loaded class.
49
     */
49
     */
50
    public Class loadedClass = null;
50
    public volatile Class loadedClass = null;
51
51
52
52
53
    /**
53
    /**
(-)java/org/apache/catalina/loader/WebappClassLoader.java (-4 / +20 lines)
Lines 32-38 Link Here
32
import java.lang.reflect.Modifier;
32
import java.lang.reflect.Modifier;
33
import java.net.MalformedURLException;
33
import java.net.MalformedURLException;
34
import java.net.URL;
34
import java.net.URL;
35
import java.net.URLClassLoader;
36
import java.security.AccessControlException;
35
import java.security.AccessControlException;
37
import java.security.AccessController;
36
import java.security.AccessController;
38
import java.security.CodeSource;
37
import java.security.CodeSource;
Lines 112-118 Link Here
112
 * @version $Revision$ $Date$
111
 * @version $Revision$ $Date$
113
 */
112
 */
114
public class WebappClassLoader
113
public class WebappClassLoader
115
    extends URLClassLoader
114
    extends LockAwareURLClassLoader
116
    implements Reloader, Lifecycle
115
    implements Reloader, Lifecycle
117
 {
116
 {
118
117
Lines 129-134 Link Here
129
    public static final boolean ENABLE_CLEAR_REFERENCES = 
128
    public static final boolean ENABLE_CLEAR_REFERENCES = 
130
        Boolean.valueOf(System.getProperty("org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES", "true")).booleanValue();
129
        Boolean.valueOf(System.getProperty("org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES", "true")).booleanValue();
131
130
131
    static {
132
        if (LockAwareURLClassLoader.registerAsParallelCapableMethod != null) {
133
            try {
134
                Object success = LockAwareURLClassLoader.registerAsParallelCapableMethod
135
                        .invoke(null);
136
                if (log.isDebugEnabled() && Boolean.TRUE.equals(success)) {
137
                    log.debug("Registered the class loader as parallelCapable");
138
                }
139
            } catch (Exception ex) {
140
                if (log.isDebugEnabled()) {
141
                    log.debug("Failed to register the class loader as parallelCapable",
142
                            ex);
143
                }
144
            }
145
        }
146
    }
147
132
    /**
148
    /**
133
     * @deprecated Not used
149
     * @deprecated Not used
134
     */
150
     */
Lines 1391-1397 Link Here
1391
    public Class loadClass(String name, boolean resolve)
1407
    public Class loadClass(String name, boolean resolve)
1392
        throws ClassNotFoundException {
1408
        throws ClassNotFoundException {
1393
1409
1394
        synchronized (name.intern()) {
1410
        synchronized (getTomcatClassLoadingLock(name)) {
1395
            if (log.isDebugEnabled())
1411
            if (log.isDebugEnabled())
1396
                log.debug("loadClass(" + name + ", " + resolve + ")");
1412
                log.debug("loadClass(" + name + ", " + resolve + ")");
1397
            Class clazz = null;
1413
            Class clazz = null;
Lines 2469-2475 Link Here
2469
        if (clazz != null)
2485
        if (clazz != null)
2470
            return clazz;
2486
            return clazz;
2471
2487
2472
        synchronized (name.intern()) {
2488
        synchronized (getTomcatClassLoadingLock(name)) {
2473
            clazz = entry.loadedClass;
2489
            clazz = entry.loadedClass;
2474
            if (clazz != null)
2490
            if (clazz != null)
2475
                return clazz;
2491
                return clazz;
(-)java/org/apache/jasper/servlet/JasperLoader.java (-3 / +32 lines)
Lines 20-30 Link Here
20
import java.io.IOException;
20
import java.io.IOException;
21
import java.io.InputStream;
21
import java.io.InputStream;
22
import java.net.URL;
22
import java.net.URL;
23
import java.net.URLClassLoader;
24
import java.security.CodeSource;
23
import java.security.CodeSource;
25
import java.security.PermissionCollection;
24
import java.security.PermissionCollection;
26
25
27
import org.apache.jasper.Constants;
26
import org.apache.jasper.Constants;
27
import org.apache.catalina.loader.LockAwareURLClassLoader;
28
28
29
/**
29
/**
30
 * Class loader for loading servlet class files (corresponding to JSP files) 
30
 * Class loader for loading servlet class files (corresponding to JSP files) 
Lines 34-47 Link Here
34
 * @author Harish Prabandham
34
 * @author Harish Prabandham
35
 * @author Jean-Francois Arcand
35
 * @author Jean-Francois Arcand
36
 */
36
 */
37
public class JasperLoader extends URLClassLoader {
37
public class JasperLoader extends LockAwareURLClassLoader {
38
38
39
    private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
40
            .getLog(JasperLoader.class);
41
39
    private PermissionCollection permissionCollection;
42
    private PermissionCollection permissionCollection;
40
    private CodeSource codeSource;
43
    private CodeSource codeSource;
41
    private String className;
44
    private String className;
42
    private ClassLoader parent;
45
    private ClassLoader parent;
43
    private SecurityManager securityManager;
46
    private SecurityManager securityManager;
44
47
48
    static {
49
        if (LockAwareURLClassLoader.registerAsParallelCapableMethod != null) {
50
            try {
51
                Object success = LockAwareURLClassLoader.registerAsParallelCapableMethod
52
                        .invoke(null);
53
                if (log.isDebugEnabled() && Boolean.TRUE.equals(success)) {
54
                    log.debug("Registered the class loader as parallelCapable");
55
                }
56
            } catch (Exception ex) {
57
                if (log.isDebugEnabled()) {
58
                    log.debug("Failed to register the class loader as parallelCapable",
59
                            ex);
60
                }
61
            }
62
        }
63
    }
64
45
    public JasperLoader(URL[] urls, ClassLoader parent,
65
    public JasperLoader(URL[] urls, ClassLoader parent,
46
			PermissionCollection permissionCollection,
66
			PermissionCollection permissionCollection,
47
			CodeSource codeSource) {
67
			CodeSource codeSource) {
Lines 131-137 Link Here
131
	    return clazz;
151
	    return clazz;
132
	}
152
	}
133
153
134
	return findClass(name);
154
        synchronized(getTomcatClassLoadingLock(name)) {
155
            clazz = findLoadedClass(name);
156
            if (clazz == null) {
157
                clazz = findClass(name);
158
            }
159
            if( resolve ) {
160
                resolveClass(clazz);
161
            }
162
            return clazz;
163
        }
135
    }
164
    }
136
165
137
    
166
    
(-)webapps/docs/config/systemprops.xml (+19 lines)
Lines 333-338 Link Here
333
      </p>
333
      </p>
334
    </property>
334
    </property>
335
335
336
    <property
337
    name="org.apache.catalina.loader. WebappClassLoader.CLASSLOADER_LOCK_POLICY">
338
      <p>If equals to <code>jdk7</code>, Tomcat will try to use JDK 7
339
      ClassLoader class API to register <code>WebappClassLoader</code> and
340
      <code>JasperLoader</code> as parallelCapable. If the API is not
341
      available, Tomcat will fallback to the <code>strict</code> policy.
342
      </p>
343
      <p>If equals to <code>strict</code>, the default locking policy will be
344
      used, which is to obtain a lock over the entire ClassLoader instance.
345
      </p>
346
      <p>If equals to <code>name</code>, the name of the class being loaded
347
      will be used as the lock token. This option is experimental, and is
348
      known to cause deadlocks (see bug <bug>48903</bug>), but might work
349
      with some JRE versions and settings.</p>
350
      <p>If not specified, the default value of <code>strict</code> will be
351
      used.
352
      </p>
353
    </property>
354
336
    <property name="catalina.config">
355
    <property name="catalina.config">
337
      <p>The URL for the catalina.properties configuration file.</p>
356
      <p>The URL for the catalina.properties configuration file.</p>
338
    </property>
357
    </property>

Return to bug 48903