Java Web Programming with Eclipse contents
Last modified February 05, 2011 10:57 pm

back next

Item Management (continued)

Video

Edit Page

Overview

In this section, we create functionality needed to edit the attributes of a particular news item. We will follow the same sequence of activities as previously followed to build the other servlets in this chapter, but with the addition of a step to modify the NewsItemDAO. The steps are as follows.

  1. Add an update method to the news item DAO.
  2. Configure a new servlet in the deployment descriptor.
  3. Implement the new servlet.
  4. Implement the jsp.
  5. Test.

Editing a news item is a two step process for the user. First, the user requests the edit form for a selected news item. Second, the user submits the form. The first step of this process requires that an SQL select command be executed because the application needs to populate the edit form with the current state of the selected news item. The second step of this process requires execution of an SQL update command because the application needs to modify the state of the news item as represented in the database. In accordance with our DAO-based approach, we hide these details from the edit news item servlet. The form will be populated with data from an instance of NewsItem that we get from calling the find method of the news item DAO, and the state of the news item in the database will be updated through a call to the update method of the news item DAO.

The two-step process of retrieving the current value of the item and then changing it is referred to as a business transaction. Business transactions encompass one or more database transactions. In our case, our business transaction encompasses two database transactions: a read and a write. One issue with business transactions such as this one is how to handle inconsistent user input. For example, if person A goes to the edit page for a given news item and person B goes to the same page just a moment after A goes there, but before A submits changes, then both A and B see the same version of the news item. However, as soon as one person submits their changes, the other person will be making changes on the basis of stale information. When the second person submits their changes, they will overwrite the changes made by the first person without ever having seen those changes. It is possible that the two people are responsible for different things, and that these resposibilities have them modify two different fields of the news items. In this case, one person loses the results of their work. If these types of problems are a threat to the application, then strategies must be used to eliminate the problems. In this chapter we not consider this problem any further.

The following figure is a sequence diagram that illustrates the sequence of activities resulting from a request for the edit form for a news item, which is the first step in our two-step process.

Sequence diagram for getting a NewsItem edit form

When the user wishes to modify a news item, he or she first views the object through the view-news-item page. In this page, there is an edit link. When the user clicks the edit link, the browser will send an HTTP GET request for the following resource (assuming the selected NewsItem has an id equal to 3):

/edit-news-item?id=3

The doGet method of EditNewsItemServlet extracts the value of the id parameter, which is 3 in our example, and then passes this value to the find method of the news item DAO.

The servlet then stores a reference to the news item instance returned by the find method in the HttpServletRequest object (which acts as a container for request-specific data), and then invokes the forward method on an instance of the RequestDispathcer class that encapsulates the JSP that generates the HTML for the edit form. The edit-news-item JSP uses the properties of the news item instance to populate its form fields, so the user can modify the current state of the selected news item.

The following figure is a sequence diagram that illustrates the sequence of activities that result when the user submits changes to the news item edit form, which is the second step in the two-step business transaction.

Sequence diagram for posting a NewsItem edit form

After the user finishes making edits to the news item, the user clicks the submit button to save the changes. When this happens, the browser constructs an HTTP POST request message with the data from the form and sends it to the servlet container. The servlet container parses the request message into an instance of the HttpServletRequest class and passes this to the doPost method of the singleton instance of the EditNewsItemServlet class. The servlet container also passes an instance of HttpServletResponse, which is used to construct a response to be sent back to the browser.

The doPost method of the EditNewsItemServlet instance checks to see if the data entered by the user is valid (a process called validation), and if valid, invokes the update method of the news item DAO to modify the attributes of the given news item according to the values entered by the user in the HTML form. After updating the database, the servlet redirects the browser to the view-news-item page, which causes the browser to submit a separate HTTP request to the web application to view the result of the edit operation.

Modify News Item DAO

Open the news item DAO class and add the following implementation of the update method.

   public void update(NewsItem newsItem)
   {
      PreparedStatement statement = null;
      Connection connection = null;
      try
      {
         connection = getConnection();
         String sql = "update news_item set " + "title=?, url=? where id=?";
         statement = connection.prepareStatement(sql);
         statement.setString(1, newsItem.getTitle());
         statement.setString(2, newsItem.getUrl());
         statement.setLong(3, newsItem.getId().longValue());
         statement.execute();
      } catch (SQLException e)
      {
         throw new RuntimeException(e);
      } finally
      {
         close(statement, connection);
      }
   }

As you can see, the update method issues an SQL update command to modify the state of a news item in the news item table. Note that it is possible that another user deleted the news item just prior to executing the update method. When this happens, the call to execute will throw an SQLException. We wrap the SQLException in a RuntimeException so that we can throw it to calling code with declaring the method with a throws clause. We can do this because RuntimeException is an unchecked exception. Throwing unchecked exceptions simplifies the code by avoid the need to explicitly throw or catch exceptions. Using unchecked exceptions is the current trend in programming style; all exception in C# for instance are unchecked.

The finally clause ensures that we call the close method regardless of whether an exception occurs. Remeber that closing database connections is essential when using database connection pooling. If we fail to close connection, we will exhaust the connection pool and threads will start blocking forever on calls for new connections.

Modify Deployment Descriptor

On the view news item page, we provide a menu choice to edit the news item being viewed. This link invokes the edit-news-item servlet through the URL edit-news-item?id=n, where n is the id of the news item. Therefore, you should modify web.xml to route requests that match this pattern to an instance of EditNewsItemSerlvet that is named edit-news-item. To do this, add the contents of the following listing to the deployment descriptor.

