Hector
Hector

Reputation: 5418

How to implement Wormhole pattern with AspectJ (cflow)

I am trying to develop a simple wormhole using AspectJ and cflow.

I have two value objects Person and Account as shown below

ACCOUNT

public class Account {

    private final String sortCode;
    private final String accountNumber;
    private final int balance;

    public Account(String sortCode, String accountNumber, int balance) {
        this.sortCode = sortCode;
        this.accountNumber = accountNumber;
        this.balance = balance;
    }

    public String getSortCode() {
        return sortCode;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public int getBalance() {
        return balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "sortCode='" + sortCode + '\'' +
                ", accountNumber='" + accountNumber + '\'' +
                ", balance=" + balance +
                '}';
    }
}

PERSON

public class Person {

    private final String name;
    private final String address;
    private final int age;

    public Person(String name, String address, int age) {
        this.name = name;
        this.address = address;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", age=" + age +
                '}';
    }
}

And three "Layers"

Layer A

public class Layer_A {

    public void doSomeLevelAprocess(Person person){

        System.out.println("doSomeLevelAprocess person " + person);
        new Layer_B().doSomeLevelBprocess();

    }
}

Layer B

public class Layer_B {

    public void doSomeLevelBprocess(){

        System.out.println("doSomeLevelBprocess");
        new Layer_C().doSomeLevelCprocess(new Account("123456", "87654321", 1001));

    }
}

Layer C

public class Layer_C {

    public void doSomeLevelCprocess(Account account){

        System.out.println("doSomeLevelCprocess " );

    }
}

I would like to have the Person object thats passed to Layer_A method doSomeLevelAprocess made available at Layer_C method doSomeLevelCprocess

Is this possible?

my aspect looks like this a

public aspect WormWhole {

    pointcut callerSpace(Person person): execution(* Layer_A.*(..)) && this(person);

    pointcut calleeSpace(Account account): this(account) && execution(public * Layer_C.*(..));

    pointcut wormhole(Person person, Account account):
            cflow(callerSpace(person)) && calleeSpace(account);

    before(Person person, Account account):
            wormhole(person, account){ **//WHAT DO I DO HERE?????** }
}

Have I selected pointcuts callerSpace and calleeSpace correctly?

What logic is required in

before(Person person, Account account):
                wormhole(person, account){ **//WHAT DO I DO HERE?????** }

UPDATE

The "Possible Duplicate" question does not actually illustrate HOW to achieve a wormhole, it simply shows a "template" for an AspectJ wormhole. In addition question answer referred to doesnt attempt to pass context through multiple layers, which is the entire point of a wormhole.

UPDATE

When i change the advice to this:-

public aspect WormWhole {

    pointcut callerSpace(Person person): execution(* Layer_A.*(..)) && this(person);

    pointcut calleeSpace(Account account): this(account) && execution(public * Layer_C.*(..));

    pointcut wormhole(Person person, Account account):
            cflow(callerSpace(person)) && calleeSpace(account);

    before(Person person, Account account):
            wormhole(person, account){
        System.out.println("Wormhole - " + person);
        System.out.println("Wormhole - " + account);


    }
}

I receive this output:-

doSomeLevelAprocess person Person{name='aaa', address='aaa', age=19}
doSomeLevelBprocess
doSomeLevelCprocess 

Process finished with exit code 0

This proves my pointcuts are not correct (I believe), but why????

UPDATE

I have added in simple pointcut which works fine

public aspect WormWhole {

    pointcut callSayHello(): call(* Layer_A.*(..)) || call(* Layer_B.*(..)) || call(* Layer_C.*(..))  ;

    pointcut callerSpace(Person person): execution(* Layer_A.*(..)) && this(person);

    pointcut calleeSpace(Account account): this(account) && execution(public * Layer_C.*(..));

    pointcut wormhole(Person person, Account account):
            cflow(callerSpace(person)) && calleeSpace(account);

    before(Person person, Account account):
            wormhole(person, account){
        System.out.println("Wormhole - " + person);
        System.out.println("Wormhole - " + account);


    }

    after() : callSayHello()  {
        System.out.println("After call sayHello" + thisJoinPoint);
    }
}

Output from test execution

doSomeLevelAprocess person aaa
doSomeLevelBprocess
doSomeLevelCprocess account 87654321
After call sayHellocall(void Layer_C.doSomeLevelCprocess(Account))
After call sayHellocall(void Layer_B.doSomeLevelBprocess())
After call sayHellocall(void Layer_A.doSomeLevelAprocess(Person))

Process finished with exit code 0

I am using CTW within IntelliJ 14.1.14, with aspectjrt-1.8.6.jar

Upvotes: 0

Views: 391

Answers (1)

kriegaex
kriegaex

Reputation: 67437

You are right, you got something wrong with your pointcuts, which is why the AspectJ compiler shows this warning:

advice defined in Wormhole has not been applied [Xlint:adviceDidNotMatch]   

The reason is simple: You are trying to bind your method arguments via this() instead of args(). This way both pointcuts cannot match because the this objects in the two execution contexts are instances of Layer_A and Layer_C, not Person and Account.

This little change fixes your wormhole pattern aspect:

pointcut callerSpace(Person person) :
    execution(* Layer_A.*(..)) && args(person);

pointcut calleeSpace(Account account) : 
    execution(public * Layer_C.*(..)) && args(account);

Upvotes: 1

Related Questions