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
70
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
98
99
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
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
139 String name,
140 Object
141 int off,
142 int len,
143 ProtectionDomain pd) {
144 byte[] bytes = new byte[len];
145
146 byteBufferGet(byteBuffer, bytes, off, len);
147 byte[] newbytes = defineClass0Pre(caller, name, bytes, 0, bytes.length, pd);
148
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
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 }