Annotated Declare Error/Warning

The principles behind declare error/warning, and the example used here is exactly the same as in the XML example. But the errors/warnings are declared differently using annotations.

Declaring warnings

To declare a warning, you annotate a field with @DeclareWarning. This field must be within an @Aspect or @InterceptorDef annotated class. The type of the field does not actually matter, though Pointcut has been used in this example. DeclareAspect is not actually bound to anything and does no interceptions, its sole purpose in this example is as a vessel for the @DeclareWarning annotations.

   import org.jboss.aop.DeclareWarning;

   @Aspect
   public class DeclareAspect
   {
      @DeclareWarning (expr="class($instanceof{VehicleDAO}) AND !has(public void *->save())", msg="All VehicleDAO subclasses must override the save() method.")
      public static Pointcut warning1;

      @DeclareWarning (expr="call(Driver->new(..)) AND within(*DAO)", msg="DAO classes should not access the Driver class")
      public static Pointcut warning2;

      @DeclareWarning (expr="call(* Driver->*(..)) AND withincode(* *DAO->save())", msg="DAO classes should not access the Driver class")
      public static Pointcut warning3;
   }

Declaring errors

You declare an error in the same way as a warning, but you use the @DeclareError annotation instead. Modify DeclareAspect so that it becomes:

   import org.jboss.aop.DeclareError;
   import org.jboss.aop.Aspect;
   import org.jboss.aop.pointcut.Pointcut;

   @Aspect
   public class DeclareAspect
   {
      @DeclareError (expr="class($instanceof{VehicleDAO}) AND !has(public void *->save())", msg="All VehicleDAO subclasses must override the save() method.")
      public static Pointcut warning1;

      @DeclareError (expr="call(Driver->new(..)) AND within(*DAO)", msg="DAO classes should not access the Driver class")
      public static Pointcut warning2;

      @DeclareError (expr="call(* Driver->*(..)) AND withincode(* *DAO->save())", msg="DAO classes should not access the Driver class")
      public static Pointcut warning3;
   }

Run the example compile-time instrumented

Now if you run the example:

  $ ant run.aopc

It will generate the following output:

_aopc.annotated:
     [aopc] WARNING: declare-warning condition
     [aopc]     'call(Driver->new(..)) AND within(*DAO)'
     [aopc] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [aopc]     DAO classes should not access the Driver class

     [aopc] WARNING: declare-warning condition
     [aopc]     'call(* Driver->*(..)) AND withincode(* *DAO->save())'
     [aopc] was broken for method call:CarDAO.save()V calls Driver.method()V
     [aopc]     DAO classes should not access the Driver class

     [aopc] WARNING: declare-warning condition
     [aopc]     'class($instanceof{VehicleDAO}) AND !has(public void *->save())'
     [aopc] was broken for class MotorbikeDAO
     [aopc]     All VehicleDAO subclasses must override the save() method.

     [aopc] Build Successful: 438 ms

_run.aopc.annotated:
     [java] ---- Start ----
     [java] Car DAO save

Note that when using compile time instrumentation the warnings are generated during the aopc phase.

Run the example load-time instrumented

Run the example:

  $ ant run.loadtime

It will generate the following output

_run.loadtime.annotated:
     [java] ---- Start ----
     [java] WARNING: declare-warning condition
     [java]     'call(Driver->new(..)) AND within(*DAO)'
     [java] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [java]     DAO classes should not access the Driver class

     [java] WARNING: declare-warning condition
     [java]     'call(* Driver->*(..)) AND withincode(* *DAO->save())'
     [java] was broken for method call:CarDAO.save()V calls Driver.method()V
     [java]     DAO classes should not access the Driver class

     [java] Car DAO save
     [java] WARNING: declare-warning condition
     [java]     'class($instanceof{VehicleDAO}) AND !has(public void *->save())'
     [java] was broken for class MotorbikeDAO
     [java]     All VehicleDAO subclasses must override the save() method.

Note that now the warnings are displayed when running the application, as the classes are transformed when loaded.

Declaring errors

If we replace all the @DeclareWarning occurances with @DeclareError in DeclareAspect, an error will get thrown instead
import org.jboss.aop.DeclareError;

@Aspect
public class DeclareAspect
{
   @DeclareError (expr="class($instanceof{VehicleDAO}) AND !has(public void *->save())", msg="All VehicleDAO subclasses must override the save() method.")
   Pointcut warning1;

   @DeclareError (expr="call(Driver->new(..)) AND within(*DAO)", msg="DAO classes should not access the Driver class")
   Pointcut warning2;

   @DeclareError (expr="call(* Driver->*(..)) AND withincode(* *DAO->save())", msg="DAO classes should not access the Driver class")
   Pointcut warning3;

}

declare-error precompiled

When running precompiled we get:
$ ant run.aopc
Buildfile: build.xml

...

