You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/09/01 02:01:42 UTC

svn commit: r991401 - in /tapestry/tapestry5/trunk/tapestry-ioc/src: main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java test/java/org/apache/tapestry5/ioc/ReloadTest.java

Author: hlship
Date: Wed Sep  1 00:01:41 2010
New Revision: 991401

URL: http://svn.apache.org/viewvc?rev=991401&view=rev
Log:
TAP5-1188: Tune some of the logic related to which classes are reloaded

Modified:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java?rev=991401&r1=991400&r2=991401&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java Wed Sep  1 00:01:41 2010
@@ -14,13 +14,10 @@
 
 package org.apache.tapestry5.ioc.internal;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.security.ProtectionDomain;
+import java.util.Set;
 
 import javassist.CannotCompileException;
 import javassist.ClassPath;
@@ -30,11 +27,17 @@ import javassist.Loader;
 import javassist.LoaderClassPath;
 import javassist.NotFoundException;
 import javassist.Translator;
+import javassist.expr.ConstructorCall;
+import javassist.expr.ExprEditor;
+import javassist.expr.FieldAccess;
+import javassist.expr.MethodCall;
+import javassist.expr.NewExpr;
 
 import org.apache.tapestry5.ioc.Invokable;
 import org.apache.tapestry5.ioc.ObjectCreator;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.services.ClassFactoryClassPool;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.ClassFabUtils;
 import org.apache.tapestry5.services.UpdateListener;
