Source code: com/chaoswg/xtc4y/classloaders/LogClassLoader.java
1
2 //$Header: /cvsroot/xtc4y/xtc4y/src/com/chaoswg/xtc4y/classloaders/LogClassLoader.java,v 1.4 2003/09/08 21:10:50 toggm Exp $
3 /******************************************************************************
4 * XTC4y - eXtreme Testing Collection 4 you *
5 * -------------------------------------------------------------------------- *
6 * URL: http://www.chaoswg.com/xtc4y *
7 * Author: Mike Toggweiler (2.dog@gmx.ch) *
8 * *
9 * Last Updated: $Date: 2003/09/08 21:10:50 $, by $Author: toggm $ *
10 * Version: $Revision: 1.4 $ *
11 * -------------------------------------------------------------------------- *
12 * COPYRIGHT: (c) 2003 by Mike Toggweiler *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 *****************************************************************************/
19 package com.chaoswg.xtc4y.classloaders;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.DataInputStream;
24 import java.io.DataOutputStream;
25 import java.io.InputStream;
26 import java.io.IOException;
27 import java.io.FileOutputStream;
28
29 import java.util.Vector;
30 import java.util.HashMap;
31
32 import com.chaoswg.util.ExceptionPrinter;
33
34 import com.chaoswg.xtc4y.classdesc.AttributeInfo;
35 import com.chaoswg.xtc4y.classdesc.Attribute;
36 import com.chaoswg.xtc4y.classdesc.SourceFileAttribute;
37 import com.chaoswg.xtc4y.classdesc.BaseType;
38 import com.chaoswg.xtc4y.classdesc.ClassCPEntry;
39 import com.chaoswg.xtc4y.classdesc.ClassDescriptor;
40 import com.chaoswg.xtc4y.classdesc.ClassAccessFlags;
41 import com.chaoswg.xtc4y.classdesc.CodeAttribute;
42 import com.chaoswg.xtc4y.classdesc.FieldAccessFlags;
43 import com.chaoswg.xtc4y.classdesc.FieldInfo;
44 import com.chaoswg.xtc4y.classdesc.FieldrefCPEntry;
45 import com.chaoswg.xtc4y.classdesc.MethodAccessFlags;
46 import com.chaoswg.xtc4y.classdesc.MethodInfo;
47 import com.chaoswg.xtc4y.classdesc.NameAndTypeCPEntry;
48 import com.chaoswg.xtc4y.classdesc.MethodrefCPEntry;
49 import com.chaoswg.xtc4y.classdesc.StringCPEntry;
50 import com.chaoswg.xtc4y.classdesc.code.Code;
51 import com.chaoswg.xtc4y.classdesc.code.instructions.UnaryInstruction;
52 import com.chaoswg.xtc4y.classdesc.code.instructions.Instruction;
53 import com.chaoswg.xtc4y.classdesc.code.instructions.InvokeVirtual;
54 import com.chaoswg.xtc4y.classdesc.code.instructions.InvokeStatic;
55 import com.chaoswg.xtc4y.classdesc.code.instructions.GetField;
56 import com.chaoswg.xtc4y.classdesc.code.instructions.PutField;
57 import com.chaoswg.xtc4y.classdesc.code.instructions.LDC_W;
58 import com.chaoswg.xtc4y.classdesc.code.instructions.CPRefInstruction;
59
60 /**
61 * This is a classloader appending to every implemented method a begin
62 * and end log message of the level DEBUG using log4j. The log4j framework
63 * may be configured using the following the system property
64 * 'log4j.configuration', i.e. starting the application with
65 * java -Dlog4j.configuration=foobar.properties com.athome.myclass
66 * @author Mike Toggweiler
67 **/
68 public class LogClassLoader extends ClassLoader {
69 private MethodrefCPEntry getLogger;
70 private InvokeVirtual debug;
71 private FieldrefCPEntry outRef;
72 private boolean writeGeneratedFile = false;
73 private HashMap cached = new HashMap();
74
75 private static final String[] PROHIBITED_PCKG = new String[]{
76 "java.lang", "java.awt", "java.io", "java.util", "java.rmi",
77 "javax.swing"};
78 private static final String LOG4J_PKG = "org.apache.log4j";
79
80 public LogClassLoader() {
81 NameAndTypeCPEntry nat =
82 new NameAndTypeCPEntry("getLogger",
83 "(Ljava/lang/String;)"+
84 "Lorg/apache/log4j/Logger;");
85 ClassCPEntry classCP = new ClassCPEntry("org/apache/log4j/Logger");
86 getLogger = new MethodrefCPEntry(classCP, nat);
87
88 nat = new NameAndTypeCPEntry("info", "(Ljava/lang/Object;)V");
89 classCP = new ClassCPEntry("org/apache/log4j/Logger");
90 MethodrefCPEntry debugM = new MethodrefCPEntry(classCP, nat);
91 debug = new InvokeVirtual(debugM);
92 }
93
94 /**
95 * Overwrite default loadClass behaviour. Instead of first delegating to
96 * find the class through the parent classloader, try to load it through
97 * this classloader
98 * @param name the name of the class to be loaded
99 * @param resolve true if the class should be resolved
100 * @return the class if found
101 * @throws ClassNotFoundException if class was not found
102 **/
103 protected Class loadClass(String name, boolean resolve)
104 throws ClassNotFoundException {
105 Class cl = null;
106 try {
107
108 cl = findLoadedClass(name);
109
110 if (cl == null) {
111 cl = findClass(name);
112 }
113 }
114 catch (Exception e) {
115 //if an exception occurs, try to load class through parent class
116 //loader. This should be removed when all the features of a class
117 //are implemented
118 if (!(e instanceof RuntimeException) ||
119 e instanceof SecurityException) {
120 System.out.println(ExceptionPrinter.getStackTrace(e));
121 }
122 cl = getParent().loadClass(name);
123 }
124
125 if (resolve) {
126 resolveClass(cl);
127 }
128 return cl;
129 }
130
131 /**
132 * Find a class by name
133 * @param name the name of the class
134 * @return a class if found
135 * @exception ClassNotFoundException
136 **/
137 protected Class findClass(String name) throws ClassNotFoundException {
138 try {
139 if (name.startsWith(LOG4J_PKG)) {
140 //don't accept classfiles in the log4j package
141 //otherwise it would run in an endless loop
142 throw new RuntimeException();
143 }
144 for (int i=0; i<PROHIBITED_PCKG.length; ++i) {
145 if (name.startsWith(PROHIBITED_PCKG[i])) {
146 throw new RuntimeException();
147 }
148 }
149
150 //try to load class through the current resources
151 String className = name.replace('.', '/') + ".class";
152 InputStream is = getResourceAsStream(className);
153 if (is == null) {
154 throw new IOException("Couldn't find resource for name:"+
155 className);
156 }
157
158 //load class through classDescriptor
159 DataInputStream dis = new DataInputStream(is);
160 ClassDescriptor descriptor = new ClassDescriptor(dis);
161 dis.close();
162
163 //check if the class is a interface, if so, ignore modifications
164 ClassAccessFlags classAccess = descriptor.getClassAccessFlags();
165 if (!classAccess.isInterface()) {
166 //append log messages to every implemented method
167 MethodInfo[] methods = descriptor.getMethods();
168 //create fieldrefCPEntry for the instance variable
169 String nm = "log_"+hashCode();
170 String desc = "Lorg/apache/log4j/Logger;";
171 FieldAccessFlags fieldAccess = new FieldAccessFlags((short)0);
172 fieldAccess.setPrivate();
173 descriptor.addField(new FieldInfo(fieldAccess, nm, desc));
174
175 NameAndTypeCPEntry nat =
176 new NameAndTypeCPEntry(nm, desc);
177 ClassCPEntry classCP =
178 new ClassCPEntry(className.substring(0, className.length()
179 - 6));
180 FieldrefCPEntry field = new FieldrefCPEntry(classCP, nat);
181
182 for (int i=0; i<methods.length; ++i) {
183 MethodAccessFlags access =
184 (MethodAccessFlags)methods[i].getAccessFlags();
185 if (!access.isAbstract() && !access.isStatic() &&
186 !methods[i].isInternalConstructor()) {
187 appendLogMessage(methods[i], name, field);
188 }
189 }
190 }
191
192 //print new class into byte array
193 ByteArrayOutputStream baos = new ByteArrayOutputStream();
194 DataOutputStream dos = new DataOutputStream(baos);
195
196
197 //write new class
198 descriptor.writeClass(dos);
199
200 byte[] bytes = baos.toByteArray();
201
202 dos.close();
203 baos.close();
204
205 if (writeGeneratedFile) {
206 try {
207 writeClassFile(bytes, className);
208 }
209 catch (IOException ioe) {
210 System.out.println("Couldn't write class, cause is:"+
211 ioe.getMessage());
212 }
213 }
214
215 return defineClass(name, bytes, 0, bytes.length);
216 }
217 catch (IOException ioe) {
218 System.out.println("Couldn't load class data:"+
219 ExceptionPrinter.getStackTrace(ioe));
220 }
221 throw new ClassNotFoundException();
222 }
223
224 /**
225 * Append a log message to a method
226 **/
227 private void appendLogMessage(MethodInfo method, String className,
228 FieldrefCPEntry field) {
229 AttributeInfo[] attrs = method.getAttributes();
230 boolean isConstructor = method.isConstructor();
231
232 for (int i=0; i<attrs.length; ++i) {
233 if (attrs[i].getAttribute() instanceof CodeAttribute) {
234 CodeAttribute codeAttr =
235 (CodeAttribute)attrs[i].getAttribute();
236 Code code = codeAttr.getCode();
237
238 //create strings to append
239 String prefix = method.getName()+method.getDescriptor();
240 String beginMessage = prefix + ":Begin";
241 String endMessage = prefix + ":End";
242
243 short index = 0;
244 if (isConstructor) {
245 //search the index of the invokespecial instruction and
246 //append log message after this
247 for (short j=0; j<code.getLength(); ++j) {
248 Object instr = code.getInstructionAt(j);
249 if (instr instanceof Instruction &&
250 ((Instruction)instr).getOpcode() ==
251 CPRefInstruction.INVOKESPECIAL) {
252 index = (short)(j+1);
253 break;
254 }
255 }
256 }
257
258
259 //print begin message
260 code.insertInstructionAt(debug, index);
261 code.insertInstructionAt(new
262 LDC_W(new StringCPEntry(beginMessage)),
263 index);
264 code.insertInstructionAt(new GetField(field), index);
265 code.insertInstructionAt(new UnaryInstruction(UnaryInstruction.ALOAD_0), index);
266
267 if (isConstructor) {
268 //instanciate logger
269 code.insertInstructionAt(new PutField(field), index);
270 code.insertInstructionAt(new InvokeStatic(getLogger),
271 index);
272 code.insertInstructionAt(new
273 LDC_W(new StringCPEntry(className)),
274 index);
275 code.insertInstructionAt(new UnaryInstruction(UnaryInstruction.ALOAD_0), index);
276 }
277
278 short length = (short)code.getLength();
279
280 //determine if a return value is expected
281 index = (short)(length-1);
282
283 //print end message
284 code.insertInstructionAt(debug, index);
285 code.insertInstructionAt(new
286 LDC_W(new StringCPEntry(endMessage)),
287 index);
288 code.insertInstructionAt(new GetField(field), index);
289 code.insertInstructionAt(new UnaryInstruction(UnaryInstruction.ALOAD_0), index);
290
291 //increment stack size by two
292 codeAttr.setMaxStack((short)(codeAttr.getMaxStack()+2));
293 }
294 }
295 }
296
297 /**
298 * Write an array of bytes into an output file
299 * @param bytes the byte array to write
300 * @param name the classname
301 **/
302 private void writeClassFile(byte[] bytes, String name) throws IOException {
303 //org.apache.log4j.Logger.getLogger("logger").debug("test");
304 FileOutputStream fileoutputstream = new FileOutputStream(name);
305 fileoutputstream.write(bytes);
306 fileoutputstream.close();
307 }
308 }