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.
Please note: I am going to answer my own question here, adding more information and the additional feature of parametrisation to the solution suggested by loddar2012. Because his answer led me to the right direction, I am going to accept it even though this answer here really addresses all my needs from the original question, such as (quoting myself):
The basic thing we are dealing with here is what Ramnivas Laddad calls the worker object pattern in his book AspectJ in Action. His (and loddar2012's) idea is, in plain prose
proceed()
therein,An elegant solution if you need to execute your
proceed()
calls asynchronously would be to create instances of anonymousRunnable
classes. We will use our own abstract base classLogHelper
, though, because we want some more sugar in our tea, specifically the option to pass a log message and some other parameters influencing log output to each worker. So this is what I did (package names and imports not shown in the sample code):Abstract worker base class:
Logging advice capturing execution of worker objects:
As you can see, the logging advice does some things before calling
proceed(logHelper)
(i.e. executing the worker object'slog()
method) and some things afterwards, using the state information stored inside the worker object, such asBecause in my use case all logged methods return
void
, there is no need to implement return value passing, but this would be easily possible, if necessary. The advice's return value would then just beObject
and we would pass the result ofproceed()
back to our caller, no big deal.Some advice capturing joinpoints to be logged and utilising parametrised worker objects to get the work done:
The examples show how you can
LogHelper
as a worker object with one or more constructor parameters, setting its statelog()
method, optionally using joinpoint state bound viathis()
orargs()
,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:
Best regards Marko