/**************************************************************************************
* Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.hook.impl;
import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
import java.security.ProtectionDomain;
import java.lang.reflect.Method;
/**
* Helper class called by the modified java.lang.ClassLoader. <p/>This class is called at different points by the
* modified java.lang.ClassLoader of the org.codehaus.aspectwerkz.hook.impl.ClassLoaderPreProcessorImpl implemention.
* <br/>This class must reside in the -Xbootclasspath when AspectWerkz layer 1 is used, but the effective implementation
* of the class preprocessor (AspectWerkz layer 2) can be in standard system classpath (-cp).
*
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
*/
public class ClassPreProcessorHelper {
/**
* ClassPreProcessor used if aspectwerkz.classloader.preprocessor property is defined to full qualified class name
*/
private static ClassPreProcessor preProcessor;
/**
* true if preProcesor already initalized
*/
private static boolean preProcessorInitialized;
/**
* option used to defined the class preprocessor
*/
private static String PRE_PROCESSOR_CLASSNAME_PROPERTY = "aspectwerkz.classloader.preprocessor";
/**
* default class preprocessor
*/
private static String PRE_PROCESSOR_CLASSNAME_DEFAULT = "org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor";
static {
initializePreProcessor();
}
/**
* Returns the configured class preprocessor Should be called after initialization only
*
* @return the preprocessor or null if not initialized
*/
public static ClassPreProcessor getClassPreProcessor() {
return preProcessor;
}
/**
* Initialization of the ClassPreProcessor The ClassPreProcessor implementation is lazy loaded. This allow to put it
* in the regular classpath whereas the instrumentation layer (layer 1) is in the bootclasspath
*/
public static synchronized void initializePreProcessor() {
if (preProcessorInitialized) {
return;
}
preProcessorInitialized = true;
Class klass = null;
String s = System.getProperty(PRE_PROCESSOR_CLASSNAME_PROPERTY, PRE_PROCESSOR_CLASSNAME_DEFAULT);
try {
// force loading thru System class loader to allow
// preprocessor implementation to be in standard classpath
klass = Class.forName(s, true, ClassPreProcessor.class.getClassLoader());
//klass = Class.forName(s, true, ClassLoader.getSystemClassLoader());
} catch (ClassNotFoundException _ex) {
System.err.println("AspectWerkz - WARN - Pre-processor class '" + s + "' not found");
}
if (klass != null) {
try {
preProcessor = (ClassPreProcessor) klass.newInstance();
preProcessor.initialize();
} catch (Throwable throwable) {
System.err.println("AspectWerkz - WARN - Error initializing pre-processor class " + s + ':');
throwable.printStackTrace();
}
}
}
/**
* byte code instrumentation of class loaded
*/
public static byte[] defineClass0Pre(ClassLoader caller,
String name,
byte[] b,
int off,
int len,
ProtectionDomain pd) {
if (preProcessor == null) {
// we need to check this due to reentrancy when ClassPreProcessorHelper is beeing
// initialized
// since it tries to load a ClassPreProcessor implementation
byte[] obyte = new byte[len];
System.arraycopy(b, off, obyte, 0, len);
return obyte;
} else {
try {
byte[] ibyte = new byte[len];
System.arraycopy(b, off, ibyte, 0, len);
return preProcessor.preProcess(name, ibyte, caller);
} catch (Throwable throwable) {
System.err.println(
"AspectWerkz - WARN - Error pre-processing class "
+ name
+ " in "
+ Thread.currentThread()
);
throwable.printStackTrace();
// fallback to unweaved bytecode
byte[] obyte = new byte[len];
System.arraycopy(b, off, obyte, 0, len);
return obyte;
}
}
}
/**
* Byte code instrumentation of class loaded using Java 5 style thru NIO
* Since Java 5 comes with JVMTI this helper should be rarely used.
* We do no reference ByteBuffer directly to allow Java 1.3 compilation, though
* this helper will be really slow
*
* @param caller
* @param name
* @param byteBuffer Object that is instance of Java 1.4 NIO ButeBuffer
* @param off
* @param len
* @param pd
* @return Object instance of Java 1.4 NIO ByteBuffer
*/
public static Object/*java.nio.ByteBuffer*/ defineClass0Pre(ClassLoader caller,
String name,
Object/*java.nio.ByteBuffer*/ byteBuffer,
int off,
int len,
ProtectionDomain pd) {
byte[] bytes = new byte[len];
//Java 1.4 : byteBuffer.get(bytes, off, len);
byteBufferGet(byteBuffer, bytes, off, len);
byte[] newbytes = defineClass0Pre(caller, name, bytes, 0, bytes.length, pd);
//Java 1.4 : ByteBuffer newBuffer = ByteBuffer.wrap(newbytes);
Object newBuffer = byteBufferWrap(newbytes);
return newBuffer;
}
/**
* Equivalent to Java 1.4 NIO aByteBuffer.get(bytes, offset, length) to populate
* the bytes array from the aByteBuffer.
*
* @param byteBuffer
* @param dest
* @param offset
* @param length
*/
private static void byteBufferGet(Object byteBuffer, byte[] dest, int offset, int length) {
try {
Class cByteBuffer = Class.forName("java.nio.ByteBuffer");
Method mGet = cByteBuffer.getDeclaredMethod("get", new Class[]{BYTE_ARRAY_CLASS, int.class, int.class});
mGet.invoke(byteBuffer, new Object[]{dest, new Integer(offset), new Integer(length)});
} catch (Throwable t) {
System.err.println("AW : java.nio not supported");
throw new RuntimeException(t.toString());
}
}
/**
* Equivalent to Java 1.4 NIO static ByteBuffer.wrap(bytes) to create
* a new byteBuffer instance.
*
* @param bytes
* @return a ByteBuffer
*/
private static Object/*java.nio.ByteBuffer*/ byteBufferWrap(byte[] bytes) {
try {
Class cByteBuffer = Class.forName("java.nio.ByteBuffer");
Method mGet = cByteBuffer.getDeclaredMethod("wrap", new Class[]{BYTE_ARRAY_CLASS});
Object byteBuffer = mGet.invoke(null, new Object[]{bytes});
return byteBuffer;
} catch (Throwable t) {
System.err.println("AW : java.nio not supported");
throw new RuntimeException(t.toString());
}
}
private final static byte[] EMPTY_BYTEARRAY = new byte[0];
private final static Class BYTE_ARRAY_CLASS = EMPTY_BYTEARRAY.getClass();
}