9.5 A Guestbook Servlet

Java Servlet Programming, 2nd Edition > 9. Database Connectivity > 9.5 A Guestbook Servlet

 
< BACKCONTINUE >

9.5 A Guestbook Servlet

To help the database concepts sink in, let's look at a real servlet. Example 9-11 shows the code for a typical database-backed "guestbook" servlet. This servlet manages a web page where visitors can enter comments and see what comments others have written. A screen shot is provided in Figure 9-4. All the visitor comments are held inside a database. In accessing the database, this servlet uses several of the techniques covered in this chapter including using a connection pool, using a prepared statement, and using ContextProperties to read database configuration information from context init parameters. The servlet even extends com.oreilly.servlet.CacheHttpServlet from Chapter 4 to optimize its output performance.

Figure 9-4. Guestbook output
Example 9-11. Please Sign In
import java.io.*; import java.sql.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import com.oreilly.servlet.CacheHttpServlet; public class Guestbook extends CacheHttpServlet {   static final String SELECT_ALL =     "SELECT name, email, cmt, id FROM guestlist ORDER BY id DESC";   static final String INSERT =      "INSERT INTO guestlist (id, name, email, cmt) " +      "VALUES (?, ?, ?, ?)";   private long lastModified = 0;  // Time database last changed   private ConnectionPool pool;      // Get a pointer to a connection pool   public void init() throws ServletException {     try {       ServletContext context = getServletContext();       synchronized (context) {         // A pool may already be saved as a context attribute         pool = (ConnectionPool) context.getAttribute("pool");         if (pool == null) {           // Construct a pool using our context init parameters           // connection.driver, connection.url, user, password, etc           pool = new ConnectionPool(new ContextProperties(context), 3);           context.setAttribute("pool", pool);         }       }     }     catch (Exception e) {       throw new UnavailableException(       "Failed to fetch a connection pool from the context: " + e.getMessage());     }   }   // Display the current entries, then ask for a new entry   public void doGet(HttpServletRequest req, HttpServletResponse res)                                 throws ServletException, IOException {     res.setContentType("text/html");     PrintWriter out = res.getWriter();     printHeader(out);     printForm(out);     printMessages(out);     printFooter(out);   }   // Add a new entry, then dispatch back to doGet()   public void doPost(HttpServletRequest req, HttpServletResponse res)                                  throws ServletException, IOException {     handleForm(req, res);     doGet(req, res);   }   private void printHeader(PrintWriter out) {     out.println("<HTML><HEAD><TITLE>Guestbook</TITLE></HEAD>");     out.println("<BODY>");   }   private void printForm(PrintWriter out) {     out.println("<FORM METHOD=POST>");  // posts to itself     out.println("<B>Please submit your feedback:</B><BR>");     out.println("Your name: <INPUT TYPE=TEXT NAME=name><BR>");     out.println("Your email: <INPUT TYPE=TEXT NAME=email><BR>");     out.println("Comment: <INPUT TYPE=TEXT SIZE=50 NAME=comment><BR>");     out.println("<INPUT TYPE=SUBMIT VALUE=\"Send Feedback\"><BR>");     out.println("</FORM>");     out.println("<HR>");   }   // Read the messages from the database, and print   private void printMessages(PrintWriter out) throws ServletException {     String name, email, comment;     Connection con = null;     Statement stmt = null;     ResultSet rs = null;     try {       con = pool.getConnection();       stmt = con.createStatement();       rs = stmt.executeQuery(SELECT_ALL);       while (rs.next()) {         name = rs.getString(1);         if (rs.wasNull() || name.length() == 0) name = "Unknown user";         email = rs.getString(2);         if (rs.wasNull() || email.length() == 0) name = "Unknown email";         comment = rs.getString(3);         if (rs.wasNull() || comment.length() == 0) name = "No comment";         out.println("<DL>");         out.println("<DT><B>" + name + "</B> (" + email + ") says");         out.println("<DD><PRE>" + comment + "</PRE>");         out.println("</DL>");       }     }     catch (SQLException e) {       throw new ServletException(e);     }     finally {       try {         if (stmt != null) stmt.close();       }       catch (SQLException ignored) { }       pool.returnConnection(con);     }   }   private void printFooter(PrintWriter out) {     out.println("</BODY>");   }   // Save the new comment to the database   private void handleForm(HttpServletRequest req,                           HttpServletResponse res) throws ServletException {     String name = req.getParameter("name");     String email = req.getParameter("email");     String comment = req.getParameter("comment");     Connection con = null;     PreparedStatement pstmt = null;     try {       con = pool.getConnection();       // Use a prepared statement for automatic string escaping       pstmt = con.prepareStatement(INSERT);       long time = System.currentTimeMillis();       pstmt.setString(1, Long.toString(time));       pstmt.setString(2, name);       pstmt.setString(3, email);       pstmt.setString(4, comment);       pstmt.executeUpdate();     }     catch (SQLException e) {       throw new ServletException(e);     }     finally {       try {         if (pstmt != null) pstmt.close();       }       catch (SQLException ignored) { }       pool.returnConnection(con);     }     // Make note we have a new last modified time     lastModified = System.currentTimeMillis();   }   public long getLastModified(HttpServletRequest req) {     return lastModified;  // supports CacheHttpServlet   } }

The SQL statements for accessing the database are given at the top of the class in static final variables. Separating the SQL makes future modifications much easier.

The init( ) method gets (or creates) a connection pool for this servlet to use. It first looks for a preexisting pool saved in as a ServletContext attribute under the name pool. If that attribute does not exist, the init( ) method constructs a new pool using the ContextProperties class to specify database configuration information and saves that pool to the context. To be honest, a connection pool isn't really needed for this servlet because there are no transactions and the bandwidth through a single reused Connection wouldn't be too great. In cases like this it makes sense to still use a connection pool but one that has the ability to return a shared Connection object for simultaneous use by multiple servlets.

The doGet( ) method prints the header, a form asking the user to comment, the previous comments retrieved from the database, and a footer. Techniques for more elegant HTML creation in cases like this are shown beginning with Chapter 14. For now this gets the job done.

The printMessages( ) method uses the pool to get a Connection, then executes the SELECT_ALL query using a Statement retrieved from that Connection. For each row in the ResultSet it prints a <DL> entry. In the finally block, the Connection goes back into the pool.

The doPost( ) method is called when a user submits comments using the form generated by doGet( ). This method calls handleForm( ) to store the comment inside the database, then dispatches to doGet( ) to render the page. The handleForm( ) method retrieves the parameter's name, email, and comment and uses a PreparedStatement and the INSERT statement to save the parameter information in the database. By using a PreparedStatement to save the strings we automatically escape any special characters. A timestamp for the comment is also saved to the database, to provide a way to sort the comments and for possible later display.

At the bottom of the handleForm( ) method it sets lastModified to the current time. This allows the getLastModified( ) method to return the time the database was last updated. Because this servlet extends CacheHttpServlet, the last modified information will be used by the superclass to manage a cache of its output, changing the cache only after database updates.[1]

[1] Remember, though, there's no caching performed for POST requests, since they are not idempotent. This has an interesting side effect for the Guestbook example if you do a page reload after submitting a new comment, you'll see the page generation will take some time even though it's the same page output you already viewed. This is because the submit was a POST request whose output was not cached.


Last updated on 3/20/2003
Java Servlet Programming, 2nd Edition, © 2001 O'Reilly

< BACKCONTINUE >


Java servlet programming
Java Servlet Programming (Java Series)
ISBN: 0596000405
EAN: 2147483647
Year: 2000
Pages: 223

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net