Professional Java Servlets 2.3
| < Free Open Study > |
|
In this section we'll build an application that demonstrates servlets and JSP pages working in tandem. The application is a simple database application that maintains the items stored in an inventory database. The functionality provided by the application to the end user is illustrated in the following use case diagram:
Creating the Database
The database uses a single item table. The database I used for this example was MySQL. However, the example can be easily altered to use any JDBC-compliant database. Create a database called inventory and a table called item (along with some sample data) using the following SQL script:
CREATE DATABASE inventory; USE inventory; CREATE TABLE item ( id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL, description VARCHAR(30) NOT NULL, price DOUBLE NOT NULL, stock INTEGER NOT NULL); INSERT INTO item (name, description, price, stock) VALUES ('Straw Hat', 'The best in town', 78.99, 9012); INSERT INTO item (name, description, price, stock) VALUES ('Polo Shirt', 'The latest fashion', 49.99, 99);
System Architecture
For the purposes of this example, we won't be delving into the details of J2EE web application architecture and design patterns; such discussions are reserved for Chapter 12. It's sufficient to say that we will be using servlets as request processing components and JSP pages as presentation components (this is a flavor of the very popular Model II architecture).
All the use cases that involve data access will be sent to specific servlets. These servlets will look up a data source, the database connections will then be used to perform the various database operations and then the request will be forwarded to the JSP page that renders the next view using the request dispatcher. If the JSP page requires data from the database, the servlet will store the data as a request attribute that can be retrieved by the JSP using the useBean action. All this is illustrated in the following diagram:
Choosing a Data Transfer Object
Traditionally, custom Java classes that comply with the JavaBeans design pattern have been used to transfer data from the request processing tier to the presentation tier. These objects are populated by request delegate classes from SQL result sets and are stored as request attributes. These components are later extracted and rendered by the view JSP pages. In the View Dispatcher pattern the actual data retrieval is performed at the time of processing the view JSP pages.
However, in this case, the tabular data stream present in the SQL result set is transformed into a hierarchy of objects. The rendering JSP pages will then use custom tags or scriptlets to transform the data into an HTML table. This means that we are performing redundant data transformation and, depending on the size of the system, we may end up with a plethora of custom JavaBean components.
Ideally the JSP pages should loop through the result set and render the data. However, this is not good practice because iterating through the result set requires the underlying database connection to be open as we would be accessing the database resources directly. This has got lot of implications for performance, scalability, error handling, extensibility, and resource cleanup. The javax.sql.RowSet interface from the JDBC optional extensions come to our rescue. This interface adds support to the JDBC API for the JavaBeans component model.
Sun provides an implementation of this interface called CachedRowSet that can function in a disconnected mode from the connection that is used to populate it. The interface provides methods for setting the JNDI name of the data source used to acquire connections, and for setting the SQL command string. When the execute() method on the interface is called, it uses the specified connection and SQL command to populate the internal data structure with the resulting data. The CachedRowSet implementation closes the connection after retrieving the data. This can be then used in the JSP pages in the same way as the result set for iterating through and retrieving the data.
Accordingly, we will use CachedRowSet in our example.
Listing Items
The following occurs when we want to list the items:
-
The browser sends the request to an instance of ListServlet
-
The servlet executes the SQL and populates the resulting row set as a request attribute
-
The request is forwarded to List.jsp
-
The JSP iterates through the row set and renders the data
The following servlet processes the request to list the items:
package jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.sql.*; import sun.jdbc.rowset.CachedRowSet; public class ListServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException { doGet(req, res); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { try {
Create a new cached row set:
RowSet rs = new CachedRowSet();
Set the JNDI name of the data source. Note that you need to configure this in your web container. Accomplishing this using Tomcat is explained in the section on deploying the application:
rs.setDataSourceName("java:comp/env/jdbc/inventoryDB");
Set the SQL command for getting the list of items:
rs.setCommand("SELECT * FROM item");
Execute the SQL, which will close the underlying connection:
rs.execute();
Store the row set as a request attribute:
req.setAttribute("rs", rs);
Forward the request to List.jsp:
getServletContext().getRequestDispatcher("/List.jsp"). forward(req, res); } catch(Exception ex) { throw new ServletException(ex); } } }
List.jsp renders the list of items as an HTML table and provides links for editing and deleting individual items and adding new items:
<%@page contentType="text/html"%>
Now we define the row set as a bean:
<jsp:useBean scope="request" type="javax.sql.RowSet" /> <html> <head> <title>Inventory - List</title> </head> <body > <table cellpadding="5" > <tr> <th>Name</th> <th>Description</th> <th>Price</th> <th>Stock</th> <th></th> <th></th> </tr>
Next, we must iterate through the row set (we could write a custom tag to avoid the use of scriptlets):
<% while(rs.next()) { %>
Render the various item attributes:
<tr> <td><%= rs.getString(2) %></td> <td><%= rs.getString(3) %></td> <td><%= rs.getString(4) %></td> <td><%= rs.getString(5) %></td> <td>
Render the link to delete an item:
<a href="Delete?id=<%= rs.getString(1) %>"> Delete </a> </td> <td>
Render the link to edit an item:
<a href="Edit?id=<%= rs.getString(1) %>"> Edit </a> </td> </tr> <% } %> </table>
Render the link to add a new item:
<a href="New.html">New Item</a> </body> </html>
Deleting Items
The following occurs when we delete an item:
-
The browser sends the request to an instance of DeleteServlet
-
The servlet looks up the data source and gets the connection
-
The servlet then executes the SQL to delete the selected item
-
The request is then forwarded to the URI that is mapped to ListServlet to display a new list of items
The following servlet processes the deletion:
package jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.sql.DataSource; import javax.naming.InitialContext; import java.sql.*; public class DeleteServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException { doGet(req, res); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { Connection con = null; try {
Create the JNDI initial context:
InitialContext ctx = new InitialContext();
Look up the data source:
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/inventory");
Get a connection:
con = ds.getConnection();
Create the prepared statement to issue the delete SQL statement:
PreparedStatement stmt = con.prepareStatement("DELETE FROM item WHERE id = ?");
Set the selected item id as an SQL input parameter:
stmt.setInt(1, Integer.parseInt(req.getParameter("id")));
Execute the SQL:
stmt.executeUpdate(); stmt.close();
Forward the request to the URI that is mapped to ListServlet to display a new list of items:
getServletContext().getRequestDispatcher("/List"). forward(req, res); } catch(Exception ex) { throw new ServletException(ex); } finally { try { if(con != null) con.close(); } catch(Exception ex) { throw new ServletException(ex); } } } }
Editing Items
An item can be edited by clicking on the edit link against the item. The following occurs when an item is edited:
-
The browser sends the request an instance of EditServlet
-
The servlet executes the SQL to get the details of the selected item and populates the resulting row set as a request attribute
-
The request is then forwarded to Edit.jsp
-
The JSP page extracts the data and renders it as an HTML form
The following servlet processes the edit:
package jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.sql.*; import sun.jdbc.rowset.CachedRowSet; public class EditServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException { doGet(req, res); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { try {
Create a cached row set:
RowSet rs = new CachedRowSet();
Set the data source:
rs.setDataSourceName("java:comp/env/jdbc/inventory");
Set the SQL command:
rs.setCommand("SELECT * FROM item WHERE id = ?");
Set the selected item id as the SQL input parameter:
rs.setInt(1, Integer.parseInt(req.getParameter("id")));
Populate the row set and store it as a request attribute:
rs.execute(); req.setAttribute("rs", rs);
Forward the request to Edit.jsp:
getServletContext().getRequestDispatcher("/Edit.jsp"). forward(req, res); } catch(Exception ex) { throw new ServletException(ex); } } }
Edit.jsp is used to edit an item:
<%@page contentType="text/html"%>
Define the row set as a bean:
<jsp:useBean scope="request" type="javax.sql.RowSet" /> <html> <head> <title>Inventory - Edit</title> </head> <body >
Move the cursor to the first record in the row set:
<% if(rs.next()) { %> <form action="Update">
Render the ID as a hidden parameter:
<input name="id" type="hidden" value="<%= rs.getString(1) %>"/> <table cellpadding="5" > <tr> <td><b>Name:</b></td> <td>
Render the name:
<input name="name" type="text" value="<%= rs.getString(2) %>"/> </td> </tr> <tr> <td><b>Description:</b></td> <td>
Render the description:
<input name="description" type="text" value="<%= rs.getString(3) %>"/> </td> </tr> <tr> <td><b>Price:</b></td> <td>
Render the price:
<input name="price" type="text" value="<%= rs.getString(4) %>"/> </td> </tr> <tr> <td><b>Stock:</b></td> <td>
Render the number of items in the stock:
<input name="stock" type="text" value="<%= rs.getString(5) %>"/> </td> </tr> <tr> <td></td> <td> <input type="submit" value="Update"/> </td> </tr> </table> <% } %> </body> </html>
Updating Items
The following occurs when we update an item:
-
The browser sends the request an instance of UpdateServlet
-
The servlets looks up the data source and gets the connection
-
The servlet executes the SQL to update the selected item. (The modified details of the items are extracted from the request parameter list.)
-
The request is then forwarded to the URI that is mapped to ListServlet to display a new list of items.
The following servlet is used to update items:
package jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.sql.DataSource; import javax.naming.InitialContext; import java.sql.*; public class UpdateServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException { doGet(req, res); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { Connection con = null; try {
Look up the data source and get the connection:
InitialContext ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/inventoryDB"); con = ds.getConnection();
Create the prepared statement for updating the item:
PreparedStatement stmt = con.prepareStatement("UPDATE item " + "SET name = ?, " + "description = ?, " + "price = ?, " + "stock = ? " + "WHERE id = ?");
Set the modified item details as SQL in parameters:
stmt.setString(1, req.getParameter("name")); stmt.setString(2, req.getParameter("description")); stmt.setDouble(3, Double.parseDouble(req.getParameter("price"))); stmt.setInt(4, Integer.parseInt(req.getParameter("stock"))); stmt.setInt(5, Integer.parseInt(req.getParameter("id")));
Issue the update:
stmt.executeUpdate(); stmt.close();
Forward the request to the URI that is mapped to ListServlet to display a new list of items:
getServletContext().getRequestDispatcher("/List"). forward(req, res); } catch(Exception ex) { throw new ServletException(ex); } finally { try { if(con != null) { con.close(); } } catch(Exception ex) { throw new ServletException(ex); } } } }
Adding New Items
Adding new items involves two separate tasks:
-
Clicking on the link to add a new item in the screen that displays the list of items. This will send the request to the HTML page New.html, which will display a form for adding the new item.
-
Updating this form will create a new item record in the database with the details entered by the user.
We use the following page to enter the information for a new item:
<html> <head> <title>Inventory - Add New Item</title> </head> <body > <form action="Create"> <table cellpadding="5" > <tr> <td><b>Name:</b></td>
Input field for entering the name:
<td><input name="name" type="text"/></td> </tr> <tr> <td><b>Description:</b></td>
Input field for entering the description:
<td><input name="description" type="text"/></td> </tr> <tr> <td><b>Price:</b></td>
Input field for entering the price:
<td><input name="price" type="text"/></td> </tr> <tr> <td><b>Stock:</b></td>
Input field for entering the stock:
<td><input name="stock" type="text"/></td> </tr> <tr> <td></td> <td><input type="submit" value="Create"/></td> </tr> </table> </body> </html>
The sequence of events in submitting the above form is:
-
The browser sends the request an instance of CreateServlet.
-
The servlets looks up the data source and gets the connection.
-
Then it executes the SQL to create the new item. (The details of the new item are extracted from the request parameter list.)
-
The request is then forwarded to the URI that is mapped to ListServlet to display a new list of items.
The following servlet is used to create a new item:
package jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.sql.DataSource; import javax.naming.InitialContext; import java.sql.*; public class CreateServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException { doGet(req, res); } public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException { Connection con = null; try {
Look up the data source and get the connection:
InitialContext ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/inventoryDB"); con = ds.getConnection();
Create the prepared statement by specifying the insert SQL statement:
PreparedStatement stmt = con.prepareStatement( "INSERT INTO item " + "(name, description, price," + " stock) VALUES (?, ?, ?, ?)");
Set the details of the new items as SQL in parameters:
stmt.setString(1, req.getParameter("name")); stmt.setString(2, req.getParameter("description")); stmt.setDouble(3, Double.parseDouble(req.getParameter("price"))); stmt.setInt(4, Integer.parseInt(req.getParameter("stock")));
Execute the insert SQL:
stmt.executeUpdate(); stmt.close();
Forward the request to the URI that is mapped to ListServlet for displaying new list of items:
getServletContext().getRequestDispatcher("/List"). forward(req, res); } catch(Exception ex) { throw new ServletException(ex); } finally { try { if(con != null) con.close(); } catch(Exception ex) { throw new ServletException(ex); } } } }
Deploying and Running the Application
This is the deployment descriptor for the application. It defines the servlets and URI mappings:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>List</servlet-name> <servlet-class>jsp.ListServlet</servlet-class> </servlet> <servlet> <servlet-name>Edit</servlet-name> <servlet-class>jsp.EditServlet</servlet-class> </servlet> <servlet> <servlet-name>Delete</servlet-name> <servlet-class>jsp.DeleteServlet</servlet-class> </servlet> <servlet> <servlet-name>Update</servlet-name> <servlet-class>jsp.UpdateServlet</servlet-class> </servlet> <servlet> <servlet-name>Create</servlet-name> <servlet-class>jsp.CreateServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>List</servlet-name> <url-pattern>/List</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Edit</servlet-name> <url-pattern>/Edit</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Delete</servlet-name> <url-pattern>/Delete</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Update</servlet-name> <url-pattern>/Update</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Create</servlet-name> <url-pattern>/Create</url-pattern> </servlet-mapping> </web-app>
To deploy and run the application, perform the following steps:
-
Start your database (I used MySQL).
-
Create the inventory database using the supplied SQL script.
-
Download the row set implementation from http://developer.java.sun.com/developer/earlyAccess/crs/.
-
Compile the servlet classes. Remember to include the JDBC extension API (jdbc2_0-ext.jar) and the row set implementation (rowset.jar) in your classpath.
-
Create the following web application
webapps/ inventory/ Edit.jsp List.jsp New.html WEB-INF/ web.xml classes/ jsp/ ListServlet.class EditServlet.class DeleteServlet.class CreateServlet.class UpdateServlet.class /lib/ rowset.jar
-
Make sure that you have the MM.MySQL driver in your classpath.
-
Add the following context to the Tomcat configuration file:
<Context path="/inventory" docBase="inventory"> <Resource name="jdbc/inventoryDB" auth="Container" type="javax.sql.DataSource"/> <ResourceParams name="jdbc/inventoryDB"> <parameter> <name>driverClassName</name> <value>org.gjt.mm.mysql.Driver</value> </parameter> <parameter> <name>driverName</name> <value>jdbc:mysql://localhost:3306/inventory</value> </parameter> </ResourceParams> </Context>
Now we can start Tomcat and run the application. Navigate to http://localhost:8080/inventory/List:
Then if we click on New Item we can add an item to the inventory:
Then we can delete our supply of polo shirts by clicking the Delete link next to the item:
| < Free Open Study > |
|