@@ -43,8 +46,6 @@ import org.slf4j.Logger;
 @SuppressWarnings("all")
 public abstract class AbstractReloadableObjectCreator implements ObjectCreator, UpdateListener, Translator
 {
-    private final ProtectionDomain domain = getClass().getProtectionDomain();
-
     private class XLoader extends Loader
     {
         public XLoader(ClassLoader parent, ClassPool pool)
@@ -66,6 +67,8 @@ public abstract class AbstractReloadable
 
     private final String implementationClassName;
 
+    private final String packageName;
+
     private final String classFilePath;
 
     private final Logger logger;
@@ -80,6 +83,8 @@ public abstract class AbstractReloadable
 
     private boolean firstTime = true;
 
+    private final Set<String> classesToLoad = CollectionFactory.newSet();
+
     protected AbstractReloadableObjectCreator(ClassLoader baseClassLoader, String implementationClassName,
             Logger logger, OperationTracker tracker)
     {
@@ -88,10 +93,19 @@ public abstract class AbstractReloadable
         this.logger = logger;
         this.tracker = tracker;
 
-        this.classFilePath = ClassFabUtils.getPathForClassNamed(implementationClassName);
+        packageName = toPackageName(implementationClassName);
+
+        classFilePath = ClassFabUtils.getPathForClassNamed(implementationClassName);
 
     }
 
+    private String toPackageName(String name)
+    {
+        int dotx = name.lastIndexOf('.');
+
+        return dotx < 0 ? "" : name.substring(0, dotx);
+    }
+
     public synchronized void checkForUpdates()
     {
         if (instance == null)
@@ -147,10 +161,6 @@ public abstract class AbstractReloadable
 
         ClassFactoryClassPool pool = new ClassFactoryClassPool(baseClassLoader);
 
-        // For TAPESTRY-2561, we're introducing a class loader between the parent (i.e., the
-        // context class loader), and the component class loader, to try and prevent the deadlocks
-        // that we've been seeing.
-
         ClassLoader threadDeadlockBuffer = new URLClassLoader(new URL[0], baseClassLoader);
 
         Loader loader = new XLoader(threadDeadlockBuffer, pool);
@@ -159,13 +169,14 @@ public abstract class AbstractReloadable
 
         pool.appendClassPath(path);
 
+        classesToLoad.clear();
+        add(implementationClassName);
+
         try
         {
             loader.addTranslator(pool, this);
 
-            CtClass implCtClass = pool.get(implementationClassName);
-
-            Class result = pool.toClass(implCtClass, loader, domain);
+            Class result = loader.loadClass(implementationClassName);
 
             firstTime = false;
 
@@ -178,44 +189,6 @@ public abstract class AbstractReloadable
         }
     }
 
-    private byte[] readClassData(String name) throws ClassNotFoundException
-    {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
-        byte[] buffer = new byte[10000];
-
-        URL url = getURLForClass(name);
-
-        InputStream in = null;
-
-        try
-        {
-            in = url.openStream();
-
-            while (true)
-            {
-                int length = in.read(buffer);
-
-                if (length < 0)
-                    break;
-
-                baos.write(buffer, 0, length);
-            }
-
-            in.close();
-
-            in = null;
-        }
-        catch (IOException ex)
-        {
-            InternalUtils.close(in);
-
-            throw new ClassNotFoundException(InternalUtils.toMessage(ex), ex);
-        }
-
-        return baos.toByteArray();
-    }
-
     private URL getURLForClass(String className) throws ClassNotFoundException
     {
         String path = ClassFabUtils.getPathForClassNamed(className);
@@ -249,12 +222,98 @@ public abstract class AbstractReloadable
 
     private boolean shouldLoadClassNamed(String name)
     {
-        return name.equals(implementationClassName) || name.startsWith(implementationClassName + "$");
+        return classesToLoad.contains(name);
+    }
+
+    private void add(String className)
+    {
+        if (classesToLoad.contains(className))
+            return;
+
+        // System.err.printf("Adding %s\n", className);
+        logger.debug(String.format("Marking class %s to be (re-)loaded", className));
+
+        classesToLoad.add(className);
     }
 
     public void onLoad(ClassPool pool, String className) throws NotFoundException, CannotCompileException
     {
+        logger.debug(String.format("BEGIN Analyzing %s", className));
+
+        CtClass ctClass = pool.get(className);
+
+        ctClass.instrument(new ExprEditor()
+        {
+            public void edit(ConstructorCall c) throws CannotCompileException
+            {
+                if (c.getMethodName().equals("this"))
+                    return;
+
+                String cn = c.getClassName();
+
+                String classFilePath = ClassFabUtils.getPathForClassNamed(cn);
+
+                URL url = baseClassLoader.getResource(classFilePath);
+
+                // If the base class is also a file on the file system then mark
+                // that it should be loaded by the same class loader. This serves two
+                // purposes: first, if the base class is in the same package then
+                // protected access will work properly. Secondly, if the base implementation
+                // changes, the service implementation will be reloaded.
+
+                if (url != null && url.getProtocol().equals("file"))
+                    add(cn);
+            }
+
+            public void edit(FieldAccess f) throws CannotCompileException
+            {
+
+            }
+
+            public void edit(MethodCall m) throws CannotCompileException
+            {
+                // String invokedMethodClassName = m.getClassName();
+                //
+                // if (classesToLoad.contains(invokedMethodClassName))
+                // return;
+                //
+                // try
+                // {
+                // CtMethod method = m.getMethod();
+                //
+                // if (!Modifier.isPublic(method.getModifiers()))
+                // return;
+                //
+                // add(invokedMethodClassName);
+                // }
+                // catch (NotFoundException ex)
+                // {
+                // throw new RuntimeException(ex);
+                // }
+            }
+
+            public void edit(NewExpr e) throws CannotCompileException
+            {
+                String newInstanceClassName = e.getClassName();
+
+                if (classesToLoad.contains(newInstanceClassName))
+                    return;
+
+                if (isInnerClass(newInstanceClassName))
+                    add(newInstanceClassName);
+            }
+
+        });
+
+        logger.debug(String.format("  END Analyzing %s", className));
+    }
+
+    /** Is the class an inner class of some other class already marked to be loaded by the special class loader? */
+    private boolean isInnerClass(String className)
+    {
+        int dollarx = className.indexOf("$");
 
+        return dollarx < 0 ? false : classesToLoad.contains(className.substring(0, dollarx));
     }
 
     public void start(ClassPool pool) throws NotFoundException, CannotCompileException

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java?rev=991401&r1=991400&r2=991401&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/ReloadTest.java Wed Sep  1 00:01:41 2010
@@ -226,14 +226,9 @@ public class ReloadTest extends TestBase
 
         CtClass ctClass = pool.makeClass(CLASS);
 
+        ctClass.setModifiers(Modifier.ABSTRACT | Modifier.PUBLIC);
         ctClass.addInterface(pool.get(ReloadableService.class.getName()));
 
-        CtMethod method = new CtMethod(pool.get("java.lang.String"), "getStatus", null, ctClass);
-
-        method.setBody("return \"unreachable\";");
-
-        ctClass.addMethod(method);
-
         CtConstructor constructor = new CtConstructor(new CtClass[0], ctClass);
 
         constructor.setBody("return $0;");