Contents


Get started with Servlet 4.0

How to use HTTP/2 server push and the new HttpServletMapping interface in your Java servlet applications

Comments

The Java™ Servlet API is a fundamental building block of mainstream server-side Java, and part of Java EE technologies such as JAX-RS for web services, JSF (JavaServer Faces), and JSP (JavaServer Pages). Java servlets also stand on their own, providing a range of features supporting dynamic web content. These include filters, web security, and features for handling HTTP requests and responses.

Servlet 4.0 is the latest version of the API, and is a core update in the Java EE 8 specification. As you'll learn in this tutorial, Servlet 4.0 is HTTP/2-ready and fully embraces server push, as well as extending it to servlet-based technologies like JSF 2.3. This tutorial also gets you started with the new HttpServletMapping interface, which enables runtime discovery of a servlet's mapping URL.

Servlets in a nutshell

A Java servlet is a server-side technology that runs over the HTTP protocol. Servlets wait for the client to send a request message to the server, and will then return a response message to the client. The request and response messages consists of two parts:

  • The header contains information about the message.
  • The body contains the payload of the message, which is its content.

In a typical exchange, the client invokes a servlet by requesting a specific URL from the browser, or from another HTTP client such as curl.

In Listing 1, the servlet is activated when the servlet path is requested. The request is delegated to the appropriate method, which is determined by the HTTP method. In this case, because the request was a GET method request, the request is handled by the Java servlet’s doGet() method.

The servlet path for the following exchange is: http://hostname/applicationroot/showlogoservlet.

Listing 1. Simple servlet implementation
@WebServlet("/showlogoservlet")
public class SimpleServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest request,
                        HttpServletResponse response) 
                        throws ServletException, IOException {

       getServletContext()
         .getRequestDispatcher("/showlogo.jsp")
         .forward(request, response);

   }
}

Top new features in Servlet 4.0

Servlet 4.0’s top new features are server push and a new API that discovers a servlet's URL mappings at runtime.

Server push is the most visible HTTP/2 enhancement, and is exposed in a servlet via the PushBuilder interface. Server push is also implemented in JavaServer Faces API and invoked during the RenderResponsePhase lifecycle phase, so that JSF pages can leverage its performance enhancements.

The new servlet mapping discovery interface, HttpServletMapping, enables a framework to obtain information about the URL that activated a given servlet request. This is likely to be of particular use to frameworks that need this information for their internal workings.

In the next sections I’ll provide an overview of server push and how it works in Java servlets, including server push in JSF 2.3. I’ll also show you an example exchange that highlights the new servlet mapping discovery feature.

Server push in Servlet 4.0

Server push enables the server to anticipate the resource requirements of a client request. It can then send those resources to the client before request processing has completed.

To imagine the benefits of server push, consider a web page that consists of images and other dependencies such as CSS and JavaScript files. The client simply makes a request for the web page. The server then analyzes the page requested, determines the resources required to render it, and proactively sends those to the client's cache.

It does all of this while it is still processing the original web page request. By the time the client receives a response, the resources it needs are already in the cache.

PushBuilder

Servlet 4.0 exposes server push via the PushBuilder interface. In order to access it, you need to obtain an instance of PushBuilder from HttpServletRequest, by calling the newPushBuilder() method. Listing 2 shows how to obtain a PushBuilder instance.

Listing 2. How to obtain a new PushBuilder
@Override
protected void doGet(HttpServletRequest request, 
                     HttpServletResponse response) 
					 throws ServletException, IOException {

   PushBuilder pushBuilder = request.newPushBuilder();

}

A new instance of PushBuilder will be returned for each invocation of the newPushBuilder() method. If server push isn’t available, newPushBuilder() will return null. In some cases, the client may refuse server push for a request transaction. Server push also will not work if the client isn’t using a secure connection. Thus, it is important to ensure that you test for a null return value before calling methods on the PushBuilder instance.

As its name hints, PushBuilder implements the Builder pattern. In this implementation, a push request is built by chaining mutator methods together. These mutator methods configure the PushBuilder instance by setting its HTTP headers, method type (GET is the only acceptable value), query string, session ID, and resource path (that is, the path to the resource that will be pushed out).

Most request headers from the original HttpServletRequest instance are simply added to the PushBuilder instance. Because they are not required for the proper function of server push, the following are not included:

  • Conditional headers
  • Range headers
  • Expect headers
  • Authorization headers
  • Referrer headers

Now let’s look how to construct and provoke a server push action.

1. Setting the resource to push

The path is the only configuration that must be set before pushing a resource to the client. To set the path you call the path() method. This method should only be called once, as it mutates the path value of the PushBuilder object. The path may start with a forward slash (“/”) to indicate that the resource path is an absolute path; otherwise the resource is deemed relative to the context path of the associated request. The path can contain a query string, which will be merged with any string set by the queryString() method.

2. Pushing the resource

Next you will call the push() method, which pushes the resource to the client. The push() method initiates the push “conversation” with the client. Behind the scenes, the client is sent a PUSH_PROMISE frame, which acts like a notification of intent to send the resource. The client can reject the resource by sending back an RST_STREAM. This mechanism allows the client to retain control of which resources it receives. The client is thus protected from becoming overloaded with resources that it doesn’t want, or which it already has in its cache.

