Chapter 2

The Servlet API

In Chapter 1, “Getting Started” you learned how to use NetBeans to write a Java web application. It is now time to delve into the technology behind it all, Servlet. Understanding the Servlet API is your gateway to becoming a formidable Java web developer. There are more than seventy types in this API and it is imperative that you be familiar with the most important ones.

This chapter introduces the classes and interfaces in the Servlet API that you should know. In addition, it teaches you to write servlets. All examples in this chapter can be found in the servletapi1, servletapi2 and servletapi3 sample applications.

Servlet API Overview

The Servlet API comes in four Java packages. The packages are as follows.

  • javax.servlet. Contains classes and interfaces that define the contract between a servlet and a servlet container.
  • javax.servlet.http. Contains classes and interfaces that define the contract between an HTTP servlet and a servlet container.
  • javax.servlet.annotation. Contains annotations to annotate servlets, filters, and listeners. It also specifies metadata for annotated components.
  • javax.servlet.descriptor. Contains types that provide programmatic access to a web application’s configuration information.

This chapter focuses on members of javax.servlet and javax.servlet.http.

The javax.servlet Package

Figure 2.1 shows the main types in javax.servlet.

Figure 2.1: Prominent members of javax.servlet

At the center of Servlet technology is Servlet, an interface that all servlet classes must implement either directly or indirectly. You implement it directly when you write a servlet class that implements Servlet. You implement it indirectly when you extend a class that implements this interface.

The Servlet interface defines a contract between a servlet and the servlet container. The contract boils down to the promise by the servlet container to load the servlet class into memory and call specific methods on the servlet instance. There can only be one instance for each servlet type in an application.

A user request causes the servlet container to call a servlet’s service method, passing an instance of ServletRequest and an instance of ServletResponse. The ServletRequest encapsulates the current HTTP request so that servlet developers do not have to parse and manipulate raw HTTP data. The ServletResponse represents the HTTP response for the current user and makes it easy to send response back to the user.

For each application the servlet container also creates an instance of ServletContext. This object encapsulates the environment details of the context (application). There is only one ServletContext for each context. For each servlet instance, there is also a ServletConfig that encapsulates the servlet configuration.

Let’s first look at the Servlet interface. Other interfaces mentioned above will be explained in the other sections of this chapter.

Servlet

The Servlet interface defines these five methods.

void init(ServletConfig config) throws ServletException
void service(ServletRequest request, ServletResponse response) 
        throws ServletException, java.io.IOException
void destroy()
java.lang.String getServletInfo()
ServletConfig getServletConfig()

Note that the convention for writing a Java method signature is to use fully-qualified names for types that are not in the same package as the type containing the method. As such, in the signature of the service method, javax.servlet.ServletException, which is in the same package as Servlet, is written without the package information whereas java.io.Exception is written fully.

init, service, and destroy are lifecycle methods. The servlet container invokes these three methods according to these rules.

  • init. The servlet container invokes this method the first time the servlet is requested. This method is not called at subsequent requests. You use this method to write initialization code. When invoking this method, the servlet container passes a ServletConfig. Normally, you will assign the ServletConfig to a class level variable so that this object can be used from other points in the servlet class.
  • service. The servlet container invokes this method each time the servlet is requested. You write the code that the servlet is supposed to do here. The first time the servlet is requested, the servlet container calls the init method and the service method. For subsequent requests, only service is invoked.
  • destroy. The servlet container invokes this method when the servlet is about to be destroyed. This occurs when the application is unloaded or when the servlet container is being shut down. Normally, you write clean-up code in this method.

The other two methods in Servlet are non-life cycle methods: getServletInfo and getServletConfig.

  • getServletInfo. This method returns the description of the servlet. You can return any string that might be useful or even null.
  • getServletConfig. This method returns the ServletConfig passed by the servlet container to the init method. However, in order for getServletConfig to return a non-null value, you must have assigned the ServletConfig passed to the init method to a class level variable. ServletConfig is explained in the section “ServletConfig” in this chapter.

