* @author colyer
* Annotation for declare error...
*
- * usage:
- * @DeclareError("somePcut()")
- * private static final String "a message";
+ * usage: @DeclareError("somePcut()")
+ * private static final String "a message";
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DeclareError {
+ /**
+ * The pointcut expression where to bind the error (don't use if, formal bindings, cflow etc)
+ */
String value();
}
* @author colyer
* Annotation for declare warning...
*
- * usage:
- * @DeclareWarning("somePcut()")
- * private static final String "a message";
+ * usage: @DeclareWarning("somePcut()")
+ * private static final String "a message";
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DeclareWarning {
+ /**
+ * The pointcut expression where to bind the error (don't use if, formal bindings, cflow etc)
+ */
String value();
}
public aspect Foo {}
]]></programlisting>
- <para>Privileged aspects are declared as:</para>
-
+ <para>Privileged aspects are not supported by the annotation style</para>
+ <!--
<programlisting><![CDATA[
@Aspect(isPrivileged=true)
public class Foo {}
public privileged aspect Foo {}
]]></programlisting>
-
+ -->
<para>To specify an aspect an aspect instantiation model (the default is
- singleton), use the <literal>instantionModel</literal> and
- <literal>perClausePattern</literal> attributes. For example:</para>
+ singleton), provide the perclause as the <literal>@Aspect</literal> value.
+ For example:</para>
<programlisting><![CDATA[
- @Aspect(instantiationModel=AspectInstantiationModel.PERTHIS,
- perClausePattern="execution(* abc..*(..))")
+ @Aspect("perthis(execution(* abc..*(..)))")
public class Foo {}
is equivalent to...
public aspect Foo perthis(execution(* abc..*(..))) {}
]]></programlisting>
-
- <para>The full definitions of the Aspect annotation type and the
- AspectInstantiationModel enumerated type are:</para>
-
- <programlisting><![CDATA[
- /**
- * Use to indicate that a class should be treated as an aspect by
- * AspectJ's weaver.
- */
- @Target({ElementType.TYPE})
- public @interface Aspect {
- AspectInstantiationModel instantiationModel() default AspectInstantiationModel.SINGLETON;
- String perClausePattern() default "";
- boolean isPrivileged() default false;
- }
-
- /**
- * The different aspect instantiation models supported by AspectJ
- */
- public enum AspectInstantiationModel {
- SINGLETON,
- PERTHIS,
- PERTARGET,
- PERCFLOW,
- PERCFLOWBELOW,
- PERTYPEWITHIN
- }
- ]]></programlisting>
</sect1>
<literal>thisJoinPointStaticPart</literal>,
<literal>thisEnclosingJoinPointStaticPart</literal> then these need to
be declared as additional method parameters when using the annotation
- style. In AspectJ 1.5.0 we require that these parameters be declared
+ style. <!-- TODO AV - not any more -- In AspectJ 1.5.0 we require that these parameters be declared
first in the parameter list, in later releases we may relax this
- requirement.</para>
+ requirement.--></para>
<programlisting><![CDATA[
@AdviceName("callFromFoo")
<programlisting><![CDATA[
public interface ProceedingJoinPoint extends JoinPoint {
- public Object proceed(Object... args);
+ public Object proceed(Object[] args);
}
]]></programlisting>
<programlisting><![CDATA[
public aspect ProceedAspect {
pointcut setAge(int i): call(* setAge(..)) && args(i);
-
+
Object around(int i): setAge(i) {
return proceed(i*2);
}
}
-
+
can be written as...
-
+
@Aspect
public class ProceedAspect {
-
+
@Pointcut("call(* setAge(..)) && args(i)")
void setAge(int i) {}
-
+
@Around("setAge(i)")
public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint, int i) {
- return thisJoinPoint.proceed(i*2);
+ return thisJoinPoint.proceed(new Object[]{i*2}); //using Java 5 autoboxing
}
-
- }
+
+ }
+
+ Note that the ProceedingJoinPoint does not need to be passed as the proceed(..) arguments.
]]></programlisting>
</sect2>
(This is the same behaviour as when using declare warning or error with the
code style). Declare warning and error declarations are made by annotating
a string constant whose value is the message to be issued.</para>
-
+
+ <para>Note that the String must be a constant and not the result of the invocation
+ of a static method for example.</para>
+
<programlisting><![CDATA[
declare warning : call(* javax.sql..*(..)) && !within(org.xyz.daos..*)
: "Only DAOs should be calling JDBC.";
-
+
declare error : execution(* IFoo+.*(..)) && !within(org.foo..*)
: "Only foo types can implement IFoo";
-
+
can be written as...
-
+
@DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
static final String aMessage = "Only DAOs should be calling JDBC.";
-
+
@DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
static final String badIFooImplementors = "Only foo types can implement IFoo";
-
+
+ // the following is not valid since the message is not a String constant
+ @DeclareError("execution(* IFoo+.*(..)) && !within(org.foo..*)")
+ static final String badIFooImplementorsCorrupted = getMessage();
+ static String getMessage() {
+ return "Only foo types can implement IFoo " + System.currentTimeMillis();
+ }
+
]]></programlisting>
public static boolean hasAspect(Object anAspect, Class forType) {...}
}
]]></programlisting>
-
+
+ <!-- TODO AV - stuff below is not done -->
+ <!--
<para>When the AspectJ weaver sees calls to these methods, it will convert
them into the most efficient form possible (to get performance equivalent
- to a direct <literal>MyAspect.aspectOf()</literal> call).</para>
+ to a direct <literal>MyAspect.aspectOf()</literal> call).</para>
+ -->
</sect1>
</chapter>
need to specify the <literal>-Xreweavable</literal> compiler option when building
them. This causes AspectJ to save additional state in the class files that is used
to support subsequent reweaving. </para>
+ <para><!-- FIXME AV -->As per AspectJ 1.5.0 M3 aspects (code style or annotation style) are
+ reweavable by default, and weaved classes may be as well in 1.5.0 final.</para>
</sect2>
</sect1>
<varlistentry>
<term>Command line</term>
<listitem>
+ <!-- FIXME AV - wondering what is the status of this one as per aop.xml etc.. -->
<para> AspectJ includes a script "aj" that allows programs executed at
the command line to take advantage of load-time weaving.
The script is customized when AspectJ is installed depending on the chosen
found on the search path (regular <literal>getResourceAsStream</literal> lookup)
according to the following rules: </para>
<itemizedlist>
+ <!-- FIXME AV - looks like we can refine conf in a child CL - not good -->
<listitem> The set of available aspects is the set of all
declared and defined aspects (<literal>aspect</literal> and
<literal>concrete-aspect</literal> elements of the <literal>aspects</literal>
<entry>Performance optimization for aspects making use
of thisJoinPoint (non-static parts)</entry>
</row>
+ <row>
+ <entry>
+ <literal>-Xlint, -Xlint:ignore, ...</literal>
+ </entry>
+ <entry>Configure lint messages</entry><!--FIXME AV - default to blabla, see link X -->
+ </row>
<row>
<entry>
<literal>-nowarn, -warn:none</literal>
</row>
<row>
<entry>
- <literal>-Xnoinline</literal>
+ <literal>-XnoInline</literal>
</entry>
<entry>Don't inline around advice.</entry>
</row>
</row>
<row>
<entry>
- <literal>-XmessageHolderClass</literal>
+ <literal>-XmessageHolderClass:...</literal>
</entry>
- <entry>Provide alternative output destination to stderr for all weaver messages</entry>
+ <entry>Provide alternative output destination to stdout/stderr for all weaver messages.
+ The given value must be the full qualified class name of a class that implements
+ <literal>org.aspectj.weaver.loadtime</literal>
+ and that is visible from where the <literal>aop.xml</literal> is packed.
+ If more than one such options are used,
+ the first occurence only is taken into account.</entry>
</row>
</tbody>
</tgroup>
<sect2>
<title>JVMTI</title>
<para> When using JDK 1.5 the JVMTI agent can be used by starting the JVM with the
- following option: </para>
+ following option (adapt according to the path to aspectjweaver.jar): </para>
<programlisting><![CDATA[
-javaagent=aspectjweaver.jar
]]></programlisting>
<!-- ajc-ant script, not to be used from Ant commant line - see AntSpec -->
<project name="foo" default="javac.ltw">
+ <!-- using this we can debug the forked VM -->
<property
name="jdwp"
- value=""/>
+ value="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"/>
<target name="compile:javac">
<!-- compile only javac compilable stuff -->
<classpath refid="aj.path"/>
<jvmarg value="-javaagent:${aj.root}/lib/test/loadtime5.jar"/>
<jvmarg value="-Daj5.def=ataspectj/aop-aroundinlinemungertest.xml"/>
+ <!--<jvmarg line="${jdwp}"/>--><!-- uncomment to debug with JDWP -->
</java>
</target>
@Aspect
public class Test014{
- @Pointcut("call%dddd\n\n\n\n\n\n\n\n\n\n\n%dwdwudwdwbuill817pe;][{\ngrgrgnjk78877&&<:{{{+=``\"")
- void somecall(){
- }
+ @Pointcut("call%dddd\n\n\n\n\n\n\n\n\n\n\n%dwdwudwdwbuill817pe;][{\ngrgrgnjk78877&&<:{{{+=``\"")
+ public void somecall(){
+ }
+
+ @Before("fhfh()")
+ public void beforeA() {}
+
+ @After("fhfh()")
+ public void afterA() {}
+
+ @Around("fhfh()")
+ public Object aroundA() {return null;}
+
+ @AfterThrowing(value = "fhfh()", pointcut = "wups()")
+ public void afterAT2() {}
+
+ @AfterThrowing("fhfh()")
+ public void afterAT() {}
+
+ @AfterReturning(value = "fhfh()", pointcut = "wups()")
+ public void afterAR2() {}
+
+ @AfterReturning("fhfh()")
+ public void afterAR() {}
+
+ @DeclareError("execution(* Foo.bar())")
+ private int X;
+
+ @DeclareWarning("execution(* Foo.bar())")
+ private final static String X2 = getX2();
+
+ static String getX2() {return "not supported";}
}
suite.addTest(GenericsTests.suite());
suite.addTest(AtAjSyntaxTests.suite());
suite.addTest(AtAjMisuseTests.suite());
- //FIXME AV - #75442
- //suite.addTest(AtAjLTWTests.suite());
+ suite.addTest(AtAjLTWTests.suite());
//$JUnit-END$
return suite;
}
<comment>line is enclosing class - TBD</comment>
<ajc-test dir="java5/ataspectj"
pr="" title="@Pointcut with garbage string">
- <compile files="ataspectj/misuse/Test014.java" options="-1.5 -Xdev:NoAtAspectJProcessing">
+ <compile files="ataspectj/misuse/Test014.java" options="-1.5 -Xdev:NoAtAspectJProcessing -Xlint:ignore">
<message kind="error" line="7" text="Cannot parse @Pointcut 'call%dddd"/>
+ <message kind="error" text="can't find referenced pointcut"/>
+ <message kind="error" text="can't find pointcut"/>
+ <message kind="error" text="@AfterThrowing: either 'value' or 'poincut' must be provided, not both"/>
+ <message kind="error" text="@AfterReturning: either 'value' or 'poincut' must be provided, not both"/>
+ <message kind="error" text="@DeclareWarning used on a non String constant field"/>
+ <message kind="error" text="@DeclareError used on a non String constant field"/>
</compile>
</ajc-test>
import org.aspectj.weaver.patterns.Pointcut;
import org.aspectj.weaver.patterns.SimpleScope;
import org.aspectj.weaver.patterns.ParserException;
+import org.aspectj.weaver.patterns.Declare;
+import org.aspectj.weaver.patterns.DeclareErrorOrWarning;
/**
* Annotation defined aspect reader.
}
}
+ /**
+ * A struct when we read @AJ on field
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+ private static class AjAttributeFieldStruct extends AjAttributeStruct {
+
+ final Field field;
+
+ public AjAttributeFieldStruct(Field field, ResolvedTypeX type, ISourceContext sourceContext, IMessageHandler messageHandler) {
+ super(type, sourceContext, messageHandler);
+ this.field = field;
+ }
+ }
+
/**
* Annotations are RuntimeVisible only. This allow us to not visit RuntimeInvisible ones.
*
// in order to be able to resolve the pointcut references later on
//FIXME alex loop over class super class
//FIXME alex can that be too slow ?
- for (int m = 0; m < javaClass.getMethods().length; m++) {
- Method method = javaClass.getMethods()[m];
+ for (int i = 0; i < javaClass.getMethods().length; i++) {
+ Method method = javaClass.getMethods()[i];
if (method.getName().startsWith(NameMangler.PREFIX)) continue; // already dealt with by ajc...
//FIXME alex optimize, this method struct will gets recreated for advice extraction
AjAttributeMethodStruct mstruct = new AjAttributeMethodStruct(method, type, context, msgHandler);
Attribute[] mattributes = method.getAttributes();
- for (int i = 0; i < mattributes.length; i++) {
- Attribute mattribute = mattributes[i];
+ for (int j = 0; j < mattributes.length; j++) {
+ Attribute mattribute = mattributes[j];
if (acceptAttribute(mattribute)) {
RuntimeAnnotations mrvs = (RuntimeAnnotations) mattribute;
handlePointcutAnnotation(mrvs, mstruct);
}
struct.ajAttributes.addAll(mstruct.ajAttributes);
}
+
+
+ // code style declare error / warning are class attributes
+ for (int i = 0; i < javaClass.getFields().length; i++) {
+ Field field = javaClass.getFields()[i];
+ if (field.getName().startsWith(NameMangler.PREFIX)) continue; // already dealt with by ajc...
+ //FIXME alex optimize, this method struct will gets recreated for advice extraction
+ AjAttributeFieldStruct fstruct = new AjAttributeFieldStruct(field, type, context, msgHandler);
+ Attribute[] fattributes = field.getAttributes();
+
+ for (int j = 0; j < fattributes.length; j++) {
+ Attribute fattribute = fattributes[j];
+ if (acceptAttribute(fattribute)) {
+ RuntimeAnnotations frvs = (RuntimeAnnotations) fattribute;
+ if (handleDeclareErrorOrWarningAnnotation(frvs, fstruct)) {
+ // semantic check - must be in an @Aspect [remove if previous block bypassed in advance]
+ if (!type.isAnnotationStyleAspect()) {
+ msgHandler.handleMessage(
+ new Message(
+ "Found @AspectJ annotations in a non @Aspect type '" + type.getName() + "'",
+ IMessage.WARNING,
+ null,
+ type.getSourceLocation()
+ )
+ );
+ ;// go ahead
+ }
+ }
+ // there can only be one RuntimeVisible bytecode attribute
+ break;
+ }
+ }
+ struct.ajAttributes.addAll(fstruct.ajAttributes);
+ }
return struct.ajAttributes;
}
* @return list of AjAttributes, always empty for now
*/
public static List readAj5FieldAttributes(Field field, ResolvedTypeX type, ISourceContext context,IMessageHandler msgHandler) {
- if (field.getName().startsWith(NameMangler.PREFIX)) return Collections.EMPTY_LIST; // already dealt with by ajc...
- return EMPTY_LIST;
+ return Collections.EMPTY_LIST;
}
/**
String pointcut = null;
String returned = null;
if ((annValue!=null && annPointcut!=null) || (annValue==null && annPointcut==null)) {
- throw new RuntimeException("AfterReturning at most value or pointcut must be filled");
+ struct.handler.handleMessage(
+ new Message(
+ "@AfterReturning: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+ struct.enclosingType.getSourceLocation(),
+ true
+ )
+ );
+ return false;
}
if (annValue != null) {
pointcut = annValue.getValue().stringifyValue();
pointcut = annPointcut.getValue().stringifyValue();
}
if (isNullOrEmpty(pointcut)) {
- throw new RuntimeException("AfterReturning pointcut unspecified");
+ struct.handler.handleMessage(
+ new Message(
+ "@AfterReturning: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+ struct.enclosingType.getSourceLocation(),
+ true
+ )
+ );
+ return false;
}
if (annReturned!=null) {
returned = annReturned.getValue().stringifyValue();
String pointcut = null;
String throwned = null;
if ((annValue!=null && annPointcut!=null) || (annValue==null && annPointcut==null)) {
- throw new RuntimeException("AfterReturning at most value or pointcut must be filled");
+ struct.handler.handleMessage(
+ new Message(
+ "@AfterThrowing: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+ struct.enclosingType.getSourceLocation(),
+ true
+ )
+ );
+ return false;
}
if (annValue != null) {
pointcut = annValue.getValue().stringifyValue();
pointcut = annPointcut.getValue().stringifyValue();
}
if (isNullOrEmpty(pointcut)) {
- throw new RuntimeException("AfterReturning pointcut unspecified");
+ struct.handler.handleMessage(
+ new Message(
+ "@AfterThrowing: either 'value' or 'poincut' must be provided, not both: " + methodToString(struct.method),
+ struct.enclosingType.getSourceLocation(),
+ true
+ )
+ );
+ return false;
}
if (annThrowned!=null) {
throwned = annThrowned.getValue().stringifyValue();
}
}
+ /**
+ * Read @DeclareError, @DeclareWarning
+ *
+ * @param runtimeAnnotations
+ * @param struct
+ * @return true if found
+ */
+ private static boolean handleDeclareErrorOrWarningAnnotation(RuntimeAnnotations runtimeAnnotations, AjAttributeFieldStruct struct) {
+ Annotation error = getAnnotation(runtimeAnnotations, "org.aspectj.lang.annotation.DeclareError");
+ boolean hasError = false;
+ if (error != null) {
+ ElementNameValuePair declareError = getAnnotationElement(error, "value");
+ if (declareError != null) {
+ if (!"Ljava/lang/String;".equals(struct.field.getSignature()) || struct.field.getConstantValue()==null) {
+ struct.handler.handleMessage(
+ new Message(
+ "@DeclareError used on a non String constant field " + fieldToString(struct.field),
+ struct.enclosingType.getSourceLocation(),
+ true
+ ));
+ return false;
+ }
+ FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
+ IScope binding = new BindingScope(
+ struct.enclosingType,
+ bindings
+ );
+ Pointcut pc = Pointcut.fromString(declareError.getValue().stringifyValue()).resolve(binding);
+ struct.ajAttributes.add(new AjAttribute.DeclareAttribute(
+ new DeclareErrorOrWarning(true, pc, struct.field.getConstantValue().toString())
+ ));
+ return hasError = true;
+ }
+ }
+ Annotation warning = getAnnotation(runtimeAnnotations, "org.aspectj.lang.annotation.DeclareWarning");
+ boolean hasWarning = false;
+ if (warning != null) {
+ ElementNameValuePair declareWarning = getAnnotationElement(warning, "value");
+ if (declareWarning != null) {
+ if (!"Ljava/lang/String;".equals(struct.field.getSignature()) || struct.field.getConstantValue()==null) {
+ struct.handler.handleMessage(
+ new Message(
+ "@DeclareWarning used on a non String constant field " + fieldToString(struct.field),
+ struct.enclosingType.getSourceLocation(),
+ true
+ ));
+ return false;
+ }
+ FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
+ IScope binding = new BindingScope(
+ struct.enclosingType,
+ bindings
+ );
+ Pointcut pc = Pointcut.fromString(declareWarning.getValue().stringifyValue()).resolve(binding);
+ struct.ajAttributes.add(new AjAttribute.DeclareAttribute(
+ new DeclareErrorOrWarning(false, pc, struct.field.getConstantValue().toString())
+ ));
+ return hasWarning = true;
+ }
+ }
+ return hasError || hasWarning;
+ }
+
/**
* Returns a readable representation of a method.
* Method.toString() is not suitable.
return sb.toString();
}
+ /**
+ * Returns a readable representation of a field.
+ * Field.toString() is not suitable.
+ *
+ * @param field
+ * @return
+ */
+ private static String fieldToString(Field field) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(field.getName()).append(' ');
+ sb.append(field.getSignature());
+ return sb.toString();
+ }
+
/**
* Build the bindings for a given method (pointcut / advice)
*