import org.aspectj.weaver.tools.WeavingAdaptor; | import org.aspectj.weaver.tools.WeavingAdaptor; | ||||
/** | /** | ||||
* Adapter between the generic class pre processor interface and the AspectJ weaver | |||||
* Load time weaving consistency relies on Bcel.setRepository | |||||
* | |||||
* Adapter between the generic class pre processor interface and the AspectJ weaver Load time weaving consistency relies on | |||||
* Bcel.setRepository | |||||
* | |||||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | ||||
*/ | */ | ||||
public class Aj implements ClassPreProcessor { | public class Aj implements ClassPreProcessor { | ||||
private IWeavingContext weavingContext; | private IWeavingContext weavingContext; | ||||
/** | |||||
* References are added to this queue when their associated classloader is removed, and once on here that indicates | |||||
* that we should tidy up the adaptor map and remove the adaptor (weaver) from the map we are maintaining from | |||||
* adaptorkey > adaptor (weaver) | |||||
*/ | |||||
private static ReferenceQueue adaptorQueue = new ReferenceQueue(); | |||||
/** | |||||
* References are added to this queue when their associated classloader is removed, and once on here that indicates that we | |||||
* should tidy up the adaptor map and remove the adaptor (weaver) from the map we are maintaining from adaptorkey > adaptor | |||||
* (weaver) | |||||
*/ | |||||
private static ReferenceQueue adaptorQueue = new ReferenceQueue(); | |||||
private static Trace trace = TraceFactory.getTraceFactory().getTrace(Aj.class); | private static Trace trace = TraceFactory.getTraceFactory().getTrace(Aj.class); | ||||
public Aj(){ | |||||
public Aj() { | |||||
this(null); | this(null); | ||||
} | } | ||||
public Aj(IWeavingContext context){ | |||||
if (trace.isTraceEnabled()) trace.enter("<init>",this,new Object[] {context, getClass().getClassLoader()}); | |||||
public Aj(IWeavingContext context) { | |||||
if (trace.isTraceEnabled()) | |||||
trace.enter("<init>", this, new Object[] { context, getClass().getClassLoader() }); | |||||
this.weavingContext = context; | this.weavingContext = context; | ||||
if (trace.isTraceEnabled()) trace.exit("<init>"); | |||||
if (trace.isTraceEnabled()) | |||||
trace.exit("<init>"); | |||||
} | |||||
/** | |||||
* Initialization | |||||
*/ | |||||
public void initialize() { | |||||
} | |||||
/** | |||||
* Weave | |||||
* | |||||
* @param className | |||||
* @param bytes | |||||
* @param loader | |||||
* @return weaved bytes | |||||
*/ | |||||
public byte[] preProcess(String className, byte[] bytes, ClassLoader loader) { | |||||
// TODO AV needs to doc that | |||||
if (loader == null || className == null) { | |||||
// skip boot loader or null classes (hibernate) | |||||
return bytes; | |||||
} | |||||
if (trace.isTraceEnabled()) | |||||
trace.enter("preProcess", this, new Object[] { className, bytes, loader }); | |||||
if (trace.isTraceEnabled()) | |||||
trace.event("preProcess", this, new Object[] { loader.getParent(), Thread.currentThread().getContextClassLoader() }); | |||||
try { | |||||
synchronized (loader) { | |||||
WeavingAdaptor weavingAdaptor = WeaverContainer.getWeaver(loader, weavingContext); | |||||
if (weavingAdaptor == null) { | |||||
if (trace.isTraceEnabled()) | |||||
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; | |||||
} | |||||
/* Don't like to do this but JVMTI swallows all exceptions */ | |||||
} catch (Throwable th) { | |||||
trace.error(className, th); | |||||
Dump.dumpWithException(th); | |||||
// FIXME AV wondering if we should have the option to fail (throw runtime exception) here | |||||
// would make sense at least in test f.e. see TestHelper.handleMessage() | |||||
if (trace.isTraceEnabled()) | |||||
trace.exit("preProcess", th); | |||||
return bytes; | |||||
} finally { | |||||
CompilationAndWeavingContext.resetForThread(); | |||||
} | |||||
} | |||||
/** | |||||
* An AdaptorKey is a WeakReference wrapping a classloader reference that will enqueue to a specified queue when the classloader | |||||
* is GC'd. Since the AdaptorKey is used as a key into a hashmap we need to give it a non-varying hashcode/equals | |||||
* implementation, and we need that hashcode not to vary even when the internal referent has been GC'd. The hashcode is | |||||
* calculated on creation of the AdaptorKey based on the loader instance that it is wrapping. This means even when the referent | |||||
* is gone we can still use the AdaptorKey and it will 'point' to the same place as it always did. | |||||
*/ | |||||
private static class AdaptorKey extends WeakReference { | |||||
private int hashcode = -1; | |||||
public AdaptorKey(ClassLoader loader) { | |||||
super(loader, adaptorQueue); | |||||
hashcode = loader.hashCode() * 37; | |||||
} | |||||
public ClassLoader getClassLoader() { | |||||
ClassLoader instance = (ClassLoader) get(); | |||||
// Assert instance!=null - shouldn't be asked for after a GC of the referent has occurred ! | |||||
return instance; | |||||
} | |||||
public boolean equals(Object obj) { | |||||
if (!(obj instanceof AdaptorKey)) | |||||
return false; | |||||
AdaptorKey other = (AdaptorKey) obj; | |||||
return other.hashcode == hashcode; | |||||
} | |||||
public int hashCode() { | |||||
return hashcode; | |||||
} | |||||
} | |||||
/** | |||||
* The reference queue is only processed when a request is made for a weaver adaptor. This means there can be one or two stale | |||||
* weavers left around. If the user knows they have finished all their weaving, they might wish to call removeStaleAdaptors | |||||
* which will process anything left on the reference queue containing adaptorKeys for garbage collected classloaders. | |||||
* | |||||
* @param displayProgress produce System.err info on the tidying up process | |||||
* @return number of stale weavers removed | |||||
*/ | |||||
public static int removeStaleAdaptors(boolean displayProgress) { | |||||
int removed = 0; | |||||
synchronized (WeaverContainer.weavingAdaptors) { | |||||
if (displayProgress) { | |||||
System.err.println("Weaver adaptors before queue processing:"); | |||||
Map m = WeaverContainer.weavingAdaptors; | |||||
Set keys = m.keySet(); | |||||
for (Iterator iterator = keys.iterator(); iterator.hasNext();) { | |||||
Object object = iterator.next(); | |||||
System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object)); | |||||
} | |||||
} | |||||
Object o = adaptorQueue.poll(); | |||||
while (o != null) { | |||||
if (displayProgress) | |||||
System.err.println("Processing referencequeue entry " + o); | |||||
AdaptorKey wo = (AdaptorKey) o; | |||||
boolean didit = WeaverContainer.weavingAdaptors.remove(wo) != null; | |||||
if (didit) { | |||||
removed++; | |||||
} else { | |||||
throw new RuntimeException("Eh?? key=" + wo); | |||||
} | |||||
if (displayProgress) | |||||
System.err.println("Removed? " + didit); | |||||
o = adaptorQueue.poll(); | |||||
} | |||||
if (displayProgress) { | |||||
System.err.println("Weaver adaptors after queue processing:"); | |||||
Map m = WeaverContainer.weavingAdaptors; | |||||
Set keys = m.keySet(); | |||||
for (Iterator iterator = keys.iterator(); iterator.hasNext();) { | |||||
Object object = iterator.next(); | |||||
System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object)); | |||||
} | |||||
} | |||||
} | |||||
return removed; | |||||
} | |||||
/** | |||||
* @return the number of entries still in the weavingAdaptors map | |||||
*/ | |||||
public static int getActiveAdaptorCount() { | |||||
return WeaverContainer.weavingAdaptors.size(); | |||||
} | |||||
/** | |||||
* Process the reference queue that contains stale AdaptorKeys - the keys are put on the queue when their classloader referent | |||||
* is garbage collected and so the associated adaptor (weaver) should be removed from the map | |||||
*/ | |||||
public static void checkQ() { | |||||
synchronized (adaptorQueue) { | |||||
Object o = adaptorQueue.poll(); | |||||
while (o != null) { | |||||
AdaptorKey wo = (AdaptorKey) o; | |||||
// boolean removed = | |||||
WeaverContainer.weavingAdaptors.remove(wo); | |||||
// DBG System.err.println("Evicting key " + wo + " = " + didit); | |||||
o = adaptorQueue.poll(); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Cache of weaver There is one weaver per classloader | |||||
*/ | |||||
static class WeaverContainer { | |||||
final static Map weavingAdaptors = Collections.synchronizedMap(new HashMap()); | |||||
static WeavingAdaptor getWeaver(ClassLoader loader, IWeavingContext weavingContext) { | |||||
ExplicitlyInitializedClassLoaderWeavingAdaptor adaptor = null; | |||||
AdaptorKey adaptorKey = new AdaptorKey(loader); | |||||
synchronized (weavingAdaptors) { | |||||
checkQ(); | |||||
adaptor = (ExplicitlyInitializedClassLoaderWeavingAdaptor) weavingAdaptors.get(adaptorKey); | |||||
if (adaptor == null) { | |||||
String loaderClassName = loader.getClass().getName(); | |||||
if (loaderClassName.equals("sun.reflect.DelegatingClassLoader")) { | |||||
// we don't weave reflection generated types at all! | |||||
return null; | |||||
} else { | |||||
// create it and put it back in the weavingAdaptors map but avoid any kind of instantiation | |||||
// within the synchronized block | |||||
ClassLoaderWeavingAdaptor weavingAdaptor = new ClassLoaderWeavingAdaptor(); | |||||
adaptor = new ExplicitlyInitializedClassLoaderWeavingAdaptor(weavingAdaptor); | |||||
weavingAdaptors.put(adaptorKey, adaptor); | |||||
} | |||||
} | |||||
} | |||||
// perform the initialization | |||||
return adaptor.getWeavingAdaptor(loader, weavingContext); | |||||
} | |||||
} | } | ||||
/** | |||||
* Initialization | |||||
*/ | |||||
public void initialize() { | |||||
; | |||||
} | |||||
/** | |||||
* Weave | |||||
* | |||||
* @param className | |||||
* @param bytes | |||||
* @param loader | |||||
* @return weaved bytes | |||||
*/ | |||||
public byte[] preProcess(String className, byte[] bytes, ClassLoader loader) { | |||||
//TODO AV needs to doc that | |||||
if (loader == null || className == null) { | |||||
// skip boot loader or null classes (hibernate) | |||||
return bytes; | |||||
} | |||||
if (trace.isTraceEnabled()) trace.enter("preProcess",this,new Object[] {className, bytes, loader}); | |||||
if (trace.isTraceEnabled()) trace.event("preProcess",this,new Object[] {loader.getParent(), Thread.currentThread().getContextClassLoader()}); | |||||
try { | |||||
synchronized (loader) { | |||||
WeavingAdaptor weavingAdaptor = WeaverContainer.getWeaver(loader, weavingContext); | |||||
if (weavingAdaptor == null) { | |||||
if (trace.isTraceEnabled()) 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; | |||||
static class ExplicitlyInitializedClassLoaderWeavingAdaptor { | |||||
private final ClassLoaderWeavingAdaptor weavingAdaptor; | |||||
private boolean isInitialized; | |||||
public ExplicitlyInitializedClassLoaderWeavingAdaptor(ClassLoaderWeavingAdaptor weavingAdaptor) { | |||||
this.weavingAdaptor = weavingAdaptor; | |||||
this.isInitialized = false; | |||||
} | |||||
private void initialize(ClassLoader loader, IWeavingContext weavingContext) { | |||||
if (!isInitialized) { | |||||
isInitialized = true; | |||||
weavingAdaptor.initialize(loader, weavingContext); | |||||
} | } | ||||
/* Don't like to do this but JVMTI swallows all exceptions */ | |||||
} catch (Throwable th) { | |||||
trace.error(className,th); | |||||
Dump.dumpWithException(th); | |||||
//FIXME AV wondering if we should have the option to fail (throw runtime exception) here | |||||
// would make sense at least in test f.e. see TestHelper.handleMessage() | |||||
if (trace.isTraceEnabled()) trace.exit("preProcess",th); | |||||
return bytes; | |||||
} finally { | |||||
CompilationAndWeavingContext.resetForThread(); | |||||
} | |||||
} | |||||
/** | |||||
* An AdaptorKey is a WeakReference wrapping a classloader reference that will enqueue to a specified queue when the | |||||
* classloader is GC'd. Since the AdaptorKey is used as a key into a hashmap we need to give it a non-varying | |||||
* hashcode/equals implementation, and we need that hashcode not to vary even when the internal referent has been | |||||
* GC'd. The hashcode is calculated on creation of the AdaptorKey based on the loader instance that it is wrapping. | |||||
* This means even when the referent is gone we can still use the AdaptorKey and it will 'point' to the same place | |||||
* as it always did. | |||||
*/ | |||||
private static class AdaptorKey extends WeakReference { | |||||
private int hashcode = -1; | |||||
public AdaptorKey(ClassLoader loader) { | |||||
super(loader, adaptorQueue); | |||||
hashcode = loader.hashCode() * 37; | |||||
} | |||||
public ClassLoader getClassLoader() { | |||||
ClassLoader instance = (ClassLoader) get(); | |||||
// Assert instance!=null - shouldn't be asked for after a GC of the referent has occurred ! | |||||
return instance; | |||||
} | |||||
public boolean equals(Object obj) { | |||||
if (!(obj instanceof AdaptorKey)) return false; | |||||
AdaptorKey other = (AdaptorKey) obj; | |||||
return other.hashcode == hashcode; | |||||
} | |||||
public int hashCode() { | |||||
return hashcode; | |||||
} | |||||
} | |||||
/** | |||||
* The reference queue is only processed when a request is made for a weaver adaptor. This means there can be one or | |||||
* two stale weavers left around. If the user knows they have finished all their weaving, they might wish to call | |||||
* removeStaleAdaptors which will process anything left on the reference queue containing adaptorKeys for garbage | |||||
* collected classloaders. | |||||
* | |||||
* @param displayProgress produce System.err info on the tidying up process | |||||
* @return number of stale weavers removed | |||||
*/ | |||||
public static int removeStaleAdaptors(boolean displayProgress) { | |||||
int removed = 0; | |||||
synchronized (WeaverContainer.weavingAdaptors) { | |||||
if (displayProgress) { | |||||
System.err.println("Weaver adaptors before queue processing:"); | |||||
Map m = WeaverContainer.weavingAdaptors; | |||||
Set keys = m.keySet(); | |||||
for (Iterator iterator = keys.iterator(); iterator.hasNext();) { | |||||
Object object = iterator.next(); | |||||
System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object)); | |||||
} | |||||
} | |||||
Object o = adaptorQueue.poll(); | |||||
while (o != null) { | |||||
if (displayProgress) System.err.println("Processing referencequeue entry " + o); | |||||
AdaptorKey wo = (AdaptorKey) o; | |||||
boolean didit = WeaverContainer.weavingAdaptors.remove(wo) != null; | |||||
if (didit) { | |||||
removed++; | |||||
} else { | |||||
throw new RuntimeException("Eh?? key="+wo); | |||||
} | |||||
if (displayProgress) System.err.println("Removed? "+didit); | |||||
o = adaptorQueue.poll(); | |||||
} | |||||
if (displayProgress) { | |||||
System.err.println("Weaver adaptors after queue processing:"); | |||||
Map m = WeaverContainer.weavingAdaptors; | |||||
Set keys = m.keySet(); | |||||
for (Iterator iterator = keys.iterator(); iterator.hasNext();) { | |||||
Object object = iterator.next(); | |||||
System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object)); | |||||
} | |||||
} | |||||
} | |||||
return removed; | |||||
} | |||||
/** | |||||
* @return the number of entries still in the weavingAdaptors map | |||||
*/ | |||||
public static int getActiveAdaptorCount() { | |||||
return WeaverContainer.weavingAdaptors.size(); | |||||
} | |||||
/** | |||||
* Process the reference queue that contains stale AdaptorKeys - the keys are put on the queue when their | |||||
* classloader referent is garbage collected and so the associated adaptor (weaver) should be removed from the map | |||||
*/ | |||||
public static void checkQ() { | |||||
synchronized (adaptorQueue) { | |||||
Object o = adaptorQueue.poll(); | |||||
while (o != null) { | |||||
AdaptorKey wo = (AdaptorKey) o; | |||||
boolean removed = WeaverContainer.weavingAdaptors.remove(wo) != null; | |||||
// DBG System.err.println("Evicting key " + wo + " = " + didit); | |||||
o = adaptorQueue.poll(); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Cache of weaver | |||||
* There is one weaver per classloader | |||||
*/ | |||||
static class WeaverContainer { | |||||
final static Map weavingAdaptors = Collections.synchronizedMap(new HashMap()); | |||||
static WeavingAdaptor getWeaver(ClassLoader loader, IWeavingContext weavingContext) { | |||||
ExplicitlyInitializedClassLoaderWeavingAdaptor adaptor = null; | |||||
AdaptorKey adaptorKey = new AdaptorKey(loader); | |||||
synchronized (weavingAdaptors) { | |||||
checkQ(); | |||||
adaptor = (ExplicitlyInitializedClassLoaderWeavingAdaptor) weavingAdaptors.get(adaptorKey); | |||||
if (adaptor == null) { | |||||
String loaderClassName = loader.getClass().getName(); | |||||
if (loaderClassName.equals("sun.reflect.DelegatingClassLoader")) { | |||||
// we don't weave reflection generated types at all! | |||||
return null; | |||||
} else { | |||||
// create it and put it back in the weavingAdaptors map but avoid any kind of instantiation | |||||
// within the synchronized block | |||||
ClassLoaderWeavingAdaptor weavingAdaptor = new ClassLoaderWeavingAdaptor(); | |||||
adaptor = new ExplicitlyInitializedClassLoaderWeavingAdaptor(weavingAdaptor); | |||||
weavingAdaptors.put(adaptorKey, adaptor); | |||||
} | |||||
} | |||||
} | |||||
// perform the initialization | |||||
return adaptor.getWeavingAdaptor(loader, weavingContext); | |||||
} | |||||
} | |||||
static class ExplicitlyInitializedClassLoaderWeavingAdaptor { | |||||
private final ClassLoaderWeavingAdaptor weavingAdaptor; | |||||
private boolean isInitialized; | |||||
public ExplicitlyInitializedClassLoaderWeavingAdaptor(ClassLoaderWeavingAdaptor weavingAdaptor) { | |||||
this.weavingAdaptor = weavingAdaptor; | |||||
this.isInitialized = false; | |||||
} | |||||
private void initialize(ClassLoader loader, IWeavingContext weavingContext) { | |||||
if (!isInitialized) { | |||||
isInitialized = true; | |||||
weavingAdaptor.initialize(loader, weavingContext); | |||||
} | |||||
} | |||||
public ClassLoaderWeavingAdaptor getWeavingAdaptor(ClassLoader loader, IWeavingContext weavingContext) { | |||||
initialize(loader, weavingContext); | |||||
return weavingAdaptor; | |||||
} | |||||
} | |||||
/** | |||||
* Returns a namespace based on the contest of the aspects available | |||||
*/ | |||||
public String getNamespace (ClassLoader loader) { | |||||
ClassLoaderWeavingAdaptor weavingAdaptor = (ClassLoaderWeavingAdaptor)WeaverContainer.getWeaver(loader, weavingContext); | |||||
return weavingAdaptor.getNamespace(); | |||||
} | |||||
/** | |||||
* Check to see if any classes have been generated for a particular classes loader. | |||||
* Calls ClassLoaderWeavingAdaptor.generatedClassesExist() | |||||
* @param loader the class cloder | |||||
* @return true if classes have been generated. | |||||
*/ | |||||
public boolean generatedClassesExist(ClassLoader loader){ | |||||
return ((ClassLoaderWeavingAdaptor)WeaverContainer.getWeaver(loader, weavingContext)).generatedClassesExistFor(null); | |||||
} | |||||
public void flushGeneratedClasses(ClassLoader loader){ | |||||
((ClassLoaderWeavingAdaptor)WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClasses(); | |||||
} | |||||
} | |||||
public ClassLoaderWeavingAdaptor getWeavingAdaptor(ClassLoader loader, IWeavingContext weavingContext) { | |||||
initialize(loader, weavingContext); | |||||
return weavingAdaptor; | |||||
} | |||||
} | |||||
/** | |||||
* Returns a namespace based on the contest of the aspects available | |||||
*/ | |||||
public String getNamespace(ClassLoader loader) { | |||||
ClassLoaderWeavingAdaptor weavingAdaptor = (ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext); | |||||
return weavingAdaptor.getNamespace(); | |||||
} | |||||
/** | |||||
* Check to see if any classes have been generated for a particular classes loader. Calls | |||||
* ClassLoaderWeavingAdaptor.generatedClassesExist() | |||||
* | |||||
* @param loader the class cloder | |||||
* @return true if classes have been generated. | |||||
*/ | |||||
public boolean generatedClassesExist(ClassLoader loader) { | |||||
return ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).generatedClassesExistFor(null); | |||||
} | |||||
public void flushGeneratedClasses(ClassLoader loader) { | |||||
((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClasses(); | |||||
} | |||||
} | } |
*******************************************************************************/ | *******************************************************************************/ | ||||
package org.aspectj.weaver.loadtime; | package org.aspectj.weaver.loadtime; | ||||
import java.lang.reflect.Modifier; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.Collections; | |||||
import java.util.HashMap; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.aspectj.apache.bcel.Constants; | import org.aspectj.apache.bcel.Constants; | ||||
import org.aspectj.apache.bcel.classfile.JavaClass; | import org.aspectj.apache.bcel.classfile.JavaClass; | ||||
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; | import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; | ||||
import org.aspectj.weaver.patterns.PerClause; | import org.aspectj.weaver.patterns.PerClause; | ||||
import org.aspectj.weaver.patterns.PerSingleton; | import org.aspectj.weaver.patterns.PerSingleton; | ||||
import java.lang.reflect.Modifier; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.Collections; | |||||
import java.util.HashMap; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | /** | ||||
* Generates bytecode for concrete-aspect | |||||
* <p/> | |||||
* The concrete aspect is @AspectJ code generated. As it is build during aop.xml definitions registration | |||||
* we perform the type munging for perclause ie aspectOf artifact directly, instead of waiting for it | |||||
* to go thru the weaver (that we are in the middle of configuring). | |||||
* | |||||
* Generates bytecode for concrete-aspect <p/> The concrete aspect is @AspectJ code generated. As it is build during aop.xml | |||||
* definitions registration we perform the type munging for perclause ie aspectOf artifact directly, instead of waiting for it to go | |||||
* thru the weaver (that we are in the middle of configuring). | |||||
* | |||||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | ||||
*/ | */ | ||||
public class ConcreteAspectCodeGen { | public class ConcreteAspectCodeGen { | ||||
private final static String[] EMPTY_STRINGS = new String[0]; | |||||
private final static Type[] EMPTY_TYPES = new Type[0]; | |||||
/** | |||||
* Concrete aspect definition we build for | |||||
*/ | |||||
private final Definition.ConcreteAspect m_concreteAspect; | |||||
/** | |||||
* World for which we build for | |||||
*/ | |||||
private final World m_world; | |||||
/** | |||||
* Set to true when all is checks are verified | |||||
*/ | |||||
private boolean m_isValid = false; | |||||
/** | |||||
* The parent aspect, not concretized | |||||
*/ | |||||
private ResolvedType m_parent; | |||||
/** | |||||
* Aspect perClause, used for direct munging of aspectOf artifacts | |||||
*/ | |||||
private PerClause m_perClause; | |||||
/** | |||||
* Create a new compiler for a concrete aspect | |||||
* | |||||
* @param concreteAspect | |||||
* @param world | |||||
*/ | |||||
ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) { | |||||
m_concreteAspect = concreteAspect; | |||||
m_world = world; | |||||
} | |||||
/** | |||||
* Checks that concrete aspect is valid | |||||
* | |||||
* @return true if ok, false otherwise | |||||
*/ | |||||
public boolean validate() { | |||||
if (!(m_world instanceof BcelWorld)) { | |||||
reportError("Internal error: world must be of type BcelWorld"); | |||||
return false; | |||||
} | |||||
// name must be undefined so far | |||||
// TODO only convert the name to signature once, probably earlier than this | |||||
ResolvedType current = m_world.lookupBySignature(UnresolvedType.forName(m_concreteAspect.name).getSignature()); | |||||
if (current!=null && !current.isMissing()) { | |||||
reportError("Attempt to concretize but chosen aspect name already defined: " + stringify()); | |||||
return false; | |||||
} | |||||
// it can happen that extends is null, for precedence only declaration | |||||
if (m_concreteAspect.extend == null && m_concreteAspect.precedence != null) { | |||||
if (m_concreteAspect.pointcuts.isEmpty()) { | |||||
m_isValid = true; | |||||
m_perClause = new PerSingleton(); | |||||
m_parent = null; | |||||
return true;// no need to checks more in that special case | |||||
} else { | |||||
reportError("Attempt to use nested pointcuts without extends clause: "+stringify()); | |||||
return false; | |||||
} | |||||
} | |||||
m_parent = m_world.resolve(m_concreteAspect.extend, true); | |||||
// handle inner classes | |||||
if (m_parent.isMissing()) { | |||||
// fallback on inner class lookup mechanism | |||||
String fixedName = m_concreteAspect.extend; | |||||
int hasDot = fixedName.lastIndexOf('.'); | |||||
while (hasDot > 0) { | |||||
char[] fixedNameChars = fixedName.toCharArray(); | |||||
fixedNameChars[hasDot] = '$'; | |||||
fixedName = new String(fixedNameChars); | |||||
hasDot = fixedName.lastIndexOf('.'); | |||||
m_parent = m_world.resolve(UnresolvedType.forName(fixedName), true); | |||||
if (!m_parent.isMissing()) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (m_parent.isMissing()) { | |||||
reportError("Cannot find m_parent aspect for: " + stringify()); | |||||
return false; | |||||
} | |||||
// extends must be abstract | |||||
if (!m_parent.isAbstract()) { | |||||
reportError("Attempt to concretize a non-abstract aspect: " + stringify()); | |||||
return false; | |||||
} | |||||
// m_parent must be aspect | |||||
if (!m_parent.isAspect()) { | |||||
reportError("Attempt to concretize a non aspect: " + stringify()); | |||||
return false; | |||||
} | |||||
// must have all abstractions defined | |||||
List elligibleAbstractions = new ArrayList(); | |||||
Collection abstractMethods = getOutstandingAbstractMethods(m_parent); | |||||
for (Iterator iter = abstractMethods.iterator(); iter.hasNext();) { | |||||
private final static String[] EMPTY_STRINGS = new String[0]; | |||||
private final static Type[] EMPTY_TYPES = new Type[0]; | |||||
/** | |||||
* Concrete aspect definition we build for | |||||
*/ | |||||
private final Definition.ConcreteAspect m_concreteAspect; | |||||
/** | |||||
* World for which we build for | |||||
*/ | |||||
private final World m_world; | |||||
/** | |||||
* Set to true when all is checks are verified | |||||
*/ | |||||
private boolean m_isValid = false; | |||||
/** | |||||
* The parent aspect, not concretized | |||||
*/ | |||||
private ResolvedType m_parent; | |||||
/** | |||||
* Aspect perClause, used for direct munging of aspectOf artifacts | |||||
*/ | |||||
private PerClause m_perClause; | |||||
/** | |||||
* Create a new compiler for a concrete aspect | |||||
* | |||||
* @param concreteAspect | |||||
* @param world | |||||
*/ | |||||
ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) { | |||||
m_concreteAspect = concreteAspect; | |||||
m_world = world; | |||||
} | |||||
/** | |||||
* Checks that concrete aspect is valid | |||||
* | |||||
* @return true if ok, false otherwise | |||||
*/ | |||||
public boolean validate() { | |||||
if (!(m_world instanceof BcelWorld)) { | |||||
reportError("Internal error: world must be of type BcelWorld"); | |||||
return false; | |||||
} | |||||
// name must be undefined so far | |||||
// TODO only convert the name to signature once, probably earlier than this | |||||
ResolvedType current = m_world.lookupBySignature(UnresolvedType.forName(m_concreteAspect.name).getSignature()); | |||||
if (current != null && !current.isMissing()) { | |||||
reportError("Attempt to concretize but chosen aspect name already defined: " + stringify()); | |||||
return false; | |||||
} | |||||
// it can happen that extends is null, for precedence only declaration | |||||
if (m_concreteAspect.extend == null && m_concreteAspect.precedence != null) { | |||||
if (m_concreteAspect.pointcuts.isEmpty()) { | |||||
m_isValid = true; | |||||
m_perClause = new PerSingleton(); | |||||
m_parent = null; | |||||
return true;// no need to checks more in that special case | |||||
} else { | |||||
reportError("Attempt to use nested pointcuts without extends clause: " + stringify()); | |||||
return false; | |||||
} | |||||
} | |||||
m_parent = m_world.resolve(m_concreteAspect.extend, true); | |||||
// handle inner classes | |||||
if (m_parent.isMissing()) { | |||||
// fallback on inner class lookup mechanism | |||||
String fixedName = m_concreteAspect.extend; | |||||
int hasDot = fixedName.lastIndexOf('.'); | |||||
while (hasDot > 0) { | |||||
char[] fixedNameChars = fixedName.toCharArray(); | |||||
fixedNameChars[hasDot] = '$'; | |||||
fixedName = new String(fixedNameChars); | |||||
hasDot = fixedName.lastIndexOf('.'); | |||||
m_parent = m_world.resolve(UnresolvedType.forName(fixedName), true); | |||||
if (!m_parent.isMissing()) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (m_parent.isMissing()) { | |||||
reportError("Cannot find m_parent aspect for: " + stringify()); | |||||
return false; | |||||
} | |||||
// extends must be abstract | |||||
if (!m_parent.isAbstract()) { | |||||
reportError("Attempt to concretize a non-abstract aspect: " + stringify()); | |||||
return false; | |||||
} | |||||
// m_parent must be aspect | |||||
if (!m_parent.isAspect()) { | |||||
reportError("Attempt to concretize a non aspect: " + stringify()); | |||||
return false; | |||||
} | |||||
// must have all abstractions defined | |||||
List elligibleAbstractions = new ArrayList(); | |||||
Collection abstractMethods = getOutstandingAbstractMethods(m_parent); | |||||
for (Iterator iter = abstractMethods.iterator(); iter.hasNext();) { | |||||
ResolvedMember method = (ResolvedMember) iter.next(); | ResolvedMember method = (ResolvedMember) iter.next(); | ||||
if ("()V".equals(method.getSignature())) { | if ("()V".equals(method.getSignature())) { | ||||
String n = method.getName(); | |||||
if (n.startsWith("ajc$pointcut")) { // Allow for the abstract pointcut being from a code style aspect compiled with -1.5 (see test for 128744) | |||||
n = n.substring(14); | |||||
n = n.substring(0,n.indexOf("$")); | |||||
elligibleAbstractions.add(n); | |||||
} else if (hasPointcutAnnotation(method)) { | |||||
elligibleAbstractions.add(method.getName()); | |||||
} else { | |||||
// error, an outstanding abstract method that can't be concretized in XML | |||||
reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify()); | |||||
return false; | |||||
} | |||||
} else { | |||||
if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) { | |||||
// it may be a pointcut but it doesn't meet the requirements for XML concretization | |||||
reportError("Abstract method '" + method.toString() + "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): " + stringify()); | |||||
return false; | |||||
} else { | |||||
// error, an outstanding abstract method that can't be concretized in XML | |||||
reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify()); | |||||
return false; | |||||
} | |||||
} | |||||
String n = method.getName(); | |||||
if (n.startsWith("ajc$pointcut")) { // Allow for the abstract pointcut being from a code style aspect compiled with | |||||
// -1.5 (see test for 128744) | |||||
n = n.substring(14); | |||||
n = n.substring(0, n.indexOf("$")); | |||||
elligibleAbstractions.add(n); | |||||
} else if (hasPointcutAnnotation(method)) { | |||||
elligibleAbstractions.add(method.getName()); | |||||
} else { | |||||
// error, an outstanding abstract method that can't be concretized in XML | |||||
reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify()); | |||||
return false; | |||||
} | |||||
} else { | |||||
if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) { | |||||
// it may be a pointcut but it doesn't meet the requirements for XML concretization | |||||
reportError("Abstract method '" | |||||
+ method.toString() | |||||
+ "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): " | |||||
+ stringify()); | |||||
return false; | |||||
} else { | |||||
// error, an outstanding abstract method that can't be concretized in XML | |||||
reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify()); | |||||
return false; | |||||
} | |||||
} | |||||
} | |||||
List pointcutNames = new ArrayList(); | |||||
for (Iterator it = m_concreteAspect.pointcuts.iterator(); it.hasNext();) { | |||||
Definition.Pointcut abstractPc = (Definition.Pointcut) it.next(); | |||||
pointcutNames.add(abstractPc.name); | |||||
} | } | ||||
List pointcutNames = new ArrayList(); | |||||
for (Iterator it = m_concreteAspect.pointcuts.iterator(); it.hasNext();) { | |||||
Definition.Pointcut abstractPc = (Definition.Pointcut) it.next(); | |||||
pointcutNames.add(abstractPc.name); | |||||
} | |||||
for (Iterator it = elligibleAbstractions.iterator(); it.hasNext();) { | |||||
String elligiblePc = (String) it.next(); | |||||
if (!pointcutNames.contains(elligiblePc)) { | |||||
reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify()); | |||||
return false; | |||||
} | |||||
} | |||||
m_perClause = m_parent.getPerClause(); | |||||
m_isValid = true; | |||||
return m_isValid; | |||||
} | |||||
private Collection getOutstandingAbstractMethods(ResolvedType type) { | |||||
Map collector = new HashMap(); | |||||
// let's get to the top of the hierarchy and then walk down ... recording abstract methods then removing | |||||
// them if they get defined further down the hierarchy | |||||
getOutstandingAbstractMethodsHelper(type,collector); | |||||
for (Iterator it = elligibleAbstractions.iterator(); it.hasNext();) { | |||||
String elligiblePc = (String) it.next(); | |||||
if (!pointcutNames.contains(elligiblePc)) { | |||||
reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify()); | |||||
return false; | |||||
} | |||||
} | |||||
m_perClause = m_parent.getPerClause(); | |||||
m_isValid = true; | |||||
return m_isValid; | |||||
} | |||||
private Collection getOutstandingAbstractMethods(ResolvedType type) { | |||||
Map collector = new HashMap(); | |||||
// let's get to the top of the hierarchy and then walk down ... recording abstract methods then removing | |||||
// them if they get defined further down the hierarchy | |||||
getOutstandingAbstractMethodsHelper(type, collector); | |||||
return collector.values(); | return collector.values(); | ||||
} | } | ||||
// We are trying to determine abstract methods left over at the bottom of a hierarchy that have not been | |||||
// concretized. | |||||
private void getOutstandingAbstractMethodsHelper(ResolvedType type,Map collector) { | |||||
if (type==null) return; | |||||
// Get to the top | |||||
if (type!=null && !type.equals(ResolvedType.OBJECT)) { | |||||
if (type.getSuperclass()!=null) | |||||
getOutstandingAbstractMethodsHelper(type.getSuperclass(),collector); | |||||
} | |||||
ResolvedMember[] rms = type.getDeclaredMethods(); | |||||
if (rms!=null) { | |||||
for (int i = 0; i < rms.length; i++) { | |||||
// We are trying to determine abstract methods left over at the bottom of a hierarchy that have not been | |||||
// concretized. | |||||
private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map collector) { | |||||
if (type == null) | |||||
return; | |||||
// Get to the top | |||||
if (!type.equals(ResolvedType.OBJECT)) { | |||||
if (type.getSuperclass() != null) | |||||
getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector); | |||||
} | |||||
ResolvedMember[] rms = type.getDeclaredMethods(); | |||||
if (rms != null) { | |||||
for (int i = 0; i < rms.length; i++) { | |||||
ResolvedMember member = rms[i]; | ResolvedMember member = rms[i]; | ||||
String key = member.getName()+member.getSignature(); | |||||
String key = member.getName() + member.getSignature(); | |||||
if (member.isAbstract()) { | if (member.isAbstract()) { | ||||
collector.put(key,member); | |||||
collector.put(key, member); | |||||
} else { | } else { | ||||
collector.remove(key); | collector.remove(key); | ||||
} | } | ||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | /** | ||||
* Rebuild the XML snip that defines this concrete aspect, for log error purpose | |||||
* | |||||
* @return string repr. | |||||
*/ | |||||
private String stringify() { | |||||
StringBuffer sb = new StringBuffer("<concrete-aspect name='"); | |||||
sb.append(m_concreteAspect.name); | |||||
sb.append("' extends='"); | |||||
sb.append(m_concreteAspect.extend); | |||||
sb.append("'/> in aop.xml"); | |||||
return sb.toString(); | |||||
} | |||||
private boolean hasPointcutAnnotation(ResolvedMember member) { | |||||
AnnotationX[] as = member.getAnnotations(); | |||||
if (as==null || as.length==0) return false; | |||||
for (int i = 0; i < as.length; i++) { | |||||
* Rebuild the XML snip that defines this concrete aspect, for log error purpose | |||||
* | |||||
* @return string repr. | |||||
*/ | |||||
private String stringify() { | |||||
StringBuffer sb = new StringBuffer("<concrete-aspect name='"); | |||||
sb.append(m_concreteAspect.name); | |||||
sb.append("' extends='"); | |||||
sb.append(m_concreteAspect.extend); | |||||
sb.append("'/> in aop.xml"); | |||||
return sb.toString(); | |||||
} | |||||
private boolean hasPointcutAnnotation(ResolvedMember member) { | |||||
AnnotationX[] as = member.getAnnotations(); | |||||
if (as == null || as.length == 0) | |||||
return false; | |||||
for (int i = 0; i < as.length; i++) { | |||||
if (as[i].getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) { | if (as[i].getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) { | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | |||||
} | |||||
public String getClassName () { | |||||
return m_concreteAspect.name; | |||||
} | |||||
/** | |||||
* Build the bytecode for the concrete aspect | |||||
* | |||||
* @return concrete aspect bytecode | |||||
*/ | |||||
public byte[] getBytes() { | |||||
if (!m_isValid) { | |||||
throw new RuntimeException("Must validate first"); | |||||
} | |||||
//TODO AV - abstract away from BCEL... | |||||
// @Aspect //inherit clause from m_parent | |||||
// @DeclarePrecedence("....") // if any | |||||
// public class xxxName [extends xxxExtends] { | |||||
// [@Pointcut(xxxExpression-n) | |||||
// public void xxxName-n() {}] | |||||
// } | |||||
// @Aspect public class ... | |||||
LazyClassGen cg = new LazyClassGen( | |||||
m_concreteAspect.name.replace('.', '/'), | |||||
(m_parent==null)?"java/lang/Object":m_parent.getName().replace('.', '/'), | |||||
null,//TODO AV - we could point to the aop.xml that defines it and use JSR-45 | |||||
Modifier.PUBLIC + Constants.ACC_SUPER, | |||||
EMPTY_STRINGS, | |||||
m_world | |||||
); | |||||
AnnotationGen ag = new AnnotationGen( | |||||
new ObjectType("org/aspectj/lang/annotation/Aspect"), | |||||
Collections.EMPTY_LIST, | |||||
true, | |||||
cg.getConstantPool() | |||||
); | |||||
cg.addAnnotation(ag); | |||||
if (m_concreteAspect.precedence != null) { | |||||
SimpleElementValueGen svg = new SimpleElementValueGen( | |||||
ElementValueGen.STRING, | |||||
cg.getConstantPool(), | |||||
m_concreteAspect.precedence | |||||
); | |||||
List elems = new ArrayList(); | |||||
elems.add(new ElementNameValuePairGen("value", svg, cg.getConstantPool())); | |||||
AnnotationGen agprec = new AnnotationGen( | |||||
new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), | |||||
elems, | |||||
true, | |||||
cg.getConstantPool() | |||||
); | |||||
cg.addAnnotation(agprec); | |||||
} | |||||
// default constructor | |||||
LazyMethodGen init = new LazyMethodGen( | |||||
Modifier.PUBLIC, | |||||
Type.VOID, | |||||
"<init>", | |||||
EMPTY_TYPES, | |||||
EMPTY_STRINGS, | |||||
cg | |||||
); | |||||
InstructionList cbody = init.getBody(); | |||||
cbody.append(InstructionConstants.ALOAD_0); | |||||
cbody.append(cg.getFactory().createInvoke( | |||||
(m_parent==null)?"java/lang/Object":m_parent.getName().replace('.', '/'), | |||||
"<init>", | |||||
Type.VOID, | |||||
EMPTY_TYPES, | |||||
Constants.INVOKESPECIAL | |||||
)); | |||||
cbody.append(InstructionConstants.RETURN); | |||||
cg.addMethodGen(init); | |||||
for (Iterator it = m_concreteAspect.pointcuts.iterator(); it.hasNext();) { | |||||
Definition.Pointcut abstractPc = (Definition.Pointcut) it.next(); | |||||
LazyMethodGen mg = new LazyMethodGen( | |||||
Modifier.PUBLIC,//TODO AV - respect visibility instead of opening up? | |||||
Type.VOID, | |||||
abstractPc.name, | |||||
EMPTY_TYPES, | |||||
EMPTY_STRINGS, | |||||
cg | |||||
); | |||||
SimpleElementValueGen svg = new SimpleElementValueGen( | |||||
ElementValueGen.STRING, | |||||
cg.getConstantPool(), | |||||
abstractPc.expression | |||||
); | |||||
List elems = new ArrayList(); | |||||
elems.add(new ElementNameValuePairGen("value", svg, cg.getConstantPool())); | |||||
AnnotationGen mag = new AnnotationGen( | |||||
new ObjectType("org/aspectj/lang/annotation/Pointcut"), | |||||
elems, | |||||
true, | |||||
cg.getConstantPool() | |||||
); | |||||
AnnotationX max = new AnnotationX(mag, m_world); | |||||
mg.addAnnotation(max); | |||||
InstructionList body = mg.getBody(); | |||||
body.append(InstructionConstants.RETURN); | |||||
cg.addMethodGen(mg); | |||||
} | |||||
// handle the perClause | |||||
ReferenceType rt = new ReferenceType(ResolvedType.forName(m_concreteAspect.name).getSignature(),m_world); | |||||
BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt,m_perClause.getKind()); | |||||
perClauseMunger.forceMunge(cg, false); | |||||
//TODO AV - unsafe cast | |||||
// register the fresh new class into the world repository as it does not exist on the classpath anywhere | |||||
JavaClass jc = cg.getJavaClass((BcelWorld) m_world); | |||||
((BcelWorld) m_world).addSourceObjectType(jc); | |||||
return jc.getBytes(); | |||||
} | |||||
/** | |||||
* Error reporting | |||||
* | |||||
* @param message | |||||
*/ | |||||
private void reportError(String message) { | |||||
m_world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null)); | |||||
} | |||||
return false; | |||||
} | |||||
public String getClassName() { | |||||
return m_concreteAspect.name; | |||||
} | |||||
/** | |||||
* Build the bytecode for the concrete aspect | |||||
* | |||||
* @return concrete aspect bytecode | |||||
*/ | |||||
public byte[] getBytes() { | |||||
if (!m_isValid) { | |||||
throw new RuntimeException("Must validate first"); | |||||
} | |||||
// TODO AV - abstract away from BCEL... | |||||
// @Aspect //inherit clause from m_parent | |||||
// @DeclarePrecedence("....") // if any | |||||
// public class xxxName [extends xxxExtends] { | |||||
// [@Pointcut(xxxExpression-n) | |||||
// public void xxxName-n() {}] | |||||
// } | |||||
// @Aspect public class ... | |||||
LazyClassGen cg = new LazyClassGen(m_concreteAspect.name.replace('.', '/'), (m_parent == null) ? "java/lang/Object" | |||||
: m_parent.getName().replace('.', '/'), null,// TODO AV - we could point to the aop.xml that defines it and use | |||||
// JSR-45 | |||||
Modifier.PUBLIC + Constants.ACC_SUPER, EMPTY_STRINGS, m_world); | |||||
AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), Collections.EMPTY_LIST, true, cg | |||||
.getConstantPool()); | |||||
cg.addAnnotation(ag); | |||||
if (m_concreteAspect.precedence != null) { | |||||
SimpleElementValueGen svg = new SimpleElementValueGen(ElementValueGen.STRING, cg.getConstantPool(), | |||||
m_concreteAspect.precedence); | |||||
List elems = new ArrayList(); | |||||
elems.add(new ElementNameValuePairGen("value", svg, cg.getConstantPool())); | |||||
AnnotationGen agprec = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), elems, true, | |||||
cg.getConstantPool()); | |||||
cg.addAnnotation(agprec); | |||||
} | |||||
// default constructor | |||||
LazyMethodGen init = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", EMPTY_TYPES, EMPTY_STRINGS, cg); | |||||
InstructionList cbody = init.getBody(); | |||||
cbody.append(InstructionConstants.ALOAD_0); | |||||
cbody.append(cg.getFactory().createInvoke((m_parent == null) ? "java/lang/Object" : m_parent.getName().replace('.', '/'), | |||||
"<init>", Type.VOID, EMPTY_TYPES, Constants.INVOKESPECIAL)); | |||||
cbody.append(InstructionConstants.RETURN); | |||||
cg.addMethodGen(init); | |||||
for (Iterator it = m_concreteAspect.pointcuts.iterator(); it.hasNext();) { | |||||
Definition.Pointcut abstractPc = (Definition.Pointcut) it.next(); | |||||
LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC,// TODO AV - respect visibility instead of opening up? | |||||
Type.VOID, abstractPc.name, EMPTY_TYPES, EMPTY_STRINGS, cg); | |||||
SimpleElementValueGen svg = new SimpleElementValueGen(ElementValueGen.STRING, cg.getConstantPool(), | |||||
abstractPc.expression); | |||||
List elems = new ArrayList(); | |||||
elems.add(new ElementNameValuePairGen("value", svg, cg.getConstantPool())); | |||||
AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Pointcut"), elems, true, cg | |||||
.getConstantPool()); | |||||
AnnotationX max = new AnnotationX(mag, m_world); | |||||
mg.addAnnotation(max); | |||||
InstructionList body = mg.getBody(); | |||||
body.append(InstructionConstants.RETURN); | |||||
cg.addMethodGen(mg); | |||||
} | |||||
// handle the perClause | |||||
ReferenceType rt = new ReferenceType(ResolvedType.forName(m_concreteAspect.name).getSignature(), m_world); | |||||
BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt, m_perClause.getKind()); | |||||
perClauseMunger.forceMunge(cg, false); | |||||
// TODO AV - unsafe cast | |||||
// register the fresh new class into the world repository as it does not exist on the classpath anywhere | |||||
JavaClass jc = cg.getJavaClass((BcelWorld) m_world); | |||||
((BcelWorld) m_world).addSourceObjectType(jc); | |||||
return jc.getBytes(); | |||||
} | |||||
/** | |||||
* Error reporting | |||||
* | |||||
* @param message | |||||
*/ | |||||
private void reportError(String message) { | |||||
m_world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null)); | |||||
} | |||||
} | } |
*******************************************************************************/ | *******************************************************************************/ | ||||
package org.aspectj.weaver.loadtime; | package org.aspectj.weaver.loadtime; | ||||
import java.util.Collections; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import org.aspectj.bridge.IMessage; | import org.aspectj.bridge.IMessage; | ||||
import org.aspectj.bridge.IMessageHandler; | import org.aspectj.bridge.IMessageHandler; | ||||
import org.aspectj.bridge.Message; | import org.aspectj.bridge.Message; | ||||
import org.aspectj.util.LangUtil; | import org.aspectj.util.LangUtil; | ||||
import java.util.Collections; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
/** | /** | ||||
* A class that hanldes LTW options. | |||||
* Note: AV - I choosed to not reuse AjCompilerOptions and alike since those implies too many dependancies on | |||||
* jdt and ajdt modules. | |||||
* | |||||
* A class that hanldes LTW options. Note: AV - I choosed to not reuse AjCompilerOptions and alike since those implies too many | |||||
* dependancies on jdt and ajdt modules. | |||||
* | |||||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | ||||
*/ | */ | ||||
public class Options { | public class Options { | ||||
private final static String OPTION_15 = "-1.5"; | |||||
private final static String OPTION_lazyTjp = "-XlazyTjp"; | |||||
private final static String OPTION_noWarn = "-nowarn"; | |||||
private final static String OPTION_noWarnNone = "-warn:none"; | |||||
private final static String OPTION_proceedOnError = "-proceedOnError"; | |||||
private final static String OPTION_verbose = "-verbose"; | |||||
private final static String OPTION_debug = "-debug"; | |||||
private final static String OPTION_reweavable = "-Xreweavable";//notReweavable is default for LTW | |||||
private final static String OPTION_noinline = "-Xnoinline"; | |||||
private final static String OPTION_addSerialVersionUID = "-XaddSerialVersionUID"; | |||||
private final static String OPTION_hasMember = "-XhasMember"; | |||||
private final static String OPTION_pinpoint = "-Xdev:pinpoint"; | |||||
private final static String OPTION_showWeaveInfo = "-showWeaveInfo"; | |||||
private final static String OPTIONVALUED_messageHandler = "-XmessageHandlerClass:"; | |||||
private static final String OPTIONVALUED_Xlintfile = "-Xlintfile:"; | |||||
private static final String OPTIONVALUED_Xlint = "-Xlint:"; | |||||
private static final String OPTIONVALUED_joinpoints = "-Xjoinpoints:"; | |||||
private static final String OPTIONVALUED_Xset = "-Xset:"; | |||||
private final static String OPTION_15 = "-1.5"; | |||||
private final static String OPTION_lazyTjp = "-XlazyTjp"; | |||||
private final static String OPTION_noWarn = "-nowarn"; | |||||
private final static String OPTION_noWarnNone = "-warn:none"; | |||||
private final static String OPTION_proceedOnError = "-proceedOnError"; | |||||
private final static String OPTION_verbose = "-verbose"; | |||||
private final static String OPTION_debug = "-debug"; | |||||
private final static String OPTION_reweavable = "-Xreweavable";// notReweavable is default for LTW | |||||
private final static String OPTION_noinline = "-Xnoinline"; | |||||
private final static String OPTION_addSerialVersionUID = "-XaddSerialVersionUID"; | |||||
private final static String OPTION_hasMember = "-XhasMember"; | |||||
private final static String OPTION_pinpoint = "-Xdev:pinpoint"; | |||||
private final static String OPTION_showWeaveInfo = "-showWeaveInfo"; | |||||
private final static String OPTIONVALUED_messageHandler = "-XmessageHandlerClass:"; | |||||
private static final String OPTIONVALUED_Xlintfile = "-Xlintfile:"; | |||||
private static final String OPTIONVALUED_Xlint = "-Xlint:"; | |||||
private static final String OPTIONVALUED_joinpoints = "-Xjoinpoints:"; | |||||
private static final String OPTIONVALUED_Xset = "-Xset:"; | |||||
public static WeaverOption parse(String options, ClassLoader laoder, IMessageHandler imh) { | |||||
WeaverOption weaverOption = new WeaverOption(imh); | |||||
public static WeaverOption parse(String options, ClassLoader laoder, IMessageHandler imh) { | |||||
WeaverOption weaverOption = new WeaverOption(imh); | |||||
if (LangUtil.isEmpty(options)) { | |||||
return weaverOption; | |||||
} | |||||
// the first option wins | |||||
List flags = LangUtil.anySplit(options, " "); | |||||
Collections.reverse(flags); | |||||
if (LangUtil.isEmpty(options)) { | |||||
return weaverOption; | |||||
} | |||||
// the first option wins | |||||
List flags = LangUtil.anySplit(options, " "); | |||||
Collections.reverse(flags); | |||||
// do a first round on the message handler since it will report the options themselves | |||||
for (Iterator iterator = flags.iterator(); iterator.hasNext();) { | |||||
String arg = (String) iterator.next(); | |||||
if (arg.startsWith(OPTIONVALUED_messageHandler)) { | |||||
if (arg.length() > OPTIONVALUED_messageHandler.length()) { | |||||
String handlerClass = arg.substring(OPTIONVALUED_messageHandler.length()).trim(); | |||||
try { | |||||
Class handler = Class.forName(handlerClass, false, laoder); | |||||
weaverOption.messageHandler = ((IMessageHandler) handler.newInstance()); | |||||
} catch (Throwable t) { | |||||
weaverOption.messageHandler.handleMessage( | |||||
new Message( | |||||
"Cannot instantiate message handler " + handlerClass, | |||||
IMessage.ERROR, | |||||
t, | |||||
null | |||||
) | |||||
); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// do a first round on the message handler since it will report the options themselves | |||||
for (Iterator iterator = flags.iterator(); iterator.hasNext();) { | |||||
String arg = (String) iterator.next(); | |||||
if (arg.startsWith(OPTIONVALUED_messageHandler)) { | |||||
if (arg.length() > OPTIONVALUED_messageHandler.length()) { | |||||
String handlerClass = arg.substring(OPTIONVALUED_messageHandler.length()).trim(); | |||||
try { | |||||
Class handler = Class.forName(handlerClass, false, laoder); | |||||
weaverOption.messageHandler = ((IMessageHandler) handler.newInstance()); | |||||
} catch (Throwable t) { | |||||
weaverOption.messageHandler.handleMessage(new Message("Cannot instantiate message handler " + handlerClass, | |||||
IMessage.ERROR, t, null)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// configure the other options | |||||
for (Iterator iterator = flags.iterator(); iterator.hasNext();) { | |||||
String arg = (String) iterator.next(); | |||||
if (arg.equals(OPTION_15)) { | |||||
weaverOption.java5 = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_lazyTjp)) { | |||||
weaverOption.lazyTjp = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_noinline)) { | |||||
weaverOption.noInline = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_addSerialVersionUID)) { | |||||
weaverOption.addSerialVersionUID=true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_noWarn) || arg.equalsIgnoreCase(OPTION_noWarnNone)) { | |||||
weaverOption.noWarn = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_proceedOnError)) { | |||||
weaverOption.proceedOnError = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_reweavable)) { | |||||
weaverOption.notReWeavable = false; | |||||
} else if (arg.equalsIgnoreCase(OPTION_showWeaveInfo)) { | |||||
weaverOption.showWeaveInfo = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_hasMember)) { | |||||
weaverOption.hasMember = true; | |||||
} else if (arg.startsWith(OPTIONVALUED_joinpoints)) { | |||||
if (arg.length()>OPTIONVALUED_joinpoints.length()) | |||||
weaverOption.optionalJoinpoints = arg.substring(OPTIONVALUED_joinpoints.length()).trim(); | |||||
} else if (arg.equalsIgnoreCase(OPTION_verbose)) { | |||||
weaverOption.verbose = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_debug)) { | |||||
weaverOption.debug = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_pinpoint)) { | |||||
weaverOption.pinpoint = true; | |||||
} else if (arg.startsWith(OPTIONVALUED_messageHandler)) { | |||||
;// handled in first round | |||||
} else if (arg.startsWith(OPTIONVALUED_Xlintfile)) { | |||||
if (arg.length() > OPTIONVALUED_Xlintfile.length()) { | |||||
weaverOption.lintFile = arg.substring(OPTIONVALUED_Xlintfile.length()).trim(); | |||||
} | |||||
} else if (arg.startsWith(OPTIONVALUED_Xlint)) { | |||||
if (arg.length() > OPTIONVALUED_Xlint.length()) { | |||||
weaverOption.lint = arg.substring(OPTIONVALUED_Xlint.length()).trim(); | |||||
} | |||||
} else if (arg.startsWith(OPTIONVALUED_Xset)) { | |||||
if (arg.length() > OPTIONVALUED_Xlint.length()) { | |||||
weaverOption.xSet = arg.substring(OPTIONVALUED_Xset.length()).trim(); | |||||
} | |||||
} else { | |||||
weaverOption.messageHandler.handleMessage( | |||||
new Message( | |||||
"Cannot configure weaver with option '" + arg + "': unknown option", | |||||
IMessage.WARNING, | |||||
null, | |||||
null | |||||
) | |||||
); | |||||
} | |||||
} | |||||
// configure the other options | |||||
for (Iterator iterator = flags.iterator(); iterator.hasNext();) { | |||||
String arg = (String) iterator.next(); | |||||
if (arg.equals(OPTION_15)) { | |||||
weaverOption.java5 = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_lazyTjp)) { | |||||
weaverOption.lazyTjp = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_noinline)) { | |||||
weaverOption.noInline = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_addSerialVersionUID)) { | |||||
weaverOption.addSerialVersionUID = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_noWarn) || arg.equalsIgnoreCase(OPTION_noWarnNone)) { | |||||
weaverOption.noWarn = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_proceedOnError)) { | |||||
weaverOption.proceedOnError = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_reweavable)) { | |||||
weaverOption.notReWeavable = false; | |||||
} else if (arg.equalsIgnoreCase(OPTION_showWeaveInfo)) { | |||||
weaverOption.showWeaveInfo = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_hasMember)) { | |||||
weaverOption.hasMember = true; | |||||
} else if (arg.startsWith(OPTIONVALUED_joinpoints)) { | |||||
if (arg.length() > OPTIONVALUED_joinpoints.length()) | |||||
weaverOption.optionalJoinpoints = arg.substring(OPTIONVALUED_joinpoints.length()).trim(); | |||||
} else if (arg.equalsIgnoreCase(OPTION_verbose)) { | |||||
weaverOption.verbose = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_debug)) { | |||||
weaverOption.debug = true; | |||||
} else if (arg.equalsIgnoreCase(OPTION_pinpoint)) { | |||||
weaverOption.pinpoint = true; | |||||
} else if (arg.startsWith(OPTIONVALUED_messageHandler)) { | |||||
// handled in first round | |||||
} else if (arg.startsWith(OPTIONVALUED_Xlintfile)) { | |||||
if (arg.length() > OPTIONVALUED_Xlintfile.length()) { | |||||
weaverOption.lintFile = arg.substring(OPTIONVALUED_Xlintfile.length()).trim(); | |||||
} | |||||
} else if (arg.startsWith(OPTIONVALUED_Xlint)) { | |||||
if (arg.length() > OPTIONVALUED_Xlint.length()) { | |||||
weaverOption.lint = arg.substring(OPTIONVALUED_Xlint.length()).trim(); | |||||
} | |||||
} else if (arg.startsWith(OPTIONVALUED_Xset)) { | |||||
if (arg.length() > OPTIONVALUED_Xlint.length()) { | |||||
weaverOption.xSet = arg.substring(OPTIONVALUED_Xset.length()).trim(); | |||||
} | |||||
} else { | |||||
weaverOption.messageHandler.handleMessage(new Message("Cannot configure weaver with option '" + arg | |||||
+ "': unknown option", IMessage.WARNING, null, null)); | |||||
} | |||||
} | |||||
// refine message handler configuration | |||||
if (weaverOption.noWarn) { | |||||
weaverOption.messageHandler.ignore(IMessage.WARNING); | |||||
} | |||||
if (weaverOption.verbose) { | |||||
weaverOption.messageHandler.dontIgnore(IMessage.INFO); | |||||
} | |||||
if (weaverOption.debug) { | |||||
weaverOption.messageHandler.dontIgnore(IMessage.DEBUG); | |||||
} | |||||
if (weaverOption.showWeaveInfo) { | |||||
weaverOption.messageHandler.dontIgnore(IMessage.WEAVEINFO); | |||||
} | |||||
// refine message handler configuration | |||||
if (weaverOption.noWarn) { | |||||
weaverOption.messageHandler.ignore(IMessage.WARNING); | |||||
} | |||||
if (weaverOption.verbose) { | |||||
weaverOption.messageHandler.dontIgnore(IMessage.INFO); | |||||
} | |||||
if (weaverOption.debug) { | |||||
weaverOption.messageHandler.dontIgnore(IMessage.DEBUG); | |||||
} | |||||
if (weaverOption.showWeaveInfo) { | |||||
weaverOption.messageHandler.dontIgnore(IMessage.WEAVEINFO); | |||||
} | |||||
return weaverOption; | |||||
} | |||||
return weaverOption; | |||||
} | |||||
public static class WeaverOption { | |||||
public static class WeaverOption { | |||||
boolean java5; | boolean java5; | ||||
boolean lazyTjp; | |||||
boolean hasMember; | |||||
String optionalJoinpoints; | |||||
boolean noWarn; | |||||
boolean proceedOnError; | |||||
boolean verbose; | |||||
boolean debug; | |||||
boolean notReWeavable = true;//default to notReweavable for LTW (faster) | |||||
boolean noInline; | |||||
boolean addSerialVersionUID; | |||||
boolean showWeaveInfo; | |||||
boolean pinpoint; | |||||
IMessageHandler messageHandler; | |||||
String lint; | |||||
String lintFile; | |||||
String xSet; | |||||
boolean lazyTjp; | |||||
boolean hasMember; | |||||
String optionalJoinpoints; | |||||
boolean noWarn; | |||||
boolean proceedOnError; | |||||
boolean verbose; | |||||
boolean debug; | |||||
boolean notReWeavable = true;// default to notReweavable for LTW (faster) | |||||
boolean noInline; | |||||
boolean addSerialVersionUID; | |||||
boolean showWeaveInfo; | |||||
boolean pinpoint; | |||||
IMessageHandler messageHandler; | |||||
String lint; | |||||
String lintFile; | |||||
String xSet; | |||||
public WeaverOption(IMessageHandler imh) { | |||||
// messageHandler = new DefaultMessageHandler();//default | |||||
this.messageHandler = imh; | |||||
} | |||||
} | |||||
public WeaverOption(IMessageHandler imh) { | |||||
// messageHandler = new DefaultMessageHandler();//default | |||||
this.messageHandler = imh; | |||||
} | |||||
} | |||||
} | } |
/** | /** | ||||
* FIXME AV - doc, concrete aspect | * FIXME AV - doc, concrete aspect | ||||
* | |||||
* | |||||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | ||||
*/ | */ | ||||
public class DocumentParser extends DefaultHandler { | public class DocumentParser extends DefaultHandler { | ||||
/** | |||||
* The current DTD public id. The matching dtd will be searched as a resource. | |||||
*/ | |||||
private final static String DTD_PUBLIC_ID = "-//AspectJ//DTD 1.5.0//EN"; | |||||
/** | |||||
* The current DTD public id. The matching dtd will be searched as a resource. | |||||
*/ | |||||
private final static String DTD_PUBLIC_ID = "-//AspectJ//DTD 1.5.0//EN"; | |||||
/** | |||||
* The DTD alias, for better user experience. | |||||
*/ | |||||
private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectJ//DTD//EN"; | |||||
/** | |||||
* The DTD alias, for better user experience. | |||||
*/ | |||||
private final static String DTD_PUBLIC_ID_ALIAS = "-//AspectJ//DTD//EN"; | |||||
/** | |||||
* A handler to the DTD stream so that we are only using one file descriptor | |||||
*/ | |||||
private final static InputStream DTD_STREAM = DocumentParser.class.getResourceAsStream("/aspectj_1_5_0.dtd"); | |||||
/** | |||||
* A handler to the DTD stream so that we are only using one file descriptor | |||||
*/ | |||||
private final static InputStream DTD_STREAM = DocumentParser.class.getResourceAsStream("/aspectj_1_5_0.dtd"); | |||||
private final static String ASPECTJ_ELEMENT = "aspectj"; | |||||
private final static String WEAVER_ELEMENT = "weaver"; | |||||
private final static String DUMP_ELEMENT = "dump"; | |||||
private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; | |||||
private final static String INCLUDE_ELEMENT = "include"; | |||||
private final static String EXCLUDE_ELEMENT = "exclude"; | |||||
private final static String OPTIONS_ATTRIBUTE = "options"; | |||||
private final static String ASPECTS_ELEMENT = "aspects"; | |||||
private final static String ASPECT_ELEMENT = "aspect"; | |||||
private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; | |||||
private final static String NAME_ATTRIBUTE = "name"; | |||||
private final static String EXTEND_ATTRIBUTE = "extends"; | |||||
private final static String PRECEDENCE_ATTRIBUTE = "precedence"; | |||||
private final static String POINTCUT_ELEMENT = "pointcut"; | |||||
private final static String WITHIN_ATTRIBUTE = "within"; | |||||
private final static String EXPRESSION_ATTRIBUTE = "expression"; | |||||
private final static String ASPECTJ_ELEMENT = "aspectj"; | |||||
private final static String WEAVER_ELEMENT = "weaver"; | |||||
private final static String DUMP_ELEMENT = "dump"; | |||||
private final static String DUMP_BEFOREANDAFTER_ATTRIBUTE = "beforeandafter"; | |||||
private final static String INCLUDE_ELEMENT = "include"; | |||||
private final static String EXCLUDE_ELEMENT = "exclude"; | |||||
private final static String OPTIONS_ATTRIBUTE = "options"; | |||||
private final static String ASPECTS_ELEMENT = "aspects"; | |||||
private final static String ASPECT_ELEMENT = "aspect"; | |||||
private final static String CONCRETE_ASPECT_ELEMENT = "concrete-aspect"; | |||||
private final static String NAME_ATTRIBUTE = "name"; | |||||
private final static String EXTEND_ATTRIBUTE = "extends"; | |||||
private final static String PRECEDENCE_ATTRIBUTE = "precedence"; | |||||
private final static String POINTCUT_ELEMENT = "pointcut"; | |||||
private final static String WITHIN_ATTRIBUTE = "within"; | |||||
private final static String EXPRESSION_ATTRIBUTE = "expression"; | |||||
private final Definition m_definition; | |||||
private final Definition m_definition; | |||||
private boolean m_inAspectJ; | |||||
private boolean m_inWeaver; | |||||
private boolean m_inAspects; | |||||
private boolean m_inAspectJ; | |||||
private boolean m_inWeaver; | |||||
private boolean m_inAspects; | |||||
private Definition.ConcreteAspect m_lastConcreteAspect; | |||||
private Definition.ConcreteAspect m_lastConcreteAspect; | |||||
private DocumentParser() { | |||||
m_definition = new Definition(); | |||||
} | |||||
private DocumentParser() { | |||||
m_definition = new Definition(); | |||||
} | |||||
public static Definition parse(final URL url) throws Exception { | |||||
InputStream in = null; | |||||
try { | |||||
DocumentParser parser = new DocumentParser(); | |||||
public static Definition parse(final URL url) throws Exception { | |||||
InputStream in = null; | |||||
try { | |||||
DocumentParser parser = new DocumentParser(); | |||||
XMLReader xmlReader = getXMLReader(); | |||||
xmlReader.setContentHandler(parser); | |||||
xmlReader.setErrorHandler(parser); | |||||
XMLReader xmlReader = getXMLReader(); | |||||
xmlReader.setContentHandler(parser); | |||||
xmlReader.setErrorHandler(parser); | |||||
try { | |||||
xmlReader.setFeature("http://xml.org/sax/features/validation", false); | |||||
} catch (SAXException e) { | |||||
;//fine, the parser don't do validation | |||||
} | |||||
try { | |||||
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); | |||||
} catch (SAXException e) { | |||||
;//fine, the parser don't do validation | |||||
} | |||||
try { | |||||
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); | |||||
} catch (SAXException e) { | |||||
;//fine, the parser don't do validation | |||||
} | |||||
try { | |||||
xmlReader.setFeature("http://xml.org/sax/features/validation", false); | |||||
} catch (SAXException e) { | |||||
// fine, the parser don't do validation | |||||
} | |||||
try { | |||||
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false); | |||||
} catch (SAXException e) { | |||||
// fine, the parser don't do validation | |||||
} | |||||
try { | |||||
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); | |||||
} catch (SAXException e) { | |||||
// fine, the parser don't do validation | |||||
} | |||||
xmlReader.setEntityResolver(parser); | |||||
in = url.openStream(); | |||||
xmlReader.parse(new InputSource(in)); | |||||
return parser.m_definition; | |||||
} finally { | |||||
try { | |||||
in.close(); | |||||
} catch (Throwable t) { | |||||
xmlReader.setEntityResolver(parser); | |||||
in = url.openStream(); | |||||
xmlReader.parse(new InputSource(in)); | |||||
return parser.m_definition; | |||||
} finally { | |||||
try { | |||||
in.close(); | |||||
} catch (Throwable t) { | |||||
; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
private static XMLReader getXMLReader() throws SAXException, ParserConfigurationException { | private static XMLReader getXMLReader() throws SAXException, ParserConfigurationException { | ||||
XMLReader xmlReader = null; | XMLReader xmlReader = null; | ||||
try { | try { | ||||
xmlReader = XMLReaderFactory.createXMLReader(); | xmlReader = XMLReaderFactory.createXMLReader(); | ||||
} | } | ||||
/* .. and ignore "System property ... not set" and then try this instead */ | /* .. and ignore "System property ... not set" and then try this instead */ | ||||
catch (SAXException ex) { | catch (SAXException ex) { | ||||
xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); | xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); | ||||
return xmlReader; | return xmlReader; | ||||
} | } | ||||
public InputSource resolveEntity(String publicId, String systemId) throws SAXException { | |||||
if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) { | |||||
InputStream in = DTD_STREAM; | |||||
if (in == null) { | |||||
System.err.println( | |||||
"AspectJ - WARN - could not read DTD " | |||||
+ publicId | |||||
); | |||||
return null; | |||||
} else { | |||||
return new InputSource(in); | |||||
} | |||||
} else { | |||||
System.err.println( | |||||
"AspectJ - WARN - unknown DTD " | |||||
+ publicId | |||||
+ " - consider using " | |||||
+ DTD_PUBLIC_ID | |||||
); | |||||
return null; | |||||
} | |||||
} | |||||
public InputSource resolveEntity(String publicId, String systemId) throws SAXException { | |||||
if (publicId.equals(DTD_PUBLIC_ID) || publicId.equals(DTD_PUBLIC_ID_ALIAS)) { | |||||
InputStream in = DTD_STREAM; | |||||
if (in == null) { | |||||
System.err.println("AspectJ - WARN - could not read DTD " + publicId); | |||||
return null; | |||||
} else { | |||||
return new InputSource(in); | |||||
} | |||||
} else { | |||||
System.err.println("AspectJ - WARN - unknown DTD " + publicId + " - consider using " + DTD_PUBLIC_ID); | |||||
return null; | |||||
} | |||||
} | |||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { | |||||
if (ASPECT_ELEMENT.equals(qName)) { | |||||
String name = attributes.getValue(NAME_ATTRIBUTE); | |||||
if (!isNull(name)) { | |||||
m_definition.getAspectClassNames().add(name); | |||||
} | |||||
} else if (WEAVER_ELEMENT.equals(qName)) { | |||||
String options = attributes.getValue(OPTIONS_ATTRIBUTE); | |||||
if (!isNull(options)) { | |||||
m_definition.appendWeaverOptions(options); | |||||
} | |||||
m_inWeaver = true; | |||||
} else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { | |||||
String name = attributes.getValue(NAME_ATTRIBUTE); | |||||
String extend = attributes.getValue(EXTEND_ATTRIBUTE); | |||||
String precedence = attributes.getValue(PRECEDENCE_ATTRIBUTE); | |||||
if (!isNull(name)) { | |||||
if (isNull(precedence) && !isNull(extend)) {//if no precedence, then extends must be there | |||||
m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend); | |||||
} else if (!isNull(precedence)) { | |||||
// wether a pure precedence def, or an extendsANDprecedence def. | |||||
m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend, precedence); | |||||
} | |||||
m_definition.getConcreteAspects().add(m_lastConcreteAspect); | |||||
} | |||||
} else if (POINTCUT_ELEMENT.equals(qName) && m_lastConcreteAspect != null) { | |||||
String name = attributes.getValue(NAME_ATTRIBUTE); | |||||
String expression = attributes.getValue(EXPRESSION_ATTRIBUTE); | |||||
if (!isNull(name) && !isNull(expression)) { | |||||
m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression))); | |||||
} | |||||
} else if (ASPECTJ_ELEMENT.equals(qName)) { | |||||
if (m_inAspectJ) { | |||||
throw new SAXException("Found nested <aspectj> element"); | |||||
} | |||||
m_inAspectJ = true; | |||||
} else if (ASPECTS_ELEMENT.equals(qName)) { | |||||
m_inAspects = true; | |||||
} else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getIncludePatterns().add(typePattern); | |||||
} | |||||
} else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getExcludePatterns().add(typePattern); | |||||
} | |||||
} else if (DUMP_ELEMENT.equals(qName) && m_inWeaver) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getDumpPatterns().add(typePattern); | |||||
} | |||||
String beforeAndAfter = attributes.getValue(DUMP_BEFOREANDAFTER_ATTRIBUTE); | |||||
if (isTrue(beforeAndAfter)) { | |||||
m_definition.setDumpBefore(true); | |||||
} | |||||
} else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getAspectExcludePatterns().add(typePattern); | |||||
} | |||||
} else if (INCLUDE_ELEMENT.equals(qName) && m_inAspects) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getAspectIncludePatterns().add(typePattern); | |||||
} | |||||
} else { | |||||
throw new SAXException("Unknown element while parsing <aspectj> element: " + qName); | |||||
} | |||||
super.startElement(uri, localName, qName, attributes); | |||||
} | |||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { | |||||
if (ASPECT_ELEMENT.equals(qName)) { | |||||
String name = attributes.getValue(NAME_ATTRIBUTE); | |||||
if (!isNull(name)) { | |||||
m_definition.getAspectClassNames().add(name); | |||||
} | |||||
} else if (WEAVER_ELEMENT.equals(qName)) { | |||||
String options = attributes.getValue(OPTIONS_ATTRIBUTE); | |||||
if (!isNull(options)) { | |||||
m_definition.appendWeaverOptions(options); | |||||
} | |||||
m_inWeaver = true; | |||||
} else if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { | |||||
String name = attributes.getValue(NAME_ATTRIBUTE); | |||||
String extend = attributes.getValue(EXTEND_ATTRIBUTE); | |||||
String precedence = attributes.getValue(PRECEDENCE_ATTRIBUTE); | |||||
if (!isNull(name)) { | |||||
if (isNull(precedence) && !isNull(extend)) {// if no precedence, then extends must be there | |||||
m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend); | |||||
} else if (!isNull(precedence)) { | |||||
// wether a pure precedence def, or an extendsANDprecedence def. | |||||
m_lastConcreteAspect = new Definition.ConcreteAspect(name, extend, precedence); | |||||
} | |||||
m_definition.getConcreteAspects().add(m_lastConcreteAspect); | |||||
} | |||||
} else if (POINTCUT_ELEMENT.equals(qName) && m_lastConcreteAspect != null) { | |||||
String name = attributes.getValue(NAME_ATTRIBUTE); | |||||
String expression = attributes.getValue(EXPRESSION_ATTRIBUTE); | |||||
if (!isNull(name) && !isNull(expression)) { | |||||
m_lastConcreteAspect.pointcuts.add(new Definition.Pointcut(name, replaceXmlAnd(expression))); | |||||
} | |||||
} else if (ASPECTJ_ELEMENT.equals(qName)) { | |||||
if (m_inAspectJ) { | |||||
throw new SAXException("Found nested <aspectj> element"); | |||||
} | |||||
m_inAspectJ = true; | |||||
} else if (ASPECTS_ELEMENT.equals(qName)) { | |||||
m_inAspects = true; | |||||
} else if (INCLUDE_ELEMENT.equals(qName) && m_inWeaver) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getIncludePatterns().add(typePattern); | |||||
} | |||||
} else if (EXCLUDE_ELEMENT.equals(qName) && m_inWeaver) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getExcludePatterns().add(typePattern); | |||||
} | |||||
} else if (DUMP_ELEMENT.equals(qName) && m_inWeaver) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getDumpPatterns().add(typePattern); | |||||
} | |||||
String beforeAndAfter = attributes.getValue(DUMP_BEFOREANDAFTER_ATTRIBUTE); | |||||
if (isTrue(beforeAndAfter)) { | |||||
m_definition.setDumpBefore(true); | |||||
} | |||||
} else if (EXCLUDE_ELEMENT.equals(qName) && m_inAspects) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getAspectExcludePatterns().add(typePattern); | |||||
} | |||||
} else if (INCLUDE_ELEMENT.equals(qName) && m_inAspects) { | |||||
String typePattern = getWithinAttribute(attributes); | |||||
if (!isNull(typePattern)) { | |||||
m_definition.getAspectIncludePatterns().add(typePattern); | |||||
} | |||||
} else { | |||||
throw new SAXException("Unknown element while parsing <aspectj> element: " + qName); | |||||
} | |||||
super.startElement(uri, localName, qName, attributes); | |||||
} | |||||
private String getWithinAttribute(Attributes attributes) { | private String getWithinAttribute(Attributes attributes) { | ||||
return replaceXmlAnd(attributes.getValue(WITHIN_ATTRIBUTE)); | return replaceXmlAnd(attributes.getValue(WITHIN_ATTRIBUTE)); | ||||
} | } | ||||
public void endElement(String uri, String localName, String qName) throws SAXException { | |||||
if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { | |||||
m_lastConcreteAspect = null; | |||||
} else if (ASPECTJ_ELEMENT.equals(qName)) { | |||||
m_inAspectJ = false; | |||||
} else if (WEAVER_ELEMENT.equals(qName)) { | |||||
m_inWeaver = false; | |||||
} else if (ASPECTS_ELEMENT.equals(qName)) { | |||||
m_inAspects = false; | |||||
} | |||||
super.endElement(uri, localName, qName); | |||||
} | |||||
//TODO AV - define what we want for XML parser error - for now stderr | |||||
public void warning(SAXParseException e) throws SAXException { | |||||
super.warning(e); | |||||
} | |||||
public void error(SAXParseException e) throws SAXException { | |||||
super.error(e); | |||||
} | |||||
public void endElement(String uri, String localName, String qName) throws SAXException { | |||||
if (CONCRETE_ASPECT_ELEMENT.equals(qName)) { | |||||
m_lastConcreteAspect = null; | |||||
} else if (ASPECTJ_ELEMENT.equals(qName)) { | |||||
m_inAspectJ = false; | |||||
} else if (WEAVER_ELEMENT.equals(qName)) { | |||||
m_inWeaver = false; | |||||
} else if (ASPECTS_ELEMENT.equals(qName)) { | |||||
m_inAspects = false; | |||||
} | |||||
super.endElement(uri, localName, qName); | |||||
} | |||||
public void fatalError(SAXParseException e) throws SAXException { | |||||
super.fatalError(e); | |||||
} | |||||
// TODO AV - define what we want for XML parser error - for now stderr | |||||
public void warning(SAXParseException e) throws SAXException { | |||||
super.warning(e); | |||||
} | |||||
public void error(SAXParseException e) throws SAXException { | |||||
super.error(e); | |||||
} | |||||
private static String replaceXmlAnd(String expression) { | |||||
//TODO AV do we need to handle "..)AND" or "AND(.." ? | |||||
return LangUtil.replace(expression, " AND ", " && "); | |||||
} | |||||
public void fatalError(SAXParseException e) throws SAXException { | |||||
super.fatalError(e); | |||||
} | |||||
private boolean isNull(String s) { | |||||
return (s == null || s.length() <= 0); | |||||
} | |||||
private static String replaceXmlAnd(String expression) { | |||||
// TODO AV do we need to handle "..)AND" or "AND(.." ? | |||||
return LangUtil.replace(expression, " AND ", " && "); | |||||
} | |||||
private boolean isTrue(String s) { | |||||
return (s != null && s.equals("true")); | |||||
} | |||||
private boolean isNull(String s) { | |||||
return (s == null || s.length() <= 0); | |||||
} | |||||
private boolean isTrue(String s) { | |||||
return (s != null && s.equals("true")); | |||||
} | |||||
} | } |
public class AjTest extends TestCase { | public class AjTest extends TestCase { | ||||
public void testAj() { | public void testAj() { | ||||
Aj aj = new Aj(); | |||||
// Aj aj = | |||||
new Aj(); | |||||
} | } | ||||
public void testAjIWeavingContext() { | public void testAjIWeavingContext() { | ||||
ClassLoader loader = new URLClassLoader(new URL[] {}, null); | ClassLoader loader = new URLClassLoader(new URL[] {}, null); | ||||
IWeavingContext weavingContext = new DefaultWeavingContext(loader); | IWeavingContext weavingContext = new DefaultWeavingContext(loader); | ||||
Aj aj = new Aj(weavingContext); | |||||
// Aj aj = | |||||
new Aj(weavingContext); | |||||
} | } | ||||
public void testInitialize() { | public void testInitialize() { | ||||
ClassLoader loader = new URLClassLoader(new URL[] {}, null); | ClassLoader loader = new URLClassLoader(new URL[] {}, null); | ||||
Aj aj = new Aj(); | Aj aj = new Aj(); | ||||
String namespace = aj.getNamespace(loader); | String namespace = aj.getNamespace(loader); | ||||
assertEquals("Namespace should be empty","",namespace); | |||||
assertEquals("Namespace should be empty", "", namespace); | |||||
} | } | ||||
public void testGeneratedClassesExist() { | public void testGeneratedClassesExist() { | ||||
ClassLoader loader = new URLClassLoader(new URL[] {}, null); | ClassLoader loader = new URLClassLoader(new URL[] {}, null); | ||||
Aj aj = new Aj(); | Aj aj = new Aj(); | ||||
boolean exist = aj.generatedClassesExist(loader); | boolean exist = aj.generatedClassesExist(loader); | ||||
assertFalse("There should be no generated classes",exist); | |||||
assertFalse("There should be no generated classes", exist); | |||||
} | } | ||||
public void testFlushGeneratedClasses() { | public void testFlushGeneratedClasses() { | ||||
Aj aj = new Aj(); | Aj aj = new Aj(); | ||||
aj.flushGeneratedClasses(loader); | aj.flushGeneratedClasses(loader); | ||||
boolean exist = aj.generatedClassesExist(loader); | boolean exist = aj.generatedClassesExist(loader); | ||||
assertFalse("There should be no generated classes",exist); | |||||
assertFalse("There should be no generated classes", exist); | |||||
} | } | ||||
} | } |
import java.util.jar.JarFile; | import java.util.jar.JarFile; | ||||
import java.util.zip.ZipEntry; | import java.util.zip.ZipEntry; | ||||
import junit.framework.TestCase; | |||||
import com.bea.jvm.ClassPreProcessor; | import com.bea.jvm.ClassPreProcessor; | ||||
import com.bea.jvm.JVMFactory; | import com.bea.jvm.JVMFactory; | ||||
import junit.framework.TestCase; | |||||
public class JRockitAgentTest extends TestCase { | public class JRockitAgentTest extends TestCase { | ||||
protected void setUp() throws Exception { | protected void setUp() throws Exception { | ||||
public void testJRockitAgent() { | public void testJRockitAgent() { | ||||
ClassPreProcessor preProcessor = new JRockitAgent(); | ClassPreProcessor preProcessor = new JRockitAgent(); | ||||
ClassPreProcessor expectedPreProcessor = JVMFactory.getJVM().getClassLibrary().getClassPreProcessor(); | ClassPreProcessor expectedPreProcessor = JVMFactory.getJVM().getClassLibrary().getClassPreProcessor(); | ||||
assertEquals("JRocketAgent must be registered",expectedPreProcessor,preProcessor); | |||||
assertEquals("JRocketAgent must be registered", expectedPreProcessor, preProcessor); | |||||
} | } | ||||
public void testPreProcess() { | public void testPreProcess() { | ||||
ClassPreProcessor preProcessor = new JRockitAgent(); | ClassPreProcessor preProcessor = new JRockitAgent(); | ||||
preProcessor.preProcess(null,"foo.Bar",new byte[] {}); | |||||
preProcessor.preProcess(null, "foo.Bar", new byte[] {}); | |||||
} | } | ||||
public void testJrockitRecursionProtection () { | |||||
URLClassLoader thisLoader = (URLClassLoader)getClass().getClassLoader(); | |||||
public void testJrockitRecursionProtection() { | |||||
URLClassLoader thisLoader = (URLClassLoader) getClass().getClassLoader(); | |||||
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); | ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); | ||||
try { | try { | ||||
ClassLoader loader = new JRockitClassLoader(thisLoader); | ClassLoader loader = new JRockitClassLoader(thisLoader); | ||||
Class clazz; | |||||
clazz = Class.forName("java.lang.Object",false,loader); | |||||
clazz = Class.forName("junit.framework.TestCase",false,loader); | |||||
} | |||||
catch (Exception ex) { | |||||
Class.forName("java.lang.Object", false, loader); | |||||
Class.forName("junit.framework.TestCase", false, loader); | |||||
} catch (Exception ex) { | |||||
ex.printStackTrace(); | ex.printStackTrace(); | ||||
fail(ex.toString()); | fail(ex.toString()); | ||||
} | |||||
finally { | |||||
} finally { | |||||
Thread.currentThread().setContextClassLoader(contextLoader); | Thread.currentThread().setContextClassLoader(contextLoader); | ||||
} | } | ||||
} | } | ||||
private class JRockitClassLoader extends ClassLoader { | private class JRockitClassLoader extends ClassLoader { | ||||
public final static boolean debug = false; | public final static boolean debug = false; | ||||
private List path = new LinkedList(); | private List path = new LinkedList(); | ||||
// private com.bea.jvm.ClassPreProcessor agent; | |||||
// private com.bea.jvm.ClassPreProcessor agent; | |||||
private Object agent; | private Object agent; | ||||
private Method preProcess; | private Method preProcess; | ||||
public JRockitClassLoader (URLClassLoader clone) throws Exception { | |||||
public JRockitClassLoader(URLClassLoader clone) throws Exception { | |||||
/* Use extensions loader */ | /* Use extensions loader */ | ||||
super(clone.getParent()); | super(clone.getParent()); | ||||
for (int i = 0; i < urls.length; i++) { | for (int i = 0; i < urls.length; i++) { | ||||
Object pathElement; | Object pathElement; | ||||
URL url = urls[i]; | URL url = urls[i]; | ||||
if (debug) System.out.println("JRockitClassLoader.JRockitClassLoader() url=" + url.getPath()); | |||||
if (debug) | |||||
System.out.println("JRockitClassLoader.JRockitClassLoader() url=" + url.getPath()); | |||||
File file = new File(encode(url.getFile())); | File file = new File(encode(url.getFile())); | ||||
if (debug) System.out.println("JRockitClassLoader.JRockitClassLoader() file" + file); | |||||
if (file.isDirectory()) pathElement = file; | |||||
else if (file.exists() && file.getName().endsWith(".jar")) pathElement = new JarFile(file); | |||||
else throw new RuntimeException(file.getAbsolutePath().toString()); | |||||
if (debug) | |||||
System.out.println("JRockitClassLoader.JRockitClassLoader() file" + file); | |||||
if (file.isDirectory()) | |||||
pathElement = file; | |||||
else if (file.exists() && file.getName().endsWith(".jar")) | |||||
pathElement = new JarFile(file); | |||||
else | |||||
throw new RuntimeException(file.getAbsolutePath().toString()); | |||||
path.add(pathElement); | path.add(pathElement); | ||||
} | } | ||||
Class agentClazz = Class.forName("org.aspectj.weaver.loadtime.JRockitAgent",false,this); | |||||
Class agentClazz = Class.forName("org.aspectj.weaver.loadtime.JRockitAgent", false, this); | |||||
Object obj = agentClazz.newInstance(); | Object obj = agentClazz.newInstance(); | ||||
if (debug) System.out.println("JRockitClassLoader.JRockitClassLoader() obj=" + obj); | |||||
if (debug) | |||||
System.out.println("JRockitClassLoader.JRockitClassLoader() obj=" + obj); | |||||
this.agent = obj; | this.agent = obj; | ||||
byte[] bytes = new byte[] {}; | byte[] bytes = new byte[] {}; | ||||
Class[] parameterTypes = new Class[] { java.lang.ClassLoader.class, java.lang.String.class, bytes.getClass() }; | |||||
preProcess = agentClazz.getMethod("preProcess",parameterTypes); | |||||
Class[] parameterTypes = new Class[] { java.lang.ClassLoader.class, java.lang.String.class, bytes.getClass() }; | |||||
preProcess = agentClazz.getMethod("preProcess", parameterTypes); | |||||
} | } | ||||
/* Get rid of escaped characters */ | /* Get rid of escaped characters */ | ||||
private String encode (String s) { | |||||
private String encode(String s) { | |||||
StringBuffer result = new StringBuffer(); | StringBuffer result = new StringBuffer(); | ||||
int i = s.indexOf("%"); | int i = s.indexOf("%"); | ||||
while (i != -1) { | while (i != -1) { | ||||
result.append(s.substring(0,i)); | |||||
String escaped = s.substring(i+1,i+3); | |||||
s = s.substring(i+3); | |||||
Integer value = Integer.valueOf(escaped,16); | |||||
result.append(new Character((char)value.intValue())); | |||||
result.append(s.substring(0, i)); | |||||
String escaped = s.substring(i + 1, i + 3); | |||||
s = s.substring(i + 3); | |||||
Integer value = Integer.valueOf(escaped, 16); | |||||
result.append(new Character((char) value.intValue())); | |||||
i = s.indexOf("%"); | i = s.indexOf("%"); | ||||
} | } | ||||
result.append(s); | result.append(s); | ||||
return result.toString(); | return result.toString(); | ||||
} | } | ||||
protected Class findClass(String name) throws ClassNotFoundException { | protected Class findClass(String name) throws ClassNotFoundException { | ||||
if (debug) System.out.println("> JRockitClassLoader.findClass() name=" + name); | |||||
if (debug) | |||||
System.out.println("> JRockitClassLoader.findClass() name=" + name); | |||||
Class clazz = null; | Class clazz = null; | ||||
try { | try { | ||||
clazz = super.findClass(name); | clazz = super.findClass(name); | ||||
} | |||||
catch (ClassNotFoundException ex) { | |||||
for (Iterator i = path.iterator(); clazz == null && i.hasNext();) { | |||||
} catch (ClassNotFoundException ex) { | |||||
for (Iterator i = path.iterator(); clazz == null && i.hasNext();) { | |||||
byte[] classBytes = null; | byte[] classBytes = null; | ||||
try { | try { | ||||
Object pathElement = i.next(); | Object pathElement = i.next(); | ||||
if (pathElement instanceof File) { | if (pathElement instanceof File) { | ||||
File dir = (File)pathElement; | |||||
String className = name.replace('.','/') + ".class"; | |||||
File classFile = new File(dir,className); | |||||
if (classFile.exists()) classBytes = loadClassFromFile(name,classFile); | |||||
} | |||||
else { | |||||
JarFile jar = (JarFile)pathElement; | |||||
String className = name.replace('.','/') + ".class"; | |||||
File dir = (File) pathElement; | |||||
String className = name.replace('.', '/') + ".class"; | |||||
File classFile = new File(dir, className); | |||||
if (classFile.exists()) | |||||
classBytes = loadClassFromFile(name, classFile); | |||||
} else { | |||||
JarFile jar = (JarFile) pathElement; | |||||
String className = name.replace('.', '/') + ".class"; | |||||
ZipEntry entry = jar.getEntry(className); | ZipEntry entry = jar.getEntry(className); | ||||
if (entry != null) classBytes = loadBytesFromZipEntry(jar,entry); | |||||
if (entry != null) | |||||
classBytes = loadBytesFromZipEntry(jar, entry); | |||||
} | } | ||||
if (classBytes != null) { | if (classBytes != null) { | ||||
clazz = defineClass(name,classBytes); | |||||
clazz = defineClass(name, classBytes); | |||||
} | } | ||||
} | |||||
catch (IOException ioException) { | |||||
} catch (IOException ioException) { | |||||
ex.printStackTrace(); | ex.printStackTrace(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (debug) System.out.println("< JRockitClassLoader.findClass() name=" + name); | |||||
if (debug) | |||||
System.out.println("< JRockitClassLoader.findClass() name=" + name); | |||||
return clazz; | return clazz; | ||||
} | } | ||||
private Class defineClass (String name, byte[] bytes) { | |||||
if (debug) System.out.println("> JRockitClassLoader.defineClass() name=" + name); | |||||
private Class defineClass(String name, byte[] bytes) { | |||||
if (debug) | |||||
System.out.println("> JRockitClassLoader.defineClass() name=" + name); | |||||
try { | try { | ||||
if (agent != null) preProcess.invoke(agent,new Object[] { this, name, bytes }); | |||||
} | |||||
catch (IllegalAccessException iae) { | |||||
if (agent != null) | |||||
preProcess.invoke(agent, new Object[] { this, name, bytes }); | |||||
} catch (IllegalAccessException iae) { | |||||
iae.printStackTrace(); | iae.printStackTrace(); | ||||
throw new ClassFormatError(iae.getMessage()); | throw new ClassFormatError(iae.getMessage()); | ||||
} | |||||
catch (InvocationTargetException ite) { | |||||
} catch (InvocationTargetException ite) { | |||||
ite.printStackTrace(); | ite.printStackTrace(); | ||||
throw new ClassFormatError(ite.getTargetException().getMessage()); | throw new ClassFormatError(ite.getTargetException().getMessage()); | ||||
} | } | ||||
if (debug) System.out.println("< JRockitClassLoader.defineClass() name=" + name); | |||||
return super.defineClass(name,bytes,0,bytes.length); | |||||
if (debug) | |||||
System.out.println("< JRockitClassLoader.defineClass() name=" + name); | |||||
return super.defineClass(name, bytes, 0, bytes.length); | |||||
} | } | ||||
private byte[] loadClassFromFile (String name, File file) throws IOException { | |||||
if (debug) System.out.println("JRockitClassLoader.loadClassFromFile() file=" + file); | |||||
private byte[] loadClassFromFile(String name, File file) throws IOException { | |||||
if (debug) | |||||
System.out.println("JRockitClassLoader.loadClassFromFile() file=" + file); | |||||
byte[] bytes; | byte[] bytes; | ||||
bytes = new byte[(int)file.length()]; | |||||
bytes = new byte[(int) file.length()]; | |||||
FileInputStream fis = null; | FileInputStream fis = null; | ||||
try { | try { | ||||
fis = new FileInputStream(file); | fis = new FileInputStream(file); | ||||
bytes = readBytes(fis,bytes); | |||||
bytes = readBytes(fis, bytes); | |||||
} finally { | |||||
if (fis != null) | |||||
fis.close(); | |||||
} | } | ||||
finally { | |||||
if (fis != null) fis.close(); | |||||
} | |||||
return bytes; | return bytes; | ||||
} | } | ||||
private byte[] loadBytesFromZipEntry (JarFile jar, ZipEntry entry) throws IOException { | |||||
if (debug) System.out.println("JRockitClassLoader.loadBytesFromZipEntry() entry=" + entry); | |||||
private byte[] loadBytesFromZipEntry(JarFile jar, ZipEntry entry) throws IOException { | |||||
if (debug) | |||||
System.out.println("JRockitClassLoader.loadBytesFromZipEntry() entry=" + entry); | |||||
byte[] bytes; | byte[] bytes; | ||||
bytes = new byte[(int)entry.getSize()]; | |||||
bytes = new byte[(int) entry.getSize()]; | |||||
InputStream is = null; | InputStream is = null; | ||||
try { | try { | ||||
is = jar.getInputStream(entry); | is = jar.getInputStream(entry); | ||||
bytes = readBytes(is,bytes); | |||||
bytes = readBytes(is, bytes); | |||||
} finally { | |||||
if (is != null) | |||||
is.close(); | |||||
} | } | ||||
finally { | |||||
if (is != null) is.close(); | |||||
} | |||||
return bytes; | return bytes; | ||||
} | } | ||||
private byte[] readBytes (InputStream is, byte[] bytes) throws IOException { | |||||
private byte[] readBytes(InputStream is, byte[] bytes) throws IOException { | |||||
for (int offset = 0; offset < bytes.length;) { | for (int offset = 0; offset < bytes.length;) { | ||||
int read = is.read(bytes,offset,bytes.length - offset); | |||||
int read = is.read(bytes, offset, bytes.length - offset); | |||||
offset += read; | offset += read; | ||||
} | } | ||||
return bytes; | return bytes; |