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:

To compile and run:

  $ ant

It will generate the following output

Buildfile: build.xml

prepare:

compile:
     [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.

run:
     [java] ---- Start ----
     [java] Car DAO save

BUILD SUCCESSFUL

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

Run the example load-time instrumented

  $ ant run.50.instrumented

run.50.instrumented:
     [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.




BUILD SUCCESSFUL
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
Buildfile: build.xml

prepare:

compile:
    [javac] Compiling 5 source files to C:\cygwin\home\Kab\cvs\jboss-head\aop\docs\examples\annotated-declare
     [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.50.instrumented
Buildfile: build.xml

prepare:

compile50standalone:
    [javac] Compiling 5 source files to C:\cygwin\home\Kab\cvs\jboss-head\aop\docs\examples\annotated-declare

run.50.instrumented:
     [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.