Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }