Blog

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

  • Java Updates, Jetty, and the Future

    There has been a tremendous amount of information, and a fair amount of disinformation, coming out over the last several months with regards to Java versioning, the effects of modularization, and how projects like Jetty may or may not respond to them. In light of that, we wanted to more comprehensively explain what we have seen thus far, where things are going, and what we are planning to do with Jetty. We are also interested in any feedback from our clients as to what their expectations might be as well as any trials and tribulations you might be having.
    With that in mind, here we go!

    Oracle’s new JDK Release Schedule

    Oracle has published an updated JDK release schedule.
    Oracle will make JDK 8 updates for critical bugs or security fixes publicly available until January 2019. After that, you need to purchase Oracle support for the JDK if you want to continue to use JDK 8 and receive the latest bugs and security fixes.
    As part of their revamped Support Roadmap, Oracle has introduced the term “Long Term Support” for certain JDK releases. However, the term may be confusing, as it practically refers only to customers that purchase Oracle support for the JDK.
    Therefore, if you don’t purchase Oracle support for the JDK, then:

    • JDK 8 will be updated up to January 2019; after that, the supported JDK version will be JDK 11.
    • JDK 9 will not be updated.
    • JDK 10 will be updated up to September 2018; after that, the supported JDK version will be JDK 11.
    • JDK 11 will be updated up to March 2019; after that, the supported JDK version will be JDK 12.
    • JDK 12 will be updated up to September 2019; after that, the supported JDK version will be JDK 13.
    • And so on.

    In different words, those that did not purchase Oracle support for the JDK will have “Long Term Support” by moving to a new JDK version every 6 months.
    Alternatively, it is possible to stay on JDK 11 for more than 6 months by purchasing Oracle support for the JDK, as outlined in the JDK release schedule linked above.

    Oracle’s JavaFX Support

    Oracle’s JavaFX support is outlined in this blog post.
    JavaFX was previously bundled with the JDK, but starting in JDK 11 this will no longer be the case, and JavaFX will be available as a separate download.

    Jetty and JDK versions

    Jetty 9.4.x will be supported on publicly available releases of the JDK from JDK 8 or later. However, we strongly encourage Jetty users to keep pace with JDK releases, to benefit from critical bug fixes, security fixes and new features. At the time of writing Jetty 9.4.x already builds and runs on JDK 8, JDK 9, JDK 10 and early access builds of JDK 11.
    JDK 11 will remove classes previously available in the JDK, as detailed in this issue on GitHub. Jetty does not need those classes, but often applications do.
    Standalone Jetty users will have the choice of either adding the jars containing those classes to their applications (for example in the WEB-INF/lib directory of a web application) or to write a custom Jetty module that exposes those classes to all web applications deployed to Jetty.
    Embedded Jetty users will need to add the jars containing those classes to the classpath.
    If you have questions as to what jars are available for your specific need that may be removed we encourage you to reach out to us early!

    Recommended JDK version migration plan

    We strongly recommend to start planning an upgrade to a JDK version greater than 8 (at this time, JDK 10) right nowMoving from JDK 8 to JDK 11 in September 2018, when JDK 11 will be out, will likely be a too big of a change and too risky.
    In particular, the update from JDK 8 to JDK 9 resulted in a possibly large number of incompatibilities, among which include:

    • Removal of tools.jar
    • Removal of JavaDB
    • Removal of the endorsed directory mechanism
    • Removal of the extension directory mechanism
    • Classloading implementation changes
    • A new Java version string scheme – leads to issues when parsing the Java version string
    • Removal of non-critical internal APIs – for example sun.misc.Base64Decoder
    • URLStreamHandler mechanism changes
    • Removal of many (50+) JVM startup options – the JVM refuses to start if a removed option is specified on the command line
    • New format for the JVM logging – big impact on GC logging

    The main problem is that many of these issues may impact your application and all the libraries your application depends on. Your application may not perform classloading, or lookup the system property “java.version”, but the libraries your application depends on may do so. Most of the issues listed above will only manifest at runtime, perhaps while executing an if statement branch that may only be entered under rare conditions.
    The changes listed above impact not only the development of your applications but also their build and their deployment. In some cases, development and deployment are the responsibility of different departments throughout a company, and it may take time to coordinate the changes.
    The best approach for the development side is to update both your build tools (for example, Maven and Maven Plugins) and your library dependencies to their latest versions.
    After that, it is a process of trial and error to make sure that your application works correctly in every possible scenario. A good test suite helps tremendously in this case, as you will be able to test most if not all of your code.
    The update from JDK 9 to JDK 10 should instead be really smooth, typically just a matter of updating the JDK: if your application works in JDK 9, most probably will work in JDK 10 without changes. The update from JDK 10 to JDK 11 may again have issues because of the classes removed from JDK 11, as discussed above.
    In light of the large number of changes that happened with JDK 9, and considering that the changes may affect your application and the libraries your application depends on (which you may need to update, or ask their maintainers to update), and further considering that the changes affect the build, development and the deployment of your applications, we strongly suggest that you start planning for these changes right now, as putting it off any longer may be too late. If you cannot finish the migration from JDK 8 to JDK 11 by January 2019, you risk running on an outdated JDK 8 release that may contain critical bugs or security issues.

    JDK Vendors

    Since OpenJDK is an open source project, many vendors have licensed the Java Test Compatibility Kit (TCK) and will produce OpenJDK builds that have passed the TCK and therefore are suitable for production use.
    Among these vendors there are:

    These vendors, excluding LJC, will offer free OpenJDK builds that are TCK-tested for the current JDK version and commercial support for previous JDK versions.
    Azul System has nicely summarized both Oracle’s and their own support plans for JDK 8 through JDK 19 in the graphs at this page.
    LJC is providing free OpenJDK builds that are TCK-tested at this page: https://adoptopenjdk.net/LJC “support” is just to build and TCK-test the tags of the OpenJDK project.
    For example:
    You decide to use JDK 9; the current version is JDK 10; a JDK security issue is discovered. Then:

    • The issue will be fixed in the OpenJDK 10 project and a new OpenJDK 10 tag will be created.
    • Oracle will issue a new micro version of JDK 10 (e.g. 10.0.2).
    • LJC will provide an OpenJDK 10 build for the newly created OpenJDK 10 tag.
    • If the issue is backported to OpenJDK 9 (and a new OpenJDK 9 tag created), then:
      • Oracle will not provide any support (will ask you to move to JDK 10.0.2)
      • Azul will provide a JDK 9 build, but only to its paying customers
      • LJC should provide a free OpenJDK 9 build for the newly created OpenJDK 9 tag.

    JakartaEE and JavaEE

    In addition to the efforts by Oracle to plot a more rapid release environment for the JDK, they have donated the entire JavaEE platform to the Eclipse Foundation. JavaEE has been subsequently rebranded as JakartaEE and is currently under the process of being migrated and re-released with the same version number. That is JavaEE 8 will be released as JakartaEE 8 once that migration process has been completed. One interesting note is there is as this point no API changes nor functionality changes for JakartaEE 8 which will carry with it the same minimal JDK 8 version.
    A point of interest, Webtide has members who are serving as co-leads on both the Servlet and Websocket specifications as well as Architecture Council representation within the Foundation itself.

    What everything means!

    The Jetty team has already, and will continue to, do the work of migrating and making sure the Jetty code base is compatible with the most up-to-date JDK versions. In addition, we will endeavor to support the minimal version of JDK that is required for the forthcoming JakartaEE specifications. As new JDK versions are released, we are constantly updating and reviewing them to guarantee Jetty remains stable and ready for consumption.
    This means that for now, it is our intention to support JDK 8, 9, 10, and 11 for Jetty 9.4.x releases. We have yet to begin Jetty 10.0.x releases due in large part to the upheaval associated with the JakartaEE code donation coupled with this relatively new accelerated JDK release schedule. We anticipate a different approach to new Jetty 10.0.x releases as they relate to JDK releases but nothing is set in stone in this regard. One approach may be to support the minimal version required by the JakartaEE version and the most current two JDK releases. It seems an untenable situation to pledge support for a minimal version like JDK 8 and every version of the JDK between official LTS versions (JDK 11 through JDK 17!).
    Regardless, we are always available to assist our clients with any questions or migration issues may come across related to this situation. If you have internal plans for how you intend to update JDK versions in the future or expectations on how Jetty will interact with future JDK releases we encourage you to share with us to help manage expectations.

  • Fast MultiPart FormData

    Jetty’s venerable MultiPartInputStreamParser for parsing MultiPart form-data has been deprecated and replaced by the much more efficient MultiPartFormInputStream, based on a new MultiPartParser. This is much faster, but less forgiving of non-compliant format. So we have implemented a legacy mode to access the old parser, but with enhancements to make logging of compliance violations possible.

    Benchmarks

    We have achieved an order of magnitude speed-up in the parsing of large uploaded content and even small content is significantly faster.
    We performed a JMH benchmark of the (new) HTTP MultiPartFormInputStream vs the (old) UTIL MultiPartInputStreamParser. Our tests were:

    • testLargeGenerated:  parses a 10MB file of random binary data
    • testParser:  parses a series of small multipart forms captured by a browser

    Our results clearly show that the new multipart processing is superior in terms of speed to the old processing:

    # Run complete. Total time: 00:02:09
    Benchmark                              (parserType)  Mode  Cnt  Score   Error  Units
    MultiPartBenchmark.testLargeGenerated          UTIL  avgt   10  0.252 ± 0.025   s/op
    MultiPartBenchmark.testLargeGenerated          HTTP  avgt   10  0.035 ± 0.004   s/op
    MultiPartBenchmark.testParser                  UTIL  avgt   10  0.028 ± 0.005   s/op
    MultiPartBenchmark.testParser                  HTTP  avgt   10  0.015 ± 0.006   s/op
    

    How To Use

    By default in Jetty 9.4, the old MultiPartInputStreamParser will be used. The default will be switched to the new MultiPartInputStreamParser in jetty-10.  To use the new parser (available since release 9.4.10)  you can change the compliance mode in the server.ini file so that it defaults to using RFC7578 instead of the LEGACY mode.

    ## multipart/form-data compliance mode of: LEGACY(slow), RFC7578(fast)
    # jetty.httpConfig.multiPartFormDataCompliance=LEGACY

    This feature can also be used programmatically by setting the compliance mode through the HttpConfiguration instance which can be obtained through the HttpConnectionFactory in the connector.

    connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration()
    .setMultiPartFormDataCompliance(MultiPartFormDataCompliance.RFC7578);
    

    Compliance Modes

    There are now two compliance modes for MultiPart form parsing:

    • LEGACY mode which uses the old MultiPartInputStreamParser in jetty-util, this will be slower but more forgiving in accepting formats that are non-compliant with RFC7578.
    • RFC7578 mode which uses the new MultiPartFormInputStream in jetty-http, this will perform faster than the LEGACY mode, however, there may be issues in receiving badly formatted MultiPart forms that were previously accepted.

    The default compliance mode is currently LEGACY, however, this will be changed to RFC7578 a future release.

    Legacy Mode Compliance Warnings

    When the old MultiPartInputStreamParser accepts a format non-compliant with the RFC, a violation is recorded as an attribute in the request. These violations include:

    The list of violations as Strings can be obtained from the request by accessing the attribute  HttpCompliance.VIOLATIONS_ATTR.

    (List<String>)request.getAttribute(HttpCompliance.VIOLATIONS_ATTR);

    Each violation string gives the name of the violation followed by a link to the RFC describing that particular violation.
    Here’s an example:
    CR_LINE_TERMINATION: https://tools.ietf.org/html/rfc2046#section-4.1.1
    NO_CRLF_AFTER_PREAMBLE: https://tools.ietf.org/html/rfc2046#section-5.1.1

    The Future

    The parser is async capable, so expect further innovations with non-blocking uploads and possibly reactive parts.

  • Getting Started with Jetty and JDK 9

    It’s finally here! Java 9 has officially been released and includes a whole host of changes and new functionality. Jetty, too, has been built with Java 9 over the past few releases as we ramp up support for the new JDK. It’s important to note that while Jetty is being built with Java 9, it currently does not support the entire suite of changes that came with it. To be clear – Jetty does not currently support Java 9 modules.
    Being that JDK 9 is still so new, Jetty’s support for it is still evolving. As such, it is imperative that users wishing to get the most out of JDK 9 and Jetty remain up-to-date as releases become available. Below is a summary of common problem areas that Jetty has resolved in the most recent releases.

    Annotation scanning

    Prior to Jetty 9.4.7,  multi-release jar files will produce exceptions trying to scan “module-info.class” for annotations. It will also not scan a multi-release jar for the correct versions of classes (i.e. Jetty will scan everything in the root and also everything in META-INF/versions).
    If any of your 3rd party jars (multi-release or not) use Java 9 APIs, then you will need to use Jetty 9.4.8 to get the updated version of ASM capable of parsing those classes.

    Classpath issues

    Pertinent only to embedded uses, prior to Jetty 9.4.8 Jetty was unable to find jars to scan for annotations, or for META-INF information such as web-fragments, resources and tag libs from the container classpath when running with Java 9. This did not apply to executing Jetty via the distribution or via the Jetty Maven Plugin.

    Compilation

    Since Jetty 9.4.7, JDK 9 is used to build Jetty (although it can still be built using JDK 8).
    JDK 9 incorporates a new switch in the javac compiler (–release) that allows to easily compile against previous JDK versions, as defined by JEP 247.
    This new compiler switch is supported by the Maven Compiler Plugin, and we use it while building Jetty with JDK 9, with the configuration –release=8, which allows Jetty to run in JDK 8.
    Only few modules (namely those that use new JDK 9 APIs, such as the jetty-alpn-java-[client|server] modules) are compiled with –release=9.
    With this compiler configuration it is less likely that usages of JDK 9 classes and/or APIs can accidentally slip into modules that are meant to be JDK 8 compliant.

    ALPN Support

    Jetty 9.4.6 only supports ALPN via the alpn-boot mechanism, which requires the alpn-boot jar to be in the bootclasspath.
    Since Jetty 9.4.7, ALPN is also supported using JDK 9 specific APIs without the need of the alpn-boot mechanism. This only works when Jetty is run using JDK 9.
    Since Jetty 9.4.8, ALPN is also supported via the Conscrypt provider, which binds natively to OpenSSL. This is supported for both JDK 8 and JDK 9.
    The advantage of using the Conscrypt provider rather than the default JDK provider is increased performance and increased ease of configuration (since there is no need for the alpn-boot mechanism).
     
    As Java continues to roll out updates and as Jetty continues to bring them into the fold, we will keep you up-to-date on how your implementations might be affected and how best to implement them into your environment.

  • Conscrypting native SSL for Jetty

    By default, Jetty uses the JSSE provider from the JVM for SSL, which has three significant problems:

    • It’s slow!
    • It doesn’t support ALPN in Java 8, which is needed for HTTP/2
    • It’s REALLY slow!

    There are workarounds for both problems: using SSL offloading and/or using our boot path patched JSSE for ALPN. Neither approach is optimal, however, especially as the interest in having connections secure within the data center continues to rise.
    We have previously looked at JNI native integration with a library like OpenSSL, but it was simply too much work to integrate and maintain. Having a well-maintained SSL integration is important as ciphers do change and exploits are found, so it is vital that updates are available as soon as the base library is updated.
    Luckily, all that hard work is now done by others and we are able to stand on the shoulders of taller giants! Google maintains BoringSSL as a fork of the OpenSSL project that is used in their Chrome and Android products, thus it is an excellent well maintained library with good security and performance. Google have also built on the work of the Netty project to develop Conscrypt as the Java library that maps the native BoringSSL API to be a compliant JSSE SecurityProvider.
    So how do you implement Conscrypt in Jetty? It is actually quite easy: instantiate an instance of Conscrypt’s OpenSslProvider; add it as a provider in the JVM’s Security class; set “Conscrypt” as the provider name on Jetty’s SslContextFactory.
    Using the Jetty distribution? Since Jetty-9.4.7, these steps can all be done by enabling the “conscrypt” module:

    cd $JETTY_BASE
    java -jar $JETTY_HOME/start.jar --add-to-start=conscrypt
    

    The integration with ALPN required a bit more work and some collaboration with the Conscrypt team. This will be included in the 9.4.8 release of Jetty as part of the conscrypt module enabled above.
    So far, we’ve had reports of almost a 10 times increase in throughput with Conscrypt! It also provides ALPN support on both Java 8 and Java 9 without the need to amend the boot path.  Try it out!

  • Testing JDK 9 with Dynamic Module Switching

    If you have been following Jetty’s adoption of Java 9, you might have read that builds using JDK 9 have started being produced. As the release of JDK 9 looms, developers are no doubt already doing everything they can to test their current implementations and platforms against the available early-access builds.
    Here at Webtide, we are no different. We’ve been testing our website on JDK 9 and are happy to report positive results. That said, switching back and forth between JDKs was not as straight forward as you might think, notably when it comes to setting JVM arguments.
    Consider the following jvm.mod file (a custom module located in our jetty.base/modules directory). We use this module to set JVM arguments when starting a Jetty standalone server.

    [exec]
    -Xms512m
    -Xmx512m
    -XX:+PrintGCDetails
    -Xloggc:logs/gc.log
    

    The problem is that -XX:+PrintGCDetails is no longer valid in JDK 9. Similarly, the -Xloggc:logs/gc.log has been deprecated (and the new format is not valid for JDK 8).
    While we could change the jvm.mod file to accept the new values for JDK 9, it does not allow for quick switching back to JDK 8 without editing the module file each time, or copy/pasting in a new module file every time we change JVM version.
    To combat this, we created a a dynamic module file that loads a dependent module based on what version of the JVM is found at startup using the Jetty java.version.platform property.
    We still define the module as normal in our jetty.base/start.ini file:

    ...
    --module=jvm
    ...
    

    But if we examine our jvm.mod file we now have:

    [depend]
    jvm${java.version.platform}
    

    On startup, once it is determined which version of the Java platform we are running (8 or 9 in this case), it will load one of two module files, either jetty.base/modules/jvm8.mod or jetty.base/modules/jvm9.mod. Our jvm8.mod file is identical to the file we used at the start of this example. The jvm9.mod file substitutes in the correct values for JDK 9 so that we do not face errors on startup:

    [exec]
    -Xms512m
    -Xmx512m
    -Xlog:gc*:file=logs/gc.log
    

    In this way we were able to switch back and forth between JDK 8 and JDK 9 without having to manually change arguments each time.
    We hope this helps you with your own testing!

  • Jetty ReactiveStreams HTTP Client

    ReactiveStreams has gained a lot of attention recently, especially because of its inclusion in JDK 9 in the Flow class.
    A number of libraries have been written on top of ReactiveStreams that provide a functional-style API that makes asynchronous processing of a data stream very easy.
    Notable examples of such libraries are RxJava 2 and Spring Reactor. ReactiveStreams provides a common API for all these libraries so that they are interoperable, and interoperable with any library that offers a ReactiveStreams API.
    It is not uncommon that applications (especially REST applications) need to interact with a HTTP client to either consume or produce a stream of data. The typical example is an application making REST calls to a server and obtaining response content that needs to be transformed using ReactiveStreams APIs.
    Another example is having a stream of data that you need to upload to a server using a REST call.
    It’s evident that it would be great if there was a HTTP client that provides ReactiveStreams API, and this is what we have done with the Jetty ReactiveStreams HttpClient.
    Maven coordinates:

    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-reactive-httpclient</artifactId>
      <version>0.9.0</version>
    </dependency>
    

    The Jetty ReactiveStreams HttpClient is a tiny wrapper over Jetty’s HttpClient; Jetty’s HttpClient was already fully non-blocking and already had full support for backpressure, so wrapping it with the ReactiveStreams API was fairly simple.
    You can find detailed usage examples at the project page on GitHub.

  • Building Jetty with JDK 9

    The Jetty Project has been trying to to build Jetty using JDK 9 for some time now.
    We still have a number of things to fix (a few test cases and integration with ASM for class scanning), but overall we have reached a point where we can build Jetty with JDK 9 and will start using JDK 9 to build Jetty releases.
    This process started with our first attempts using older JDK 9 releases, and the results were ugly. Until recently the Maven Plugins required to build Jetty were not updated to work with JDK 9, so trying to build Jetty with JDK 9 was not possible. After most of the Maven Plugins released versions compatible with JDK 9, fiddling with MAVEN_OPTS and adding a number of --add-opens we had our first success, but it was not pretty.
    Fast forward to the JDK 9 release candidate (JDK 9+181) and we could build Jetty without too much effort, mostly thanks to the latest changes applied to the JDK module system. Below is a report of what we had to do, which will hopefully be of use to others.
    The Jetty Project is a multi-module Maven project where we want to maintain JDK 8 compatibility, but we also want to build 2 modules that are JDK 9-specific (the client and server JDK 9 ALPN implementations).
    First and foremost, you’ll want to use the latest versions of the Maven Plugins, especially the maven-compiler-plugin (as of today at 3.6.2) and the maven-javadoc-plugin (as of today at 3.0.0-M1). Make sure you have them all declared in a <pluginManagement> section, so that you specify their version only there (and only once).
    It’s fairly easy to build a module only when building with JDK 9:

    <project ...>
      ...
      <profiles>
        <profile>
          <id>jdk9</id>
          <activation>
            <jdk>[1.9,)</jdk>
          </activation>
          <modules>
            <module>jdk9-specific-module</module>
          </modules>
        </profile>
      </profiles>
    </project
    

    Next, we want to target JDK 8 for all other modules. This is typically done by configuring the maven-compiler-plugin, specifying <target>1.8</target>.
    It turns out that this is not enough though, because while the JDK 9 compiler will produce class files compatible with JDK 8, it will compile against a JDK 9 runtime with the risk that a JDK 9 only class or method was used by mistake in the source code. Even if you are careful and you don’t use JDK 9 classes or methods in your source code, you can still hit binary incompatibilities that exist between JDK 8 and JDK 9.
    Take, for example, the class java.nio.ByteBuffer.
    In JDK 8 it inherits method limit(int) from the parent class, java.nio.Buffer, and the exact signature is:

    public final Buffer limit(int newLimit)
    

    In JDK 9 this has changed; method limit(int) is now overridden in java.nio.ByteBuffer to covariantly return java.nio.ByteBuffer, so the signature is now:

    public ByteBuffer limit(int newLimit)
    

    The difference in return type causes a binary incompatibility, so that if you compile a class that uses ByteBuffer.limit(int) with JDK 9 and try to run with JDK 8 it fails with:

    java.lang.NoSuchMethodError: java.nio.ByteBuffer.limit(I)Ljava/nio/ByteBuffer;
    

    Indeed, JDK 8 does not have that method with that specific signature.
    Luckily, the JDK 9 compiler has a new switch, --release, that allows to easily target previous JDKs (as specified by JEP 247).
    JDK 9 comes with a file ($JDK_HOME/lib/ct.sym) that is a zipped file (you can view its content in a normal file manager) containing directories for the supported target platforms (JDK 6, 7, 8 and 9), and each directory contains all the symbols for that specific JDK platform.
    The latest maven-compiler-plugin supports this new compiler switch, so compiling correctly is now just a matter of simple configuration:

    <project ...>
      <pluginManagement>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.2</version>
            <configuration>
              <source>1.8</source>
              <target>1.8</target>
            </configuration>
          </plugin>
        </plugins>
      </pluginManagement>
      <profiles>
        <profile>
          <id>jdk9</id>
          <activation>
            <jdk>[1.9,)</jdk>
          </activation>
          <build>
            <plugins>
              <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                  <release>8</release>
                </configuration>
              </plugin>
            </plugins>
          </build>
        </profile>
      </profiles>
    </project>
    

    In the <pluginManagement> section, we configure the maven-compiler-plugin with source and target to be JDK 8. This section will be taken into account when compiling using JDK 8.
    In the profile, activated only when compiling using JDK 9, we configure the maven-compiler-plugin with release to be JDK 8, so that the JDK 9 compiler generates the right class files using the symbols for JDK 8 contained in ct.sym.
    Finally, we uncovered a strange issue that we think is a Javadoc bug, apparently also hit by the Lucene Project (see https://github.com/eclipse/jetty.project/issues/1741). For us the workaround was simple, just converting an anonymous inner class into a named inner class, but your mileage may vary.
    Hopefully this blog will help you build your projects with Maven and JDK 9.