Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Web Applications as Java Servlets


May01: Web Applications as Java Servlets

Brad is the originator of the Objective-C programming language and author of Superdistribution: Objects as Property on the Electronic Frontier (Addison-Wesley, 1996). He can be contacted at [email protected].


The conventional approach to building web-based applications involves writing the user interface components of the application in HTML-based languages such as Sun's Java Server Pages (JSP) or Microsoft's Active Server Pages (ASP). In this article, I'll describe a different approach — one that exploits Java's type-checking ability to validate field parameters and detect invalid links between pages. This approach is the outcome of a decade of experience building large interactive web-based applications, including:

  • Virtual School, a distributed learning project that won the $25,000 Paul Allen competition in 1997 for the best distance-learning system nationwide. The site started as Perl CGI programs with a handcoded database and evolved into a Mod-Perl/MySQL-based version.
  • Superdistributed.com, my company's web site. It was originally developed in Apache/JServe with GNU JSP, then ported to Apache/Tomcat/JSP. Then I converted the entire site to the architecture to be described shortly.

Application Architecture

The state machine in Figure 1 illustrates the sample application I'll be using as an example. The states are shown as ovals and the transitions between states as arrows. Briefly, the Home oval is the entry (home) page that can be accessed without logging in. From the homepage, users can either leave via the arrow at the top right or click a link to that user's Account page. The Account page automatically redirects visitors who haven't logged in to the Login page, which provides a link to the Register page for visitors who do not have an account. In a real system, the Account page would provide links for accessing the services of that user's account. The example only provides a single service — an Address page through which visitors can edit the account's address and phone number.

Each state abides by the Model/View/Controller (MVC) framework; see Figure 2. This framework was originated by Trygve Reenskaug for Smalltalk and has since been adapted to other languages and environments.

  • The model is the object to be made accessible to users. The lens in the center supports the interaction by providing two parts — the view and controller.
  • The controller controls the interaction by validating information arriving from users along the upper arrow and updates the model accordingly.

  • The view receives updated information from the model and notifies users by sending HTML text to the browser.

In this architecture, each state is a Java class (a subclass of State) and its controller is a public method named controller(). How the view is implemented is not specified by the architecture because the view is private to each state. The style I present here implements views as private methods within each state class, but other approaches are common, such as reading HTML text from files.

An application consists of a foundation package (com.sdi.wap.demo.bean) that contains the application models as JavaBeans, and a superstructure (com.sdi.wap.demo.site) that contains the application's State classes. The superstructure package also provides a subclass of Site that defines the site as a whole. The site class provides a servlet that manages the servlet protocol and calls the States' controller() methods as required. That's it. Everything is Java. HTML-based languages like JSP are not used.

Figure 3 shows the architecture in more detail. An interaction begins with the application sending HTML text to the user's browser along the response arrow. Typically the text includes HTML forms commands to invite users to revise information that the view retrieved from the model via the retrieve arrows and embedded into the value="..." fields of the HTML form. Users click a Submit button to return revised information along the request arrow as HTML request parameters, including a special parameter that tells the PageServlet the state class that should handle this request. The state's controller method validates the received information and responds accordingly, either returning incorrect entries to users for correction or sending correct entries to the model. The model applies the highest level of validation (business rules) and updates the DBMS as appropriate.

In practice, each state is a subclass of the abstract class State. This class provides instance variables for maintaining execution-time variables (in particular, the HttpServletRequest and HttpServlet response objects provided by the servlet engine). The abstract class provides a number of methods that subclasses use to access session attributes, obtain request parameters, and so forth.

Validation Architecture

Since the servlet request.getParameter() method returns form field values as Java Strings and because they usually end up stored as Strings in the DBMS, it is tempting to manage field values as Strings throughout the application.

Don't do this. If you consider only the endpoint requirements, you ignore everything that goes between. Those field variables will be passed to internal methods and stored in internal variables. If they are all declared as String, Java's type checking will be unable to prevent common programming errors like passing a zip code to a method that expects a street name. Validation is tedious because it must be duplicated in each state that handles that kind of field. If a validation architecture is not adopted for the lowest-level types (Street and Zipcode, for example), no basis exists for validating higher level types (such as AddressBean or AccountBean). Finally, the servlet engine's methods for retrieving Strings from request parameters can return null under some conditions. If you let these escape into the application, you have multiplied the number of cases your controllers will have to handle.

