Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [servlet-dev] Big ticket items for Servlet 6 / Jakarta EE 10?


Jan Bartel and I have had a bit of a drill down conversation into exactly how Proxy/wrapper style integration for CDI (or other similar use-cases) could work, specifically about how some of the behaviours need to be clarified and perhaps some extra listeners to make things easier.

We think we need to be a bit more specific about the behaviour of the ServletContext.createServlet/Filter/Listener methods, specially in respect to what they may return and when they can be called.

If a call to ServletContext.createServlet delegates to CDI to create/decorate the instance, then CDI might see some extra annotations on it that are interceptors and/or special scopes.   Thus the servlet it passes back might not be the actual instance of that servlet, but a wrapper/proxy object that will invoke the actual servlet - which may be a dynamically created instance (not unlike what JSP does).     So firstly we probably should javadoc that the instance returned might be proxied and that the instance that receives a service call may be different.    

 Note that the signature is public <T extends Servlet> T createServlet(Class<T> clazz), so the return type is T not Servlet, which complicates any wrapper/proxy returned. It will have to be a java.lang.reflect.Proxy or similar, but can that be used for Classes or only Interfaces?     If there was a Servlet return, it would be much simpler, but I guess it is too late to change that signature now.

Next issue is that we have to be clear that createServlet is to be used for creating the initial static instances and not any dynamic instances.   So if a CDI proxy wanted to make request or session scope instances, it would need to do it's own creation of these objects and not call ServletContext.createInstance, as it would just get back another proxy and not the dynamic instance.    Perhaps we should disable the create methods after initialization to be clear?

If the servlet returned can be a proxy, then should we be intercepting the a call to destroy and routing that to CDI release (or more correctly a release method on the pluggable object factory)?  Several options here:
  • For container created servlets, they don't actually call ServletContext.createInstance, but would call the pluggable object factory below the covers. The container could then know that it has to call a release method on the pluggable object factory at the end of the lifecycle.   Any servlet instance passed into addServlet method would not be released via the object factory, but it's destroy method would be called at the appropriate time - so if a release is to be done, then the destroy call would need to be intercepted.    This means that the object factory needs to know in which circumstance it is being called, so it knows if it has to intercept destroy or not.
  • The object factory could always intercept the destroy method and thus the container never needs to explicitly do a release, only call destroy.... which is fine for Filters and Servlets, but Listeners don't have a destroy method???
  • The object factory could never intercept the destroy method, instead the container would call an explicit release on the object factory before (or is it after) calling the destroy method.   We would need to be very explicit on which components we call release for - probably being those that are added rather than those that are created.  This means that we might release some components that were never actually created with a createMethod.  
Another issue for implementing a CDI proxy is that the scopes are not always going to be simple and/or obvious.  Examples include:
  • An request for which startAsync has been called and then dispatched to a different servlet.  When is the first servlet destroyed? when the request is complete or when it is dispatched to the other servlet?  What if the other servlet does another async cycle and dispatches back to the first servlet? is that the same instance or a different one.
  • For session scoped instances, what if a servlet invalidates and creates a new session?
Whilst I think much of these complications are for the CDI proxy to work out (and are the reasons I don't want to bake this into the container), the question is do we provide enough events/features for this to be implemented.    I think the answer is "yes but....".  We do have lots of listeners so most events can be caught and acted on.  However, just intercepting the "simple" event of the completion of a request cycle is moderately complex, because different listeners are involved for sync dispatches vs async dispatches. I often see complex code that is checking if a request is async in case it has to install an async onComplete listener.     I think there is scope for us to improve our listeners so detecting the end of request scope is much simpler.

tl;dr;  I think it is a big mistake (one which we've often made before) to assume that a new feature is simple and won't interact badly with the many other features already in the servlet container.    Even just adding a plugging object factory for a lifecycle that matches the context is difficult because of all the different ways components can be discovered/configured/injected.    Supporting our existing context lifecycle should be our primary focus.   A secondary aim should be looking at what we can improve to better support "frameworks" to simply implement other lifecycles if they so wish, but without taking on those lifecycles ourselves.




















On Wed, 16 Dec 2020 at 08:23, Greg Wilkins <gregw@xxxxxxxxxxx> wrote:


On Tue, 15 Dec 2020 at 18:57, arjan tijms <arjan.tijms@xxxxxxxxx> wrote:

Integrating web frameworks into servlets has been and will be a mistake. 

I'm not really sure which web framework would have to be integrated in servlets. There are only two in scope from the Jakarta perspective: Jakarta Faces and Jakarta MVC and neither have that need AFAIK.

From my point of view, a CDI that creates components dynamically to handle specific servlet requests is a web framework.  

When we start adding functionality to support a specific way/style to handle requests by dynamically routing them to beans, then we start integrating a specific framework responsibility into servlets.   We should support frameworks, but only generically, so when there is a new style for handling individual requests we don't have a whole CDI request scope legacy baked into the servlet container.

So I'm fully supportive of making it so that CDI can be used to create the components that Servlet needs anyway within the life cycles that the Servlet spec defines.  But I'm not supportive of making Servlets support the fine grained features that CDI needs for dynamic routing of requests to beans.

More specifically, container managed Servlets are not dynamic, they can be created during initialization but not afterwards.  CDI should be able to be used to assist with that initial creation, but if CDI wants more fine grained lifecycles for request or session, then that needs to be done within a proxy Servlet and not by dynamic registration of servlets on the container.

--


--

Back to the top