[ Team LiB ] Previous Section Next Section

Using Sessions to Save Data in a Shopping Cart Application

Although there are many uses for the session object, one of the most common uses is to store items for online shopping. When you shop online, you usually browse around the Web site, occasionally clicking an "Add to Shopping Cart" button to signal that you want to buy something. When you are done, you click "Check Out" and fill out your billing information.

The session object is a logical place to keep shopping cart data. You can, of course, keep the data on the client's browser, but it can get pretty cumbersome trying to keep up with the shopping cart on the client when the user goes from page to page.

You can also store the shopping cart in a database and just keep enough data in the session to make it possible to retrieve the shopping cart out of the database. If the database is fast enough and you have so many sessions active at one time that you can't keep all the data in memory, the database might be a better solution. For most applications, however, the session is the ideal place.

Designing the Shopping Cart

When designing applications, it's often best to focus on the model portion of the application first. In other words, create Java classes that implement the behavior of the shopping cart. These classes should have no relationship to servlets or JSPs. You should be able to use them in an entirely different kind of application if you want to.

Model-View-Controller

graphics/bytheway_icon.gif

You've probably heard of Model-View-Controller (MVC). It's an architectural pattern that is successful in achieving design and runtime flexibility. Using MVC often leads to more reusable and maintainable code. In short, the model represents some kind of data and the behavior necessary to work with it. A view presents a model to a user. And a controller defines how a view responds to user input. This example uses MVC. You'll spend more time with MVC in Hour 20, "Building Web Applications with JavaServer Pages and Servlets."


First, ask yourself, "What should I be able to do to a shopping cart?" For a shopping cart, you want to add items, remove items, list items, and purchase items contained in the cart. Next, ask yourself "What exactly is an item?" That's a good question. An item should have a description, a price, a quantity, and some kind of product code that you would use for your ordering system. Also, to purchase a product, the user must supply billing and shipping information. You should have one or more classes that represent this information.

Creating the Data Objects

To kick things off, Listing 13.1 shows the Item object that will be stored in the shopping cart. For good measure, it has get and set methods and implements the Serializable interface, so it is fairly well behaved as a bean.

Listing 13.1 Source Code for Item.java
package examples.cart;

public class Item implements java.io.Serializable
{
    public String productCode;
    public String description;
    public double price;
    public int quantity;

    public Item()
    {
    }

    public Item(String aProductCode, String aDescription,
        double aPrice, int aQuantity)
    {
        productCode = aProductCode;
        description = aDescription;
        price = aPrice;
        quantity = aQuantity;
    }

// Make get/set methods so the attributes will appear
// as bean attributes.

    public String getProductCode() { return productCode; }
    public void setProductCode(String aProductCode) {
        productCode = aProductCode; }

    public String getDescription() { return description; }
    public void setDescription(String aDescription) {
        description = aDescription; }

    public double getPrice() { return price; }
    public void setPrice(double aPrice) { price = aPrice; }

    public int getQuantity() { return quantity; }
    public void setQuantity(int aQuantity) { quantity = aQuantity; }
}

The next step is to create a class to hold the billing information. Because this is just an example, the billing is limited to credit card orders. All you need for a credit card order is the name of the person on the card, the card number, the type of card, and the expiration date. Listing 13.2 shows the Billing class.

Listing 13.2 Source Code for Billing.java
package examples.cart;

public class Billing implements java.io.Serializable
{
    public String nameOnCard;
    public String creditCardType;
    public String creditCardNumber;
    public String creditCardExpiration;

    public Billing()
    {
    }

    public String getNameOnCard() { return nameOnCard; }
    public void setNameOnCard(String aName) { nameOnCard = aName; }

    public String getCreditCardType() { return creditCardType; }
    public void setCreditCardType(String aCreditCardType)
        { creditCardType = aCreditCardType; }

    public String getCreditCardNumber() { return creditCardNumber; }
    public void setCreditCardNumber(String aCreditCardNumber)
        { creditCardNumber = aCreditCardNumber; }

    public String getCreditCardExpiration()
        { return creditCardExpiration; }
    public void setCreditCardExpiration(String aCreditCardExpiration)
        { creditCardExpiration = aCreditCardExpiration; }
}

The shopping cart needs one more data object. You have the items; you have the billing information; now you just need to know where to ship the item. Listing 13.3 shows the Shipping class. Like the other two classes, the Shipping class has the get and set methods to make various bean tools happy.

Listing 13.3 Source Code for Shipping.java
package examples.cart;