_aopc.annotated:
     [aopc] ERROR: declare-error condition
     [aopc]     'call(Driver->new(..)) AND within(*DAO)'
     [aopc] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [aopc]     DAO classes should not access the Driver class

     [aopc] java.lang.RuntimeException: ERROR: declare-error condition
     [aopc]     'call(Driver->new(..)) AND within(*DAO)'
     [aopc] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [aopc]     DAO classes should not access the Driver class

     [aopc]     at org.jboss.aop.instrument.DeclareChecker.checkDeclares(DeclareChecker.java:124)
     [aopc]     at org.jboss.aop.instrument.DeclareChecker.checkDeclares(DeclareChecker.java:57)
     [aopc]     at org.jboss.aop.instrument.CallerTransformer$CallerExprEditor.edit(CallerTransformer.java:472)
     [aopc]     at javassist.expr.ExprEditor.doit(ExprEditor.java:136)
     [aopc]     at javassist.CtBehavior.instrument(CtBehavior.java:362)
     [aopc]     at org.jboss.aop.instrument.CallerTransformer.applyCallerPointcuts(CallerTransformer.java:69)
     [aopc]     at org.jboss.aop.instrument.Instrumentor.applyCallerPointcuts(Instrumentor.java:495)
     [aopc]     at org.jboss.aop.instrument.Instrumentor.transform(Instrumentor.java:562)
     [aopc]     at org.jboss.aop.AspectManager.translate(AspectManager.java:564)
     [aopc]     at org.jboss.aop.AspectManager.transform(AspectManager.java:482)
     [aopc]     at org.jboss.aop.standalone.Compiler.compileFile(Compiler.java:251)
     [aopc]     at org.jboss.aop.standalone.Compiler.compile(Compiler.java:184)
     [aopc]     at org.jboss.aop.standalone.Compiler.main(Compiler.java:67)
     [aopc] [error] failed to transform: CarDAO.. Do verbose mode if you want full stack trace.
     [aopc] Exception in thread "main" java.lang.RuntimeException: failed to transform: CarDAO
     [aopc]     at org.jboss.aop.instrument.Instrumentor.transform(Instrumentor.java:615)
     [aopc]     at org.jboss.aop.AspectManager.translate(AspectManager.java:564)
     [aopc]     at org.jboss.aop.AspectManager.transform(AspectManager.java:482)
     [aopc]     at org.jboss.aop.standalone.Compiler.compileFile(Compiler.java:251)
     [aopc]     at org.jboss.aop.standalone.Compiler.compile(Compiler.java:184)
     [aopc]     at org.jboss.aop.standalone.Compiler.main(Compiler.java:67)
     [aopc] Caused by: javassist.CannotCompileException: by java.lang.RuntimeException: ERROR: declare-error condition
     [aopc]     'call(Driver->new(..)) AND within(*DAO)'
     [aopc] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [aopc]     DAO classes should not access the Driver class

     [aopc]     at org.jboss.aop.instrument.CallerTransformer$CallerExprEditor.edit(CallerTransformer.java:501)
     [aopc]     at javassist.expr.ExprEditor.doit(ExprEditor.java:136)
     [aopc]     at javassist.CtBehavior.instrument(CtBehavior.java:362)
     [aopc]     at org.jboss.aop.instrument.CallerTransformer.applyCallerPointcuts(CallerTransformer.java:69)
     [aopc]     at org.jboss.aop.instrument.Instrumentor.applyCallerPointcuts(Instrumentor.java:495)
     [aopc]     at org.jboss.aop.instrument.Instrumentor.transform(Instrumentor.java:562)
     [aopc]     ... 5 more

See how the compiler stops at the first error and execution stops.

DeclareError loadtime

When running with loadtime transformations we get:
$ ant run.loadtime
...

_run.loadtime.annotated:
     [java] ---- Start ----
     [java] ERROR: declare-error condition
     [java]     'call(Driver->new(..)) AND within(*DAO)'
     [java] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [java]     DAO classes should not access the Driver class

     [java] java.lang.RuntimeException: ERROR: declare-error condition
     [java]     'call(Driver->new(..)) AND within(*DAO)'
     [java] was broken for constructor call: CarDAO.save()V calls Driver.new()V
     [java]     DAO classes should not access the Driver class

     [java]     at org.jboss.aop.instrument.DeclareChecker.checkDeclares(DeclareChecker.java:124)
     [java]     at org.jboss.aop.instrument.DeclareChecker.checkDeclares(DeclareChecker.java:57)
     [java]     at org.jboss.aop.instrument.CallerTransformer$CallerExprEditor.edit(CallerTransformer.java:472)
     [java]     at javassist.expr.ExprEditor.doit(ExprEditor.java:136)
     [java]     at javassist.CtBehavior.instrument(CtBehavior.java:362)
     [java]     at org.jboss.aop.instrument.CallerTransformer.applyCallerPointcuts(CallerTransformer.java:69)
     [java]     at org.jboss.aop.instrument.Instrumentor.applyCallerPointcuts(Instrumentor.java:495)
     [java]     at org.jboss.aop.instrument.Instrumentor.transform(Instrumentor.java:562)
     [java]     at org.jboss.aop.AspectManager.translate(AspectManager.java:564)
     [java]     at org.jboss.aop.AspectManager.transform(AspectManager.java:482)
     [java]     at org.jboss.aop.standalone.AOPTransformer.transform(AOPTransformer.java:28)
     [java]     at sun.instrument.TransformerManager.transform(TransformerManager.java:122)
     [java]     at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:155)
     [java]     at java.lang.ClassLoader.defineClass1(Native Method)
     [java]     at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
     [java]     at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
     [java]     at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
     [java]     at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
     [java]     at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
     [java]     at java.security.AccessController.doPrivileged(Native Method)
     [java]     at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
     [java]     at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
     [java]     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
     [java]     at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
     [java]     at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
     [java]     at Driver.createVehicles(Driver.java:24)
     [java]     at Driver.main(Driver.java:19)
     [java] [error] failed to transform: CarDAO.. Do verbose mode if you want full stack trace.
     [java] Car DAO save
     [java] [error] failed to transform: MotorbikeDAO.. Do verbose mode if you want full stack trace.

Again you can see how the first broken condition causes execution to stop.