Updated: 2017-04-19 11:46 -0500: Called out the need to use exactly JDK 1.8.0_121 and why.

I still recommend JSF 2.3 EG member and major Mojarra committer Arjan Tijms' blog post for a feature overview of JSF 2.3, but this post gives a brief look into a feature I consider a key validation of the two Java EE 8 specs to which I contribute: Servlet 4.0 and JSF 2.3. The feature is HTTP/2 Server Push, and it's the biggest new feature in Servlet 4.0 in terms of API impact. This blog post shows how Mojarra 2.3.1-SNAPSHOT uses Servlet 4.0 Server Push and solicits testing of this feature for eventual inclusion in Mojarra 2.3.1, and hopefully into GlassFish 5.0.

The Origin of the Feature

Way back at the beginning of Servlet 4.0 in July 2014, when HTTP/2 itself was not even yet final, the idea of using Server Push for JSF was already apparent. After all, the whole point of the feature, from the HTTP/2 protocol level, is to increase the perceived performance of web pages by allowing the server to start sending the assets of the web page before the browser has even started parsing the web page to discover what assets it needs. (I like to call this the Matthew 6:8 feature of HTTP/2.) Web Frameworks such as JSF are in a great position to automatically leverage this feature because the act of authoring the application causes the page author to explicitly indicate at runtime which assets go with which pages. In other words, JSF users get this feature for free without having to change their app in the least.

The Specification of the Feature

The Servlet Expert Group, in particular Greg Wilkins, has crafted an easy-to-use way to take advantage of Server Push: simply ask the current HttpServletRequest for a newPushBuilder, manipulate the PushBuilder API as desired, and then call push() to initiate sending the bytes of the resource to the client.

From the JSF side, the use of the feature is not captured in the JavaDoc. Rather, it is in the spec PDF section 2.2.6. This section is a high level overview of what happens in the Render Response phase of the request processing lifecycle. We intentionally left the push requirements very high level to allow maximum latitude in implementation. We also had to specify it well in advance of Servlet 4.0 going final due to scheduling constraints with finishing JSF 2.3.

The Implementation of the Feature

I am very thankful to my co-spec lead Dr. Shing Wai Chan for implementing the GlassFish side of this feature, and Ryan Lubke for implementing the underlying Grizzly support for HTTP/2, including push. Ryan had to do several last minute tweaks to get the implementation this far, and it's not 100% done yet. That's why I'm hoping you'll try it.

On the JSF side, I modified the implementation of ExternalContext.encodeResourceURL() to push the resource using the PushBuilder API. Because our tests run Mojarra against both GlassFish 4.1.1 and GlassFish 5.0 trunk, I had to code it such that the feature only kicks in if Servlet 4.0 is actually available.

Testing the Feature

To test the feature, you must first make sure you are using exactly JDK 1.8.0_121, no more no less. The reason for this precise requirement is explained in this email to the Servlet EG. Then, download the GlassFish nightly from <http://download.oracle.com/glassfish/5.0/nightly/> (from 2017-04-17 or later) and replace the javax.faces.jar in the modules directory with org.glassfish:javax.faces:2.3.1-SNAPSHOT (or later).

Once your GlassFish has been patched to have the latest Mojarra, deploy an app that has lots of resources. I suggest the PrimeFaces 6.0 showcase. Once deployed the easiest way to see HTTP/2 in action is to use Chrome with Developer Tools to hit <https://localhost:8181/showcase-6.0/index.xhtml> Note that you have to jump through some extra hoops because Chrome finds it easiest to only support HTTP/2 over TLS.

Image of Chrome Your connection is not private warning

Image of Chrome Your connection is not private warning

Once the PrimeFaces is being served over HTTPS to Chrome from your patched GlassFish 5.0, you can visit the "Compare" widget within the "Multimedia" section. In another Chrome tab, open up <chrome://net-internals/#events&q=type:HTTP2_SESSION>. Find the lowest HTTP2_SESSION item to localhost:8181 in the list and check the checkbox. Do a find in page for lara-ps3. You can see that this image was pushed with server push, as shown next.

Image of Chrome Your connection is not private warning

You can use find in page to look for other hits on HTTP2_SESSION_RECV_PUSH_PROMISE to see resources being pushed.

Clicking over to View->Developer->Developer Tools, click the "Disable cache" checkbox and reload the page. Look at the Waterfall column ta see the impact that push has on page load times. You can see that some of the resources are being delivered before the page even finishes loading.

Image of Chrome Your connection is not private warning

It is beyond the scope of this article to explore this vital aspect of push, but I hope someone else will do so. After all, what's the point of the feature if it does not make page load times seem faster.

Summary

This feature is still in SNAPSHOT form. I need testing to verify it works for some more cases. If it causes any trouble at all, we may have to put it behind a com.sun.faces context-param. Please leave comments here with your findings.