Java Examples In A Nutshell (3rd Edition) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Examples In A Nutshell (3rd Edition) [Electronic resources] - نسخه متنی

O'Reilly Media, Inc

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








20.4 Servlet Initialization and Persistence: A Counter Servlet


Example 20-3 is a
listing of Counter.java, a servlet that
maintains any number of named counters. Each time the value of one of
these counters is requested, the servlet increments the counter and
outputs its new value. The servlet is suitable for use as a simple
hit counter for multiple web pages but can also count any other type
of event.

This servlet defines init(
)
and destroy( ) methods and saves its
state to a file, so it does not lose count when the web server (or
servlet container) shuts down. To understand init(
)
and destroy( ), you have to understand
something about the servlet life cycle. Servlet instances are not
usually created anew for each client request. Instead, once a servlet
is created, it can serve many requests before it is destroyed. A
servlet such as Counter is typically not shut down
unless the servlet container itself is shutting down, or the servlet
is inactive and the container is trying to free up memory to make
room for other servlets.

The init( ) method is
invoked when the servlet container first instantiates the servlet,
before any requests are serviced. The first thing this method does is
look up the value of two initialization
parameters
: the filename of the file that contains the
saved state and an integer value that specifies how often to save the
state back into that file. Once the init( ) method
has read these parameters, it reads the counts (using object
serialization) from the specified file and is ready to begin serving
requests. The values of the initialization parameters come from the
WEB-INF/web.xml deployment file. Before running
this example, you probably need to edit that file to tell the servlet
where to save its state. Look for these lines:

<init-param>
<param-name>countfile</param-name> <!-- where to save state -->
<param-value>/tmp/counts.ser</param-value>
<!-- adjust for your system-->
</init-param>

If the filename /tmp/counts.ser does not make
sense on your system, replace it with a filename that does.

The destroy( ) method is the companion to
init( ); it is invoked after the servlet has been
taken out of service and there are no more requests being processed
by the servlet. The Counter servlet uses this
method to save its state, so it can be correctly restored when the
servlet container starts the servlet up again. Note, however, that
the destroy( ) method is invoked only when the
servlet is shut down in a controlled way. In the case of a server
crash or power outage, for example, there is no opportunity to save
state. Thus, the Counter servlet also periodically
saves it state from the doGet( ) method, so it
never loses more than a small amount of data.

The doGet(
)
method must first determine the name of the counter whose
value is to be displayed. Since the Counter
servlet is designed for use in a variety of ways, doGet(
)
uses three techniques for obtaining the counter name.
First, it checks for a parameter sent with the HTTP request. Next, it
checks for a request attribute, which is a named value associated
with the request by the servlet container or by another servlet that
has invoked the Counter servlet.
(You'll see an example of this later in the
chapter.) Finally, if the servlet cannot find a counter name using
either of these techniques, it uses the URL through which it was
invoked as the counter name. As you'll see shortly,
the servlet container can be configured to invoke the servlet in
response to any URL that ends with the suffix
.count.

Note that the
doGet( ) method contains a
synchronized block of code. The servlet API allows
multiple threads to execute the body of doGet( )
at the same time. Even though many threads representing many
different user sessions may be running, they all share a single data
structurethe hashtable of named counts. The
synchronized block prevents the threads from
accessing (and possibly corrupting) the shared data structure at the
same time. Finally, note the use of the log( )
method. When asked to start counting with a counter name it has never
used before, the servlet uses this method to produce a permanent
record of the event in a log file.

Example 20-3. Counter.java

