Author: cpopetz
Date: 2009-05-13 21:29:51 -0400 (Wed, 13 May 2009)
New Revision: 10910
Modified:
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketClassLoader.java
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketInstrumentationTask.java
Log:
JBSEAM-4173
Modified:
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java
===================================================================
---
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2009-05-13
20:31:38 UTC (rev 10909)
+++
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/JavassistInstrumentor.java 2009-05-14
01:29:51 UTC (rev 10910)
@@ -120,6 +120,11 @@
* If true, only instrument classes annotated with @WicketComponent and their
non-static inner classes.
*/
private boolean scanAnnotations;
+
+ /**
+ * If we're only instrumenting a specific set of classes, these are the names of
those classes
+ */
+ private Set<String> onlyTheseClasses;
public JavassistInstrumentor(ClassPool classPool)
{
@@ -173,13 +178,32 @@
*/
public void instrumentClass(CtClass implementation) throws NotFoundException,
CannotCompileException
{
+
String className = implementation.getName();
CtClass handlerClass = classPool.get(WicketHandler.class.getName());
CtClass componentClass = classPool.get(WicketComponent.class.getName());
- CtField handlerField = new CtField(handlerClass, "handler",
implementation);
- Initializer handlerInitializer = Initializer.byCall(handlerClass,
"create");
- implementation.addField(handlerField, handlerInitializer);
+ /*
+ * We only want one WicketHandler field per bean, so don't add that field to
classes whose
+ * parent has been or is to be be instrumented.
+ */
+ CtClass superclass = implementation.getSuperclass();
+ if (!isInstrumented(superclass)) {
+ if (!isInstrumentable(superclass)) {
+ //we're the top-most instrumentable class, so add the handler field
+ CtField handlerField = new CtField(handlerClass, "handler",
implementation);
+ handlerField.setModifiers(Modifier.PROTECTED);
+ Initializer handlerInitializer = Initializer.byCall(handlerClass,
"create");
+ implementation.addField(handlerField, handlerInitializer);
+ CtMethod getHandlerMethod = CtNewMethod.getter("getHandler",
handlerField);
+ implementation.addMethod(getHandlerMethod);
+ }
+ else {
+ //in order for the below code to make reference to the handler instance we
need to
+ //recursively instrument until we reach the top of the instrumentable class
tree
+ instrumentClass(superclass);
+ }
+ }
CtField wicketComponentField = new CtField(componentClass, "component",
implementation);
wicketComponentField.setModifiers(Modifier.STATIC);
@@ -189,10 +213,8 @@
CtClass exception = classPool.get(Exception.class.getName());
implementation.addInterface(getInstrumentedComponentInterface());
- CtMethod getHandlerMethod = CtNewMethod.getter("getHandler",
handlerField);
- CtMethod getEnclosingInstance = CtNewMethod.make("public " +
InstrumentedComponent.class.getName() + " getEnclosingInstance() { return handler ==
null ? null : handler.getEnclosingInstance(this); }", implementation);
+ CtMethod getEnclosingInstance = CtNewMethod.make("public " +
InstrumentedComponent.class.getName() + " getEnclosingInstance() { return
getHandler() == null ? null : getHandler().getEnclosingInstance(this); }",
implementation);
implementation.addMethod(getEnclosingInstance);
- implementation.addMethod(getHandlerMethod);
for (CtMethod method : implementation.getDeclaredMethods())
{
@@ -216,9 +238,9 @@
{
{
String constructorObject = createConstructorObject(className,
constructor);
- constructor.insertBeforeBody(constructorObject +
"handler.beforeInvoke(this, constructor);");
- constructor.addCatch("{" + constructorObject + "throw new
RuntimeException(handler.handleException(this, constructor, e));}", exception,
"e");
- constructor.insertAfter(constructorObject +
"handler.afterInvoke(this, constructor);");
+ constructor.insertBeforeBody(constructorObject +
"getHandler().beforeInvoke(this, constructor);");
+ constructor.addCatch("{" + constructorObject + "throw new
RuntimeException(getHandler().handleException(this, constructor, e));}", exception,
"e");
+ constructor.insertAfter(constructorObject +
"getHandler().afterInvoke(this, constructor);");
log.trace("instrumented constructor " + constructor.getName());
}
}
@@ -235,7 +257,7 @@
*/
private static String createBody(CtClass clazz, CtMethod method, CtMethod newMethod)
throws NotFoundException
{
- String src = "{" + createMethodObject(clazz, method) + "if
(this.handler != null) this.handler.beforeInvoke(this, method);" +
createMethodDelegation(newMethod) + "if (this.handler != null) result = ($r)
this.handler.afterInvoke(this, method, ($w) result); return ($r) result;}";
+ String src = "{" + createMethodObject(clazz, method) + "if
(getHandler() != null) getHandler().beforeInvoke(this, method);" +
createMethodDelegation(newMethod) + "if (this.handler != null) result = ($r)
this.handler.afterInvoke(this, method, ($w) result); return ($r) result;}";
log.trace("Creating method " + clazz.getName() + "." +
newMethod.getName() + "(" + newMethod.getSignature() + ")" + src);
return src;
@@ -269,7 +291,7 @@
*/
private static String wrapInExceptionHandler(String src)
{
- return "try {" + src + "} catch (Exception e) { throw new
RuntimeException(this.handler == null ? e : this.handler.handleException(this, method,
e)); }";
+ return "try {" + src + "} catch (Exception e) { throw new
RuntimeException(getHandler() == null ? e : getHandler().handleException(this, method,
e)); }";
}
/**
@@ -390,6 +412,10 @@
{
return false;
}
+ if (onlyTheseClasses != null &&
!onlyTheseClasses.contains(clazz.getName()))
+ {
+ return false;
+ }
try
{
@@ -417,9 +443,8 @@
// do not instrument something we've already instrumented.
// can't use 'isSubtype' because the superclass may be instrumented
// while we are not
- for (String inf : clazz.getClassFile2().getInterfaces())
- if (inf.equals(getInstrumentedComponentInterface().getName()))
- return false;
+ if (isInstrumented(clazz))
+ return false;
}
catch (Exception e)
{
@@ -429,6 +454,14 @@
return true;
}
+ private boolean isInstrumented(CtClass clazz)
+ {
+ for (String inf : clazz.getClassFile2().getInterfaces())
+ if (inf.equals(getInstrumentedComponentInterface().getName()))
+ return true;
+ return false;
+ }
+
/**
* We have to look this up lazily because when our constructor is called we may not
have the appropriate paths added to our ClassPool,
* particularly if we are doing runtime instrumentation using WEB-INF/wicket
@@ -465,14 +498,18 @@
{
try
{
- CtClass result = instrumentClass(classfileBuffer);
- if (result == null)
+ CtClass clazz = classPool.get(className);
+ if (clazz.isModified())
+ return clazz.toBytecode();
+ clazz = instrumentClass(classfileBuffer);
+ if (clazz == null)
return null;
else
- return result.toBytecode();
+ return clazz.toBytecode();
}
catch (Exception e)
{
+ e.printStackTrace();
throw new RuntimeException(e);
}
}
@@ -529,4 +566,32 @@
classPool.appendSystemPath();
instrumentation.addTransformer(new JavassistInstrumentor(classPool,
packagesToInstrument, scanAnnotations));
}
+
+ /**
+ * This instruments a specific set of classes and writes their classes to the
specified directory, if that
+ * directory is non-null
+ * @param toInstrument the set of class names to instrument
+ * @param path where to write the modified classes, or null to not write anything
+ * @throws CannotCompileException
+ * @throws NotFoundException
+ */
+ public void instrumentClassSet(Set<String> toInstrument, String path) throws
CannotCompileException, NotFoundException
+ {
+ this.onlyTheseClasses = toInstrument;
+ for (String className : toInstrument)
+ {
+ CtClass clazz = instrumentClass(className);
+ if (path != null && clazz.isModified())
+ {
+ try
+ {
+ clazz.writeFile(path);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
}
Modified:
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketClassLoader.java
===================================================================
---
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketClassLoader.java 2009-05-13
20:31:38 UTC (rev 10909)
+++
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketClassLoader.java 2009-05-14
01:29:51 UTC (rev 10910)
@@ -5,8 +5,8 @@
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
import javassist.CannotCompileException;
import javassist.ClassPool;
@@ -22,7 +22,7 @@
private static LogProvider log = Logging.getLogProvider(WicketClassLoader.class);
- private List<String> classes;
+ private Set<String> classes;
private File wicketComponentDirectory;
private ClassPool classPool;
private ClassLoader parentLoader;
@@ -33,7 +33,7 @@
public WicketClassLoader(ClassLoader parent, ClassPool classPool, File
wicketComponentDirectory)
{
super(parent, classPool);
- this.classes = new ArrayList<String>();
+ this.classes = new HashSet<String>();
this.wicketComponentDirectory = wicketComponentDirectory;
this.classPool = classPool;
this.parentLoader = parent;
@@ -48,7 +48,8 @@
// Scan for classes
if (wicketComponentDirectory.exists())
{
- handleDirectory(wicketComponentDirectory, null);
+ handleDirectory(wicketComponentDirectory, null, classes);
+ instrumentor.instrumentClassSet(classes,null);
}
// Ensure classes' static initializers have run, to register the classes
@@ -60,7 +61,15 @@
return this;
}
- private void handleDirectory(File file, String path) throws NotFoundException,
CannotCompileException
+ /**
+ * Recursively collect all class names for class files found in this directory.
+ * @param file which directory
+ * @param path parent path
+ * @param collectedClasses where to store the classes
+ * @throws NotFoundException
+ * @throws CannotCompileException
+ */
+ private void handleDirectory(File file, String path, Set<String>
collectedClasses) throws NotFoundException, CannotCompileException
{
log.trace("directory: " + file);
for (File child : file.listFiles())
@@ -68,22 +77,18 @@
String newPath = path == null ? child.getName() : path + '/' +
child.getName();
if (child.isDirectory())
{
- handleDirectory(child, newPath);
+ handleDirectory(child, newPath, collectedClasses);
}
else
{
- handleItem(newPath);
+ if (newPath.endsWith(".class"))
+ {
+ collectedClasses.add(filenameToClassname(newPath));
+ }
}
}
}
- private void handleItem(String path) throws NotFoundException, CannotCompileException
- {
- if (path.endsWith(".class"))
- {
- classes.add(instrumentor.instrumentClass(filenameToClassname(path)).getName());
- }
- }
@Override
protected Class loadClassByDelegation(String name) throws ClassNotFoundException
Modified:
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketInstrumentationTask.java
===================================================================
---
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketInstrumentationTask.java 2009-05-13
20:31:38 UTC (rev 10909)
+++
branches/community/Seam_2_1/src/wicket/org/jboss/seam/wicket/ioc/WicketInstrumentationTask.java 2009-05-14
01:29:51 UTC (rev 10910)
@@ -2,7 +2,9 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javassist.ClassPool;
import javassist.CtClass;
@@ -75,18 +77,15 @@
JavassistInstrumentor instrumentor = new
JavassistInstrumentor(classPool,useAnnotations);
+ Set<String> toInstrument = new HashSet<String>();
for (String file :
fileset.getDirectoryScanner(getProject()).getIncludedFiles())
{
if (file.endsWith(".class"))
{
-
instrumentedClasses.add(instrumentor.instrumentClass(filenameToClassname(file)));
+ toInstrument.add(filenameToClassname(file));
}
}
-
- for (CtClass clazz : instrumentedClasses)
- {
- clazz.writeFile(outputDirectory.getPath());
- }
+ instrumentor.instrumentClassSet(toInstrument,outputDirectory.getPath());
}
catch (Exception e)
{