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 2011/10/07 20:41:11 UTC

svn commit: r1180147 - in /tapestry/tapestry5/trunk: plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassLoader.java tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/AbstractReloadableObjectCreator.java

Author: hlship
Date: Fri Oct  7 18:41:11 2011
New Revision: 1180147

URL: http://svn.apache.org/viewvc?rev=1180147&view=rev
Log:
TAP5-1650: Change the service/object reloader to use Plastic, not Javassist

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

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassLoader.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassLoader.java?rev=1180147&r1=1180146&r2=1180147&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassLoader.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassLoader.java Fri Oct  7 18:41:11 2011
@@ -47,7 +47,7 @@ public class PlasticClassLoader extends 
         }
     }
 
-    synchronized Class<?> defineClassWithBytecode(String className, byte[] bytecode)
+    public synchronized Class<?> defineClassWithBytecode(String className, byte[] bytecode)
     {
         return defineClass(className, bytecode, 0, bytecode.length);
     }

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=1180147&r1=1180146&r2=1180147&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 Fri Oct  7 18:41:11 2011
@@ -14,45 +14,33 @@
 
 package org.apache.tapestry5.ioc.internal;
 
-import javassist.*;
-import javassist.expr.ConstructorCall;
-import javassist.expr.ExprEditor;
+import org.apache.tapestry5.internal.plastic.ClassLoaderDelegate;
+import org.apache.tapestry5.internal.plastic.PlasticClassLoader;
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+import org.apache.tapestry5.internal.plastic.asm.ClassAdapter;
+import org.apache.tapestry5.internal.plastic.asm.ClassReader;
+import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
+import org.apache.tapestry5.internal.plastic.asm.commons.EmptyVisitor;
 import org.apache.tapestry5.ioc.Invokable;
 import org.apache.tapestry5.ioc.ObjectCreator;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.ReloadAware;
-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.internal.util.URLChangeTracker;
-import org.apache.tapestry5.ioc.services.ClassFabUtils;
 import org.apache.tapestry5.services.UpdateListener;
 import org.slf4j.Logger;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.Set;
 
 @SuppressWarnings("all")
-public abstract class AbstractReloadableObjectCreator implements ObjectCreator, UpdateListener, Translator
+public abstract class AbstractReloadableObjectCreator implements ObjectCreator, UpdateListener, ClassLoaderDelegate
 {
-    private class InternalLoader extends Loader
-    {
-        public InternalLoader(ClassLoader parent, ClassPool pool)
-        {
-            super(parent, pool);
-        }
-
-        @Override
-        protected Class findClass(String name) throws ClassNotFoundException
-        {
-            if (shouldLoadClassNamed(name))
-                return super.findClass(name);
-
-            return null; // Force delegation to parent class loader
-        }
-    }
-
     private final ClassLoader baseClassLoader;
 
     private final String implementationClassName;
@@ -73,6 +61,8 @@ public abstract class AbstractReloadable
 
     private boolean firstTime = true;
 
+    private PlasticClassLoader loader;
+
     protected AbstractReloadableObjectCreator(ClassLoader baseClassLoader, String implementationClassName,
                                               Logger logger, OperationTracker tracker)
     {
@@ -84,18 +74,21 @@ public abstract class AbstractReloadable
 
     public synchronized void checkForUpdates()
     {
-        if (instance == null)
-            return;
-
-        if (!changeTracker.containsChanges())
+        if (instance == null || !changeTracker.containsChanges())
+        {
             return;
+        }
 
         if (logger.isDebugEnabled())
+        {
             logger.debug(String.format("Implementation class %s has changed and will be reloaded on next use.",
                     implementationClassName));
+        }
 
         changeTracker.clear();
 
+        loader = null;
+
         boolean reloadNow = informInstanceOfReload();
 
         instance = reloadNow ? createInstance() : null;
@@ -116,7 +109,9 @@ public abstract class AbstractReloadable
     public synchronized Object createObject()
     {
         if (instance == null)
+        {
             instance = createInstance();
+        }
 
         return instance;
     }
@@ -131,8 +126,6 @@ public abstract class AbstractReloadable
 
                 return createInstance(reloadedClass);
             }
-
-            ;
         });
     }
 
@@ -147,25 +140,18 @@ public abstract class AbstractReloadable
     private Class reloadImplementationClass()
     {
         if (logger.isDebugEnabled())
+        {
             logger.debug(String.format("%s class %s.", firstTime ? "Loading" : "Reloading", implementationClassName));
+        }
 
-        ClassFactoryClassPool pool = new ClassFactoryClassPool(baseClassLoader);
-
-        ClassLoader threadDeadlockBuffer = new URLClassLoader(new URL[0], baseClassLoader);
-
-        Loader loader = new InternalLoader(threadDeadlockBuffer, pool);
-
-        ClassPath path = new LoaderClassPath(loader);
-
-        pool.appendClassPath(path);
+        loader = new PlasticClassLoader(baseClassLoader, this);
 
         classesToLoad.clear();
+
         add(implementationClassName);
 
         try
         {
-            loader.addTranslator(pool, this);
-
             Class result = loader.loadClass(implementationClassName);
 
             firstTime = false;
@@ -178,92 +164,127 @@ public abstract class AbstractReloadable
         }
     }
 
