Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ensuring one method's call stack always includes another method in Java

I have a design problem in a common utility that we use in our Java project where I want to ensure that all callers of a particular method A are wrapped by another method B. The general form of this code as I've written it today is:

x.B(new Runnable() {
    y.A();
});

The runnable that is being executed by B can have arbitrary code, and may call A multiple times, so I can't get rid of the runnable in this code by adding the call to A directly into B. Also, A is third-party code, so we can't modify it. It's possible that the runnable could call B again with another nested call to A, but today that never happens so I am ok ignoring that case for now.

I see a few options:

  1. Declare A() throws BlahException and make it so that B is the only catcher of that exception. This is ugly because there isn't any exception that should truly be thrown, but it's nice because the compiler would ensure the call hierarchy for me.
  2. Write some sort of static analysis tool to ensure this rule for me. I haven't investigated this case much yet, as it sounds like more work than anything else (but maybe there is a preexisting tool that can do this?).
  3. Add an assertion to the "beginning of A" (really, this code would have to live in a custom version of Runnble since I can't modify A directly) that we are running inside of a call to B. This could either use some additional thread-/object-local state or traverse the call stack itself, both of which are kind of ugly.

Are there other options I haven't considered?

like image 805
Dan Avatar asked Nov 01 '22 03:11

Dan


1 Answers

Have you considered using AspectJ, or some other aspect-oriented programming (AOP) tools? Then you could intercept every call of method A, check for method B in the stacktrace. If it's not there, you could throw exception preventing execution of A, or write an error to log, or do whatever you like. Something like this:

@Aspect
public class CheckInsideMethodBAspect {

    @Around("execution(* com.example.AClass.A(..))")
    public void checkNestedMethod(ProceedingJoinPoint joinPoint) {

        // checking for method B in the call stack
        boolean isInsideB = false;
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        for (StackTraceElement element: stackTraceElements) {
            if (element.getClassName().equals("ClassB") && element.getMethodName().equals("B")) {
                isInsideB = true;
                break;
            }
        }

        // if not inside B, throwing exception
        if (!isInsideB) {
            throw new NotInsideBException();
        }

        // if inside B, then proceeding with method A execution
        joinPoint.proceed();
    }

}
like image 142
Sergei Avatar answered Nov 12 '22 10:11

Sergei