package test3; import javassist.*; import java.lang.reflect.Method; import java.lang.reflect.Field; /* Test code */ class EnhanceTest { public EnhanceTest() { super(); } public void foo(String s) { System.out.println(s); } } public class Enhancer { private ClassPool pool; private CtClass superClass; private CtClass thisClass; private Class thisJavaClass; private Interceptor interceptor; private int unique; private static final String INTERCEPTOR = "interceptor"; /* Test method */ public static void main(String[] args) throws Exception { Enhancer e = new Enhancer(test3.EnhanceTest.class); e.overrideAll(); e.setCallback(new Interceptor() { public Object invoke(Object self, Method m, Object[] args) throws Exception { System.out.println("intercept: " + m); return m.invoke(self, args); } }); Class c = e.createClass(); EnhanceTest obj = (EnhanceTest)c.getConstructor().newInstance(); obj.foo("test"); } public static interface Interceptor { Object invoke(Object self, Method m, Object[] args) throws Exception; } public Enhancer(Class clazz) throws CannotCompileException, NotFoundException { this(makeClassPool(clazz).get(clazz.getName())); } private static ClassPool makeClassPool(Class clazz) { ClassPool cp = new ClassPool(); cp.appendSystemPath(); cp.insertClassPath(new ClassClassPath(clazz)); return cp; } public Enhancer(CtClass superClass) throws CannotCompileException, NotFoundException { this.pool = superClass.getClassPool(); this.superClass = superClass; String name = superClass.getName() + "_proxy"; thisClass = pool.makeClass(name); thisClass.setSuperclass(superClass); String src = "public static " + this.getClass().getName() + ".Interceptor " + INTERCEPTOR + ";"; thisClass.addField(CtField.make(src, thisClass)); this.thisJavaClass = null; unique = 0; } public void overrideAll() throws CannotCompileException, NotFoundException { CtMethod[] methods = superClass.getMethods(); String delegatorNamePrefix = thisClass.makeUniqueName("d"); for (int i = 0; i < methods.length; i++) { CtMethod m = methods[i]; int mod = m.getModifiers(); if (!Modifier.isFinal(mod) && !Modifier.isAbstract(mod) && !Modifier.isStatic(mod)) override(m, delegatorNamePrefix + i); } } public void override(CtMethod m, String delegatorName) throws CannotCompileException, NotFoundException { String fieldName = "m" + unique++; thisClass.addField( CtField.make("private java.lang.reflect.Method " + fieldName + ";", thisClass)); CtMethod delegator = CtNewMethod.delegator(m, thisClass); delegator.setModifiers(Modifier.clear(delegator.getModifiers(), Modifier.NATIVE)); delegator.setName(delegatorName); thisClass.addMethod(delegator); thisClass.addMethod(makeMethod(m, fieldName, delegatorName)); } private CtMethod makeMethod(CtMethod m, String fieldName, String delegatorName) throws CannotCompileException, NotFoundException { String factory = this.getClass().getName() + ".findMethod(this, \"" + delegatorName + "\");"; String body = "{ if (" + fieldName + " == null) " + fieldName + " = " + factory + "return ($r)" + INTERCEPTOR + ".invoke(this, " + fieldName + ", $args); }"; CtMethod m2 = CtNewMethod.make(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes(), body, thisClass); m2.setModifiers(Modifier.clear(m.getModifiers(), Modifier.NATIVE)); return m2; } /* A runtime support routine called by an enhanced object. */ public static Method findMethod(Object self, String name) { Method[] methods = self.getClass().getMethods(); int n = methods.length; for (int i = 0; i < n; i++) if (methods[i].getName().equals(name)) return methods[i]; throw new RuntimeException("not found " + name + " in " + self.getClass()); } public Class createClass() { if (thisJavaClass == null) try { thisClass.debugWriteFile(); thisJavaClass = thisClass.toClass(); setInterceptor(); } catch (CannotCompileException e) { throw new RuntimeException(e); } return thisJavaClass; } private static void writeFile(CtClass cc) { try { cc.stopPruning(true); cc.writeFile(); cc.defrost(); cc.stopPruning(false); } catch (Exception e) { throw new RuntimeException(e); } } public void setCallback(Interceptor mi) { interceptor = mi; setInterceptor(); } private void setInterceptor() { if (thisJavaClass != null && interceptor != null) try { Field f = thisJavaClass.getField(INTERCEPTOR); f.set(null, interceptor); } catch (Exception e) { throw new RuntimeException(e); } } }