public class Shipping implements java.io.Serializable
{
    public String name;
    public String address1;
    public String address2;
    public String city;
    public String state;
    public String country;
    public String postalCode;
    public String email;

    public Shipping()
    {
    }

    public String getName() { return name; }
    public void setName(String aName) { name = aName; }

    public String getAddress1() { return address1; }
    public void setAddress1(String anAddress1)
        { address1 = anAddress1; }

    public String getAddress2() { return address2; }
    public void setAddress2(String anAddress2)
        { address2 = anAddress2; }

    public String getCity() { return city; }
    public void setCity(String aCity) { city = aCity; }

    public String getState() { return state; }
    public void setState(String aState) { state = aState; }

    public String getCountry() { return country; }
    public void setCountry(String aCountry) { country = aCountry; }

    public String getPostalCode() { return postalCode; }
    public void setPostalCode(String aPostalCode)
        { postalCode = aPostalCode; }

    public String getEmail() { return email; }
    public void setEmail(String anEmail) { email = anEmail; }
}

Creating the Shopping Cart Class

Now, all that is left is the shopping cart itself. For the purposes of this example, the shopping cart doesn't do much with the order. Normally, you would insert the order in a database or send it to an application server. But the important thing as it relates to JSPs and servlets is that you can order some items and submit the order. To keep track of the order, you really don't need anything more than a vector of items. The billing and shipping information gets passed to the shopping cart when the order is finally completed. Listing 13.4 shows the source for the ShoppingCart class.

Listing 13.4 Source Code for ShoppingCart.java
package examples.cart;

import java.util.*;
import java.io.*;

public class ShoppingCart implements java.io.Serializable
{
// The shopping cart items are stored in a Vector.
    protected Vector items;

    public ShoppingCart()
    {
        items = new Vector();
    }

/** Returns a Vector containing the items in the cart. The Vector
 *  returned is a clone, so modifying the vector won't affect the
 *  contents of the cart.
 */
    public Vector getItems()
    {
        return (Vector) items.clone();
    }

    public void addItem(Item newItem)
    {
        items.addElement(newItem);
    }

    public void removeItem(int itemIndex)
    {
        items.removeElementAt(itemIndex);
    }

// Warning! This order number is reset every time the server is
// restarted. This technique of generating an order number is
// just for demonstration.
    protected static int nextOrderNumber = 1;

// Submit the order and return a confirmation number.
    public String completeOrder(Shipping shipping, Billing billing)
        throws ShoppingCartException
    {
// You would normally insert the order into a database or send
// it to an application server. For the sake of simplicity
// this shopping cart just writes the order to a file.
        try
        {
            int orderNumber = 0;

// Make sure no other threads can be generating an order number.
            synchronized (this)
            {
                orderNumber = nextOrderNumber;
                nextOrderNumber = nextOrderNumber + 1;
            }
            PrintWriter out = new PrintWriter(
                new FileOutputStream("order"+orderNumber));

// Print the shipping info.
            out.println(shipping.nameOnCard);
            out.println(shipping.address1);
            if (shipping.address2 != null)
            {
                out.println(shipping.address2);
            }
            out.print(shipping.city);
            if (shipping.state != null)
            {
                out.print(", "+shipping.state);
            }
            if (shipping.postalCode != null)
            {
                out.print(" "+shipping.postalCode);
            }
            out.println(" "+shipping.country);
            out.println(shipping.email);

// Print the billing info.
            out.println(billing.name);
            out.println(billing.creditCardType);
            out.println(billing.creditCardNumber);
            out.println(billing.creditCardExpiration);

// Print out the items.
            Enumeration e = items.elements();
            while (e.hasMoreElements())
            {
                Item item = (Item) e.nextElement();

                out.println(item.productCode+","+
                    item.quantity);
            }
            out.close();

// Return a confirmation number (the order number as a string in this case).
            return ""+orderNumber;
        }
        catch (Exception exc)
        {
            throw new ShoppingCartException(
                "Error saving order: "+exc.toString());
        }
    }
}

The ShoppingCartException used by the ShoppingCart class is just a subclass of java.lang.Exception and doesn't add any additional capabilities. It does help you organize your error handling, however, because you can catch ShoppingCartException rather than the more generic Exception. Listing 13.5 shows the ShoppingCartException class.

Listing 13.5 Source Code for ShoppingCartException.java
package examples.cart;

public class ShoppingCartException extends Exception
{
    public ShoppingCartException()
    {
    }

    public ShoppingCartException(String message)
    {
        super(message);
    }
}

Displaying the Contents of the Shopping Cart

