To create a servlet, perform the following tasks:
Design the servlet into your web application, or, if accessed in a generic way, design it to access no application data.
Create a class that extends either GenericServlet or HttpServlet, overriding the appropriate methods so it handles requests.
Use the Sun Java System Web Server Administration interface to create a web application deployment descriptor. For details, see Chapter 7, Deploying Web Applications.
The rest of this section discusses the following topics:
To create a servlet, write a public Java class that includes basic I/O support as well as the package javax.servlet. The class must extend either GenericServlet or HttpServlet. Since Sun Java System Web Server servlets exist in an HTTP environment, the latter class is recommended. If the servlet is part of a package, you must also declare the package name so the class loader can properly locate it.
The following example header shows the HTTP servlet declaration called myServlet:
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class myServlet extends HttpServlet { ...servlet methods... } |
Override one or more methods to provide servlet instructions to perform its intended task. A servlet does all the processing on a request-by-request basis and happens in the service methods, either service() for generic servlets or one of the do Operation() methods for HTTP servlets. This method accepts incoming requests, processes them according to the instructions you provide, and directs the output appropriately. You can create other methods in a servlet as well.
Override the class initializer init() to initialize or allocate resources for the servlet instance's life, such as a counter. The init() method runs after the servlet is instantiated but before it accepts any requests. For more information, see the servlet API specification.
All init() methods must call super.init(ServletConfig) to set their scope. This makes the servlet's configuration object available to other servlet methods. If this call is omitted, a 500 SC_INTERNAL_SERVER_ERROR displays in the browser when the servlet starts up.
The following example of the init() method initializes a counter by creating a public integer variable called thisMany:
public class myServlet extends HttpServlet { int thisMany; public void init (ServletConfig config) throws ServletException { super.init(config); thisMany = 0; } } |
Now other servlet methods can access the variable.
Override the class destructor destroy() to write log messages or to release resources that have been opened in the servlet's life cycle. Resources should be appropriately closed and dereferenced so that they are recycled or garbage collected. The destroy() method runs just before the servlet itself is deallocated from memory. For more information, see the servlet API specification.
Based on the example for Overriding Initialize, the destroy() method could write a log message like the following:
out.println("myServlet was accessed " + thisMany " times.\n"); |
When a request is made, the Sun Java System Web Server hands the incoming data to the servlet engine to process the request. The request includes form data, cookies, session information, and URL name-value pairs, all in a type HttpServletRequest object called the request object. Client metadata is encapsulated as a type HttpServletResponse object called the response object. The servlet engine passes both objects as the servlet's service() method parameters.
The default service() method in an HTTP servlet routes the request to another method based on the HTTP transfer method (POST, GET, and so on). For example, HTTP POST requests are routed to the doPost() method, HTTP GET requests are routed to the doGet() method, and so on. This enables the servlet to perform different request data processing depending on the transfer method. Since the routing takes place in service(), there is no need to generally override service() in an HTTP servlet. Instead, override doGet(), doPost(), and so on, depending on the expected request type.
The automatic routing in an HTTP servlet is based simply on a call to request.getMethod(), which provides the HTTP transfer method. In a Sun Java System Web Server, request data is already preprocessed into a name-value list by the time the servlet sees the data, so simply overriding the service() method in an HTTP servlet does not lose any functionality. However, this does make the servlet less portable, since it is now dependent on preprocessed request data.
Override the service() method (for generic servlets) or the doGet() or doPost() methods (for HTTP servlets) to perform tasks needed to answer the request. Very often, this means collating the needed information (in the request object or in a JDBC result set object), and then passing the newly generated content to a JSP for formatting and delivery back to the client.
Most operations that involve forms use either a GET or a POST operation, so for most servlets you override either doGet() or doPost(). Implement both methods to provide for both input types or simply pass the request object to a central processing method, as shown in the following example:
public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } |
All request-by-request traffic in an HTTP servlet is handled in the appropriate doOperation() method, including session management, user authentication, JSPs, and accessing Sun Java System Web Server features.
If a servlet intends to call the RequestDispatcher method include() or forward(), be aware the request information is no longer sent as HTTP POST, GET, and so on. In other words, if a servlet overrides doPost(), it may not process anything if another servlet calls it, if the calling servlet happens to receive its data through HTTP GET. For this reason, be sure to implement routines for all possible input types, as explained above. RequestDispatcher methods always call service().
For more information, see Calling a Servlet Programmatically.
Arbitrary binary data, such as uploaded files or images, can be problematic, because the web connector translates incoming data into name-value pairs by default. You can program the web connector to properly handle these kinds of data and package them correctly in the request object.
Incoming data is encapsulated in a request object. For HTTP servlets, the request object type is HttpServletRequest. For generic servlets, the request object type is ServletRequest. The request object contains all request parameters, including your own request values called attributes.
To access all incoming request parameters, use the getParameter() method. For example:
String username = request.getParameter("username"); |
Set and retrieve values in a request object using setAttribute() and getAttribute(), respectively. For example:
request.setAttribute("favoriteDwarf", "Dwalin"); |
From a web server's perspective, a web application is a series of unrelated server hits. There is no automatic recognition if a user has visited the site before, even if their last interaction was seconds before. A session provides a context between multiple user interactions by remembering the application state. Clients identify themselves during each interaction by a cookie, or, in the case of a cookie-less browser, by placing the session identifier in the URL.
A session object can store objects, such as tabular data, information about the application's current state, and information about the current user. Objects bound to a session are available to other components that use the same session.
For more information, see Chapter 5, Session Managers.
After a successful login, you should direct a servlet to establish the user's identity in a standard object called a session object. This object holds information about the current session, including the user's login name and any additional information to retain. Application components can then query the session object to obtain user authentication.
For more information about providing a secure user session for your application, see Chapter 6, Securing Web Applications.
By default, servlets are not thread-safe. The methods in a single servlet instance are usually executed numerous times simultaneously (up to the available memory limit). Each execution occurs in a different thread, though only one servlet copy exists in the servlet engine.
This is efficient system resource usage, but is dangerous because of the way Java manages memory. Because variables belonging to the servlet class are passed by reference, different threads can overwrite the same memory space as a side effect. To make a servlet (or a block within a servlet) thread-safe, do one of the following:
Synchronize write access to all instance variables, as in public synchronized void method() (whole method) or synchronized(this) {...} (block only). Because synchronizing slows response time considerably, synchronize only blocks, or make sure that the blocks in the servlet do not need synchronization.
For example, this servlet has a thread-safe block in doGet() and a thread-safe method called mySafeMethod():
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class myServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //pre-processing synchronized (this) { //code in this block is thread-safe } //other processing; } public synchronized int mySafeMethod (HttpServletRequest request) { //everything that happens in this method is thread-safe } } |
Use the SingleThreadModel class to create a single-threaded servlet. When a single-threaded servlet is deployed to the Sun Java System Web Server, the servlet engine creates a servlet instance pool used for incoming requests (multiple copies of the same servlet in memory). You can change the number of servlet instances in the pool by setting the singleThreadedServletPoolSize property in the Sun Java System Web Server-specific web application deployment descriptor. For more information, see Chapter 7, Deploying Web Applications. Servlet is slower under load because new requests must wait for a free instance in order to proceed. In load-balanced applications, the load automatically shifts to a less busy process.
For example, this servlet is completely single-threaded:
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class myServlet extends HttpServlet implements SingleThreadModel { servlet methods... } |
If a servlet is specifically written as a single thread, the servlet engine creates a pool of servlet instances to be used for incoming requests. If a request arrives when all instances are busy, it is queued until an instance becomes available. The number of pool instances is configurable in the sun-web.xml file, in the singleThreadedServletPoolSize property of the sun-web-app element.
The final user interaction activity is to provide a response page to the client. The response page can be delivered in two ways, as described in the following topics:
Generate the output page within a servlet by writing to the output stream. The recommended way to do this depends on the output type.
Always specify the output MIME type using setContentType() before any output commences, as in this example:
response.setContentType("text/html"); |
For textual output, such as plain HTML, create a PrintWriter object and then write to it using println. For example:
PrintWriter output = response.getWriter(); output.println("Hello, World\n"); |
For binary output, write to the output stream directly by creating a ServletOutputStream object and then write to it using print(). For example:
ServletOutputStream output = response.getOutputStream(); output.print(binary_data); |
Servlets can invoke JSPs in two ways, the include() method and the forward() method:
The include() method in the RequestDispatcher interface calls a JSP and waits for it to return before continuing to process the interaction. The include() method can be called multiple times within a given servlet.
This example shows a JSP using include():
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("JSP_URI"); dispatcher.include(request, response); ... //processing continues |
The forward() method in the RequestDispatcher interface hands the JSP interaction control. The servlet is no longer involved with the current interaction's output after invoking forward(), thus only one call to the forward() method can be made in a particular servlet.
You cannot use the forward() method if you have already defined a PrintWriter or ServletOutputStream object.
This example shows a JSP using forward():
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("JSP_URI"); dispatcher.forward(request, response); |
Identify which JSP to call by specifying a Universal Resource Identifier (URI). The path is a String describing a path within the ServletContext scope. There is also a getRequestDispatcher() method in the request object that takes a String argument indicating a complete path. For more information about this method, see the Java Servlet 2.3 specification, section 8.
For more information about JSPs, see Chapter 4, Using JavaServer Pages.