An important point to note is thread safety. A servlet instance is shared by all users in an application, so class-level variables are not recommended, unless they are read-only or members of the java.util.concurrent.atomic package.

The next section, “Writing A Basic Servlet Application,” shows how you can write a Servlet Implementation.

Writing A Basic Servlet Application

Writing a servlet application is surprisingly easy. All you have to do is create a directory structure and place your servlet classes in a certain directory. If you are using NetBeans, then NetBeans will create the correct directory structure for you.

In this section you’ll learn how to write a simple servlet application named servletapi1. Initially it will contain one servlet, MyServlet, which sends a greeting to the user.

You need a servlet container to run your servlets. Tomcat, an open source servlet container, is available free of charge and runs on any platform where Java is available. If you are using NetBeans, every time you run a web application, the IDE will also start Tomcat. If you are not using NetBeans, Appendix C, “Tomcat” shows how to install Tomcat and manage it outside an IDE.

Writing and Compiling the Servlet Class

After making sure you have a servlet container on your local machine, the next step is to write and compile a servlet class. The servlet class for this example, MyServlet, is given in Listing 2.1. By convention, the name of a servlet class is suffixed with Servlet.

Listing 2.1: The MyServlet class

package servletapi1;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "MyServlet", urlPatterns = { "/my" })
public class MyServlet implements Servlet {
    
    private transient ServletConfig servletConfig;

    @Override
    public void init(ServletConfig servletConfig)
            throws ServletException {
        this.servletConfig = servletConfig;
    }
    
    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public String getServletInfo() {
        return "My Servlet";
    }

    @Override
    public void service(ServletRequest request,
            ServletResponse response) throws ServletException,
            IOException {
        String servletName = servletConfig.getServletName();
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<!DOCTYPE html>"
                + "<html>"
                + "<body>Hello from " + servletName 
                + "</body>"
                + "</html>");
    }

    @Override
    public void destroy() {
    }    
}

The first thing that you may notice when reading the code in Listing 2.1 is this annotation.

@WebServlet(name = "MyServlet", urlPatterns = { "/my" })

The WebServlet annotation type is used to declare a servlet. You can name the servlet as well as tell the container what URL invokes the servlet. The name attribute is optional and, if present, ordinarily given the name of the servlet class. What’s important is the urlPatterns attribute, which is also optional but almost always present. In MyServlet, urlPattern tells the container that the pattern /my should invoke the servlet.

Note that a URL pattern must begin with a forward slash.

The servlet’s init method is called once and sets the private transient servletConfig variable to the ServletConfig object passed to the method.

    private transient ServletConfig servletConfig;

    @Override
    public void init(ServletConfig servletConfig)
            throws ServletException {
        this.servletConfig = servletConfig;
    }

You need to assign the ServletConfig passed to init to a class variable if you intend to use the ServletConfig from inside your servlet.

The service method sends the String “Hello from MyServlet” to the browser. service is invoked for every incoming HTTP request that targets the servlet.

If you are using an IDE, you never have to worry about having to compile a servlet class manually. If you are not using an IDE, to compile the servlet you have to include the types in the Servlet API in your class path. Tomcat comes with the servlet-api.jar file that packages members of the javax.servlet and javax.servlet.http packages. The jar file is located in the lib directory under Tomcat’s installation directory.

Application Directory Structure

A servlet application must be deployed in a certain directory structure. Figure 2.2 shows the directory structure for this application.

Figure 2.2: The application directory

The web directory at the top of the structure is the application directory. Under the application directory is a WEB-INF directory. It may have the following subdirectories, both optional:

  • classes. Your servlet classes and other Java classes must reside here. The directories under classes reflect the class package. In Figure 2.2 there is one class deployed, servletapi1.MyServlet.
  • lib. Deploy jar files required by your servlet application here. The Servlet API jar file does not need to be deployed here because the servlet container already has a copy of it.

