Skip Headers
Oracle® Application Development Framework Case Manual
10g Release 2 (10.1.2)  
B19163-01
  Go To Documentation Library
Library
Go To Product List
Product
Go To Table Of Contents
Contents

Previous
Previous
Next
Next
 

6 Lesson Three: Assembling the Shopping Cart

This chapter contains the following sections:

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:

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

This image shows the page flow described in lesson 3.

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:

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.