PentaKon
PentaKon

Reputation: 4636

Using CDI with AsyncResponse and ExecutorService

Before adding CDI into our application I had created a resource that used the @Suspended AsyncResponse object to implement long polling for a chat client. What I did was create a new newSingleThreadExecutor() and submit a Runnable to it that used .wait(30000) on a message list until notification that a new message was sent. Inside that task I used the HttpServletRequest which was obtained using @Context and everything worked perfectly.

However once we added CDI to our application and even without making the resource class a bean (scanning only annotated beans and we didn't give it any scope annotation) I got a runtime exception that the request object INSIDE the Runnable task couldn't be accessed because of an illegal state exception:

Method threw 'java.lang.IllegalStateException' exception. Cannot evaluate com.sun.proxy.$Proxy74.toString()

I'm not really sure why this happens but I know it is CDI related since it refers to a proxy object. One guess is that the resource class itself has become CDI scoped and that scope can't be accessed from a different thread? I read somewhere that manually started threads are not managed and thus can't have access to any scope related objects. However how did this use to work until CDI was implemented?

Right now I THINK I've solved the issue (that is releasing the thread servicing request I/O and having a worker take over the waiting until notified) using jersey's @ManagedAsync annotation which supposedly has the whole method be run in an internal jersey executor service. Is this correct? Also in that case, is there any need of the AsyncResponse object?

EDIT: I have NOT solved the issue. @ManagedAsync worked when the resource class was not defined as a CDI bean. After making it @RequestScoped, whenever I try to call the method I get the following exception

org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.RequestScoped

I think this is because the request can end before the async thread has finished which means all scope objects (like HttpServletRequest) will be destroyed and thus we won't have access to them. Is there a way to used @ManagedAsync in a @RequestScoped bean and make use of @Context HttpServletRequest??

TL;DR:

  1. How can I have access to a context resource inside a manually started thread?
  2. Why did I have access to the request object before CDI was implemented?
  3. Is it possible to use @ManagedAsync in a @RequestScoped cdi bean?

Old method:

@GET
@Path("method")
public void method(@Context HttpServletRequest request, @Suspended AsyncResponse ar) {
  //request object was accessible here
  Executors.newSingleTHreadExecutor().submit(() -> {
    //request object was also accessible here but lost access after implementing CDI.
    Object o = foo.bar(request);
    ar.resume(Response.ok(o).build());
  });
}

Current non-working method:

@GET
@Path("method")
@ManagedAsync
public void method(@Context HttpServletRequest request, @Suspended AsyncResponse ar) {
  Object o = foo.bar(request);
  ar.resume(Response.ok(o).build()); //Is there any point to this?
}

Upvotes: 1

Views: 2001

Answers (1)

John Ament
John Ament

Reputation: 11723

To answer your question - no. You cannot use async and request scoped objects. Async support is lacking in CDI - see also https://issues.jboss.org/browse/CDI-452

Upvotes: 2

Related Questions