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.
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).
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