View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.hook.impl;
9   
10  import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
11  
12  import java.security.ProtectionDomain;
13  import java.lang.reflect.Method;
14  
15  /***
16   * Helper class called by the modified java.lang.ClassLoader. <p/>This class is called at different points by the
17   * modified java.lang.ClassLoader of the org.codehaus.aspectwerkz.hook.impl.ClassLoaderPreProcessorImpl implemention.
18   * <br/>This class must reside in the -Xbootclasspath when AspectWerkz layer 1 is used, but the effective implementation
19   * of the class preprocessor (AspectWerkz layer 2) can be in standard system classpath (-cp).
20   *
21   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
22   */
23  public class ClassPreProcessorHelper {
24      /***
25       * ClassPreProcessor used if aspectwerkz.classloader.preprocessor property is defined to full qualified class name
26       */
27      private static ClassPreProcessor preProcessor;
28  
29      /***
30       * true if preProcesor already initalized
31       */
32      private static boolean preProcessorInitialized;
33  
34      /***
35       * option used to defined the class preprocessor
36       */
37      private static String PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";
38  
39      /***
40       * default class preprocessor
41       */
42      private static String PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";
43  
44      static {
45          initializePreProcessor();
46      }
47  
48      /***
49       * Returns the configured class preprocessor Should be called after initialization only
50       *
51       * @return the preprocessor or null if not initialized
52       */
53      public static ClassPreProcessor getClassPreProcessor() {
54          return preProcessor;
55      }
56  
57      /***
58       * Initialization of the ClassPreProcessor The ClassPreProcessor implementation is lazy loaded. This allow to put it
59       * in the regular classpath whereas the instrumentation layer (layer 1) is in the bootclasspath
60       */
61      public static synchronized void initializePreProcessor() {
62          if (preProcessorInitialized) {
63              return;
64          }
65          preProcessorInitialized = true;
66          Class klass = null;
67          String s = System.getProperty(PRE_PROCESSOR_CLASSNAME_PROPERTY, PRE_PROCESSOR_CLASSNAME_DEFAULT);
68          try {
69              // force loading thru System class loader to allow
70              // preprocessor implementation to be in standard classpath
71              klass = Class.forName(s, true, ClassLoader.getSystemClassLoader());
72          } catch (ClassNotFoundException _ex) {
73              System.err.println("AspectWerkz - WARN - Pre-processor class '" + s + "' not found");
74          }
75          if (klass != null) {
76              try {
77                  preProcessor = (ClassPreProcessor) klass.newInstance();
78                  preProcessor.initialize();
79                  System.out.println("AspectWerkz - INFO - Pre-processor " + s + " loaded and initialized");
80              } catch (Throwable throwable) {
81                  System.err.println("AspectWerkz - WARN - Error initializing pre-processor class " + s + ':');
82                  throwable.printStackTrace();
83              }
84          }
85      }
86  
87      /***
88       * byte code instrumentation of class loaded
89       */
90      public static byte[] defineClass0Pre(ClassLoader caller,
91                                           String name,
92                                           byte[] b,
93                                           int off,
94                                           int len,
95                                           ProtectionDomain pd) {
96          if (preProcessor == null) {
97              // we need to check this due to reentrancy when ClassPreProcessorHelper is beeing
98              // initialized
99              // since it tries to load a ClassPreProcessor implementation
100             byte[] obyte = new byte[len];
101             System.arraycopy(b, off, obyte, 0, len);
102             return obyte;
103         } else {
104             try {
105                 byte[] ibyte = new byte[len];
106                 System.arraycopy(b, off, ibyte, 0, len);
107                 return preProcessor.preProcess(name, ibyte, caller);
108             } catch (Throwable throwable) {
109                 System.err.println(
110                         "AspectWerkz - WARN - Error pre-processing class "
111                         + name
112                         + " in "
113                         + Thread.currentThread()
114                 );
115                 throwable.printStackTrace();
116                 // fallback to unweaved bytecode
117                 byte[] obyte = new byte[len];
118                 System.arraycopy(b, off, obyte, 0, len);
119                 return obyte;
120             }
121         }
122     }
123 
124     /***
125      * Byte code instrumentation of class loaded using Java 5 style thru NIO
126      * Since Java 5 comes with JVMTI this helper should be rarely used.
127      * We do no reference ByteBuffer directly to allow Java 1.3 compilation, though
128      * this helper will be really slow
129      *
130      * @param caller
131      * @param name
132      * @param byteBuffer Object that is instance of Java 1.4 NIO ButeBuffer
133      * @param off
134      * @param len
135      * @param pd
136      * @return Object instance of Java 1.4 NIO ByteBuffer
137      */
138     public static Object/*java.nio.ByteBuffer*/ defineClass0Pre(ClassLoader caller,
139                                              String name,
140                                              Object/*java.nio.ByteBuffer*/ byteBuffer,
141                                              int off,
142                                              int len,
143                                              ProtectionDomain pd) {
144         byte[] bytes = new byte[len];
145         //Java 1.4 : byteBuffer.get(bytes, off, len);
146         byteBufferGet(byteBuffer, bytes, off, len);
147         byte[] newbytes = defineClass0Pre(caller, name, bytes, 0, bytes.length, pd);
148         //Java 1.4 : ByteBuffer newBuffer = ByteBuffer.wrap(newbytes);
149         Object newBuffer = byteBufferWrap(newbytes);
150         return newBuffer;
151     }
152 
153     /***
154      * Equivalent to Java 1.4 NIO aByteBuffer.get(bytes, offset, length) to populate
155      * the bytes array from the aByteBuffer.
156      *
157      * @param byteBuffer
158      * @param dest
159      * @param offset
160      * @param length
161      */
162     private static void byteBufferGet(Object byteBuffer, byte[] dest, int offset, int length) {
163         try {
164             Class cByteBuffer = Class.forName("java.nio.ByteBuffer");
165             Method mGet = cByteBuffer.getDeclaredMethod("get", new Class[]{BYTE_ARRAY_CLASS, int.class, int.class});
166             mGet.invoke(byteBuffer, new Object[]{dest, new Integer(offset), new Integer(length)});
167         } catch (Throwable t) {
168             System.err.println("AW : java.nio not supported");
169             throw new RuntimeException(t.toString());
170         }
171     }
172 
173     /***
174      * Equivalent to Java 1.4 NIO static ByteBuffer.wrap(bytes) to create
175      * a new byteBuffer instance.
176      *
177      * @param bytes
178      * @return a ByteBuffer
179      */
180     private static Object/*java.nio.ByteBuffer*/ byteBufferWrap(byte[] bytes) {
181         try {
182             Class cByteBuffer = Class.forName("java.nio.ByteBuffer");
183             Method mGet = cByteBuffer.getDeclaredMethod("wrap", new Class[]{BYTE_ARRAY_CLASS});
184             Object byteBuffer = mGet.invoke(null, new Object[]{bytes});
185             return byteBuffer;
186         } catch (Throwable t) {
187             System.err.println("AW : java.nio not supported");
188             throw new RuntimeException(t.toString());
189         }
190     }
191 
192     private final static byte[] EMPTY_BYTEARRAY = new byte[0];
193     private final static Class BYTE_ARRAY_CLASS = EMPTY_BYTEARRAY.getClass();
194 
195 }