Missing @WithSession with quarkus-resteasy-reactive causes java.lang.IllegalStateException: No current Mutiny.Session found

286 views Asked by At

I'm working on a reactive application using Hibernate Reactive with Panache and am trying to implement a repository-service-rest-controller architecture. Here's my rest-controller method:

@GET
@Path("/translations/{language}")
public Uni<List<EntityTranslationTo>> getEntityTranslations(@PathParam("language") String langCode) {
    return entityTranslationService.getAllEntityTranslationsTo(langCode);
}

This controller calls the following service method:

public Uni<List<EntityTranslationTo>> getAllEntityTranslationsTo(String langCode) {
    Uni<List<EntityTranslationTo>> entityTranslationQuery = repository.getAllEntityTranslations(langCode.toLowerCase())
            .onItem().transform(translations -> translations.stream()
                    .map(translation -> new EntityTranslationTo(translation.getEntity().getId(), translation.getTranslation()))
                    .collect(Collectors.toList()));
    return PanacheExceptionHandler.handleDatabaseError(entityTranslationQuery, "Database connection exception");
}

And the service, in turn, calls this repository method:

public Uni<List<EntityTranslation>> getAllEntityTranslations(String langCode) {
    Uni<List<EntityTranslation>> entityTranslationQuery = find("langCode", langCode).list();
    return PanacheExceptionHandler.handleDatabaseError(entityTranslationQuery, "Database connection exception");
}

This code worked flawlessly in Quarkus version 2.16.7.Final. However, after updating to 3.4.3, I'm encountering this error:

java.lang.IllegalStateException: No current Mutiny.Session found
- no reactive session was found in the context and the context was not marked to open a new session lazily
- you might need to annotate the business method with @WithSession

The migration guide for version 3.0 (https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.0#hibernate-reactive-panache) mentions that in some cases, the session is automatically opened on demand, especially when a Panache entity method is invoked in a JAX-RS resource method in an application with the quarkus-resteasy-reactive extension. But in other cases, it's not as clear.

Why is this happening? Both the service and the repository should be called within the JAX-RS context. If I move all the logic from the service to the controller and eliminate the service, the code works without errors. It seems that the context is somehow lost when the service is involved.

If the code is left as is and the @WithSession annotation is added to the repository method, it will also work.

Has anyone faced this issue and found a solution? I couldn't find relevant examples in the official documentation.

1

There are 1 answers

0
f4lco On

TL;DR: works as designed.
I think the documentation could have described in more detail what "some cases" are:

In some cases, the session is opened automatically on demand. For example, if a Panache entity method is invoked in a JAX-RS resource method in an application that includes the quarkus-resteasy-reactive extension.

Migration Guide 3.0

Specifically, most users will think that "invoked in a JAX-RS resource method" is a transitive property - such that we get a session as long as the JAX-RS resource method calls a Panache method with any number of middlemen.

However, Quarkus only analyses the direct clients of the JAX-RS resource class and method.

In your example, as long as the supposed EntityTranslationResource does not call the methods on entities or repositories (EntityTranslationRepository) directly, Quarkus will not open the session automatically.

We find the precise criteria in the Quarkus source:

// Add @WithSessionOnDemand to a method that
// - is not static
// - is not synthetic
// - returns Uni
// - is declared in a class that uses a panache entity/repository
// - is annotated with @GET, @POST, @PUT, @DELETE ,@PATCH ,@HEAD or @OPTIONS
// - is not annotated with @ReactiveTransactional, @WithSession, @WithSessionOnDemand, or @WithTransaction
context.transform().add(DotNames.WITH_SESSION_ON_DEMAND).done();

Quarkus PanacheJpaCommonResourceProcessor

Specifically, the criterion "is declared in a class that uses a panache entity/repository" is interesting: it means all methods within the REST resource class get the Hibernate session on demand, as long as any of the methods actually use a Panache repository or entity directly.

Suppose resource method A uses Panache directly, and resource method B uses Panache indirectly via some other service. If we moved method A to another REST controller, we would break method B if we forgot to add the @WithSession annotation. Refactor with care!