jakarta-eejax-rswildflycdiresteasy

CDI 4.0 Integration with Jakarta REST 3.1 in WildFly server


I am trying to understand JAX-RS, CDI integration in Wildfly, specifically how the bean lifecycle looks like. The following behaviour is not what I expected. Below is drilled down example.

@Dependent
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserResourceImpl {

   @PostConstruct
   public void init() {
    System.out.println("UserResourceImpl init ...");
   }

   @PreDestroy
   public void destroy() {
    System.out.println("UserResourceImpl destroy ...");
   }

   @GET
   public Response test() {
    return Response.ok("{ \"answer\": \"working...\"}").build();
   }
 }

Simple REST resource implementation with @Dependent scope. Why I am annotating this bean explicitly because my beans.xml is configured like that.

<?xml version="1.0" encoding="UTF-8"?>
<beans version="4.0"
   xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      https://jakarta.ee/xml/ns/jakartaee
      https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
   bean-discovery-mode="annotated">

</beans>

Bean discovery mode is annotated. This is on purpose, since I am studying bean lifecycle and wanted to keep things in my control.

So when this simple application is deployed in Wildfly 32 application server, what I notice is that @PostContruct is called for each HTTP request being received. Even after I already see the response on the client application i.e. HTTP request is completed, @PreDestroy never get called. Even when application is terminated, @PreDestroy does not get called. Why?

Looking at the documentation from RESTEasy https://docs.resteasy.dev/6.2/userguide/#_default_scopes

A CDI bean that does not explicitly define a scope is @Dependent scoped by default. This pseudo scope means that the bean adapts to the lifecycle of the bean it is injected into. Normal scopes (request, session, application) are more suitable for Jakarta RESTful Web Services components as they designate component’s lifecycle boundaries explicitly. Therefore, the resteasy-cdi module alters the default scoping in the following way:

If a Jakarta RESTful Web Services root resource does not define a scope explicitly, it is bound to the Request scope.

If a Jakarta RESTful Web Services Provider or jakarta.ws.rs.Application subclass does not > define a scope explicitly, it is bound to the Application scope.

So I expect this to behave like @RequestScoped bean, calling both @PostConstruct and @PreDestroy for each completed HTTP request.

Could someone explain this to me? Is this a Bug?


Solution

  • Minor nit, if you're using WildFly 32 you're using Jakarta REST 3.1.

    By default RESTEasy uses @RequestScoped for endpoints and @ApplicationScoped for jakarta.ws.rs.core.Application's and providers.

    Since you defined @Dependent, you're effectively saying "give me a new bean each time" as you're not injecting that Jakarta REST resource into another scoped bean. As to why the bean is not being destroyed, I'm not sure yet. The JavaDoc is a bit odd IMO.

    When the container destroys an instance of a bean or of any Java EE component class supporting injection, the container destroys all its dependent objects, after the @PreDestroy callback completes and after the servlet destroy() method is called.

    While RESTEasy itself does support Servlet, there is no requirement in the Jakarta REST specification that a Servlet be used. I'll have to look into why the beans are not getting destroyed.

    All this said, I do think @Dependent is the wrong scope for a Jakarta REST endpoint and you're better to, at a minimum, use @RequestScoped endpoints.