The solution is straightforward — just examine each form in your application while asking "What types are really being handled here?" The answer will never be "String," but an application-specific datatype such as (in this case) Street, City, USState, Zipcode, and Phone. Make this explicit by providing a Java class for each one and encapsulate the syntactic validation rules within that class. At the very least, every class should prevent inputs that are too long to fit without truncation into the database field size. There are often several ways for inputs to fail, so the validation logic should provide a way of explaining why the validation failed in terms users can understand. For example, a phone number might fail because the field is empty or null, too long for the database field, not parsable as a phone number, and so forth.

A validation architecture is a set of interfaces and abstract classes that provide a consistent foundation for building the application-specific classes. The validation architecture I use here is defined by the following API, as defined in the Validatable interface:

  • boolean isValid(). Does the value of this instance meet the syntactic validity requirements of this type? Only syntactic validity (whether this value can be parsed) is checked here. Checking semantic validity (whether this value is allowable in this context) will be handled elsewhere.
  • String getMessage(). If isValid() is True, this message returns an empty string (""). If False, it returns a String that the application can display to tell users what's wrong. For example, a required field might be empty, too long, too short, or it might contain text that cannot be parsed.

  • String getValue(). Returns the value of the field as a String.

The com.sdi.wap.demo.field package (available electronically; see "Resource Center," page 5 and also at http://www.viritualschool.edu/wap/) defines several low-level field types, each implemented as a subclass of Field: Op, Identifier, Email, Street, City, USState, Zipcode, and Phone. The Op field, by convention, holds the value of the Submit button (which is, by convention, named "op"). Identifier is used for values that will serve as public keys in the database.

The abstract Field class provides supporting methods that help to make these subclasses remarkably simple. For example, Listing One (the full source of the Zipcode class) shows that these classes are often just a few lines of code. The version shown here detects valid zipcodes by passing a regular expression to the requireMatch() method, which Zipcode inherits from Field.

The architecture also provides a simple technique that helps reduce the dreaded NullPointerException from your application. The Zipcode class defines a public final static instance named Null that is initialized to an empty (and therefore invalid) instance of that class. Aggregate types such as AddressBean use these to substitute for Java's null when initializing variables of each type; see Listing Two.

Controllers

The controller() method for the LoginView state (Listing Three) supports the login procedure. The try block at the beginning of this method bypasses the login procedure if users are already logged in. The first statement inside the try block asks the site object to retrieve the AccountBean of the currently logged in user. If this succeeds, LoginView simply forwards the request to the AccountView page. Otherwise, here the catch block is taken, which falls through to the body of the method.

The first step of all controllers is to retrieve information from the request parameters into the application-specific field types discussed earlier. The getField() method provides a simple way of doing this. The method's first argument is the name of the request parameter and the second is a default value to be used if the request parameter is empty ("") or null. When this controller is called the first time, no request parameters will exist and all fields will take the default values in the second argument, so the else clause at the very bottom will be taken to display the login page.

If a visitor types a valid e-mail address into the account identifier field and clicks the Submit button, the form returns control to LoginView.controller(). This time, the Op field contains the value that the form assigned to the Submit button (Op) and the identifier contains the value of the input field named "identifier." If both are valid, the second if statement succeeds and the login procedure in the main clause is attempted.

The login procedure first obtains a connection from the ConnectionPool and tells AccountBean to attempt to load the instance from the database record that has identifier as its primary key. This attempt fails and an exception is thrown if the identifier is semantically invalid; for example, an otherwise valid e-mail address was provided, but it is not the identifier of a registered account. If so, the code marks the identifier as semantically invalid by calling setValid() with a description of the problem that the view displays to explain the problem.

As recommended in most JDBC books, connection pooling is used to avoid the overhead of creating a new connection for each DBMS access. LoginView retrieves a connection from the pool. The AccountBean.load() method saves this connection in the loaded instance and provides a finalize() method that returns the connection to the pool when the AccountBean is no longer referenced, which will happen when users log off or the session expires.

If the load succeeds, the connection is saved in a transient AccountBean instance variable so that this connection is available for all database accesses during this session. The site object (Site) stores the AccountBean in the servlet session object. It persists there as the account of the currently logged-in user until the user logs out or the session expires. The redirect() call reroutes the request to the AccountView page, which gives users access to the account's services.

The pattern for the LoginBean controller is followed by all controllers. They all begin by calling getField() to construct field variables from the request parameters, providing suitable default parameters in the second argument for when the request parameters do not exist. The LoginView is unique only in that all of its default parameters are Null. Typically, controllers use getField's second argument to provide initial values for all of their fields by reading existing values from the database. This page is called by clicking a hot link that AccountView provides as the sole account service in this small demonstration. The first line retrieves the AccountBean for the currently logged-in user by requesting the site object to retrieve it. If the session has expired, this call throws a SessionExpiredException. This is caught by the site's servlet that redirects the request to LoginView so that users can log in again.

The AccountBean loads its AddressBean from the database and uses it to obtain default values for each field variable. The Op field's default value is always Op.Null to ensure that the else branch will be taken when the controller is first called.

The simplicity of the controller methods is due to two things:

  • The logic for presenting the session state to users is encapsulated within view methods and doesn't clutter the controller's code.
  • The logic for syntactically validating user input is encapsulated by the Validatable interface. Low-level Java types like String or null never appear in higher levels of the application.

Site Architecture

In conventional HTML, web site pages reference one another via URLs that are hard-coded into <a href="..."> or <form action="...">. If a target page is moved or renamed, the error isn't noticed until the invalid link is clicked. The site architecture originated in the desire to use Java type checking to report invalid links at compile time.

My solution was to define a container object to represent the site as a whole, and to have this object define protected final static variables for each web page within that site. The com.sdi.wap package supports this via the Site interface. The package also provides an abstract implementation of this interface, SiteImpl, which each site subclasses to define that site. For example, DemoSite defines the demonstration application described here.

DemoSite defines a protected final static variables for each page, as in Listing Five. The initializers call the static methods, newStaticPage() and newDynamicPage(), according to whether the page is plain HTML text (to be served by the web server) or dynamic servlet-based code (served by the servlet engine). These two methods pass their arguments to the appropriate StaticPage or DynamicPage constructors, storing the resulting instances in private static final Hashtables, staticPages, and dynamicPages. These classes extend the abstract Page class, which defines storage for the information provided in the constructors and provides default implementations for methods inherited by the two Page subclasses.

When the servlet's init() method is called (when the servlet engine is starting up), the instances in the two hashtables validate the information provided to their constructor. In particular, StaticPages ensure that the file specified in their URL constructor parameter actually exists in the appropriate filesystem location relative to the web server's document root, and prints a warning in the log file as appropriate. When a view needs to emit a <a href="..."> or <form> to the browser, it calls the desired page's emitLink() or emitForm() method to generate the URL. Therefore, if the code compiles correctly and the log file does not contain warnings, all hotlinks within the DynamicPages of that web site are guaranteed to be valid. (Any StaticPages are, of course, immune to this protection since they, by definition, hold hardcoded URLs.) For example, AccountView generates a link to the EditAddress page like: DemoSite.EditAddress.emitLink(this, "Address").

A second benefit is only obvious to those who've used servlet sessions extensively. The session machinery uses cookies to manage sessions if the browser supports cookies. But because some browsers do not support cookies and since users often turn cookies off, the servlet engine provides a low-level mechanism that will encode session information into URLs. For this to work, every URL must be passed through this mechanism, which isn't even possible for StaticPages because their URLs are hardcoded into their HTML text. Thus, if cookies are not available and the user clicks such a link, their session information will be lost. The site infrastructure eliminates this problem for DynamicPages by automatically passing all URLs through the session-encoding mechanism. StaticPages are still prone to this problem, so I use only one StaticPage per application. The first page users encounter when entering the site (the Home page in this example) is a StaticPage. Any links from internal pages to the Home page point to a DynamicPage that looks exactly the same to the user, but has links that are properly session encoded.

The Site object is responsible for providing the HttpServlet protocol for that site, so SiteImpl is a subclass of HttpServlet. It defines HttpServlet's doGet/doPost methods to transfer requests to the doRequest method. This method handles both kinds of request by obtaining the identifier of the requested State from a request parameter reserved for this purpose (do), looking up the DynamicPage with this identifier, and launching the Page's controller by using the Reflection API to call the no-args constructor and calling the instance's initialize method to populate its instance variables with a reference to the site and the request/response servlet arguments. If you are familiar with JSP, you will notice the strong similarity between State and JSP's PageContext class.

Each site's Site subclass is also responsible for providing concrete implementations of the abstract htmlPageOpen() and htmlPageClose() methods. Each controller inherits sendPageOpen()/sendPageClose() methods, which rely on the site's htmlPageOpen()/htmlPageClose() to generate standard strings of HTML text to begin and end each page. Most simple pages use the simpler sendPage(aString) method, which calls these before and after emitting aString as the content of the page. Every site override these methods and they can be as complex as desired. For example, the htmlPageOpen() method for the http://www.superdistributed.com/ web application generates a rather elaborate navigational menu at the top of each page that shows not only where users are now and where they can get to with respect to the current page, but also the site resources that are accessible to that user's role in the system. This menu simply presents additional links for users authorized to play employee versus administrative versus customer roles.

Sites are defined by defining a Java subclass, not by editing resource files, configuration files, XML files, or the like. I'm more comfortable working with Java than with the arcane and continually changing configuration conventions of systems such as Apache and Tomcat. Every Tomcat release seems to involve a new configuration format based on new terms understood only by the Tomcat developers. Common configuration errors are rarely checked and reported with the precision we take for granted of Java compilers. Why force users to learn a specialized configuration language for each tool when the job could be done in a widely understood language that reports errors properly?

Models

In the Model/View/Controller framework, views do not access the database directly. Rather, they interact with models that provide an object-oriented API and hide messy SQL details inside their load() or save() methods. In this example, when users register a new account, the RegisterView object calls the AccountBean load method by providing it with a database connection object and the Identifier (primary key) of the instance to be loaded. Notice how getString() values are immediately converted to type-checkable Fields as soon as they emerge from the database; see Listing Six.

When all fields pass their syntactic validation checks, RegisterView calls the AccountBean's save() method to add the new account to the database. The save methods typically call isValid() internally before issuing the SQL commands to add this instance to the DBMS. Listing Seven shows how JDBC is used to add information to the database.

Views

I've saved the views for last because how they are implemented is not specified by the architecture described here. That is, the State interface only requires that each State must provide a public controller() method. How the views are implemented is up to the controller, not the architecture.

The reason the architecture doesn't specify a single right way is that there is no single right way for all environments. Most shops involve cooperation between programmers and user-interface designers, and the world-view of these two groups is opposed. User-interface designers (and their tools) view web applications as static data, composed of HTML text held in files. Programmers view web applications as dynamic constructs, as programs that compute data such as HTML text on the fly. Between the static and dynamic polar opposites are an infinity of intermediate points, any of which could be criticized for veering too far to one side or another.

The typical solution is to extend HTML with a limited set of programming constructs, thus turning HTML into a template language. For example, JSP, JSP Tablibs, WebMacro, Enhydra, Turbine, WebObjects, Cocoon, and others provide HTML extensions that add programming language features to HTML, such as executable inclusions (variable substitution), conditionals (if statements), loops, and so forth.

Here, I play both roles and only use WYSIWYG page layout tools for designing the look-and-feel for sites as a whole. When the site design is completed, I import the site's HTML into the site's htmlPageOpen()/htmlPageClose() methods, then extend these with code to provide dynamic features such as automatically generated navigation menus and the like. The HTML within each individual page is simple, routine, and usually written by hand. Rather than inventing a restricted language to embed code within HTML, I use Java as the template language. Instead of using language restrictions to enforce discipline, I use a language that is capable of supporting a disciplined architecture to separate presentation (HTML) from implementation (code). I simply code the views as private methods of each state class that the controller calls like any other.

The main obstacle to this way of working is that Java's string syntax makes long sequences of HTML text tedious to write by hand. Consequently, I've built a multiline string preprocessor that addresses the problems of multiline strings and executable inclusions. (This preprocessor will be presented in a future issue of DDJ.) The preprocessor's executable inclusion feature provides a way of integrating code with HTML, and could therefore be considered a template language. The difference is that the tool is used during the compile process instead of when files are read at run time. For instance, Example 1 is the viewLogin method of the LoginView class. The listing has been colored to emphasize the two modes that the preprocessor operates in. The blue indicates parts that the preprocessor treats as an executable inclusion and red as part of a Java string. The views rely heavily on the validation architecture to emit field values ({{identifier.getValue()}}) and diagnostic messages ({{identifier.getMessage()}}) into the form. Notice that the site architecture only emits the opening <form> command ({{DemoSite.Login.emitForm(this)}}), but the closing </form> is done by hand.

Tools

The Model/View/Controller paradigm basically divides a web application into two parts. The models (beans) provide the low-level foundation upon which the high-level superstructure rests. This consists of the controller/view classes that interface the models to users.

For the foundation-level classes, I adopted Visual Age for Java. My only real dissatisfaction with Visual Age is that the Linux edition still only supports Java 1.1.8. (I'm evaluating OTI's Visual Age Micro Edition as a possible replacement.)

For superstructure-level work, I use vi, mls, and IBM's jikes, driven by a UNIX Makefile to emit binary into the application's WEB-INF/classes directory. I often import the superstructure's source code into Visual Age for browsing and to double check that all is well.

Visual Age is a capable debugging environment, reminiscent of Smalltalk and Interlisp. By loading Tomcat into the Visual Age environment, the entire web applications can operate under control of the debugger. However, Tomcat must be specially configured for this to work, and the complexity of the Tomcat configuration process and the frequency of new Tomcat releases that seem to always involve a painful reconfiguration made me seek a more sustainable approach.

The boundary between foundation and superstructure is a natural place to do unit testing as distinct from debugging. I've adopted Kent Beck's JUnit unit test infrastructure (http://www.JUnit.org/) to build a large library of unit tests for most of the beans in the system. I run these inside Visual Age, whose debugger is quite useful for tracking down any problems. Ensuring that the superstructure interacts with a stable, tested foundation goes a long way toward reducing the need to do line-by-line debugging of the superstructure level code, so I usually just add print statements to debug problems at this level.

Conclusion

The primary conclusions from this project is that Java is a congenial environment for building web applications once its main limitation — uniline strings — is relaxed with tools such as MLS. The validation architecture makes controller logic simpler than in JSP by encapsulating syntactic field validation logic in application-specific Field classes and by providing a simple mechanism whereby views can obtain error messages to return to the user. A second reason is that the Site architecture provides a high-level technique for ensuring that all lines are valid at compile time while ensuring that all URLs are properly encoded for browsers that don't support cookies. Although similar features could be added to JSP, I doubt that I'll go back because the current approach has a significantly smaller footprint and is more supportive of my style of work.

The architecture described here is neutral to whether HTML text should be held in files or in RAM as Java strings. I've described a personal coding style that uses a version of the MLS preprocessor that converts text to Java strings, which is how JSP handles HTML text, as Java strings in RAM instead of in HTML text files. The difference is that JSP compiles JSP files, loads the resulting Java code into memory when the page is referenced, and unloads it when the last reference disappears.

By contrast, the site's SiteImpl subclass holds a reference to all DynamicPages that will keep them in memory for the lifetime of the servlet. This consequence follows directly from the need to emit all <a href="..."> and <form action"..."> commands via Java code instead of hardcoding them into HTML files so that all URLs will be session encoded, and to support invalid link detection at compile time. Table 1 suggests that the space overhead is modest even for large sites such as http://www .superdistributed.com/. The high-level classes for this site (the views and controllers) add, at most, 1.5 KB to the executable image. This is negligible compared to the image size as a whole (64 MB) and is only 1/6 the size of the Tomcat and XML distributions. This supports the conclusion that space optimization efforts needn't start from the assumption that the html text within the view methods of dynamic pages is the dominant contributor to execution image size.

DDJ

Listing One

package com.sdi.field;
import gnu.regexp.*;
public class Zipcode extends Field implements Validatable
{
  private final static RE re = re("\\d{5}([ -_]?\\d{4})?");
  private final static String msg = "A 5 or 9 digit zipcode is required";
  public final static Zipcode Null = new Zipcode(null);
public Zipcode(String value) { super(value); }
public boolean setValue(String v) 
{
  setValue(v, true, "");
  return requireNonNull() & requireMatch(re, msg);
}
}

Back to Article

Listing Two

public class AddressBean extends BeanImpl
{
  private Identifier accountID = Identifier.Null;
  private Street street = Street.Null;
  private City city = City.Null;
  private USState state = USState.Null;
  private Zipcode zipcode = Zipcode.Null;
  private Phone phone = Phone.Null;

  public final static AddressBean Null = new AddressBean(
    Identifier.Null, Street.Null, City.Null, 
                     USState.Null, Zipcode.Null, Phone.Null
  );

Back to Article

Listing Three

public final void controller() throws Exception
{
  try
  {
    AccountBean thisAccount = (AccountBean)site.getAccount(this);
    redirect(DemoSite.Account);
    return; // should never happen
  }
  catch (SessionExpiredException e) { /* empty */ }

  Op op = (Op)getField("op", Op.Null);
  Identifier identifier = (Identifier)getField("identifier", Identifier.Null);

  if ( op.isValid() && identifier.isValid())
  {
    try
    {
      Connection connection = ConnectionPool.getConnection();
      AccountBean account = AccountBean.load(connection, identifier);
      site.setAccount(account, this);
      redirect(DemoSite.Account);
    }
    catch (Exception e)
    {
      identifier.setValid(false, "This identifier was not found");
      sendPage(viewLogin(identifier));
    }
  }
  else sendPage(viewLogin(identifier));
}

Back to Article

Listing Four

public void controller() throws Exception
{
  AccountBean thisAccount = (AccountBean)site.getAccount(this);
  AddressBean address = thisAccount.getAddress();

  Op op = (Op)getField("op", Op.Null);
  Street street = (Street)getField("street", address.getStreet());
  City city = (City)getField("city", address.getCity());
  USState state = (USState)getField("state", address.getState());
  Zipcode zipcode = (Zipcode)getField("zipcode", address.getZipcode());
  Phone phone = (Phone)getField("phone", address.getPhone());

  if ( op.isValid() && street.isValid() && city.isValid() &&
    state.isValid() && zipcode.isValid() && phone.isValid())
  {
    address.setAddress( street, city, state, zipcode, phone);
    address.save(thisAccount.getConnection());
    forward(DemoSite.Account);
  }
  else sendPage(viewAddress( op, street, city, state, zipcode, phone));
}

Listing Five

public class DemoSite extends SiteImpl implements Site
{
  public static final Page Home = newStaticPage(
    "Home",
    null,
    Role.Null,
    "/html/index.htm",
    "Demo home page"
  );
  public final static DynamicPage Account = newDynamicPage(
    "Account",
    AccountView.class,
    "Account",
    Registered,
    "To your account"
  );
  public final static DynamicPage AccountRegistration = newDynamicPage(
    "AccountRegistration",
    AccountRegistrationView.class,
    "Registration",
    Role.Null,
    "Account Registration"
  );
  public final static DynamicPage EditAddress= newDynamicPage(
    "EditAddress",
    EditAddressView.class,
    "Address",
    Registered,
    "Modify your address and/or phone number"
  );
  public final static DynamicPage Login = newDynamicPage(
    "Login",
    LoginView.class,
    "Login",
    Role.Null,
    "Login procedure"
  );
  public final static DynamicPage Logout = newDynamicPage(
    "Logout",
    LogoutView.class,
    "Logout",
    Role.Null,
    "Logout"
  );
  public final static DynamicPage Refuse = newDynamicPage(
    "Refuse",
    RefuseView.class,
    "Permission Denied",
    Role.Null,
    "Permission denied"
  );
}

Back to Article

Listing Six

public static AddressBean load(Connection connection, Identifier accountID)
  throws Exception
{
  String sql = "select * from DemoAddress " +
      "where accountID=\"" + accountID + "\"";
  try 
  {
    Statement stmt = connection.createStatement();
    ResultSet s = stmt.executeQuery(sql);
    if (!s.next())
      throw new Exception("couldn't do " + sql);

    return new AddressBean(
      accountID,
      new Street(s.getString("street")),
      new City(s.getString("city")),
      new USState(s.getString("state")),
      new Zipcode(s.getString("zipcode")),
      new Phone(s.getString("phone"))
    );
  } 
  catch (SQLException e) 
  {
    throw new Exception("SQLException in " + sql + "\n\t" + e);
  }
}

Back to Article

Listing Seven

public Identifier save(Connection connection) 
  throws Exception 
{
  String sql = "replace into DemoAddress set " +
    "accountID=?, " +
    "street=?, " +
    "city=?, " +
    "state=?, " +
    "zipcode=?, " +
    "phone=?";
  try 
  {
    PreparedStatement s = connection.prepareStatement(sql);
    s.setString(1, accountID.toString());
    s.setString(2, street.toString());
    s.setString(3, city.toString());
    s.setString(4, state.toString());

    s.setString(5, zipcode.toString());
    s.setString(6, phone.toString());
    
    int n = s.executeUpdate();
    s.close();
    return accountID;
  }
  catch (Throwable e)
  {
    throw new Exception("Throwable in " + sql + "\n\t" + e);
  }
}



Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.