glassfish
  1. glassfish
  2. GLASSFISH-17152

CDI HttpServletRequestContext not active for asynchronous servlet

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 3.1.1
    • Fix Version/s: 4.0_b78
    • Component/s: web_container
    • Labels:
      None
    • Environment:

      Windows 7 (x64), JDK 7 (x64), Eclipse Indigo (3.7), Glassfish Open Source 3.1.1

      Description

      I have a servlet which performs background processing using servlet 3 AsyncContext:

      final AsyncContext context = request.startAsync();
      context.addListener(this);
      context.start(new Runnable() {
      public void run()

      { // [ ... do background RPC call, store result in request ...] context.dispatch(); }

      );

      This will dispatch back to the original servlet which uses a request scoped managed bean, which causes the following exception:

      org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped
      at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:664)
      at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:77)
      at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:87)
      at [Weld Client Proxy]
      at AuthenticateServlet.doPost(AuthenticateServlet.java:78)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
      at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1539)
      at org.apache.catalina.core.ApplicationDispatcher.doInvoke(ApplicationDispatcher.java:787)
      at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:649)
      at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:483)
      at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:454)
      at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:350)
      at org.apache.catalina.connector.AsyncContextImpl$Handler.run(AsyncContextImpl.java:406)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
      at java.lang.Thread.run(Thread.java:722)

      After some investigation, it seems this is related to Weld ServletRequestListener not be notified of "new" request.

      This also seems to have happened under different conditions as well, both in Glassfish and Tomcat 7:

      https://issues.apache.org/bugzilla/show_bug.cgi?id=49991
      https://issues.apache.org/bugzilla/show_bug.cgi?id=50789

      http://java.net/jira/browse/GLASSFISH-11504
      http://java.net/jira/browse/GLASSFISH-12642 (this fix should have handled my case, but was reverted by change in GLASSFISH-13974)
      http://java.net/jira/browse/GLASSFISH-13974

        Activity

        Hide
        Sivakumar Thyagarajan added a comment -

        Requesting Shing Wai to investigate this further.

        Show
        Sivakumar Thyagarajan added a comment - Requesting Shing Wai to investigate this further.
        Hide
        Shing Wai Chan added a comment -

        Can you provide a unit test case for the scenario?

        Show
        Shing Wai Chan added a comment - Can you provide a unit test case for the scenario?
        Hide
        Shing Wai Chan added a comment -

        Please provide a test case for further investigation.

        Show
        Shing Wai Chan added a comment - Please provide a test case for further investigation.
        Hide
        Neil Rutherford added a comment -

        Sorry for the delay, been really busy with Family and Work.

        I've attached a simple eclipse maven project with 1 servlet and 1 request scoped bean.

        If you perform a GET or POST on the servlet it will start an async context and start a runnable which will wait for 3 seconds, set a request attribute and then do a context.dispatch() back to servlet which will try and access request scoped bean - which will throw a ContextNotActiveException.

        Show
        Neil Rutherford added a comment - Sorry for the delay, been really busy with Family and Work. I've attached a simple eclipse maven project with 1 servlet and 1 request scoped bean. If you perform a GET or POST on the servlet it will start an async context and start a runnable which will wait for 3 seconds, set a request attribute and then do a context.dispatch() back to servlet which will try and access request scoped bean - which will throw a ContextNotActiveException.
        Hide
        Sivakumar Thyagarajan added a comment -

        Shing Wai: I am able to reproduce this issue with the attached testcase. I think this may be due to a request listeners not getting called during async dispatched back to the original servlet. Could you please look into this?

        Show
        Sivakumar Thyagarajan added a comment - Shing Wai: I am able to reproduce this issue with the attached testcase. I think this may be due to a request listeners not getting called during async dispatched back to the original servlet. Could you please look into this?
        Hide
        Shing Wai Chan added a comment -

        Web container integrated with weld through org.jboss.weld.servlet.WeldListener which is a ServletRequestListener.
        There are two issues in the case of async.

        I. When should ServletRequestListener#requestDestroyed be called?
        Suppose we have a servlet A which has startAsync.
        One one hand, servlet A will continue to the end of javax.servlet.Servlet#service.
        (ii) On the other hand, the async request will continue processing.

        Is it after ? Or after (ii)? Or wait until both and (ii) are done.
        I have started the discussion in expert group.

        II. I have manually put a sleep at the end of Servlet#service in the test case.
        It still does not work.
        In WeldListener#requestInitialized, we invoke
        org.jboss.weld.context.http.HttpRequestContextImpl.active()
        which will invoke RequestScopedBeanCache.beginRequest()
        which will set a ThreadLocal list.
        The given test case start async in a different thread. Hence the data is lost and the test fails.

        So, we may like to fix (II) in weld code in this case.

        Show
        Shing Wai Chan added a comment - Web container integrated with weld through org.jboss.weld.servlet.WeldListener which is a ServletRequestListener. There are two issues in the case of async. I. When should ServletRequestListener#requestDestroyed be called? Suppose we have a servlet A which has startAsync. One one hand, servlet A will continue to the end of javax.servlet.Servlet#service. (ii) On the other hand, the async request will continue processing. Is it after ? Or after (ii)? Or wait until both and (ii) are done. I have started the discussion in expert group. II. I have manually put a sleep at the end of Servlet#service in the test case. It still does not work. In WeldListener#requestInitialized, we invoke org.jboss.weld.context.http.HttpRequestContextImpl.active() which will invoke RequestScopedBeanCache.beginRequest() which will set a ThreadLocal list. The given test case start async in a different thread. Hence the data is lost and the test fails. So, we may like to fix (II) in weld code in this case.
        Hide
        Sivakumar Thyagarajan added a comment -

        RequestScoped Beans do not seem to be available during an async invocation (running in a thread that is different from the thread that initiated the ServletRequest in the Servlet). Shingwai debugged this above and found out that this is due to the ThreadLocal usage to cache RequestScoped Beans in org.jboss.weld.context.cache.RequestScopedBeanCache.

        As per the definition of RequestScoped [3], the request scope must be active until a call to onComplete is done. So, I assume the request scoped bean must also be available in the Servlet when it was dispatched to from the async thread. I am following this up with the weld team. There already seems to be a WELD issue at https://issues.jboss.org/browse/WELD-1020

        Show
        Sivakumar Thyagarajan added a comment - RequestScoped Beans do not seem to be available during an async invocation (running in a thread that is different from the thread that initiated the ServletRequest in the Servlet). Shingwai debugged this above and found out that this is due to the ThreadLocal usage to cache RequestScoped Beans in org.jboss.weld.context.cache.RequestScopedBeanCache. As per the definition of RequestScoped [3] , the request scope must be active until a call to onComplete is done. So, I assume the request scoped bean must also be available in the Servlet when it was dispatched to from the async thread. I am following this up with the weld team. There already seems to be a WELD issue at https://issues.jboss.org/browse/WELD-1020
        Hide
        Joe Di Pol added a comment -

        Since this likely requires a new Weld integration, and it's too late in 3.1.2 for this to happen, I'm excluding from 3.1.2.

        Show
        Joe Di Pol added a comment - Since this likely requires a new Weld integration, and it's too late in 3.1.2 for this to happen, I'm excluding from 3.1.2.
        Hide
        Sivakumar Thyagarajan added a comment -

        The CDI 1.0 spec does not define this behaviour and so we can't expect this to work portably. We are discussing this with the CDI 1.1 group to clarify this in CDI 1.1 and hence this could only be fixed in Weld 2.0 builds when it becomes available. Marking this issue as weld-int-required as well.

        Show
        Sivakumar Thyagarajan added a comment - The CDI 1.0 spec does not define this behaviour and so we can't expect this to work portably. We are discussing this with the CDI 1.1 group to clarify this in CDI 1.1 and hence this could only be fixed in Weld 2.0 builds when it becomes available. Marking this issue as weld-int-required as well.
        Hide
        jjsnyder83 added a comment -

        Committed revision 59872.

        Show
        jjsnyder83 added a comment - Committed revision 59872.
        Hide
        jjsnyder83 added a comment -

        I had to make sure that the weld listener's requestInitialized method was called before the async method was called and make sure that the weld listener's requestDestroyed method was called after the async method was called. This is necessary because CDI requires that a request context is active during async method executions. Calling the Weld listener's methods sets up the request context correctly for Weld.

        Show
        jjsnyder83 added a comment - I had to make sure that the weld listener's requestInitialized method was called before the async method was called and make sure that the weld listener's requestDestroyed method was called after the async method was called. This is necessary because CDI requires that a request context is active during async method executions. Calling the Weld listener's methods sets up the request context correctly for Weld.

          People

          • Assignee:
            Shing Wai Chan
            Reporter:
            Neil Rutherford
          • Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: