users@servlet-spec.java.net

[servlet-spec users] Re: Clarification of ServletRequestListener

From: Mark Thomas <markt_at_apache.org>
Date: Tue, 16 Dec 2014 10:25:37 +0000

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
>