You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by aw...@apache.org on 2006/12/13 21:14:27 UTC

svn commit: r486825 - in /incubator/openjpa/trunk/openjpa-kernel/src/main: java/org/apache/openjpa/util/Proxies.java java/org/apache/openjpa/util/ProxyManagerImpl.java resources/org/apache/openjpa/util/localizer.properties

Author: awhite
Date: Wed Dec 13 12:14:26 2006
New Revision: 486825

URL: http://svn.apache.org/viewvc?view=rev&rev=486825
Log:
Option to create proxies for standard java.util types at build time.  These
proxies can be serialized as proxies for use with detached state managers,
whereas runtime-generated proxies have to serialize themselves as a copied 
instance of their corresponding java.util type in case they are transferred to 
a different classloading environment.


Modified:
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java
    incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java?view=diff&rev=486825&r1=486824&r2=486825
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/Proxies.java Wed Dec 13 12:14:26 2006
@@ -78,13 +78,10 @@
     /**
      * Used by proxy types to serialize non-proxy versions.
      */
-    public static Object writeReplace(Proxy proxy) {
-        // we can't send dynamically-created classes to another JVM
-        /*
-        if (proxy == null || proxy.getOwner() == null 
-            || proxy.getOwner().isDetached())
+    public static Object writeReplace(Proxy proxy, boolean detachable) {
+        if (detachable && (proxy == null || proxy.getOwner() == null 
+            || proxy.getOwner().isDetached()))
             return proxy;
-        */
         return proxy.copy(proxy);
     }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java?view=diff&rev=486825&r1=486824&r2=486825
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java Wed Dec 13 12:14:26 2006
@@ -24,6 +24,7 @@
 import java.lang.reflect.Modifier;
 import java.sql.Timestamp;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Comparator;
@@ -46,6 +47,7 @@
 import org.apache.openjpa.lib.util.Files;
 import org.apache.openjpa.lib.util.JavaVersions;
 import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Options;
 import org.apache.openjpa.lib.util.concurrent.ConcurrentHashMap;
 import serp.bytecode.BCClass;
 import serp.bytecode.BCClassLoader;
@@ -331,9 +333,11 @@
         // we don't lock here; ok if two proxies get generated for same type
         ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
         if (proxy == null) {
-            proxy = (ProxyCollection) instantiateProxy
-                (generateProxyCollectionBytecode(type), type, 
-                ProxyCollection.class, null, null);
+            ClassLoader l = getMostDerivedLoader(type, ProxyCollection.class);
+            Class pcls = loadBuildTimeProxy(type, l);
+            if (pcls == null)
+                pcls = loadProxy(generateProxyCollectionBytecode(type, true),l);
+            proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
             _proxies.put(type, proxy);
         }
         return proxy;
@@ -346,8 +350,11 @@
         // we don't lock here; ok if two proxies get generated for same type
         ProxyMap proxy = (ProxyMap) _proxies.get(type);
         if (proxy == null) {
-            proxy = (ProxyMap) instantiateProxy(generateProxyMapBytecode(type), 
-                type, ProxyMap.class, null, null);
+            ClassLoader l = getMostDerivedLoader(type, ProxyMap.class);
+            Class pcls = loadBuildTimeProxy(type, l);
+            if (pcls == null)
+                pcls = loadProxy(generateProxyMapBytecode(type, true), l);
+            proxy = (ProxyMap) instantiateProxy(pcls, null, null);
             _proxies.put(type, proxy);
         }
         return proxy;
@@ -360,9 +367,11 @@
         // we don't lock here; ok if two proxies get generated for same type
         ProxyDate proxy = (ProxyDate) _proxies.get(type);
         if (proxy == null) {
-            proxy = (ProxyDate) instantiateProxy
-                (generateProxyDateBytecode(type), type, ProxyDate.class, null, 
-                null);
+            ClassLoader l = getMostDerivedLoader(type, ProxyDate.class);
+            Class pcls = loadBuildTimeProxy(type, l);
+            if (pcls == null)
+                pcls = loadProxy(generateProxyDateBytecode(type, true), l);
+            proxy = (ProxyDate) instantiateProxy(pcls, null, null);
             _proxies.put(type, proxy);
         }
         return proxy;