A servlet/JSP application normally has JSP pages, HTML files, image files, and other resources. These should go under the application directory and are often organized in subdirectories. For instance, all image files can go to an image directory, all JSP pages to jsp, and so on.

Any resource you put under the application directory is directly accessible to the user by typing the URL to the resource. If you want to include a resource that can be accessed by a servlet but not accessible to the user, put it under WEB-INF or a directory under it.

Invoking the Servlet

To test your servlet, right-click on the servlet class in NetBeans and select Run File. And then click OK on the dialog that appears. The output should be similar to Figure 2.3.

Figure 2.3: Response from MyServlet

ServletRequest

For every HTTP request, the servlet container creates an instance of ServletRequest and passes it to the servlet’s service method. The ServletRequest encapsulates information about the request.

These are some of the methods in the ServletRequest interface.

public int getContentLength()

Returns the number of bytes in the request body. If the length is not known, this method returns -1.

public java.lang.String getContentType()

Returns the MIME type of the request body or null if the type is not known.

public java.lang.String getParameter(java.lang.String name)

Returns the value of the specified request parameter.

public java.lang.String getProtocol()

Returns the name and version of the protocol of this HTTP request.

getParameter is the most frequently used method in ServletRequest. A common use of this method is to return the value of an HTML form field. You’ll learn how you can retrieve form values in the section “Working with Forms” later in this chapter.

getParameter can also be used to get the value of a query string. For example, if a servlet is invoked using this URI

http://domain/context/servletName?id=123

you can retrieve the value of id from inside your servlet using this statement:

String id = request.getParameter("id");

Note that getParameter returns null if the parameter does not exist.

In addition to getParameter, you can also use getParameterNames, getParameterMap, and getParameterValues to retrieve form field names and values as well as query strings. See the section “HttpServlet” for examples of how you can use these methods.

ServletResponse

The javax.servlet.ServletResponse interface represents a servlet response. Prior to invoking a servlet’s service method, the servlet container creates a ServletResponse and pass it as the second argument to the service method. The ServletResponse hides the complexity of sending response to the browser.

One of the methods defined in ServletResponse is the getWriter method, which returns a java.io.PrintWriter that can send text to the client. By default, the PrintWriter object uses ISO-8859-1 encoding.

When sending a response to the client, most of the time you send it as HTML. I therefore assume that you are familiar with HTML. If not, don’t worry. Appendix A, “HTML” provides a tutorial.

Note

There is also another method that you can use to send output to the browser: getOutputStream. However, this method is for sending binary data, so in most cases you will use getWriter and not getOutputStream. See Chapter 13, “File Download” for instructions on how to send binary content.

Before sending any HTML tag, you should set the content type of the response by calling the setContentType method, passing “text/html as an argument. This is how you tell the browser that the content type is HTML. Most browsers by default render a response as HTML in the absence of a content type. However, some browsers will display HTML tags as plain text if you don’t set the response content type.

You have used ServletResponse in MyServlet in Listing 2.1. You’ll see it used in other applications in this chapter and next chapters.

ServletConfig

The servlet container passes a ServletConfig to the servlet’s init method when the servlet container initializes the servlet. The ServletConfig encapsulates configuration information that you can pass to a servlet through @WebServlet or the deployment descriptor. The deployment descriptor is an XML file that contain configuration values. It may or may not be present in a servlet/JSP application and is discussed in Chapter 20, “Deployment.”

Every piece of information passed to the ServletConfig is called an initial parameter. An initial parameter has two components: key and value.

To retrieve the value of an initial parameter from inside a servlet, call the getInitParameter method on the ServletConfig. The signature of getInitParameter is as follows.

java.lang.String getInitParameter(java.lang.String name)

In addition, the getInitParameterNames method returns an Enumeration of all initial parameter names:

java.util.Enumeration<java.lang.String> getInitParameterNames()

For example, to retrieve the value of a contactName parameter, use this.

String contactName = servletConfig.getInitParameter("contactName");

On top of getInitParameter and getInitParameterNames, ServletConfig offers another useful method, getServletContext. Use this method to retrieve the ServletContext from inside a servlet. See the section “ServletContext” later in this chapter for a discussion about this object.

