[jboss-cvs] javassist/src/main/javassist/util/proxy ...
Shigeru Chiba
chiba at is.titech.ac.jp
Sun Nov 5 18:16:35 EST 2006
User: chiba
Date: 06/11/05 18:16:35
Modified: src/main/javassist/util/proxy ProxyFactory.java
RuntimeSupport.java
Added: src/main/javassist/util/proxy SerializedProxy.java
Log:
made a proxy class serializable (JASSIST-20).
Revision Changes Path
1.20 +89 -38 javassist/src/main/javassist/util/proxy/ProxyFactory.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: ProxyFactory.java
===================================================================
RCS file: /cvsroot/jboss/javassist/src/main/javassist/util/proxy/ProxyFactory.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -b -r1.19 -r1.20
--- ProxyFactory.java 2 Nov 2006 14:30:22 -0000 1.19
+++ ProxyFactory.java 5 Nov 2006 23:16:35 -0000 1.20
@@ -32,6 +32,11 @@
import javassist.CannotCompileException;
import javassist.bytecode.*;
+/*
+ * This class is implemented only with the lower-level API of Javassist.
+ * This design decision is for maximizing performance.
+ */
+
/**
* Factory of dynamic proxy classes.
*
@@ -92,6 +97,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
*/
@@ -117,6 +127,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";
@@ -137,8 +148,8 @@
private static WeakHashMap proxyCache = new WeakHashMap();
static class CacheKey {
- private String classes;
- private MethodFilter filter;
+ String classes;
+ MethodFilter filter;
private int hash;
WeakReference proxyClass;
MethodHandler handler;
@@ -233,18 +244,25 @@
public Class createClass() {
if (thisClass == null) {
ClassLoader cl = getClassLoader();
+ synchronized (proxyCache) {
if (useCache)
createClass2(cl);
else
createClass3(cl);
}
+ }
return thisClass;
}
private void createClass2(ClassLoader cl) {
CacheKey key = new CacheKey(superClass, interfaces, methodFilter, handler);
- synchronized (proxyCache) {
+ /*
+ * 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();
@@ -264,9 +282,9 @@
}
}
}
- }
+ // }
- synchronized (key) {
+ // synchronized (key) {
Class c = isValidEntry(key);
if (c == null) {
createClass3(cl);
@@ -274,7 +292,7 @@
}
else
thisClass = c;
- }
+ // }
}
private Class isValidEntry(CacheKey key) {
@@ -295,7 +313,8 @@
FactoryHelper.writeFile(cf, writeDirectory);
thisClass = FactoryHelper.toClass(cf, cl, getDomain());
- setHandler();
+ setField(DEFAULT_INTERCEPTOR, handler);
+ setField(METHOD_FILTER_FIELD, methodFilter);
}
catch (CannotCompileException e) {
throw new RuntimeException(e.getMessage(), e);
@@ -303,6 +322,40 @@
}
+ private void setField(String fieldName, Object value) {
+ if (thisClass != null && value != null)
+ try {
+ Field f = thisClass.getField(fieldName);
+ f.setAccessible(true);
+ f.set(null, value);
+ f.setAccessible(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.
*
@@ -403,33 +456,7 @@
*/
public void setHandler(MethodHandler mi) {
handler = mi;
- setHandler();
- }
-
- 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);
- }
- }
-
- static MethodHandler getHandler(Class clazz) {
- try {
- Field f = clazz.getField(DEFAULT_INTERCEPTOR);
- f.setAccessible(true);
- MethodHandler h = (MethodHandler)f.get(null);
- f.setAccessible(false);
- return h;
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
+ setField(DEFAULT_INTERCEPTOR, handler);
}
private static int counter = 0;
@@ -473,6 +500,11 @@
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);
@@ -480,6 +512,8 @@
addMethodsHolder(cf, pool, classname, s);
addSetter(classname, cf, pool);
+ cf.addMethod(makeWriteReplace(pool));
+
thisClass = null;
return cf;
}
@@ -932,4 +966,21 @@
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;
+ }
}
1.4 +19 -1 javassist/src/main/javassist/util/proxy/RuntimeSupport.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: RuntimeSupport.java
===================================================================
RCS file: /cvsroot/jboss/javassist/src/main/javassist/util/proxy/RuntimeSupport.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -b -r1.3 -r1.4
--- RuntimeSupport.java 14 Feb 2006 04:27:38 -0000 1.3
+++ RuntimeSupport.java 5 Nov 2006 23:16:35 -0000 1.4
@@ -16,6 +16,7 @@
package javassist.util.proxy;
import java.lang.reflect.Method;
+import java.io.Serializable;
/**
* Runtime support routines that the classes generated by ProxyFactory use.
@@ -26,7 +27,9 @@
/**
* A method handler that only executes a method.
*/
- public static MethodHandler default_interceptor = new MethodHandler() {
+ public static MethodHandler default_interceptor = new DefaultMethodHandler();
+
+ static class DefaultMethodHandler implements MethodHandler, Serializable {
public Object invoke(Object self, Method m,
Method proceed, Object[] args)
throws Exception
@@ -166,4 +169,19 @@
sbuf.append('L').append(type.getName().replace('.', '/'))
.append(';');
}
+
+ /**
+ * Converts a proxy object to an object that is writable to an
+ * object stream. This method is called by <code>writeReplace()</code>
+ * in a proxy class.
+ *
+ * @since 3.4
+ */
+ public static SerializedProxy makeSerializedProxy(Object proxy)
+ throws java.io.InvalidClassException
+ {
+ Class clazz = proxy.getClass();
+ return new SerializedProxy(clazz, ProxyFactory.getFilter(clazz),
+ ProxyFactory.getHandler(clazz));
+ }
}
1.1 date: 2006/11/05 23:16:35; author: chiba; state: Exp;javassist/src/main/javassist/util/proxy/SerializedProxy.java
Index: SerializedProxy.java
===================================================================
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2006 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
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.util.proxy;
import java.io.Serializable;
import java.io.ObjectStreamException;
/**
* A proxy object is converted into an instance of this class
* when it is written to an output stream.
*
* @see RuntimeSupport#makeSerializedProxy(Object)
*/
class SerializedProxy implements Serializable {
private String superClass;
private String[] interfaces;
private MethodFilter filter;
private MethodHandler handler;
SerializedProxy(Class proxy, MethodFilter f, MethodHandler h) {
filter = f;
handler = h;
superClass = proxy.getSuperclass().getName();
Class[] infs = proxy.getInterfaces();
int n = infs.length;
interfaces = new String[n - 1];
String setterInf = ProxyObject.class.getName();
for (int i = 0; i < n; i++) {
String name = infs[i].getName();
if (!name.equals(setterInf))
interfaces[i] = name;
}
}
Object readResolve() throws ObjectStreamException {
try {
int n = interfaces.length;
Class[] infs = new Class[n];
for (int i = 0; i < n; i++)
infs[i] = Class.forName(interfaces[i]);
ProxyFactory f = new ProxyFactory();
f.setSuperclass(Class.forName(superClass));
f.setInterfaces(infs);
f.setFilter(filter);
f.setHandler(handler);
return f.createClass().newInstance();
}
catch (ClassNotFoundException e) {
throw new java.io.InvalidClassException(e.getMessage());
}
catch (InstantiationException e2) {
throw new java.io.InvalidObjectException(e2.getMessage());
}
catch (IllegalAccessException e3) {
throw new java.io.InvalidClassException(e3.getMessage());
}
}
}
More information about the jboss-cvs-commits
mailing list