Using the wormhole pattern with AspectJ, is there a way that the executing method name can be seen in the wormhole advice?
I have an example where a service class (C
Client:
package de.scrum_master.app;
public class Client {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Client service incl. sample main method:
package de.scrum_master.app;
public class ClientService {
private Client client;
public ClientService(Client client) { this.client = client; }
public String getSomething() { return client.getName(); }
public void setSomething(String something) { client.setName(something); }
public static void main(String[] args) {
ClientService clientService = new ClientService(new Client());
clientService.setSomething("new value");
clientService.getSomething();
}
}
Aspect:
Here you need to change pointcut modelGetter
to use call()
instead of execution()
and target()
instead of this()
.
package de.scrum_master.aspect;
import de.scrum_master.app.Client;
import de.scrum_master.app.ClientService;
public aspect MyAspect {
pointcut serviceExecution(ClientService srv) :
execution(* ClientService.*(..)) && this(srv);
pointcut modelGetter(Client client) :
call(public * Client.get*()) && target(client);
pointcut wormhole(ClientService srv, Client client) :
cflow(serviceExecution(srv)) && modelGetter(client);
Object around(ClientService srv, Client client) : wormhole(srv, client) {
System.out.println("Caller: " + thisEnclosingJoinPointStaticPart.getSignature().toShortString() + " -> " + srv);
System.out.println("Callee: " + thisJoinPointStaticPart.getSignature().toShortString() + " -> " + client);
return proceed(srv, client);
}
}
Console output:
Caller: ClientService.getSomething() -> de.scrum_master.app.ClientService@72bcecc0
Callee: Client.getName() -> de.scrum_master.app.Client@515b6c19
Update: Well, actually if the client service directly calls the client you do not need to use a wormhole pattern. The latter should only be used if the call chain is longer (other classes or method calls in between). In that case my sample code would fail because thisEnclosingJoinPointStaticPart
would always capture the last caller in the call chain outside the client method call, not necessarily the client service entry point at the beginning of the control flow you are interested in. Thus, I have updated the sample code to show a more generic solution:
Client: same as above
New client delegate (to introduce some indirection):
package de.scrum_master.app;
public class ClientDelegate {
private Client client;
public ClientDelegate(Client client) { this.client = client; }
public String getName() { return client.getName(); }
public void setName(String name) { client.setName(name); }
}
Updated client service using the delegate:
package de.scrum_master.app;
public class ClientService {
private ClientDelegate clientDelegate;
public ClientService(ClientDelegate clientDelegate) { this.clientDelegate = clientDelegate; }
public String getSomething() { return clientDelegate.getName(); }
public void setSomething(String something) { clientDelegate.setName(something); }
public static void main(String[] args) {
ClientService clientService = new ClientService(new ClientDelegate(new Client()));
clientService.setSomething("new value");
clientService.getSomething();
}
}
Updated aspect:
The aspect now is not a sigleton anymore, but uses percflow()
instantiation. It also comes with an extra before()
advice for the client service control flow, saving its join point context in private member serviceContext
. This is needed later in order to print the information to the console.
In the around()
advice I also switched back from call()
to execution()
and from target()
to this()
because now we do not use thisEnclosingJoinPointStaticPart
anymore.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint.StaticPart;
import de.scrum_master.app.Client;
import de.scrum_master.app.ClientService;
public aspect MyAspect percflow(serviceExecution(ClientService)) {
private StaticPart serviceContext;
pointcut serviceExecution(ClientService srv) :
execution(* ClientService.*(..)) && this(srv);
pointcut modelGetter(Client client) :
execution(public * Client.get*()) && this(client);
pointcut wormhole(ClientService srv, Client client) :
cflow(serviceExecution(srv)) && modelGetter(client);
before(ClientService srv) : serviceExecution(srv) {
serviceContext = thisJoinPointStaticPart;
}
Object around(ClientService srv, Client client) : wormhole(srv, client) {
System.out.println("Service: " + serviceContext.getSignature().toShortString() + " -> " + srv);
System.out.println("Client: " + thisJoinPointStaticPart.getSignature().toShortString() + " -> " + client);
return proceed(srv, client);
}
}
New console output:
Service: ClientService.getSomething() -> de.scrum_master.app.ClientService@4cc4dfc5
Client: Client.getName() -> de.scrum_master.app.Client@113f25e3