Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-dev] Possible Library Aspect: Aspect Error Containment

Hi All,
 
I've written an aspect that will prevent other aspects (typically auxiliary
ones) from raising exceptions that interrupt core program logic. The basic
idea is that if the function of the aspect fails, you want to log it, but
not block the core system from functioning. This is useful for auxiliary
aspects like monitoring (what I'm working on), auditing, logging, and
tracing but certainly not for applications like security or caching. This
aspect came to mind from a discussion with the NearInfinity developers and I
recently found the first case where I needed it on my project.
 
Here's the core of the aspect (somewhat simplified):
 
public aspect ErrorContainment {
  pointcut scope() : within(Foo);
 
  Object around() : adviceexecution() && scope() { //&& if(((AdviceSignature
        thisJoinPointStaticPart.getSignature()).getReturnType() ==
void.class){
    try {
        return proceed();
    } catch (Error e) {
        handle(e);
    } catch (RuntimeException rte) {
        handle(rte);
    }
    return null; // will only be void
  }
  void handle(Throwable t) {     // swallows
    System.err.println("throwable "+t);
  }
}
 
Now, I've run into a few interesting issues in doing this. 

The short summary is that optimizing the weaver to inline this around advice
would make this aspect much more useful, and it would be a bit cleaner if
AspectJ supported signature matching for adviceexecution. In more detail:

* The 1.5.0 M2 compiler won't inline the use of this advice, which makes it
too expensive to use for my intended application. It certainly doesn't fit
the design case of around advice that won't be inlined "If there was a
proceed call in a nested type the weaver must assume that the call to
proceed is closed over." (from Hugunin & Hilsdale, "Advice Weaving in
AspectJ"). A little testing suggests to me that a closure is also created if
there's a catch block around the proceed, which seems like a good area to
optimize for any kind of error handling aspect.
* I don't believe there's any way to write this aspect to work correctly for
around advice with AspectJ 1.5 as planned. The problem is that you can't
proceed with the underlying proceed if there's an error before it occurs.(*)
* Failing that, I wanted to apply the advice to only before or after advice.
However, there's no portable way to detect the type of advice (one could
parse the generated method name but that's clearly fragile). Failing that,
to only apply this advice to advice that returns void, you have to execute
the (commented out) test which translates into some runtime overhead
(dispatching to an if-test method), and you still have to use the
polymorphic return of Object from the advice, since the compiler doesn't
know it can only return void. 
This is a good case where having a *signature* inside adviceexecution would
be useful, e.g., adviceexecution(before), or adviceexecution(void
around(int)) As an aside, this would also be a natural way to match
annotations on adviceexecution (presumably it's currently done using
@annotation?)

Ron

(*) In general, of course, you can't really define how this should work for
around advice. But for the common case of around advice that does something,
always proceeds with the original arguments, and then does something else,
it would be nice to be able to write this. If there were a proceeding join
point, then you could write something like this:

aspect ErrorContainment {
  private ThreadLocal<Boolean> hasProceeded;
  private ThreadLocal<ProceedClosure> proceedInstance;

  Object around() : adviceexecution(* around(..)) && scope() {
    try {
        hasProceeded.set(false);
        proceedInstance.set(thisJoinPoint.getProceedClosure());
        return proceed();
    } catch (RuntimeException e) {
        try {
            handleError(e);
        } finally {
            if (!hasProceeded.get()) {
                return thisJoinPoint.getProceedClosure(); // this would
force not inlining ...
            }
        }
    }
  }
  before() : proceeding() && scope() {
     if (this.equals(proceedInstance.get())) {
         hasProceed.set(true);
     }
  }
...

In practice, I don't think handling this case for around advice is very
important. My goal is to use only before and after advice wherever possible
and to manually add error containment to that where required...
 
Ron Bodkin
Chief Technology Officer
New Aspects of Software
w: (415) 824-4690




Back to the top