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;");