You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2008/11/10 22:22:16 UTC

svn commit: r712839 - /tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassFactoryClassPool.java

Author: hlship
Date: Mon Nov 10 13:22:16 2008
New Revision: 712839

URL: http://svn.apache.org/viewvc?rev=712839&view=rev
Log:
TAP5-337: Rapidly refreshing a page, even the same page, can cause a deadlock related to class loading

Modified:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassFactoryClassPool.java

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassFactoryClassPool.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassFactoryClassPool.java?rev=712839&r1=712838&r2=712839&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassFactoryClassPool.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassFactoryClassPool.java Mon Nov 10 13:22:16 2008
@@ -15,10 +15,16 @@
 package org.apache.tapestry5.ioc.internal.services;
 
 import javassist.*;
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newSet;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.ClassFabUtils;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
 import java.util.Map;
 import java.util.Set;
 
@@ -29,12 +35,49 @@
  */
 public class ClassFactoryClassPool extends ClassPool
 {
+
+    // Kind of duplicating some logic from ClassPool to avoid a deadlock-producing synchronized block.
+
+    private static final Method defineClass = findMethod("defineClass", String.class, byte[].class,
+                                                         int.class, int.class);
+
+    private static final Method defineClassWithProtectionDomain = findMethod("defineClass", String.class, byte[].class,
+                                                                             int.class, int.class,
+                                                                             ProtectionDomain.class);
+
+    private static Method findMethod(final String methodName, final Class... parameterTypes)
+    {
+        try
+        {
+            return AccessController.doPrivileged(new PrivilegedExceptionAction<Method>()
+            {
+                public Method run() throws Exception
+                {
+                    Class cl = Class.forName("java.lang.ClassLoader");
+
+                    Method result = cl.getDeclaredMethod(methodName, parameterTypes);
+
+                    // Just make it accessible; no particular reason to make it unaccessible again.
+
+                    result.setAccessible(true);
+
+                    return result;
+                }
+            });
+        }
+        catch (PrivilegedActionException ex)
+        {
+            throw new RuntimeException(String.format("Unable to initialize ClassFactoryClassPool: %s",
+                                                     InternalUtils.toMessage(ex)), ex);
+        }
+    }
+
     /**
      * Used to identify which class loaders have already been integrated into the pool.
      */
-    private final Set<ClassLoader> allLoaders = newSet();
+    private final Set<ClassLoader> allLoaders = CollectionFactory.newSet();
 
-    private final Map<ClassLoader, ClassPath> leafLoaders = newMap();
+    private final Map<ClassLoader, ClassPath> leafLoaders = CollectionFactory.newMap();
 
     public ClassFactoryClassPool(ClassLoader contextClassLoader)
     {
@@ -112,4 +155,43 @@
             l = l.getParent();
         }
     }
+
+    /**
+     * Overriden to remove a deadlock producing synchronized block. We expect that the defineClass() methods will have
+     * been marked as accessible statically (by this class), so there's no need to set them accessible again.
+     */
+    @Override
+    public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
+            throws CannotCompileException
+    {
+        Throwable failure;
+
+        try
+        {
+            byte[] b = ct.toBytecode();
+
+            boolean hasDomain = domain != null;
+
+            Method method = hasDomain ? defineClass : defineClassWithProtectionDomain;
+
+            Object[] args = hasDomain
+                            ? new Object[] {ct.getName(), b, 0, b.length}
+                            : new Object[] {ct.getName(), b, 0, b.length, domain};
+
+            return (Class) method.invoke(loader, args);
+        }
+        catch (InvocationTargetException ite)
+        {
+            failure = ite.getTargetException();
+        }
+        catch (Exception ex)
+        {
+            failure = ex;
+        }
+
+        throw new CannotCompileException(
+                String.format("Failure defining new class %s: %s",
+                              ct.getName(),
+                              InternalUtils.toMessage(failure)), failure);
+    }
 }