<servlet>
   <servlet-name>edit-news-item</servlet-name>
   <servlet-class>publisher.web.EditNewsItemServlet</servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>edit-news-item</servlet-name>
   <url-pattern>/edit-news-item</url-pattern>
</servlet-mapping>

Implement EditNewsItemServlet

Create EditNewsItemServlet with the contents of the following listing.

package publisher.web;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import publisher.data.NewsItem;
import publisher.data.NewsItemDAO;

public class EditNewsItemServlet extends HttpServlet
{
   private Logger logger = Logger.getLogger(this.getClass());
   private RequestDispatcher jsp;
   
   public void init(ServletConfig config) throws ServletException {
      ServletContext context = config.getServletContext();
      jsp = context.getRequestDispatcher("/WEB-INF/jsp/edit-news-item.jsp");
   }

   protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
   throws ServletException, IOException {
      logger.debug("doGet()");
      String idString = req.getParameter("id");
      Long id = new Long(idString);
      NewsItem newsItem = new NewsItemDAO().find(id);
      req.setAttribute("newsItem", newsItem);
      jsp.forward(req, resp);
   }

   protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
   throws ServletException, IOException
   {
      String id = req.getParameter("id");
      
      // Check if cancel button was pressed.
      String cancelButton = req.getParameter("cancel-button");
      if (cancelButton != null)
      {
         logger.debug("cancel button pressed");
         resp.sendRedirect("view-news-item?id=" + id);
         return;
      }
      Map<String, String> errors = validate(req);
      if (!errors.isEmpty())
      {
         logger.debug("validation errors");
         jsp.forward(req, resp);
         return;
      }

      NewsItem newsItem = (NewsItem) req.getAttribute("newsItem");
      new NewsItemDAO().update(newsItem);
      resp.sendRedirect("view-news-item?id=" + id);
   }
   
   public static Map<String, String> validate(HttpServletRequest req)
   {
      NewsItem newsItem = new NewsItem();
      HashMap<String, String> errors = new HashMap<String, String>();
      req.setAttribute("errors", errors);
      req.setAttribute("newsItem", newsItem);

      String idString = req.getParameter("id");
      if (idString != null && idString.length() > 0)
      {
         Long id = new Long(idString);
         newsItem.setId(id);
      }
      
      // title
      String title = req.getParameter("title");
      if (title == null || title.trim().length() == 0)
      {
         errors.put("title", "Title required.");
      }
      newsItem.setTitle(title);

      // url
      String url = req.getParameter("url");
      if (url == null || url.trim().length() == 0)
      {
         errors.put("url", "URL required.");
      }
      newsItem.setUrl(url);
      
      return errors;
   }
}

There are two ways the doPost methods will be called: the user clicks cancel or the user clicks submit. Accordingly, the doPost method first checks to see if the user clicked on cancel. If the user clicked cancel, then the doPost method will redirect the browser back to the view-news-item page. If the user clicked submit, then doPost will check to see that the data entered by the user is valid, which it does by calling the validate method.

The validate method is implemented as a static method because it will also be called from the doPost method of the servlet that will be used to handle requests to create news items. By placing the code in its own method that can be externally invoked, we keep the validation code in a single location and avoid creating duplicate code.

The validate method returns a map that contains an entry for each validation error that is detected. Therefore, the doPost method checks to see if any validation errors occurred by checking to see if this map is empty. If the map is not empty, then it forwards processing of the request to the jsp with the edit form, which will display the data entered by the user in the form along with the error messages. If there are no entries in the errors map, then doPost will consider the data submission to be valid and will then proceed to store the new values in the database.

The validate method extracts the title and url attributes from the request parameters. It checks to see if the values submitted by the user are valid. If a submitted value is invalid, the validate method adds an entry in a map called errors. The errors map associates a parameter in the form with a validation error message, and it is returned by the validate method. In addition to checking for validation errors, the validate method has a side effect: it stores the values of the parameters as attributes in the request object. The reason for doing this is that if there are validation errors, then the calling code will forward the request to the jsp with the form; the jsp needs access to the values the user submitted in order to echo them back to the user.

Create JSP

Create edit-news-item.jsp with the contents of the following listing.

<jsp:useBean id="errors" scope="request" type="java.util.Map" class="java.util.HashMap" />

<%@ include file="top.inc" %>
<%@ include file="middle.inc" %>

<form method="post">
   <table>
      <tr>
         <td>Title</td>
         <td><input type="text" name="title" value="${newsItem.title}" size="50" />
            <%
               if (errors.containsKey("title")) {
                  out.println("<span class=\"error\">" + errors.get("title") + "</span>");
               }
            %>
         </td>
      </tr>
      <tr>
         <td>URL</td>
         <td><input type="text" name="url" value="${newsItem.url}" size="50" />
            <%
               if (errors.containsKey("url")) {
                  out.println("<span class=\"error\">" + errors.get("url") + "</span>");
               }
            %>
         </td>
      </tr>
      <tr>
         <td>
             <input type="submit" name="submit-button" value="Submit" />
             <input type="submit" name="cancel-button" value="Cancel" />
         </td>
      </tr>
   </table>
   <input type="hidden" name="id" value="${newsItem.id}" />

</form>

<%@ include file="bottom.inc" %>

Notice how field-specific error messages are displayed next to their fields.

Test

Stop and start the publisher application with the Tomcat manager application, so that Tomcat reloads the web.xml file and the new class files. Verify that the new functionality works correctly by going to http://localhost:8080/publisher/home and editing an existing news item. You should try entering in an empty title and an empty url to see how validation works. The following figure shows approximately how the edit news item page should render.

Edit news item page

back next

Copyright 2007-2009 David Turner and Jinseok Chae. All rights reserved.