Rossen,
Yes I am saying that an asynchronous thread should not call some
request methods because their values may change.
If ContextX.ServletA forwards to ContextX.ServletB, then the servlet
path and path info of the request will be changed while the handling
thread is in ContextX.ServletB. The getRequestDispatcher return will
also change for relative paths.
If ContextX.ServletA forwards to ContextY.ServletC, then the context
path and session methods will also give different results while the
request is in ServletC
So an asynchronous thread that is calling any of these methods may see
different results depending on the exactly timing that occurs. The
request may even be forwarded again before it has returned from the
initial dispatch.
An async thread should be able to access the input stream and output
stream safely, but my experience is that there are lots of bugs
lurking in all the containers with such access - specially for input.
My advice if you want portable safe code is to never interact with
the request object from any asynchronous thread. Writing to the
response is OK, but any request methods are dubious at best. Also
code that passes references to requests to async threads is often
vulnerable to using a recycled request object unless they are really
rigorous with their event listeners. Asynchronous threads should do
an asyncContext.dispatch and perform request handling from a container
thread in a servlet context.
cheers
On 31 May 2012 00:21, Rossen Stoyanchev <rstoyanchev_at_vmware.com> wrote:
> Greg,
>
> Thanks for your explanation.
>
> Just to be sure we're talking about the same things, by "current context" I assume you mean request properties such as requestURI, pathInfo and others that may vary before and after a forward and may for example produce different results when a relative URL is used. Also by "dispatch from ContextA to ContextB" I assume you mean any kind of dispatch such as forward, include, or an AsyncContext dispatch. Please correct me if not so.
>
> I've created a project to experiment with the scenario you mentioned (full source at [1]):
>
> 1. Servlet A forwards to Servlet B
> 2. Servlet B calls request.startAsync and starts a new (async) thread
> 3. Thread gets RequestDispatcher and forwards to a JSP
>
> What I found out is in Tomcat, Glassfish, and JBoss, the async thread has no trouble seeing the requestURI and the pathInfo of ServletB, which is what I expected since that's where the AsyncContext was created. I didn't check how all implementations work but at least in Tomcat it seems the request in ServletB is wrapped and all the forwarding context is private to it and hence should not be affected when the stack unwinds. I'm not quite sure if the timing issues you're referring to are specific to Jetty's implementation or not?
>
> That said only JBoss succeeded in actually rendering the JSP. Tomcat and Glassfish failed with exceptions because the response had been committed. This might be a bug where the initial forward commits the response even though the an async request was started. I'm not sure but I plan to file bug reports.
>
> Even more surprisingly if (3) above is changed to dispatch through the AsyncContext, no containers worked besides Jetty. Tomcat and Glassfish failed in the same way (again I suspect a bug). Not sure why JBoss failed, I didn't see any log output.
>
> Do I understand correctly, an async thread should not use some properties of the request? You even mentioned that getSession should not be used? If this is true then it that's a pretty major gap in the spec! Does the spec have a perspective on this?
>
> Regards,
> Rossen
>
> [1] https://github.com/rstoyanchev/dispatch-test
>
>
> ----- Original Message -----
>> From: "Greg Wilkins" <gregw_at_intalio.com>
>> To: users_at_servlet-spec.java.net
>> Sent: Wednesday, May 30, 2012 6:15:50 AM
>> Subject: [servlet-spec users] Re: Getting a RequestDispatcher from an async thread
>>
>> Rossen,
>>
>> the reason why jetty returns null, is that getRequestDispatcher is a
>> method that is relative to the current context that is handling the
>> request and this is only known inside the scope of a call to a
>> context.
>>
>> Consider a request that is dispatched from ContextA to ContextB and
>> that while in the a servlet inside ContextB, async is started and the
>> method returns.
>>
>> While still in the ContextB servlet, a call to
>> request.getRequestDispatcher should be possible and should return
>> results relative to ContextB
>> but when returned from the dispatch to ContextB we are back in a
>> filter/servlet in ContextA, then a call to getRequestDispatcher
>> should
>> be returned relative to ContextA
>>
>> But if we let an async thread call getRequestDispatcher after async
>> is
>> started, then we have a race condition. Depending on the timing of
>> the call, the method will return a result relative to ContextB, then
>> ContextA and then undefined. Even if we define which context it
>> should be relative to, then the value will still be changing and we
>> still have a race condition. This get's even more confusing as we can
>> have a request that has been dispatched to ContextC by an async
>> thread, so then before the dispatch is actually done, should the
>> method be relative to the suspending Context or the destination
>> context?
>>
>> Any request method that is dependent on context (even getSession)
>> should not be called from an async thread because the context of a
>> request is a volatile attribute of the request. Some containers may
>> return values, and they may even be correct for most situations, but
>> they are intrinsically not safe.
>>
>> cheers
>>