So far, you have a lot of shopping cart code and not a single JSP or servlet. Like any technology, you should use Java's Web technologies where they are applicable. In this case, you are much better off with a shopping cart that doesn't care whether it's on the Web. If you end up making some sort of standalone kiosk application where someone can walk up, select some items, and immediately make a purchase, you could still use the ShoppingCart class even though the application would not be Web-based.

Now it's time to put the shopping cart out on the Web by putting servlets and JSPs on top of it. When it comes to displaying a shopping cart's contents, you might need to show the contents at different times. For example, the user might click a Display Shopping Cart button and see just the shopping cart's contents. Later, when the user clicks the Check Out button to make a purchase, you need to display the shopping cart again, but this time with other information on the page as well.

Because you need to display the shopping cart in different pages, it makes sense to create a separate JSP to display the shopping cart. The trick is, this JSP is not a complete Web page in itself. That is, it does not contain the <html> or <body> tags. Instead, it contains just the HTML tags necessary to display the shopping cart. That way, you can include it in other pages. Listing 13.6 shows the code for DisplayShoppingCart.jsp.

Listing 13.6 Source Code for DisplayShoppingCart.jsp
<%@ page language="java" import="examples.cart.*,java.util.*,java.text.*" %>

<%-- Show the header with the shopping cart image --%>
<table border="0">
<tr><td><img src="cart4.png"><td><h1>Shopping Cart</h1>
</table>

<%
// Get the current shopping cart from the user's session.
    ShoppingCart cart = (ShoppingCart) session.getAttribute("ShoppingCart");

// If the user doesn't have a shopping cart yet, create one.
    if (cart == null)
    {
        cart = new ShoppingCart();
        session.setAttribute("ShoppingCart", cart);
    }

// Get the items from the cart.
    Vector items = cart.getItems();

// If there are no items, tell the user that the cart is empty.
    if (items.size() == 0)
    {
        out.println("<h3>Your shopping cart is empty.</h3>");
    }
    else
    {
%>
<%-- Display the header for the shopping cart table --%>
<br>
<table border=4>
<tr><th>Description</th><th>Quantity</th><th>Price</th></tr>
<%

        int numItems = items.size();

// Get a formatter to write out currency values.
        NumberFormat currency = NumberFormat.getCurrencyInstance();

        for (int i=0; i < numItems; i++)
        {
            Item item = (Item) items.elementAt(i);

// Print the table row for the item.
            out.print("<tr><td>");
            out.print(item.description);
            out.print("</td><td>");
            out.print(item.quantity);
            out.print("</td><td>");
            out.print(currency.format(item.price));

// Print out a link that allows the user to delete an item from the cart.
            out.println("</td><td>"+
                "<a href=\"/shoppingcart/RemoveItemServlet?item="+
                i+"\">Remove</a></td></tr>");
        }
    }
%>
</table>

Figure 13.1 shows the shopping cart as it looks after you have added some items.

Figure 13.1. Many pages can use the same shopping cart display.

graphics/13fig01.gif

Adding and Removing Items

Adding and removing shopping cart items are tasks that are nicely suited for servlets. We'll reserve functions that interface with the user for JSPs and use servlets for functions such as this.

The addItem method in the ShoppingCart class seems simple enough. You just need to create an Item object to add. There are three basic approaches to creating the Item object:

  • The client browser can send all the information necessary to create the item. This implies that you must pass all the necessary information to the client, even if it won't be needed.

  • You can create the Item object ahead of time and store it in memory on the server in the session or application objects. When the user orders the item, you pull it out of the session or application object and put it in the shopping cart.

  • You retrieve the Item information from an application server or a database. You need only enough information to uniquely identify the item you want to add.

Each of these approaches has advantages and disadvantages, and you might find yourself using different approaches for different types of systems. Although the Item class in this example contains only four data elements, the Item class for your real-world application might contain far more data elements. A complex item structure really requires the item information to stay somewhere on the server, either in the session object or in a database.

You might be wondering why you would create an Item object ahead of time and store it in memory. If you think about the way you shop for things online, you usually look at a page of items, each of which can be added to your shopping cart. The chances are good that the code that generated that page of items had to work with a bunch of Item objects. That is, when you generate the page from which you can order, you probably need the Item object anyway. Why not just store it in the application object or the session and bring it back out when the user orders it?

Users May Need Their Own Objects

graphics/watchout_icon.gif

If you allow the user to modify the contents of an Item object, such as changing the quantity or color, you must have a separate copy of the item for each user. If all the users share the same object, then when one user changes the quantity, it changes for everyone.


