2

Description How to create a pointcut for Runnable.run method So that @Before and @After aspect can be invoked in java 8 lambda expression.

  1. Create pointcut for Runnable.run method
  2. Create @Before aspect for pointcut in step 1 . ---> Print Before runnbale
  3. Create @Aefore aspect for pointcut in step 1 . ---> Print After runnable

When below line invoked

executor.runAsync(() ->

{ System.out.println("Runnable invoked"); }
)

Output expected :

Before runnable
Runnable invoked
After runnable

Solution from @AspectJ. Pointcut for scala (and probably java) lambdas not worked in this problem.

@Around("execution(void com.test..lambda*(..)) This will work for all lambda expression ... And I want to restrict this for Runnable.run method only.

Manoj
  • 21
  • 4

1 Answers1

2

You cannot because the executed lambda methods are static, i.e. you cannot even check something like thisJoinPoint.getTarget() instanceof Runnable because the target is null. With anonymous subclasses this would work.

I.e. before something has been done about my AspectJ Bugzilla issue, you cannot really solve this problem.


Update: I found a workaround for you. It is not nice, but at least it works until AspectJ supports lambdas better. You need to tailor it to the method calling the runnable in question, though:

Driver application:

package de.scrum_master.app;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class Application {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
      try {
        MILLISECONDS.sleep(100);
      } catch (InterruptedException e) {
        throw new IllegalStateException(e);
      }
      System.out.println("Separate thread lambda");
    });

    CompletableFuture<Void> future2 = CompletableFuture.runAsync(new Runnable() {
      @Override
      public void run() {
        try {
          MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
          throw new IllegalStateException(e);
        }
        System.out.println("Separate thread anonymous Runnable");
      }
    });

    System.out.println("Main thread");
    future.get();
    future2.get();
  }
}

Aspect:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AsyncRunInterceptor {
  private static final String CLASS_ASYNC_RUN = "java.util.concurrent.CompletableFuture$AsyncRun";
  private static final String METHOD_RUN = "run";

  @Pointcut("execution(void *(..)) && !within(de.scrum_master.aspect.AsyncRunInterceptor) && if()")
  public static boolean isAsyncRun() {
    final StackTraceElement[] stackTrace = new Exception().getStackTrace();
    if (stackTrace.length < 3)
      return false;
    final StackTraceElement stackTraceElement = stackTrace[2];
    return
      stackTraceElement.getClassName() == CLASS_ASYNC_RUN
        && stackTraceElement.getMethodName() == METHOD_RUN;
  }

  @Before("isAsyncRun()")
  public void beforeAsyncRun(JoinPoint thisJoinPoint) {
    System.out.println("[" + Thread.currentThread().getId() + "] BEFORE " + thisJoinPoint);
  }

  @After("isAsyncRun()")
  public void afterAsyncRun(JoinPoint thisJoinPoint) {
    System.out.println("[" + Thread.currentThread().getId() + "] AFTER " + thisJoinPoint);
  }
}

Console log:

Main thread
[10] BEFORE execution(void de.scrum_master.app.Application.lambda$0())
[11] BEFORE execution(void de.scrum_master.app.Application.1.run())
Separate thread lambda
Separate thread anonymous Runnable
[11] AFTER execution(void de.scrum_master.app.Application.1.run())
[10] AFTER execution(void de.scrum_master.app.Application.lambda$0())
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks a lot for your quick response. – Manoj Apr 03 '18 at 08:15
  • Well, you did not accept the answer yet. It is correct. Do you have any follow-up questions? Besides, I just updated it, showing you how to work around the problem. – kriegaex Apr 04 '18 at 03:32
  • The above solution will work ... But the problem is .. thisJoinPoint.getTarget() is still null. :( – Manoj Apr 04 '18 at 10:31
  • Of course it is. What is the problem about it? You do not need it with this solution. The code does exactly what you asked for in your question. – kriegaex Apr 04 '18 at 12:31
  • You are correct. The workaround for the above problem is right. But I am looking for target as well in my project – Manoj Apr 04 '18 at 16:31
  • **a)** You have not mentioned that with one syllable in your question. **b)** There _is no target_ for lambdas. As I said, they are implemented as _static_ methods. Welcome to the JVM! I cannot change that for you. **c)** Even if it was implemented otherwise, what would you do with the target? Lambdas only have one method which is already executed. They have no meaningful class name either (see log output above), being anonymous. I think you are just constructing a problem here which does not exist. – kriegaex Apr 05 '18 at 00:06
  • So how about accepting and upvoting my answer now? Just because you do not like the way the JVM handles lambdas it does not mean that anyone can come up with a better or more correct answer for you. As I said, static methods do not have a target. – kriegaex May 08 '18 at 12:45