I forgot to add that while an application running on top of a Servlet
container that provides (2) can easily achieve (1),
it does not work the other way around and that's why anything using
ThreadLocals won't work on Tomcat.
Before moving further and proposing additional callbacks in the Servlet
API I would appreciate feedback from other EG members on how
ServletRequestListeners were originally designed to work with async
Servlets.
Thanks,
Jozef
On 12/16/2014 11:25 AM, Mark Thomas wrote:
> On 16/12/2014 09:19, Jozef Hartinger wrote:
>> Dear Servlet experts,
>>
>> the behavior of *javax.servlet.ServletRequestListener* seems to be
>> underspecified in Servlet 3.1 especially in case of asynchronous
>> requests. I am seeking clarification for the case that follows.
> Thanks for the clear summary of the problem.
>
>> There two properties of ServletRequestListener invocation that a Servlet
>> implementation may want to have:
>>
>> 1) requestInitialized()/requestDestroyed() are only called once per request
>>
>> This one simply says that the pair of
>> requestInitialized()/requestDestroyed() methods of a given listener is
>> not called more than once per a given request.
>>
>> 2) Symmetry with respect to the calling thread
>>
>> This property says that if the
>> ServletRequestListener.requestInitialized() is called by a given thread
>> (T1) then the corresponding ServletRequestListener.requestDestroyed()
>> invocation is performed by the same thread (T1). This second property is
>> very important for integration with various other
>> frameworks/technologies as it allows them to set a ThreadLocal in
>> requestInitialized() and remove it in requestDestroyed()
>>
>>
>> Both these properties are achievable at the same time for synchronous
>> Servlets. However, for asynchronous Servlets these properties are
>> mutually exclusive. Most Servlet containers deal with this by
>> sacrificing property (1) and only guarantee (2).
> I can see valid uses cases for both (1) and (2).
>
> Based on the event names (which is why Tomcat behaves as it does
> currently) I think that the current events map to property (1).
>
> I don't believe that any current listener events map directly to (2)
> which would mean new events and possibly a new listener although we
> could use default methods to avoid creating a new interface.
>
> We could add the following to ServletRequestListener:
>
> default public void containerThreadBind() {
> }
>
> default public void containerThreadUnbind() {
> }
>
>
> With these methods added, the requestInitialized() and
> requestDestroyed() would get called as per the current Tomcat
> implementation and containerThreadBind() and containerThreadUnbind()
> would get called as per the current Undertow / JBoss Web / Jetty /
> Grizzly implementation of requestInitialized() and requestDestroyed().
>
> Would this meet your requirement?
>
> Mark
>
>> An example follows. Suppose we have a simple asynchronous servlet that:
>>
>> A1) Does initial processing of the request
>> A2) Goes async (req.startAsync())
>> A3) Spawns a new thread T that calculates something
>> A4) The servlet exits
>>
>> B1) The spawn thread T calculates the value
>> B2) Once the thread T finishes calculation, it dispatches back to
>> Servlet container for rendering (e.g.
>> ctx.dispatch("/asyncFinish?value=calculatedValue"))
>>
>> C1) Servlet container creates response and completes the request
>>
>> A1-A4 steps are performed by thread A that initially handles the
>> request. B1 and B2 steps are performed by thread B spawned by a Servlet,
>> C1 step is performed again by a Servlet container thread.
>>
>> Now the question arises: When and how should ServletRequestListener
>> callbacks be invoked?
>>
>> Servlet containers that guarantee second of aforementioned properties,
>> symmetry of the calling thread, do the following:
>>
>> Thread A calls ServletRequestListener.requestInitialized()
>> A1, A2, A3 and A4 are performed
>> Thread A calls ServletRequestListener.requestDestroyed()
>>
>> When the value is calculated by thread B and the request is dispatched
>> to the Servlet container again:
>>
>> Thread C calls ServletRequestListener.requestInitialized()
>> C1 is performed
>> Thread C calls ServletRequestListener.requestDestroyed()
>>
>> Implementation such as this one:
>> sacrifices property (1) - the pair of ServletRequestListener methods is
>> called more than once for a given request - each time request is being
>> worked on by Servlet container
>> guarantees property (2) - symmetry of the calling thread - allows
>> frameworks that depend on ThreadLocal to use ServletRequestListeners as
>> reliable hooks
>>
>> Undertow, JBoss Web, Jetty and Grizzly all do this.
>>
>> Tomcat [1] uses an alternative implementation which simply does the
>> following:
>>
>> 1) Thread A calls ServletRequestListener.requestInitialized()
>> 2) the entire request is processed in threads A, B and C
>> 3) Thread C calls ServletRequestListener.requestDestroyed()
>>
>> This implementation:
>> guarantees property (1) - the pair of callbacks only called once
>> sacrifices property (2) - as a result, a ThreadLocal set by
>> ServletRequestListener.requestInitialized() will probably not be cleaned
>> up in the corresponding ServletRequestListener.requestDestroyed() call
>> as it is invoked in a different thread - making frameworks that work on
>> all the other implementations leak on Tomcat
>>
>> I do not find the specification clear enough to tell which of the two
>> options is the correct one. I found the following mentions in the
>> specification:
>>
>> Table 11-3
>> A servlet request has started being processed by Web components. -
>> javax.servlet.ServletRequestListener
>> Javadoc:
>> A ServletRequest is defined as coming into scope of a web application
>> when it is about to enter the first servlet or filter of the web
>> application, and as going out of scope as it exits the last servlet or
>> the first filter in the chain.
>>
>> Which all leave room for interpretation.
>>
>> The fact that two very different implementations of this behavior exist
>> significantly reduces the usability of ServletRequestListener API by
>> frameworks. I am therefore asking you for clarification of which one of
>> these two implementations is correct and/or matches the best the
>> original intention of specification authors and should be prefered.
>>
>> Regards,
>>
>> Jozef Hartinger
>>
>> [1] https://issues.apache.org/bugzilla/show_bug.cgi?id=57314
>>