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?



On Fri, 28 Aug 2020 at 02:06, Joakim Erdfelt <joakim.erdfelt@xxxxxxxxx> wrote:
Inline ...

Warning: this is all my opinions, based on supporting servlet-api over the past 15 years.
Both as a user of the servlet-api and as an implementer / committer of a Servlet API container (Jetty).
The opinions expressed here do not represent or imply anything from the Eclipse Jetty project's point of view.

On Thu, Aug 27, 2020 at 6:53 AM arjan tijms <arjan.tijms@xxxxxxxxx> wrote:
Hi,

A number of other specs in Jakarta have provided some ideas or roadmap items for their next major versions.

Until so far I don't think Servlet has provided any such ideas, although we do have a backlog of about 112 issues, which are mostly clarifications or small enhancements.

One of the pitched themes for Jakarta EE 10 is alignment, and for this theme I'd like to make a few proposals:

1. In cooperation with Jakarta REST (previously JAX-RS), see if we can define a subset of Servlet on which Jakarta REST can conveniently depend. At the moment most Jakarta REST implementations are based on Servlet anyway, but officially there's no such dependency.

I support a low level http API, emphatically.

Here's what I view it as ...
  • No WebApp concepts - no WAR, no Annotations, no WEB-INF, no descriptors, no fragments, no ServletContext, nada.
  • Programmatic Assembly of server behavior.
  • 100% Async behaviors - no Input/Output streams, no blocking behaviors of any kind, reduced threading needs (consider Channels, or ByteBuffers, perhaps even with JDK Flow API)
  • No request or response wrapping allowed.
  • Request and Response interception behaviors allowed (similar to Tomcat valves, and Jetty I/O interceptors, but formalized)
  • No request include/forward dispatching of any kind.
The existing Servlet API should be able to be built on top of this.