If you can store the objects in the application object, you save a lot of memory. If you store the objects in the session, you end up saving many copies of the same object. With specialized items, however, it might be better to save the objects in the server. For example, if your pricing structure varies depending on the user, it might be better to keep track of items on a per-session basis. If you have several kinds of pricing, you can store one copy of an item for each price. The item may have other attributes that change depending on the user as well.

Passing the Item Object from the Browser

If your item is small enough that all its data can be passed easily from the browser, that's probably your best solution. After all, you won't have to worry about cleaning up the session when the user has finished placing the order.

Cleaning up After a Session Terminates

graphics/didyouknow_icon.gif

Remember, cleaning up after a session terminates is not a big deal. You just have to create objects that recognize when they have been unbound from a session and clean themselves up. You learned how to do this in the last hour.


Listing 13.7 shows a JSP that generates a page of items that are available to be ordered. Notice that the Add to Shopping Cart links each have all the information for the item embedded in their URLs. When the user clicks on a link, all the data elements for an item are passed to the server.

Listing 13.7 Source Code for ShowProductCatalog.jsp
<%@ page language="java" import="examples.cart.*,java.net.*,java.text.*" %>
<html>
<body bgcolor="#ffffff">

<%
// Initialize the array of available products.
    Item[] catalog = new Item[] {
        new Item("X-1", "Jet Plane", 2999999.95, 1),
        new Item("GWU-123876345-27B/6",
            "Graphite Writing Utensil", 12000.00, 12),
        new Item("BCT-12", "Bionic Cat Tongue", 3700.00, 1),
        new Item("EZ-1", "Professional Electronic Zither",
            699.95, 1),
        new Item("PF-101", "Pink Flamingo", 12.00, 1),
        new Item("LOD-7", "Lump Of Dirt (Medium)", 1.00, 1)
    };

%>

<a href="/shoppingcart/ViewShoppingCart.jsp">View Shopping Cart</a>
<p>
<h1>Available Products</h1>
<table border="1">
<tr><th>Description</th><th>Quantity</th><th>Price</th></tr>
<%

// Get a currency formatter for showing the price.
    NumberFormat currency = NumberFormat.getCurrencyInstance();

    for (int i=0; i < catalog.length; i++)
    {
        Item item = catalog[i];

// Create the URL for adding the item to the shopping cart.
        String addItemURL =
            "/shoppingcart/AddToShoppingCartServlet?"+
            "productCode="+URLEncoder.encode(item.getProductCode())+
            "&description="+URLEncoder.encode(item.getDescription())+
            "&quantity="+URLEncoder.encode(""+item.getQuantity())+
            "&price="+URLEncoder.encode(""+item.getPrice());
%>
<tr><td><%=item.getDescription()%></td><td><%=item.getQuantity()%>
    </td><td><%=item.getPrice()%></td>
<td><a href="<%=addItemURL%>">Add to Shopping Cart</a></td></tr>
<%
    }
%>
</table>
</body>
</html>

Figure 13.2 shows the product catalog as displayed by ShowProductCatalog.jsp.

Figure 13.2. A catalog display contains links to add items to the shopping cart.

graphics/13fig02.gif

Listing 13.8 shows the AddToShoppingCartServlet class that takes the items from the product catalog and adds them to the shopping cart.

Listing 13.8 Source Code for AddToShoppingCartServlet.java
package examples.cart;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class AddToShoppingCartServlet extends HttpServlet
{
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws IOException, ServletException
    {

// First get the item values from the request.
        String productCode = request.getParameter("productCode");
        String description = request.getParameter("description");
        int quantity = Integer.parseInt(
            request.getParameter("quantity"));
        double price = Double.parseDouble(
            request.getParameter("price"));

// Now create an item to add to the cart.
        Item item = new Item(productCode, description, price, quantity);

        HttpSession session = request.getSession();

// Get the cart.
        ShoppingCart cart = (ShoppingCart) session.
            getAttribute("ShoppingCart");

// If there is no shopping cart, create one.
        if (cart == null)
        {
            cart = new ShoppingCart();

            session.setAttribute("ShoppingCart", cart);
        }

        cart.addItem(item);

// Now display the cart and allow the user to check out or order more items.
        response.sendRedirect(response.encodeRedirectURL(
            "/shoppingcart/ShowCartAfterAdd.jsp"));
    }
}
Keeping Items in Memory

If your shopping cart items are fairly complex, you are usually better off keeping them on the server and passing only a unique identifier for each item back to the client. For example, suppose you offer an item called "Pale Blue Japanese Guitar" with a product code of PBJG-1. When the user clicks the Add to Shopping Cart link to buy the guitar, the browser just sends the product code PBJG-1 to the server. The server looks up the product code and retrieves the object associated with that code.

Listing 13.9 shows a product catalog class that allows you to look up objects by product code, and also get a list of the available products. You might not want to display all the products on one page, so the product catalog enables you to display a certain number of objects at a time, starting at a particular position in the list.

Listing 13.9 Source Code for ProductCatalog.java
package examples.cart;

import java.util.Vector;

public class ProductCatalog
{
    protected Item[] items;

    public ProductCatalog()
    {

// Set up an array of items that represents the catalog.

        items = new Item[] {
            new Item("PBJG-1", "Pale Blue Japanese Guitar",
                700.00, 1),
            new Item("PBJZ-1", "Pale Blue Japanese Zither",
                1400.00, 1),
            new Item("PBJS-1", "Peanut Butter & Jelly Sandwich",
                1.00, 1),
            new Item("GCX", "Garlic Clove", 0.40, 1),
            new Item("XC", "Xenophobic Cat", 72.00, 1),
            new Item("BH", "Buttonhole", 0.05, 6),
            new Item("K9", "Dog", 100.00, 1),
            new Item("ATL", "Atlanta Airport", 9000000.00, 1),
            new Item("TEG", "Sheep", 75.00, 1),
            new Item("UPC", "Universal Price Code", 1.00, 1),
            new Item("ALL", "The Universe", 0.01, 1),
            new Item("ZYZZYVAS", "The last word in Scrabble",
                74.00, 1),
            new Item("SAM", "Urchin (aged for 7 years)", 0.0, 1),
            new Item("KATY", "Urchin (aged for 4 years)", 0.0, 1),
            new Item("FR44", "Flamingo Relish (tastes like chicken)",
                2.00, 1),
            new Item("PF44", "Pickled Flamingo (tastes like chicken)",
                2.00, 1),
            new Item("LF44", "Pink Lawn Flamingo (tasteless)",
                12.00, 1)
        };
    }

/** returns an array containing all the items in the catalog */
    public Item[] getItems()
    {
        return getItems(0, items.length);
    }

/** returns an array containing a subset of items from the catalog */
    public Item[] getItems(int startingLocation, int numItems)
    {
// If the number of items to be returned is larger than the number
// in the catalog, adjust the number to be returned.
        if (numItems > items.length)
        {
            numItems = items.length;
        }

// If by returning numItems items you would run out of items (if there
// are 5 items, you ask for 3, but give a starting location of 4),
// adjust the starting location backward to ensure that the proper
// number of items is returned.
        if (startingLocation+numItems >= items.length)
        {
            startingLocation = items.length - numItems;
        }

// Create an array for the returned items.
        Item[] returnItems = new Item[numItems];

// Copy the items from the catalog into the return array.
        System.arraycopy(items, startingLocation,
            returnItems, 0, numItems);

        return returnItems;
    }

/** Returns true if there are items at a particular starting location.
    This is helpful in determining whether a page should show a "Next"
    button to see the next page of catalog items.
*/
    public boolean itemsAvailable(int startingLocation)
    {
        if (startingLocation >= items.length) return false;
        return true;
    }

/** Searches for an item by product code and returns it. If there is
    no such item, this method returns null.  */
    public Item findItemByProductCode(String productCode)
    {
// Linear searches aren't a good idea for big arrays, but this
// one is small.
        for (int i=0; i < items.length; i++)
        {
            if (items[i].getProductCode().equals(
                productCode))
            {
                return items[i];
            }
        }

        return null;
    }
}

Listing 13.10 shows a JSP that displays the contents of the product catalog, allowing the user to add items to the shopping cart. The user can also view successive pages of the catalog by clicking the Next button.

Listing 13.10 Source Code for ShowProductCatalog2.jsp
<%@ page language="java" import="examples.cart.*,java.net.*,java.text.*" %>

<%!
// Declare a constant for the number of items to show on a page.
    public static final int ITEMS_PER_PAGE = 5;
%>

<html>
<body bgcolor="#ffffff">

<a href="/shoppingcart/ViewShoppingCart.jsp">View Shopping Cart</a>
<p>
<h1>Available Products</h1>
<table border="1">
<tr><th>Description<th>Quantity<th>Price
<%

// Get the shared product catalog.
    ProductCatalog catalog = (ProductCatalog) application.getAttribute(
        "ProductCatalog");

// If the shared product catalog hasn't been created yet, create it.
    if (catalog == null)
    {

// Not that it matters because it would be okay for two threads to initialize
// the product catalog, but synchronize this anyway to make sure only one
// thread stores the catalog. Any other JSP or servlet that needs to store
// the product catalog in the application object must also synchronize
// on application.

        synchronized (application)
        {
            catalog = new ProductCatalog();
            application.setAttribute("ProductCatalog", catalog);
        }
    }

// Get the next starting position for displaying catalog items.
    String startingPositionStr = (String) request.
        getParameter("StartingPosition");

    int startingPosition = 0;

// If there is a starting position parameter, parse it as an integer.
    if (startingPositionStr != null)
    {
        try
        {
// If there's an error parsing the number, the starting position will
// just remain 0.
            startingPosition = Integer.parseInt(startingPositionStr);
        }
        catch (Exception ignore)
        {
        }
    }

// Get ITEMS_PER_PAGE items at a time.
    Item[] items = catalog.getItems(startingPosition, ITEMS_PER_PAGE);

// Get a currency formatter for showing the price.
    NumberFormat currency = NumberFormat.getCurrencyInstance();

    for (int i=0; i < items.length; i++)
    {
        Item item = items[i];

// Create the URL for adding the item to the shopping cart.
        String addItemURL =
            "/shoppingcart/AddToShoppingCartServlet?"+
            "productCode="+URLEncoder.encode(item.getProductCode())+
            "&description="+URLEncoder.encode(item.getDescription())+
            "&quantity="+URLEncoder.encode(""+item.getQuantity())+
            "&price="+URLEncoder.encode(""+item.getPrice());
%>
<tr><td><%=item.getDescription()%></td><td><%=item.getQuantity()%>
    </td><td><%=item.getPrice()%></td>
<td><a href="<%=addItemURL%>">Add to Shopping Cart</a></td></tr>
<%
    }
%>
</table>
<table border="0">
<tr>
<%
    if (startingPosition > 0)
    {
        int prevPosition = startingPosition-ITEMS_PER_PAGE;

// Don't let the starting position go negative.
        if (prevPosition < 0) prevPosition = 0;

// Write out a link to display the previous catalog page.
        out.println("<td><a href=\"/shoppingcart/ShowProductCatalog2.jsp
            ?StartingPosition="+prevPosition+"\">&lt;&lt;Prev</a></td>");
    }
// Compute the next starting position in the catalog.
    int nextPosition = startingPosition+ITEMS_PER_PAGE;

// Make sure that there are still items to display at that starting
// position (that is, make sure nextPosition isn't greater than the total
// catalog size).
    if (catalog.itemsAvailable(nextPosition))
    {
// Write out a link to display the next catalog page.
        out.println("<td><a href=\"/shoppingcart/ShowProductCatalog2.jsp
            ?StartingPosition="+nextPosition+"\">Next&gt;&gt;</a></td>");
    }
%>
</tr>
</table>
</body>
</html>

Figure 13.3 shows the paged version of the product catalog. The <<Prev and Next>> links enable you to scroll through the catalog.

Figure 13.3. When you have many products, add links to allow the user to scroll through the catalog.

graphics/13fig03.gif

Having Trouble with the Example?

graphics/bytheway_icon.gif

If you are having trouble running the shopping cart examples, see the "Q&A" section at the end of this hour.


Allowing Multiple Quantities

The current shopping cart code isn't very efficient. If you try to order two of the same item, it just keeps two copies of the item in its internal vector. You can enhance the shopping cart to keep track of item quantities. Whenever a user adds an item to the shopping cart, the cart should look through its list of items. When it finds an item that is the same as the one the user is adding, the cart increments the quantity on the item it already has, instead of adding another item to its vector.

Because the Item class has a quantity that shows the number of items for one order (12 hot dogs, 6 colas, and so on), you need an order quantity that is the total number of items ordered. Every time you order another pack of 12 hot dogs, you add 12 to the order quantity. Your Item class should be something similar to the modified Item class shown in Listing 13.11.

Listing 13.11 Source Code for Modified Item.java
package examples.cart;
public class Item implements java.io.Serializable
{
    public String productCode;
    public String description;
    public double price;
    public int quantity;
    public int orderQuantity;

    public Item()
    {
    }

    public Item(String aProductCode, String aDescription,
        double aPrice, int aQuantity)
    {
        this(aProductCode, aDescription, aPrice, aQuantity, aQuantity);
    }

