
Part I: Java Servlets
Introduction
As we well know a CGI script is little more than a program that reads its standard input and creates a document on standard output. One can certainly do that with Java. But using the new Java servlet technology, you can do an even better job. Servlets allow you to write Java extensions for many web servers. These extensions are more efficient than CGI and use the power of object oriented programming. This presentation will go over getting started with servlets. We shall find that writing them is easy. Sometimes installing servlet support for an arbitrary server can be, equally, arbitrary difficult. Fortunately Apache does support servlet technology.
Here's
Goodbye CGI. Say Hello to Java Servletsat Web Review, an article that discusses Java servlet technology.
JSDK2.0
Sun's Java Servlet Development Kit (JSDK) is the source of most of the servlet
technology. It can be downloaded currently from Sun (which is what we will do
in lab) and it will ship as integral part of JDK1.2; the JSDK has a special
web server (called servletrunner, which is for the server-side
Java programming what appletviewer is for the client-side Java
programming) that understand servlets (and only servlets, so it won't server
files). This is an easy way to get started with servlets and that's what we
will do in lab this week.
Servlets Advantages
Servlets offer the advantages of CGI without many of the drawbacks. CGI scripts are inefficient: each time the server needs a CGI program, it has to fork a new process. Often this process has to load a large program like the Perl interpreter. Then it can finally get around to handling your request.
Servlets, on the other hand, take advantage of Java's dynamic loading capabilities. When you use servlets, the Java VM runs and loads classes as you need them. Once you load a class, you don't need to reload it unless it changes.
In addition to the performance benefits, Java is easier to work with than traditional CGI languages. Servlets make use of Java object-oriented features and packages, making them easier to write and maintain.
Servlet Basics
If you can get one of the example servlets from the JSDK running, you're ready to make your own servlets. (Check out this week's lab notes for a tutorial on installing JSDK2.0 and running Java servlets with it). These are just like any other Java program, but they do import some classes from the JSDK specifically. Just about everyone writes their first servlet by modifying one of the examples that come with JSDK so we will look at those examples first.
Just as an applet extends the Applet class, a servlet extends the
HttpServlet class. There are three methods that you usually want to
override in this class:
service,
init, and
destroy
service, because this is the function the web
server calls to ask the servlet to perform its designated action. The server also calls
init when it first loads the servlet, and calls destroy when
the servlet shuts down. You can also provide a getServletInfo function that
returns a string with the servlet name and version number.
The service method receives two arguments. One, of type
lets you read the web browser's headers and other information related to the request. The other argument, aServletRequest
object is used to set the server's response to the browser.ServletResponse
The init function also receives an argument, of type
that lets you read any parameters or startup configuration for the servlet.ServerConfig
From the user's perspective, there are two ways to see a servlet in action.
One is to invoke the servlet using a special pseudo path and the class name
(or a configured alias for the class name). This is similar to asking for a
CGI script by entering its URL or by clicking on a link that refers to it.
The service method supplies the entire resulting web page. It
can also redirect the request to another URL or take any other action that
a normal CGI script might.
Servlet Examples
The following example processes data POSTed by
a form. The form is detailed below and can be checked out
here
(but submitting the form won't produce anything unless the servletrunner
is up and running.
Here's the form:
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user2/dgerman/JSDK2.0/examples
tucotuco.cs.indiana.edu% cat JdcSurvey.html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>JdcSurvey</title>
</head>
<body>
<form action=http://YourBurrowHost.cs.indiana.edu:2980x/servlet/survey method=POST>
<input type=hidden name=survey value=Survey01Results>
<BR><BR>How Many Employees in your Company?<BR>
<BR>1-100<input type=radio name=employee value=1-100>
<BR>100-200<input type=radio name=employee value=100-200>
<BR>200-300<input type=radio name=employee value=200-300>
<BR>300-400<input type=radio name=employee value=300-400>
<BR>500-more<input type=radio name=employee value=500-more>
<BR><BR>General Comments?<BR>
<BR><input type=text name=comment>
<BR><BR>What IDEs do you use?<BR>
<BR>JavaWorkShop<input type=checkbox name=ide value=JavaWorkShop>
<BR>J++<input type=checkbox name=ide value=J++>
<BR>Cafe'<input type=checkbox name=ide value=Cafe'>
<BR><BR><input type=submit><input type=reset>
</form>
</body>
</html>
tucotuco.cs.indiana.edu%
Here's the interaction: And here's the servlet:tucotuco.cs.indiana.edu% pwd /nfs/paca/home/user2/dgerman/JSDK2.0/examples tucotuco.cs.indiana.edu% ../bin/servletrunner -p 29800 servletrunner starting with settings: port = 29800 backlog = 50 max handlers = 100 timeout = 5000 servlet dir = ./examples document dir = ./examples servlet propfile = ./examples/servlet.properties SurveyServlet: init ^Ctucotuco.cs.indiana.edu% ls -l /tmp | grep dgerman -rw-r--r-- 1 dgerman students 86 Nov 17 02:52 Survey01Results.txt tucotuco.cs.indiana.edu% cat /tmp/Survey01Results.txtide: JavaWorkShop survey: Survey01Results employee: 1-100 comment: None tucotuco.cs.indiana.edu%
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* A sample single-threaded servlet that takes input from a form
* and writes it out to a file. It is single threaded to serialize
* access to the file. After the results are written to the file,
* the servlet returns a "thank you" to the user.
*
* <p>You can run the servlet as provided, and only one thread will run
* a service method at a time. There are no thread synchronization
* issues with this type of servlet, even though the service method
* writes to a file. (Writing to a file within a service method
* requires synchronization in a typical servlet.)
*
* <p>You can also run the servlet without using the single thread
* model by removing the <tt>implements</tt> statement. Because the
* service method does not synchronize access to the file, multiple
* threads can write to it at the same time. When multiple threads try
* to write to the file concurrently, the data from one survey does not
* follow the data from another survey in an orderly fashion.
*
* <p>To see interaction (or lack of interaction) between threads, use
* at least two browser windows and have them access the servlet as
* close to simultaneously as possible. Expect correct results (that
* is, expect no interference between threads) only when the servlet
* implements the <code>SingleThreadedModel</code> interface.
*/
public class SurveyServlet extends HttpServlet
implements SingleThreadModel
{
String resultsDir;
public void init(ServletConfig config)
throws ServletException
{
super.init(config);
resultsDir = getInitParameter("resultsDir");
if (resultsDir == null) {
Enumeration initParams = getInitParameterNames();
System.err.println("The init parameters were: ");
while (initParams.hasMoreElements()) {
System.err.println(initParams.nextElement());
}
System.err.println("Should have seen one parameter name");
throw new UnavailableException (this,
"Not given a directory to write survey results!");
}
}
/**
* Write survey results to output file in response to the POSTed
* form. Write a "thank you" to the client.
*/
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
// first, set the "content type" header of the response
res.setContentType("text/html");
//Get the response's PrintWriter to return text to the client.
PrintWriter toClient = res.getWriter();
try {
//Open the file for writing the survey results.
String surveyName = req.getParameterValues("survey")[0];
FileWriter resultsFile = new FileWriter(resultsDir
+ System.getProperty("file.separator")
+ surveyName + ".txt", true);
PrintWriter toFile = new PrintWriter(resultsFile);
// Get client's form data & store it in the file
toFile.println("<BEGIN>");
Enumeration values = req.getParameterNames();
while(values.hasMoreElements()) {
String name = (String)values.nextElement();
String value = req.getParameterValues(name)[0];
if(name.compareTo("submit") != 0) {
toFile.println(name + ": " + value);
}
}
toFile.println("<END>");
//Close the file.
resultsFile.close();
// Respond to client with a thank you
toClient.println("<html>");
toClient.println("<title>Thank you!</title>");
toClient.println("Thank you for participating");
toClient.println("</html>");
} catch(IOException e) {
e.printStackTrace();
toClient.println(
"A problem occured while recording your answers. "
+ "Please try again.");
}
// Close the writer; the response is done.
toClient.close();
}
}
The servlet writes the form data to a file, and responds to the user with a
thank you message. The doPost method of the servlet uses the
getParameterNames and getParameterValues methods to
get the form data. Because it returns text to the client, doPost
calls the getWriter method. It sets the response header field,
content type, before writing the body of the response, and closes the writer
when the response is complete.
More about Servlets
Servers are modules that run inside request/response-oriented servers, such as Java-enabled web servers and extend them in some manner. Servlets are to servers what applets are for browsers. They are an effective substitute for CGI scripts since they provide a way to generate dynamic documents that is both easier to write and faster to run.
Servlet Architecture Overview
The central abstraction in the Servlet API is the
Servletinterface. All servlets implement this interface, either directly or, more commonly, by extending a class that implements it such asHttpServlet. TheServletinterface provides for methods that manage the servlet and its communications with clients. Servlet writers provide some or all of these methods when developing a servlet.When a servlet accepts a call from a client, it receives two objects: one is a
ServletRequestand the other is aServletResponse. TheServletRequestclass encapsulates the communication from the client to the server, while theServletResponseclass encapsulates the communication from the servlet back to the client.The
ServletRequestinterface allows the servlet access to information such as the names of the parameters passed in by the client, the protocol (scheme) being used by the client, and the names of the remote host that made the request and the server that received it. It also provides the servlet with access to the input stream,ServletInputStream, through which the servlet gets data from clients that are using application protocols such as the HTTP POST and PUT methods. Subclasses ofServletRequestallow the servlet to retrieve more protocol-specific data. For example,HttpServletRequestcontains methods for accessing HTTP-specific header information.The
ServletResponseinterface gives the servlet methods for replying to the client. It allows the servlet to set the content length and mime type of the reply, and provides an output stream,ServletOutputStream, and a Writer through which the servlet can send the reply data. Subclasses ofServletResponsegive the servlet more protocol-specific capabilities. For example,HttpServletResponsecontains methods that allow the servlet to manipulate HTTP-specific header information.The classes and interfaces described above make up a basic Servlet. HTTP servlets have some additional objects that provide session-tracking capabilities. The servlet writer can use these APIs to maintain state between the servlet and the client that persists across multiple connections during some time period.
Servlet Lifecycle
Servers load and run servlets, which then accept zero or more requests from clients and return data to them. They can also remove servlets. These are the steps of a servlets lifecycle. The next paragraphs describe each step in more detail, concentrating on concurrency issues.
When a server loads a servlet, it runs the servlet's
initmethod. Even though most servlets are run in multi-threaded servers, there are no concurrency issues during servlet initialization. This is because the server calls theinitmethod once, when it loads the servlet, and will not call it again unless it is reloading the servlet. The server can not reload a servlet until after it has removed the servlet by calling thedestroymethod. Initialization is allowed to complete before client requests are handled (that is, before theservicemethod is called) or the servlet is destroyed.After the server loads and initializes the servlet, the servlet is able to handle client requests. It processes them in its
servicemethod. Each client's request has its call to theservicemethod run in its own servlet thread: the method receives the client's request, and sends the client its response.Servlets can run multiple
servicemethods at a time. It is important, therefore, thatservicemethods be written in a thread-safe manner. For example, if aservicemethod might update a field in the servlet object, that access should be synchronized. If, for some reason, a server should not run multipleservicemethods concurrently, the servlet should implement theSingleThreadModelinterface. This interface guarantees that no two threads will execute the servlet'sservicemethods concurrently.Servlets run until they are removed from the service, for example, at the request of a system administrator. When a server removes a servlet, it runs the servlet's
destroymethod. The method is run once; the server will not run it again until after it reloads and reinitializes the servlet. When thedestroymethod runs, however, other threads might be running service requests. If, in cleaning up, it is necessary to access shared resources (such as network connections to be closed), that access should be synchronized.During a servlet's lifecycle, it is important to write thread-safe code for destroying the servlet and, unless the servlet implements the
SingleThreadModelinterface, servicing client requests.Writing a Servlet
Servlets implement the
javax.servlet.Servletinterface. While servlet writers can develop servlets by implementing the interface directly, this is usually not required. Because most servlets extend web servers that use the HTTP protocol to interact with clients, the most common way to develop servlets is by specializing thejavax.servlet.http.HttpServletclass. This version of the tutorial concentrates on describing this method of writing servlets.The
HttpServletclass implements the Servlet interface by extending the GenericServlet base class, and provides a framework for handling the HTTP protocol. Itsservicemethod supports standard HTTP/1.1 requests by dispatching each request to a method designed to handle it.By default, servlets written by specializing the
HttpServletclass can have multiple threads concurrently running itsservicemethod. If, for whatever reason, you would like to have only a single thread running aservicemethod at a time, then in addition to extending theHttpServlet, your servlet should also implement theSingleThreadModelinterface. This doesnot involve writing any extra methods, merely declaring that the servlet implements the interface. For example,public class SurveyServlet extends HttpServlet implements SingleThreadModel { /* typical servlet code, with no threading concerns * in the service method. No extra code for the * SingleThreadModel interface. */ ... }Interacting with Clients
Servlet writers who are developing HTTP servlets that specialize the
HttpServletclass should override the method or methods designed to handle the HTTP interactions that their servlet will handle. The candidate methods include,
doGet, for handling GET, conditional GET and HEAD requestsdoPost, for handling POST requestsdoPut, for handling PUT requestsdoDelete, for handling DELETE requestsBy default, these methods return a
BAD_REQUEST (400)error. An example HTTP servlet that handles GET and HEAD requests follows; it specializes thedoGetmethod. A second example is also provided. It handles POST requests from a form by specializing thedoPostmethod.The
HttpServlet'sservicemethod, by default, also calls thedoOptionsmethod when it receives an OPTIONS request, anddoTracewhen it receives a TRACE request. The default implementation ofdoOptionsautomatically determines what HTTP options are supported and returns that information. The default implementation ofdoTracecauses a response with a message containing all of the headers sent in the trace request. These methods are not typically overridden.Whatever method you override, it will take two arguments. The first encapsulates the data from the client, and is an
HttpServletRequest. The second encapsulates the response to the client, and is anHttpServletResponse. The following paragraphs discuss their use.An
HttpServletRequestobject provides access to HTTP header data, such as any cookies found in the request and the HTTP method with which the request was made. It, of course, allows the you to obtain the arguments that the client sent as part of the request. How you access the client data might depend on the HTTP method of the request.Note that you should use
- For any HTTP method, you can use the
getParameterValuesmethod, which will return the value of a named parameter. (The methodgetParameterNamesprovides the names of the parameters.) You can also manually parse the request.- For requests using the HTTP GET method, the
getQueryStringmethod will return aStringto be parsed.- For HTTP methods POST, PUT, and DELETE, you have the choice between two methods. If you expect text data, then it can be read using the
BufferedReaderreturned by thegetReadermethod; if you expect binary data, then it should be read with theServletInputStreamreturned by thegetInputStreammethod.either thegetParameterValuesmethod or one of the methods that allow you to parse the data yourself. They can not be used together in a single request.For responding to the client, an
HttpServletResponseobject provides two ways of returning the response data to the user. You can use the writer returned by thegetWritermethod or the output stream returned by thegetOutputStreammethod. You should usegetWriterto return text data to the user, andgetOutputStreamfor binary data.Before accessing the Writer or OutputStream, HTTP header data should be set. The
HttpServletResponseclass provides methods to access the header data, such as the content type and encoding, and content length of the response. After you set the headers, you may obtain the writer or output stream and send the body of the response to the user. Closing the writer or output stream after sending the response to the client allows the server to know when the response is complete.Example of an HTTP Servlet that handles the GET and HEAD methods
/** * This is a simple example of an HTTP Servlet. It responds to the GET * and HEAD methods of the HTTP protocol. */ public class SimpleServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // set header field first res.setContentType("text/html"); // then get the writer and write the response data PrintWriter out = res.getWriter(); out.println("<HEAD><TITLE> SimpleServlet Output</TITLE></HEAD><BODY>"); out.println("<h1> SimpleServlet Output </h1>"); out.println("<P>This is output is from SimpleServlet."); out.println("</BODY>"); out.close(); } public String getServletInfo() { return "A simple servlet"; }The example above shows the code for the entire servlet. The
doGetmethod, because it is returning text to the client, uses theHttpServletResponse'sgetWritermethod. It sets the sets the response header field, content type, before writing the body of the response, and closes the writer after writing the response.In addition to
doGet, there is a second method,getServletInfo. More information on thegetServletInfomethod appears in a later section. Because this servlet is an example shipped with the release, it is already compiled. To try the servlet, run it in theservletrunner.The following example processes data POSTed by a form. (This tutorial assumes that you know HTML. It does not attempt to instruct you on how to develop the forms, only on how to process one using a servlet.) The form looks like this:
<html> <head><title>JdcSurvey</title></head> <body> <form action=http://demo:8080/servlet/survey method=POST> <input type=hidden name=survey value=Survey01Results> <BR><BR>How Many Employees in your Company?<BR> <BR>1-100<input type=radio name=employee value=1-100> <BR>100-200<input type=radio name=employee value=100-200> <BR>200-300<input type=radio name=employee value=200-300> <BR>300-400<input type=radio name=employee value=300-400> <BR>500-more<input type=radio name=employee value=500-more> <BR><BR>General Comments?<BR> <BR><input type=text name=comment> <BR><BR>What IDEs do you use?<BR> <BR>JavaWorkShop<input type=checkbox name=ide value=JavaWorkShop> <BR>J++<input type=checkbox name=ide value=J++> <BR>Cafe'<input type=checkbox name=ide value=Cafe'> <BR><BR><input type=submit><input type=reset> </form> </body> </html>The servlet writes the form data to a file, and responds to the user with a thank you message. The
doPostmethod of the servlet looks like this:/** * Write survey results to output file in response to the POSTed * form. Write a "thank you" to the client. */ public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // first, set the "content type" header of the response res.setContentType("text/html"); //Get the response's PrintWriter to return text to the client. PrintWriter toClient = res.getWriter(); try { //Open the file for writing the survey results. String surveyName = req.getParameterValues("survey")[0]; FileWriter resultsFile = new FileWriter(resultsDir + System.getProperty("file.separator") + surveyName + ".txt", true); PrintWriter toFile = new PrintWriter(resultsFile); // Get client's form data & store it in the file toFile.println("<BEGIN>"); Enumeration values = req.getParameterNames(); while(values.hasMoreElements()) { String name = (String)values.nextElement(); String value = req.getParameterValues(name)[0]; if(name.compareTo("submit") != 0) { toFile.println(name + ": " + value); } } toFile.println("<END>"); //Close the file. resultsFile.close(); // Respond to client with a thank you toClient.println("<html>"); toClient.println("<title>Thank you!</title>"); toClient.println("Thank you for participating"); toClient.println("</html>"); } catch(IOException e) { e.printStackTrace(); toClient.println( "A problem occured while recording your answers. " + "Please try again."); } // Close the writer; the response is done. toClient.close(); }The
doPostmethod uses thegetParameterNamesandgetParameterValuesmethods to get the form data. Because it returns text to the client,doPostcalls thegetWritermethod. It sets the sets the response header field, content type, before writing the body of the response, and closes the writer when the response is complete.Lifecycle Methods
Servlets that manage resources do so by overriding the lifecycle methods
initanddestroy. These servlets might need to be given arguments at startup, in order to initialize correctly.Overriding the
InitMethodDuring initialization, the servlet should prepare the resources it manages, to ready the servlet for accepting service requests. It can do this without regard for multi-threading concerns, since there is only a single thread running on behalf of the servlet during initialization. As soon as the
initmethod returns, the servlet can receive client requests. If, for some reason, the servlet's required resources can not be made available (for example, a required network connection can not be established), or some other initialization error occurs that would make it impossible for the servlet to handle requests, theinitmethod should throw anUnavailableExceptionexception.The
initmethod takes aServletConfigobject as a parameter. The method should save this object, so that it can be returned by thegetServletConfigmethod. The simplest way to do this is to have the newinitmethod callsuper.init. If you do not do this, you should store the ServletConfig object yourself, and override thegetServletConfigmethod so that it can obtain the object from its new location.An example
initmethod follows. It is theinitmethod from the Survey Servlet, which accepts input from a form and stores it in a file. In order store the survey information, it needs a directory. It receives the directory as an initialization parameter; initialization parameters are discussed in the next section.public void init(ServletConfig config) throws ServletException { super.init(config); //Store the directory that will hold the survey-results files resultsDir = getInitParameter("resultsDir"); //If no directory was provided, can't handle clients if (resultsDir == null) { throw new UnavailableException (this, "Not given a directory to write survey results!"); } }As you can see, this
initmethod calls thesuper.initmethod to manage the ServletConfig object. Theinitmethod also sets a field,resultsDir, with the directory name that is provided as an initialization parameter. If no directory name was provided, the servlet throws an unavailable exception. If the init method completes successfully, the servlet can then handle client requests.Initialization Parameters
The specification of initialization parameters is server-specific. For example, they are specified with a property when a servlet is run with the servlet runner. This tutorial contains a general explanation of properties, and how to create them.
However the initialization parameters are specified, they are always obtained the same way: with the
getInitParametermethod. This method takes the parameter name as an argument. The exampleinitmethod callsgetInitParameter. If, for some reason, you need to get the parameter names, you can get them with thegetParameterNamesmethod.Overriding the
DestroyMethodWhen a server unloads a servlet, it calls the servlet's
destroymethod. Thedestroymethod should undo any initialization work and synchronize persistent state with the current in-memory state. This section begins with a description of how to write a simpledestroymethod, then describes how to structure a servlet if threads running itsservicemethod might still be running when thedestroymethod is called.Though it is often the case that a servlet that overrides the
initmethod must also override thedestroymethod to undo that initialization, this is not required. For example, the phone servlet, whoseinitmethod is used as an example, does not have a correspondingdestroymethod. Because initialization involves reading a file and using its contents to initialize a shared data structure, there is no work to undo when the server is finished with the servlet.For many servlets, however, there is initialization work that must be undone. For example, assume there is a servlet that opens a database connection during initialization. Its
destroymethod, shown as an example below, would close that connection./** * Cleans up database connection */ public void destroy() { try { con.close(); } catch (SQLException e) { while(e != null) { log("SQLException: " + e.getSQLState() + '\t' + e.getMessage() + '\t' + e.getErrorCode() + '\t'); e = e.getNextException(); } } catch (Exception e) { e.printStackTrace(); } }Coping with Service Threads at Servlet TerminationWhen a server removes a servlet, it typically calls destroy after all service calls have been completed, or a server-specific number of seconds have passed, whichever comes first. If your servlet has operations that take a long time to run (that is, they may run longer than the server's grace period), then threads could still be running when destroy is called. The servlet writer is responsible for making sure that any threads still handling client requests complete; the remainder of this section describes a technique for doing this.
A servlet with potentially long-running service requests should keep track of how many
servicemethods are currently running. Its long-running methods should periodically poll to make sure that they should continue to run. If the servlet is being destroyed, then the long-running method should stop working, clean up if necessary, and return.For example, the instance variable that counts the number of
servicemethods running could be calledserviceCounter, and the indicator of whether the servlet is being destroyed could be an instance variable calledshuttingDown. Each variable should have its own set of access methods:public ShutdownExample extends HttpServlet { private int serviceCounter = 0; private Boolean shuttingDown; ... //Access methods for serviceCounter protected synchronized void enteringServiceMethod() { serviceCounter++; } protected synchronized void leavingServiceMethod() { serviceCounter--; } protected synchronized int numServices() { return serviceCounter; } //Access methods for shuttingDown protected setShuttingDown(Boolean flag) { shuttingDown = flag; } protected Boolean isShuttingDown() { return shuttingDown; } }The
servicemethod should increment the service counter each time it is entered and decrement it each time it returns:protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { enteringServiceMethod(); try { super.service(req, resp); } finally { leavingServiceMethod(); } }The
destroymethod should check theserviceCounter, and if there are any long-running methods, set theshuttingDownvariable. This variable will let the threads still handling client requests know that it is time to shut down. Thedestroymethod should then wait for theservicemethods to complete, in order to provide a clean shutdown.public void destroy() { /* Check to see whether there are still service methods running, * and if there are, tell them to stop. */ if (numServices() > 0) { setShuttingDown(true); } /* Wait for the service methods to stop. */ while(numService() > 0) { try { thisThread.sleep(interval); } catch (InterruptedException e) { } } }The long-running methods should check this variable, and interrupt their work if neceesary:public void doPost(...) { ... for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++) { try { partOfLongRunningOperation(i); } catch (InterruptedException e) { } } }Providing Information about the Servlet
Some applets and applications, for example, the Java Web Server Administration Tool, display information about a servlet. This information can include a short description of the purpose of the servlet, its author, and perhaps its version number. The Servlet API provides a method to return this information,
getServletInfo. By default, this method returns null. While servlet writers are not required to override this method, it is strongly recommended. The simple servlet, shown as an example earlier, overrides this method:/** * This is a simple example of an HTTP Servlet. It responds to the GET * and HEAD methods of the HTTP protocol. */ public class SimpleServlet extends HttpServlet { ... public String getServletInfo() { return "A simple servlet"; } }
Another Simple Example
The next simple example is presented below:
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class test extends HttpServlet {
public String getServletInfo() {
return "Test Servlet $Revision: 1.0 $.\nTest Servlet by Al Williams " ;
}
/**
* Service the request.
*
* @exception ServletException If a ServletException occurred
*
*/
public void service(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
ServletOutputStream out = response.getOutputStream();
// set the title and the HTML preamble
out.println("<HTML>");
out.println("<HEAD><TITLE>Test Servlet</TITLE></HEAD>");
out.println("<BODY>");
// greet user with host and IP name
out.println("<H1>You bet you are at " +
request.getHeader("Host") + "</H1>");
out.println("<P>How are things at "+request.getRemoteAddr()+"?</P>");
// Print query string, if any
String s=request.getQueryString();
if (s!="") out.println(s);
// Enumerate all headers
Enumeration e=request.getHeaderNames();
while (e.hasMoreElements())
{
s=(String)e.nextElement();
out.println("<p>"+s+": " + request.getHeader(s)+"</p>");
}
// Close out HTML
out.println("</BODY>");
out.println("</HTML>");
}
}
This example is the equivalent of $query->dump from CGI.pm.
Notice that the response object argument allows you to set the type (via
setContentType), and retrieve an output stream you can use to write to the
HTML output (getOutputStream). Once you have the output stream, you can use
println, for example, to write output that the user will see.
Another interesting thing is that the request object can obtain information
about the client, including form data, query strings, and header information. The simple
server reads and prints the server's name, the client's address, and the query string (if
any). Then it dumps all the headers using an enumeration.
A Servlet Hit Counter
Since servlets stick around after they load it isn't hard to keep a running count. However the count resets every time the servlet reloads.
/* Example Servlet provides an hourly hit counter */
import java.util.*;
import java.text.DateFormat;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class WebCounter extends HttpServlet {
static int counter;
static int lasthour;
public String getServletInfo() {
return "WebCounter $Revision: 1.0 $.\nby Al Williams " ;
}
/**
* Set up initial count
**/
public void init(ServletConfig config) throws ServletException
{
super.init(config);
// Write message to servlet.log
System.err.println("WebCounter initializing!");
if (counter!=0) System.err.println(
"WebCounter:Warning Init called more than once");
lasthour=-1; // make sure we start fresh
}
/**
* Service the request.
*
* @exception ServletException If a ServletException occurred
*
*/
public void service(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
Date date=new Date(); // current date
Calendar cal=Calendar.getInstance();
DateFormat fmt=DateFormat.getDateTimeInstance();
cal.setTime(date);
// check to see if we have a new hour
// this code assumes we get visitors every hour
// (or at least every 12 hours)
// so no need to check the day or AM/PM, etc.
if (lasthour==cal.get(Calendar.HOUR))
{
counter++;
}
else
{
lasthour=cal.get(Calendar.HOUR);
counter=1;
}
// Write it out
response.setContentType("text/html");
ServletOutputStream out = response.getOutputStream();
out.println("It is now " + fmt.format(date));
out.println("You are visitor # " + counter + " this hour.");
}
}
This servlet does not implement a persistent count. Instead, it writes back the time
of day and the date. It also tells you how many hits the page has had in the current
hour. The code is fairly simplistic and only checks for the hour. The counter can get
yconfused if you don't receive visitors for exactly 12 hours. Servlets Meet Apache
Next week we will look into what it takes to install server side Java support for Apache.
Summary
Between server-side scripting and Java servlets, it's easy to see that CGI programming is (slowly) on the way out. Catch the wave early and get started writing servlets today.
JSDK Servlet Tutorial the source for half of these notes. The other half is based on Al Williams' Web Techniques article: Apache Java Servlets (August 1998 issue).
Further Reading
November 16, 1998
Dear JavaWorld reader,Enjoy!Our DECEMBER ISSUE is now online! Stop by JavaWorld to get the information you need from the experts you've come to trust:
Servlets are one of the most exciting Java-based technologies because they fluidly and effectively handle difficult server-side needs. This month, JavaWorld features two articles on servlets to help you get up to speed with this popular technology.http://www.javaworld.com/jw-12-1998/jw-12-toc.html?111698txtIn other Nuts & Bolts articles, you'll learn how to get rid of those empty black squares in your text editor; how to develop software for the global marketplace; and how RMI can help you define extensible, distributed object-oriented frameworks through behavior objects. And that's just for starters!
...