1

Problem

I'm currently building an application using Quarkus (2.9.2.Final). The project contains multiple modules that depend on each other. The usecase module contains the interfaces for the repositories of the repository module. I now want to restrict access to the data, depending on some business-rules. Therefore, I added a decorator with the @Decorator annotation to the usecase module that decorates the repository interfaces.

The decorator runs and gets called before the call is delegated to the concrete bean. However, after that there seems to be a recursion happening somewhere, which triggers the decorator again, and so on and so forth. The repositories also have an @Interceptor that logs method calls.

Here is some sample code of what I have in the decorator (usecase module)

@Decorator
public class MyDecorator implements SomeRepository {

    @Inject
    @Delegate
    @Any
    SomeRepository delegate;

    private boolean canAccess() // check business-rules if access is allowed

    @Override
    public Entity get(Long id) {
        var cannotAccess = !canAccess();
        if (cannotAccess) {
          throw new RuntimeException("Access not allowed");
        }

        return delegate.get(id);
    }

    // other repository methods skipped for brevity

}

and the repository (repository module)

@ApplicationScoped
public class DefaultSomeRepositoryRepository implements SomeRepository {

    // inject some dependencies here

    @Override
    public Entity get(Long id) {
        // retrieve data from database
        // calls should only end up here, when access
        // has already been checked and allowed
        return new Entity();
    }

    // other repository methods skipped for brevity
    
}

What I've tried

  • Adding, changing the priority of the Decorator/Interceptor classes
  • Removing the Interceptor
  • Making the decorator abstract

Links

  • Using an interceptor instead of a decorator to check repository accessibility can be a viable atlernative? – Luca Basso Ricci Jun 20 '22 at 05:36
  • Luca is correct. Interceptors are best suited for cross-cutting concerns, whereas decorators are able to implement business logic. – Martin Kouba Jun 20 '22 at 06:08
  • The decorator is a good fit here, if the interception logic is not cross cutting (e.g. applies to all `get(Long)` methods from all repositories), rather specific to the underlying business logic (i.e. the authorization logic specific to `SomeRepository.get()`). – Nikos Paraskevopoulos Jun 20 '22 at 06:24
  • But indeed, the code above should work. I guess you end up with a StackOverflowException eventually. Does the stack trace contain any useful hint as to what consists the cycle? (probably not, but worth taking a look) Are there any other places in the application where you inject `SomeRepository`? – Nikos Paraskevopoulos Jun 20 '22 at 06:29
  • I do not see any problem in your code. We will need more information. Ideally, a small reproducer app. You can also file a new issue here: https://github.com/quarkusio/quarkus/issues – Martin Kouba Jun 20 '22 at 12:19
  • I'm facing exactly the same issue. Did you solve the problem? Did you file an issue? – Virginie Aug 19 '22 at 09:27
  • Sorry, no. This occurred in a private project which I didn‘t have time to further pursue so far. – Michael Burgstaller Aug 20 '22 at 10:19
  • I am facing the same problem. My decorator is abstract and handle generics though. My decorator (despite the fact of being abstract) is forcing me to implement all methods of the interface (it throws a NoSuchMethodError at runtime if not), and it is calling itself recursively. I don't understand why some people answer with: "use interceptor instead ...". The inquiry is not about which one to use, but why this is not working! please post if you find the solution. – A.N.T. Sep 12 '22 at 06:45
  • By the way, there is a similar issue with decorator here: https://github.com/quarkusio/quarkus/issues/24624 – A.N.T. Sep 12 '22 at 06:56

0 Answers0