    public Item(String aProductCode, String aDescription,
        double aPrice, int aQuantity, int anOrderQuantity)
    {
        productCode = aProductCode;
        description = aDescription;
        price = aPrice;
        quantity = aQuantity;
        orderQuantity = anOrderQuantity;
    }

// Make get/set methods so the attributes will appear
// as bean attributes.

    public String getProductCode() { return productCode; }
    public void setProductCode(String aProductCode) {
        productCode = aProductCode; }

    public String getDescription() { return description; }
    public void setDescription(String aDescription) {
        description = aDescription; }

    public double getPrice() { return price; }
    public void setPrice(double aPrice) { price = aPrice; }

    public int getQuantity() { return quantity; }
    public void setQuantity(int aQuantity) { quantity = aQuantity; }

    public int getOrderQuantity() { return orderQuantity; }
    public void setOrderQuantity(int anOrderQuantity)
    {
        orderQuantity = anOrderQuantity;
    }

    public boolean equals(Object ob)
    {
        if (ob == this) return true;
        if (!(ob instanceof Item)) return false;
        if (((Item)ob).getProductCode().equals(getProductCode()))
        {
            return true;
        }
        return false;
    }
}

Next, modify the ShoppingCart class to update the orderQuantity property when you add and remove items. Listing 13.12 shows the modified methods.

Listing 13.12 Modifications to ShoppingCart.java for Multiple Items
public synchronized void addItem(Item newItem)
    {
        Enumeration e = items.elements();

// See if there is already an item like this in the cart.
        while (e.hasMoreElements())
        {
            Item currItem = (Item) e.nextElement();

            if (newItem.equals(currItem))
            {
// Update the order quantity on the existing item.
                currItem.orderQuantity = currItem.orderQuantity +
                    newItem.orderQuantity;
                return;
            }
        }

// Didn't find one like this one, so add this one to the cart.
        items.addElement(newItem);
    }

    public synchronized void removeItem(int itemIndex)
    {
        Item item = (Item) items.elementAt(itemIndex);

// Remove 1 instance of this item from the quantity (an instance
// is the number of items in the quantity, such as 1 car, 12 hot dogs).
        item.orderQuantity = item.orderQuantity - item.quantity;

// If the order quantity reaches 0, remove this item from the cart.
        if (item.orderQuantity <= 0)
        {
            items.removeElementAt(itemIndex);
        }
    }

Also, you must modify the line in ShoppingCart.java that writes out the quantity from


out.println(item.productCode+","+ item.quantity);

to


out.println(item.productCode+","+ item.orderQuantity);

You must also make one other change. Modify DisplayShoppingCart.jsp to show orderQuantity rather than quantity.

The Source Is Available on the CD

graphics/bytheway_icon.gif

The complete sources for the examples in the book are available on the accompanying CD. You're encouraged to try this example out.


Removing Shopping Cart Items

In the example shopping cart class, you remove items based on the item's index in the shopping cart. One of the advantages of using the index is that you don't have to perform any searches. You can just ask the vector to remove the item with the specific index.

The shopping cart display JSP automatically generates the Remove buttons for the items in the cart. Be very careful to ensure that the view of the shopping cart is updated after the user removes an item. When an item is removed from the cart, the indexes on the other items in the cart can change. If the view of the cart isn't updated to match the changed indexes, the user might accidentally delete the wrong item from the cart. Listing 13.13 shows the servlet that performs the removal from the shopping cart.

Listing 13.13 Source Code for RemoveItemServlet.java
package examples.cart;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class RemoveItemServlet extends HttpServlet
{
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws IOException, ServletException
    {

// Get the index of the item to remove.
        int itemIndex = Integer.parseInt(request.getParameter("item"));

        HttpSession session = request.getSession();

// Get the cart.
        ShoppingCart cart =
            (ShoppingCart) session.getAttribute("ShoppingCart");

// If there is no shopping cart, create one.
        if (cart == null)
        {
            cart = new ShoppingCart();

            session.setAttribute("ShoppingCart", cart);
        }

        cart.removeItem(itemIndex);

// Now display the cart and allow the user to check out or order more items.
        response.sendRedirect(response.encodeRedirectURL(
            "/shoppingcart/ShowCartAfterRemove.jsp"));
    }
}

Completing the Order

After the user has selected some items and decides to complete the order, you need to get the user's billing information and shipping address. Listing 13.14 shows the JSP that displays the shopping cart and asks the user for billing and shipping information.

Listing 13.14 Source Code for Checkout.jsp
<%@ page language="java" import="examples.cart.*" %>
<html>
<body bgcolor="#ffffff">
<p>
<jsp:include page="DisplayShoppingCart.jsp" flush="true"/>
<p>
<h1>Please enter your shipping information</h1>
<p>
<form action="/shoppingcart/CheckoutServlet" method="post">