@@ -375,9 +384,11 @@
         // we don't lock here; ok if two proxies get generated for same type
         ProxyCalendar proxy = (ProxyCalendar) _proxies.get(type);
         if (proxy == null) {
-            proxy = (ProxyCalendar) instantiateProxy
-                (generateProxyCalendarBytecode(type), type, ProxyCalendar.class,
-                null, null);
+            ClassLoader l = getMostDerivedLoader(type, ProxyCalendar.class);
+            Class pcls = loadBuildTimeProxy(type, l);
+            if (pcls == null)
+                pcls = loadProxy(generateProxyCalendarBytecode(type, true), l);
+            proxy = (ProxyCalendar) instantiateProxy(pcls, null, null);
             _proxies.put(type, proxy);
         }
         return proxy;
@@ -388,40 +399,62 @@
      */
     private ProxyBean getFactoryProxyBean(Object orig) {
         // we don't lock here; ok if two proxies get generated for same type
-        ProxyBean proxy = (ProxyBean) _proxies.get(orig.getClass());
-        if (proxy == null && !_proxies.containsKey(orig.getClass())) {
-            BCClass bc = generateProxyBeanBytecode(orig.getClass());
-            if (bc == null)
-                _proxies.put(orig.getClass(), null);
-            else {
-                BCMethod m = bc.getDeclaredMethod("<init>", (Class[]) null);
-                proxy = (ProxyBean) instantiateProxy(bc, orig.getClass(), 
-                    ProxyBean.class, findCopyConstructor(orig.getClass()), 
-                    new Object[] {orig});
-                _proxies.put(orig.getClass(), proxy);
+        Class type = orig.getClass();
+        ProxyBean proxy = (ProxyBean) _proxies.get(type);
+        if (proxy == null && !_proxies.containsKey(type)) {
+            ClassLoader l = getMostDerivedLoader(type, ProxyBean.class);
+            Class pcls = loadBuildTimeProxy(type, l);
+            if (pcls == null) {
+                BCClass bc = generateProxyBeanBytecode(type, true);
+                if (bc != null)
+                    pcls = loadProxy(bc, l);
             }
+            if (pcls != null)
+                proxy = (ProxyBean) instantiateProxy(pcls, 
+                    findCopyConstructor(type), new Object[] {orig});
+            _proxies.put(type, proxy);
         }
         return proxy;
     }
 
     /**
-     * Instantiate the given proxy bytecode.
+     * Load the proxy class generated at build time for the given type,
+     * returning null if none exists.
+     */
+    private static Class loadBuildTimeProxy(Class type, ClassLoader loader) {
+        try {
+            return Class.forName(getProxyClassName(type, false), true, loader);
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    /**
+     * Load the proxy class represented by the given bytecode.
+     */
+    private Class loadProxy(BCClass bc, ClassLoader loader) {
+        BCClassLoader bcloader = new BCClassLoader(bc.getProject(), loader);
+        try {
+            return Class.forName(bc.getName(), true, bcloader);
+        } catch (Throwable t) {
+            throw new GeneralException(bc.getName()).setCause(t);
+        }
+    }
+
+    /**
+     * Instantiate the given proxy class.
      */
-    private Proxy instantiateProxy(BCClass bc, Class type, Class proxy, 
-        Constructor cons, Object[] args) {
-        BCClassLoader loader = new BCClassLoader(bc.getProject(),
-            getMostDerivedClassLoader(type, proxy));
+    private Proxy instantiateProxy(Class cls, Constructor cons, Object[] args) {
         try {
-            Class cls = Class.forName(bc.getName(), true, loader);
             if (cons != null)
                 return (Proxy) cls.getConstructor(cons.getParameterTypes()).
                     newInstance(args);
             return (Proxy) cls.newInstance();
         } catch (InstantiationException ie) {
-            throw new UnsupportedException(_loc.get("cant-classforname", 
-                bc.getSuperclassName()));
+            throw new UnsupportedException(_loc.get("cant-newinstance", 
+                cls.getSuperclass().getName()));
         } catch (Throwable t) {
-            throw new GeneralException(t);
+            throw new GeneralException(cls.getName()).setCause(t);
         }
     }
 
@@ -429,7 +462,7 @@
      * Return the more derived loader of the class laoders for the given 
      * classes.
      */
-    private static ClassLoader getMostDerivedClassLoader(Class c1, Class c2) {
+    private static ClassLoader getMostDerivedLoader(Class c1, Class c2) {
         ClassLoader l1 = c1.getClassLoader();
         ClassLoader l2 = c2.getClassLoader();
         if (l1 == l2)
@@ -448,12 +481,11 @@
     /**
      * Generate the bytecode for a collection proxy for the given type.
      */
-    protected BCClass generateProxyCollectionBytecode(Class type) {
+    protected BCClass generateProxyCollectionBytecode(Class type, 
+        boolean runtime) {
         assertNotFinal(type);
         Project project = new Project(); 
-        BCClass bc = project.loadClass(Strings.getPackageName
-            (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
-            + "$" + nextProxyId() + PROXY_SUFFIX);
+        BCClass bc = project.loadClass(getProxyClassName(type, runtime));
         bc.setSuperclass(type);
         bc.declareInterface(ProxyCollection.class);
  
@@ -463,11 +495,20 @@
         proxyRecognizedMethods(bc, type, ProxyCollections.class, 
             ProxyCollection.class);
         proxySetters(bc, type);
-        addWriteReplaceMethod(bc);
+        addWriteReplaceMethod(bc, runtime);
         return bc;
     }
 
     /**
+     * Return the name of the proxy class to generate for the given type.   
+     */
+    private static String getProxyClassName(Class type, boolean runtime) {
+        String id = (runtime) ? "$" + nextProxyId() : "";
+        return Strings.getPackageName(ProxyManagerImpl.class) + "." 
+            + type.getName().replace('.', '$') + id + PROXY_SUFFIX;
+    }
+
+    /**
      * Throw appropriate exception if the given type is final.
      */
     private static void assertNotFinal(Class type) {
@@ -478,12 +519,10 @@
     /**
      * Generate the bytecode for a map proxy for the given type.
      */
-    protected BCClass generateProxyMapBytecode(Class type) {
+    protected BCClass generateProxyMapBytecode(Class type, boolean runtime) {
         assertNotFinal(type);
         Project project = new Project(); 
-        BCClass bc = project.loadClass(Strings.getPackageName
-            (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
-            + "$" + nextProxyId() + PROXY_SUFFIX);
+        BCClass bc = project.loadClass(getProxyClassName(type, runtime));
         bc.setSuperclass(type);
         bc.declareInterface(ProxyMap.class);
  
@@ -492,19 +531,17 @@
         addProxyMapMethods(bc, type);
         proxyRecognizedMethods(bc, type, ProxyMaps.class, ProxyMap.class);
         proxySetters(bc, type);
-        addWriteReplaceMethod(bc);
+        addWriteReplaceMethod(bc, runtime);
         return bc;
     }
 
     /**
      * Generate the bytecode for a date proxy for the given type.
      */
-    protected BCClass generateProxyDateBytecode(Class type) {
+    protected BCClass generateProxyDateBytecode(Class type, boolean runtime) {
         assertNotFinal(type);
         Project project = new Project(); 
-        BCClass bc = project.loadClass(Strings.getPackageName
-            (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
-            + "$" + nextProxyId() + PROXY_SUFFIX);
+        BCClass bc = project.loadClass(getProxyClassName(type, runtime));
         bc.setSuperclass(type);
         bc.declareInterface(ProxyDate.class);
  
@@ -512,19 +549,18 @@
         addProxyMethods(bc, type, true);
         addProxyDateMethods(bc, type);
         proxySetters(bc, type);
-        addWriteReplaceMethod(bc);
+        addWriteReplaceMethod(bc, runtime);
         return bc;
     }
 
     /**
      * Generate the bytecode for a calendar proxy for the given type.
      */
-    protected BCClass generateProxyCalendarBytecode(Class type) {
+    protected BCClass generateProxyCalendarBytecode(Class type, 
+        boolean runtime) {
         assertNotFinal(type);
         Project project = new Project(); 
-        BCClass bc = project.loadClass(Strings.getPackageName
-            (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
-            + "$" + nextProxyId() + PROXY_SUFFIX);
+        BCClass bc = project.loadClass(getProxyClassName(type, runtime));
         bc.setSuperclass(type);
         bc.declareInterface(ProxyCalendar.class);
  
@@ -532,14 +568,14 @@
         addProxyMethods(bc, type, true);
         addProxyCalendarMethods(bc, type);
         proxySetters(bc, type);
-        addWriteReplaceMethod(bc);
+        addWriteReplaceMethod(bc, runtime);
         return bc;
     }
 
     /**
      * Generate the bytecode for a bean proxy for the given type.
      */
-    protected BCClass generateProxyBeanBytecode(Class type) {
+    protected BCClass generateProxyBeanBytecode(Class type, boolean runtime) {
         if (Modifier.isFinal(type.getModifiers()))
             return null;
         if (ImplHelper.isManagedType(type))
@@ -558,9 +594,7 @@
         }
 
         Project project = new Project(); 
-        BCClass bc = project.loadClass(Strings.getPackageName
-            (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
-            + "$" + nextProxyId() + PROXY_SUFFIX);
+        BCClass bc = project.loadClass(getProxyClassName(type, runtime));
         bc.setSuperclass(type);
         bc.declareInterface(ProxyBean.class);
  
@@ -569,7 +603,7 @@
         addProxyBeanMethods(bc, type, cons);
         if (!proxySetters(bc, type))
             return null;
-        addWriteReplaceMethod(bc);
+        addWriteReplaceMethod(bc, runtime);
         return bc;
     }
 
@@ -1409,16 +1443,17 @@
 
     /**
      * Add a writeReplace implementation that serializes to a non-proxy type
-     * unless detached.
+     * unless detached and this is a build-time generated class.
      */
-    private void addWriteReplaceMethod(BCClass bc) {
+    private void addWriteReplaceMethod(BCClass bc, boolean runtime) {
         BCMethod m = bc.declareMethod("writeReplace", Object.class, null);
         m.makeProtected();
         m.getExceptions(true).addException(ObjectStreamException.class);
         Code code = m.getCode(true);
         code.aload().setThis();
+        code.constant().setValue(!runtime);
         code.invokestatic().setMethod(Proxies.class, "writeReplace", 
-            Object.class, new Class[] { Proxy.class });
+            Object.class, new Class[] { Proxy.class, boolean.class });
         code.areturn();
         code.calculateMaxLocals();
         code.calculateMaxStack();
@@ -1474,13 +1509,20 @@
     }
 
     /**
-     * Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl
-     * &lt;class name&gt;+
+     * Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl [option]*
+     * &lt;class name&gt;+<br />
+     * Where the following options are recognized:
+     * <ul> 
+     * <li><i>-utils/-u &lt;number&gt;</i>: Generate proxies for the standard
+     * java.util collection, map, date, and calendar classes of the given Java
+     * version.  Use 4 for Java 1.4, 5 for Java 5, etc.</li>
+     * </ul>
      *
      * The main method generates .class files for the proxies to the classes    
-     * given on the command line.  The .class files are placed in the same
-     * package as this class, or the current directory if the directory for this
-     * class cannot be accessed.
+     * given on the command line.  It writes the generated classes to beside the
+     * ProxyManagerImpl.class file if possible; otherwise it writes to the 
+     * current directory.  The proxy manager looks for these classes 
+     * before generating its own proxies at runtime.
      */
     public static void main(String[] args) 
         throws ClassNotFoundException, IOException {
@@ -1488,21 +1530,59 @@
         dir = (dir == null) ? new File(System.getProperty("user.dir"))
             : dir.getParentFile();
 
+        Options opts = new Options();
+        args = opts.setFromCmdLine(args);
+
+        List types = new ArrayList();
+        types.addAll(Arrays.asList(args));
+        int utils = opts.removeIntProperty("utils", "u", 0);
+        if (utils >= 4) {
+            types.addAll(Arrays.asList(new String[] {
+                java.sql.Date.class.getName(),
+                java.sql.Time.class.getName(),
+                java.sql.Timestamp.class.getName(),
+                java.util.ArrayList.class.getName(),
+                java.util.Date.class.getName(),
+                java.util.GregorianCalendar.class.getName(),
+                java.util.HashMap.class.getName(),
+                java.util.HashSet.class.getName(),
+                java.util.Hashtable.class.getName(),
+                java.util.LinkedList.class.getName(),
+                java.util.Properties.class.getName(),
+                java.util.TreeMap.class.getName(),
+                java.util.TreeSet.class.getName(),
+                java.util.Vector.class.getName(),
+            })); 
+        }
+        if (utils >= 5) {
+            types.addAll(Arrays.asList(new String[] {
+                "java.util.EnumMap",
+                "java.util.IdentityHashMap",
+                "java.util.LinkedHashMap",
+                "java.util.LinkedHashSet",
+                "java.util.PriorityQueue",
+            })); 
+        }
+
         ProxyManagerImpl mgr = new ProxyManagerImpl();
         Class cls;
         BCClass bc;
-        for (int i = 0; i < args.length; i++) {
-            cls = Class.forName(args[i]);
+        for (int i = 0; i < types.size(); i++) {
+            cls = Class.forName((String) types.get(i));
+            if (loadBuildTimeProxy(cls, getMostDerivedLoader(cls, Proxy.class))
+                != null)
+                continue;
+
             if (Collection.class.isAssignableFrom(cls))
-                bc = mgr.generateProxyCollectionBytecode(cls);         
+                bc = mgr.generateProxyCollectionBytecode(cls, false);         
             else if (Map.class.isAssignableFrom(cls))
-                bc = mgr.generateProxyMapBytecode(cls);         
+                bc = mgr.generateProxyMapBytecode(cls, false);         
             else if (Date.class.isAssignableFrom(cls))
-                bc = mgr.generateProxyDateBytecode(cls);
+                bc = mgr.generateProxyDateBytecode(cls, false);
             else if (Calendar.class.isAssignableFrom(cls))
-                bc = mgr.generateProxyCalendarBytecode(cls);
+                bc = mgr.generateProxyCalendarBytecode(cls, false);
             else
-                bc = mgr.generateProxyBeanBytecode(cls);
+                bc = mgr.generateProxyBeanBytecode(cls, false);
 
             System.out.println(bc.getName());
             bc.write(new File(dir, bc.getClassName() + ".class"));

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties?view=diff&rev=486825&r1=486824&r2=486825
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties Wed Dec 13 12:14:26 2006
@@ -49,7 +49,7 @@
     "{0}".  No corresponding concrete types are known.
 no-proxy-abstract: Unable to create a second class object proxy for abstract \
     type "{0}".  You must use a concrete type or a recognized interface.
-cant-classforname: Unable to instantiate proxy for type "{0}".  Make sure the \
+cant-newinstance: Unable to instantiate proxy for type "{0}".  Make sure the \
     class has a default constructor.
 no-date-cons: Custom date type "{0}" needs a default constructor or a \
     millisecond constructor.