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?

Hi, 



On Thu, Aug 27, 2020 at 6:06 PM Joakim Erdfelt <joakim.erdfelt@xxxxxxxxx> wrote:
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.

That sounds pretty close to what I had in mind. The existing HTTP APIs out there should likely have little issues with implementing this.



 


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.

It was indeed the idea to be like jakarta.servlet.jsp, so in the jakarta.servlet namespace.

 
Any attempt to make servlet depend on CDI will cause an immediate schism in the servlet user-base.

Don't worry, it's not about making Servlet depend on CDI in this proposal. If you're up to date with my work for Jakarta Security, it would follow the same basic design principles (in Jakarta Security, something like the HttpAuthenticationMechanism is implemented by the user as a CDI bean, and the runtime delegates it to a Jakarta Authentication ServerAuthModule (SAM).

In this case it would roughly look like this:

import jakarta.servlet.cdi.Requestlet;

@RequestScoped
@Requestlet("/foo/bar")
public class MyBean {
    
    @Get
    public void doGet(HttpServletRequest req, HttpServletResponse response) {
        // ...
    }
}

The "servlet" here is 100% a bean, meaning all usual CDI features work out of the box (interception, decoration, alternatives, vetoing, scopes, etc).

Behind the scenes, the runtime would install a bridge plain Servlet for this (error handling omitted for brevity):

public class HttpBridgeServlet extends HttpServlet {
  
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        String method = request.getMethod();

        if (method.equals("get")) {
            CDI.current().select(Requestlet.class)
                                .method(Get.class, ServletRequest.class, ServletResponse.class)
                                .invoke(request, response);
      } else { 
         // other methods
      }
    }
}

So it builds on top of the existing Servlet API, which is itself unaware of this existing. 

The idea would be to keep this as close to being a Servlet as possible, i.e. not introduce other fancy things. It's simply a Servlet still, but re-imagined as a bean.





 


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

That itself wouldn't hurt to have, as that's essentially how Servlets are made to support say @Inject today. However, being an actual (CDI) bean goes much further than that, and I think further than such API could possibly support. E.g. to start with you'd need to support interceptors on the method as well, so you could do something like:

@RequestScoped
@Requestlet("/foo/bar")public class MyBean {        
    
    @Get
    @RolesPermitted("myrole1", "myrole2") // CDI interceptor binding, not necessarily the @RolesAllowed
    public void doGet(HttpServletRequest req, HttpServletResponse response) {        
         // ...    
    }
}

But also the scoping as shown would be hard to support with a general API and the existing signature of the Servlet interface and base classes.


Big on my list: REMOVE deprecated methods and concepts. 


Big +100.

Next to alignment, this is also going to be the main topic for Jakarta Faces.

 

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.

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
  • Upgrade jakarta.servlet.http.Cookie from it's RFC2109 support to RFC6265 with SameSite behaviors. 
  • 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.
  • 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)
  • 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.
  • 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)

Interestingly, I remember this being a proprietary feature of some Servlet implementations in the early to mid 00s.
 
  • 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)

Yes, ideally, I'd even say that the programmatic API, annotations, and web.xml should all have the same features. 

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

As a Server/Servlet implementer I will indeed thank you ;) 

At the moment it looks like it's optional already though, as the spec talks about allowing to return null for getContext(...). Unfortunately the TCK does have a check for this, which I *think* is wrong.

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

I sent a mail about almost exactly this to the list some time ago. I proposed to re-use the @Priority semantics and even annotation there, but anything having the same effect will do of course.

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

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.

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!

Yes indeed. I think Greg attempted to spearhead this some time ago but I'm not sure what became of it.

Kind regards,
Arjan

 

- 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