<table>
<tr><td>Name:</td><td><input type="text" name="name"></td></tr>
<tr><td>Address:</td><td><input type="text" name="address1"></td>
</tr>
<tr><td></td><td><input type="text" name="address2"></td></tr>
<tr><td>City:</td><td><input type="text" name="city"></td></tr>
    <td>State:</td>
    <td><input type="text" name="state" size=2 maxlength=2></td></tr>
<tr><td>Postal Code (Zip in U.S.):</td>
    <td><input type="text" name="postalCode"></td></tr>
<tr><td>Country:</td><td><input type="text" name="country"></td></tr>
<tr></tr>
<tr><td>Email Address:</td><td><input type="text" name="email">
</td></tr>
</table>
<p>
<h1>Please enter your billing information</h1>
<table>
<tr><td>Name (as it appears on credit card):</td>
    <td><input type="text" name="nameOnCard"></td></tr>
<tr><td>Credit Card:</td>
<td><select name="creditCardType">
    <option value="amex">American Express</option>
    <option value="visa">Visa</option>
    <option value="mc">Mastercard</option>
    <option value="discover">Discover</option>
    <option value="bbbt">Billy Bob's Bank &amp; Trust</option>
    </select></td></tr>
<tr><td>Credit Card Number:</td>
    <td><input type="text" name="creditCardNumber"></td></tr>
<tr><td>Expiration Date:</td>
    <td><input type="text" name="creditCardExpiration"></td></tr>
</table>
<p>
<input type="submit" value="Complete Order">
</form>
</body>
</html>

Figure 13.4 shows a portion of the checkout page.

Figure 13.4. When checking out, you can show the cart again and then ask for additional information.

graphics/13fig04.gif

Listing 13.15 shows the servlet that takes the billing and shipping information and passes it to the shopping cart to complete the order. After the order has been submitted, the servlet calls a JSP to display the order confirmation.

Listing 13.15 Source Code for CheckoutServlet.java
package examples.cart;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.*;

public class CheckoutServlet extends HttpServlet
{
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws IOException, ServletException
    {

// First get the shipping values from the request.
        Shipping shipping = new Shipping();

        shipping.setName(request.getParameter("name"));
        shipping.setAddress1(request.getParameter("address1"));
        shipping.setAddress2(request.getParameter("address2"));
        shipping.setCity(request.getParameter("city"));
        shipping.setState(request.getParameter("state"));
        shipping.setPostalCode(request.getParameter("postalCode"));
        shipping.setCountry(request.getParameter("country"));
        shipping.setEmail(request.getParameter("email"));

// Next, get the billing values.
        Billing billing = new Billing();

        billing.setNameOnCard(request.getParameter("nameOnCard"));
        billing.setCreditCardType(request.getParameter("creditCardType"));
        billing.setCreditCardNumber(request.getParameter(
            "creditCardNumber"));
        billing.setCreditCardExpiration(request.getParameter(
            "creditCardExpiration"));

        HttpSession session = request.getSession();

// Get the cart.
        ShoppingCart cart =
            (ShoppingCart) session.getAttribute("ShoppingCart");

// If there is no shopping cart, create one (this should really be an error).
        if (cart == null)
        {
            cart = new ShoppingCart();

            session.setAttribute("ShoppingCart", cart);
        }

        try
        {
            String confirmation = cart.completeOrder(shipping, billing);

// Now display the cart and allow the user to check out or order more items.
            response.sendRedirect(response.encodeRedirectURL(
                "/shoppingcart/ShowConfirmation.jsp"+
                "?confirmationNumber="+URLEncoder.encode(confirmation)));
        }
        catch (ShoppingCartException exc)
        {
            PrintWriter out = response.getWriter();

            out.println("<html><body><h1>Error</h1>");
            out.println("The following error occurred while "+
                "processing your order:");
            out.println("<pre>");
            out.println(exc.getMessage());
            out.println("</pre>");
            out.println("</body></html>");
            return;
        }
    }
}

Listing 13.16 shows the order confirmation page.

Listing 13.16 Source Code for ShowConfirmation.jsp
<html>
<body>
<h1>Order Submitted Successfully!</h1>
<p>
Thank you for your order. Your order confirmation number is:
<br>
<pre>
<%=request.getParameter("confirmationNumber")%>
</pre>
<p>
Please use this number when calling to check on your order.
</body>
</html>
    [ Team LiB ] Previous Section Next Section