Skip Headers

Oracle® Application Development Framework Case Manual 10g Release 2 (10.1.2) B19163-01 |
| Library | Product | Contents

Previous | Next |

# 6 Lesson Three: Assembling the Shopping Cart

This chapter contains the following sections:

- Section 6.1, "Introduction"

- Section 6.2, "Analyzing the Shopping Cart Page Flow"

- Section 6.3, "Managing the State of the Shopping Cart"

- Section 6.4, "Handling the Product Pages' Add-to-Cart Event"

- Section 6.5, "Handling the Shopping Cart Page's Add/Remove Events"

- Section 6.6, "Handling the Shopping Cart Page's Review-Checkout Event"

- Section 6.7, "Working with Operations in Forms"

- Section 6.8, "Hands-On for Lesson 3"

## 6.1 Introduction

In Lesson 2, we examined the initial pages of the Oracle ADF Toy Store application that permit the user to browse the product catalog. However, we have not yet addressed how the application will assemble the user's shopping cart prior to processing an order. In this lesson, we'll examine how to:

- Extend the Struts page flow to manage the shopping cart and process an order

- Handle updates to the shopping cart when the user removes an item or changes item quantities

- Capture the shopping cart changes in the business model

Let's begin by returning to the application workbench, the Struts page flow diagram, to extend our page flow based on these requirements.

## 6.2 Analyzing the Shopping Cart Page Flow

The page flow proceeds with the action forward `addToCart` from two data pages: `/showproduct` and `/showproductdetails`. The forward destination is the `/yourcart` data page. The `/yourcart` data page will respond to adding and removing products by updating the cart.

Following the `/yourcart` data page, we display the actual order before completing checkout. The page flow now includes a new data page for each task and is shown in Figure 6-1, "Shopping Cart Page Flow".

Figure 6-1 Shopping Cart Page Flow

We continue to use the Oracle ADF data page construct to post back to data actions from mapped JSP pages. The data page's underlying data action ensures that the Oracle ADF model layer is updated before the controller displays the target databound JSP page.

## 6.3 Managing the State of the Shopping Cart

The goal of managing the contents of the shopping cart is to populate the rows of the cart programmatically as the user adds and removes items from the cart. In the `/yourcart` data page, there is no database query involved in rendering the page. Instead, the shopping cart is implemented as an Oracle ADF view object named `toystore.model.dataaccess.ShoppingCart` in the ToyStoreModel project. This view object has all transient attributes (and no SQL query).

As we'll see in the following sections, the `YourCartAction` action class calls the `ToyStoreService` business service method `adjustQuantitiesInCartAsStringArrays()` to add, change, or remove items from the cart. Since the application relies on the Oracle ADF Business Components stateful mode, the application code does not have to worry about how to store the pending shopping cart data. Examine the Tuning page of the View Object Editor for the `ShoppingCart` view object. There we indicate declaratively that all transient attributes of this view object will be passivated by the framework's state management mechanism. Just checking the checkbox there is the only work required to leverage this feature.

On each subsequent request, our actions can access the `ShoppingCart` object and programmatically adjust the contents of its default rowset.

## 6.4 Handling the Product Pages' Add-to-Cart Event

In the previous pages, we saw how to pass the `id` of the product on the request object using an HTML link and the `<c:url>` tag to specify a named event. The `addToCart` action is another such event, and it is triggered with this syntax from the `showproductdetails.jsp` and the `showproduct.jsp` pages:

    <a href="<c:url
                value='showproductdetails.do?event=addToCart&id=${bindings.ItemId}'/>"
                 ><img src="<bean:message key='images.buttons.addtocart'/>" border="0"
                        alt="<bean:message key="cart.addItem"/>"> </a>

While the previous pages' actions handled no events, we want the product pages' actions to update the quantity attribute of the cart business object. Whenever the user clicks the Add to Cart image displayed by the above link, the controller executes the product page's corresponding data action and its `onAddToCart` event handler:

    public void onAddToCart(DataActionContext ctx) {
       String[] id = new String[] { ctx.getHttpServletRequest().getParameter("id") };
       String[] qty = new String[] { "1" };
       getToyStoreService(ctx).adjustQuantitiesInCartAsStringArrays(id, qty);
    }

Again, we see the use of a service method to encapsulate business logic, where the `adjustQuantitiesInCartAsStringArrays()` method is implemented. We'll use this same method to handle removing items and updating quantities in the displayed shopping cart page, as described next.

## 6.5 Handling the Shopping Cart Page's Add/Remove Events

The Struts controller displays the shopping cart page (`yourcart.do`) anytime the user clicks the Add to Cart link for a specific product. The shopping cart page comprises the same product list table with the `<c:forEach>` loop used in the product by category page. However, this time we render the table as the body of an HTML `<form>` element in order to accept the user's quantities on individual items in the cart:

    <form action="<c:url value='yourcart.do'/>" method="post">

