@@ -13,6 +13,7 @@ package org.aspectj.weaver.loadtime; | |||
import java.lang.ref.ReferenceQueue; | |||
import java.lang.ref.WeakReference; | |||
import java.security.ProtectionDomain; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
@@ -73,7 +74,7 @@ public class Aj implements ClassPreProcessor { | |||
* @param loader | |||
* @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 | |||
if (loader == null || className == null || loader.getClass().getName().equals(deleLoader)) { | |||
// skip boot loader, null classes (hibernate), or those from a reflection loader | |||
@@ -93,11 +94,16 @@ public class Aj implements ClassPreProcessor { | |||
trace.exit("preProcess"); | |||
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 */ |
@@ -19,6 +19,7 @@ import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.security.ProtectionDomain; | |||
import java.util.ArrayList; | |||
import java.util.Enumeration; | |||
import java.util.HashMap; | |||
@@ -137,8 +138,12 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { | |||
} catch (Throwable throwable) { | |||
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 | |||
} | |||
} | |||
} | |||
@@ -977,6 +982,9 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { | |||
generatedClasses = new HashMap(); | |||
} | |||
private Method defineClassMethod; | |||
private Method defineClassWithProtectionDomainMethod; | |||
private void defineClass(ClassLoader loader, String name, byte[] bytes) { | |||
if (trace.isTraceEnabled()) { | |||
trace.enter("defineClass", this, new Object[] { loader, name, bytes }); | |||
@@ -985,11 +993,45 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { | |||
debug("generating class '" + name + "'"); | |||
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) { | |||
if (e.getTargetException() instanceof LinkageError) { | |||
warn("define generated class failed", e.getTargetException()); |
@@ -11,26 +11,29 @@ | |||
*******************************************************************************/ | |||
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 | |||
*/ | |||
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); | |||
} |
@@ -16,66 +16,66 @@ import com.bea.jvm.ClassLibrary; | |||
import com.bea.jvm.JVMFactory; | |||
/** | |||
* BEA JRocket JMAPI agent. | |||
* BEA JRocket JMAPI agent. | |||
* | |||
* Use "-Xmanagement:class=org.aspectj.weaver.loadtime.JRockitAgent" | |||
*/ | |||
public class JRockitAgent implements com.bea.jvm.ClassPreProcessor { | |||
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(); | |||
public JRockitAgent () { | |||
public JRockitAgent() { | |||
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) { | |||
byte[] newBytes = bytes; | |||
if (stack.empty()) { | |||
stack.push(className); | |||
newBytes = preProcessor.preProcess(className, bytes, loader); | |||
newBytes = preProcessor.preProcess(className, bytes, loader, null); | |||
stack.pop(); | |||
} | |||
return newBytes; | |||
} | |||
private static class ThreadLocalStack extends ThreadLocal { | |||
public boolean empty () { | |||
Stack stack = (Stack)get(); | |||
public boolean empty() { | |||
Stack stack = (Stack) get(); | |||
return stack.empty(); | |||
} | |||
public Object peek () { | |||
public Object peek() { | |||
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; | |||
} | |||
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); | |||
} | |||
public Object pop () { | |||
Stack stack = (Stack)get(); | |||
public Object pop() { | |||
Stack stack = (Stack) get(); | |||
return stack.pop(); | |||
} | |||
protected Object initialValue() { | |||
return new Stack(); | |||
} |