Search code examples
aopaspectjpointcut

How to intercept proceed() in another AspectJ aspect?


I have a situation as follows: I have a LoggingAspect with several pointcuts matching specific method executions in my main application. The corresponding advice bodies basically all look similar, causing a lot of code duplication:

void around() : download() {
    String message = "Downloading, verifying (MD5) and unpacking";
    SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
    proceed();
    SimpleLogger.verbose(message + " - done", IndentMode.DEDENT_BEFORE);
}

There is some variation, though. Sometimes the pointcut & advice have an arg or this parameter which is also printed to the log. Sometimes the "done" message is not printed if it s just a minor call not wrapping a lot of other calls, like this:

void around(BasicFilter filter) : fixFaultyLinkTargets()  && this(filter) {
    String message = "TOC file: checking for faulty link targets";
    SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
    proceed(filter);
    SimpleLogger.dedent();
}

The constant thing is that I manually tell the logger

  • to increase the indent level after the first message is printed, i.e. directly before proceed() is called, and
  • to decrease the indent level before the final message is printed (if any is printed), i.e. directly after proceed() has returned.

My idea is that I would like to write a meta aspect (or call it a helper aspect) with a pointcut which intercepts the proceed() calls in LoggingAspect so as to automatically adjust the indentation level accordingly. But there seems to be no pointcut matching proceed(). I have tried call(SomeMethodInMyMainApp), even a pointcut matching everything in the logging aspect, but the pointcut matches anything I do not need, but never ever the proceed.

If anybody knows how I can do this, I would appreciate a hint or a code snippet.

An indirect way of doing this might be to intercept not the advice themselves, but the method calls (or executions) advised by those advice by creating an extra pointcut like this:

// ATTENTION: each new pointcut must also be added here
pointcut catchAll() : download() || fixFaultyLinkTargets() || ...;

void around() : catchAll() {
    SimpleLogger.indent();
    proceed();
    SimpleLogger.dedent();
}

I would prefer another way though, without me having to remember to update the extra catchAll() pointcut everytime I change something in the logging aspect.


Solution

  • Suggestion wrap the proceed() in an anonymous class. And the write an aspect which adress this execution (but don't forget potential exceptions of proceed()).

    My suggestion:

    // AspectProceedCaller.java
    public abstract class AspectProceedCaller { 
        public abstract Object doProceed(); 
    };
    
    // aspect ProceedCallerAspect.aj
    aspect ProceedCallerAspect {
         pointcut execProceedCaller() : execution( * AspectProceedCaller+.doProceed() );
    
         Object around() : execProceedCaller() {
             try {
                  SimpleLogger.indent();
                  return proceed();
             }
             finally {
                  SimpleLogger.dedent();
             }
         }
    };
    
    
    // Your application aspect 
    aspect AnyAspect {
        pointcut anyPointcut() : ...;
    
        Object around() : anyPointcut() {
            AspectProceedCaller apc=new AspectProceedCaller() {
                public Object doProceed() {
                    return proceed();
                }
            };  
    
            // DO Stuff before ....
    
            Object retval = apc.doProceed();
    
            // ... and after calling proceed.
    
            return retval;
        }
    };
    

    Best regards Marko