The form posts back three pieces of information on the response object, where it can be retrieved by the `/yourcart` action for processing:

- The `event_updateCart` named event, specified by the Update Totals input

- The `Itemid` of the current product being iterated on by the `<c:forEach>` loop, specified by a hidden input element

- The `Quantity` to assign to the current product of the `<c:forEach>` loop, specified by a text edit input element

To trigger the quantities update, the user presses the Update Totals button. The event is submitted on the `name` attribute of the input element:

    <input type="image" src="<bean:message key='images.buttons.updatecart'/>"
           name="event_updateCart">

Open the file `YourCartAction.java`, which implements the data action (in the page flow diagram, right-click /yourcart and choose Go to Code), and find the `onUpdateCart()` event handler. First, the event handler obtains the product `id` of the current row and the product quantity entered by the user:

    String[] id = ctx.getHttpServletRequest().getParameterValues("id");
    String[] qty = ctx.getHttpServletRequest().getParameterValues("qty");

Next, the update cart event handler calls the `adjustQuantitiesInCartAsStringArrays()` service method to modify the cart quantities.

A similar event handler (`onRemoveItem`) exists to reset the cart quantity to zero when the user clicks the Remove button. In this case, the event is submitted with a URL and no form input is submitted:

    <a href="<c:url value='yourcart.do?event=removeItem&id=&{Row.Itemid}'/>">
       <img src="<bean:message key='images.buttons.removefromcart'/>" border="0" ></a>

After the data action executes the `onUpdateCart` or `onRemoveItem` event handler, the `invokeCustomMethod()` method is executed to prepare the cart total for display by `yourcart.jsp`. Unlike the shopping cart's `ExtendedTotal` attribute (which is calculated based on item quantities), the cart total is not a business service attribute to be accessed by the Oracle ADF shopping cart table binding. In order to make the result of the `getCartTotal()` method available to the JSTL tags in the shopping cart page, the `invokeCustomMethod()` adds the cart total as a request attribute:

    protected void invokeCustomMethod(DataActionContext ctx) {
        Double cartTotal = getToyStoreService(ctx).getCartTotal();
        ctx.getHttpServletRequest().setAttribute("CartTotal", cartTotal);
    }

The action mapping for the `/yourcart` action completes the postback pattern by invoking the `yourcart.jsp` page to render the form with the prepared data. You can confirm the target page by examining the `struts-config.xml` file or by mousing over the `/yourcart` action in the page flow diagram.

Note that the `yourcart.jsp` page does not use the Struts `<html:form>` and `<html:input>` elements to render the quantity input fields. In this example of form input, a programmatic approach was taken that simplifies handling the multiple form inputs provided by the user. Currently, Oracle ADF data binding provides no built-in support for easily handling multi-row input forms. By creating a String array with the product IDs and quantities, the `adjustQuantitiesInCartAsStringArray()` business service method (see `ToyStoreServiceImpl.java` in the `toystore.model.services` package) encapsulates the task to greatly simplify the `onUpdateCart` event handler. In Lesson 5, we'll see the use of a Struts form to display and modify the customer's account.

## 6.6 Handling the Shopping Cart Page's Review-Checkout Event

Once the user is satisfied with the cart, they can initiate their purchase by triggering the `reviewcheckout` forward from the shopping cart page. The Proceed to Checkout button sends the event `reviewcheckout`:

    <a href="<c:url value='yourcart.do?event=reviewcheckout'/>">
        <img src="<bean:message key='images.buttons.checkout'/>"
             alt="Proceed To Checkout" border="0"> </a>

Notice that the action forward `reviewcheckout` (shown in the page flow diagram) and the event share the same name. By default, after the Oracle ADF data action executes, the next phase of the Oracle ADF lifecycle will return the name of the action forward to use. If this check returns null, meaning that the developer has not previously programmatically set the action forward, then as a useful fallback behavior, the Oracle ADF lifecycle will try to find a forward with the same name as the current event being handled. If such a forward with a matching name exists, the default `findForward()` implementation will set that action forward to be used.

This explains why, without writing any custom controller code, the user can click the Proceed to Checkout button and see the `reviewcheckout.jsp` page. The HTML form submits the `reviewcheckout` operation and the Oracle ADF lifecycle uses the matching `reviewcheckout` forward to perform declarative navigation.

## 6.7 Working with Operations in Forms

The Oracle ADF named events let you handle multiple operations initiated from HTML form inputs by writing event handlers in the Oracle ADF data action. The result is a more declarative implementation of the Struts `ForwardAction` class. The naming conventions for operations (named events) differ from those Struts uses for the dispatch parameter in the `DispatchAction` or `LookupDispatchAction` objects. In a Struts-only application, the dispatch parameter (used to identify the operation corresponding to an HTML form submit element) can have any desired name. However, in ADF, the operation submitted by the input element's name attribute is usually specified as `event_``[operationName]`. The following discussion provides examples.

