Category: Jakarta

  • End of Life: Changes to Eclipse Jetty and CometD

    Webtide (https://webtide.com) is the company behind the open-source Jetty and CometD projects. Since 2006, Webtide has fully funded the Jetty and CometD projects through services and support, including migration assistance, production support, developer assistance, and CVE resolution. 

    First, the change.

    Starting January 1, 2026, Webtide will no longer publish releases for Jetty 9, Jetty 10, and Jetty 11, as well as CometD 5, 6, and 7 to Maven Central or other public repositories. 

    Take a look at the primary announcement if you’re interested. 

    So, the motivation.

    Why we are in this situation now harks back to the beginnings of Webtide. Briefly, Greg Wilkins founded the Jetty project in 1995 as part of a contest created by Sun Microsystems for a new language called Java. For a decade, he and Jan Bartel carefully stewarded the project as part of their consulting company Mort Bay Consulting. Around the Jetty 6 timeframe, in 2006, Webtide was founded as an LLC to evolve the project further commercially. Still, at its core, the goal was to support the incredible community that had developed over the years. When I joined in 2007, we began working to join the Eclipse Foundation. We took steps to formalize our development processes, aiming to add more commercial predictability to the open-source project. Joining the Eclipse Foundation also meant adhering to their rigorous IP policy for both the Jetty codebase and its dependencies, an essential step in improving corporate uptake.

    This was also the time for the project to handle the end-of-life process for Jetty 6, while establishing Jetty 7 and Jetty 8. This was the opportunity that Webtide needed to support the project’s development by offering commercial services and support for EOL Jetty 6, while focusing on supporting and funding the future of Jetty 7 and Jetty 8. 

    It was the crux; after careful consideration, we decided that all commercial support releases would be open-source for the benefit of all. While not a traditional business decision, it aligned with our values and dedication to the community, which was rewarded as the community continued to grow its usage of Jetty.

    This worked wonderfully for almost 20 years.

    Something shifted…

    We started to notice a shift in the community a few years ago. For almost 20 years, the companies we spoke with valued how our support could help them become more successful, with many ultimately becoming customers who truly understood the benefits of supporting open-source. Every single one of them saw the value in releasing EOL releases freely. When I became CEO a decade ago and Webtide became 100% developer-owned and operated, we were able to continue operating in this commercial environment with ease, to such an extent that the future of Webtide and the Jetty project is assured for many years to come.

    So what changed? The tone of many companies we spoke to. Increasingly, while explaining the model that served Webtide well for so many years, where I used to hear ‘That makes so much sense, this works great!’, I now hear “So it’s just free? Great, I need to check a box.” Followed up with the galling question “Could you put this policy of yours in writing on your company letterhead?”.

    And today?

    Twenty years ago, things were different; Maven 2 dominance was emerging, and Maven Central was gaining ubiquity. Managing transitive dependencies was novel in many circles. Managing CVEs in a corporate setting was in its infancy, particularly with Java developer software stacks. 

    Now, build tooling is diverse, Maven Central is a global central repository system, and corporations should have their own caching repository servers, or they really should! Even JavaEE was rebranded as Jakarta at the Eclipse Foundation. So much change, but the one I’ll highlight is the emergence of business units focused on corporate software policies, complete with BOM files containing ever more metadata and checkboxes to click, managing CVE risks associated with software developed internally. Developers, the primary people Webtide has interacted with over the years, are increasingly far removed from software maintenance activities. 

    Now our approach to endlessly updating EOL releases seems remarkably outdated. Look at Jetty 9, which we have been releasing since 2013. It turns out our approach of making things as easy as possible for the community, for software that should have officially gone EOL years ago, was a benefit to many, but also enabled far more to grow complacent. Instead of scheduling migrations and updating to more recent versions, we inadvertently provided an environment that allowed companies to deploy onto software well over a decade old, when newer, more performant options were readily available. Then, when security postures started changing and businesses began looking deeper into their dependencies, they realized they were using outdated software, three or more major versions behind. Then, to our shock, many are perfectly fine with that so long as it is free and someone tells them it is ok. 

    If we have learned one thing within this time, it is that the EOL policy needs to be so much clearer, using established industry terminology. Looking back, we have been guilty of inventing terminology and inadvertently exacerbating the situation.

    What is heartening is seeing other organizations work to address EOL as well; notably, MITRE has been developing changes to the CVE system to support EOL concepts fully. If you have ever seen the text “Unsupported When Assigned” in a CVE, then you have encountered the early efforts for EOL in a CVE.

    You have to applaud the efforts of businesses to prioritize security and sane open-source policies.

    However, this is also a call to open-source projects like Jetty, as we are operating in a different world. Everyone understands that ‘End-of-Life’ does not mean ‘End-of-Use’. Clearly, the system for many companies has changed from a Developer Support perspective to a Security Support perspective. EOL Software support is purchased differently now. There are companies, like Sonar (formerly Tidelift), that exist to manage security metadata about open-source software, enabling companies to manage their software risk more effectively. 

    EOL Jetty and CometD by Webtide

    To address this industry evolution, Webtide has launched a partnership program that enables businesses relying on EOL Jetty and CometD versions to obtain CVE resolutions officially and predictably. 

    Webtide continues to resolve CVEs and issues for EOL Jetty and CometD in support of our commercial customers. However, the resulting binaries are now distributed directly to our commercial support customers and through our partnership network. No longer are we calling software EOL but deploying to Maven Central with a nod and a wink.

    Our partners are established leaders in the open-source EOL landscape, creating products that directly address the problems the security and business industries are facing. 

    This synergy works perfectly with Webtide, as we are the company that offers services and support on Jetty and CometD. Migrations, developer assistance, production support, and performance are the things that directly influence the ongoing development of the open-source projects we steward. We can continue to focus on our strengths, and our partners can focus on theirs.

    At last, the partners!

    We are pleased to announce two partnerships. With these partners, you will be able to build a secure EOL solution for your software stack, not just for your usage of Jetty or CometD. Best yet, if you are interested in Webtide’s Lifecycle Support, you can use these partner versions in conjunction with our support!


    TuxCare secures the open-source software the world builds on. Today, we protect over 1.2 million workloads – keeping them secure, compliant, and unstoppable at scale. From operating systems to development libraries and production applications, we power your open-source stack with enterprise-grade security and support, including endless lifecycle extensions for out-of-support software versions, rebootless patching for every major Linux distribution, enterprise-optimized support for community Linux, and our Linux-first vulnerability scanner that cuts through the noise.


    HeroDevs provides secure, long-term maintenance for open-source frameworks that have reached End-of-Life. Through our Never-Ending Support (NES) initiative, we deliver continuous CVE remediation and compliance-grade updates, allowing your team to migrate at your own pace. Our engineers monitor upstream changes, backport verified fixes, and publish fully tested binaries for seamless drop-in replacement. With NES for Jetty and NES for CometD, you can stay secure, stable, and compliant—without refactoring or rushing a migration.

    If your business is interested in our partner program, please direct inquiries to partnership@webtide.com.

    Wrapping it up.

    One important thing to note is that Webtide will continue to support the Jetty Project with a standard open-source release process, ensuring that older versions are released to provide the community with ample time to update to newer versions through a transition period. When Jetty 13 is released, Jetty 12.1 will continue to receive updates for a period, just as Jetty 12.0 does currently. If that is six months or a year, it remains to be seen. Once we finalize this release strategy with timelines, we will make sure the community is well-informed.

    Fundamentally, the change coming is that the End of Life versions for Jetty and CometD will no longer be an empty EOL notice and quiet deployments to Maven Central. It will mean EOL and provide established industry solutions to address those who need additional support.

  • Back to the Future with Cross-Context Dispatch

    Cross-Context Dispatch reintroduced to Jetty-12

    With the release of Jetty 12.0.8, we’re excited to announce the (re)implementation of a somewhat maligned and deprecated feature: Cross-Context Dispatch. This feature, while having been part of the Servlet specification for many years, has seen varied levels of use and support. Its re-introduction in Jetty 12.0.8, however, marks a significant step forward in our commitment to supporting the diverse needs of our users, especially those with complex legacy and modern web applications.

    Understanding Cross-Context Dispatch

    Cross-Context Dispatch allows a web application to forward requests to or include responses from another web application within the same Jetty server. Although it has been available as part of the Servlet specification for an extended period, it was deemed optional with Servlet 6.0 of EE10, reflecting its status as a somewhat niche feature.

    Initially, Jetty 12 moved away from supporting Cross-Context Dispatch, driven by a desire to simplify the server architecture amidst substantial changes, including support for multiple environments (EE8, EE9, and EE10). These updates mean Jetty can now deploy web applications using either the javax namespace (EE8) or the jakarta namespace (EE9 and EE10), all using the latest optimized jetty core implementations of HTTP: v1, v2 or v3.

    Reintroducing Cross-Context Dispatch

    The decision to reintegrate Cross-Context Dispatch in Jetty 12.0.8 was influenced significantly by the needs of our commercial clients, some who still leveraging this feature in their legacy applications. Our commitment to supporting our clients’ requirements, including the need to maintain and extend legacy systems, remains a top priority.

    One of the standout features of the newly implemented Cross-Context Dispatch is its ability to bridge applications across different environments. This means a web application based on the javax namespace (EE8) can now dispatch requests to, or include responses from, a web application based on the jakarta namespace (EE9 or EE10). This functionality opens up new pathways for integrating legacy applications with newer, modern systems.

    Looking Ahead

    The reintroduction of Cross-Context Dispatch in Jetty 12.0.8 is more than just a nod to legacy systems; it can be used as a bridge to the future of Java web development. By allowing for seamless interactions between applications across different Servlet environments, Jetty-12 opens the possibility of incremental migration away from legacy web applications.

  • New Jetty 12 Maven Coordinates

    Now that Jetty 12.0.1 is released to Maven Central, we’ve started to get a few questions about where some artifacts are, or when we intend to release them (as folks cannot find them).

    Things have change with Jetty, starting with the 12.0.0 release.

    First, is that our historical versioning of <servlet_support>.<major>.<minor> is no longer being used.

    With Jetty 12, we are now using a more traditional <major>.<minor>.<patch> versioning scheme for the first time.

    Also new in Jetty 12 is that the Servlet layer has been separated away from the Jetty Core layer.

    The Servlet layer has been moved to the new Environments concept introduced with Jetty 12.

    EnvironmentJakarta EEServletJakarta NamespaceJetty GroupID
    ee8EE84javax.servletorg.eclipse.jetty.ee8
    ee9EE95jakarta.servletorg.eclipse.jetty.ee9
    ee10EE106jakarta.servletorg.eclipse.jetty.ee10
    Jetty Environments

    This means the old Servlet specific artifacts have been moved to environment specific locations both in terms of Java namespace and also their Maven Coordinates.

    Example:

    Jetty 11 – Using Servlet 5
    Maven Coord: org.eclipse.jetty:jetty-servlet
    Java Class: org.eclipse.jetty.servlet.ServletContextHandler

    Jetty 12 – Using Servlet 6
    Maven Coord: org.eclipse.jetty.ee10:jetty-ee10-servlet
    Java Class: org.eclipse.jetty.ee10.servlet.ServletContextHandler

    We have a migration document which lists all of the migrated locations from Jetty 11 to Jetty 12.

    This new versioning and environment features built into Jetty means that new major versions of Jetty are not as common as they have been in the past.




  • Eclipse Jetty Servlet Survey

    This short 5-minute survey is being presented to the Eclipse Jetty user community to validate conjecture the Jetty developers have for how users will leverage JakartaEE servlets and the Jetty project. We have some features we are gauging interest in before supporting in Jetty 12 and your responses will help shape its forthcoming release.

    We will summarize results in a future blog.

  • Less is More? Evolving the Servlet API!

    With the release of the Servlet API 5.0 as part of Eclipse Jakarta EE 9.0 the standardization process has completed its move from the now-defunct Java Community Process (JCP) to being fully open source at the Eclipse Foundation, including the new Eclipse EE Specification Process (JESP) and the transition of the APIs from the javax.* to the jakarta.* namespace.  The move represents a huge amount of work from many parties, but ultimately it was all meta work, in that Servlet 5.0 API is identical to the 4.0 API in all regards but name, licenses, and process, i.e. nothing functional has changed.

    But now with the transition behind us, the Servlet API project is now free to develop the standard into a 5.1 or 6.0 release.  So in this blog, I will put forward my ideas for how we should evolve the Servlet specification, specifically that I think that before we add new features to the API, it is time to remove some.

    Backward Compatibility

    Version 1.0  was created in 1997 and it is amazing that over 2 decades later, a Servlet written against that version should still run in the very latest EE container.  So why with such a great backward compatible record should we even contemplate introducing breaking changes to future Servlet API specification?  Let’s consider some of the reasons that a developer might choose to use EE Servlets over other available technologies:

    Performance
    Not all web applications need high performance and when they do, it is seldom the Servlet container itself that is the bottleneck.   Yet pure performance remains a key selection criteria for containers as developers either wish to have the future possibility of high request rates or need every spare cycle available to help their application meet an acceptable quality of service. Also there is the environmental impact of the carbon foot print of unnecessary cycles wasted in the trillion upon trillions of HTTP requests executed.   Thus application containers always compete on performance, but unfortunately many of the features added over the years have had detrimental affects to over-all performance as they often break the “No Taxation without Representation” principle: that there should not be a cost for all requests for a feature only used by <1%.
    Features
    Developers seek to have the current best practice features available in their container.   This may be as simple as changing from byte[] to ByteBuffers or Collections, or it may be more fundamental integration of things such as dependency injection, coding by convention, asynchronous, reactive, etc.  The specification has done a reasonable job supporting such features over the years, but mistakes have been made and some features now clash, causing ambiguity and complexity. Ultimately feature integration can be an N2 problem, so reducing or simplifying existing features can greatly reduce the complexity of introducing new features.
    Portability
    The availability of multiple implementations of the Servlet specification is a key selling point.  However the very same issues of poor integration of many features has resulted in too many dark corners of the specification where the expected behavior of a container is simply not defined, so portability is by no means guaranteed.   Too often we find ourselves needing to be bug-for-bug compatible with other implementations rather than following the actual specification.
    Familiarity
    Any radical departure from the core Servlet API will force developers away from what  they know and to evaluate alternatives.  But there are many non core features in the API and this blog will make the case that there are some features which can can be removed and/or simplified without hardly being noticed by the bulk of applications.  My aim with this blog is that your typical Servlet developer will think: “why is he making such a big fuss about something I didn’t know was there”, whilst your typical Servlet container implementer will think “Exactly! that feature is such a PITA!!!”.

    If the Servlet API is to continue to be relevant, then it needs to be able to compete with start-of-the-art HTTP servers that do not support decades of EE legacy.  Legacy can be both a strength and a weakness, and I believe now is the time to focus on the former.  The namespace break from java.* to jakarta.* has already introduced a discontinuity in backward compatibility.   Keeping 5.0 identical in all but name to 4.0 was the right thing to do to support automatic porting of applications.  However, it has also given developers a reason to consider alternatives, so now is the time to act to ensure that Servlet 6.0 a good basis for the future of EE Servlets.

    Getting Cross about Cross-Context Dispatch

    Let’s just all agree upfront, without going into the details, that cross-context dispatch is a bad thing. For the purposes of the rest of this blog, I’m ignoring the many issues of cross-context dispatch.  I’ll just say that every issue I will discuss below becomes even more complex when cross-context dispatch is considered, as it introduces: additional class loaders; different session values in the same session ID space; different authentication realms; authorization bypass. Don’t even get me started on the needless mind-bending complexities of a context that forwards to another then forwards back to the original…

    Modern web applications are now often broken up into many microservices, so the concept of one webapp invoking another is not in itself bad, but the idea of those services being co-located in the same container instance is not very general nor flexible assumption. By all means, the Servlet API should support a mechanism to forward or include other resources, but ideally, this should be done in a way that works equally for co-resident, co-located, and remote resources.

    So let’s just assume cross-context dispatch is already dead.

    Exclude Include

    The concept of including another resource in a response should be straight forward, but the specification of RequestDispatcher.include(...) is just bizarre!

    @WebServlet(urlPatterns = {"/servletA/*"})
    public static class ServletA extends HttpServlet
    {
        @Override protected void doGet(HttpServletRequest request,
                                       HttpServletResponse response) throws IOException
        {
            request.getRequestDispatcher("/servletB/infoB").include(request, response);
        }
    }

    The ServletA above includes ServletB in its response.  However, whilst within ServletB any calls to getServletPath() or getPathInfo(),will still return the original values used to call ServletA, rather than the “/servletB” or “/infoB”  values for the target Servlet (as is done for a call to  forward(...)).  Instead the container must set an ever-growing list of Request attributes to describe the target of the include and any non trivial Servlet that acts on the actual URI path must do something like:

    public boolean doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String servletPath;
        String pathInfo;
        if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null)
        {
            servletPath = (String)
                request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
            pathInfo = (String)
                request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
        }
        else
        {
            servletPath = request.getServletPath();
            pathInfo = request.getPathInfo();
        }
        String pathInContext = URIUtil.addPaths(servletPath, pathInfo);
        // ...
    }

    Most Servlets do not do this, so they are unable to be correctly be the target of an include.  For the Servlets that do correctly check, they are more often than not wasting CPU cycles needlessly for the vast majority of requests that are not included.

    Meanwhile,  the container itself must set (and then reset) at least 5 attributes, just in case the target resource might lookup one of them. Furthermore, the container must disable most of the APIs on the response object during an include, to prevent the included resource from setting the headers. So the included Servlet must be trusted to know that it is being included in order to serve the correct resource, but is then not trusted to not call APIs that are inconsistent with that knowledge. Servlets should not need to know the details of how they were invoked in order to generate a response. They should just use the paths and parameters of the request passed to them to generate a response, regardless of how that response will be used.

    Ultimately, there is no need for an include API given that the specification already has a reasonable forward mechanism that supports wrapping. The ability to include one resource in the response of another can be provided with a basic wrapper around the response:

    @WebServlet(urlPatterns = {"/servletA/*"})
    public static class ServletA extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request,
                             HttpServletResponse response) throws IOException
        {
            request.getRequestDispatcher("/servletB/infoB")
                .forward(request, new IncludeResponseWrapper(response));
        }
    }

    Such a response wrapper could also do useful things like ensuring the included content-type is correct and better dealing with error conditions rather than ignoring an attempt to send a 500 status. To assist with porting, the include can be deprecated it’s implementation replaced with a request wrapper that reinstates the deprecated request attributes:

    @Deprecated
    default void include(ServletRequest request, ServletResponse response)
        throws ServletException, IOException
    {
        forward(new Servlet5IncludeAttributesRequestWrapper(request),
                new IncludeResponseWrapper(response));
    }

    Dispatch the DispatcherType

    The inclusion of the method Request.getDispatcherType()in the Servlet API is almost an admission of defeat that the specification got it wrong in so many ways that required a Servlet to know how and/or why it is being invoked in order to function correctly. Why must a Servlet know its DispatcherType? Probably so it knows it has to check the attributes for the corresponding values? But what if an error page is generated asynchronously by including a resource that forwards to another? In such a pathological case, the request will contain attributes for ERROR, ASYNC, and FORWARD, yet the type will just be FORWARD.

    The concept of DispatcherType should be deprecated and it should always return REQUEST.  Backward compatibility can be supported by optionally applying a wrapper that determines the deprecated DispatcherType only if the method is called.

    Unravelling Wrappers

    A key feature that really needs to be revised is 6.2.2 Wrapping Requests and Responses, introduced in Servlet 2.3. The core concept of wrappers is sound, but the requirement of Wrapper Object Identity (see Object Identity Crisis below) has significant impacts. But first let’s look at a simple example of a request wrapper:

    public static class ForcedUserRequest extends HttpServletRequestWrapper
    {
        private final Principal forcedUser;
        public ForcedUserRequest(HttpServletRequest request, Principal forcedUser)
        {
            super(request);
            this.forcedUser = forcedUser;
        }
        @Override
        public Principal getUserPrincipal()
        {
            return forcedUser;
        }
        @Override
        public boolean isUserInRole(String role)
        {
            return forcedUser.getName().equals(role);
        }
    }

    This request wrapper overrides the existing getUserPrincipal() and isUserInRole(String)methods to forced user identity.  This wrapper can be applied in a filter or in a Servlet as follows:

    @WebServlet(urlPatterns = {"/servletA/*"})
    public static class ServletA extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
        {
            request.getServletContext()
                .getRequestDispatcher("/servletB" + req.getPathInfo())
                .forward(new ForcedUserRequest(req, new UserPrincipal("admin")),
                         response);
        }
    }

    Such wrapping is an established pattern in many APIs and is mostly without significant problems. For Servlets there are some issues: it should be better documented if  the wrapped user identity is propagated if ServletB makes any EE calls (I think no?);  some APIs have become too complex to sensibly wrap (e.g HttpInputStream with non-blocking IO). But even with these issues, there are good safe usages for this wrapping to override existing methods.

    Object Identity Crisis!

    The Servlet specification allows for wrappers to do more than just override existing methods! In 6.2.2, the specification says that:

    “… the developer not only has the ability to override existing methods on the request and response objects, but to provide new API… “

    So the example above could introduce new API to access the original user principal:

    public static class ForcedUserRequest extends HttpServletRequestWrapper
    {
        // ... getUserPrincipal & isUserInRole as above
        public Principal getOriginalUserPrincipal()
        {
            return super.getUserPrincipal();
        }
        public boolean isOriginalUserInRole(String role)
        {
            return super.isUserInRole(role);
        }
    }
    

    In order for targets to be able to use these new APIs then they must be able to downcast the passed request/response to the known wrapper type:

    @WebServlet(urlPatterns = {"/servletB/*"})
    public static class ServletB extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
        {
            MyWrappedRequest myr = (MyWrappedRequest)req;
            resp.getWriter().printf("user=%s orig=%s wasAdmin=%b%n",
                req.getUserPrincipal(),
                myr.getOriginalUserPrincipal(),
                myr.isOriginalUserInRole("admin"));
        }
    }

    This downcast will only work if the wrapped object is passed through the container without any further wrapping, thus the specification requires “wrapper object identity”:

    … the container must ensure that the request and response object that it passes to the next entity in the filter chain, or to the target web resource if the filter was the last in the chain, is the same object that was passed into the doFilter method by the calling filter. The same requirement of wrapper object identity applies to the calls from a Servlet or a filter to RequestDispatcher.forward  or  RequestDispatcher.include, when the caller wraps the request or response objects.

    This “wrapper object identity” requirement means that the container is unable to itself wrap requests and responses as they are passed to filters and servlets. This restriction has, directly and indirectly, a huge impact on the complexity, efficiency, and correctness of Servlet container implementations, all for very dubious and redundant benefits:

    Bad Software Components
    In the example of ServletB above, it is a very bad software component as it cannot be invoked simply by respecting the signature of its methods. The caller must have a priori knowledge that the passed request will be downcast and any other caller will be met with a ClassCastException. This defeats the whole point of an API specification like Servlets, which is to define good software components that can be variously assembled according to their API contracts.
    No Multiple Concerns
    It is not possible for multiple concerns to wrap request/responses. If another filter applies its own wrappers, then the downcast will fail. The requirement for “wrapper object identity” requires the application developer to have total control over all aspects of the application, which can be difficult with discovered web fragments and ServletContainerInitializers.
    Mutable Requests
    By far the biggest impact of “wrapper object identity” is that it forces requests to be mutable! Since the container is not allowed to do its own wrapping within RequestDispatcher.forward(...) then the container must make the original request object mutable so that it changes the value returned from getServletPath() to reflect the target of the dispatch.  It is this impact that has significant impacts on complexity, efficiency, and correctness:

    • Mutating the underlying request makes the example implementation of isOriginalUserInRole(String) incorrect because it calls super.isUserInRole(String) whose result can be mutated if the target Servlet has a run-as configuration.  Thus this method will inadvertently return the target rather than the original role.
    • There is the occasional need for a target Servlet to know details of the original request (often for debugging), but the original request can mutate so it cannot be used. Instead, an ever-growing list of Request attributes that must be set and then cleared on the original request attributes, just in case of the small chance that the target will need one of them.  A trivial forward of a request can thus require at least 12 Map operations just to make available the original state, even though it is very seldom required. Also, some aspects of the event history of a request are not recoverable from the attributes: the isUserInRolemethod; the original target of an include that does another include.
    • Mutable requests cannot be safely passed to asynchronous processes, because there will be a race between the other thread call to a request method and any mutations required as the request propagates through the Servlet container (see the “Off to the Races” example below).  As a result, asynchronous applications SHOULD copy all the values from the request that they MIGHT later need…. or more often than not they don’t, and many work by good luck, but may fail if timing on the server changes.
    • Using immutable objects can have significant benefits by allowing the JVM optimizer and GC to have knowledge that field values will not change.   By forcing the containers to use mutable request implementations, the specification removes the opportunity to access these benefits. Worse still, the complexity of the resulting request object makes them rather heavy weight and thus they are often recycled in object pools to save on the cost of creation. Such pooled objects used in asynchronous environments can be a recipe for disaster as asynchronous processes may reference a request object after it has been recycled into another request.
    Unnecessary
    New APIs can be passed on objects set as request attribute values that will pass through multiple other wrappers, coexist with other new APIs in attributes and do not require the core request methods to have mutable returns.

    The “wrapper object identity” requirement has little utility yet significant impacts on the correctness and performance of implementations. It significantly impairs the implementation of the container for a feature that can be rendered unusable by a wrapper applied by another filter.  It should be removed from Servlet 6.0 and requests passed in by the container should be immutable.

    Asynchronous Life Cycle

    A bit of history

    Jetty continuations were a non-standard feature introduced in Jetty-6 (around 2005) to support thread-less waiting for asynchronous events (e.g. typically another HTTP request in a chat room). Because the Servlet API had not been designed for thread-safe access from asynchronous processes, the continuations feature did not attempt to let arbitrary threads call the Servlet API.  Instead, it has a suspend/resume model that once the asynchronous wait was over, the request was re-dispatched back into the Servlet container to generate a response, using the normal blocking Servlet API from a well-defined context.

    When the continuation feature was standardized in the Servlet 3.0 specification, the Jetty suspend/resume model was supported with the APIs ServletRequest.startAsync() and AsyncContext.dispatch() methods.  However (against our strongly given advice), a second asynchronous model was also enabled, as represented by ServletRequest.startAsync() followed by AsyncContext.complete().  With the start/complete model, instead of generating a response by dispatching a container-managed thread, serialized on the request, to the Servlet container, arbitrary asynchronous threads could generate the response by directly accessing the request/response objects and then call the AsyncContext.complete() method when the response had been fully generated to end the cycle.   The result is that the entire API, designed not to be thread safe, was now exposed to concurrent calls. Unfortunately there was (and is) very little in the specification to help resolve the many races and ambiguities that resulted.

    Off to the Races

    The primary race introduced by start/complete is that described above caused by mutable requests that are forced by “wrapper object identity”. Consider the following asynchronous Servlet:

    @WebServlet(urlPatterns = {"/async/*"}, asyncSupported = true)
    @RunAs("special")
    public static class AsyncServlet extends HttpServlet
    {
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
        {
            AsyncContext async = request.startAsync();
            PrintWriter out = response.getWriter();
            async.start( () ->
            {
                response.setStatus(HttpServletResponse.SC_OK);
                out.printf("path=%s special=%b%n",
                           request.getServletPath(),
                           request.isUserInRole("special"));
                async.complete();
            });
        }
    }

    If invoked via a RequestDispatcher.forward(...), then the result produced by this Servlet is a race: will the thread dispatched to execute the lambda execute before or after the thread returns from the `doGet` method (and any applied filters) and the pre-forward values for the path and role are restored? Not only could the path and role be reported either for the target or caller, but the race could even split them so they are reported inconsistently.  To avoid this race, asynchronous Servlets must copy any value that they may use from the request before starting the asynchronous thread, which is needless complexity and expense. Many Servlets do not actually do this and just rely on happenstance to work correctly.

    This problem is the result of  the start/complete lifecycle of asynchronous Servlets permitting/encouraging arbitrary threads to call the existing APIs that were not designed to be thread-safe.  This issue is avoided if the request object passed to doGet is immutable and if it is the target of a forward, it will always act as that target. However, there are other issues of the asynchronous lifecycle that cannot be resolved just with immutability.

    Out of Time

    The example below is a very typical race that exists in many applications between a timeout and asynchronous processing:

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) throws IOException
    {
        AsyncContext async = request.startAsync();
        PrintWriter out = response.getWriter();
        async.addListener(new AsyncListener()
        {
            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException
            {
                response.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
                out.printf("Request %s timed out!%n", request.getServletPath());
                out.printf("timeout=%dms%n ", async.getTimeout());
                async.complete();
            }
        });
        CompletableFuture<String> logic = someBusinessLogic();
        logic.thenAccept(answer ->
        {
            response.setStatus(HttpServletResponse.SC_OK);
            out.printf("Request %s handled OK%n", request.getServletPath());
            out.printf("The answer is %s%n", answer);
            async.complete();
        });
    }

    Because the handling of the result of the business logic may be executed by a non-container-managed thread, it may run concurrently with the timeout callback. The result can be an incorrect status code and/or the response content being interleaved. Even if both lambdas grab a lock to mutually exclude each other, the results are sub-optimal, as both will eventually execute and one will ultimately throw an IllegalStateException, causing extra processing and a spurious exception that may confuse developers/deployers.

    The current specification of the asynchronous life cycle is the worst of both worlds for the implementation of the container. On one hand, they must implement the complexity of request-serialized events, so that for a given request there can only be a single container-managed thread in service(...), doFilter(...), onWritePossible(), onDataAvailable(), onAllDataRead()and onError(), yet on the other hand an arbitrary application thread is permitted to concurrently call the API, thus requiring additional thread-safety complexity. All the benefits of request-serialized threads are lost by the ability of arbitrary other threads to call the Servlet APIs.

    Request Serialized Threads

    The fix is twofold: firstly make more Servlet APIs immutable (as discussed above) so they are safe to call from other threads;  secondly and most importantly, any API that does mutate state should only be able to be called from request-serialized threads!   The latter might seem a bit draconian as it will make the lambda passed to thenAccept in the example above throw an IllegalStateException when it tries to setStatus(int) or call complete(), however, there are huge benefits in complexity and correctness and only some simple changes are needed to rework existing code.

    Any code running within a call to service(...), doFilter(...), onWritePossible(), onDataAvailable(), onAllDataRead()and onError() will already be in a request-serialized thread, and thus will require no change. It is only code executed by threads managed by other asynchronous components (e.g. the lambda passed to thenAccept() above) that need to be scoped. There is already the method AsyncContext.start(Runnable) that allows a non-container thread to access the context (i.e. classloader) associated with the request. An additional similar method AsyncContext.dispatch(Runnable) can be provided that not only scopes the execution but mutually excludes it and serializes it against any call to the methods listed above and any other dispatched Runnable. The Runnables passed may be executed within the scope of the dispatch call if possible (making the thread momentarily managed by the container and request serialized) or scheduled for later execution.  Thus calls to mutate the state of a request can only be made from threads that are serialized.

    To make accessing the dispatch(Runnable) method more convenient, an executor can be provided with AsyncContext.getExecutor() which provides the same semantic.  The example above can now be simply updated:

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) throws IOException
    {
        AsyncContext async = request.startAsync();
        PrintWriter out = response.getWriter();
        async.addListener(new AsyncListener()
        {
            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException
            {
                response.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
                out.printf("Request timed out after %dms%n ", async.getTimeout());
                async.complete();
            }
        });
        CompletableFuture<String> logic = someBusinessLogic();
        logic.thenAcceptAsync(answer ->
        {
            response.setStatus(HttpServletResponse.SC_OK);
            out.printf("The answer is %s%n", answer);
            async.complete();
        }, async.getExecutor());
    }

    Because the AsyncContext.getExecutor() is used to invoke the business logic consumer, then the timeout and business logic response methods are mutually excluded. Moreover, because they are serialized by the container, the request state can be checked between each, so that if the business logic has completed the request, then the timeout callback will never be called, even if the underlying timer expires while the response is being generated. Conversely, if the business logic result is generated after the timeout, then the lambda to generate the response will never be called.  Because both of the tasks in this example call complete, then only one of them will ever be executed.

    And Now You’re Complete

    In the example below, a non-blocking read listener has been set on the request input stream, thus a callback to onDataAvailable() has been scheduled to occur at some time in the future.  In parallel, an asynchronous business process has been initiated that will complete the response:

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
    {
        AsyncContext async = request.startAsync();
        request.getInputStream().setReadListener(new MyReadListener());
        CompletableFuture<String> logicB = someBusinessLogicB();
        PrintWriter out = response.getWriter();
        logicB.thenAcceptAsync(b ->
        {
            out.printf("The answer for %s is B=%s%n", request.getServletPath(), b);
            async.complete();
        }, async.getExecutor());
    }

    The example uses the proposed APIs above so that any call to complete is mutually excluded and serialized with the call to doGet and onDataAvailable(...). Even so, the current spec is unclear if the complete should prevent any future callback to onDataAvailable(...) or if the effect of complete() should be delayed until the callback is made (or times out). Given that the actions can now be request-serialized, the spec should require that once a request serialized thread that has called complete returns, then the request cycle is complete and there will be no other callbacks other than onComplete(...), thus cancelling any non-blocking IO callbacks.

    To Be Removed

    Before extending the Servlet specification, I believe the following existing features should be removed or deprecated:

    • Cross context dispatch deprecated and existing methods return null.  Once a request is matched to a context, then it will only ever be associated with that context and the getServletContext() method will return the same value no matter what state the request is in.
    • The “Wrapper Object Identity” requirement is removed and the request object will be required to be immutable in regards to the methods affected by a dispatch and may be referenced by asynchronous threads.
    • The RequestDispatcher.include(...) is deprecated and replaced with utility response wrappers.  The existing API can be deprecated and its implementation changed to use a request wrapper to simulate the existing attributes.
    • The special attributes for FORWARD, INCLUDE, ASYNC are removed from the normal dispatches.  Utility wrappers will be provided that can simulate these attributes if needed for backward compatibility.
    • The getDispatcherType() method is deprecated and returns REQUEST, unless a utility wrapper is used to replicate the old behavior.
    • Servlet API methods that mutate state will only be callable from request-serialized container-managed threads and will otherwise throw IllegalStateException. New AsyncContext.dispatch(Runnable) and AsyncContext.getExecutor() methods will provide access to request-serialization for arbitrary threads/lambdas/Runnables

    With these changes, I believe that many web applications will not be affected and most of the remainder could be updated with minimal effort. Furthermore, utility filters can be provided that apply wrappers to obtain almost all deprecated behaviors other than Wrapper Object Identity. In return for the slight break in backward compatibility, the benefit of these changes would be significant simplifications and efficiencies of the Servlet container implementations. I believe that only with such simplifications can we have a stable base on which to build new features into the Servlet specification. If we can’t take out the cruft now, then when?

    The plan is to follow this blog up with another proposing some more rationalisation of features (I’m looking at you sessions and authentication), before another blog proposing some new features an future directions.

  • Community Projects & Contributors Take on Jakarta EE 9

    With the recent release of JakartaEE9, the future for Java has never been brighter. In addition to headline projects moving forward into the new jakarta.* namespace, there has been a tremendous amount of work done throughout the community to stay at the forefront of the changing landscape. These efforts are the summation of hundreds of hours by just as many developers and highlight the vibrant ecosystem in the Jakarta workspace.
    The Jakarta EE contributors and committers came together to shape the 9 release. They chose to create a reality that benefits the entire Jakarta EE ecosystem. Sometimes, we tend to underestimate our influence and the power of our actions. Now that open source is the path of Jakarta EE, you, me, all of us can control the outcome of this technology. 
    Such examples that are worthy of emulation include the following efforts. In their own words:

    Eclipse Jetty – The Jetty project recently released Jetty 11, which has worked towards full compatibility with JakartaEE9 (Servlet, JSP, and WebSocket). We are driven by a mission statement of “By Developers, For Developers”, and the Jetty team has worked since the announcement of the so-called “Big Bang” approach to move Jetty entirely into the jakarta.* namespace. Not only did this position Jetty as a platform for other developers to push their products into the future, but also allowed the project to quickly adapt to innovations that are sure to come.

    [Michael Redich] The Road to Jakarta EE 9, an InfoQ news piece, was published this past October to highlight the efforts by Kevin Sutter, Jakarta EE 9 Release Lead at IBM, and to describe the progress made this past year in making this new release a reality. The Java community should be proud of their contributions to Jakarta EE 9, especially implementing the “big bang,” and discussions have already started for Jakarta EE 9.1 and Jakarta EE 10. The Q&A with Kevin Sutter in the news piece includes the certification and voting process for all the Jakarta EE specifications, plans for upcoming releases of Jakarta EE, and how Java developers can get involved in contributing to Jakarta EE. Personally, I am happy to have been involved in Jakarta EE having authored 14 Jakarta EE-related InfoQ news items for the three years, and I look forward to taking my Jakarta EE contributions to the next level. I have committed to contributing to the Jakarta NoSQL specification which is currently under development. The Garden State Java User Group (in which I serve as one of its directors) has also adopted Jakarta NoSQL. I challenge anyone who still thinks that the Java programming language is dead because these past few years have been an exciting time to be part of this amazing Java community!

    WildFly 22 Beta1 contains a tech preview EE 9 variant called WildFly Preview that you can download from the WildFly download page.  The WildFly team is still working on passing the needed (Jakarta EE 9) TCKs (watch for updates via the wildfly.org site.)  WildFly Preview includes a mix of native EE 9 APIs and implementations (i.e. ones that use the  jakarta.* namespace) along with many APIs and implementations from EE 8 (i.e. ones that use the  java.* namespace). This mix of namespaces is made possible by using the Eclipse community’s excellent Eclipse Transformer project to bytecode transformer legacy EE 8 artifacts to EE 9 when the server is provisioned. Applications that are written for EE 8 can also run on WildFly Preview, as a similar transformation is performed on any deployments managed by the server.

    Apache TomEE is a Jakarta EE application server based on Apache Tomcat. The project main focus is the Web Profile up until Jakarta EE 8. However, with Jakarta EE 9 and some parts being optional or pruned, the project is considering the full platform for the future. TomEE is so far a couple of tests down (99% coverage) before it reaches compatibility with Jakarta EE 8 (See Introducing TCK Work and how it helps the community jump into the effort). For Jakarta EE 9, the Community decided to pick a slightly different path than other implementations. We have already produced a couple of Apache TomEE 9 milestones for Jakarta EE 9 based on a customised version of the Eclipse Transformer. It fully supports the new jakarta.* namespace. Not to forget, the project also implements MicroProfile.

    Open Liberty is in the process of completing a Compatible Implementation for Jakarta EE 9.  For several months, the Jakarta EE 9 implementation has been rolling out via the “monthly” Betas.  Both of the Platform and Web Profile TCK testing efforts are progressing very well with 99% success rates.  The expectation is to declare one (or more) of the early Open Liberty 2021 Betas as a Jakarta EE 9 Compatible Implementation.  Due to Open Liberty’s flexible architecture and “zero migration” goal, customers can be assured that their current Java EE 7, Java EE 8, and Jakarta EE 8 applications will continue to execute without any changes required to the application code or server configuration.  But, with a simple change to their server configuration, customers can easily start experimenting with the new “jakarta” namespace in Jakarta EE 9.

    Jelastic PaaS is the first cloud platform that has already made Jakarta EE 9 release available for the customers across a wide network of distributed hosting service providers. For the last several months Jelastic team has been actively integrating Jakarta EE 9 within the cloud platform and in December made an official release. The certified container images with the following software stacks are already updated and available for customers across over 100 data centers: Tomcat, TomEE, GlassFish, WildFly and Jetty. Jelastic PaaS provides an easy way to create environments with new Jakarta EE 9 application servers for deep testing, compatibility checks and running live production environments. It’s also possible now to redeploy existing containers with old versions to the newest ones in order to reduce the necessary migration efforts, and to expedite adoption of cutting-edge cloud native tools and products. 


    [
    Amelia Eiras] Pull Request 923- Jakarta EE 9 Contributors Card is a formidable example of eleven-Jakartees coming together to create, innovate and collaborate on an Integration-Feature that makes it so that no contributor, who helped on Jakarta EE 9 release, be forgotten in the new landing page for the EE 9 Release. Who chose those Contributors? None. That is the sole point of the existence of PR923.I chose to lead the work on the PR and worked openly by prompt communications delivered the day that Tomitribe submitted the PR – Jakarta EE Working Group message to the forum to invite other Jakartees to provide input in the creation of the new feature. With Triber Andrii, who wrote the code and the feedback of those involved, the feature is active and used in the EE 9 contributors cards, YOU ROCK WALL
    The Integration-Feature will be used in future releases.  We hope that it is also adopted by any project, community, or individual in or outside the Eclipse Foundation to say ThankYOU with actions to those who help power & maintain any community. 

    • PR logistics: 11 Jakartees came together and produced 116 exchanges that helped merge the code. Thank you, Chris (Eclipse WebMaster) for helping check the side of INFRA. The PR’s exchanges lead us to choose the activity from 2 GitHub sources: 1) https://github.com/jakartaee/specifications/pulls all merged pulls and 2)  https://github.com/eclipse-ee4j all repositories.
    • PR Timeframe: the Contributors’ work accomplished from October 31st, 2019 to November 20th, 2020, was boxed and is frozen.   The result is that the Contributor Cards highlight 6 different Jakartees at a time every 15 seconds.  A total of 171 Jakartee Contributors (committers and contributors, leveled) belong to the amazing people behind EE 9 code. While working on that PR, other necessary improvements become obvious. A good example is the visual tweaks PR #952 we submitted that improved the landing page’s formatting, cards’ visual, etc. 

    Via actions, we chose to not “wait & see”, saving the project $budget, but also enabling openness to tackle the stuff that could have been dropped into “nonsense”. 

     
    In open-source, our actions project a temporary part of ourselves, with no exceptions. Those actions affect positively or negatively any ecosystem. Thank you for taking the time to read this #SharingIsCaring blog.  
     

  • CometD 5.0.3, 6.0.0 and 7.0.0

    Following the releases of Eclipse Jetty 10.0.0 and 11.0.0, the CometD project has released versions 5.0.3, 6.0.0 and 7.0.0.

    CometD 5.0.x Series

    CometD 5.0.x, of which the latest is the newly released 5.0.3, require at least Java 8 and it is based on Jetty 9.4.x.
    This version will be maintained as long as Jetty 9.4.x is maintained, likely many more years, to allow migration away from Java 8.

    CometD 6.0.x Series

    CometD 6.0.x, with the newly released 6.0.0, requires at least Java 11 and it is based on Jetty 10.0.x.
    In turn, Jetty 10.0.x is based on Java EE 8 / Jakarta EE 8, which provide Servlet and WebSocket APIs under the javax.* packages.
    This version of CometD provides a smooth transition from CometD 5 (or earlier) to Java 11 and Jetty 10.
    In this way, you can leverage new Java 11 language features in your application source code, as well as the few new APIs made available in the Servlet 4.0 Specification without having to change much of your code, since you will still be using javax.* Servlet and WebSocket APIs (see the next section on CometD 7.0.x for the migration to the jakarta.* APIs).
    However, server-side CometD applications should not depend much on Servlet or WebSocket APIs, so the migration from CometD 5 (or earlier) should be pretty straightforward.
    CometD 6.0.x, as Jetty 10.0.x, are transition releases to the new Jakarta EE 9 APIs in the jakarta.* packages.
    CometD 6.0.x will be maintained as long as Jetty 10.0.x is maintained.
    Since CometD applications do not depend much on Servlet or WebSocket APIs, consider moving directly to CometD 7.0.x if you don’t depend on other libraries (for example, Spring) that require javax.* classes.

    CometD 7.0.x Series

    CometD 7.0.x, with the newly released 7.0.0, requires at least Java 11 and it is based on Jetty 11.0.x.
    In turn, Jetty 11.0.x is based on Jakarta EE 9, which provide Servlet and WebSocket APIs under the jakarta.* packages.
    Migrating from CometD 6.0.x (or earlier) to CometD 7.0.x should be very easy if your applications depend mostly on the CometD APIs (few or no changes there) and depend very little on the Servlet or WebSocket APIs.
    Dependencies on the javax.* APIs should be migrated to the jakarta.* APIs.
    If your applications depend on third-party libraries, you need to make sure to update the third-party library to a version that supports — if necessary — jakarta.* APIs.
    Migrating to CometD 7.0.x allows you to leverage Java 11 language features and the benefit that you can base your applications on the new Jakarta EE Specifications, therefore abandoning the now-dead Java EE Specifications.
    This migration keeps your applications up-to-date with the current state-of-the-art of EE Specifications, reducing the technical debt to a minimum.
    CometD 7.0.x will be maintained as long as Jetty 11.0.x is maintained.
    The evolution of the Jakarta EE Specifications will be implemented by future Jetty versions, and future CometD versions will keep the pace.

    Which CometD Series Do I Use?

    If your applications depend on third-party libraries that use or depend on Jetty 9.4.x (such as Spring / Spring Boot), use CometD 5.0.x and Jetty 9.4.x until the third party libraries update to newer Jetty versions.
    If your applications depend on third-party libraries that depend on javax.* APIs and that have not been updated to Jakarta EE 9 yet (and therefore to jakarta.* APIs), use CometD 6.0.x and Jetty 10.0.x until the third party libraries update to Jakarta EE 9.
    If your applications depend on third-party libraries that have already been updated to use Jakarta EE 9 APIs, for example, Jakarta Restful Web Services (previously known as JAX-RS) using Eclipse Jersey 3.0, use CometD 7.0.x and Jetty 11.0.x.
    If you are migrating from earlier CometD versions, skip entirely CometD 6.0.x: for example move from CometD 5.0.x and Jetty 9.4.x directly to CometD 7.0.x and Jetty 11.0.x.

  • Reactive HttpClient 1.1.5, 2.0.0 and 3.0.0

    Following the releases of Eclipse Jetty 10.0.0 and 11.0.0, the Reactive HttpClient project — introduced back in 2017 — has released versions 1.1.5, 2.0.0 and 3.0.0.

    Reactive HttpClient 1.1.x Series

    Reactive HttpClient Versions 1.1.x, of which the latest is the newly released 1.1.5, requires at least Java 8 and it is based on Jetty 9.4.x.
    This version will be maintained as long as Jetty 9.4.x is maintained, likely many more years, to allow migration away from Java 8.

    Reactive HttpClient 2.0.x Series

    Reactive HttpClient Versions 2.0.x, with the newly released 2.0.0, requires at least Java 11 and it is based on Jetty 10.0.x.
    The Reactive HttpClient 2.0.x series is incompatible with the 1.1.x series, since the Jetty HttpClient APIs changed between Jetty 9.4.x and Jetty 10.0.x.
    This means that projects such as Spring WebFlux, at the time of this writing, are not compatible with the 2.0.x series of Reative HttpClient.

    Reactive HttpClient 3.0.x Series

    Reactive HttpClient Versions 3.0.x, with the newly released 3.0.0, requires at least Java 11 and it is based on Jetty 11.0.x.
    In turn, Jetty 11.0.x is based on the Jakarta EE 9 Specifications, which means jakarta.servlet and not javax.servlet.
    The Reactive HttpClient 3.0.x series is fundamentally identical to the 2.0.x series, apart from the Jetty dependency.
    While the HttpClient APIs do not change between Jetty 10 and Jetty 11, if you are using Jakarta EE 9 it will be more convenient to use the Reactive HttpClient 3.0.x series.
    For example when using Reactive HttpClient to call a third party service from within a REST service, it will be natural to use Reactive HttpClient 2.0.x if you use javax.ws.rs, and Reactive HttpClient 3.0.x if you use jakarta.ws.rs.
    Enjoy the new releases and tell us which series you use by adding a comment here!
    For further information, refer to the project page on GitHub.

  • Renaming Jetty from javax.* to jakarta.*

    The Issue

    The Eclipse Jakarta EE project has not obtained the rights from Oracle to extend the Java EE APIs living in the javax.* package. As such, the Java community is faced with a choice between continuing to use the frozen javax.* APIs or transitioning to a new jakarta.* namespace where these specifications could continue to be evolved and/or significantly changed.
    This name change is now a “fait accompli” (a.k.a. “done deal”), so it will happen no matter what. However, how the Eclipse Jakarta EE specification process handles the transition and how vendors like Webtide respond are yet to be determined.   
    This blog discusses some of the options for how Webtide can evolve the Jetty project to deal with this renaming.

    Jakarta EE Roadmap

    Jakarta EE 8

    A release of Jakarta EE 8 will happen first, and that will include:

    • An update to Licensing
    • An update to Naming (to limit the use of the trademarked term “Java”)
    • An update to the Maven GroupID Coordinate Space in Maven Central
    • No change to the Java packaging of the old Java EE 8 classes; they still retain their javax.* namespace.

    For example, the current Maven coordinates for the Servlet jar are (groupId:artifactId:version):
    javax.servlet:javax.servlet-api:4.0.1
    With Jakarta EE 8, these coordinates will change to:
    jakarta.servlet:jakarta.servlet-api:4.0.2
    The content of these 2 jars will be identical: both will contain javax.servlet.Servlet.

    Jakarta EE 9

    The Eclipse Jakarta EE project is currently having a debate about what the future of Jakarta looks like. Options include:

    Jakarta EE “Big Bang” Rename Option

    Jakarta EE 9 would rename every API and implementation to use the jakarta.* namespace API.
    This means that javax.servlet.Servlet will become jakarta.servlet.Servlet.
    No other significant changes would be made (including not removing any deprecated methods or behaviours). This requires applications to update the package imports from javax.* to jakarta.* en mass to update possible dependencies to versions that use the jakarta.* APIs (e.g. REST frameworks, etc.) and recompile the application.
    Alternatively, backward compatibility would be required (provided by Containers) in some form so that legacy javax.* code could be deployed (at least initially) with static and/or dynamic translation and/or adaption.
    For example, Container vendors could provide tools that pre-process your javax.* application, transforming it into a jakarta.* application. Exact details of how these tools will work (or even exist) are still uncertain.

    Jakarta EE Incremental Change

    Jakarta EE 9 would maintain any javax.* API that did not require any changes. Only those APIs that have been modified/updated/evolved/replaced would be in the jakarta.* namespace.
    For example, if the Servlet 4.0 Specification is not updated, it will remain in the javax.* package.
    However, if the Servlet Specification leaders decide to produce a Servlet 5.0 Specification, it will be in the jakarta.* package.
     
     

    The Current State of Servlet Development

    So far, there appears very little user demand for iterations on the Servlet API.
    The current stable Jetty release series, 9.4.x, is on Servlet 3.1, which has not changed since 2013.
    Even though Servlet 4.0 was released in 2017, we have yet to finalize our Jetty 10 release using it, in part waiting for new eclipse Jakarta artifacts for the various EE specs, eg: servlet-api, jsp-api, el-api, websocket-api, and mail-api.  Despite being 2 years late with Servlet 4.0, we have not received a single user request asking for Servlet 4.0 features or a release date.
    On the other hand, there has been interest from our users in more significant changes, some of which we have already started supporting in Jetty specific APIs:

    • JPMS integration/support
    • Asynchronously filter input/output streams without request/response wrapping
    • Reactive style asynchronous I/O
    • Minimal startup times (avoiding discovery mechanisms) to facilitate fast spin-up of new cloud instances
    • Micro deployments of web functions
    • Asynchronous/background consumption of complex request content: XML, JSON, form parameters, multipart uploads etc.

    Jetty Roadmap

    Jetty 10

    Jetty 10, implementing the Servlet 4.0 Specification, will be released once the frozen Jakarta EE 8 artifacts are available. These artifacts will have a Maven groupId of jakarta.*, but will contain classes in the javax.* packages.
    It is envisioned that Jetty 10 will soon after become our primary stable release of Jetty and will be enhanced for several years and maintained for many more. In the mid-term, it is not Webtide’s intention to force any user to migrate to the new jakarta.* APIs purely due to the lack of availability of a javax.* implementation.  Any innovations or developments done in Jetty 10 will have to be non standard extensions. 
    However, we are unable to commit to long term support for the external dependencies bundled with a Jetty release that use the javax.* package (eg. JSP, JSTL, JNDI, etc.) unless we receive such commitments from their developers.

    Jetty 11

    Jetty 11 would be our first release that uses the Jakarta EE 9 APIs. These artifacts will have a Maven groupId of jakarta.* and contain classes also in the jakarta.* packages.
    We are currently evaluating if we will simply do a rename (aka Big Bang) or instead take the opportunity to evolve the server to be able to run independently of the EE APIs and thus be able to support both javax.* and jakarta.*

    Jetty Options with Jakarta EE “Big Bang”

    If the Eclipse foundation determines that the existing javax.* APIs are to be renamed to jakarta.* APIs at once and evolved, then the following options are available for the development of future Jetty versions.
    Note that current discussions around this approach will not change anything about the functionality present in the existing APIs (eg: no removal of deprecated methods or functions. no cleanup or removal of legacy behaviors. no new functionality, etc)

    Option 0. Do Nothing

    We keep developing Jetty against the javax.* APIs which become frozen in time.
    We would continue to add new features but via Jetty specific APIs. The resources that would have otherwise been used supporting the rename to jakarta.* will be used to improve and enhance Jetty instead.
    For users wishing to stay with javax.* this is what we plan to do with Jetty 10 for many years, so “Do Nothing” will be an option for some time. That said, if Jetty is to continue to be a standards-based container, then we do need to explore additional options.

    Option 1. “Big Bang” Static Rename

    Jetty 11 would be created by branching Jetty 10 and renaming all javax.* code to use jakarta.* APIs from Jakarta EE9. Any users wishing to run javax.* code would need to do so on a separate instance of Jetty 10 as Jetty 11 would only run the new APIs.
    New features implemented in future releases of Jakarta EE APIs would only be available in Jetty 11 or beyond. Users that wish to use the latest Jetty would have to do the same rename in their entire code base and in all their dependencies.
    The transition to the new namespace would be disruptive but is largely a one-time effort. However, significant new features in the APIs would be delayed by the transition and then constrained by the need to start from the existing APIs.

    Option 2. “Big Bang” Static Rename with Binary Compatibility

    This option is essentially the same as Option 1, except that Jetty 11 would include tools/features to dynamically rename (or adapt – the technical details to be determined) javax.* code usage, either as it is deployed or via pre-deployment tooling. This would allow existing code to run on the new Jetty 11 releases. However, as the Jakarta APIs evolve, there is no guarantee that this could continue to be done, at least not without some runtime cost. The Jakarta EE project is currently discussing backward compatibility modes and how (or if) these will be specified.
    This approach minimizes the initial impact of transitioning, as it allows legacy code to continue to be deployed. The downside, however, is that these impacts may be experienced for many years until that legacy code is refactored. These effects will include the lost opportunities for new features to be developed as development resources are consumed implementing and maintaining the binary compatibility feature.

    Option 3. Core Jetty

    We could take the opportunity forced on us by the renaming to make the core of Jetty 11 independent of any Jakarta EE APIs.
    A new modern lightweight core Jetty server would be developed, based on the best parts of Jetty 10, but taking the opportunity to update, remove legacy concerns, implement current best practices and support new features.
    Users could develop to this core API as they do now for embedded Jetty usage. Current embedded Jetty code would need to be ported to the new API (as it would with any javax.* rename to jakarta.* ).
    Standard-based deployments would be supported by adaption layers providing the servlet container and implementations would be provided for both javax.* and jakarta.*. A single server would be able to run both old and new APIs, but within a single context, the APIs would not be able to be mixed.
    This is a significant undertaking for the Jetty Project but potentially has the greatest reward as we will obtain new features, not just a rename.

    Jetty Options with Jakarta EE Incremental Change

    These are options if the Eclipse foundation determines that the jakarta.*  package will be used only for new or enhanced APIs.

    Option 4. Jakarta Jetty

    This option is conceptually similar to Option 3, except that the new Jetty core API will be standardized by Jakarta EE 9 (which will hopefully be a lightweight core) to current best practices and include new features.
    Users could develop to this new standard API. Legacy deployments would be supported by adaption layers providing the javax.* servlet container, which may also be able to see new APIs and thus mix development.

    Option 5.  Core Jetty

    This option is substantially the same as Option 3 as Jetty 11 would be based around an independent lightweight core API.
    Users could develop to this core API as they do now for embedded Jetty usage. Standard-based deployments would be supported by adaption layers providing the servlet container and implementations would be provided for both javax.* and jakarta.*.

    How You Can Get Involved

    As with any large-scale change, community feedback is paramount. We invite all users of Jetty, regardless of how you consume it, to make your voice heard on this issue.
    For Jetty-specific feedback, we have opened messages on both the jetty-users and jetty-dev mailing lists. These discussions can be found below:
    https://www.eclipse.org/lists/jetty-users/msg08908.html
    https://www.eclipse.org/lists/jetty-dev/msg03307.html
    To provide feedback on the broader change from the javax.* to jakarta.* namespace, you can find a discussion on the jakartaee-platform-dev mailing list:
    https://www.eclipse.org/lists/jakartaee-platform-dev/msg00029.html

  • Running Jetty on the JPMS module-path

    Jetty and the Java Module System.

    Java 9 introduced the arguably biggest change in the Java platform since its inception, the Java Module System (a.k.a. Project Jigsaw, or Java Platform Module System – JPMS).
    The Java Module System primarily targets the modularization of the JDK itself, but can also be used to write modularized applications.
    Historically, Jetty has been often used to write applications that needed to embed an HTTP server without having too many dependencies. The fact that Jetty itself is a highly modular set of components allows you to use only the Jetty components that you need, reducing the dependencies and keeping your single application jar small.
    For example, you may need to embed Jetty as an HTTP server only, without the need for JSPs, without support for WebSocket, without HTTP/2: Jetty allows you to do that.
    You will be able to mix and match Jetty features by simply adding dependencies on the Jetty modules you need.
    Since Jetty is already modularized into Jetty modules, it would be great if a JPMS application could reference Jetty modules as JPMS modules.
    This is now possible since Jetty 9.4.14.

    JPMS modules for Jetty 9.4.x and Jetty 10.0.x

    To make Jetty modules available as JPMS modules we have taken a two-steps approach.
    First, the Jetty 9.4.x series now include the entryAutomatic-Module-Name in the fileMANIFEST.MF of each jar representing a Jetty module.
    This allowed us to lock down the definition of the Jetty JPMS module names, in the form of org.eclipse.jetty.* names.
    For example, the jetty-util Jetty module has a JPMS module name of org.eclipse.jetty.util, and so forth for other Jetty modules.
    Second, the Jetty 10.0.x series will include a proper module-info.java JPMS module file for each Jetty module. This will allow us to take advantage of the JPMS module system and hiding implementation classes that we don’t want to expose.
    With stable Jetty JPMS module names, JPMS applications that use proper module-info.java JPMS module files can reliably reference JPMS Jetty modules by name, and little will change when they will update their Jetty version from 9.4.x to 10.0.x (where Jetty will also use proper module-info.java JPMS module files).

    Running Jetty standalone on the module-path

    But Jetty is also a great standalone server!
    It is now possible to run Jetty as a standalone server on the module-path rather than on the class-path as it has always been.
    To run Jetty as a standalone server on the module-path, you just need to add an additional command line option, namely --jpms:

    # Create a new JETTY_BASE directory.
    $ mkdir jetty-base
    $ cd jetty-base
    # Configure the HTTP module.
    $ java -jar $JETTY_HOME/start.jar --add-to-start=http
    # Start Jetty on the module-path.
    $ java -jar $JETTY_HOME/start.jar --jpms
    

    That is all!
    Note that this will run the Jetty server on the module path.
    Web applications deployed to Jetty will not take advantage of the JPMS module system (yet).
    Making Web application take advantage of the JPMS module system is a matter for the Eclipse EE4J effort, but we may anticipate that effort in Jetty 10.0.x with some experiments on our own (if you are interested, follow this Jetty enhancement issue).
    We recommend using Java 11 to run Jetty on the module-path.
    Follow the Jetty JPMS documentation if you want to know more details about how to customize Jetty modules so that they are JPMS friendly, and let us know what you think!