]> source.dussan.org Git - javassist.git/commitdiff
made a proxy class serializable (JASSIST-20).
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Sun, 5 Nov 2006 23:16:35 +0000 (23:16 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Sun, 5 Nov 2006 23:16:35 +0000 (23:16 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@329 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

src/main/javassist/bytecode/ClassFile.java
src/main/javassist/util/proxy/ProxyFactory.java
src/main/javassist/util/proxy/RuntimeSupport.java
src/main/javassist/util/proxy/SerializedProxy.java [new file with mode: 0644]

index 5e3230007a497d0740b954336f0353e2db389cd2..5dbc19a009f244264024484c1ebe1a33eca685d6 100644 (file)
@@ -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());
         }
     }
 
index 3dd5b1e6f05ac482287f82fd50edd080953478ec..444697ec24dbbf11e66bc0e08f1af45a377256a5 100644 (file)
@@ -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.*;
  * }
  * </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 @@ 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;
+    }
 }
index db96bcfbe2a25200582aedac72fb9dd7f60be5f5..df931d5ffb69ac8bab040055cbdb139d6826115a 100644 (file)
@@ -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 <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));
+    }
 }
diff --git a/src/main/javassist/util/proxy/SerializedProxy.java b/src/main/javassist/util/proxy/SerializedProxy.java
new file mode 100644 (file)
index 0000000..b3fcc85
--- /dev/null
@@ -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());
+        }
+    }
+}