As an example of ServletConfig, let’s add a servlet named ServletConfigDemoServlet to servletapi1. The new servlet is given in Listing 2.2.

Listing 2.2: The ServletConfigDemoServlet class

package servletapi1;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "ServletConfigDemoServlet", 
    urlPatterns = { "/servletConfigDemo" },
    initParams = {
        @WebInitParam(name="admin", value="Harry Taciak"),
        @WebInitParam(name="email", value="[email protected]")
    }
)
public class ServletConfigDemoServlet implements Servlet {
    private transient ServletConfig servletConfig;

    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    @Override
    public void init(ServletConfig servletConfig) 
            throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    public void service(ServletRequest request, 
            ServletResponse response)
            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<!DOCTYPE html>"
                + "<html>"
                + "<body>" 
                + "Admin:" + admin 
                + "<br/>Email:" + email
                + "</body></html>");
    }

    @Override
    public String getServletInfo() {
        return "ServletConfig demo";
    }
    
    @Override
    public void destroy() {
    }    
}

As you can see in Listing 2.2, you pass two initial parameters (admin and email) to the servlet in the initParams attribute in @WebServlet:

@WebServlet(name = "ServletConfigDemoServlet", 
    urlPatterns = { "/servletConfigDemo" },
    initParams = {
        @WebInitParam(name="admin", value="Harry Taciak"),
        @WebInitParam(name="email", value="[email protected]")
    }
)

You can invoke ServletConfigDemoServlet using this URL:

http://localhost:8080/servletapi1/servletConfigDemo

The result should be similar to that in Figure 2.4.

Figure 2.4: ServletConfigDemoServlet in action

Alternatively, you can pass initial parameters in the deployment descriptor. Utilizing the deployment descriptor for this purpose is easier than using @WebServlet since the deployment descriptor is a text file and you can edit it without recompiling the servlet class.

The deployment descriptor is discussed in the section “Using the Deployment Descriptor” later in this chapter and in Chapter 20, “Deployment.”

ServletContext

The ServletContext represents the servlet application. There is only one context per web application. In a distributed environment where an application is deployed simultaneously to multiple containers, there is one ServletContext object per Java Virtual Machine.

You can obtain the ServletContext by calling the getServletContext method on the ServletConfig. In addition, there is also a getServletContext method in ServletRequest, that returns the same ServletContext.

The ServletContext is there so that you can share information that can be accessed from all resources in the application and to enable dynamic registration of web objects. The former is done by storing objects in an internal Map within the ServletContext. Objects stored in ServletContext are called attributes.

The following methods in ServletContext deal with attributes:

java.lang.Object getAttribute(java.lang.String name)
java.util.Enumeration<java.lang.String> getAttributeNames()
void setAttribute(java.lang.String name, java.lang.Object object)
void removeAttribute(java.lang.String name)

Examples on these methods are given in Chapter 9, “Listeners.” Using ServletContext to register web objects dynamically is discussed in Chapter 22, “Dynamic Registration and Servlet Container Initializers.”

GenericServlet

The preceding examples showed how to write servlets by implementing the Servlet interface. However, did you notice that you had to provide implementations for all the methods in Servlet even though some of them did not contain code? In addition, you needed to preserve the ServletConfig object into a class level variable.

Fortunately, the GenericServlet abstract class comes to the rescue. In keeping with the spirit of easier code writing in object-oriented programming, GenericServlet implements both Servlet and ServletConfig and perform the following tasks:

  • Assign the ServletConfig in the init method to a class level variable so that it can be retrieved by calling getServletConfig.
  • Provide default implementations of all methods in the Servlet interface.
  • Provide methods that wrap the methods in the ServletConfig.

GenericServlet preserves the ServletConfig object by assigning it to a class level variable servletConfig in the init method. Here is the implementation of init in GenericServlet.

public void init(ServletConfig servletConfig) 
        throws ServletException {
    this.servletConfig = servletConfig;
    this.init();
}

