Servlets

Goal

Concepts

Library

Dependencies

Preview

Simple Hello World servlet.
@WebServlet("/helloworld")
public class HelloServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/plain");
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    response.getWriter().println("Hello, World!");
  }

}

Preparation

  1. Download and install the latest Apache Tomcat 8.5.
    • Download the source code JAR separately so that you can attach it to Eclipse for viewing servlet source code.
  2. Install the Postman Chrome app.

    Lesson

    As you've discovered in previous lessons, the most prevalent TCP/IP application-level protocol is the Hypertext Transfer Protocol (HTTP). The basic HTTP transaction consists of a request followed by a response. You've written HTTP client code that makes a request using the HTTP GET method and processed the response using JSON.

    For client software to communicate using HTTP, there must be a server software component listening for HTTP requests and sending appropriate responses. Traditionally an HTTP server was simply a program that would return static HTML documents in response to GET requests; this formed the basis of the original World Wide Web (WWW). As the web evolved, HTTP servers allowed requests to be handed off for processing by separate executable programs. This allowed the generation of dynamic content and was referred to as a Common Gateway Interface (CGI).

    CGI scripts had a central drawback in that each time a request came in the web server would be required to spawn a new execution environment for the CGI program. To address this performance hit, Java introduced the servlet specification. Rather than starting a new JVM to service each HTTP request, a web server can now keep a single JVM loaded in memory and make calls to Java code to process incoming HTTP requests. The servlet specification has continued to evolve and is now distributed as part of the Java Platform, Enterprise Edition (Java EE).

    Servlets

    Most Java HTTP technologies today are based upon servlets at their core. Servlet technology centers around an implementation of the javax.servlet.Servlet interface. A servlet container on the server will create an instance of the servlet implementation as needed and pass HTTP requests to it for processing. Writing a servlet then consists of providing an implementation of the Servlet interface and registering it with the servlet container.

    Here is the basic Servlet interface. Its Servlet.service(ServletRequest req, ServletResponse res)method is the primary doorway for servicing client requests, represented by javax.servlet.ServletRequest, and returning the appropriate response, represented by javax.servlet.ServletResponse.

    javax.servlet.Servlet
    public interface Servlet {
    
      void init(ServletConfig config) throws ServletException;
    
      ServletConfig getServletConfig();
    
      void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
    
      String getServletInfo();
    
      void destroy();
    }

    Servlet Life Cycle

    Like many objects that are managed by containers, servlets go through a life cycle analogous to the states of a thread which you studied in a previous lesson. The container will notify the servlet as it passes through these simple stages by calling the appropriate life cycle method in the servlet interface.

    1. The container creates the servlet.
    2. The container initializes the servlet by calling Servlet.init(ServletConfig config).
    3. The servlet processes client requests as needed in its Servlet.service(ServletRequest req, ServletResponse res) method.
    4. The container takes the servlet out of service and calls its Servlet.destroy() method.

    Servicing HTTP Requests

    Classes can use the Servlet interface to implement any network protocol that uses a request/response pattern. But to service HTTP requests specifically, it is usually best to extend the javax.servlet.http.HttpServlet abstract class.

    javax.servlet.http.HttpServlet
    public abstract class HttpServlet extends GenericServlet {
    
      protected void doGet(HttpServletRequest req, HttpServletResponse response)  throws ServletException, IOException {…}
    
      …
    
      protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {…}
    
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {…}
    
      protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {…}
    
      protected void doDelete(HttpServletRequest req,HttpServletResponse resp) throws ServletException, IOException {…}
    
      protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {…}
    
      protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {…}
    
      …
    
    }

    The HttpServlet base class overrides the Servlet.service(…) method to provide basic dispatching based on the HTTP method used. A separate Servlet.doXXX(…) method is provided for each HTTP method. You have already worked with the HTTP GET method. The other methods available in the HttpServlet base class should give you an idea of the HTTP methods that exist.

    HTTP GET Method

    To process the GET HTTP method, override the HttpServlet.doGet(HttpServletRequest req, HttpServletResponse resp) method and return the appropriate content. As with the more general Servlet.service(…) method, the method parameters encapsulate the client's request and the servlet's response. javax.servlet.http.HttpServletRequest extends ServletRequest, while javax.servlet.http.HttpServletResponse extends ServletResponse, providing access to HTTP-specific information.

    Returning Response Content

    To send back response content bytes, you can use the ServletResponse.getOutputStream() method of the response. If you specifically want to return text content, you can use ServletResponse.getWriter() instead. Before retrieving a Writer, you need to first call ServletResponse.setCharacterEncoding(String charset) with the name of the appropriate charset so that the ServletResponse knows how to convert the characters to bytes to send back to the client.

    Here is an implementation of a servlet that would be send back the text Hello, World! in response to an HTTP GET request.

    Hello World servlet.
    public class HelloServlet extends HttpServlet {
    
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/plain");  //set the type of response; explained below
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().println("Hello, World!");
      }
    
    }

    HTTP Content-Type Header

    In your response you will need to indicate the media type of the content you are returning. You briefly saw the use of HTTP headers when first learning Internet protocols. HTTP uses the response header Content-Type to indicate the media type of the content being returned.

    Example HTTP Response (Wikipedia)
    HTTP/1.1 200 OK
    Date: Mon, 23 May 2005 22:38:34 GMT
    Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
    Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
    ETag: "3f80f-1b6-3e1cb03b"
    Content-Type: text/html; charset=UTF-8
    Content-Length: 138
    Accept-Ranges: bytes
    Connection: close
    
    <html>
    <head>
      <title>An Example Page</title>
    </head>
    <body>
      Hello World, this is a very simple HTML document.
    </body>
    </html>

    You indicate the content type of a servlet response by calling ServletResponse.setContentType(String type) before producing content. This allows the servlet to include the Content-Type header when the response is generated.

    Specifying the media type for the Content-Type response header.
    public class HelloServlet extends HttpServlet {
    
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/plain");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().println("Hello, World!");
      }
    
    }

    Setting the Response Code

    A successful HTTP request will send back a status code of 200 (OK), and this is what HttpServlet.doGet(…) sends back by default. If you wish to send back another code such as 404 (Not Found), you may call HttpServletResponse.setStatus(int sc). The HttpServletResponse class provides constants for status codes such as HttpServletResponse.SC_OK and HttpServletResponse.NOT_FOUND; when writing servlets, it is best to use these rather than those in java.net.HttpURLConnection.

    Indicating the response code in a servlet.
    public class HelloServlet extends HttpServlet {
    
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setStatus(HttpServletResponse.SC_OK);  //optional for OK
        response.setContentType("text/plain");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().println("Hello, World!");
      }
    
    }

    Declaring a Servlet

    A servlet cannot function until it has been recognized by the servlet container, which will create and initialize, a servlet instance. The container also needs to know which requests the servlet should service. This is done by mapping some relative URI path to the servlet. The javax.servlet.annotation.WebServlet annotation provides a simple way to indicate the URL path at which the servlet should service HTTP requests in the servlet container.

    Here is an implementation of a Hello World servlet that would be registered to process HTTP GET requests sent to the helloworld path segment in some URL.

    Declaring and mapping the "Hello World" servlet com.example.HelloServlet.
    @WebServlet("/helloworld")
    public class HelloServlet extends HttpServlet {
    
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        …
      }
    
    }

    HTTP Redirects

    You've already learned that the 200 (OK) response is the most common HTTP code for indicating success. HTTP response codes are divided into categories, and the 2XX codes all indicate success of the requested operation. The 404 (Not Found) response code is one of the 4xx codes representing client-related errors.

    The 3XX response codes indicate that the HTTP request should be redirected, or performed again at another URL. In particular there is the permanent redirect 301 (Moved Permanently) and the temporary redirect 302 (Found). The former might be used if you have moved some content to another location but still want old links to work; the HTTP client should know to follow the 301 response and find the content at its new location. The temporary redirect 302 indicates that an item has temporarily been moved to another location. With either type of redirect, the Location header must indicate the new URL of the moved content.

    HTTP HEAD Method

    The HTTP HEAD method functions virtually identically to the GET method, except that it only returns the headers (not the content) of the HTTP response that would normally be sent in response to a GET request. The base HttpServlet implementation provides a default implementation for handling of the HEAD request, using the headers from the HttpServlet.doGet(…) implementation.

    Tomcat

    Apache Tomcat® is an J2EE compliant open-source server written in Java and that implements the servlet API. It easily integrates into Eclipse. To install it, simply download the appropriate compressed archive and extract the files to the desired program location.

    Installation

    1. Download the latest Tomcat 8.5.x from Tomcat 8 Software Downloads. Linux users will usually download the .tar.gz archive, while Windows users will probably want to get the .zip archive.
      1. Download the Core package under Binary Distributions.
      2.  (optional)Download the archive under Source Code Distributions.
    2. Extract the archive to the directory of your choice. Linux users may prefer /opt/tomcat, while Windows users may want to use C:\Program Files\Tomcat.

    Running Tomcat Standalone

    TODO

    Eclipse

    The Eclipse environment makes it easy for you to run an embedded Tomcat instance and deploy a servlet class in the Tomcat servlet container.

    1. Configure the a runtime environment for your Tomcat installation.
      1. Go to Window → Preferences → Server → Runtime Environment.
      2. Select Add... and choose Apache Tomcat v8.5; then select Next >.
      3. Enter the directory where you extracted Tomcat, or find the directory using Browse....
      4. Select Finish and then OK.
    2. Add a configured server and deploy your servlet.
      1. by going to Window → Show View → Other..., selecting Servers → Servers, and selecting OK. Thiswill bring up the Servers view tab.
      2. In the Servers view click the link for creating a new server, or Right-ClickNew → Server.
      3. Leave the server's name as localhost, which the hostname indicating the computer you're running on.
      4. Click Next, select your project in the Available list, and then Add to add your project to the Configured panel. This deploys your servlet to the configured Tomcat instance.
      5. Select Finish.

    You can now start and stop the server from the Servers view. By default the server will be listening on port 8080.

    Postman

    Google's Postman Chrome app is invaluable for testing and debugging HTTP applications. In its simplest configuration it performs a function similar a browser, issuing an HTTP GET request and displaying the response. But Postman also knows how to issue a variety of HTTP requests, including the HEAD method you learned about in this lesson, and allows in-depth exploration of the details of requests and responses. You can find complete instructional videos on using Postman in the See Also section below.

    Review

    Summary

    Assume that a servlet container is configured to service the /foo/* path on the localhost machine on port 8080, and that com.example.FooBarServlet extends the HttpServlet class and has an annotation @WebServlet("/bar").

    1. The user enters http://locahost:8080/foo/bar into the browser.
    2. The Tomcat server recognizes that the request is for http://locahost:8080/foo/* and routes it to the servlet container.
    3. The servlet container looks at the ending /bar and sees that this is mapped to FooBarServlet.
    4. The servlet container passes the request to FooBarServlet.service(ServletRequest req, ServletResponse res).
    5. The default Servlet.service(ServletRequest req, ServletResponse res) implementation determines that this is a GET request and delegates to HttpServlet.doGet(HttpServletRequest req, HttpServletResponse resp).
    6. The FooBarServlet has overridden doGet(…) and returns the appropriate response for http://locahost:8080/foo/bar.

    Gotchas

    In the Real World

    The decision to use a 301 (Moved Permanently) or a 302 (Found) redirect can have implications on how your web page is indexed by search engines. If you use a permanent redirect, the search engine may store the redirected URL in its index and provide that address as the official URL of your site. (If your site continues to use a temporary redirect, Google may nevertheless eventually consider it to be permanent, so the distinction between the two can be complicated in practice. See Google: We May Make Your 302 Temporary Redirects As 301 Permanent Redirects.)

    Think About It

    The servlet specification allows a single servlet to be mapped to an entire tree of paths, using e.g. @WebServlet("/vehicles/*"). This single servlet could therefore service requests for all vehicle URIs such as …/vehicles/new/12345, looking up vehicle information in a database and generating a JSON document or even an HTML web page dynamically. To the web browser client it would appear as if an HTML file was being retrieved for each vehicle, but in reality no web pages would exist on the file system—they would be produced on-the-fly as needed by a single servlet, based upon the incoming request URI.

    Self Evaluation

    Task

    Create a Booker server and test it.

    1. Create a separate package named ….booker.server. For now you can keep everything in the same booker project.
    2. Create a BookerServlet that will return the plain text string "booker" when accessing http://localhost:8080/booker/application using the HTTP GET method.
      • Verify using Postman that an HTTP GET request to http://localhost:8080/booker/application will return the correct content type and content.
      • Verify using Postman that an HTTP HEAD request to http://localhost:8080/booker/application will return the correct headers but no content.
    3. Create another servlet that will redirect HTTP GET requests for http://localhost:8080/booker/app to http://localhost:8080/booker/application. Which type of redirect should you use?
      • Verify using Postman that an HTTP HEAD request to http://localhost:8080/booker/app will be redirected correctly.

    See Also

    References

    Resources

    Acknowledgments