Author: Simone Bordet

  • Jetty 12 – Virtual Threads Support

    Executive Summary

    Virtual Threads, introduced in Java 19, are supported in Jetty 12, as they have been in Jetty 10 and Jetty 11 since 10.0.12 and 11.0.12, respectively.

    When virtual threads are supported by the JVM and enabled in Jetty (see embedded usage and standalone usage), applications are invoked using a virtual thread, which allows them to use simple blocking APIs, but with the scalability benefits of virtual threads.

    Introduction

    Virtual threads were introduced as a preview feature in Java 19 via JEP 425 and in Java 20 via JEP 436, and finally integrated as an official feature in Java 21 via JEP 444.

    Historically, the APIs provided to application developers, especially web application developers, were blocking APIs based on InputStream and OutputStream, or based on JDBC.
    These APIs are very simple to use, so applications are simple to develop, understand and troubleshoot.

    However, these APIs come with a cost: when a thread blocks, typically waiting for I/O or a contended lock, all the resources associated with that thread are retained, waiting for the thread to unblock and continue processing: the native thread and its native memory are retained, as well as network buffers, lock structures, etc.

    This means that blocking APIs are less scalable because they retain the resources they use.
    For example, if you have configured your server thread pool with 256 threads, and all are blocked, your server cannot process other requests until one of the blocked threads unblocks, limiting the server’s scalability.

    Furthermore, if you increase the server thread pool capacity, you will use more memory and likely require bigger hardware.

    Asynchronous/Reactive

    For these reasons, non-blocking asynchronous and reactive APIs have been introduced. The primary examples are the asynchronous I/O APIs introduced in Servlet 3.1 and reactive APIs provided by libraries such as RxJava and Spring’s Project Reactor, based on Reactive Streams.
    Unfortunately, REST APIs such as JAX-RS or Jakarta RESTful Web Services have not been (fully) updated with non-blocking APIs, so web applications that use REST are stuck with blocking APIs and scalability problems.

    Essential to note is that asynchronous and reactive APIs are more difficult to use, understand, and troubleshoot than blocking APIs, but are more scalable and typically achieve similar performances at a fraction of the resources. We have seen web applications that, when switched from blocking APIs to non-blocking APIs, reduced the threads usage from 1000+ to 10+.

    Virtual threads aim to be the best of both worlds: simple-to-use blocking APIs for developers, with the scalability of non-blocking APIs provided by the JVM.

    Jetty 12 Architecture

    The Jetty 12 architecture, at its core, is completely non-blocking and uses an AdaptiveExecutionStrategy (formerly known as “eat what you kill” which was covered in previous blogs here and here) to determine how to consume tasks.

    The key feature of AdaptiveExecutionStrategy is that it has a strong preference for consuming tasks in the same thread that produces them, so they are executed with a hot CPU cache, without parallel slowdown and no context-switch latency, yet avoids the risk of the server exhausting its thread pool.

    Simplifying a bit, each task is marked either as blocking or non-blocking; AdaptiveExecutionStrategy looks at the task and at how many threads are available to decide how to consume the task.

    If the task is non-blocking, the current thread runs it immediately.
    Otherwise, if no other threads are available to continue producing tasks, the current thread takes over the production of tasks and gives the tasks to an Executor, where they are likely queued and executed later by different threads.

    Virtual Threads Integration

    This architecture made it easy to integrate virtual threads in Jetty: when virtual threads are supported by the JVM and Jetty’s virtual threads support is enabled (see embedded usage and standalone usage), AdaptiveExecutionStrategy consumes a blocking task by offering the task to the virtual thread Executor rather than the native thread Executor, so that a newly spawned virtual thread runs the blocking task.

    That’s it.

    As a Servlet Container implementation, Jetty calls Servlets assuming they will use blocking APIs, so the task that invokes the Servlet is a blocking task.
    When virtual threads are supported and enabled, the thread that calls Servlet Filters and eventually the HttpServlet.service(...) method is a virtual thread.

    For non-blocking tasks, it is more efficient to have them run by the same native thread that created them; it is only for blocking tasks that you may want to use virtual threads.

    Conclusions

    Jetty’s AdaptiveExecutionStrategy allows the best of all worlds.
    Jetty provides a fast scalable asynchronous implementation, which avoids any possible limitations of virtual threads, whilst giving applications the full benefits of virtual threads. 

    Jetty deals with complex asynchronous concerns, so you don’t have to!

  • Jetty HTTP/3 Support

    Introduction

    HTTP/3 is the next iteration of the HTTP protocol.

    HTTP/1.0 was released in 1996 and HTTP/1.1 in 1997; HTTP/1.x is a fairly simple textual protocol based on TCP, possibly wrapped in TLS, that experienced over the years a tremendous growth that was not anticipated in the late ’90s.
    With the growth, a few issues in the HTTP/1.x scalability were identified, and addressed first by the SPDY protocol (HTTP/2 precursor) and then by HTTP/2.

    The design of HTTP/2, released in 2015 (and also based on TCP), resolved many of the HTTP/1.x shortcomings and  protocol became binary and multiplexed.

    The deployment at large of HTTP/2 revealed some issues in the HTTP/2 protocol itself, mainly due a shift towards mobile devices where connectivity is less reliable and packet loss more frequent.

    Enter HTTP/3, which ditches TCP for QUIC (RFC 9000) to address the connectivity issues of HTTP/2.
    HTTP/3 and QUIC are inextricably entangled together because HTTP/3 relies heavily on QUIC features that are not provided by any other lower-level protocol.

    QUIC is based on UDP (rather than TCP) and has TLS built-in, rather than layered on top.
    This means that you cannot offload TLS in a front-end server, like with HTTP/1.x and HTTP/2, and then forward the clear-text HTTP/x bytes to back-end servers.

    Due to HTTP/3 relying heavily on QUIC features, it’s not possible anymore to separate the “carrier” protocol (QUIC) from the “semantic” protocol (HTTP). Therefor reverse proxying should either:

    • decrypt QUIC+HTTP/3, perform some proxy processing, and re-encrypt QUIC+HTTP/3 to forward to back-end servers; or
    • decrypt QUIC+HTTP/3, perform some proxy processing, and re-encode into a different protocol such as HTTP/2 or HTTP/1.x to forward to back-end servers, with the risk of losing features by using older HTTP protocol versions.

    The Jetty Project has always been on the front at implementing Web protocols and standard, and QUIC+HTTP/3 is no exception.

    Jetty’s HTTP/3 Support

    At this time, Jetty’s support for HTTP/3 is still experimental and not recommended for production use.

    We decided to use the Cloudflare’s Quiche library because QUIC’s use of TLS requires new APIs that are not available in OpenJDK; we could not implement QUIC in pure Java.

    We wrapped the native calls to Quiche with either JNA or with Java 17’s Foreign APIs (JEP 412) and retrofitted the existing Jetty’s I/O library to work with UDP as well.
    A nice side effect of this work is that now Jetty is a truly generic network server, as it can be used to implement any generic protocol (not just web protocols) on either TCP or UDP.

    HTTP/3 was implemented in Jetty 10.0.8/11.0.8 for both the client and the server.
    The implementation is quite similar to Jetty’s HTTP/2 implementation, since the protocols are quite similar as well.

    HTTP/3 on the client is available in two forms:

    • Using the high-level APIs provided by Jetty’s HttpClient with the HTTP/3 specific transport (that only speaks HTTP/3), or with the dynamic transport (that can speak multiple protocols).
    • Using the low-level HTTP/3 APIs provided by Jetty’s HTTP3Client that allow you to deal directly with HTTP/3 sessions, streams and frames.

    HTTP/3 on the server is available in two forms:

    • Using embedded code via HTTP3ServerConnector listening on a specific network port.
    • Using Jetty as a standalone server by enabling the http3 Jetty module.

    In both cases, an incoming HTTP/3 request is processed and forwarded to your standard Web Applications, or to your Jetty Handlers.

    Finally, the HTTP/3 specification at the IETF is still a draft and may change, and we prioritized a working implementation over performance.

  • UnixDomain Support in Jetty

    UnixDomain sockets support was added in Jetty 9.4.0, back in 2015, based on the JNR UnixSocket library.

    The support for UnixDomain sockets with JNR was experimental, and has remained so until now.

    In Jetty 10.0.7/11.0.7 we re-implemented support for UnixDomain sockets based on JEP 380, which shipped with Java 16.

    We have kept the source compatibility at Java 11 and used a little bit of Java reflection to access the new APIs introduced by JEP 380, so that Jetty 10/11 can still be built with Java 11.
    However, if you run Jetty 10.0.7/11.0.7 or later with Java 16 or later, then you will be able to use UnixDomain sockets based on JEP 380.

    The UnixDomain implementation from Java 16 is very stable, so we have switched our own website to use it.
    The page that you are reading right now has been requested by your browser and processed on the server by Jetty using Jetty’s HttpClient to send the request via UnixDomain sockets to our local WordPress.

    We have therefore deprecated the old Jetty modules based on JNR in favor of the new Jetty modules based on JEP 380.

    Note that since UnixDomain sockets are an alternative to TCP network sockets, any TCP-based protocol can be carried via UnixDomain sockets: HTTP/1.1, HTTP/2 and FastCGI.

    We have improved the documentation to detail how to use the new APIs introduced to support JEP 380, for the client and for the server.
    If you are configuring Jetty behind a load balancer (or Apache HTTPD or Nginx) you can now use UnixDomain sockets to communicate from the load balancer to Jetty, as explained in this section of the documentation.

    Enjoy!

  • Introducing Jetty Load Generator

    The Jetty Project just released the Jetty Load Generator, a Java 11+ library to load-test any HTTP server, that supports both HTTP/1.1 and HTTP/2.
    The project was born in 2016, with specific requirements. At the time, very few load-test tools had support for HTTP/2, but Jetty’s HttpClient did. Furthermore, few tools supported web-page like resources, which were important to model in order to compare the multiplexed HTTP/2 behavior (up to ~100 concurrent HTTP/2 streams on a single connection) against the HTTP/1.1 behavior (6-8 connections). Lastly, we were more interested in measuring quality of service, rather than throughput.
    The Jetty Load Generator generates requests asynchronously, at a specified rate, independently from the responses. This is the Jetty Load Generator core design principle: we wanted the request generation to be constant, and measure response times independently from the request generation. In this way, the Jetty Load Generator can impose a specific load on the server, independently of the network round-trip and independently of the server-side processing time. Adding more load generators (on the same machine if it has spare capacity, or using additional machines) will allow the load against the server to increase linearly.
    Using this core principle, you can setup the load testing by having N load generator loaders that impose the load on the server, and 1 load generator probe that imposes a very light load and measures response times.
    For example, you can have 4 loaders that impose 20 requests/s each, for a total of 80 requests/s seen by the server. With this load on the server, what would be the experience, in terms of response times, of additional users that make requests to the server? This is exactly what the probe measures.
    If the load on the server is increased to 160 requests/s, what would the probe experience? The same response times? Worse? And what are the probe response times if the load on the server is increased to 240 requests/s?
    Rather than trying to measure some form of throughput (“what is the max number of requests/s the server can sustain?”), the Jetty Load Generator measures the quality of service seen by the probe, as the load on the server increases. This is, in practice, what matters most for HTTP servers: knowing that, when your server has a load of 1024 requests/s, an additional user can still see response times that are acceptable. And knowing how the quality of service changes as the load increases.
    The Jetty Load Generator builds on top of Jetty’s HttpClient features, and offers:

    • A builder-style Java API, to embed the load generator into your own code and to have full access to all events emitted by the load generator
    • A command-line tool, similar to Apache’s ab or wrk2, with histogram reporting, for ease of use, scripting, and integration with CI servers.

    Download the latest command-line tool uber-jar from: https://repo1.maven.org/maven2/org/mortbay/jetty/loadgenerator/jetty-load-generator-starter/

    $ cd /tmp
    $ curl -O https://repo1.maven.org/maven2/org/mortbay/jetty/loadgenerator/jetty-load-generator-starter/1.0.2/jetty-load-generator-starter-1.0.2-uber.jar
    

    Use the --help option to display the available command line options:

    $ java -jar jetty-load-generator-starter-1.0.2-uber.jar --help
    

    Then run it, for example:

    $ java -jar jetty-load-generator-starter-1.0.2-uber.jar --scheme https --host your_server --port 443 --resource-rate 1 --iterations 60 --display-stats
    

    You will obtain an output similar to the following:

    ----------------------------------------------------
    -------------  Load Generator Report  --------------
    ----------------------------------------------------
    https://your_server:443 over http/1.1
    resource tree     : 1 resource(s)
    begin date time   : 2021-02-02 15:38:39 CET
    complete date time: 2021-02-02 15:39:39 CET
    recording time    : 59.657 s
    average cpu load  : 3.034/1200
    histogram:
    @                     _  37 ms (0, 0.00%)
    @                     _  75 ms (0, 0.00%)
    @                     _  113 ms (0, 0.00%)
    @                     _  150 ms (0, 0.00%)
    @                     _  188 ms (0, 0.00%)
    @                     _  226 ms (0, 0.00%)
    @                     _  263 ms (0, 0.00%)
    @                     _  301 ms (0, 0.00%)
                       @  _  339 ms (46, 76.67%) ^50%
       @                  _  376 ms (7, 11.67%) ^85%
      @                   _  414 ms (5, 8.33%) ^95%
    @                     _  452 ms (1, 1.67%)
    @                     _  489 ms (0, 0.00%)
    @                     _  527 ms (0, 0.00%)
    @                     _  565 ms (0, 0.00%)
    @                     _  602 ms (0, 0.00%)
    @                     _  640 ms (0, 0.00%)
    @                     _  678 ms (0, 0.00%)
    @                     _  715 ms (0, 0.00%)
    @                     _  753 ms (1, 1.67%) ^99% ^99.9%
    response times: 60 samples | min/avg/50th%/99th%/max = 303/335/318/753/753 ms
    request rate (requests/s)  : 1.011
    send rate (bytes/s)        : 189.916
    response rate (responses/s): 1.006
    receive rate (bytes/s)     : 41245.797
    failures          : 0
    response 1xx group: 0
    response 2xx group: 60
    response 3xx group: 0
    response 4xx group: 0
    response 5xx group: 0
    ----------------------------------------------------
    

    Use the Jetty Load Generator for your load testing, and report comments and issues at https://github.com/jetty-project/jetty-load-generator. Enjoy!

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

  • Jetty, ALPN & Java 8u252

    Introduction

    The Jetty Project provided to the Java community support for NPN first (the precursor of ALPN) in Java 7, and then support for ALPN in Java 8.
    The ALPN support was implemented by modifying sun.security.ssl classes, and this required that the modified classes were prepended to the bootclasspath, so the command line would like like this:

    java -Xbootclasspath/p:/path/to/alpn-boot-8.1.13.v20181017.jar ...

    However, every different OpenJDK version could require a different version of the alpn-boot jar. ALPN support is there, but cumbersome to use.
    Things improved a bit with the Jetty ALPN agent. The agent could detect the Java version and redefine on-the-fly the sun.security.ssl classes (using the alpn-boot jars). However, a new OpenJDK version could generate a new alpn-boot jar, which in turn generates a new agent version, and therefore for every new OpenJDK version applications needed to verify whether the agent version needed to be updated. A bit less cumbersome, but still another hoop to jump through.

    OpenJDK ALPN APIs in Java 9

    The Jetty Project worked with the OpenJDK project to introduce proper ALPN APIs, and this was introduced in Java 9 with JEP 244.

    ALPN APIs backported to Java 8u252

    On April 14th 2020, OpenJDK 8u252 released (the Oracle version is named 8u251, and it may be equivalent to 8u252, but there is no source code available to confirm this; we will be referring only to the OpenJDK version in the following sections).
    OpenJDK 8u252 contains the ALPN APIs backported from Java 9.
    This means that for OpenJDK 8u252 the alpn-boot jar is no longer necessary, because it’s no longer necessary to modify sun.security.ssl classes as now the official OpenJDK ALPN APIs can be used instead of the Jetty ALPN APIs.
    The Jetty ALPN agent version 2.0.10 does not perform class redefinition if it detects that the OpenJDK version is 8u252 or later. It can still be left in the command line (although we recommend not to), but will do nothing.

    Changes for libraries/applications directly using Jetty ALPN APIs

    Libraries or applications that are directly using the Jetty ALPN APIs (the org.eclipse.jetty.alpn.ALPN and related classes) should now be modified to detect whether the backported OpenJDK ALPN APIs are available. If the backported OpenJDK ALPN APIs are available, libraries or applications must use them; otherwise they must fall back to use the Jetty ALPN APIs (like they were doing in the past).
    For example, the Jetty project is using the Jetty ALPN APIs in artifact jetty-alpn-openjdk8-[client|server]. The classes in that Jetty artifact have been changed like described above, starting from Jetty version 9.4.28.v20200408.

    Changes for applications indirectly using ALPN

    Applications that are using Java 8 and that depend on libraries that provide ALPN support (such as the jetty-alpn-openjdk8-[client|server] artifact described above) must modify the way they are started.
    For an application that is still using an OpenJDK version prior to 8u252, the typical command line requires the alpn-boot jar in the bootclasspath and a library that uses the Jetty ALPN APIs (here as an example, jetty-alpn-openjdk8-server) in the classpath:

    /opt/openjdk-8u242/bin/java -Xbootclasspath/p:/path/to/alpn-boot-8.1.13.v20181017.jar -classpath jetty-alpn-openjdk8-server-9.4.27.v20200227:...

    For the same application that wants to use OpenJDK 8u252 or later, the command line becomes:

    /opt/openjdk-8u252/bin/java -classpath jetty-alpn-openjdk8-server-9.4.28.v20200408:...

    That is, the -Xbootclasspath option must be removed and the library must be upgraded to a version that supports the backported OpenJDK ALPN APIs.
    Alternatively, applications can switch to use the Jetty ALPN agent as described below.

    Frequently Asked Questions

    Was the ALPN APIs backport a good idea?

    Yes, because Java vendors are planning to support Java 8 until (at least) 2030 and this would have required to provide alpn-boot for another 10 years.
    Now instead, applications and libraries will be able to use the official OpenJDK ALPN APIs provided by Java for as long as Java 8 is supported.

    Can I use OpenJDK 8u252 with an old version of Jetty?

    Yes, if your application does not need ALPN (in particular, it does not need artifacts jetty-alpn-openjdk8-[client|server]).
    No, if your application needs ALPN (in particular, it needs artifacts jetty-alpn-openjdk8-[client|server]). You must upgrade to Jetty 9.4.28.v20200402 or later.

    Can I use OpenJDK 8u252 with library X, which depends on the Jetty ALPN APIs?

    You must verify that library X has been updated to OpenJDK 8u252.
    In practice, library X must detect whether the backported OpenJDK ALPN APIs are available; if so, it must use the backported OpenJDK ALPN APIs; otherwise, it must use the Jetty ALPN APIs.

    I don’t know what OpenJDK version my application will be run with!

    You need to make a version of your application that works with OpenJDK 8u252 and earlier, see the previous question.
    For example, if you depend on Jetty, you need to depend on jetty-alpn-openjdk8-[client|server]-9.4.28.v20200402 or later.
    Then you must add the Jetty ALPN agent to the command line, since it supports all Java 8 versions.

    /opt/openjdk-8u242/bin/java -javaagent:/path/to/jetty-alpn-agent-2.0.10.jar -classpath jetty-alpn-openjdk8-server-9.4.28.v20200408:...
    /opt/openjdk-8u252/bin/java -javaagent:/path/to/jetty-alpn-agent-2.0.10.jar -classpath jetty-alpn-openjdk8-server-9.4.28.v20200408:...

    The command lines are identical apart the Java version.
    In this way, if your application is run with with OpenJDK 8u242 or earlier, the Jetty ALPN agent will apply the sun.security.ssl class redefinitions, and jetty-alpn-openjdk8-[client|server]-9.4.28.v20200402 will use the Jetty ALPN APIs.
    Otherwise your application is run with OpenJDK 8u252 or later, the Jetty ALPN agent will do nothing (no class redefinition is necessary), and the jetty-alpn-openjdk8-[client|server]-9.4.28.v20200402 will use the OpenJDK ALPN APIs.

  • OpenJDK 11 and TLS 1.3 issues

    At the Jetty Project we have been getting reports from the community as well as seeing random failures of load tests and benchmarks that were using TLS, and the failures were only happening with Java 11 (any version up to 11.0.2).

    Jetty users also saw TLS failures in their environments and opened issues about these failure, most notably issue #2939. Following that Jetty issue, OpenJDK issue JDK-8213202 was reported by @rraptorr (kudos for that!).

    The bad news is that JDK-8213202 is not fixed in Java 11.0.2, but it has been fixed in Java 12 (since jdk-12+21), and it has been backported to the OpenJDK 11 repository (and therefore will eventually be part of a future OpenJDK 11.0.x release – hopefully 11.0.3).

    The good news is that the issue can be worked around, while waiting for a Java 11 release that fixes it.

    It may be possible that you have been running Java 11 with TLS 1.3 without any problem for months, as JDK-8213202 is difficult to reproduce and we have only seen it trigger under moderate load and even in that case not all the times.

    Upgrade to Java 12 Solution

    If you can upgrade to Java 12 (at the time of this writing Java 12 is in Release Candidate status), that will solve JDK-8213202. The upgrade to Java 12 should be a drop-in from Java 11, but we recommend you test the upgrade thoroughly.

    Stay on Java 11 Solution

    If you must/want to stay on Java 11 – it is a long-term supported release – then you can work around JDK-8213202 by disabling TLS 1.3, which is used by default in Java 11 or greater, and use TLS 1.2.

    If you are using Jetty embedded you can use this code:

    SslContextFactory sslContextFactory = new SslContextFactory();
    sslContextFactory.setExcludeProtocols("TLSv1.3");

    If you are using Jetty standalone, you can create file $JETTY_BASE/etc/disable-tls13.xml as follows:

    
    <!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure_9_3.dtd">
    <Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
      <Call name="addExcludeProtocols">
        <Arg>
         <Array type="java.lang.String">
           <Item>TLSv1.3
         </Array>
        </Arg>
      </Call>
    </Configure>

    Then you can start the Jetty standalone server in this way (from directory $JETTY_BASE):

    $ java -jar ../start.jar etc/disable-tls13.xml

    Alternatively, you can add the XML file to $JETTY_BASE/start.ini:

    ... # The existing content of your start.ini
    etc/disable-tls13.xml

    Java 11 Remarks

    Java 11.0.3 will hopefully contain the fix for JDK-8213202. It is unclear if Oracle will build a binary of OpenJDK 11.0.3 since OpenJDK 12 is due soon.

    If you have a support contract with an OpenJDK vendor, you will be able to obtain OpenJDK 11.0.3 through your vendor.

    If you don’t have a support contract with an OpenJDK vendor, you will still be able to obtain OpenJDK 11.0.3, for example through the AdoptOpenJDK Project.

    We will keep you up-to-date about the progress of this issue on this blog and on the @JettyProject Twitter account.

     

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

  • CometD 4.0.0 Released

    The CometD Project is happy to announce the availability of CometD 4.0.0.
    CometD 4.0.0 builds on top of the CometD 3.1.x series, bringing improvements and new features.
    You can find a migration guide at the official CometD documentation site.

    What’s new in CometD 4.0.0

    The main theme behind CometD 4.0.x is the complete support for asynchronous APIs.
    CometD 3.1.x had a number of APIs, in particular server-side extensions and server-side channel listeners that required to return a value from API methods that applications implemented.
    A typical example is the support for authentication during a CometD handshake: applications needed to implement SecurityPolicy.canHandshake(...):

    // CometD 3.1.x
    public class MyAppAuthenticator extends DefaultSecurityPolicy {
        @Override
        public boolean canHandshake(BayeuxServer server, ServerSession session, ServerMessage message) {
            // Call third party service via HTTP.
            CompletableFuture future = thirdParty.authenticate(message);
            return future.get(); // Blocks until the result is available.
        }
    }
    

    Because canHandshake(...) returns a boolean, authentication using a third party service via HTTP must block (via CompletableFuture.get()) until the third party service returned a response. Furthermore, it was not possible to call the third party service in a different thread, freeing the CometD thread and allowing it to handle other messages: the CometD thread still had to be blocked until a boolean response was available. This was severely harming the scalability of CometD if hundreds of threads were blocked waiting for a slow third party authentication system, and it was only due to a CometD API design mistake.
    With CometD 4.0.x we took the chance to correct this design mistake and make all CometD APIs completely asynchronous, with the introduction of a CometD Promise class: a callback that holds a future result or that is failed.
    The example above can now be written as:

    // CometD 4.0.x
    public class MyAppAuthenticator extends DefaultSecurityPolicy {
        @Override
        public void canHandshake(BayeuxServer server, ServerSession session, ServerMessage message, Promise promise) {
            // Call third party service via HTTP.
            CompletableFuture future = thirdParty.authenticate(message);
            future.whenComplete((result, failure) -> {
                if (failure == null) {
                    promise.succeed(result);
                } else {
                    promise.fail(failure);
                }
            });
        }
    }
    

    Method canHandshake(...) can now return immediately and handle other messages; when the CompletableFuture returned by the third party authentication system is completed (i.e. succeeded or failed), then also the CometD Promise is completed and the processing of that CometD message can continue.
    This apparently simple API change required a massive rewrite of the internals of CometD and few other breaking API changes, in particular, they related to how to obtain a BayeuxContext instance. In CometD 3.1.x, BayeuxContext instances could be retrieved via BayeuxServer.getContext() thanks to the fact that the BayeuxContext instance was stored in a ThreadLocal. In CometD 4.0.x, it is not possible to use ThreadLocal due to the asynchronous nature of the API: the thread that starts an asynchronous operation is not the same thread that finishes the asynchronous operation. In CometD 4.0.x, you can now obtain a BayeuxContext instance via ServerMessage.getBayeuxContext().
    A complete list of breaking and removed API can be found in the migration guide.
    Another new feature of CometD 4.0.x is the support for JPMS modules. Currently, this is achieved by adding Automatic-Module-Name entries to the relevant CometD jars. Proper support for JPMS modules via module-info.java files is scheduled for CometD 5.x.

    What’s changed in CometD 4.0.0

    CometD 4.0.x now requires JDK 8 and Jetty 9.4.x as detailed in the migration guide.

    Conclusions

    CometD 4.0.x is now the mainstream CometD release, and will be the primary focus for development and bug fixes. CometD 3.1.x enters a maintenance mode, so that only urgent or sponsored fixes will be applied to it, possibly leading to new CometD 3.1.x releases – although these will be rare.
    Work on CometD 5.x will likely require JDK 11 and Jetty 10 and will start as soon as Jetty 10 is released.