However, if you override this method in your class, the init method in your servlet will be called instead and you have to call super.init(servletConfig) to preserve the ServletConfig. To save you from having to do so, GenericServlet provides a second init method, which does not take arguments. This method is called by the first init method after ServletConfig is assigned to a variable servletConfig. Here is the implementation of the first init method in GenericServlet.

public void init(ServletConfig servletConfig) 
        throws ServletException {
    this.servletConfig = servletConfig;
    this.init();
}

This means, you can write initialization code by overriding the no-argument init method and the ServletConfig will still be preserved by the GenericServlet instance.

The GenericServletDemoServlet class in Listing 2.3 is a rewrite of ServletConfigDemoServlet in Listing 2.2. Note that the new servlet extends GenericServlet instead of implementing Servlet.

Listing 2.3: The GenericServletDemoServlet class

package servletapi1;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "GenericServletDemoServlet", 
        urlPatterns = { "/generic" },
        initParams = {
            @WebInitParam(name="admin", value="Harry Taciak"),
            @WebInitParam(name="email", value="[email protected]")
        }
)
public class GenericServletDemoServlet extends GenericServlet {
    
    private static final long serialVersionUID = 62500890L;

    @Override
    public void service(ServletRequest request, 
            ServletResponse response)
            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<!DOCTYPE html>"
                + "<html><head></head><body>" 
                + "Admin:" + admin
                + "<br/>Email:" + email
                + "</body></html>");
    }
}

As you can see, by extending GenericServlet you do not need to override methods that you don’t plan to change. As a result, you have cleaner code. In Listing 2.3, the only method overridden is the service method. Also, there is no need to preserve the ServletConfig yourself.

Invoke the servlet using this URL and the result should be similar to that of ServletConfigDemoServlet.

http://localhost:8080/servletapi1/generic

Even though GenericServlet is a nice enhancement to Servlet, it is not something you use frequently, however, as it is not as advanced as HttpServlet. HttpServlet is the real deal and used in real-world applications. It is explained in the next section, “HTTP Servlets.”

HTTP Servlets

Most, if not all, servlet applications you write will work with HTTP. This means, you can make use of the features offered by HTTP. The javax.servlet.http package is the second package in the Servlet API that contains classes and interfaces for writing servlet applications. Many of the types in javax.servlet.http override those in javax.servlet.

Figure 2.5 shows the main types in javax.servlet.http.

Figure 2.5: The more important members of javax.servlet.http

HttpServlet

The HttpServlet class overrides the javax.servlet.GenericServlet class. When using HttpServlet, you will also work with the HttpServletRequest and HttpServletResponse objects that represent the servlet request and the servlet response, respectively. The HttpServletRequest interface extends javax.servlet.ServletRequest and HttpServletResponse extends javax.servlet.ServletResponse.

HttpServlet overrides the service method in GenericServlet and adds another service method with the following signature:

protected void service(HttpServletRequest request,
        HttpServletResponse response) 
        throws ServletException, java.io.IOException

The difference between the new service method and the one in javax.servlet.Servlet is that the former accepts an HttpServletRequest and an HttpServletResponse, instead of a ServletRequest and a ServletResponse.

The servlet container, as usual, calls the original service method in javax.servlet.Servlet, which in HttpServlet is written as follows:

public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    service(request, response);
}

The original service method downcasts the request and response objects from the servlet container to HttpServletRequest and HttpServletResponse, respectively, and call the new service method. The downcasting is always successful because the servlet container always passes an HttpServletRequest and an HttpServletResponse when calling a servlet’s service method, to anticipate the use of HTTP. Even if you are implementing javax.servlet.Servlet or extending javax.servlet.GenericServlet, you can downcast the servlet request and servlet response passed to the service method to HttpServletRequest and HttpServletResponse, respectively.