-    private boolean shouldLoadClassNamed(String name)
-    {
-        return classesToLoad.contains(name);
-    }
-
     private void add(String className)
     {
-        if (classesToLoad.contains(className))
-            return;
+        if (!classesToLoad.contains(className))
+        {
+            logger.debug(String.format("Marking class %s to be (re-)loaded", className));
 
-        logger.debug(String.format("Marking class %s to be (re-)loaded", className));
+            classesToLoad.add(className);
+        }
+    }
 
-        classesToLoad.add(className);
+    public boolean shouldInterceptClassLoading(String className)
+    {
+        return classesToLoad.contains(className);
     }
 
-    public void onLoad(ClassPool pool, String className) throws NotFoundException, CannotCompileException
+    public Class<?> loadAndTransformClass(String className) throws ClassNotFoundException
     {
         logger.debug(String.format("BEGIN Analyzing %s", className));
 
-        analyze(pool, className);
+        Class<?> result;
+
+        try
+        {
+            result = doClassLoad(className);
+        } catch (IOException ex)
+        {
+            throw new ClassNotFoundException(String.format("Unable to analyze and load class %s: %s", className,
+                    InternalUtils.toMessage(ex)), ex);
+        }
 
         trackClassFileChanges(className);
 
         logger.debug(String.format("  END Analyzing %s", className));
+
+        return result;
     }
 
-    private void analyze(ClassPool pool, String className) throws NotFoundException, CannotCompileException
+    public Class<?> doClassLoad(String className) throws IOException
     {
-        CtClass ctClass = pool.get(className);
+        ClassVisitor analyzer = new ClassAdapter(new EmptyVisitor())
+        {
+            @Override
+            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
+            {
+                String path = superName + ".class";
 
-        CtClass[] nestedClasses = ctClass.getNestedClasses();
+                URL url = baseClassLoader.getResource(path);
 
-        for (CtClass nc : nestedClasses)
-        {
-            add(nc.getName());
-        }
+                if (isFileURL(url))
+                {
+                    add(PlasticInternalUtils.toClassName(superName));
+                }
+            }
 
-        ctClass.instrument(new ExprEditor()
-        {
-            public void edit(ConstructorCall c) throws CannotCompileException
+            @Override
+            public void visitInnerClass(String name, String outerName, String innerName, int access)
             {
-                if (c.getMethodName().equals("this"))
-                    return;
+                add(PlasticInternalUtils.toClassName(name));
+            }
+        };
+
 
-                String cn = c.getClassName();
+        String path = PlasticInternalUtils.toClassPath(className);
 
-                String classFilePath = ClassFabUtils.getPathForClassNamed(cn);
+        InputStream stream = baseClassLoader.getResourceAsStream(path);
 
-                URL url = baseClassLoader.getResource(classFilePath);
+        assert stream != null;
 
-                // 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.
+        ByteArrayOutputStream classBuffer = new ByteArrayOutputStream(5000);
+        byte[] buffer = new byte[5000];
+
+        while (true)
+        {
+            int length = stream.read(buffer);
 
-                if (url != null && url.getProtocol().equals("file"))
-                    add(cn);
+            if (length < 0)
+            {
+                break;
             }
-        });
+
+            classBuffer.write(buffer, 0, length);
+        }
+
+        stream.close();
+
+        byte[] bytecode = classBuffer.toByteArray();
+
+        new ClassReader(new ByteArrayInputStream(bytecode)).accept(analyzer,
+                ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+
+
+        return loader.defineClassWithBytecode(className, bytecode);
     }
 
     private void trackClassFileChanges(String className)
     {
         if (isInnerClassName(className))
+        {
             return;
+        }
 
-        String path = ClassFabUtils.getPathForClassNamed(className);
+        String path = PlasticInternalUtils.toClassPath(className);
 
         URL url = baseClassLoader.getResource(path);
 
-        if (url != null && url.getProtocol().equals("file"))
+        if (isFileURL(url))
+        {
             changeTracker.add(url);
-    }
-
-    private boolean isInnerClassName(String className)
-    {
-        return className.indexOf('$') >= 0;
+        }
     }
 
     /**
-     * Does nothing.
+     * Returns true if the url is non-null, and is for the "file:" protocol.
      */
-    public void start(ClassPool pool) throws NotFoundException, CannotCompileException
+    private boolean isFileURL(URL url)
     {
-
+        return url != null && url.getProtocol().equals("file");
     }
 
+    private boolean isInnerClassName(String className)
+    {
+        return className.indexOf('$') >= 0;
+    }
 }