- Documentation
- Developer's corner
Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering
Generic aspects are particularly suitable to express crosscutting changes that follow a common structure, but vary in the names of created or modified entities.
A simple example is the use of mock objects. Mock Objects are a common technique for narrowing down potential sources of error during tests. This is done by replacing some of the tested classes by mock classes that provide a fixed expected well-known behaviour.
Below you see a comparison between an AspectJ implementation of mock objects (right-hand side) and a possible LogicAJ counterpart (left-hand side). The core of both aspects is the replacement of a constructor call by a call to the respective mock-class-constructor, if such a (mock) class exists (as illustrated above):
AspectJ example code size increases due to reflection (thus giving up on static type checking) and is much too large to fit on this page. (An ad-hoc implementation of resolveConstructor(Class, Class[]) using Java reflection can be found here) This LogicAJ aspect can be expressed in five lines of advice code whose type-correctness can be determined at weave-time.
The left example illustrates the syntax of logic variables (highlighted in red), their binding by evaluation of conditions, and their use in composition of generic advice code. The pointcut part of the advice uses four predicates: call , concat , class and args . The call predicate is the generic version of AspectJ's call pointcut, but it supports logic variables additionally. The concat predicate is true, if and only if the third argument is the concatenation of the first and the second. It is used here to create names of mock classes by appending the suffix “Mock”. The class predicate is true if its argument denotes a valid class-type. The args predicate is an extension to the args pointcut. Logical generalization makes it possible to match an entire parameter list in one variable (?args). In this example it matches all arguments of any constructor invocation. Finally, if all upper variables have been bound and all predicates become true, the original constructor call is replaced by the instantiation of the mock object.
This is an example for the extraction of a pointcut to (for example) reuse the pointcut or just make the code easier to understand.
The original code:
<code logicaj>
Statement around getStatement(DBContext dbcontext):
equals(?db, de.tarent.dblayer.engine.DB) && call(* ?db.getStatement(..))&& args(dbcontext){
</Code> the extracted pointcut: <code logicaj> pointcut getStatementPC(?db, DBContext dbcontext):
equals(?db, de.tarent.dblayer.engine.DB) && call(* ?db.getStatement(..))&& args(dbcontext);
</Code> and this is how to use the pointcut to achieve the same behavior as the code above: <code logicaj> Statement around getStatement(?db, DBContext dbcontext):
getStatementPC(?db, dbcontext){
</Code>
With the example code below, you can introduce a member named field of type List and a getter into a class named Destination with full qualified name f.q.n.Destination.
<code logicaj> introduce field(?destination):
equals(?destination, f.q.n.Destination){ public List ?destination.field; }
</Code> <code logicaj> introduce implGetField(?destination):
equals(?destination, f.q.n.Destination) && equals(?fieldname, field){
public List ?destination.getField() { if (?fieldname==null){ ?fieldname = new ArrayList(); } return ?fieldname; } }
</Code> If you want to refer to the introduced method from within an advice (which is most likely because why else would you introduce it?), you need to make the method name available vie metavariable: <code logicaj> pointcut getField(?getField):
equals(?getField, getField);
</Code> Now you can use code like this in your advice: <code logicaj> after useIntroducedMethod() :
anySelectivePointcut(?var) && equals(?destination, f.q.n.Destination) && getField(?getField)&&{
List list = ?destination.?getField();
}
</Code> WATCH OUT: the introduction has to be defined above the first use of the introduced method within the aspect.
The outer pointcut binds the outer class name of an anonymous class. And vise versa:
<code logicaj> introduce outerthis(?fqn) :
equals(?fqn, generic.type.introductions.ContainsAnonynousClasses) && outer(?inner,?fqn)
{
void ?inner.innerMethod() { innerMethod(); }
}
</Code>