Java 1.1 Unleashed
- 55 -
|
Section | Explanation |
HTTP Configuration | Sets HTTP protocol parameters. |
Log Configuration | Defines names used for log files that track server accesses and errors. |
File Aliasing | Controls the mapping of URLs to files and directories. |
Servlet Aliasing | Controls the mapping of URLs to servlets. |
Servlet Loading | Defines where the code for servlets is loaded from and any parameters passed to the servlet. |
MIME Section | Sets up mappings from file extensions to the MIME type returned. |
Users, Groups, and ACLs | These three sections control the creation, deletion, and modification of access control-related settings. |
Resource Protection | Allows you to grant privileges using the information entered with the preceding sections. More information on this is provided in "Access Control," later in this chapter. |
Reauthenticate | Lets you enter (authenticate) yourself to the server with your user name and password. It allows you to log in as a different user, or to reconnect to the server if you have restarted the server while leaving your browser to run the admin applet. |
The Jeeves Configuration applet.
The HTTP Configuration section of the Jeeves Configuration applet enables control of the settings related to the HTTP protocol. In this section, you set the port on which the server listens. You can also define the maximum number of connections the server will accept. Related to this are the minimum and maximum numbers of threads that can start to handle requests.
Jeeves provides support for the HTTP "keep-alive" directive. This extension to the HTTP protocol allows a client to ask that a connection be kept open and used to retrieve multiple URLs. The HTTP Configuration section has fields for setting the maximum number of requests for each keep-alive session and a timeout value (to prevent a client from monopolizing a connection).
The Log Configuration section of the Jeeves Configuration applet lets you define the names of the log files to which Jeeves will write information. The level of information written to each log can be customized depending on the log. For example, the access_log (logs which pages are being retrieved) and the referer_log (logs which page the browser came from before your page) can be either on or off. The error_log (records any errors the server encounters, such as trying to retrieve a nonexistent page) can be turned off, set to log only major problems, set to log major and minor problems, or set to record all errors.
In addition to the access log (which tracks the resources being accessed by clients) and the error log (which notes errors such as when nonexistent files are requested), Jeeves provides an event log for servlets. Servlets can use the log() method of the java.servlet.Servlet class to write a String to the event log.
Although the primary purpose of an HTTP server is to return files in response to client requests, you don't want to give out access to your complete file system to just anyone. The File Aliasing section of the Jeeves Configuration applet allows you to map URLs to particular directories or files. This facility also lets you give an easy-to-remember URL to a particular resource. For example, if you have a page with a feature that changes monthly, you can set the URL http://myhost.com/features/current to point to the current month's page.
The Servlet Aliasing and Servlet Loading sections of the Jeeves Configuration applet provide control over a server's Java servlets. Servlet Aliasing is the servlet equivalent of the File Aliasing section (just described). The Servlet Aliasing section allows mappings between URLs and servlet names. The Servlet Loading section sets mappings from servlet names to the Java class for the servlet. The location from which the servlet code is loaded (that is, from the local disk or from the network) and any parameters to be passed to the servlet can be defined here as well.
The MIME Section of the Jeeves Configuration applet controls the mappings between file extensions and MIME content types. These mappings let a browser know what type of resource it is retrieving so that it can handle it properly. For example, if you have several Microsoft Word documents, you can use this configuration to tell Jeeves to return a content type of application/msword for all files that have the file extension .doc. Assuming that the client's browser is properly configured, it automatically launches the application to view the file.
The Users, Groups, ACLs, and Resource Protection sections of the Jeeves Configuration applet allow you to control who can access resources on your server. Refer to the following section, "Access Control," for a detailed description of how Jeeves provides resource controls.
Jeeves provides a very flexible system for controlling access to Web pages and servlets. Privileges can be granted to users (referred to as principals in this context), groups of users, or network hosts.
Each user has an account name (which must be unique) and a password. The User Section of the Configuration applet allows you to create and delete user accounts as well as modify a user's password. Likewise, the Groups section allows the creation and deletion of groups of users. Individual user accounts can be assigned or removed from groups.
Access Control Lists (ACLs) are the basis for resource controls with Jeeves. An ACL can be made up of any combination of users, groups, and network hosts. Membership in an ACL can be either positive (the entity is in the list) or negative (the entity is not in the list). In addition to keeping track of which entities are in the ACL, the ACL defines which HTTP request (that is, GET or POST) can be sent. Once you have created an ACL, the Resource Protection section of the Configuration applet enables you to assign lists to a particular URL. Figure 55.2 shows what the ACL Configuration screen looks like.
The Access Control List Configuration section.
The practical upshot of this arrangement is that Jeeves gives you very flexible control over deciding who can get what from your server. For example, you can create an ACL that contains the host names for each department's machines. Engineering documents can be specified as available to the developer ACL and the quality assurance ACL. This information is available only to those two departments--the marketing department's machines are not allowed to retrieve it. If the marketing people want to track who is accessing a particular resource, individual user accounts (or a group account) can be placed in an ACL and that ACL can be assigned to the URL in question.
The API for servlets is similar to that for applets. Servlets must subclass the java.servlet.GenericServlet class and override the service(ServletRequest, ServletResponse) method. Servlets should extend one of the base servlet classes that implement the Servlet interface (such as GenericServlet or HttpServlet). Like the java.applet.Applet class, the GenericServlet class provides a method to retrieve parameters: getInitParameter(). The getServletContext() method obtains a ServletContext object, which provides references to other servlets; the method also provides a way to find out what server the servlet is running on.
If a servlet needs special initialization, an init() method can be provided. This method is called when the servlet class is loaded. A servlet can contain a getServletInfo() method to return information about what the servlet does and its author. Jeeves displays this information in the Configuration applet.
The most important method for a servlet is the service() method. This method is invoked by the server whenever a request is received. Each time a client requests a URL corresponding to a servlet, Jeeves calls the service() method with two parameters: an object implementing the ServletRequest interface and an object implementing the ServletResponse interface.
The ServletRequest interface provides information similar to that passed to a CGI program in other servers. The interface defines methods to retrieve information such as the URL for the request, the remote host and port the request was received from, and any user authentication information.
The ServletResponse interface contains methods that enable a servlet to communicate the results of a request back to the client that asked for it. A getOutputStream() method provides an OutputStream object that writes to the client. Several methods are provided to set HTTP information such as the response code, the MIME Content-Type of the reply, and the status message returned.
The GenericServlet is intended to be used as a base class for servicing any generic request; the java.servlet.http.HttpServlet, on the other hand, is specifically intended for servicing requests received from the HTTP protocol. The service() method for an HttpServlet receives an HttpServletRequest and an HttpServletResponse as parameters. The HTTP-specific versions extend the ServletRequest and ServletResponse interfaces to provide information specific to an HTTP request, such as the URL requested and any HTTP headers.
We finish off this chapter with a sample servlet. This servlet reads a text file containing people's names, titles, and phone numbers and stores this information in a java.util.Hashtable. When a client requests the URL corresponding to the servlet, the servlet returns a form with a text field. The user can enter a name in the field and click the Submit button. The servlet searches its hash table and returns the corresponding phone number if it exists.
First off are the import statements and class definition. Listing 55.1 defines the Hashtable phoneList and initializes it to null.
NOTE: All the code in addition to a sample phone list for the servlet example in this chapter can be found on the CD that accompanies this book.
import java.io.*; import java.util.*; import java.servlet.*; import java.servlet.http.*; public class phoneSearch extends HttpServlet { // Hashtable to hold phone list information. Loaded by init()
Hashtable phoneList = null;
Next we define the init() method. As with an applet, this method is called automatically after the class is loaded. The init() code takes care of opening the phone database file and then calls the readDatabase() method to load the information into the hash table. It uses the Servlet.log() method to provide status updates that are written into the server's event log (see Listing 55.2).
// Initialize servlet. Reads in phone database from file. public void init( ) throws ServletException { FileInputStream infile = null; // For reading data file String phoneFile = null; // Filename of database // Log when we start up. log( "phoneSearch Servlet Started." ); // If a filename is given in our parameters use it if( (phoneFile = getInitParameter( "file" )) == null ) { phoneFile = "phone.txt"; // otherwise default to phone.txt } // Log what phone database file we're using log( "Using phone database file '" + phoneFile + "'." ); // Try and open the phone database file try { infile = new FileInputStream( phoneFile ); } catch( FileNotFoundException e ) { log( "Database file '" + phoneFile + "' does not exist." ); log( "Error was: " + e.getMessage() ); throw new ServletException( e.getMessage() ); // Rethrow exception } catch( IOException e ) { log( "I/O Error opening database file '" + phoneFile + "'." ); log( "Error was: " + e.getMessage() ); throw new ServletException( e.getMessage() ); // Rethrow exception } // Read the database into our hashtable try { readDatabase( new DataInputStream( infile ) ); } catch( java.io.IOException e ) { throw new ServletException( "Error opening database file:" + e.getMessage() ); } // Log that we're ready for business. log( "Read database. phoneSearch ready." );
}
The readDatabase() method takes a DataInputStream from which it reads the phone information (see Listing 55.3). Lines starting with a # character are ignored. Each line should have three fields: the person's name, title, and phone number. The format of each line is the three fields separated by pipe (|) characters. If the line does not contain a | character, we note the line number to the event log and go on to the next line of the file. Correctly formatted lines are split into two parts: the name field, and the title and phone fields. The title and phone string is inserted into the hash table with the name (converted to all lowercase) as the key.
public void readDatabase( DataInputStream in ) throws IOException { int pos, oldpos, lineNumber; String name, info; // Get an empty hashtable phoneList = new Hashtable( ); lineNumber = 0; // Initilaize line numbers try { try { // Read in first line String line = in.readLine( ); // While there are lines to read . . . while( line != null ) { lineNumber++; // Increment line count // Allow comment lines starting with an octothorpe if( line.charAt( 0 ) == '#' ) { line = in.readLine( ); // Read next line && loop continue; } // If line doesn't have a | character log it and go on if( (pos = line.indexOf( '|' )) < 0 ) { log( "Malformed phone database line at line " + lineNumber ); line = in.readLine( ); // Read next line && loop continue; } // Copy name from line name = line.substring( 0, pos ).toLowerCase(); // Leave title and # with | separator as one item info = line.substring( pos + 1, line.length() ); // Place info into hashtable with name as key phoneList.put( name, info ); line = in.readLine( ); // Read next line } } catch( EOFException e ) { ; } } catch( IOException e ) { log( "Error while reading database: " + e.getMessage( ) ); throw e; } log( "phoneSearch read " + lineNumber + " lines." ); log( "phoneSearch hashtable has " + phoneList.size() + " items." ); return; // Done reading database
}
The service() method is the heart of any servlet. Whenever Jeeves receives a request for a URL that maps to a servlet, it calls that servlet's service() method. Two parameters are passed with this call: one representing the request (an object implementing the ServletRequest interface) and one representing the servlet's reply (an object implementing the ServletResponse interface). Because we are extending the HttpServlet class, the parameters are an HttpServletRequest and an HttpServletResponse. The servlet can use information from the HttpServletRequest to determine how it was called and who called it. The HttpServletResponse allows the servlet to set the HTTP headers for its reply, as well as provide an OutputStream on which to write the reply.
public void service( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException { PrintWriter out = new PrintWriter( res.getOutputStream() ); // Set our content type, that the output shouldn't be cached res.setContentType( "text/html" ); res.setHeader( "Pragma", "no-cache" ); // Write out HTML for our search form out.println( "<html>" ); out.println( "<head><title>Phone List Example Servlet</title></head>" ); out.println( "<body bgcolor=\"#ffffff\">" ); // Start of form out.println( "<form method=\"GET\">" ); out.println( "<h1>Phone List Servlet</h1><hr>" ); // Print some instructions for our user out.println( "Enter the name of the person you want to search for." ); out.println( "Names are recorded as all lowercase. Search terms" ); out.println( "are converted to all lowercase." ); // Create a text field for search term out.print( "<p><input TYPE=\"text\" NAME=\"search\" VALUE=\"" ); // See if we were given a query parameter (i.e. someone's called // us already). String search = req.getParameter( "search" ); // If we have search will be non-null and we will use that // as the default value in our input box if( search != null ) out.print( search ); out.println( "\">" ); // Finish tag for search INPUT // Create a submit button out.println( "<input TYPE=\"submit\" NAME=\".submit\"><p><hr>" ); // If we were given a search parameter and its length is non-zero if( search != null && search.length() != 0 ) { // Make the search item all lowercase search = search.toLowerCase(); // See if search term is a key in hashtable String info = (String) phoneList.get( search ); if( info != null ) { // Find separator in info int pos; pos = info.indexOf( '|' ); // Format the data in a spiffy table out.println( "<table width=\"75%\" border=\"2\">" ); out.println( "<tr><th>Name</th><th>Title</th><th>Phone</th></tr>" ); // Print out a row with the data from the query and hashtable out.println( "<tr><td>" + search + "</td>" ); out.println( "<td>" + info.substring( 0, pos ) + "</td>" ); out.println( "<td>" + info.substring( pos + 1, info.length() ) + "</td>" ); out.println( "</table>" ); // Mark the end of our table } else { // Search term wasn't in hashtable, so let them know that out.println( "No one by the name '" + search + "' was found." ); } } // Close out form, body, and html tags out.println( "<p></form>" ); out.println( "</body></html>" ); out.flush(); out.close(); return; // We're done
}
Last are the getServletInfo() and destroy() methods (see Listing 55.5). The getServletInfo() method should return a String with information such as what the servlet does, who the author is, and information about the version. The destroy() method for this servlet just logs to the event log the fact that it was called. If you have a servlet that has, for example, connections to a database or information that has to be written out to disk, the destroy() method can handle those tasks.
// Provide a little information about what servlet does public String getServletInfo( ) { return "Simple Phone Database"; } public void destroy( ) { // Log when we're destroyed log( "phoneSearch Servlet destroy() called." ); }
}
The simplest way to use the servlet created in this chapter is to compile the code (phoneSearch.java, located on the CD-ROM that accompanies this book) and place the class file into the servlet directory under the main Jeeves directory. The phone database file should be placed in the Jeeves root directory and be named phone.txt. If you name the file differently or place it in another directory (in the servlet directory, for example), you must specify the location with the file parameter in the Servlet Loading section of the Configuration applet. Figure 55.3 shows the phoneSearch servlet in action.
The phoneSearch servlet in action.
After reading this chapter, you should have an understanding of Jeeves's capabilities and what sets it apart from other HTTP servers. You should have an idea how to configure Jeeves to map URLs to different files or servlets, as well as how to write your own servlets to provide dynamic content.
©Copyright, Macmillan Computer Publishing. All rights reserved.