The Polls Servlet

As we saw in Chapter 4, the Polls servlet presents a simple web API used to create a new instance of a poll, cast votes, and view the tally. Table 10.1 illustrates the Polls API.

Table 10-1. The Polls Servlet’s Web API

Action

URL

Create a new poll

/Polls?name=Picnic99&1=Sat+June+12&2=Sat+June+19&3=Sun+June+20

Cast a vote

/Polls?name=Picnic99&vote=Sun+June+20

View the tally

/Polls?name=Picnic99&tally=

At the heart of Polls is a Java hash-of-hashes (HoH)—that is, a Java Hashtable whose keys are the names of poll instances (such as Picnic99) and whose values are hashtables. The keys of each interior hashtable are the names of the choices in that poll (e.g., “Sat June 19”), and the values count the votes for each of these choices. Table 10.2 depicts these structures.

Table 10-2. The Polls Servlet’s Central Data Structure

Key: poll name

Value: Hashtable

Picnic99

key: choice name

value: vote count

 

Sat June 12

12

 

Sat June 19

5

 

Sun June 20

14

Preferred HMO

Tufts

3

 

Matthew Thornton

11

 

Harvard Pilgrim

7

In Perl, as in Java, it’s easy to create this kind of HoH. But a Perl CGI script can’t so easily meet the following requirements:

  • Retain the object in memory across multiple invocations of the script.

  • Protect the object from concurrent use by multiple clients.

  • Retrieve the object from disk at start-up and keep the in-memory version synched with the on-disk version as updates occur.

Larry Wall likes to say that Perl aims to makes easy things easy and hard things possible. These hard things, though, are easy in Java. (But as we’ll see, Java can also make some easy things hard.) When the servlet host starts up Polls, its instance data (the HoH) persists across calls to the servlet. A conventional Perl CGI script has to refresh its in-memory objects on each invocation, by querying a database or accessing some other kind of disk-based storage. An Apache mod_ perl script, always resident in memory, nevertheless can’t safely use shared Perl data structures, because there’s no simple way to synchronize access to them from concurrent Apache child processes.

Making the Hard Things Easy

In Java, protecting the Polls object from multiple concurrent voters is as easy as adding the synchronized keyword to the declaration of the servlet’s vote( ) method. Example 10.1 shows how that can work.

Example 10-1. The Polls Servlet’s vote( ) Method

private synchronized void vote ( Hashtable hParams, HttpServletResponse res )
    throws IOException
    {
    Hashtable hPoll = (Hashtable) Polls.get ( getPollName(hParams) );
    Integer voteTally = (Integer) hPoll.get ( getVoteName(hParams) );
    int tally = voteTally.intValue();
    tally++;
    hPoll.put ( getVoteName(hParams), new Integer (tally) );
    PrintStream out = new PrintStream(res.getOutputStream());
    System.out.println ( "Vote received. Poll: " + getPollName(hParams) +
                                        "Vote: " + getVoteName(hParams) );
    }

Each time you issue a request to a servlet, its host calls the servlet’s service( ) method, passing two arguments. The first, an HttpServletRequest object, contains the CGI-style parameters passed by way of an HTTP GET request, plus the HTTP headers included with the request. The second, an HttpServletResponse object, is used by the servlet to modify the HTTP headers sent back to the client. When Polls’ service( ) method sees the parameter vote=Sun+June+20, it calls the vote( ) method to increment the counter for that item. What if two requests come in at the same time? The servlet runs on a thread, and that thread blocks (has to wait) while executing a method guarded by the synchronized keyword. This technique can very easily coordinate requests to update a shared data structure.

Saving and restoring the object are trivial tasks, too, thanks to Java serialization. Java’s Hashtable object implements the Serializable interface. That means you can simply open a FileOutputStream, hook an ObjectOutputStream to it, and call writeObject( ) to save it to disk, as shown in Example 10.2.

Example 10-2. Serializing the Polls Object to a File

private synchronized void saveObjects()
    {
    try
        {
        FileOutputStream   f = new FileOutputStream("polls.obj");
        ObjectOutputStream o = new ObjectOutputStream(f);
        o.writeObject(Polls);
        o.flush();
        o.close();
        }
    catch (IOException e)
        { System.out.println(e); }
    }

The service( ) method calls saveObjects( ) right after it calls vote( ). It does this only to make a permanent record of the Polls object, so the servlet can reconstitute it after a restart. Once it loads the object file at start-up, though, it never refers to it. Votes are written to, and tallies read from, the servlet’s own live in-memory Polls object. The servlet is literally just a web API to that object.

Loading the Polls object at start-up is equally simple, as shown in Example 10.3.

Example 10-3. Reading the Polls Object at Start-up

private void loadObjects()
    {
    try
        {
        FileInputStream   f = new FileInputStream("polls.obj");
        ObjectInputStream o = new ObjectInputStream(f);
        Polls = (Hashtable) o.readObject();
        o.close();
        }
    catch (IOException e)
        {  System.out.println(e); }
    catch (ClassNotFoundException e)
        {  System.out.println(e);  }
    }

After loadObjects( ) runs, the Polls object that was written out using saveObjects( ) is reconstituted as a live Java Hashtable.

Making the Easy Things Hard

This is nifty stuff. But even as it makes some hard things easy, Java makes some easy things hard. For example, the vote( ) method goes to a lot of trouble to bridge the chasm that divides primitive Java types (i.e., int) from their object counterparts (i.e., Integer). The keys and values of a Java Hashtable have to be objects, not primitive types. But you can’t increment an Integer, so the vote( ) method has to unpack the Integer, increment its corresponding int, and then repackage it as an Integer to store it back in the Hashtable, as shown in Example 10.1. In Perl, these gymnastics would reduce to a simple statement like:

$hash{$key}++;

Then there’s the problem of sorting the results of each poll. In Perl, you can sort the keys of a hashtable by their corresponding values like this:

@sorted_keys = sort { $hash{$b} <=> $hash{$a} } keys %hash;

For a Polls hashtable this yields, in @sorted_keys, a list of the choices ordered by the number of votes for each choice. You can then print a tally, from most popular choice to least, like this:

foreach $choice (@sorted_keys)
    { print "$choice: $hash{$choice}
"; }

These kinds of quick, powerful data wrangling idioms aren’t built into the basic Java toolkit. There are plenty of freely available libraries that you can use to sort and rearrange Java objects, but even something as basic as an ordered collection isn’t part of the core language. The lesson is that Java giveth but also taketh away. Servlets are a great way to paste a web API onto dynamic, thread-safe, persistent, object-database compatible data structures. But servlets have to work harder to manipulate those live structures than do Perl scripts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset