@@ -26,269 +26,277 @@ import org.aspectj.weaver.tools.TraceFactory; | |||
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> | |||
*/ | |||
public class Aj implements ClassPreProcessor { | |||
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); | |||
public Aj(){ | |||
public Aj() { | |||
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; | |||
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(); | |||
} | |||
} |
@@ -11,6 +11,15 @@ | |||
*******************************************************************************/ | |||
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.classfile.JavaClass; | |||
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; | |||
@@ -37,365 +46,320 @@ import org.aspectj.weaver.loadtime.definition.Definition; | |||
import org.aspectj.weaver.patterns.PerClause; | |||
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> | |||
*/ | |||
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(); | |||
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(); | |||
} | |||
// 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]; | |||
String key = member.getName()+member.getSignature(); | |||
String key = member.getName() + member.getSignature(); | |||
if (member.isAbstract()) { | |||
collector.put(key,member); | |||
collector.put(key, member); | |||
} else { | |||
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;")) { | |||
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)); | |||
} | |||
} |
@@ -11,171 +11,158 @@ | |||
*******************************************************************************/ | |||
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.IMessageHandler; | |||
import org.aspectj.bridge.Message; | |||
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> | |||
*/ | |||
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 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; | |||
} | |||
} | |||
} |
@@ -28,93 +28,92 @@ import org.xml.sax.helpers.XMLReaderFactory; | |||
/** | |||
* FIXME AV - doc, concrete aspect | |||
* | |||
* | |||
* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> | |||
*/ | |||
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 { | |||
XMLReader xmlReader = null; | |||
@@ -123,7 +122,7 @@ public class DocumentParser extends DefaultHandler { | |||
try { | |||
xmlReader = XMLReaderFactory.createXMLReader(); | |||
} | |||
/* .. and ignore "System property ... not set" and then try this instead */ | |||
catch (SAXException ex) { | |||
xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); | |||
@@ -131,145 +130,135 @@ public class DocumentParser extends DefaultHandler { | |||
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) { | |||
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")); | |||
} | |||
} |
@@ -18,13 +18,15 @@ import junit.framework.TestCase; | |||
public class AjTest extends TestCase { | |||
public void testAj() { | |||
Aj aj = new Aj(); | |||
// Aj aj = | |||
new Aj(); | |||
} | |||
public void testAjIWeavingContext() { | |||
ClassLoader loader = new URLClassLoader(new URL[] {}, null); | |||
IWeavingContext weavingContext = new DefaultWeavingContext(loader); | |||
Aj aj = new Aj(weavingContext); | |||
// Aj aj = | |||
new Aj(weavingContext); | |||
} | |||
public void testInitialize() { | |||
@@ -42,14 +44,14 @@ public class AjTest extends TestCase { | |||
ClassLoader loader = new URLClassLoader(new URL[] {}, null); | |||
Aj aj = new Aj(); | |||
String namespace = aj.getNamespace(loader); | |||
assertEquals("Namespace should be empty","",namespace); | |||
assertEquals("Namespace should be empty", "", namespace); | |||
} | |||
public void testGeneratedClassesExist() { | |||
ClassLoader loader = new URLClassLoader(new URL[] {}, null); | |||
Aj aj = new Aj(); | |||
boolean exist = aj.generatedClassesExist(loader); | |||
assertFalse("There should be no generated classes",exist); | |||
assertFalse("There should be no generated classes", exist); | |||
} | |||
public void testFlushGeneratedClasses() { | |||
@@ -57,7 +59,7 @@ public class AjTest extends TestCase { | |||
Aj aj = new Aj(); | |||
aj.flushGeneratedClasses(loader); | |||
boolean exist = aj.generatedClassesExist(loader); | |||
assertFalse("There should be no generated classes",exist); | |||
assertFalse("There should be no generated classes", exist); | |||
} | |||
} |
@@ -24,11 +24,11 @@ import java.util.List; | |||
import java.util.jar.JarFile; | |||
import java.util.zip.ZipEntry; | |||
import junit.framework.TestCase; | |||
import com.bea.jvm.ClassPreProcessor; | |||
import com.bea.jvm.JVMFactory; | |||
import junit.framework.TestCase; | |||
public class JRockitAgentTest extends TestCase { | |||
protected void setUp() throws Exception { | |||
@@ -42,16 +42,16 @@ public class JRockitAgentTest extends TestCase { | |||
public void testJRockitAgent() { | |||
ClassPreProcessor preProcessor = new JRockitAgent(); | |||
ClassPreProcessor expectedPreProcessor = JVMFactory.getJVM().getClassLibrary().getClassPreProcessor(); | |||
assertEquals("JRocketAgent must be registered",expectedPreProcessor,preProcessor); | |||
assertEquals("JRocketAgent must be registered", expectedPreProcessor, preProcessor); | |||
} | |||
public void testPreProcess() { | |||
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(); | |||
try { | |||
@@ -60,29 +60,26 @@ public class JRockitAgentTest extends TestCase { | |||
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(); | |||
fail(ex.toString()); | |||
} | |||
finally { | |||
} finally { | |||
Thread.currentThread().setContextClassLoader(contextLoader); | |||
} | |||
} | |||
private class JRockitClassLoader extends ClassLoader { | |||
public final static boolean debug = false; | |||
private List path = new LinkedList(); | |||
// private com.bea.jvm.ClassPreProcessor agent; | |||
// private com.bea.jvm.ClassPreProcessor agent; | |||
private Object agent; | |||
private Method preProcess; | |||
public JRockitClassLoader (URLClassLoader clone) throws Exception { | |||
public JRockitClassLoader(URLClassLoader clone) throws Exception { | |||
/* Use extensions loader */ | |||
super(clone.getParent()); | |||
@@ -90,132 +87,142 @@ public class JRockitAgentTest extends TestCase { | |||
for (int i = 0; i < urls.length; i++) { | |||
Object pathElement; | |||
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())); | |||
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); | |||
} | |||
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(); | |||
if (debug) System.out.println("JRockitClassLoader.JRockitClassLoader() obj=" + obj); | |||
if (debug) | |||
System.out.println("JRockitClassLoader.JRockitClassLoader() obj=" + obj); | |||
this.agent = obj; | |||
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 */ | |||
private String encode (String s) { | |||
private String encode(String s) { | |||
StringBuffer result = new StringBuffer(); | |||
int i = s.indexOf("%"); | |||
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("%"); | |||
} | |||
result.append(s); | |||
return result.toString(); | |||
} | |||
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; | |||
try { | |||
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; | |||
try { | |||
Object pathElement = i.next(); | |||
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); | |||
if (entry != null) classBytes = loadBytesFromZipEntry(jar,entry); | |||
if (entry != null) | |||
classBytes = loadBytesFromZipEntry(jar, entry); | |||
} | |||
if (classBytes != null) { | |||
clazz = defineClass(name,classBytes); | |||
clazz = defineClass(name, classBytes); | |||
} | |||
} | |||
catch (IOException ioException) { | |||
} catch (IOException ioException) { | |||
ex.printStackTrace(); | |||
} | |||
} | |||
} | |||
if (debug) System.out.println("< JRockitClassLoader.findClass() name=" + name); | |||
if (debug) | |||
System.out.println("< JRockitClassLoader.findClass() name=" + name); | |||
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 { | |||
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(); | |||
throw new ClassFormatError(iae.getMessage()); | |||
} | |||
catch (InvocationTargetException ite) { | |||
} catch (InvocationTargetException ite) { | |||
ite.printStackTrace(); | |||
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; | |||
bytes = new byte[(int)file.length()]; | |||
bytes = new byte[(int) file.length()]; | |||
FileInputStream fis = null; | |||
try { | |||
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; | |||
} | |||
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; | |||
bytes = new byte[(int)entry.getSize()]; | |||
bytes = new byte[(int) entry.getSize()]; | |||
InputStream is = null; | |||
try { | |||
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; | |||
} | |||
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;) { | |||
int read = is.read(bytes,offset,bytes.length - offset); | |||
int read = is.read(bytes, offset, bytes.length - offset); | |||
offset += read; | |||
} | |||
return bytes; |