The new service method in HttpServlet then examines the HTTP method used to send the request (by calling request.getMethod) and call one of the following methods: doGet, doPost, doHead, doPut, doTrace, doOptions, and doDelete. Each of the seven methods represents an HTTP method. doGet and doPost are the most often used. As such, you rarely need to override the service methods anymore. Instead, you override doGet or doPost or both doGet and doPost.

To summarize, there are two features in HttpServlet that you do not find in GenericServlet:

  • Instead of the service method, you will override doGet, doPost, or both of them. In rare cases, you will also override any of these methods: doHead, doPut, doTrace, doOptions, doDelete.
  • You will work with HttpServletRequest and HttpServletResponse, instead of ServletRequest and ServletResponse.

HttpServletRequest

HttpServletRequest represents the servlet request in the HTTP environment. It extends the javax.servlet.ServletRequest interface and adds several methods. Some of the methods added are as follows.

java.lang.String getContextPath()

Returns the portion of the request URI that indicates the context of the request.

Cookie[] getCookies()

Returns an array of Cookie objects.

java.lang.String getHeader(java.lang.String name)

Returns the value of the specified HTTP header.

java.lang.String getMethod()

Returns the name of the HTTP method with which this request was made.

java.lang.String getQueryString()

Returns the query string in the request URL.

HttpSession getSession()

Returns the session object associated with this request. If none is found, creates a new session object.

HttpSession getSession(boolean create)

Returns the current session object associated with this request. If none is found and the create argument is true, create a new session object.

You will learn to use these methods in the chapters to come.

HttpServletResponse

HttpServletResponse represents the servlet response in the HTTP environment. Here are some of the methods defined in it.

void addCookie(Cookie cookie)

Adds a cookie to this response object.

void addHeader(java.lang.String name, java.lang.String value)

Adds a header to this response object.

void sendRedirect(java.lang.String location)

Sends a response code that redirects the browser to the specified location.

You will learn these methods further in the next chapters.

Working with HTML Forms

A web application almost always contains one or more HTML forms to take user input. You can easily send an HTML form from a servlet to the browser. When the user submits the form, values entered in the form are sent to the server as request parameters.

The value of an HTML input field (a text field, a hidden field, or a password field) or text area is sent to the server as a string. An empty input field or text area sends an empty string. As such, ServletRequest.getParameter that takes an input field name never returns null.

An HTML select element also sends a string to the server. If none of the options in the select element is selected, the value of the option that is displayed is sent.

A multiple-value select element (a select element that allows multiple selection and is indicated by <select multiple>) sends a string array and has to be handled by ServletRequest.getParameterValues.

A checkbox is a bit extraordinary. A checked checkbox sends the string “on” to the server. An unchecked checkbox sends nothing to the server and ServletRequest.getParameter(fieldName) returns null.

Radio buttons send the value of the selected button to the server. If none of the buttons is selected, nothing is sent to the server and ServletRequest.getParameter(fieldName) returns null.

If a form contains multiple input elements with the same name, all values will be submitted and you have to use ServletRequest.getParameterValues to retrieve them. ServletRequest.getParameter will only return the last value.

The FormServlet class in Listing 2.4 demonstrates how to work with an HTML form. Its doGet method sends an order form to the browser. Its doPost method retrieves the values entered and prints them. This servlet is part of the servletapi2 application.

Listing 2.4: The FormServlet class

