1   package org.apache.commons.javaflow.bytecode.transformation;
2   
3   import java.io.IOException;
4   import java.io.InputStream;
5   
6   import org.apache.commons.io.IOUtils;
7   import org.apache.commons.javaflow.bytecode.BytecodeClassLoader;
8   import org.objectweb.asm.ClassReader;
9   import org.objectweb.asm.ClassWriter;
10  import org.objectweb.asm.Label;
11  import org.objectweb.asm.MethodVisitor;
12  import org.objectweb.asm.Opcodes;
13  
14  public class ClassTransformerClassLoader extends BytecodeClassLoader {
15  
16      private final ResourceTransformer transformer;
17      private final String prefix;
18  
19      public ClassTransformerClassLoader(final ResourceTransformer pTransformer) {
20      	this("org.apache.commons.javaflow.bytecode.transformation.", pTransformer);
21      }
22  
23      public ClassTransformerClassLoader(final String pPrefix, final ResourceTransformer pTransformer) {
24          prefix = pPrefix;
25          transformer = pTransformer;
26      }
27  
28      protected byte[] transform(final String pName, final InputStream pClassStream) throws IOException {
29          final byte[] oldClass = IOUtils.toByteArray(pClassStream);
30          final byte[] newClass = transformer.transform(oldClass);
31  
32  //    	CheckClassAdapter.verify(new ClassReader(newClass), true);
33  
34  //        
35  //    	new ClassReader(oldClass).accept(
36  //    			new ASMifierClassVisitor(
37  //    					new PrintWriter(
38  //    							new FileOutputStream(
39  //    									transformer.getClass().getSimpleName() + pName + ".old"))), false);
40  //
41  //    	new ClassReader(newClass).accept(
42  //    			new ASMifierClassVisitor(
43  //    					new PrintWriter(
44  //    							new FileOutputStream(
45  //    									transformer.getClass().getSimpleName() + pName + ".new"))), false);
46      	
47          return newClass;
48      }
49  
50      
51      public Class loadLocalClass( final String name ) throws ClassNotFoundException {
52      	return loadClass( prefix + name);
53      }
54      
55  
56      public Class createImplementationOf( final Class pAbstractClass ) {
57      	
58      	final String base = pAbstractClass.getName().replace('.', '/');
59      	final int slash = base.lastIndexOf('/');
60      	final String packageName = base.substring(0, slash);
61      	final String name = base.substring(packageName.length() + "Abstract".length() + 1);
62      	final String extended = packageName + "/" + name + "Impl";
63      	
64      	ClassWriter cw = new ClassWriter(true);
65      	MethodVisitor mv;
66  
67      	cw.visit(Opcodes.V1_2, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
68      			extended, null,
69      			base, null);
70  
71      	cw.visitSource(name + "Impl.java", null);
72  
73      	mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
74      	mv.visitCode();
75      	Label l0 = new Label();
76      	mv.visitLabel(l0);
77      	mv.visitLineNumber(3, l0);
78      	mv.visitVarInsn(Opcodes.ALOAD, 0);
79      	mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
80      			base,
81      			"<init>", "()V");
82      	mv.visitInsn(Opcodes.RETURN);
83      	Label l1 = new Label();
84      	mv.visitLabel(l1);
85      	mv.visitLocalVariable("this",
86      			"L" + extended + ";",
87      			null, l0, l1, 0);
88      	mv.visitMaxs(1, 1);
89      	mv.visitEnd();
90  
91      	cw.visitEnd();
92  
93      	return loadClass(cw.toByteArray());    	    	
94      }
95      
96      
97      public Class loadClass( final String name ) throws ClassNotFoundException {
98          if (name.startsWith(prefix)) {
99  
100             try {
101                 final InputStream is = getClass().getResourceAsStream(
102                         "/" + name.replace('.', '/') + ".class");
103 
104                 byte[] bytecode;
105                 if (name.indexOf(".rewrite.") >= 0) {
106 //                    System.err.println("Instrumenting: " + name);
107 
108                 	bytecode = transform(name, is);
109 
110                 } else {
111                     ClassReader cr = new ClassReader(is);
112                     bytecode = cr.b;
113                 }
114                 
115                 return super.defineClass(name, bytecode, 0, bytecode.length);
116 
117             } catch (Throwable ex) {
118                 System.err.println("Load error: " + ex.toString());
119                 ex.printStackTrace();
120                 throw new ClassNotFoundException(name + " " + ex.getMessage(), ex);
121             }
122         }
123 //        System.err.println("Other classloader loading: " + name);
124         return super.loadClass(name);
125     }
126 }