You can use the HTML image input element and set the `name` attribute to specify the operation like this:

    <input type="image" src="<bean:message key='resource key'/>"
           name="event_[operationName]">

Or, you can use the HTML submit button similarly:

    <input type="submit" name="event_[operationName]" value="message resource key"/>

Or, you can use a URL, with the event parameter and operation name:

    <a href="<c:url value=actionName.do?event=[operationName]/>">link text or image source</a>

Finally, in the data action, the method to handle a given operation is named like this:

    public void on[operationName] (DataActionContext actionContext)

As long as the `[operationName]` parts match, the event and a handler will be bound together at runtime. It is important to remember that the operation name must match the action forward name and that it is case-sensitive. Additionally, the event method handler must also match. For example:

    event="foo"

    name="event_foo"

will match an action forward named `foo` but not `Foo` or `FOO`.

The method to fire must be named `onFoo()`, with the convention that the term after "`on`" is always initial capped regardless of the event name (thus `onfoo()` is not valid and the events `foo` and Foo both fire the same method onFoo() in the data action).

Best Practice Tip: Since the event handler method does not distinguish between `foo` and `Foo` operation names, it is best to avoid names that differ only by letter capping. Otherwise, more than one operation name would fire a single event handler method.

Best Practice Tip: In the case of submit buttons, the value attribute can alternatively be used to name the operation, but this attribute could change depending on the locale and translation string. Oracle ADF data action provides a simple approach: instead of using the value attribute of the HTML element to represent the operation name to submit, alternatively use the "`event_`" prefix in the name of the button. That is, rather than writing:
    <input type="submit"
           name="event"
           value="<bean:message key='button.add.one'/>"/>
to submit an operation whose name is given in the value of the button (the label "`Add One`"), write instead:
    <input type="submit"
           name="event_Increment"
           value="<bean:message key='button.add.one'/>"/>
changing the `name="event"` to `name="event_Increment"`. This way, regardless of the value of the button (or whether that value contains spaces in the name), you'll have a reliable way to submit the event without running into issues.

Note: To learn more about how Oracle ADF event handling integrates with Struts, see the `Oracle ADF Data Binding Primer and ADF/Struts Overview` whitepaper on OTN.

You can optionally complete the following hands-on to explore concepts presented in this lesson.

## 6.8 Hands-On for Lesson 3

The following hands-on demonstrates a practical example of the Oracle ADF framework's state management and failover support for transient view objects in ADF Business Components. You can run the Oracle ADF Toy Store application inside JDeveloper's embedded OC4J container and try the following, but you must first enable failover for the ADF Business Components:

To enable failover mode for the Oracle ADF Toy Store application, select ToyStoreService in the Application Navigator and choose Configurations from the context menu. In the dialog, select the ToyStoreServiceLocal configuration and click the (Edit) button. In the Pooling and Scalability page, select the Failover Transaction State Upon Managed Release property.

Now that you have enabled failover, return to the Toy Store application:

1. Add several items to your shopping cart.

2. Without closing your browser window, terminate the OC4J application server to simulate a hardware failure on your application server machine.

   To do this, choose View > Run Manager to display the Run Manager. Find the Embedded OC4J Server process in the list, and select it. Finally, choose Terminate from the context menu.

3. Rerun the Oracle ADF Toy Store application.

After you restart the application server — in this example, we've restarted the embedded OC4J application server in JDeveloper — the browser window you left open in step 2 above will be able to continue where it left off, with all shopping cart items intact.

If you're still curious, try disabling failover support for the `ShoppingCart` component in JDeveloper:

1. In the Application Navigator, expand the toystore.model.dataaccess package of the ToyStoreModel project and double-click the ShoppingCart view object.

2. In the View Object Editor, select the Tuning page.

3. In the Tuning page, deselect Including All Transient Values.

4. Terminate the embedded OC4J process and rerun the application.

5. Return to the shopping cart page and receive the exception `javax.servlet.jsp.JspException: Missing message for key "cart.instock."`.

The page fails to render the message because `<c:set>` fails to evaluate the value expression before rendering `inStockMsgKey` from the resource bundle:

    <c:set var="inStockMsgKey"
           value="cart.instock.${Row.InStock}"/>
    <bean:message name="inStockMsgKey"/>

Failover support works because the Oracle ADF framework offers automatic database-backed state management for pending data in your application when you use ADF Business Components. In the Oracle ADF Toy Store application, the pending shopping cart information is not stored in the HTTP session state as it is in most applications. Instead, with a declarative checkbox on the `ShoppingCart` component at design time, we indicate that we'd like this component's pending data to be managed for us. And the framework takes care of the rest.