package servletapi2;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "FormServlet", urlPatterns = { "/form" })
public class FormServlet extends HttpServlet {
    private static final long serialVersionUID = 54L;
    private static final String TITLE = "Order Form";

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<!DOCTYPE html>");
        writer.println("<html>");
        writer.println("<head>");
        writer.println("<title>" + TITLE + "</title></head>");
        writer.println("<body><h1>" + TITLE + "</h1>");
        writer.println("<form method='post'>");
        writer.println("<table>");
        writer.println("<tr>");
        writer.println("<td>Name:</td>");
        writer.println("<td><input name='name'/></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Address:</td>");
        writer.println("<td><textarea name='address' "
                + "cols='40' rows='5'></textarea></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Country:</td>");
        writer.println("<td><select name='country'>");
        writer.println("<option>United States</option>");
        writer.println("<option>Canada</option>");
        writer.println("</select></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Delivery Method:</td>");
        writer.println("<td><input type='radio' " +
                "name='deliveryMethod'"
                + " value='First Class'/>First Class");
        writer.println("<input type='radio' " +
                "name='deliveryMethod' "
                + "value='Second Class'/>Second Class</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Shipping Instructions:</td>");
        writer.println("<td><textarea name='instruction' "
                + "cols='40' rows='5'></textarea></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>&nbsp;</td>");
        writer.println("<td><textarea name='instruction' "
                + "cols='40' rows='5'></textarea></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Please send me the latest " +
                "product catalog:</td>");
        writer.println("<td><input type='checkbox' " +
                "name='catalogRequest'/></td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>&nbsp;</td>");
        writer.println("<td><input type='reset'/>" +
                "<input type='submit'/></td>");
        writer.println("</tr>");
        writer.println("</table>");
        writer.println("</form>");
        writer.println("</body>");
        writer.println("</html>");
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<head>");
        writer.println("<title>" + TITLE + "</title></head>");
        writer.println("</head>");
        writer.println("<body><h1>" + TITLE + "</h1>");
        writer.println("<table>");
        writer.println("<tr>");
        writer.println("<td>Name:</td>");
        writer.println("<td>" + request.getParameter("name")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Address:</td>");
        writer.println("<td>" + request.getParameter("address")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Country:</td>");
        writer.println("<td>" + request.getParameter("country")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Shipping Instructions:</td>");
        writer.println("<td>");
        String[] instructions = request
                .getParameterValues("instruction");
        if (instructions != null) {
            for (String instruction : instructions) {
                writer.println(instruction + "<br/>");
            }
        }
        writer.println("</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Delivery Method:</td>");
        writer.println("<td>"
                + request.getParameter("deliveryMethod")
                + "</td>");
        writer.println("</tr>");
        writer.println("<tr>");
        writer.println("<td>Catalog Request:</td>");
        writer.println("<td>");
        if (request.getParameter("catalogRequest") == null) {
            writer.println("No");
        } else {
            writer.println("Yes");
        }
        writer.println("</td>");
        writer.println("</tr>");
        writer.println("</table>");
        writer.println("<div style='border:1px solid #ddd;" +
        		"margin-top:40px;font-size:90%'>");

        writer.println("Debug Info<br/>");
        Enumeration<String> parameterNames = request
                .getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String paramName = parameterNames.nextElement();
            writer.println(paramName + ": ");
            String[] paramValues = request
                    .getParameterValues(paramName);
            for (String paramValue : paramValues) {
                writer.println(paramValue + "<br/>");
            }
        }
        writer.println("</div>");
        writer.println("</body>");
        writer.println("</html>");
    }
}

You can invoke the FormServlet by using this URL:

http://localhost:8080/servletapi2/form

This will cause the doGet method in the FormServlet to be executed. The doGet method sends this HTML form to the browser.

<form method='post'>
<input name='name'/>
<textarea name='address' cols='40' rows='5'></textarea>
<select name='country'>");
    <option>United States</option>
    <option>Canada</option>
</select>
<input type='radio' name='deliveryMethod' value='First Class'/>
<input type='radio' name='deliveryMethod' value='Second Class'/>
<textarea name='instruction' cols='40' rows='5'></textarea>
<textarea name='instruction' cols='40' rows='5'></textarea>
<input type='checkbox' name='catalogRequest'/>
<input type='reset'/>
<input type='submit'/>
</form>

The form’s method is set to post to make sure the HTTP POST method is used when the user submits the form. Its action attribute is missing, indicating that the form will be submitted to the same URL used to request it.

Figure 2.6 shows an empty order form.

Figure 2.6: An empty Order form

Now, fill in the form and click the Submit button. The values you entered in the form will be sent to the server using the HTTP POST method and this will invoke the servlet’s doPost method. As a result, you’ll see the values printed as shown in Figure 2.7.

Figure 2.7: The values entered into the Order form

Using the Deployment Descriptor

As you can see in the previous examples, writing and deploying a servlet application is easy. One aspect of deployment is configuring the mapping of your servlet with a path. In the examples, you mapped a servlet with a path by using the WebServlet annotation type.

Using the deployment descriptor is another way of configuring a servlet application and the deployment descriptor is discussed in detail in Chapter 20, “Deployment.” The deployment descriptor is always named web.xml and located under the WEB-INF directory. In this chapter I show you how to create a servlet application named servletapi3 and write a web.xml file for it.

The servletapi3 has two servlets, SimpleServlet and WelcomeServlet, and a deployment descriptor to map the servlets. Listings 2.5 and 2.6 show SimpleServlet and WelcomeServlet, respectively. Note that the servlet classes are not annotated @WebServlet. The deployment descriptor is given in Listing 2.7.

Listing 2.5: The unannotated SimpleServlet class

package servletapi3;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleServlet extends HttpServlet {
    private static final long serialVersionUID = 8946L;
    
    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) 
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<!DOCTYPE html><html><head></head>" +
        		"<body>Simple Servlet</body></html");
    }
}

