Overview
The Java Servlet API is a low-level API to extend a web server to generate dynamic content. The API's core is the Servlet interface. Classes implementing this interface can receive the client's HTTP request and generate the response.
Practically all Java web frameworks are built on the Servlet API. This makes it important to know the basics even when you will never write a servlet.
A collection of Servlets, Filters, JSPs and static web content that is bundled together in a single directory is called a web application. Web application are usually distributed and deployed in WAR archives. A web server that supports Java web applications is also called web container.
The Servlet API has two layers, a generic layer and a HTTP-specific layer. The generic part can also be used for other protocols such as FTP, SIP and SMTP. Non-HTTP servlets are not described in this document.
Servlets
Servlets are classes that handle HTTP requests and generate a response for the client. To have a Servlet ready for deployment, you need to do four things:
- Write the servlet class (must extend HttpServlet)
- Declare the servlet in your web.xml deployment descriptor
- Define one or more url patterns that the servlet responds to
- Package your whole application in a WAR file.
Base Interface | HTTP-specific Class | Purpose |
---|---|---|
Servlet | HttpServlet | Base interface to implement for handling requests |
ServletRequest | HttpServletRequest | Represents the request |
ServletResponse | HttpServletResponse | Creates a response |
ServletConfig | - | Retrieving initialization parameters, servlet name |
ServletContext | - | Access to web container features: resources, attributes, logging, dispatcher |
Filter | - | Modify a request before processing, or a response after processing |
*Listener | Http*Listener | Notifications about various events in the application |
- | HttpSession | Keeps state for a single client |
package com.jarfiller.example;
import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
// Simple dummy servlet with the most important methods
public class SkeletonServlet extends HttpServlet {
public SkeletonServlet() { // default constructor required (more)
}
@Override
public void init(ServletConfig config) // invoked before first request (more)
throws ServletException {
super.init(config); // required when overriding init!
// do all initialization here
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// handle HTTP GET requests here
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// handle HTTP POST requests here
}
@Override
public void destroy() { // invoked before server discards the servlet (more)
// release resources here
}
}
Servlets are multi-threaded. This means that the web server will call the service methods (doGet, doPost...) from several threads, and thus your servlet implementation must be thread-safe.
The next section shows you two easy ways to make your servlets multi-threaded.
To deploy your servlet in a web container, you need to package it as a web application and store it in a WAR file. WARs are JARs with a special file layout and a ".war" extension. They also require a deployment descriptor file called web.xml which must be located at /WEB-INF/web.xml in the WAR.
Web Application Layout
Path Pattern | Description |
---|---|
/* | Static content (HTML, images, stylesheets..) and JSPs (more) |
/WEB-INF/* | All data except static content |
/WEB-INF/web.xml | Deployment descriptor (shown below) |
/WEB-INF/classes/ | The application's Java classes (e.g. your servlet class) |
/WEB-INF/lib/*.jar | Libraries required by the application |
/META-INF/* | optional JAR descriptors, vendor extensions (more) |
There are no required files or directories for WARs. A WAR containing only static files does not need WEB-INF at all.
Creating the Web Application / WAR
To create a WAR, you need to
- Copy your classes, libraries and static files into the directory structure shown above
- Create a deployment descriptor at /WEB-INF/web.xml (see below)
- Put the whole directory into a WAR archive (more)
If you use an IDE (such as Eclipse or Netbeans), the IDE will help you assemble the WAR, so you don't have to do this manually.
Also, many application servers allow you to copy the directory directly instead of the deploying a WAR file. This makes deployment faster during development.
You need to declare all servlets in the web.xml deployment descriptor.
Here is a very simple web.xml, declaring only one servlet and mapping it onto a path:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <!-- Declare the servlet class --> <servlet> <servlet-name>timeServlet1</servlet-name> <servlet-class>com.jarfiller.example.TimeServlet</servlet-class> </servlet> <!-- Declare the URL path handled by this servlet (more) --> <servlet-mapping> <servlet-name>timeServlet1</servlet-name> <!-- much match servlet-name above! --> <url-pattern>/now</url-pattern> </servlet-mapping> </web-app>
For details on web.xml, visit Jarfiller's Web.xml Reference.
The context root is the web application's path on the web server. All paths that you configure in web.xml are relative to the context root. The context root needs to be set in the web container (more).
Let's assume you have a web container running on localhost port 8080, and you set the context root to 'myapp'. Then all static files and all url patterns are relative to the URL "http://localhost:8080/myapp/".
If you don't want your application to reside in a directory, you must set your context root to '/'.
All files in a WAR file (except /WEB-INF/* and /META-INF/*) will automatically be served by the web server as static files, located at the application's base URL (more).
Content Type / MIME Type Mapping
The server determines the content type (MIME type) of static files from their extension. The specification does not require the container to know any extensions, unless you define content type mappings in your web.xml deployment descriptor:
<mime-mapping> <extension>html</extension> <mime-type>text/html</mime-type> </mime-mapping> <mime-mapping> <extension>xhtml</extension> <mime-type>application/xhtml+xml</mime-type> </mime-mapping>
In practice, common file types such as HTML and PNG are known to containers though, and you don't have to define them (hardly anyone does).
Welcome Files
You can define welcome files, which are the names of files which will be shown when a user requests a directory (more). Without them, the server shows a 404 Not Found error. This is a welcome file definition for web.xml:
<welcome-file-list> <!-- ordered by preference --> <welcome-file>index.jsp</welcome-file> <welcome-file>index.xhtml</welcome-file> <welcome-file>index.html</welcome-file> </welcome-file-list>
Responses
import javax.servlet.http.*;
import java.io.*;
// Servlets that shows the current time
public class TimeServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/html; charset=UTF-8"); // required: HTML in UTF-8 (more)
response.setCharacterEncoding("UTF-8"); // recommended: use UTF-8 (more)
PrintWriter writer = response.getWriter(); // writer for sending text (more)
writer.print("<html><head><title>Time</title></head><body>");
writer.print("The current time is: ");
writer.print((new java.util.Date()).toString());
writer.print("</body></html>");
}
}
import java.io.*;
import java.util.*;
import javax.servlet.http.*;
// Servlet that sends one of two PNG images, depending on time of day
public class AmPmFileServlet extends HttpServlet {
// simple helper: copy data from InputStream to OutputStream
private void copy(InputStream in, OutputStream out)
throws IOException {
byte buffer[] = new byte[4096];
while (true) {
int r = in.read(buffer);
if (r < 0) // end of stream?
break;
out.write(buffer, 0, r);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// before or after noon? determine path to the right image in the WAR
boolean isPM = new GregorianCalendar().get(Calendar.AM_PM) == Calendar.PM;
String resName = "/WEB-INF/images/time" + (isPM ? "PM" : "AM") + ".png";
response.setContentType("image/png"); // required: content type PNG
OutputStream out = response.getOutputStream(); // stream for binary files (more)
InputStream in = getServletContext().getResourceAsStream(resName);
copy(in, out); // copy file to client
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// send status code 403 (more):
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access not allowed");
// Important: return from service method immediately (more)
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.sendRedirect("http://jarfiller.com/");
// Important: return from service method immediately (more)
}
Requests
import javax.servlet.http.*;
import java.io.*;
// Servlet that shows a simple form with two text fields,
// allowing the user to enter numbers and showing their sum
public class CalculatorServlet extends HttpServlet {
// Prints the calculator HTML
private void printCalculator(HttpServletResponse response, Integer result)
throws IOException {
response.setContentType("text/html; charset=UTF-8"); // HTML in UTF8
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.print("<html><head><title>Calculator</title></head><body>");
writer.print("<form><input name='x'> + <input name='y'> <input type='submit' value='='> ");
if (result != null)
writer.print(result);
writer.print("</form></body></html>");
}
// handles GET and POST (more)
private void doAll(HttpServletRequest request, HttpServletResponse response)
throws IOException {
Integer result = null;
String x = request.getParameter("x");
String y = request.getParameter("y");
// validation: check parameters are numeric (more)
if (x != null && y != null &&
x.matches("[0-9]+") && y.matches("[0-9]+")) {
result = Integer.parseInt(x) + Integer.parseInt(y);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
doAll(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
doAll(request, response);
}
}
With the <servlet-mapping> element you can define for which paths your servlet will be invoked. Paths are defined using URL patterns.
URL Pattern
The <url-pattern> element in the servlet mapping supports only two kinds of wildcards:
- '/*' at the end, to map the servlet on a virtual directory
- '*.ext', to map the servlet on a file extension
Other uses of wildcards are not allowed.
Servlet Mapping
This web.xml snippet maps the servlet both on the directory '/libs' and the file extension '*.do':
<servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/libs/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
Path Methods
When using wildcards like this, there are a number of methods in HttpServletRequest to find out the request path:
- getRequestURI - the complete path, starting at the server's root
- getContextPath - the context root of the servlet (empty for context root "/")
- getServletPath - for directory patterns: the mapped directory; for extension patterns: path relative to context root
- getPathInfo - for directory patterns: path relative to servlet path; for extension patterns: always null
URL Examples
Assuming the servlet mapped above has the context root 'myapp', here a few example URLs and their resulting paths:
URL | getRequestURI | getContextPath | getServletPath | getPathInfo |
---|---|---|---|---|
http://localhost/myapp/lib/x/y/a.htm | /myapp/lib/x/y/a.htm | /myapp | /lib | /x/y/a.htm |
http://localhost/myapp/lib/abc/ | /myapp/lib/abc/ | /myapp | /lib | /abc/ |
http://localhost/myapp/help.do | /myapp/help.do | /myapp | /myapp/help.do | null |
http://localhost/myapp/abc/run.do | /myapp/abc/run.do | /myapp | /myapp/abc/run.do | null |
Root Pattern
You can also set the <url-pattern> to "/". The servlet will then handle all paths, and the path methods return the same values as they do for mapped extensions.
Sessions and Cookies
final static String COOKIE_NAME = "visitCounter";
// This method uses cookies to find out how many times the user
// has visited the servlet before:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/plain; charset=UTF-8"); // plain text (more)
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
// Finding the cookie (if it exists)
int visits = 0;
if (request.getCookies() != null) // may be null if no cookies (more)
for (Cookie c: request.getCookies())
if (c.getName().equals(COOKIE_NAME) && c.getValue().matches("[0-9]+"))
visits = Integer.parseInt(c.getValue());
// Print a message for the user
if (visits == 0)
writer.println("This is your first visit.");
else
writer.printf("This is visit number %d.\n", visits); // formatted printing (more)
// Set / Update the cookie (more)
Cookie c = new Cookie(COOKIE_NAME, Integer.toString(visits + 1));
c.setMaxAge(60*60*24*28); // valid for 28 days (more)
response.addCookie(c);
}
HttpSession allows you to store arbitrary objects for a user as named attributes. Sessions are managed by the web container, which associates a session id with every session and uses mechanisms such as cookies to store the session id in the client (more).
Sessions are typically short lived, and do not survive neither a browser-restart nor a server-restart (more).
The following example implements a visitor counter with HttpSessions:
final static String ATTRIBUTE_NAME = "visitCounter";
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/plain; charset=UTF-8"); // plain text (more)
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
// Reading the session
int visits = 0;
HttpSession session = request.getSession(); // can create session (more)
if (session.getAttribute(ATTRIBUTE_NAME) != null) // not set in new sessions
if (c.getName().equals(COOKIE_NAME) && c.getValue().matches("[0-9]+"))
visits = (Integer) session.getAttribute(ATTRIBUTE_NAME);
// Print a message for the user
if (visits == 0)
writer.println("This is your first visit.");
else
writer.printf("This is visit number %d.\n", visits); // formatted printing (more)
// Update the session
session.setAttribute(ATTRIBUTE_NAME, visits + 1);
session.setMaxInactiveInterval(60*60*24); // valid for 24 hours max
}
Resources
To configure your servlet for the web container's environment, declare required resources with the @Resource annotation in the servlet. The container will inject those resources right after instantiation (but before init is called).
The deployer is responsible for configuring the web container to provide the values required by the application. The configuration itself is container-specific.
The following Servlet declares a requirement for a String and a JDBC DataSource:
package com.jarfiller.example;
import javax.sql.*;
import java.sql.*;
import javax.servlet.http.*;
import javax.servlet.*;
import javax.annotation.*;
import java.io.*;
public class ResourceServlet extends HttpServlet {
@Resource(name="jdbc/db")
private DataSource db; // inject database connection (more)
@Resource(name="param/webmaster")
private String webmaster; // inject string (more)
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
try {
Connection connection = db.getConnection(); // use injected value
// do something ...
}
catch (SQLException e) {
throw new ServletException(e); // simple error handling (more)
}
}
}
How to... Requests and Responses
How to... Sessions and Security
How to... General
And now...?
This tutorial intends to give you a quick start on using Servlets, and maybe give answers for some questions that are hard to figure out. But now you are on you own. If you need to find out more, try to find it in the API Documents and the Jarfiller Web.xml Reference. For a more thorough understanding of the Servlet API you should take the time to read the Servlet Specification.