I have actually been thinking about something kinda similar. A low level, flexible HTTP API that can be used as a kinda glue between different frameworks could be really useful (provided it was actually widely adopted, so we don't end up with yet another HTTP API ala https://xkcd.com/927/ ).

Servlet is not really suitable for this role so most frameworks come up with their own HTTP abstraction to work around this, and the end result is a massive amount of HTTP abstractions that all do largely the same thing.
 
That said though there are a few things that concern me about the idea:

- I think it will need a lot of buy in to be truly useful
- Low level HTTP API's are really performance sensitive. A lot of care would need to go into making sure that this was designed in a way to not limit performance.


2. For in a Jakarta EE environment, and as a separate related new spec, define a CDI version of Servlet, for instance with the proposed package jakarta.servlet.cdi. This would define what a Servlet would look like as a pure CDI bean, and in addition would finally allow us to transfer the HttpServletRequest producer that's now in core CDI itself. 

Alternatively this can also be proposed as a completely independent spec, not using the jakarta.servlet package, and just building on Servlet (like, e.g. Jakarta Faces does).

From your description, this needs to be an independent spec,
Just like jakarta.servlet.jsp.

I've rewritten this section about a dozen times, to make it seem less hostile towards CDI, but frankly, any criticism of CDI in the past has always resulted in arguments, so I'll leave my last edit.
Any attempt to make servlet depend on CDI will cause an immediate schism in the servlet user-base.
It will become ("the clean servlet-api that works with my chosen technologies" vs "the forced CDI behaviors that I never use and cause me problems with my chosen technology stack")

Talking with various cloud providers about java server installations, the rate of use of CDI vs Other injection libs is extremely skewed away from CDI. (Spring dominates, but other libs like Guice are not that far behind, and even the "other" category surpasses CDI use by a huge factor).

Now for the pro-CDI part of my comments.
The Servlet API needs a better, more formalized, Injection layer that all CDI implementations, and even other libs can use.
(Other libs: spring, guice, various metrics libraries, audit libraries, etc.)
Right now, every library needs to write a custom, server specific (sometimes even server version specific), layer to hook into the Servlet API.

I think having a "Injectors" and/or "Decorators" layer in the servlet API could prove very useful, for everyone, all projects.
But this layer has 2 different needs/behaviors.
One is the pure Injection behavior, seen often with CDI, where existing instances are just injected with more values.

CDI supports interception and decoration, it needs full control of the bean lifecyle.  To be fully spec compliant it also needs to retain additional metadata about the bean in order to destroy it (i.e. you can't just have destroy(Object instance), you need some kind of InstanceHandle that contains the information about how to destroy it.
 
The other is the Decorator behavior, where the class is potentially wrapped with another implementation prior to it being used. (often seen with metrics libraries, and behavior validation libraries, and security audit libraries, and cloud behaviors)

This API should be passed ALL objects that are involved in the Servlet API.
Servlets, Filters, Various Servlet Listeners, ServletContainerInitializers, ServletOutputStream, ServletInputStream, HttpServletRequest, HttpServletResponse, Cookie, HttpSession, HttpUpgradeHandler, WebConnection, AsyncContext, MultipartConfig, Part, etc ...

Having a single API would be ideal, but it would probably be more useful as two different APIs with a clean lifecycle.
 
Jakarta EE uses CDI as its injection mechanism. I would be very hesitant to suggest what is basically a new Servlet only mechanism.

 

Thoughts?

Anything else that's big? Any of the existing issues we'd like to prioritize for Servlet 6?

Big on my list: REMOVE deprecated methods and concepts. 

The Servlet API is old, old beyond words, and maintaining an implementation of the API is increasingly difficult due to this cruft.
This removal should have happened in Jakarta EE 9 (with the namespace change).  The namespace change was big enough that removing deprecated things could have been done easier.  But that's just my personal opinion.

Servlet has a huge installed user base. Everything we remove or change might be a small change to the spec, but to the downstream install base it represents a massive amount of man hours.

At the moment I don't don't see the deprecated methods as being a problem that justifies this amount of pain to our downstream users. If you look at the cost benefit equation IMHO the benefit of removing them is tiny, as in most cases the implementations just delegate to a new method, while the cost for any code that is still using it is huge.
 

The next big thing is to break the API (yes, I said break the API) in certain select areas.
  • Deprecate RFC2616 behaviors entirely, we are on RFC7230 for HTTP/1.1
What does this mean? The changes between these specs are at the transport level, they don't really affect Servlet.
 
  • Upgrade jakarta.servlet.http.Cookie from it's RFC2109 support to RFC6265 with SameSite behaviors. 
IMHO this is the #1 priority.
 
  • HttpServletResponse.setContentType(String) should fail if an attempt to set anything other than a pure mime-type is used. (that's what HttpServletResponse.setCharacterEncoding(String) is for.
Why? 
  • ALL HttpServletResponse methods that attempt to set/change things on the response headers after "committed" state has been reached MUST throw an exception (right now, they quietly fail, which is just awful)
 I agree that this is more correct, if we did it most implementation would provide a flag to enable the old behaviour though.
  • Attempting to change the mimetype or charset on a HttpServletResponse after obtaining the .getWriter() MUST throw an exception indicating that the change was not applied due to the active Writer (the user of the API can choose to reset the buffer, set the headers, and refetch the writer though)
  • The ability to manage the registered mime-types for the webapp.
  • Mime type registration (programmatic or descriptor) should have an optional charset that will be applied to it when used.   This has a nuance though, as it's 3 states.  undefined_by_mimetype (meaning the use of the mime-type makes no change to the response charset), forced_on_response_headers (meaning the use of the mime-type forces a charset on the response, and it shows up on the Content-Type header) , implied_by_mimetype (meaning the use of the mime-type forces a charset on the response, but it never shows up on the Content-Type header).
  • HttpServletResponse needs an abort mechanism. (connection termination in HTTP/1.1 and GO_AWAY in HTTP/2 and HTTP/3)
  • HttpServletRequest and HttpServletResponse need HTTP Trailer support.
This was added in 4.0,
  • The behaviors for the various HttpServletRequest.getParameter() APIs need a uri-query only mode (no request body content is interrogated at all) - perhaps a new API
  • Programmatic (or annotated) behaviors (at the Servlet level for HTTP methods that allow automatic parsing of parameters or parts)
  • HttpServletResponse.getWriter() should have a either have a new ServletPrintWriter that throws IOException on failures (not this old-school System.out behavior where PrintWriter.write() can silently fail and you won't know unless you ask  PrintWriter.checkError())
  • HttpServletRequest needs informational APIs to know what state the Request Body is in (undefined, InputStream, or Reader)
  • HttpServletResponse needs informational APIs to know what state the Response Body is in (undefined, OutputStream, or Writer)
  • More allowed patterns in url-pattern definitions (uri-template lvl 1 like websocket? regex? multiple globs? etc) 
This has the potential to negatively impact performance.  If you allow arbitrary regex then you can end up in a situation where all your mapping is O(n) on the number of paths in the map.
  • Container Static File serving - formalize this, we know that the "Default" servlet handles this, but how do we register more static file sources? how do we let a Filter or Servlet decide that the current request should/could serve static files?  This should be something simple like boolean HttpServletResponse.serveStaticFile(StaticContext context, String pathInContext), where true means it is being done (committed), false means the file wasn't found in that context.
  • Finish flushing out the jakarta.servlet.annotation interface to finally allow the WEB-INF/web.xml descriptor to be entirely optional. (right now, some configuration is only possible in the WEB-INF/web.xml)
  • Any existing API that uses Strings for dealing with the filesystem should be changed to use java.nio.file.Path objects (eg: jakarta.servlet.Part.write(Path)) - not java.io.File! (Path is more flexible for virtual filesystems like zip/jar archives and environments with multiple Filesystem behaviors)
  • Error page registrations need a programmatic interface.
  • Eliminate Cross Context RequestDispatcher behaviors from the spec.  Forbid it. (the server implementers will thank you)
End users won't though. It is not the easiest thing to implement, but I don't think we should be changing the spec to make life easier for implementers and harder for users that depend on the behaviour.
 
  • Response methods/classes that behave differently during INCLUDE (and FORWARD?) DispatcherType need to throw exceptions when an action they attempt is not allowed during INCLUDE dispatch (again, this silent / ignored behavior just introduces bugs and confuses the crap out of developers)
  • Ability to limit Filter introduction in FilterChain under more situations (currently limited to DispatcherType and url-pattern.  What about http methods/verbs? http version? request content-type? specific servlet packages? specific named servlets regardless of the url-pattern? etc..)
You can do this check programatically in the filter.  I think things like verb mapping would be ok, but in terms of declarative config if you add too many options the syntax you use to combine them gets very confusing.
  • Overhauled Filter ordering (right now it's near impossible to get right with the mix of descriptors, annotations, fragments, dynamic registrations, etc) - A programmatic API during initialization of some sort that allows injection at specific points in the FilterChain that the existing DynamicRegistration API doesn't support.  
  • FilterGroups and spec defined groups with a specific call ordering within the FilterChain .. (eg: security/auth, upgrade, cache, data, conversion, audit, general) - that way a filter can specify that it needs to operate at a specific point in the list of FilterGroups.  And third party libraries can register at that level properly (eg: SSO, Response Caches, Security Audit Tooling, etc).
  • HTTP Exchange Listener API - Request creation, Request dispatching, Request state changes, Response header changes, Response state changes, Request read complete, Response write complete, etc..
  • Collapse the API, eliminate the Servlet, GenericServlet layers with it's non-HTTP bits and bobs.  It's only HTTP anymore.
  • The Async I/O API from Servlet 3.1 is fine, but it's a veritable mine-field of gotchas, few projects have successfully and correctly used it (at first, most that have stuck with it over the years have finally settled into a stable codebase of their usage).  We either need that layer cleaned up, overhauled, replaced, or a more fundamental layer like above.
A lot of these are really good ideas, but we need to be careful with the compatibility aspects.
 

As far as general features that Jakarta EE should introduce that will benefit many Jakarta EE projects (and users), including the Servlet API.

We need a static bytecode metadata standard, ala Jandex or Classgraph.

This is an implementation detail, it does not really have anything to do with the spec.
 

One where the need for ASM or BCEL is eliminated during runtime, and the precomputed (at compile time) bytecode scan is used instead. (from data present in a META-INF directory).
This would eliminate the multiple jar file scanning that occurs now, and standardize it across all of Jakarta EE.
And it would also ease the support when the JVM is on the module-path and the restrictions that the JVM places on the application (and containers) at that point.
It would also speed up initialization greatly!

This is what Quarkus does, but I don't think it is something that belongs in the spec. Any spec defined format here would actually slow us down as we use recorded bytecode (basically directly store the bytecode instructions to create the Servlet deployment), while any spec defined thing would require an intermediate data format and as such would be step backwards for us.

Stuart
 

- Joakim
_______________________________________________
servlet-dev mailing list
servlet-dev@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/servlet-dev

Back to the top