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.
The Servlet API comes in four Java packages. The packages are as follows.
This chapter focuses on members of javax.servlet and javax.servlet.http.
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.
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.
The other two methods in Servlet are non-life cycle methods: getServletInfo and getServletConfig.
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 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.
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.
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:
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.
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
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.
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.
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.”
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.”
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:
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.”
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
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:
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 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.
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> </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> </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
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
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.
13.58.121.131