From: chiba Date: Sun, 5 Nov 2006 23:16:35 +0000 (+0000) Subject: made a proxy class serializable (JASSIST-20). X-Git-Tag: rel_3_17_1_ga~305 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=56af2ace49d2ea3778fa8fb12621117cd4b272f4;p=javassist.git made a proxy class serializable (JASSIST-20). git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@329 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index 5e323000..5dbc19a0 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/src/main/javassist/bytecode/ClassFile.java @@ -545,7 +545,8 @@ public final class ClassFile { && notBridgeMethod(minfo) && notBridgeMethod(newMinfo) && Descriptor.eqParamTypes(minfo.getDescriptor(), descriptor)) - throw new CannotCompileException("duplicate method: " + name); + throw new CannotCompileException("duplicate method: " + name + + " in " + this.getName()); } } diff --git a/src/main/javassist/util/proxy/ProxyFactory.java b/src/main/javassist/util/proxy/ProxyFactory.java index 3dd5b1e6..444697ec 100644 --- a/src/main/javassist/util/proxy/ProxyFactory.java +++ b/src/main/javassist/util/proxy/ProxyFactory.java @@ -32,6 +32,11 @@ import java.lang.ref.WeakReference; 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 @@ import javassist.bytecode.*; * } * * + *

A proxy object generated by ProxyFactory is serializable + * if its super class or interfaces implement a java.io.Serializable. + * 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 @@ public class ProxyFactory { 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 @@ public class ProxyFactory { 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,10 +244,12 @@ public class ProxyFactory { public Class createClass() { if (thisClass == null) { ClassLoader cl = getClassLoader(); - if (useCache) - createClass2(cl); - else - createClass3(cl); + synchronized (proxyCache) { + if (useCache) + createClass2(cl); + else + createClass3(cl); + } } return thisClass; @@ -244,7 +257,12 @@ public class ProxyFactory { 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 @@ public class ProxyFactory { } } } - } + // } - synchronized (key) { + // synchronized (key) { Class c = isValidEntry(key); if (c == null) { createClass3(cl); @@ -274,7 +292,7 @@ public class ProxyFactory { } else thisClass = c; - } + // } } private Class isValidEntry(CacheKey key) { @@ -295,7 +313,8 @@ public class ProxyFactory { 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 @@ public class ProxyFactory { } + 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 class ProxyFactory { */ 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 @@ public class ProxyFactory { 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 @@ public class ProxyFactory { addMethodsHolder(cf, pool, classname, s); addSetter(classname, cf, pool); + cf.addMethod(makeWriteReplace(pool)); + thisClass = null; return cf; } @@ -932,4 +966,21 @@ public class ProxyFactory { 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; + } } diff --git a/src/main/javassist/util/proxy/RuntimeSupport.java b/src/main/javassist/util/proxy/RuntimeSupport.java index db96bcfb..df931d5f 100644 --- a/src/main/javassist/util/proxy/RuntimeSupport.java +++ b/src/main/javassist/util/proxy/RuntimeSupport.java @@ -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 @@ public class RuntimeSupport { /** * 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 @@ public class RuntimeSupport { 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 writeReplace() + * 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)); + } } diff --git a/src/main/javassist/util/proxy/SerializedProxy.java b/src/main/javassist/util/proxy/SerializedProxy.java new file mode 100644 index 00000000..b3fcc85b --- /dev/null +++ b/src/main/javassist/util/proxy/SerializedProxy.java @@ -0,0 +1,72 @@ +/* + * 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()); + } + } +}