4735126: (cl) ClassLoader.loadClass locks all instances in chain when delegating

Added support for parallel-capable class loaders

Reviewed-by: alanb
This commit is contained in:
Valerie Peng 2009-04-06 18:46:20 -07:00
parent 91db470ae6
commit c10f593c2c
15 changed files with 937 additions and 212 deletions

View File

@ -1,5 +1,5 @@
# #
# Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. # Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
@ -135,7 +135,8 @@ SUNWprivate_1.1 {
Java_java_lang_ClassLoader_00024NativeLibrary_find; Java_java_lang_ClassLoader_00024NativeLibrary_find;
Java_java_lang_ClassLoader_00024NativeLibrary_load; Java_java_lang_ClassLoader_00024NativeLibrary_load;
Java_java_lang_ClassLoader_00024NativeLibrary_unload; Java_java_lang_ClassLoader_00024NativeLibrary_unload;
Java_java_lang_ClassLoader_registerNatives; Java_java_lang_ClassLoader_getCaller;
Java_java_lang_ClassLoader_registerNatives;
Java_java_lang_Compiler_registerNatives; Java_java_lang_Compiler_registerNatives;
Java_java_lang_Double_longBitsToDouble; Java_java_lang_Double_longBitsToDouble;
Java_java_lang_Double_doubleToRawLongBits; Java_java_lang_Double_doubleToRawLongBits;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -2846,14 +2846,14 @@ public final
if (loader == null) if (loader == null)
return desiredAssertionStatus0(this); return desiredAssertionStatus0(this);
synchronized(loader) { // If the classloader has been initialized with the assertion
// If the classloader has been initialized with // directives, ask it. Otherwise, ask the VM.
// the assertion directives, ask it. Otherwise, synchronized(loader.assertionLock) {
// ask the VM. if (loader.classAssertionStatus != null) {
return (loader.classAssertionStatus == null ? return loader.desiredAssertionStatus(getName());
desiredAssertionStatus0(this) : }
loader.desiredAssertionStatus(getName()));
} }
return desiredAssertionStatus0(this);
} }
// Retrieves the desired assertion status of this class from the VM // Retrieves the desired assertion status of this class from the VM

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1994-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -40,14 +40,17 @@ import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Hashtable;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.Map; import java.util.Map;
import java.util.Vector; import java.util.Vector;
import java.util.Hashtable;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import sun.misc.ClassFileTransformer; import sun.misc.ClassFileTransformer;
import sun.misc.CompoundEnumeration; import sun.misc.CompoundEnumeration;
import sun.misc.Resource; import sun.misc.Resource;
@ -91,6 +94,17 @@ import sun.security.util.SecurityConstants;
* called the "bootstrap class loader", does not itself have a parent but may * called the "bootstrap class loader", does not itself have a parent but may
* serve as the parent of a <tt>ClassLoader</tt> instance. * serve as the parent of a <tt>ClassLoader</tt> instance.
* *
* <p> Class loaders that support concurrent loading of classes are known as
* <em>parallel capable</em> class loaders and are required to register
* themselves at their class initialization time by invoking the
* {@link
* #registerAsParallelCapable <tt>ClassLoader.registerAsParallelCapable</tt>}
* method. In environments in which the delegation model is not strictly
* hierarchical, class loaders need to be parallel capable, otherise class
* loading can lead to deadlocks because the loader lock is held for the
* duration of the class loading process (see {@link #loadClass
* <tt>loadClass</tt>} methods).
*
* <p> Normally, the Java virtual machine loads classes from the local file * <p> Normally, the Java virtual machine loads classes from the local file
* system in a platform-dependent manner. For example, on UNIX systems, the * system in a platform-dependent manner. For example, on UNIX systems, the
* virtual machine loads classes from the directory defined by the * virtual machine loads classes from the directory defined by the
@ -160,31 +174,51 @@ import sun.security.util.SecurityConstants;
public abstract class ClassLoader { public abstract class ClassLoader {
private static native void registerNatives(); private static native void registerNatives();
// Set of classes which are registered as parallel capable class loaders
private static final Set<Class<? extends ClassLoader>> parallelLoaders
= Collections.newSetFromMap(Collections.synchronizedMap
(new WeakHashMap<Class<? extends ClassLoader>, Boolean>()));
static { static {
registerNatives(); registerNatives();
parallelLoaders.add(ClassLoader.class);
} }
// If initialization succeed this is set to true and security checks will // If initialization succeed this is set to true and security checks will
// succeed. Otherwise the object is not initialized and the object is // succeed. Otherwise the object is not initialized and the object is
// useless. // useless.
private boolean initialized = false; private final boolean initialized;
// The parent class loader for delegation // The parent class loader for delegation
private ClassLoader parent; // Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
// Maps class name to the corresponding lock object when the current
// class loader is parallel capable.
// Note: VM also uses this field to decide if the current class loader
// is parallel capable and the appropriate lock object for class loading.
private final ConcurrentHashMap<String, Object> parallelLockMap;
// Hashtable that maps packages to certs // Hashtable that maps packages to certs
private Hashtable<String, Certificate[]> package2certs private final Map <String, Certificate[]> package2certs;
= new Hashtable<String, Certificate[]>(11);
// Shared among all packages with unsigned classes // Shared among all packages with unsigned classes
Certificate[] nocerts; private static final Certificate[] nocerts = new Certificate[0];
// The classes loaded by this class loader. The only purpose of this table // The classes loaded by this class loader. The only purpose of this table
// is to keep the classes from being GC'ed until the loader is GC'ed. // is to keep the classes from being GC'ed until the loader is GC'ed.
private Vector<Class<?>> classes = new Vector<Class<?>>(); private final Vector<Class<?>> classes = new Vector<Class<?>>();
// The "default" domain. Set as the default ProtectionDomain on newly
// created classes.
private final ProtectionDomain defaultDomain =
new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
null, this, null);
// The initiating protection domains for all classes loaded by this loader // The initiating protection domains for all classes loaded by this loader
private Set<ProtectionDomain> domains = new HashSet<ProtectionDomain>(); private final Set<ProtectionDomain> domains;
// Invoked by the VM to record every loaded class with this loader. // Invoked by the VM to record every loaded class with this loader.
void addClass(Class c) { void addClass(Class c) {
@ -193,7 +227,9 @@ public abstract class ClassLoader {
// The packages defined in this class loader. Each package name is mapped // The packages defined in this class loader. Each package name is mapped
// to its corresponding Package object. // to its corresponding Package object.
private HashMap<String, Package> packages = new HashMap<String, Package>(); // @GuardedBy("itself")
private final HashMap<String, Package> packages =
new HashMap<String, Package>();
/** /**
* Creates a new class loader using the specified parent class loader for * Creates a new class loader using the specified parent class loader for
@ -220,6 +256,19 @@ public abstract class ClassLoader {
security.checkCreateClassLoader(); security.checkCreateClassLoader();
} }
this.parent = parent; this.parent = parent;
if (parallelLoaders.contains(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<String, Object>();
package2certs = new ConcurrentHashMap<String, Certificate[]>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<String, Certificate[]>();
domains = new HashSet<ProtectionDomain>();
assertionLock = this;
}
initialized = true; initialized = true;
} }
@ -244,10 +293,22 @@ public abstract class ClassLoader {
security.checkCreateClassLoader(); security.checkCreateClassLoader();
} }
this.parent = getSystemClassLoader(); this.parent = getSystemClassLoader();
if (parallelLoaders.contains(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<String, Object>();
package2certs = new ConcurrentHashMap<String, Certificate[]>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<String, Certificate[]>();
domains = new HashSet<ProtectionDomain>();
assertionLock = this;
}
initialized = true; initialized = true;
} }
// -- Class -- // -- Class --
/** /**
@ -296,6 +357,10 @@ public abstract class ClassLoader {
* <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
* #findClass(String)}, rather than this method. </p> * #findClass(String)}, rather than this method. </p>
* *
* <p> Unless overridden, this method synchronizes on the result of
* {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
* during the entire class loading process.
*
* @param name * @param name
* The <a href="#name">binary name</a> of the class * The <a href="#name">binary name</a> of the class
* *
@ -307,37 +372,80 @@ public abstract class ClassLoader {
* @throws ClassNotFoundException * @throws ClassNotFoundException
* If the class could not be found * If the class could not be found
*/ */
protected synchronized Class<?> loadClass(String name, boolean resolve) protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException throws ClassNotFoundException
{ {
// First, check if the class has already been loaded synchronized (getClassLoadingLock(name)) {
Class c = findLoadedClass(name); // First, check if the class has already been loaded
if (c == null) { Class c = findLoadedClass(name);
try { if (c == null) {
if (parent != null) { try {
c = parent.loadClass(name, false); if (parent != null) {
} else { c = parent.loadClass(name, false);
c = findBootstrapClass0(name); } else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
} }
} catch (ClassNotFoundException e) { }
// If still not found, then invoke findClass in order if (resolve) {
// to find the class. resolveClass(c);
c = findClass(name); }
return c;
}
}
/**
* Returns the lock object for class loading operations.
* For backward compatibility, the default implementation of this method
* behaves as follows. If this ClassLoader object is registered as
* parallel capable, the method returns a dedicated object associated
* with the specified class name. Otherwise, the method returns this
* ClassLoader object. </p>
*
* @param className
* The name of the to-be-loaded class
*
* @return the lock for class loading operations
*
* @throws NullPointerException
* If registered as parallel capable and <tt>className</tt> is null
*
* @see #loadClass(String, boolean)
*
* @since 1.7
*/
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
Object newLock = new Object();
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
} }
} }
if (resolve) { return lock;
resolveClass(c);
}
return c;
} }
// This method is invoked by the virtual machine to load a class. // This method is invoked by the virtual machine to load a class.
private synchronized Class loadClassInternal(String name) private Class loadClassInternal(String name)
throws ClassNotFoundException throws ClassNotFoundException
{ {
return loadClass(name); // For backward compatibility, explicitly lock on 'this' when
// the current class loader is not parallel capable.
if (parallelLockMap == null) {
synchronized (this) {
return loadClass(name);
}
} else {
return loadClass(name);
}
} }
// Invoked by the VM after loading class with this loader.
private void checkPackageAccess(Class cls, ProtectionDomain pd) { private void checkPackageAccess(Class cls, ProtectionDomain pd) {
final SecurityManager sm = System.getSecurityManager(); final SecurityManager sm = System.getSecurityManager();
if (sm != null) { if (sm != null) {
@ -486,31 +594,32 @@ public abstract class ClassLoader {
/* Determine protection domain, and check that: /* Determine protection domain, and check that:
- not define java.* class, - not define java.* class,
- signer of this class matches signers for the rest of the classes in package. - signer of this class matches signers for the rest of the classes in
package.
*/ */
private ProtectionDomain preDefineClass(String name, private ProtectionDomain preDefineClass(String name,
ProtectionDomain protectionDomain) ProtectionDomain pd)
{ {
if (!checkName(name)) if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name); throw new NoClassDefFoundError("IllegalName: " + name);
if ((name != null) && name.startsWith("java.")) { if ((name != null) && name.startsWith("java.")) {
throw new SecurityException("Prohibited package name: " + throw new SecurityException
name.substring(0, name.lastIndexOf('.'))); ("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
} }
if (protectionDomain == null) { if (pd == null) {
protectionDomain = getDefaultDomain(); pd = defaultDomain;
} }
if (name != null) if (name != null) checkCerts(name, pd.getCodeSource());
checkCerts(name, protectionDomain.getCodeSource());
return protectionDomain; return pd;
} }
private String defineClassSourceLocation(ProtectionDomain protectionDomain) private String defineClassSourceLocation(ProtectionDomain pd)
{ {
CodeSource cs = protectionDomain.getCodeSource(); CodeSource cs = pd.getCodeSource();
String source = null; String source = null;
if (cs != null && cs.getLocation() != null) { if (cs != null && cs.getLocation() != null) {
source = cs.getLocation().toString(); source = cs.getLocation().toString();
@ -519,14 +628,15 @@ public abstract class ClassLoader {
} }
private Class defineTransformedClass(String name, byte[] b, int off, int len, private Class defineTransformedClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain, ProtectionDomain pd,
ClassFormatError cfe, String source) ClassFormatError cfe, String source)
throws ClassFormatError throws ClassFormatError
{ {
// Class format error - try to transform the bytecode and // Class format error - try to transform the bytecode and
// define the class again // define the class again
// //
ClassFileTransformer[] transformers = ClassFileTransformer.getTransformers(); ClassFileTransformer[] transformers =
ClassFileTransformer.getTransformers();
Class c = null; Class c = null;
if (transformers != null) { if (transformers != null) {
@ -535,7 +645,7 @@ public abstract class ClassLoader {
// Transform byte code using transformer // Transform byte code using transformer
byte[] tb = transformer.transform(b, off, len); byte[] tb = transformer.transform(b, off, len);
c = defineClass1(name, tb, 0, tb.length, c = defineClass1(name, tb, 0, tb.length,
protectionDomain, source); pd, source);
break; break;
} catch (ClassFormatError cfe2) { } catch (ClassFormatError cfe2) {
// If ClassFormatError occurs, try next transformer // If ClassFormatError occurs, try next transformer
@ -552,11 +662,10 @@ public abstract class ClassLoader {
return c; return c;
} }
private void postDefineClass(Class c, ProtectionDomain protectionDomain) private void postDefineClass(Class c, ProtectionDomain pd)
{ {
if (protectionDomain.getCodeSource() != null) { if (pd.getCodeSource() != null) {
Certificate certs[] = Certificate certs[] = pd.getCodeSource().getCertificates();
protectionDomain.getCodeSource().getCertificates();
if (certs != null) if (certs != null)
setSigners(c, certs); setSigners(c, certs);
} }
@ -641,7 +750,8 @@ public abstract class ClassLoader {
try { try {
c = defineClass1(name, b, off, len, protectionDomain, source); c = defineClass1(name, b, off, len, protectionDomain, source);
} catch (ClassFormatError cfe) { } catch (ClassFormatError cfe) {
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source); c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
source);
} }
postDefineClass(c, protectionDomain); postDefineClass(c, protectionDomain);
@ -656,10 +766,10 @@ public abstract class ClassLoader {
* specified in the documentation for {@link #defineClass(String, byte[], * specified in the documentation for {@link #defineClass(String, byte[],
* int, int)}. Before the class can be used it must be resolved. * int, int)}. Before the class can be used it must be resolved.
* *
* <p>The rules about the first class defined in a package determining the set of * <p>The rules about the first class defined in a package determining the
* certificates for the package, and the restrictions on class names are identical * set of certificates for the package, and the restrictions on class names
* to those specified in the documentation for {@link #defineClass(String, byte[], * are identical to those specified in the documentation for {@link
* int, int, ProtectionDomain)}. * #defineClass(String, byte[], int, int, ProtectionDomain)}.
* *
* <p> An invocation of this method of the form * <p> An invocation of this method of the form
* <i>cl</i><tt>.defineClass(</tt><i>name</i><tt>,</tt> * <i>cl</i><tt>.defineClass(</tt><i>name</i><tt>,</tt>
@ -668,12 +778,13 @@ public abstract class ClassLoader {
* *
* <blockquote><tt> * <blockquote><tt>
* ...<br> * ...<br>
* byte[] temp = new byte[</tt><i>bBuffer</i><tt>.{@link java.nio.ByteBuffer#remaining * byte[] temp = new byte[</tt><i>bBuffer</i><tt>.{@link
* remaining}()];<br> * java.nio.ByteBuffer#remaining remaining}()];<br>
* </tt><i>bBuffer</i><tt>.{@link java.nio.ByteBuffer#get(byte[]) * </tt><i>bBuffer</i><tt>.{@link java.nio.ByteBuffer#get(byte[])
* get}(temp);<br> * get}(temp);<br>
* return {@link #defineClass(String, byte[], int, int, ProtectionDomain) * return {@link #defineClass(String, byte[], int, int, ProtectionDomain)
* </tt><i>cl</i><tt>.defineClass}(</tt><i>name</i><tt>, temp, 0, temp.length, </tt><i>pd</i><tt>);<br> * </tt><i>cl</i><tt>.defineClass}(</tt><i>name</i><tt>, temp, 0,
* temp.length, </tt><i>pd</i><tt>);<br>
* </tt></blockquote> * </tt></blockquote>
* *
* @param name * @param name
@ -682,9 +793,9 @@ public abstract class ClassLoader {
* *
* @param b * @param b
* The bytes that make up the class data. The bytes from positions * The bytes that make up the class data. The bytes from positions
* <tt>b.position()</tt> through <tt>b.position() + b.limit() -1 </tt> * <tt>b.position()</tt> through <tt>b.position() + b.limit() -1
* should have the format of a valid class file as defined by the <a * </tt> should have the format of a valid class file as defined by
* href="http://java.sun.com/docs/books/vmspec/">Java Virtual * the <a href="http://java.sun.com/docs/books/vmspec/">Java Virtual
* Machine Specification</a>. * Machine Specification</a>.
* *
* @param protectionDomain * @param protectionDomain
@ -738,11 +849,13 @@ public abstract class ClassLoader {
String source = defineClassSourceLocation(protectionDomain); String source = defineClassSourceLocation(protectionDomain);
try { try {
c = defineClass2(name, b, b.position(), len, protectionDomain, source); c = defineClass2(name, b, b.position(), len, protectionDomain,
source);
} catch (ClassFormatError cfe) { } catch (ClassFormatError cfe) {
byte[] tb = new byte[len]; byte[] tb = new byte[len];
b.get(tb); // get bytes out of byte buffer. b.get(tb); // get bytes out of byte buffer.
c = defineTransformedClass(name, tb, 0, len, protectionDomain, cfe, source); c = defineTransformedClass(name, tb, 0, len, protectionDomain, cfe,
source);
} }
postDefineClass(c, protectionDomain); postDefineClass(c, protectionDomain);
@ -769,33 +882,29 @@ public abstract class ClassLoader {
return true; return true;
} }
private synchronized void checkCerts(String name, CodeSource cs) { private void checkCerts(String name, CodeSource cs) {
int i = name.lastIndexOf('.'); int i = name.lastIndexOf('.');
String pname = (i == -1) ? "" : name.substring(0, i); String pname = (i == -1) ? "" : name.substring(0, i);
Certificate[] pcerts = package2certs.get(pname);
if (pcerts == null) {
// first class in this package gets to define which
// certificates must be the same for all other classes
// in this package
if (cs != null) {
pcerts = cs.getCertificates();
}
if (pcerts == null) {
if (nocerts == null)
nocerts = new Certificate[0];
pcerts = nocerts;
}
package2certs.put(pname, pcerts);
} else {
Certificate[] certs = null;
if (cs != null) {
certs = cs.getCertificates();
}
if (!compareCerts(pcerts, certs)) { Certificate[] certs = null;
throw new SecurityException("class \""+ name + if (cs != null) {
"\"'s signer information does not match signer information of other classes in the same package"); certs = cs.getCertificates();
}
Certificate[] pcerts = null;
if (parallelLockMap == null) {
synchronized (this) {
pcerts = package2certs.get(pname);
if (pcerts == null) {
package2certs.put(pname, (certs == null? nocerts:certs));
}
} }
} else {
pcerts = ((ConcurrentHashMap<String, Certificate[]>)package2certs).
putIfAbsent(pname, (certs == null? nocerts:certs));
}
if (pcerts != null && !compareCerts(pcerts, certs)) {
throw new SecurityException("class \""+ name +
"\"'s signer information does not match signer information of other classes in the same package");
} }
} }
@ -1075,6 +1184,47 @@ public abstract class ClassLoader {
return java.util.Collections.emptyEnumeration(); return java.util.Collections.emptyEnumeration();
} }
// index 0: java.lang.ClassLoader.class
// index 1: the immediate caller of index 0.
// index 2: the immediate caller of index 1.
private static native Class<? extends ClassLoader> getCaller(int index);
/**
* Registers the caller class loader as parallel capable.
* In order for the registration to succeed, all super classes
* of the caller class loader must also be registered as
* parallel capable when this method is called. </p>
* Note that once a class loader is registered as
* parallel capable, there is no way to change it back.
* In addition, registration should be done statically before
* any instance of the caller classloader being constructed. </p>
*
* @return true if the caller is successfully registered as
* parallel capable and false if otherwise.
*
* @since 1.7
*/
protected static boolean registerAsParallelCapable() {
Class<? extends ClassLoader> caller = getCaller(1);
Class superCls = caller.getSuperclass();
boolean result = false;
// Explicit synchronization needed for composite action
synchronized (parallelLoaders) {
if (!parallelLoaders.contains(caller)) {
if (parallelLoaders.contains(superCls)) {
// register the immediate caller as parallel capable
// if and only if all of its super classes are.
// Note: given current classloading sequence, if
// the immediate super class is parallel capable,
// all the super classes higher up must be too.
result = true;
parallelLoaders.add(caller);
}
} else result = true;
}
return result;
}
/** /**
* Find a resource of the specified name from the search path used to load * Find a resource of the specified name from the search path used to load
* classes. This method locates the resource through the system class * classes. This method locates the resource through the system class
@ -1141,7 +1291,8 @@ public abstract class ClassLoader {
private static Enumeration<URL> getBootstrapResources(String name) private static Enumeration<URL> getBootstrapResources(String name)
throws IOException throws IOException
{ {
final Enumeration<Resource> e = getBootstrapClassPath().getResources(name); final Enumeration<Resource> e =
getBootstrapClassPath().getResources(name);
return new Enumeration<URL> () { return new Enumeration<URL> () {
public URL nextElement() { public URL nextElement() {
return e.nextElement().getURL(); return e.nextElement().getURL();
@ -1377,9 +1528,11 @@ public abstract class ClassLoader {
} }
// The class loader for the system // The class loader for the system
// @GuardedBy("ClassLoader.class")
private static ClassLoader scl; private static ClassLoader scl;
// Set to true once the system class loader has been set // Set to true once the system class loader has been set
// @GuardedBy("ClassLoader.class")
private static boolean sclSet; private static boolean sclSet;
@ -1592,19 +1745,6 @@ public abstract class ClassLoader {
} }
} }
// The "default" domain. Set as the default ProtectionDomain on newly
// created classes.
private ProtectionDomain defaultDomain = null;
// Returns (and initializes) the default domain.
private synchronized ProtectionDomain getDefaultDomain() {
if (defaultDomain == null) {
CodeSource cs = new CodeSource(null, (Certificate[]) null);
defaultDomain = new ProtectionDomain(cs, null, this, null);
}
return defaultDomain;
}
// All native library names we've loaded. // All native library names we've loaded.
private static Vector<String> loadedLibraryNames private static Vector<String> loadedLibraryNames
= new Vector<String>(); = new Vector<String>();
@ -1622,8 +1762,8 @@ public abstract class ClassLoader {
= new Stack<NativeLibrary>(); = new Stack<NativeLibrary>();
// The paths searched for libraries // The paths searched for libraries
static private String usr_paths[]; private static String usr_paths[];
static private String sys_paths[]; private static String sys_paths[];
private static String[] initializePath(String propname) { private static String[] initializePath(String propname) {
String ldpath = System.getProperty(propname, ""); String ldpath = System.getProperty(propname, "");
@ -1803,7 +1943,10 @@ public abstract class ClassLoader {
// -- Assertion management -- // -- Assertion management --
final Object assertionLock;
// The default toggle for assertion checking. // The default toggle for assertion checking.
// @GuardedBy("assertionLock")
private boolean defaultAssertionStatus = false; private boolean defaultAssertionStatus = false;
// Maps String packageName to Boolean package default assertion status Note // Maps String packageName to Boolean package default assertion status Note
@ -1811,12 +1954,14 @@ public abstract class ClassLoader {
// is null then we are delegating assertion status queries to the VM, i.e., // is null then we are delegating assertion status queries to the VM, i.e.,
// none of this ClassLoader's assertion status modification methods have // none of this ClassLoader's assertion status modification methods have
// been invoked. // been invoked.
// @GuardedBy("assertionLock")
private Map<String, Boolean> packageAssertionStatus = null; private Map<String, Boolean> packageAssertionStatus = null;
// Maps String fullyQualifiedClassName to Boolean assertionStatus If this // Maps String fullyQualifiedClassName to Boolean assertionStatus If this
// field is null then we are delegating assertion status queries to the VM, // field is null then we are delegating assertion status queries to the VM,
// i.e., none of this ClassLoader's assertion status modification methods // i.e., none of this ClassLoader's assertion status modification methods
// have been invoked. // have been invoked.
// @GuardedBy("assertionLock")
Map<String, Boolean> classAssertionStatus = null; Map<String, Boolean> classAssertionStatus = null;
/** /**
@ -1834,11 +1979,13 @@ public abstract class ClassLoader {
* *
* @since 1.4 * @since 1.4
*/ */
public synchronized void setDefaultAssertionStatus(boolean enabled) { public void setDefaultAssertionStatus(boolean enabled) {
if (classAssertionStatus == null) synchronized (assertionLock) {
initializeJavaAssertionMaps(); if (classAssertionStatus == null)
initializeJavaAssertionMaps();
defaultAssertionStatus = enabled; defaultAssertionStatus = enabled;
}
} }
/** /**
@ -1878,13 +2025,14 @@ public abstract class ClassLoader {
* *
* @since 1.4 * @since 1.4
*/ */
public synchronized void setPackageAssertionStatus(String packageName, public void setPackageAssertionStatus(String packageName,
boolean enabled) boolean enabled) {
{ synchronized (assertionLock) {
if (packageAssertionStatus == null) if (packageAssertionStatus == null)
initializeJavaAssertionMaps(); initializeJavaAssertionMaps();
packageAssertionStatus.put(packageName, enabled); packageAssertionStatus.put(packageName, enabled);
}
} }
/** /**
@ -1909,13 +2057,13 @@ public abstract class ClassLoader {
* *
* @since 1.4 * @since 1.4
*/ */
public synchronized void setClassAssertionStatus(String className, public void setClassAssertionStatus(String className, boolean enabled) {
boolean enabled) synchronized (assertionLock) {
{ if (classAssertionStatus == null)
if (classAssertionStatus == null) initializeJavaAssertionMaps();
initializeJavaAssertionMaps();
classAssertionStatus.put(className, enabled); classAssertionStatus.put(className, enabled);
}
} }
/** /**
@ -1928,15 +2076,16 @@ public abstract class ClassLoader {
* *
* @since 1.4 * @since 1.4
*/ */
public synchronized void clearAssertionStatus() { public void clearAssertionStatus() {
/* /*
* Whether or not "Java assertion maps" are initialized, set * Whether or not "Java assertion maps" are initialized, set
* them to empty maps, effectively ignoring any present settings. * them to empty maps, effectively ignoring any present settings.
*/ */
classAssertionStatus = new HashMap<String, Boolean>(); synchronized (assertionLock) {
packageAssertionStatus = new HashMap<String, Boolean>(); classAssertionStatus = new HashMap<String, Boolean>();
packageAssertionStatus = new HashMap<String, Boolean>();
defaultAssertionStatus = false; defaultAssertionStatus = false;
}
} }
/** /**
@ -1961,39 +2110,40 @@ public abstract class ClassLoader {
* *
* @since 1.4 * @since 1.4
*/ */
synchronized boolean desiredAssertionStatus(String className) { boolean desiredAssertionStatus(String className) {
Boolean result; synchronized (assertionLock) {
// assert classAssertionStatus != null;
// assert packageAssertionStatus != null;
// assert classAssertionStatus != null; // Check for a class entry
// assert packageAssertionStatus != null; Boolean result = classAssertionStatus.get(className);
// Check for a class entry
result = classAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
// Check for most specific package entry
int dotIndex = className.lastIndexOf(".");
if (dotIndex < 0) { // default package
result = packageAssertionStatus.get(null);
if (result != null) if (result != null)
return result.booleanValue(); return result.booleanValue();
}
while(dotIndex > 0) {
className = className.substring(0, dotIndex);
result = packageAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
dotIndex = className.lastIndexOf(".", dotIndex-1);
}
// Return the classloader default // Check for most specific package entry
return defaultAssertionStatus; int dotIndex = className.lastIndexOf(".");
if (dotIndex < 0) { // default package
result = packageAssertionStatus.get(null);
if (result != null)
return result.booleanValue();
}
while(dotIndex > 0) {
className = className.substring(0, dotIndex);
result = packageAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
dotIndex = className.lastIndexOf(".", dotIndex-1);
}
// Return the classloader default
return defaultAssertionStatus;
}
} }
// Set up the assertions with information provided by the VM. // Set up the assertions with information provided by the VM.
// Note: Should only be called inside a synchronized block
private void initializeJavaAssertionMaps() { private void initializeJavaAssertionMaps() {
// assert Thread.holdsLock(this); // assert Thread.holdsLock(assertionLock);
classAssertionStatus = new HashMap<String, Boolean>(); classAssertionStatus = new HashMap<String, Boolean>();
packageAssertionStatus = new HashMap<String, Boolean>(); packageAssertionStatus = new HashMap<String, Boolean>();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -74,10 +74,10 @@ import sun.security.util.SecurityConstants;
*/ */
public class URLClassLoader extends SecureClassLoader implements Closeable { public class URLClassLoader extends SecureClassLoader implements Closeable {
/* The search path for classes and resources */ /* The search path for classes and resources */
URLClassPath ucp; private final URLClassPath ucp;
/* The context to be used when loading classes and resources */ /* The context to be used when loading classes and resources */
private AccessControlContext acc; private final AccessControlContext acc;
/** /**
* Constructs a new URLClassLoader for the given URLs. The URLs will be * Constructs a new URLClassLoader for the given URLs. The URLs will be
@ -105,7 +105,19 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
security.checkCreateClassLoader(); security.checkCreateClassLoader();
} }
ucp = new URLClassPath(urls); ucp = new URLClassPath(urls);
acc = AccessController.getContext(); this.acc = AccessController.getContext();
}
URLClassLoader(URL[] urls, ClassLoader parent,
AccessControlContext acc) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = acc;
} }
/** /**
@ -136,7 +148,18 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
security.checkCreateClassLoader(); security.checkCreateClassLoader();
} }
ucp = new URLClassPath(urls); ucp = new URLClassPath(urls);
acc = AccessController.getContext(); this.acc = AccessController.getContext();
}
URLClassLoader(URL[] urls, AccessControlContext acc) {
super();
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = acc;
} }
/** /**
@ -599,17 +622,14 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
public static URLClassLoader newInstance(final URL[] urls, public static URLClassLoader newInstance(final URL[] urls,
final ClassLoader parent) { final ClassLoader parent) {
// Save the caller's context // Save the caller's context
AccessControlContext acc = AccessController.getContext(); final AccessControlContext acc = AccessController.getContext();
// Need a privileged block to create the class loader // Need a privileged block to create the class loader
URLClassLoader ucl = AccessController.doPrivileged( URLClassLoader ucl = AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() { new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() { public URLClassLoader run() {
return new FactoryURLClassLoader(urls, parent); return new FactoryURLClassLoader(urls, parent, acc);
} }
}); });
// Now set the context on the loader using the one we saved,
// not the one inside the privileged block...
ucl.acc = acc;
return ucl; return ucl;
} }
@ -626,18 +646,14 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
*/ */
public static URLClassLoader newInstance(final URL[] urls) { public static URLClassLoader newInstance(final URL[] urls) {
// Save the caller's context // Save the caller's context
AccessControlContext acc = AccessController.getContext(); final AccessControlContext acc = AccessController.getContext();
// Need a privileged block to create the class loader // Need a privileged block to create the class loader
URLClassLoader ucl = AccessController.doPrivileged( URLClassLoader ucl = AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() { new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() { public URLClassLoader run() {
return new FactoryURLClassLoader(urls); return new FactoryURLClassLoader(urls, acc);
} }
}); });
// Now set the context on the loader using the one we saved,
// not the one inside the privileged block...
ucl.acc = acc;
return ucl; return ucl;
} }
@ -649,20 +665,26 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
} }
} }
); );
ClassLoader.registerAsParallelCapable();
} }
} }
final class FactoryURLClassLoader extends URLClassLoader { final class FactoryURLClassLoader extends URLClassLoader {
FactoryURLClassLoader(URL[] urls, ClassLoader parent) { static {
super(urls, parent); ClassLoader.registerAsParallelCapable();
} }
FactoryURLClassLoader(URL[] urls) { FactoryURLClassLoader(URL[] urls, ClassLoader parent,
super(urls); AccessControlContext acc) {
super(urls, parent, acc);
} }
public final synchronized Class loadClass(String name, boolean resolve) FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
super(urls, acc);
}
public final Class loadClass(String name, boolean resolve)
throws ClassNotFoundException throws ClassNotFoundException
{ {
// First check if we have permission to access the package. This // First check if we have permission to access the package. This

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -45,14 +45,19 @@ public class SecureClassLoader extends ClassLoader {
* succeed. Otherwise the object is not initialized and the object is * succeed. Otherwise the object is not initialized and the object is
* useless. * useless.
*/ */
private boolean initialized = false; private final boolean initialized;
// HashMap that maps CodeSource to ProtectionDomain // HashMap that maps CodeSource to ProtectionDomain
private HashMap<CodeSource, ProtectionDomain> pdcache = // @GuardedBy("pdcache")
private final HashMap<CodeSource, ProtectionDomain> pdcache =
new HashMap<CodeSource, ProtectionDomain>(11); new HashMap<CodeSource, ProtectionDomain>(11);
private static final Debug debug = Debug.getInstance("scl"); private static final Debug debug = Debug.getInstance("scl");
static {
ClassLoader.registerAsParallelCapable();
}
/** /**
* Creates a new SecureClassLoader using the specified parent * Creates a new SecureClassLoader using the specified parent
* class loader for delegation. * class loader for delegation.
@ -136,10 +141,7 @@ public class SecureClassLoader extends ClassLoader {
byte[] b, int off, int len, byte[] b, int off, int len,
CodeSource cs) CodeSource cs)
{ {
if (cs == null) return defineClass(name, b, off, len, getProtectionDomain(cs));
return defineClass(name, b, off, len);
else
return defineClass(name, b, off, len, getProtectionDomain(cs));
} }
/** /**
@ -172,10 +174,7 @@ public class SecureClassLoader extends ClassLoader {
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b, protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
CodeSource cs) CodeSource cs)
{ {
if (cs == null) return defineClass(name, b, getProtectionDomain(cs));
return defineClass(name, b, (ProtectionDomain)null);
else
return defineClass(name, b, getProtectionDomain(cs));
} }
/** /**
@ -209,12 +208,10 @@ public class SecureClassLoader extends ClassLoader {
if (pd == null) { if (pd == null) {
PermissionCollection perms = getPermissions(cs); PermissionCollection perms = getPermissions(cs);
pd = new ProtectionDomain(cs, perms, this, null); pd = new ProtectionDomain(cs, perms, this, null);
if (pd != null) { pdcache.put(cs, pd);
pdcache.put(cs, pd); if (debug != null) {
if (debug != null) { debug.println(" getPermissions "+ pd);
debug.println(" getPermissions "+ pd); debug.println("");
debug.println("");
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -120,7 +120,10 @@ public class Launcher {
* The class loader used for loading installed extensions. * The class loader used for loading installed extensions.
*/ */
static class ExtClassLoader extends URLClassLoader { static class ExtClassLoader extends URLClassLoader {
private File[] dirs;
static {
ClassLoader.registerAsParallelCapable();
}
/** /**
* create an ExtClassLoader. The ExtClassLoader is created * create an ExtClassLoader. The ExtClassLoader is created
@ -146,12 +149,12 @@ public class Launcher {
} }
}); });
} catch (java.security.PrivilegedActionException e) { } catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException(); throw (IOException) e.getException();
} }
} }
void addExtURL(URL url) { void addExtURL(URL url) {
super.addURL(url); super.addURL(url);
} }
/* /*
@ -159,7 +162,6 @@ public class Launcher {
*/ */
public ExtClassLoader(File[] dirs) throws IOException { public ExtClassLoader(File[] dirs) throws IOException {
super(getExtURLs(dirs), null, factory); super(getExtURLs(dirs), null, factory);
this.dirs = dirs;
} }
private static File[] getExtDirs() { private static File[] getExtDirs() {
@ -206,20 +208,27 @@ public class Launcher {
*/ */
public String findLibrary(String name) { public String findLibrary(String name) {
name = System.mapLibraryName(name); name = System.mapLibraryName(name);
for (int i = 0; i < dirs.length; i++) { URL[] urls = super.getURLs();
// Look in architecture-specific subdirectory first File prevDir = null;
String arch = System.getProperty("os.arch"); for (int i = 0; i < urls.length; i++) {
if (arch != null) { // Get the ext directory from the URL
File file = new File(new File(dirs[i], arch), name); File dir = new File(urls[i].getPath()).getParentFile();
if (dir != null && !dir.equals(prevDir)) {
// Look in architecture-specific subdirectory first
String arch = System.getProperty("os.arch");
if (arch != null) {
File file = new File(new File(dir, arch), name);
if (file.exists()) {
return file.getAbsolutePath();
}
}
// Then check the extension directory
File file = new File(dir, name);
if (file.exists()) { if (file.exists()) {
return file.getAbsolutePath(); return file.getAbsolutePath();
} }
} }
// Then check the extension directory prevDir = dir;
File file = new File(dirs[i], name);
if (file.exists()) {
return file.getAbsolutePath();
}
} }
return null; return null;
} }
@ -248,6 +257,10 @@ public class Launcher {
*/ */
static class AppClassLoader extends URLClassLoader { static class AppClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public static ClassLoader getAppClassLoader(final ClassLoader extcl) public static ClassLoader getAppClassLoader(final ClassLoader extcl)
throws IOException throws IOException
{ {
@ -281,7 +294,7 @@ public class Launcher {
/** /**
* Override loadClass so we can checkPackageAccess. * Override loadClass so we can checkPackageAccess.
*/ */
public synchronized Class loadClass(String name, boolean resolve) public Class loadClass(String name, boolean resolve)
throws ClassNotFoundException throws ClassNotFoundException
{ {
int i = name.lastIndexOf('.'); int i = name.lastIndexOf('.');

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1996-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -437,3 +437,21 @@ Java_java_lang_ClassLoader_00024NativeLibrary_find
(*env)->ReleaseStringUTFChars(env, name, cname); (*env)->ReleaseStringUTFChars(env, name, cname);
return res; return res;
} }
JNIEXPORT jobject JNICALL
Java_java_lang_ClassLoader_getCaller(JNIEnv *env, jclass cls, jint index)
{
jobjectArray jcallerStack;
int len;
jcallerStack = JVM_GetClassContext(env);
if ((*env)->ExceptionCheck(env)) {
return NULL;
}
len = (*env)->GetArrayLength(env, jcallerStack);
if (index < len) {
return (*env)->GetObjectArrayElement(env, jcallerStack, index);
}
return NULL;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package comSA;
public class Alice extends comSB.SupAlice {
static {
System.out.println("comSA.Alice loaded");
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package comSB;
public class Bob extends comSA.SupBob {
static {
System.out.println("comSB.Bob loaded");
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.*;
import java.lang.reflect.*;
public class DelegatingLoader extends URLClassLoader {
private DelegatingLoader delLoader;
private String[] delClasses;
static {
boolean supportParallel = false;
try {
Class c = Class.forName("java.lang.ClassLoader");
Method m = c.getDeclaredMethod("registerAsParallelCapable",
new Class[0]);
m.setAccessible(true);
Object result = (Boolean) m.invoke(null);
if (result instanceof Boolean) {
supportParallel = ((Boolean) result).booleanValue();
} else {
// Should never happen
System.out.println("Error: ClassLoader.registerAsParallelCapable() did not return a boolean!");
System.exit(1);
}
} catch (NoSuchMethodException nsme) {
System.out.println("No ClassLoader.registerAsParallelCapable() API");
} catch (NoSuchMethodError nsme2) {
System.out.println("No ClassLoader.registerAsParallelCapable() API");
} catch (Exception ex) {
ex.printStackTrace();
// Exit immediately to indicate an error
System.exit(1);
}
System.out.println("Parallel ClassLoader registration: " +
supportParallel);
}
public DelegatingLoader(URL urls[]) {
super(urls);
System.out.println("DelegatingLoader using URL " + urls[0]);
}
public void setDelegate(String[] delClasses, DelegatingLoader delLoader) {
this.delClasses = delClasses;
this.delLoader = delLoader;
}
public Class loadClass(String className, boolean resolve)
throws ClassNotFoundException {
for (int i = 0; i < delClasses.length; i++) {
if (delClasses[i].equals(className)) {
Starter.log("Delegating class loading for " + className);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
return null;
}
return delLoader.loadClass(className, resolve);
}
}
Starter.log("Loading local class " + className);
// synchronized (getClassLoadingLock(className)) {
return super.loadClass(className, resolve);
// }
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.net.MalformedURLException;
import java.net.URL;
public class Starter implements Runnable {
private String id;
private DelegatingLoader dl;
private String startClass;
private static DelegatingLoader saLoader, sbLoader;
public static void log(String line) {
System.out.println(line);
}
public static void main(String[] args) {
URL[] urlsa = new URL[1];
URL[] urlsb = new URL[1];
try {
String testDir = System.getProperty("test.classes", ".");
String sep = System.getProperty("file.separator");
urlsa[0] = new URL("file://" + testDir + sep + "SA" + sep);
urlsb[0] = new URL("file://" + testDir + sep + "SB" + sep);
} catch (MalformedURLException e) {
e.printStackTrace();
}
// Set up Classloader delegation hierarchy
saLoader = new DelegatingLoader(urlsa);
sbLoader = new DelegatingLoader(urlsb);
String[] saClasses = { "comSA.SupBob", "comSA.Alice" };
String[] sbClasses = { "comSB.SupAlice", "comSB.Bob" };
saLoader.setDelegate(sbClasses, sbLoader);
sbLoader.setDelegate(saClasses, saLoader);
// test one-way delegate
String testType = args[0];
if (testType.equals("one-way")) {
test("comSA.Alice", "comSA.SupBob");
} else if (testType.equals("cross")) {
// test cross delegate
test("comSA.Alice", "comSB.Bob");
} else {
System.out.println("ERROR: unsupported - " + testType);
}
}
private static void test(String clsForSA, String clsForSB) {
Starter ia = new Starter("SA", saLoader, clsForSA);
Starter ib = new Starter("SB", sbLoader, clsForSB);
new Thread(ia).start();
new Thread(ib).start();
}
public static void sleep() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
log("Thread interrupted");
}
}
private Starter(String id, DelegatingLoader dl, String startClass) {
this.id = id;
this.dl = dl;
this.startClass = startClass;
}
public void run() {
log("Spawned thread " + id + " running");
try {
// To mirror the WAS deadlock, need to ensure class load
// is routed via the VM.
Class.forName(startClass, true, dl);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
log("Thread " + id + " terminating");
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package comSB;
public class SupAlice {
static {
System.out.println("comSB.SupAlice loaded");
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package comSA;
public class SupBob {
static {
System.out.println("comSA.SupBob loaded");
}
}

View File

@ -0,0 +1,105 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
# @test
# @bug 4735126
# @summary (cl) ClassLoader.loadClass locks all instances in chain
# when delegating
#
# @run shell/timeout=10 TestCrossDelegate.sh
# if running by hand on windows, change TESTSRC and TESTCLASSES to "."
if [ "${TESTSRC}" = "" ] ; then
TESTSRC=`pwd`
fi
if [ "${TESTCLASSES}" = "" ] ; then
TESTCLASSES=`pwd`
fi
# if running by hand on windows, change this to appropriate value
if [ "${TESTJAVA}" = "" ] ; then
echo "TESTJAVA not set. Test cannot execute."
echo "FAILED!!!"
exit 1
fi
echo TESTSRC=${TESTSRC}
echo TESTCLASSES=${TESTCLASSES}
echo TESTJAVA=${TESTJAVA}
echo ""
# set platform-specific variables
OS=`uname -s`
case "$OS" in
SunOS )
FS="/"
;;
Linux )
FS="/"
;;
Windows* )
FS="\\"
;;
esac
# compile test
${TESTJAVA}${FS}bin${FS}javac \
-d ${TESTCLASSES} \
${TESTSRC}${FS}Starter.java ${TESTSRC}${FS}DelegatingLoader.java
STATUS=$?
if [ ${STATUS} -ne 0 ]
then
exit ${STATUS}
fi
# set up test
${TESTJAVA}${FS}bin${FS}javac \
-d ${TESTCLASSES}${FS} \
${TESTSRC}${FS}Alice.java ${TESTSRC}${FS}SupBob.java \
${TESTSRC}${FS}Bob.java ${TESTSRC}${FS}SupAlice.java
cd ${TESTCLASSES}
DIRS="SA SB"
for dir in $DIRS
do
if [ -d ${dir} ]; then
rm -rf ${dir}
fi
mkdir ${dir}
mv com${dir} ${dir}
done
# run test
${TESTJAVA}${FS}bin${FS}java \
-verbose:class -XX:+TraceClassLoading -cp . \
-Dtest.classes=${TESTCLASSES} \
Starter cross
# -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass \
# save error status
STATUS=$?
# clean up
rm -rf ${TESTCLASSES}${FS}SA ${TESTCLASSES}${FS}SB
# return
exit ${STATUS}

View File

@ -0,0 +1,105 @@
#
# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
# @test
# @bug 4735126
# @summary (cl) ClassLoader.loadClass locks all instances in chain
# when delegating
#
# @run shell/timeout=10 TestOneWayDelegate.sh
# if running by hand on windows, change TESTSRC and TESTCLASSES to "."
if [ "${TESTSRC}" = "" ] ; then
TESTSRC=`pwd`
fi
if [ "${TESTCLASSES}" = "" ] ; then
TESTCLASSES=`pwd`
fi
# if running by hand on windows, change this to appropriate value
if [ "${TESTJAVA}" = "" ] ; then
echo "TESTJAVA not set. Test cannot execute."
echo "FAILED!!!"
exit 1
fi
echo TESTSRC=${TESTSRC}
echo TESTCLASSES=${TESTCLASSES}
echo TESTJAVA=${TESTJAVA}
echo ""
# set platform-specific variables
OS=`uname -s`
case "$OS" in
SunOS )
FS="/"
;;
Linux )
FS="/"
;;
Windows* )
FS="\\"
;;
esac
# compile test
${TESTJAVA}${FS}bin${FS}javac \
-d ${TESTCLASSES} \
${TESTSRC}${FS}Starter.java ${TESTSRC}${FS}DelegatingLoader.java
STATUS=$?
if [ ${STATUS} -ne 0 ]
then
exit ${STATUS}
fi
# set up test
${TESTJAVA}${FS}bin${FS}javac \
-d ${TESTCLASSES}${FS} \
${TESTSRC}${FS}Alice.java ${TESTSRC}${FS}SupBob.java \
${TESTSRC}${FS}Bob.java ${TESTSRC}${FS}SupAlice.java
cd ${TESTCLASSES}
DIRS="SA SB"
for dir in $DIRS
do
if [ -d ${dir} ]; then
rm -rf ${dir}
fi
mkdir ${dir}
mv com${dir} ${dir}
done
# run test
${TESTJAVA}${FS}bin${FS}java \
-verbose:class -XX:+TraceClassLoading -cp . \
-Dtest.classes=${TESTCLASSES} \
Starter one-way
# -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass \
# save error status
STATUS=$?
# clean up
rm -rf ${TESTCLASSES}${FS}SA ${TESTCLASSES}${FS}SB
# return
exit ${STATUS}