import java.lang.ref.ReferenceQueue; | import java.lang.ref.ReferenceQueue; | ||||
import java.lang.ref.WeakReference; | import java.lang.ref.WeakReference; | ||||
import java.security.ProtectionDomain; | |||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
* @param loader | * @param loader | ||||
* @return weaved bytes | * @return weaved bytes | ||||
*/ | */ | ||||
public byte[] preProcess(String className, byte[] bytes, ClassLoader loader) { | |||||
public byte[] preProcess(String className, byte[] bytes, ClassLoader loader, ProtectionDomain protectionDomain) { | |||||
// TODO AV needs to doc that | // TODO AV needs to doc that | ||||
if (loader == null || className == null || loader.getClass().getName().equals(deleLoader)) { | if (loader == null || className == null || loader.getClass().getName().equals(deleLoader)) { | ||||
// skip boot loader, null classes (hibernate), or those from a reflection loader | // skip boot loader, null classes (hibernate), or those from a reflection loader | ||||
trace.exit("preProcess"); | trace.exit("preProcess"); | ||||
return bytes; | return bytes; | ||||
} | } | ||||
byte[] newBytes = weavingAdaptor.weaveClass(className, bytes, false); | |||||
Dump.dumpOnExit(weavingAdaptor.getMessageHolder(), true); | |||||
if (trace.isTraceEnabled()) | |||||
trace.exit("preProcess", newBytes); | |||||
return newBytes; | |||||
try { | |||||
weavingAdaptor.setActiveProtectionDomain(protectionDomain); | |||||
byte[] newBytes = weavingAdaptor.weaveClass(className, bytes, false); | |||||
Dump.dumpOnExit(weavingAdaptor.getMessageHolder(), true); | |||||
if (trace.isTraceEnabled()) | |||||
trace.exit("preProcess", newBytes); | |||||
return newBytes; | |||||
} finally { | |||||
weavingAdaptor.setActiveProtectionDomain(null); | |||||
} | |||||
} | } | ||||
/* Don't like to do this but JVMTI swallows all exceptions */ | /* Don't like to do this but JVMTI swallows all exceptions */ |
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import java.net.MalformedURLException; | import java.net.MalformedURLException; | ||||
import java.net.URL; | import java.net.URL; | ||||
import java.security.ProtectionDomain; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Enumeration; | import java.util.Enumeration; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
} catch (Throwable throwable) { | } catch (Throwable throwable) { | ||||
throwable.printStackTrace(); | throwable.printStackTrace(); | ||||
} | } | ||||
if (activeProtectionDomain != null) { | |||||
defineClass(loaderRef.getClassLoader(), name, bytes, activeProtectionDomain); | |||||
} else { | |||||
defineClass(loaderRef.getClassLoader(), name, bytes); // could be done lazily using the hook | |||||
defineClass(loaderRef.getClassLoader(), name, bytes); // could be done lazily using the hook | |||||
} | |||||
} | } | ||||
} | } | ||||
generatedClasses = new HashMap(); | generatedClasses = new HashMap(); | ||||
} | } | ||||
private Method defineClassMethod; | |||||
private Method defineClassWithProtectionDomainMethod; | |||||
private void defineClass(ClassLoader loader, String name, byte[] bytes) { | private void defineClass(ClassLoader loader, String name, byte[] bytes) { | ||||
if (trace.isTraceEnabled()) { | if (trace.isTraceEnabled()) { | ||||
trace.enter("defineClass", this, new Object[] { loader, name, bytes }); | trace.enter("defineClass", this, new Object[] { loader, name, bytes }); | ||||
debug("generating class '" + name + "'"); | debug("generating class '" + name + "'"); | ||||
try { | try { | ||||
// TODO av protection domain, and optimize | |||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, bytes.getClass(), | |||||
int.class, int.class }); | |||||
defineClass.setAccessible(true); | |||||
clazz = defineClass.invoke(loader, new Object[] { name, bytes, new Integer(0), new Integer(bytes.length) }); | |||||
if (defineClassMethod == null) { | |||||
defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, | |||||
bytes.getClass(), int.class, int.class }); | |||||
} | |||||
defineClassMethod.setAccessible(true); | |||||
clazz = defineClassMethod.invoke(loader, new Object[] { name, bytes, new Integer(0), new Integer(bytes.length) }); | |||||
} catch (InvocationTargetException e) { | |||||
if (e.getTargetException() instanceof LinkageError) { | |||||
warn("define generated class failed", e.getTargetException()); | |||||
// is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved) | |||||
// TODO maw I don't think this is OK and | |||||
} else { | |||||
warn("define generated class failed", e.getTargetException()); | |||||
} | |||||
} catch (Exception e) { | |||||
warn("define generated class failed", e); | |||||
} | |||||
if (trace.isTraceEnabled()) { | |||||
trace.exit("defineClass", clazz); | |||||
} | |||||
} | |||||
private void defineClass(ClassLoader loader, String name, byte[] bytes, ProtectionDomain protectionDomain) { | |||||
if (trace.isTraceEnabled()) { | |||||
trace.enter("defineClass", this, new Object[] { loader, name, bytes, protectionDomain }); | |||||
} | |||||
Object clazz = null; | |||||
debug("generating class '" + name + "'"); | |||||
try { | |||||
// System.out.println(">> Defining with protection domain " + name + " pd=" + protectionDomain); | |||||
if (defineClassWithProtectionDomainMethod == null) { | |||||
defineClassWithProtectionDomainMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { | |||||
String.class, bytes.getClass(), int.class, int.class, ProtectionDomain.class }); | |||||
} | |||||
defineClassWithProtectionDomainMethod.setAccessible(true); | |||||
clazz = defineClassWithProtectionDomainMethod.invoke(loader, new Object[] { name, bytes, new Integer(0), | |||||
new Integer(bytes.length), protectionDomain }); | |||||
} catch (InvocationTargetException e) { | } catch (InvocationTargetException e) { | ||||
if (e.getTargetException() instanceof LinkageError) { | if (e.getTargetException() instanceof LinkageError) { | ||||
warn("define generated class failed", e.getTargetException()); | warn("define generated class failed", e.getTargetException()); |
*******************************************************************************/ | *******************************************************************************/ | ||||
package org.aspectj.weaver.loadtime; | package org.aspectj.weaver.loadtime; | ||||
import java.security.ProtectionDomain; | |||||
/** | /** | ||||
* Generic class pre processor interface that allows to separate the AspectJ 5 load time weaving | |||||
* from Java 5 JVMTI interfaces for further use on Java 1.3 / 1.4 | |||||
* | |||||
* Generic class pre processor interface that allows to separate the AspectJ 5 load time weaving from Java 5 JVMTI interfaces for | |||||
* further use on Java 1.3 / 1.4 | |||||
* | |||||
* @author Alexandre Vasseur | * @author Alexandre Vasseur | ||||
*/ | */ | ||||
public interface ClassPreProcessor { | public interface ClassPreProcessor { | ||||
/** | |||||
* Post constructor initialization, usually empty | |||||
*/ | |||||
void initialize(); | |||||
/** | |||||
* Post constructor initialization, usually empty | |||||
*/ | |||||
void initialize(); | |||||
/** | |||||
* Weave | |||||
* | |||||
* @param className | |||||
* @param bytes | |||||
* @param classLoader | |||||
* @return | |||||
*/ | |||||
byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader); | |||||
/** | |||||
* Weave | |||||
* | |||||
* @param className | |||||
* @param bytes | |||||
* @param classLoader | |||||
* @param a protection domain that may be used for defining extraneous classes generated as part of modifying the one passed in | |||||
* @return | |||||
*/ | |||||
byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader, ProtectionDomain protectionDomain); | |||||
} | } |
import com.bea.jvm.JVMFactory; | import com.bea.jvm.JVMFactory; | ||||
/** | /** | ||||
* BEA JRocket JMAPI agent. | |||||
* BEA JRocket JMAPI agent. | |||||
* | * | ||||
* Use "-Xmanagement:class=org.aspectj.weaver.loadtime.JRockitAgent" | * Use "-Xmanagement:class=org.aspectj.weaver.loadtime.JRockitAgent" | ||||
*/ | */ | ||||
public class JRockitAgent implements com.bea.jvm.ClassPreProcessor { | public class JRockitAgent implements com.bea.jvm.ClassPreProcessor { | ||||
private ClassPreProcessor preProcessor; | private ClassPreProcessor preProcessor; | ||||
/* | /* | ||||
* This is used to implement the recursion protection offered by JVMTI | |||||
* but not by JRockit JMAPI. I we are called to preProcess a class while | |||||
* already preProcessing another we will return immediately | |||||
* This is used to implement the recursion protection offered by JVMTI but not by JRockit JMAPI. I we are called to preProcess a | |||||
* class while already preProcessing another we will return immediately | |||||
*/ | */ | ||||
private static ThreadLocalStack stack = new ThreadLocalStack(); | private static ThreadLocalStack stack = new ThreadLocalStack(); | ||||
public JRockitAgent () { | |||||
public JRockitAgent() { | |||||
this.preProcessor = new Aj(); | this.preProcessor = new Aj(); | ||||
ClassLibrary cl = JVMFactory.getJVM().getClassLibrary(); | |||||
cl.setClassPreProcessor(this); | |||||
ClassLibrary cl = JVMFactory.getJVM().getClassLibrary(); | |||||
cl.setClassPreProcessor(this); | |||||
} | } | ||||
public byte[] preProcess(ClassLoader loader, String className, byte[] bytes) { | public byte[] preProcess(ClassLoader loader, String className, byte[] bytes) { | ||||
byte[] newBytes = bytes; | byte[] newBytes = bytes; | ||||
if (stack.empty()) { | if (stack.empty()) { | ||||
stack.push(className); | stack.push(className); | ||||
newBytes = preProcessor.preProcess(className, bytes, loader); | |||||
newBytes = preProcessor.preProcess(className, bytes, loader, null); | |||||
stack.pop(); | stack.pop(); | ||||
} | } | ||||
return newBytes; | return newBytes; | ||||
} | } | ||||
private static class ThreadLocalStack extends ThreadLocal { | private static class ThreadLocalStack extends ThreadLocal { | ||||
public boolean empty () { | |||||
Stack stack = (Stack)get(); | |||||
public boolean empty() { | |||||
Stack stack = (Stack) get(); | |||||
return stack.empty(); | return stack.empty(); | ||||
} | } | ||||
public Object peek () { | |||||
public Object peek() { | |||||
Object obj = null; | Object obj = null; | ||||
Stack stack = (Stack)get(); | |||||
if (!stack.empty()) obj = stack.peek(); | |||||
Stack stack = (Stack) get(); | |||||
if (!stack.empty()) | |||||
obj = stack.peek(); | |||||
return obj; | return obj; | ||||
} | } | ||||
public void push (Object obj) { | |||||
Stack stack = (Stack)get(); | |||||
if (!stack.empty() && obj == stack.peek()) throw new RuntimeException(obj.toString()); | |||||
public void push(Object obj) { | |||||
Stack stack = (Stack) get(); | |||||
if (!stack.empty() && obj == stack.peek()) | |||||
throw new RuntimeException(obj.toString()); | |||||
stack.push(obj); | stack.push(obj); | ||||
} | } | ||||
public Object pop () { | |||||
Stack stack = (Stack)get(); | |||||
public Object pop() { | |||||
Stack stack = (Stack) get(); | |||||
return stack.pop(); | return stack.pop(); | ||||
} | } | ||||
protected Object initialValue() { | protected Object initialValue() { | ||||
return new Stack(); | return new Stack(); | ||||
} | } |