Simon Wick
Simon Wick

Reputation: 281

ArchUnit: Check only field method-calls

I need to make sure that classes annotated by @Path only call methods in classes annotated by @Secured. But only method-calls on fields (e.g. injected beans) should be checked. What I have so far is:

ArchRuleDefinition.classes().that()
        .areAnnotatedWith(Path.class)
        .or().areMetaAnnotatedWith(Path.class)
        .should().onlyCallMethodsThat(are(declaredIn(!!!))

So 2 parts are missing:

Is there a way to achieve this?

Upvotes: 0

Views: 1679

Answers (1)

Manfred
Manfred

Reputation: 3142

There's a simple solution for the second part: With

import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.properties.HasOwner.Functions.Get;

import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;

you can use

    classes()
            .that().areMetaAnnotatedWith(Path.class)
            .should().onlyCallMethodsThat(
                    Get.<JavaClass>owner().is(annotatedWith(Secured.class))
                            .as("are defined in classes annotated with @Secured")
            );

Note that areMetaAnnotatedWith also includes direct annoations since ArchUnit 0.17.0. If you wanted to test constructor calls as well, you could replace onlyCallMethodsThat with onlyCallCodeUnitsThat.


Unfortunately, I don't think that the first part can be solved with the current version ArchUnit 0.23.1: You can rewrite the above rule to test each JavaMethodCall, which is more specific than the called JavaMethod:

    noClasses()
            .that().areMetaAnnotatedWith(Path.class)
            .should().callMethodWhere(describe("owner is not @Secured",
                methodCall ->
                    !methodCall.getTargetOwner().isAnnotatedWith(Secured.class)
            ));

With JavaMethodCall#getOrigin(), you can find the JavaCodeUnit where the method call occured, but this does not include information about the object on which the method was called.

If I understood correctly, you want to distinguish the following cases:

@Secured
class SecuredOwner {
    void method() {
    }
}

class NotSecuredOwner {
    void method() {
    }
}

@Path
class ThisShouldBeOkay {
    SecuredOwner securedOwner = new SecuredOwner();

    void call_secured_method_on_field() {
        securedOwner.method();
    }

    void call_not_secured_method_on_local_variable() {
        new NotSecuredOwner().method();
    }
}

Upvotes: 1

Related Questions