package je3.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
/**
* This servlet maintains an arbitrary set of counter
variables and increments
* and displays the value of one named counter
each time it is invoked. It
* saves the state of the counters to a disk file,
so the counts are not lost
* when the server shuts down.
It is suitable for counting page hits, or any
* other type of event.
It is not typically invoked directly, but is included
* within other pages, using JSP, SSI, or a RequestDispatcher
**/
public class Counter extends HttpServlet {
HashMap counts; // A hash table: maps counter names to counts
File countfile; // The file that counts are saved in
long saveInterval;
// How often (in ms) to save our state while running?
long lastSaveTime; // When did we last save our state?
// This method is called when the web server first instantiates this
// servlet. It reads initialization parameters (which are configured
// at deployment time in the web.xml file), and loads the initial state
// of the counter variables from a file.
public void init( ) throws ServletException {
ServletConfig config = getServletConfig( );
try {
// Get the save file.
countfile = new File(config.getInitParameter("countfile"));
// How often should we save our state while running?
saveInterval =
Integer.parseInt(config.getInitParameter("saveInterval"));
// The state couldn't have changed before now.
lastSaveTime = System.currentTimeMillis( );
// Now read in the count data
loadState( );
}
catch(Exception e) {
// If something goes wrong, wrap the exception and rethrow it
throw new ServletException("Can't init Counter servlet: " +
e.getMessage( ), e);
}
}
// This method is called when the web server stops the servlet (which
// happens when the web server is shutting down, or when the servlet is
// not in active use). This method saves the counts to a file so they
// can be restored when the servlet is restarted.
public void destroy( ) {
try { saveState( ); } // Try to save the state
catch(Exception e) { } // Ignore any problems: we did the best we could
}
// These constants define the request parameter and attribute names that
// the servlet uses to find the name of the counter to increment.
public static final String PARAMETER_NAME = "counter";
public static final String ATTRIBUTE_NAME =
"je3.servlet.Counter.counter";
/**
* This method is called when the servlet is invoked. It looks for a
* request parameter named "counter", and uses its value as the name of
* the counter variable to increment. If it doesn't find the request
* parameter, then it uses the URL of the request as the name of the
* counter. This is useful when the servlet is mapped to a URL suffix.
* This method also checks how much time has elapsed since it last saved
* its state, and saves the state again if necessary. This prevents it
* from losing too much data if the server crashes or shuts down without
* calling the destroy( ) method.
**/
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
// Get the name of the counter as a request parameter
String counterName = request.getParameter(PARAMETER_NAME);
// If we didn't find it there, see if it was passed to us as a
// request attribute, which happens when the output of this servlet
// is included by another servlet
if (counterName == null)
counterName = (String) request.getAttribute(ATTRIBUTE_NAME);
// If it wasn't a parameter or attribute, use the request URL.
if (counterName == null) counterName = request.getRequestURI( );
Integer count; // What is the current count?
// This block of code is synchronized because multiple requests may
// be running at the same time in different threads. Synchronization
// prevents them from updating the counts hashtable at the same time
synchronized(counts) {
// Get the counter value from the hashtable
count = (Integer)counts.get(counterName);
// Increment the counter, or if it is new, log and start it at 1
if (count != null) count = new Integer(count.intValue( ) + 1);
else {
// If this is a counter we haven't used before, send a message
// to the log file, just so we can track what we're counting
log("Starting new counter: " + counterName);
// Start counting at 1!
count = new Integer(1);
}
// Store the incremented (or new) counter value into the hashtable
counts.put(counterName, count);
// Check whether saveInterval milliseconds have elapsed since we
// last saved our state. If so, save it again. This prevents
// us from losing more than saveInterval ms of data, even if the
// server crashes unexpectedly.
if (System.currentTimeMillis( ) - lastSaveTime > saveInterval) {
saveState( );
lastSaveTime = System.currentTimeMillis( );
}
} // End of synchronized block
// Finally, output the counter value. Since this servlet is usually
// included within the output of other servlets, we don't bother
// setting the content type.
PrintWriter out = response.getWriter( );
out.print(count);
}
// The doPost method just calls doGet, so that this servlet can be
// included in pages that are loaded with POST requests
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
doGet(request, response);
}
// Save the state of the counters by serializing the hashtable to
// the file specified by the initialization parameter.
void saveState( ) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(
new BufferedOutputStream(new FileOutputStream(countfile)));
out.writeObject(counts); // Save the hashtable to the stream
out.close( ); // Always remember to close your files!
}
// Load the initial state of the counters by de-serializing a hashtable
// from the file specified by the initialization parameter. If the file
// doesn't exist yet, then start with an empty hashtable.
void loadState( ) throws IOException {
if (!countfile.exists( )) {
counts = new HashMap( );
return;
}
ObjectInputStream in = null;
try {
in = new ObjectInputStream(
new BufferedInputStream(new FileInputStream(countfile)));
counts = (HashMap) in.readObject( );
}
catch(ClassNotFoundException e) {
throw new IOException("Count file contains bad data: " +
e.getMessage( ));
}
finally {
try { in.close( ); }
catch (Exception e) { }
}
}
}


20.4.1 Running the Counter Servlet


Our
web.xml file assigns the name
counter to the Counter servlet.
The servlet reads a request parameter, also named
counter, to find the name of the counter variable
to increment and display. Thus, you can test the servlet by pointing
your browser to a URL like the following:

http://localhost:8080/je3/counter?counter=numhits

The web.xml file also specifies that the
Counter servlet should be invoked whenever a file
ending with the suffix .count (and beginning
with the web application name) is requested from the web server.
(You'll learn how the web.xml
file specifies this later in this chapter.) In this case,
Counter uses the URL itself as the name of the
counter variable. Test this feature by entering a URL like the
following:

http://localhost:8080/je3/indexl.count

By now, you've
probably noticed that the Counter servlet
doesn't produce very interesting output: it just
displays a number. This servlet is not very useful when used on its
own; it is intended, instead, to have its output included within the
output of other servlets. (We'll explore a couple of
ways to do this later in the chapter.) If your web server supports
server-side includes (SSI) and the <servlet>
tag, though, you can use this feature to include the output of the
Counter servlet in web pages. (Tomcat 5 can be
configured to support SSI, but does not do so by default.) To do so,
you might create a test.shtml file that contains
text like the following:

<p>This page has been accessed 
<servlet name='/counter'>
<param name='counter' value='test.shtml'></param>
</servlet>
times.


/ 285