Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method Callers and Lambda #713

Closed
Nico-DF opened this issue Nov 5, 2021 · 2 comments
Closed

Method Callers and Lambda #713

Nico-DF opened this issue Nov 5, 2021 · 2 comments

Comments

@Nico-DF
Copy link

Nico-DF commented Nov 5, 2021

Hi, I don't know exactly know how to report it (is it a bug? Something I don't understand? Technical limitation? etc.), So anyway, here's my problem:

My wanted rule

All calls to any invoker method must come, at some point, from a method annoted by a custom annotation (let's call it Job)

My defined rule

 @ArchTest
 public static final ArchRule methods_using_invokers_must_be_triggered_by_jobs =
      methods()
          .that().areDeclaredInClassesThat().resideInAPackage("..invoker..")
          .should(onlyBeCalledByJobsMethods())
          .because("Only job can interact with invokers");
  
  private static ArchCondition<JavaMethod> onlyBeCalledByJobsMethods() {
    return new ArchCondition<>("only be called by @Job methods") {
      @Override
      public void check(JavaMethod method, ConditionEvents events) {
        method.getCallsOfSelf().stream()
            .filter(call -> !hasJobAsCaller(call))
            .forEach(call -> events.add(SimpleConditionEvent.violated(method, call.getDescription())));
      }

      private boolean hasJobAsCaller(JavaMethodCall call) {
        if (call.getOrigin().isAnnotatedWith(Job.class)) {
          return true;
        }

        var parent = ((JavaMethod) call.getOrigin()).getCallsOfSelf();
        if (parent.isEmpty()) {
          return false;
        }
        return parent.stream().allMatch(this::hasJobAsCaller);
      }
    };
  }

This rule seems to work fine, except in one case, except if Lambda are involved.

Problematic code

I have the following code:

@Job
public void doJob() {
  lockManager.lockResourcesAndPerform(MY_RESOURCES, () -> doAction());
}

private void doAction() {
  myInvoker.doSomething();
}

If I follow this the test excecution, at some point in my recursive function,
I pass through the method doAction(), then I arrive to:

  • call: (parameter of method hasJobAsCaller) ~JavaMethodCall{origin=JavaMethod(some.package.SomeClass.lambda$doJo$0()....), ...}

The JavaMethod in this case is the lambda, which itself is not annotated, so first check is false.
But if I ask for callers of this lambda (I naïvely expect the method itself to popup), I got an empty list, so in my Call Stack, I never got to encounter the method doJob() itself, hence my rule fails here

I don't know if I'm clear on the issue?
I could possibly update my rule to, instead checking the annotation, checking that the caller is in a certain package (as all method annotated by Job are on a specific package), but I would find it more natural and flexible if the rule could directly check the annotation

@hankem
Copy link
Member

hankem commented Nov 5, 2021

I fear that ArchUnit does unfortunately not yet recognize method references (#215) or lambda expressions (#266).

@Nico-DF
Copy link
Author

Nico-DF commented Nov 5, 2021

Ok thanks, I tried to look for any open issue but I guess I missed the #266
I guess I'll close the issue to avoid clobbering Issues list

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants