LogicAJ Examples

Mock Objects

Introduction

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. Mock Aspect Effect

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):

Mock Aspect Code

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.

Explanation

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.

Comparision

  • The generic version resolves the constructors at weave time
  • No runtime overhead for Java reflection.
  • Ratio lines of code (AspectJ : LogicAJ): 45 : 5

Introductions

Extract a pointcut with advice parameters

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:

Statement around getStatement(DBContext dbcontext):
	equals(?db, de.tarent.dblayer.engine.DB) &&
	call(* ?db.getStatement(..))&&
	args(dbcontext){

the extracted pointcut:

pointcut getStatementPC(?db, DBContext dbcontext):
	  equals(?db, de.tarent.dblayer.engine.DB) &&
	  call(* ?db.getStatement(..))&&
	  args(dbcontext);

and this is how to use the pointcut to achieve the same behavior as the code above:

Statement around getStatement(?db, DBContext dbcontext):
	getStatementPC(?db, dbcontext){

Member + getter

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.

introduce field(?destination):
        equals(?destination, f.q.n.Destination){
	        public List ?destination.field;
        }
introduce implGetField(?destination):
	equals(?destination,  f.q.n.Destination) &&
	equals(?fieldname, field){

	  public List ?destination.getField() {
	    if (?fieldname==null){
	      ?fieldname = new ArrayList();
	    }
	    return ?fieldname;
	  }
	}

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:

pointcut getField(?getField):
	equals(?getField, getField);

Now you can use code like this in your advice:

after useIntroducedMethod() :
	anySelectivePointcut(?var) &&
        equals(?destination,  f.q.n.Destination) &&
        getField(?getField)&&{

        List list = ?destination.?getField();

        }

WATCH OUT: the introduction has to be defined above the first use of the introduced method within the aspect.

Refer to the outer class of an anonymous class

The outer pointcut binds the outer class name of an anonymous class. And vise versa:

introduce outerthis(?fqn) :
    equals(?fqn, generic.type.introductions.ContainsAnonynousClasses) &&
    outer(?inner,?fqn)
{
    void ?inner.innerMethod() {
      innerMethod();
    }
}
Last modified: 2017/08/30 03:15
*