Listing 2.6: The unannotated WelcomeServlet class

package servletapi3;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WelcomeServlet extends HttpServlet {
    private static final long serialVersionUID = 27126L;

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) 
            throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("<!DOCTYPE html><html><head></head>"
                + "<body>Welcome</body></html>");
    }
}

Listing 2.7: The deployment descriptor

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

    <servlet>
        <servlet-name>SimpleServlet</servlet-name>
        <servlet-class>servletapi3.SimpleServlet</servlet-class>
        <load-on-startup>10</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SimpleServlet</servlet-name>
        <url-pattern>/simple</url-pattern>
    </servlet-mapping>  

    <servlet>
        <servlet-name>WelcomeServlet</servlet-name>
        <servlet-class>servletapi3.WelcomeServlet</servlet-class>
        <load-on-startup>20</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>WelcomeServlet</servlet-name>
        <url-pattern>/welcome</url-pattern>
    </servlet-mapping>
</web-app>

There are many advantages of using the deployment descriptor. For one, you can include elements that have no equivalent in @WebServlet, such as the load-on-startup element. This element loads the servlet at application start-up, rather than when the servlet is first called. Using load-on-startup means the first call to the servlet will take no longer than subsequent calls. This is especially useful if the init method of the servlet takes a while to complete.

Another advantage of using the deployment descriptor is that you don’t need to recompile your servlet class if you need to change configuration values, such as the servlet path.

In addition, you can pass initial parameters to a servlet and edit them without recompiling the servlet class.

The deployment descriptor also allows you to override values specified in a servlet annotation. A WebServlet annotation on a servlet that is also declared in the deployment descriptor will have no effect. However, annotating a servlet that is not declared in the deployment descriptor will still work. This means, you can have annotated servlets and declare servlets in the deployment descriptor in the same application.

Figure 2.8 presents the directory structure for servletapi3. The directory structure does not differ much from that of servletapi1. The only difference is that servletapi3 has a web.xml file (the deployment descriptor) in the WEB-INF directory.

Figure 2.8: Directory structure for servletapi3 (with deployment descriptor)

Now that SimpleServlet and WelcomeServlet are declared in the deployment descriptor. You can use these URLs to access them:

http://localhost:8080/servletapi3/simple
http://localhost:8080/servletapi3/welcome

Summary

Servlet technology is part of the Java EE. All servlets run in a servlet container, and the contract between the container and the servlets takes the form of the javax.servlet.Servlet interface. The javax.servlet package also provides the GenericServlet abstract class that implements Servlet. This is a convenient class that you can extend to create a servlet. However, most modern servlets will work in the HTTP environment. As such, subclassing the javax.servlet.http.HttpServlet class makes more sense. The HttpServlet class itself is a subclass of GenericServlet.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.121.131