[seam-commits] Seam SVN: r10020 - branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/util.
seam-commits at lists.jboss.org
seam-commits at lists.jboss.org
Fri Feb 6 16:52:21 EST 2009
Author: manaRH
Date: 2009-02-06 16:52:21 -0500 (Fri, 06 Feb 2009)
New Revision: 10020
Modified:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/util/ProxyFactory.java
Log:
JBPAPP-1669
Modified: branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/util/ProxyFactory.java
===================================================================
--- branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/util/ProxyFactory.java 2009-02-06 20:24:20 UTC (rev 10019)
+++ branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/util/ProxyFactory.java 2009-02-06 21:52:21 UTC (rev 10020)
@@ -1,11 +1,13 @@
package org.jboss.seam.util;
-/**
- * Derived from javassist MethodProxy.java to create a ProxyFactory that does not generate
+
+/* Derived from javassist MethodProxy.java to create a ProxyFactory that does not generate
* FINAL methods. It is a cut & paste due methods on the javassist version largely
* being static and private, and thus completely non-extensible.
- *
+ */
+
+/*
* Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
+ * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
@@ -18,34 +20,36 @@
* License.
*/
-import java.lang.reflect.Constructor;
+import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
-import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.HashMap;
+import java.util.WeakHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.lang.ref.WeakReference;
import javassist.CannotCompileException;
-import javassist.bytecode.AccessFlag;
-import javassist.bytecode.Bytecode;
-import javassist.bytecode.ClassFile;
-import javassist.bytecode.ConstPool;
-import javassist.bytecode.Descriptor;
-import javassist.bytecode.ExceptionsAttribute;
-import javassist.bytecode.FieldInfo;
-import javassist.bytecode.MethodInfo;
-import javassist.bytecode.Opcode;
+import javassist.bytecode.*;
import javassist.util.proxy.FactoryHelper;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;
import javassist.util.proxy.RuntimeSupport;
+/*
+ * This class is implemented only with the lower-level API of Javassist.
+ * This design decision is for maximizing performance.
+ */
+
/**
* Factory of dynamic proxy classes.
*
@@ -67,7 +71,6 @@
* return proceed.invoke(self, args); // execute the original method.
* }
* };
- * f.setHandler(mi);
* f.setFilter(new MethodFilter() {
* public boolean isHandled(Method m) {
* // ignore finalize()
@@ -76,6 +79,7 @@
* });
* Class c = f.createClass();
* Foo foo = (Foo)c.newInstance();
+ * ((ProxyObject)foo).setHandler(mi);
* </pre></ul>
*
* <p>Then, the following method call will be forwarded to MethodHandler
@@ -86,6 +90,15 @@
* foo.bar();
* </pre></ul>
*
+ * <p>The last three lines of the code shown above can be replaced with a call to
+ * the helper method <code>create</code>, which generates a proxy class, instantiates
+ * it, and sets the method handler of the instance:
+ *
+ * <ul><pre>
+ * :
+ * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
+ * </pre></ul>
+ *
* <p>To change the method handler during runtime,
* execute the following code:
*
@@ -94,7 +107,20 @@
* ((ProxyObject)foo).setHandler(mi2);
* </pre></ul>
*
- * <p>Here is an example of method handler. It does not execute
+ * <p>You can also specify the default method handler:
+ *
+ * <ul><pre>
+ * ProxyFactory f2 = new ProxyFactory();
+ * f2.setSuperclass(Foo.class);
+ * f2.setHandler(mi); // set the default handler
+ * Class c2 = f2.createClass();
+ * </pre></ul>
+ *
+ * <p>The default handler is implicitly attached to an instance of the generated class
+ * <code>c2</code>. Calling <code>setHandler</code> on the instance is not necessary
+ * unless another method handler must be attached to the instance.
+ *
+ * <p>The following code is an example of method handler. It does not execute
* anything except invoking the original method:
*
* <ul><pre>
@@ -106,6 +132,11 @@
* }
* </pre></ul>
*
+ * <p>A proxy object generated by <code>ProxyFactory</code> is serializable
+ * if its super class or interfaces implement a <code>java.io.Serializable</code>.
+ * However, a serialized proxy object will not be compatible with future releases.
+ * The serialization support should be used for short-term storage or RMI.
+ *
* @see MethodHandler
* @since 3.1
*/
@@ -131,6 +162,7 @@
private static final String HOLDER = "_methods_";
private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
+ private static final String METHOD_FILTER_FIELD = "_method_filter";
private static final String HANDLER = "handler";
private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport";
private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
@@ -140,6 +172,61 @@
private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
/**
+ * If true, a generated proxy class is cached and it will be reused
+ * when generating the proxy class with the same properties is requested.
+ * The default value is true.
+ *
+ * @since 3.4
+ */
+ public static boolean useCache = true;
+
+ private static WeakHashMap proxyCache = new WeakHashMap();
+
+ static class CacheKey {
+ String classes;
+ MethodFilter filter;
+ private int hash;
+ WeakReference proxyClass;
+ MethodHandler handler;
+
+ public CacheKey(Class superClass, Class[] interfaces,
+ MethodFilter f, MethodHandler h)
+ {
+ classes = getKey(superClass, interfaces);
+ hash = classes.hashCode();
+ filter = f;
+ handler = h;
+ proxyClass = null;
+ }
+
+ public int hashCode() { return hash; }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof CacheKey) {
+ CacheKey target = (CacheKey)obj;
+ return target.filter == filter && target.handler == handler
+ && target.classes.equals(classes);
+ }
+ else
+ return false;
+ }
+
+ static String getKey(Class superClass, Class[] interfaces) {
+ StringBuffer sbuf = new StringBuffer();
+ if (superClass != null)
+ sbuf.append(superClass.getName());
+ sbuf.append(':');
+ if (interfaces != null) {
+ int len = interfaces.length;
+ for (int i = 0; i < len; i++)
+ sbuf.append(interfaces[i].getName()).append(',');
+ }
+
+ return sbuf.toString();
+ }
+ }
+
+ /**
* Constructs a factory of proxy class.
*/
public ProxyFactory() {
@@ -159,6 +246,13 @@
}
/**
+ * Obtains the super class set by <code>setSuperclass()</code>.
+ *
+ * @since 3.4
+ */
+ public Class getSuperclass() { return superClass; }
+
+ /**
* Sets the interfaces of a proxy class.
*/
public void setInterfaces(Class[] ifs) {
@@ -166,6 +260,13 @@
}
/**
+ * Obtains the interfaces set by <code>setInterfaces</code>.
+ *
+ * @since 3.4
+ */
+ public Class[] getInterfaces() { return interfaces; }
+
+ /**
* Sets a filter that selects the methods that will be controlled by a handler.
*/
public void setFilter(MethodFilter mf) {
@@ -176,36 +277,183 @@
* Generates a proxy class.
*/
public Class createClass() {
- if (thisClass == null)
- try {
- ClassFile cf = make();
- ClassLoader cl = getClassLoader();
- if (writeDirectory != null)
- FactoryHelper.writeFile(cf, writeDirectory);
+ if (thisClass == null) {
+ ClassLoader cl = getClassLoader();
+ synchronized (proxyCache) {
+ if (useCache)
+ createClass2(cl);
+ else
+ createClass3(cl);
+ }
+ }
- thisClass = FactoryHelper.toClass(cf, cl, getDomain());
- setHandler();
+ return thisClass;
+ }
+
+ private void createClass2(ClassLoader cl) {
+ CacheKey key = new CacheKey(superClass, interfaces, methodFilter, handler);
+ /*
+ * Excessive concurrency causes a large memory footprint and slows the
+ * execution speed down (with JDK 1.5). Thus, we use a jumbo lock for
+ * reducing concrrency.
+ */
+ // synchronized (proxyCache) {
+ HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
+ if (cacheForTheLoader == null) {
+ cacheForTheLoader = new HashMap();
+ proxyCache.put(cl, cacheForTheLoader);
+ cacheForTheLoader.put(key, key);
}
- catch (CannotCompileException e) {
- throw new RuntimeException(e.getMessage(), e);
+ else {
+ CacheKey found = (CacheKey)cacheForTheLoader.get(key);
+ if (found == null)
+ cacheForTheLoader.put(key, key);
+ else {
+ key = found;
+ Class c = isValidEntry(key); // no need to synchronize
+ if (c != null) {
+ thisClass = c;
+ return;
+ }
+ }
}
+ // }
- return thisClass;
+ // synchronized (key) {
+ Class c = isValidEntry(key);
+ if (c == null) {
+ createClass3(cl);
+ key.proxyClass = new WeakReference(thisClass);
+ }
+ else
+ thisClass = c;
+ // }
}
+ private Class isValidEntry(CacheKey key) {
+ WeakReference ref = key.proxyClass;
+ if (ref != null) {
+ Class c = (Class)ref.get();
+ if(c != null)
+ return c;
+ }
+
+ return null;
+ }
+
+ private void createClass3(ClassLoader cl) {
+ try {
+ ClassFile cf = make();
+ if (writeDirectory != null)
+ FactoryHelper.writeFile(cf, writeDirectory);
+
+ thisClass = FactoryHelper.toClass(cf, cl, getDomain());
+ setField(DEFAULT_INTERCEPTOR, handler);
+ setField(METHOD_FILTER_FIELD, methodFilter);
+ }
+ catch (CannotCompileException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+
+ }
+
+ private void setField(String fieldName, Object value) {
+ if (thisClass != null && value != null)
+ try {
+ Field f = thisClass.getField(fieldName);
+ // SEAM CHANGE
+ setAccessible(f, true);
+ f.set(null, value);
+ // SEAM CHANGE
+ setAccessible(f, false);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static MethodFilter getFilter(Class clazz) {
+ return (MethodFilter)getField(clazz, METHOD_FILTER_FIELD);
+ }
+
+ static MethodHandler getHandler(Class clazz) {
+ return (MethodHandler)getField(clazz, DEFAULT_INTERCEPTOR);
+ }
+
+ private static Object getField(Class clazz, String fieldName) {
+ try {
+ Field f = clazz.getField(fieldName);
+ f.setAccessible(true);
+ Object value = f.get(null);
+ f.setAccessible(false);
+ return value;
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * A provider of class loaders.
+ *
+ * @see #classLoaderProvider
+ * @since 3.4
+ */
+ public static interface ClassLoaderProvider {
+ /**
+ * Returns a class loader.
+ *
+ * @param pf a proxy factory that is going to obtain a class loader.
+ */
+ public ClassLoader get(ProxyFactory pf);
+ }
+
+ /**
+ * A provider used by <code>createClass()</code> for obtaining
+ * a class loader.
+ * <code>get()</code> on this <code>ClassLoaderProvider</code> object
+ * is called to obtain a class loader.
+ *
+ * <p>The value of this field can be updated for changing the default
+ * implementation.
+ *
+ * <p>Example:
+ * <ul><pre>
+ * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
+ * public ClassLoader get(ProxyFactory pf) {
+ * return Thread.currentThread().getContextClassLoader();
+ * }
+ * };
+ * </pre></ul>
+ *
+ * @since 3.4
+ */
+ public static ClassLoaderProvider classLoaderProvider
+ = new ClassLoaderProvider() {
+ public ClassLoader get(ProxyFactory pf) {
+ return pf.getClassLoader0();
+ }
+ };
+
protected ClassLoader getClassLoader() {
- // return Thread.currentThread().getContextClassLoader();
+ return classLoaderProvider.get(this);
+ }
+
+ protected ClassLoader getClassLoader0() {
ClassLoader loader = null;
if (superClass != null && !superClass.getName().equals("java.lang.Object"))
loader = superClass.getClassLoader();
else if (interfaces != null && interfaces.length > 0)
loader = interfaces[0].getClassLoader();
-
+
if (loader == null) {
loader = getClass().getClassLoader();
// In case javassist is in the endorsed dir
- if (loader == null)
- loader = ClassLoader.getSystemClassLoader();
+ if (loader == null) {
+ loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null)
+ loader = ClassLoader.getSystemClassLoader();
+ }
}
return loader;
@@ -228,7 +476,24 @@
*
* @param paramTypes parameter types for a constructor.
* @param args arguments passed to a constructor.
+ * @param mh the method handler for the proxy class.
+ * @since 3.4
*/
+ public Object create(Class[] paramTypes, Object[] args, MethodHandler mh)
+ throws NoSuchMethodException, IllegalArgumentException,
+ InstantiationException, IllegalAccessException, InvocationTargetException
+ {
+ Object obj = create(paramTypes, args);
+ ((ProxyObject)obj).setHandler(mh);
+ return obj;
+ }
+
+ /**
+ * Creates a proxy class and returns an instance of that class.
+ *
+ * @param paramTypes parameter types for a constructor.
+ * @param args arguments passed to a constructor.
+ */
public Object create(Class[] paramTypes, Object[] args)
throws NoSuchMethodException, IllegalArgumentException,
InstantiationException, IllegalAccessException, InvocationTargetException
@@ -245,24 +510,16 @@
*/
public void setHandler(MethodHandler mi) {
handler = mi;
- setHandler();
+ setField(DEFAULT_INTERCEPTOR, handler);
}
- private void setHandler() {
- if (thisClass != null && handler != null)
- try {
- Field f = thisClass.getField(DEFAULT_INTERCEPTOR);
- f.setAccessible(true);
- f.set(null, handler);
- f.setAccessible(false);
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
+ private static int counter = 0;
+
+ private static synchronized String makeProxyName(String classname) {
+ // SEAM CHANGE
+ return classname + "_$$_javassist_seam_" + counter++;
}
- private static int counter = 0;
-
private ClassFile make() throws CannotCompileException {
String superName, classname;
if (interfaces == null)
@@ -282,8 +539,7 @@
if (Modifier.isFinal(superClass.getModifiers()))
throw new CannotCompileException(superName + " is final");
- // generate a proxy name.
- classname = classname + "_$$_javassist_" + counter++;
+ classname = makeProxyName(classname);
if (classname.startsWith("java."))
classname = "org.javassist.tmp." + classname;
@@ -299,12 +555,25 @@
finfo2.setAccessFlags(AccessFlag.PRIVATE);
cf.addField(finfo2);
+ FieldInfo finfo3 = new FieldInfo(pool, METHOD_FILTER_FIELD,
+ "Ljavassist/util/proxy/MethodFilter;");
+ finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
+ cf.addField(finfo3);
+
HashMap allMethods = getMethods(superClass, interfaces);
+ int size = allMethods.size();
makeConstructors(classname, cf, pool, classname);
int s = overrideMethods(cf, pool, classname, allMethods);
addMethodsHolder(cf, pool, classname, s);
addSetter(classname, cf, pool);
+ try {
+ cf.addMethod(makeWriteReplace(pool));
+ }
+ catch (DuplicateMemberException e) {
+ // writeReplace() is already declared in the super class/interfaces.
+ }
+
thisClass = null;
return cf;
}
@@ -333,6 +602,7 @@
finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
cf.addField(finfo);
MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V");
+ minfo.setAccessFlags(AccessFlag.STATIC);
Bytecode code = new Bytecode(cp, 0, 0);
code.addIconst(size * 2);
code.addAnewarray("java.lang.reflect.Method");
@@ -405,7 +675,8 @@
private void makeConstructors(String thisClassName, ClassFile cf,
ConstPool cp, String classname) throws CannotCompileException
{
- Constructor[] cons = superClass.getDeclaredConstructors();
+ // SEAM CHANGE
+ Constructor[] cons = getDeclaredConstructors(superClass);
for (int i = 0; i < cons.length; i++) {
Constructor c = cons[i];
int mod = c.getModifiers();
@@ -487,7 +758,8 @@
if (parent != null)
getMethods(hash, parent);
- Method[] methods = clazz.getDeclaredMethods();
+ // SEAM CHANGE
+ Method[] methods = getDeclaredMethods(clazz);
for (int i = 0; i < methods.length; i++)
if (!Modifier.isPrivate(methods[i].getModifiers())) {
Method m = methods[i];
@@ -511,27 +783,34 @@
code.addAload(0);
code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
- code.addOpcode(Opcode.DUP);
+ code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
+ code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
code.addOpcode(Opcode.IFNONNULL);
- code.addIndex(7);
- code.addOpcode(Opcode.POP);
+ code.addIndex(10);
+ code.addAload(0);
code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
+ int pc = code.currentPc();
code.addAload(0);
int s = addLoadParameters(code, cons.getParameterTypes(), 1);
code.addInvokespecial(superClass.getName(), "<init>", desc);
code.addOpcode(Opcode.RETURN);
code.setMaxLocals(s + 1);
- minfo.setCodeAttribute(code.toCodeAttribute());
+ CodeAttribute ca = code.toCodeAttribute();
+ minfo.setCodeAttribute(ca);
+
+ StackMapTable.Writer writer = new StackMapTable.Writer(32);
+ writer.sameFrame(pc);
+ ca.setAttribute(writer.toStackMapTable(cp));
return minfo;
}
private static MethodInfo makeDelegator(Method meth, String desc,
ConstPool cp, Class declClass, String delegatorName) {
MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
- // SEAM CHANGE - originally had Modifier.FINAL |
- delegator.setAccessFlags( Modifier.PUBLIC
+ // SEAM CHANGE: remove FINAL
+ delegator.setAccessFlags(Modifier.PUBLIC
| (meth.getModifiers() & ~(Modifier.PRIVATE
| Modifier.PROTECTED
| Modifier.ABSTRACT
@@ -555,8 +834,8 @@
Method meth, String desc, ConstPool cp,
Class declClass, String delegatorName, int index) {
MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
- // SEAM CHANGE - originally had Modifier.FINAL
- forwarder.setAccessFlags( (meth.getModifiers() & ~(Modifier.ABSTRACT
+ // SEAM CHANGE: remove FINAL
+ forwarder.setAccessFlags((meth.getModifiers() & ~(Modifier.ABSTRACT
| Modifier.NATIVE
| Modifier.SYNCHRONIZED)));
setThrows(forwarder, cp, meth);
@@ -588,7 +867,8 @@
callFindMethod(code, "findSuperMethod", arrayVar, origIndex, meth.getName(), desc);
callFindMethod(code, "findMethod", arrayVar, delIndex, delegatorName, desc);
- code.write16bit(pc, code.currentPc() - pc + 1);
+ int pc2 = code.currentPc();
+ code.write16bit(pc, pc2 - pc + 1);
code.addAload(0);
code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE);
code.addAload(0);
@@ -609,7 +889,12 @@
addUnwrapper(code, retType);
addReturn(code, retType);
- forwarder.setCodeAttribute(code.toCodeAttribute());
+ CodeAttribute ca = code.toCodeAttribute();
+ forwarder.setCodeAttribute(ca);
+ StackMapTable.Writer writer = new StackMapTable.Writer(32);
+ writer.appendFrame(pc2, new int[] { StackMapTable.OBJECT },
+ new int[] { cp.addClassInfo(HOLDER_TYPE) });
+ ca.setAttribute(writer.toStackMapTable(cp));
return forwarder;
}
@@ -758,4 +1043,64 @@
else
code.addCheckcast(type.getName());
}
+
+ private static MethodInfo makeWriteReplace(ConstPool cp) {
+ MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;");
+ String[] list = new String[1];
+ list[0] = "java.io.ObjectStreamException";
+ ExceptionsAttribute ea = new ExceptionsAttribute(cp);
+ ea.setExceptions(list);
+ minfo.setExceptionsAttribute(ea);
+ Bytecode code = new Bytecode(cp, 0, 1);
+ code.addAload(0);
+ code.addInvokestatic("javassist.util.proxy.RuntimeSupport",
+ "makeSerializedProxy",
+ "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;");
+ code.addOpcode(Opcode.ARETURN);
+ minfo.setCodeAttribute(code.toCodeAttribute());
+ return minfo;
+ }
+
+ // SEAM: from SecurityActions
+ static void setAccessible(final AccessibleObject ao,
+ final boolean accessible) {
+ if (System.getSecurityManager() == null) {
+ ao.setAccessible(accessible);
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ ao.setAccessible(accessible);
+ return null;
+ }
+ });
+ }
+ }
+
+ // SEAM: from SecurityActions
+ static Method[] getDeclaredMethods(final Class clazz) {
+ if (System.getSecurityManager() == null) {
+ return clazz.getDeclaredMethods();
+ } else {
+ return (Method[]) AccessController
+ .doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return clazz.getDeclaredMethods();
+ }
+ });
+ }
+ }
+
+ // SEAM: from SecurityActions
+ static Constructor[] getDeclaredConstructors(final Class clazz) {
+ if (System.getSecurityManager() == null) {
+ return clazz.getDeclaredConstructors();
+ } else {
+ return (Constructor[]) AccessController
+ .doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return clazz.getDeclaredConstructors();
+ }
+ });
+ }
+ }
}
More information about the seam-commits
mailing list