src/share/classes/java/lang/reflect/Proxy.java
Print this page
@@ -31,14 +31,17 @@
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiFunction;
import sun.misc.ProxyGenerator;
import sun.misc.VM;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.reflect.misc.ReflectUtil;
@@ -230,31 +233,19 @@
*/
public class Proxy implements java.io.Serializable {
private static final long serialVersionUID = -2222568056686623797L;
- /** prefix for all proxy class names */
- private final static String proxyClassNamePrefix = "$Proxy";
-
/** parameter types of a proxy class constructor */
- private final static Class[] constructorParams =
+ private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
- /** maps a class loader to the proxy class cache for that loader */
- private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache
- = new WeakHashMap<>();
-
- /** marks that a particular proxy class is currently being generated */
- private static Object pendingGenerationMarker = new Object();
-
- /** next number to use for generation of unique proxy class names */
- private static long nextUniqueNumber = 0;
- private static Object nextUniqueNumberLock = new Object();
-
- /** set of all generated proxy classes, for isProxyClass implementation */
- private static Map<Class<?>, Void> proxyClasses =
- Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
+ /**
+ * a cache of proxy classes
+ */
+ private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
+ proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/**
* the invocation handler for this proxy instance.
* @serial
*/
@@ -421,148 +412,127 @@
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
- Class<?> proxyClass = null;
-
- /* collect interface names to use as key for proxy class cache */
- String[] interfaceNames = new String[interfaces.length];
-
- // for detecting duplicates
- Set<Class<?>> interfaceSet = new HashSet<>();
-
- for (int i = 0; i < interfaces.length; i++) {
+ Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
+ for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
- String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
- interfaceClass = Class.forName(interfaceName, false, loader);
+ interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
- if (interfaceClass != interfaces[i]) {
+ if (interfaceClass != intf) {
throw new IllegalArgumentException(
- interfaces[i] + " is not visible from class loader");
+ intf + " is not visible from class loader");
}
-
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
-
/*
* Verify that this interface is not a duplicate.
*/
- if (interfaceSet.contains(interfaceClass)) {
+ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
- interfaceSet.add(interfaceClass);
+ }
+
+ return proxyClassCache.get(loader, interfaces);
+ }
- interfaceNames[i] = interfaceName;
+ /**
+ * A key composed of an array of interned strings
+ */
+ private static final class Key {
+ private final String[] strings;
+
+ Key(String[] strings) {
+ this.strings = strings;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ for (String s : strings) hash ^= System.identityHashCode(s);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || obj.getClass() != Key.class) return false;
+ String[] otherStrings = ((Key) obj).strings;
+ if (strings.length != otherStrings.length) return false;
+ for (int i = 0; i < strings.length; i++)
+ if (strings[i] != otherStrings[i]) return false;
+ return true;
+ }
}
+ /**
+ * A function that maps an array of interfaces to a Key composed of interface names.
+ */
+ private static final class KeyFactory
+ implements BiFunction<ClassLoader, Class<?>[], Key>
+ {
+ @Override
+ public Key apply(ClassLoader classLoader, Class<?>[] interfaces) {
+ // collect interface names to use as key for proxy class cache
+ String[] interfaceNames = new String[interfaces.length];
+ for (int i = 0; i < interfaces.length; i++) {
+ interfaceNames[i] = interfaces[i].getName();
+ }
/*
* Using string representations of the proxy interfaces as
* keys in the proxy class cache (instead of their Class
* objects) is sufficient because we require the proxy
* interfaces to be resolvable by name through the supplied
* class loader, and it has the advantage that using a string
* representation of a class makes for an implicit weak
* reference to the class.
*/
- List<String> key = Arrays.asList(interfaceNames);
-
- /*
- * Find or create the proxy class cache for the class loader.
- */
- Map<List<String>, Object> cache;
- synchronized (loaderToCache) {
- cache = loaderToCache.get(loader);
- if (cache == null) {
- cache = new HashMap<>();
- loaderToCache.put(loader, cache);
+ return new Key(interfaceNames);
}
- /*
- * This mapping will remain valid for the duration of this
- * method, without further synchronization, because the mapping
- * will only be removed if the class loader becomes unreachable.
- */
}
- /*
- * Look up the list of interfaces in the proxy class cache using
- * the key. This lookup will result in one of three possible
- * kinds of values:
- * null, if there is currently no proxy class for the list of
- * interfaces in the class loader,
- * the pendingGenerationMarker object, if a proxy class for the
- * list of interfaces is currently being generated,
- * or a weak reference to a Class object, if a proxy class for
- * the list of interfaces has already been generated.
- */
- synchronized (cache) {
- /*
- * Note that we need not worry about reaping the cache for
- * entries with cleared weak references because if a proxy class
- * has been garbage collected, its class loader will have been
- * garbage collected as well, so the entire cache will be reaped
- * from the loaderToCache map.
- */
- do {
- Object value = cache.get(key);
- if (value instanceof Reference) {
- proxyClass = (Class<?>) ((Reference) value).get();
- }
- if (proxyClass != null) {
- // proxy class already generated: return it
- return proxyClass;
- } else if (value == pendingGenerationMarker) {
- // proxy class being generated: wait for it
- try {
- cache.wait();
- } catch (InterruptedException e) {
- /*
- * The class generation that we are waiting for should
- * take a small, bounded time, so we can safely ignore
- * thread interrupts here.
- */
- }
- continue;
- } else {
- /*
- * No proxy class for this list of interfaces has been
- * generated or is being generated, so we will go and
- * generate it now. Mark it as pending generation.
+ /**
+ * A factory function that generates, defines and returns the proxy class given
+ * the ClassLoader and array of interfaces.
*/
- cache.put(key, pendingGenerationMarker);
- break;
- }
- } while (true);
- }
+ private static final class ProxyClassFactory
+ implements BiFunction<ClassLoader, Class<?>[], Class<?>>
+ {
+ // prefix for all proxy class names
+ private static final String proxyClassNamePrefix = "$Proxy";
- try {
+ // next number to use for generation of unique proxy class names
+ private static final AtomicLong nextUniqueNumber = new AtomicLong();
+
+ @Override
+ public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
- for (int i = 0; i < interfaces.length; i++) {
- int flags = interfaces[i].getModifiers();
+ for (Class<?> intf : interfaces) {
+ int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
- String name = interfaces[i].getName();
+ String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
@@ -575,31 +545,23 @@
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
- {
/*
* Choose a name for the proxy class to generate.
*/
- long num;
- synchronized (nextUniqueNumberLock) {
- num = nextUniqueNumber++;
- }
+ long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
- /*
- * Verify that the class loader hasn't already
- * defined a class with the chosen name.
- */
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
- proxyClass = defineClass0(loader, proxyName,
+ return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
@@ -608,31 +570,10 @@
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
- // add to set of all generated proxy classes, for isProxyClass
- proxyClasses.put(proxyClass, null);
-
- } finally {
- /*
- * We must clean up the "pending generation" state of the proxy
- * class cache entry somehow. If a proxy class was successfully
- * generated, store it in the cache (with a weak reference);
- * otherwise, remove the reserved entry. In all cases, notify
- * all waiters on reserved entries in this cache.
- */
- synchronized (cache) {
- if (proxyClass != null) {
- cache.put(key, new WeakReference<Class<?>>(proxyClass));
- } else {
- cache.remove(key);
- }
- cache.notifyAll();
- }
- }
- return proxyClass;
}
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
@@ -755,25 +696,10 @@
sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
}
}
}
- private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
- try {
- return cons.newInstance(new Object[] {h} );
- } catch (IllegalAccessException | InstantiationException e) {
- throw new InternalError(e.toString(), e);
- } catch (InvocationTargetException e) {
- Throwable t = e.getCause();
- if (t instanceof RuntimeException) {
- throw (RuntimeException) t;
- } else {
- throw new InternalError(t.toString(), t);
- }
- }
- }
-
/**
* Returns true if and only if the specified class was dynamically
* generated to be a proxy class using the {@code getProxyClass}
* method or the {@code newProxyInstance} method.
*
@@ -785,15 +711,11 @@
* @return {@code true} if the class is a proxy class and
* {@code false} otherwise
* @throws NullPointerException if {@code cl} is {@code null}
*/
public static boolean isProxyClass(Class<?> cl) {
- if (cl == null) {
- throw new NullPointerException();
- }
-
- return proxyClasses.containsKey(cl);
+ return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
}
/**
* Returns the invocation handler for the specified proxy instance.
*