The JavaTM Web ServerTM is both flexible and extensible. Using the Java Web Server's Java Servlet Development Kit (JSDK), you can write your own servlet and incorporate it into the server.
This tutorial contains,
The JavaServerTM product family
provides servers that can be
configured to run one or more services: bodies of code that
implement an application-level protocol, such as FTP, DHCP, or SMTP.
Servlets are modules that run inside request/response-oriented
services and extend them in some manner.
For example, an HTTP service responds to its clients by delivering
the HTML files they request. A servlet can extend the capabilities of
the HTTP service by taking the data that a client entered in an HTML
order-entry form and applying the business logic used to update a
company's order database. A servlet can call on other services and
servlets to satisfy a request, if appropriate.
Example Uses
A few of the many applications for servlets include,
The central abstraction in the JSDK is the Servlet
interface. All servlets implement this interface, either directly or,
more commonly, by extending a class that implements it such as
HttpServlet
. The Servlet
interface 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 ServletRequest
and the other is a
ServletResponse
. The ServletRequest
class
encapsulates the communication from the client to the server, while the
ServletResponse
class encapsulates the communication from
the servlet back to the client.
The ServletRequest
interface 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 of ServletRequest
allow the servlet
to retrieve more protocol-specific data. For example,
HttpServletRequest
contains methods for accessing
HTTP-specific header information.
The ServletResponse
interface 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 of
ServletResponse
give the servlet more protocol-specific
capabilities. For example, HttpServletResponse
contains
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
Services 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 life cycle. The next paragraphs describe each step in more detail, concentrating on concurrency issues.
When a service loads a servlet, it runs the servlet's init
method. Even though most servlets are run in multi-threaded services,
there are no concurrency issues during servlet initialization. This is
because the service calls the init
method once, when it
loads the servlet, and will not call it again unless it is reloading
the servlet. The service cannot reload a servlet until after it has
removed the servlet by calling the
destroy
method. Initialization is allowed to complete
before client requests are handled (that is, before the
service
method is called) or the servlet is destroyed.
After the service loads and initializes the servlet, the servlet is
able to handle client requests. It processes them in its service
method. Each client's request has its call to the service
method run in its own servlet thread: the method receives the client's
request, and sends the client its response.
Servlets can run multiple service
methods at a time.
It is important, therefore, that service
methods be
written in a thread-safe manner. For example, if a
service
method updates a field in the servlet object,
that access should be synchronized. If, for some reason, a service
should not run multiple service
methods concurrently, the
servlet should implement the SingleThreadModel
interface.
This interface guarantees that no two threads will execute the
servlet's service
methods concurrently.
Servlets run until they are removed from the service, for example,
at the request of a system administrator. When a service removes a
servlet, it runs the servlet's destroy
method. The method is run once; the service will not run it again
until after it reloads and reinitializes the servlet. When the
destroy
method 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
SingleThreadModel
interface, servicing client requests.
For more information on writing thread-safe code, please see the
Java Tutorial.
Writing a Servlet
Servlets implement the
javax.servlet.Servlet
interface. 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 the
javax.servlet.http.HttpServlet
class. This version of
the tutorial concentrates on describing this method of writing
servlets.
The HttpServlet
class implements the Servlet interface
by extending the GenericServlet base class, and provides a framework
for handling the HTTP protocol. Its service
method
supports standard HTTP/1.1 requests by dispatching each request to a
method designed to handle it.
By default, servlets written by specializing the
HttpServlet
class can have multiple threads concurrently
running its service
method. If, for whatever reason, you
would like to have only a single thread running a service
method at a time, then in addition to extending the
HttpServlet
, your servlet should also implement the
SingleThreadModel
interface. This does not
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. */ ... }
Servlet writers who are developing HTTP servlets that specialize the
HttpServlet
class 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
requests
doPost
, for handling POST requests
doPut
, for handling PUT requests
doDelete
, for handling DELETE requests
By default, these methods return a BAD_REQUEST (400)
error. An example HTTP servlet that
handles GET and HEAD requests follows; it specializes the
doGet
method. A second
example is also provided. It handles POST requests from a form by
specializing the doPost
method.
The HttpServlet
's service
method, by
default, also calls the
doOptions
method when it receives an OPTIONS request,
and
doTrace
when it receives a TRACE request. The default
implementation of doOptions
automatically determines what
HTTP options are supported and returns that information. The default
implementation of doTrace
causes 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 an HttpServletResponse
. The
following paragraphs discuss their use.
An HttpServletRequest
object 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 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.
getParameterValues
method, which will return the value
of a named parameter. (The method
getParameterNames
provides the names of the
parameters.) You can also manually parse the request.
getQueryString
method will return a
String
to be parsed.
BufferedReader
returned by the
getReader
method; if you expect binary data, then it
should be read with the ServletInputStream
returned by the
getInputStream
method.
getParameterValues
method or one of the methods that allow
you to parse the data yourself. They cannot be used together in a
single request.
For responding to the client, an HttpServletResponse
object provides two ways of returning the response data to the user.
You can use the writer returned by the
getWriter
method or the output stream returned by the
getOutputStream
method. You should use
getWriter
to return text data to the user, and
getOutputStream
for binary data.
Before accessing the writer or output stream, HTTP header data
should be set. The HttpServletResponse
class 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 service 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
doGet
method, because it is returning text to the client,
uses the HttpServletResponse
's getWriter
method. It sets the response header field and content type
before writing the body of the response, then closes the writer after
writing the response.
In addition to doGet
, there is a second method,
getServletInfo
. More information on the
getServletInfo
method 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 the Java Web Server.
Example of an HTTP Servlet that handles the POST method
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 doPost
method 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("lt;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 doPost
method 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 and content type before writing the
body of the response, then closes the writer when the response is
complete.
Lifecycle Methods
Servlets that manage resources do so by overriding the lifecycle
methods init
and destroy
. These servlets might
need to be given arguments at startup, in
order to initialize correctly.
Overriding the Init Method
During 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 init
method returns, the
servlet can receive client requests. If, for some reason, the
servlet's required resources cannot be made available (for example, a
required network connection cannot be established), or some other
initialization error occurs that would make it impossible for the
servlet to handle requests, the init
method should throw
an UnavailableException
exception.
The init
method takes a ServletConfig
object as a parameter. The method should save this object, so that it
can be returned by the getServletConfig
method. The
simplest way to do this is to have the new init
method
call super.init
. If you do not do this, you should store
the ServletConfig object yourself, and override the
getServletConfig
method so that it can obtain the object
from its new location.
An example init
method follows. It is the
init
method from the Survey Servlet, which accepts input
from a form and stores it in a file. To 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 init
method calls the
super.init
method to manage the ServletConfig object. The
init
method 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. To run a servlet in the Java Web Server, you specify the initialization parameters in the Administration Tool before run the servlet.
However the initialization parameters are specified, they are always
obtained the same way: with the
getInitParameter
method. This method takes the
parameter name as an argument. The example
init
method calls getInitParameter
. If,
for some reason, you need to get the parameter names, you can get them
with the
getParameterNames
method.
Overriding the Destroy Method
When a service unloads a servlet, it calls the servlet's
destroy
method. The destroy
method 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 simple destroy
method, then describes how to structure a servlet if threads
running its service
method might still be running when the
destroy
method is called.
Though it is often the case that a servlet that overrides the
init
method must also override the destroy
method to undo that initialization, this is not required. For example,
the phone servlet, whose init
method is used as an
example, does not have a corresponding destroy
method.
Because initialization involves reading a file and using its contents
to initialize a shared data structure, there is no work to undo when
the service 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 destroy
method, 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 Termination
When a service removes a servlet, it typically calls destroy after all service calls have been completed, or a specified 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 service
methods 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
service
methods running could be called
serviceCounter
, and the indicator of whether the servlet
is being destroyed could be an instance variable called
shuttingDown
. 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 service
method 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 destroy
method should check the
serviceCounter
, and if there are any long-running methods,
set the shuttingDown
variable. This variable will let the
threads still handling client requests know that it is time to shut
down. The destroy
method should then wait for the
service
methods 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) { } } }
Some applets and applications, for example, the
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"; } }
Once you have written your servlet, you can run it in the Java Web Server. Before you run your servlet, there are certain pieces of data that you must specify; this is done with the Administration Tool.
With the Java Web Server running, log into the Administration Tool. For general instructions on logging in and using the Administration Applet, see the Administration Log In/Log Out page.
After you have logged in, you can use the servlet loading facility. What follows is a brief set of instructions on how accomplish each task in loading a servlet. They assume that you have logged into the Administration Tool, and that your browser is showing the screen that appears after login.
Each step in the list below is linked to more in-depth instruction, including an example and a screen shot. The size of many of the screen shots had to be reduced for this document; click on them to see the full-sized picture.
The example, when followed step by step, installs the survey
servlet. The the code for the survey servlet was shown earlier; its files can be found
in the server_root/servlets
directory of this
release.
The screen that appears after logging in shows a list of the services running in the server. Choose the service that will run your servlet and click the Manage button.
Example
In the upper left of the window that appears, you will see four buttons. The button marked Setup is, by default, depressed. Click the button marked Servlets.
This screen allows you to manage servlets. Click the Add item, which is in the list at the left hand side of the window, to add a servlet. A form will appear on the right side of the screen.
To add a new servlet,
http://server_host_name:port/servlet/survey
javax.servlet.http.FileServlet
is a valid class name for a class called FileServlet
in
the javax.servlet.http
package. If you have written a
servlet, but not put it in a package, then the full package name is
merely the classname as it appears in your source file.
Example
survey
for the Servlet Name
SurveyServlet
for the Servlet Class
This screen has two panels that allow you to configure your servlet. To do this,
Note: If you are adding initialization arguments, the
Property is initArgs
Note: If you are adding initialization arguments, the Value consists of one or more name=value pairs.If the servlet has one initialization argument, then the Value is a single name-value pair. For example,
, provides one initialization argument ( resultsDir=/tmp
resultsDir
) and its value (/tmp
).If there are multiple initialization arguments, the name-value pairs should be delimited by commas, with no spaces between them. For example,
is a properly formatted set of name-value pairs. resultsDir=/tmp,fileName=surveyResults
Example
Takes input from a form and stores it in a
file
initArgs
.
resultsDir=/tmp
To run a servlet, create a URL with "/servlet/" prepended to the servlet name. Servlet URLs, then, have the general form:
http://server_host_name:port/servlet/servlet-name
The SimpleServlet, used earlier as an example, is already installed in the
JavaWebServer. Its name is simpleservlet
. To run it,
enter this URL in your favorite browser:
http://server_host_name:port/servlet/simpleservlet
As another example, you can run the survey servlet if you installed it using the installation instructions above. The survey servlet is called to process an HTML form. The form calls the servlet with the following line:
<form action=http://demo:8080/servlet/survey method=POST>The HTML file that contains the form, JdcSurvey.html, is in the
server_root/public_html
directory. Change the
server-host-name in the file from demo
to the machine that
will be running the survey servlet for you. Change the port if
necessary, too. After you have made these changes, access the HTML
file from your browser with the URL,
http://server_host_name:port/JdcSurvey.htmlThe form displayed will look like this:
After you fill it in and click the Submit button, the survey servlet will run, and will return the following response:
Once you've added the servlet, you can also alias it to a URL. The
servlet alias is another name that the user can type to run the
servlet, in addition to using the
servlet/servlet-name
URL. This is done in the Servlet Aliases
screen, under the Setup Button.
To alias a servlet:
http://server_host_name:port
when accessing
the servlet.
To map the simpleservlet to an alias,
The simpleservlet can now be invoked with the URL
http://<host>/simple
:
Top java-server-feedback@java.sun.com |
Copyright © 1997
Sun Microsystems, Inc. All Rights Reserved. |