Once an instance of the PushBuilder has been obtained it can be reused multiple times, as shown in Listing 3. The path and conditional headers are nulled, but all other fields are left as-is. These can be reused in another server push.

Listing 3. Re-using a PushBuilder instance
PushBuilder pushBuilder = request.newPushBuilder();

if (pushBuilder != null) {
   pushBuilder.path("images/hero-banner.jpg").push();
   pushBuilder.path("css/menu.css").push();
   pushBuilder.path("js/marquee.js").push();
}

In Listing 3 the path to the hero-banner.jpg is set on the PushBuilder instance via the path() method, and is pushed to the client by calling push(). The push() method is non-blocking and returns immediately so that further resources—in this case menu.css and marquee.js—can be subsequently pushed.

Using server push with JSF

JavaServer Faces already identifies the resource requirements of each page as part of its page-rendering lifecycle, so it is a natural fit for server push. Better yet, from a developer’s perspective, you don’t have to do anything different in order to activate this feature. You get it for free when you upgrade to JSF 2.3.

Listing 4 demonstrates the integration of JSF and server push.

Listing 4. Using server push in a JSF page
<h:head>
   <h:outputStylesheet library="css" name="logo.css"/>
   <h:outputScript library="js" name="logo.js"/>
   <title>JSF 2.3 ServerPush Example</title>
</h:head>
<h:body>
   <h:form>
       <h:graphicImage library="images" name="logo.jpg"/>
   </h:form>
</h:body>
</html>

The JSF page in Listing 4 requires three resources:

  • A CSS file called logo.css.
  • A JavaScript file called logo.js.
  • An image called logo.jpg.

These resources will be pushed to the client one-by-one while the JSF engine is processing and rendering the page. This happens during JSF’s render response phase. The ExternalContextImpl.encodeResourceURL() method is then called for each resource, and passed the resource’s new URL. A new PushBuilder object is obtained from the HttpServletRequest instance associated with the ExternalContext. If server push is supported, the resource is pushed to the client before the page is rendered to the client.

The HttpServletMapping interface

Servlet 4.0's new servlet mapping discovery API enables the server to do a runtime check of the URL that has caused a servlet to be invoked. For example, a request to file.ext, /path and /path/file.ext will activate the servlet with URL patterns /path/* and *.ext.

The HttpServletMapping interface supports runtime discovery of a servlet’s mapping URL. You can obtain an instance of the interface by calling getHttpServletMapping() on an instance of HttpServletRequest. You may use the following methods to obtain information about a servlet’s mapping URL:

  • getMatchValue() returns the portion of the URI path that caused the request to be matched.
  • getPattern() returns the String representation for the URL pattern.
  • getServletName() returns the String representation for the servlet name.
  • getMappingMatch() returns the type of the match represented as an MappingMatch enum value, which will be one of: CONTEXT_ROOT, DEFAULT, EXACT, EXTENSION, or PATH.

Listing 5 shows the four API methods in action.

Listing 5. Runtime servlet mapping discovery in Servlet 4.0
@WebServlet({"/path/*", "*.ext"})
public class ServletMapping extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response) 
                         throws IOException {

        HttpServletMapping mapping = request.getHttpServletMapping();
        String mapping = mapping.getMappingMatch().name();
        String value = mapping.getMatchValue();
        String pattern = mapping.getPattern();
        String servletName = mapping.getServletName();
   }

}

Smaller changes in Servlet 4.0

In addition to server push and the new HttpServletMapping interface, Servlet 4.0 includes a handful of smaller additions and changes worth noting.

  1. The Trailer response header allows the sender to include additional fields at the end of chunked messages. This is used to supply metadata that might be dynamically generated while the message body is sent, such as a message integrity check, digital signature, or post-processing status.
  2. Servlet 4.0 adds the GenericFilter and HttpFilter abstract classes, which simplify writing filters by providing minimal implementations of the lifecycle methods init() and destroy().
  3. Servlet 4.0 also integrates the new HTTP Trailer, which allows the sender to include additional fields at the end of a chunked message.
  4. The ServletContext interface has new methods:
    • addJspFile() adds the servlet with a given JSP file to the servlet context.
    • getSessionTimeout() and setSessionTimeout() provide access to the session timeout.
    • getRequestCharacterEncoding() and setRequestCharacterEncoding() provide access and mutate the default request character encoding for the current servlet context.
  5. The isRequestedSessionIdFromUrl() method on the HttpServletRequest interface has been deprecated.
  6. Default methods have been added to listener interfaces, thanks to the uplift to Java SE 8.

Conclusion

Servlet 4.0 has been released primarily to integrate the new HTTP/2 protocol and its many performance enhancing features. The PushBuilder interface offers fine-grained control over resources that are pushed to a client, which has already led to interesting cross-cutting implementations. As an example, Jetty 9 has implemented server push with the PushBuilder API in its PushCacheFilter web filter. This filter caches resources on first request. It is then able to push subsequent requests to the client, even while the request is still being processed on the server side.

While JSF 2.3 comes with server push built in, JavaServer Pages does not. JSF's integration of server push is a great boon, allowing developers to focus less on performance hacks and more on designing dynamic web pages. For developers wanting similar functionality in JSP, a bespoke solution such as a web filter is required, such as the PushCacheFilter web filter in Jetty 9.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java development, Open source, Web development
ArticleID=1060498
ArticleTitle=Get started with Servlet 4.0
publish-date=05102018