Skip Headers

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

Previous | Next |

# 7 Lesson Four: Processing the Shopping Cart Order

This chapter contains the following sections:

- Section 7.1, "Introduction"

- Section 7.2, "Analyzing the Proceed to Checkout Flow"

- Section 7.3, "Laying Out the Review Checkout Page"

- Section 7.4, "Laying Out the Confirm Shipping Information Page"

- Section 7.5, "Hands-On for Lesson 4"

## 7.1 Introduction

With the ability to add products to the cart, as described in Lesson 3, we're ready to extend our page flow to take the customer's shipping information and provide confirmation of their order. To accomplish this, we will examine the structure of two more JSP pages:

- `reviewcheckout.jsp`, to display the customer's order for confirmation

- `confirmshippinginfo.jsp`, to let the customer enter the shipping information and payment method

In this lesson, we'll examine the layout of these two new JSP pages.

## 7.2 Analyzing the Proceed to Checkout Flow

The page flow proceeds with the action forward `reviewcheckout` from the `/yourcart` data page. The forward destination is the `/reviewcheckout` data page. The `/reviewcheckout` data page's only function is to let customers preview their final order. The only event possible from this page is `confirmshippinginfo`, which maps to the next forward and causes the `confirmshippinginfo.jsp` page to be displayed. The page flow now includes a new data page for each task and is shown in Figure 7-1, "Proceed to Checkout Page Flow".

Figure 7-1 Proceed to Checkout Page Flow

We'll reserve the action mapping discussion for the `confirmshippinginfo.do` action until the next lesson, when we also describe the target pages in the flow diagram.

Using the page flow modeler, in actual practice, proceeds roughly as follows:

1. Identify the pages and actions of your flow.

2. Identify the events (and action forwards) of your flow.

3. Using the Component Palette, drop the page source and target into the diagram area of the Struts Page Flow Modeler.

4. Using the Component Palette, drop an action forward element (or page link) to connect the source and target elements.

## 7.3 Laying Out the Review Checkout Page

At runtime, the `reviewcheckout` forward initiated from the `yourcart.jsp` page executes the mapping for the `/reviewcheckout` data page. In this case, the data action corresponding to this page performs no model initialization. Unlike the previous pages, where model initialization was necessary to prepare data for the page to display, the `reviewcheckout.jsp` page will only access existing model objects. The purpose of this page is merely to pull data from the existing `ShoppingCart` object and present it in a concise, readable table.

Specifically, the `reviewcheckout.jsp` page uses the `<c:forEach>` tag to iterate over the Oracle ADF table binding bound to the `ShoppingCart` object through the `ShoppingCartIterator` binding. Data for the table rows is displayed using a variety of tags that take input from the table binding's current `Row`:

When the attribute to be displayed for the current row does not use a formatter to render the data, we use the standard `<c:out>` tag:

    <td><c:out value="${Row.Itemid}" /></td>
    <td><c:out value="${Row.Name}" /></td>
    ...
    <td align="center"><c:out value="${Row.Quantity}" /></td>

When the attribute to be displayed relies on language-sensitive format masks that were defined in the business components, we use the Oracle ADF tag `<adf:render>`:

    <td align="right"><adf:render model="Row.Listprice" /></td>
    <td align="right"><adf:render model="Row.ExtendedTotal" /></td>

Finally, when we want to render a translatable string based on single-character flag values, such as "Y" or "`N`" for the `InStock` attribute, we use the Struts tag `<bean:message>`:

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

The `ShoppingCart` object contains a simple transient field named `InStock`, which takes the value either `Y` (yes, in stock) or `N` (no, not in stock) to indicate whether the item is available. When the `reviewcheckout.jsp` page displays the `InStock` information, rather than showing the raw Y or N value, we use the Y or N as part of the string key name for a translatable string in the Struts message resource file. The above code first uses the JSTL `<c:set>` tag to set a local page variable named `inStockMsgKey` to the value of `cart.instock` concatenated to the value of the `InStock` field in the current `Row` of the `<c:forEach>` loop, and then it uses `<bean:message>` to display the translated string based on either the `cart.instock.Y` or `cart.instock.N` message key value in that `inStockMsgKey` object. This way, the user can see a meaningful indicator of availability in the language specified by the browser preference setting. For instance, the Y can display as `In Stock` in English or `In Magazzino` in Italian.

The last row of the table displays a double-valued attribute, `CartTotal`, using the `<bean:write format="$0.00">` tag:

    <bean:write format="$0.00" name="CartTotal"/>

Note: The decimal formats `$0.00` and `#,##0.00` are equivalent, and either may be used to represent the currency value.

The value of `CartTotal` is made available to the page when `invokeCustomMethod()` is executed on the data action:

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

This use of the `<bean:write>` tag with format attribute illustrates an alternative to the `<adf:render>` approach described for the `Listprice` and `ExtendedTotal` attribute values. With `<adf:render>`, format masks are provided as hints on the Oracle ADF business object's attributes in the model layer. With `<bean:write>`, the format masks are hard-coded into the pages (or with the `<bean:write>` `formatKey` attribute, they can be specified as translatable message keys).

Once the customer is satisfied with the order, they check out by triggering the `confirmshippinginfo` forward. The Continue button sends the event `confirmshippinginfo`:

    <a href="<c:url value='reviewcheckout.do?event=confirmshippinginfo'/>"
                 ><img src="<bean:message key='images.buttons.continue'/>"
                        alt="Continue" border="0"></a>

As we saw in Lesson 3, again the action forward `confirmshippinginfo` (shown in the page flow diagram) and the event share the same name. Although the data action doesn't explicitly set the action forward, the Oracle ADF lifecycle will automatically seek a matching forward and find `confirmshippinginfo` to perform declarative navigation.

## 7.4 Laying Out the Confirm Shipping Information Page

The main design feature of the `confirmshippinginfo.jsp` page is the use of a Struts form (`<html:form>` tag) to populate the form and accept user input. The fields of the form are also Struts HTML elements, which access the properties of the form bean:

- `<html:text>`, where the `property` attribute is the name of the Oracle ADF attribute binding that accesses the desired business object

- `<html:select>`, where the `property` attribute is the name of the Oracle ADF list binding that accesses the desired business object and `<html:optionsCollection>` populates a poplist

The `<html:select>` and `<html:optionsCollection>` tags work with Oracle ADF list bindings to populate the poplist in the page. The `<html:select>` tag is bound to a property of the form bean, which shares the same name as the list binding object (`Cardtype` and `ExprYear`). The `<html:optionsCollection>` tags get their data from the nested, list-valued `displayData` property of the Oracle ADF list binding. The beans in these display data collections each have a `prompt` and an `index` property, so we indicate to use those as the label and value (respectively) for each option in the list. In this sample, we show the selection list for the charge card type (corresponding to the `Cardtype` bean and binding object):

    <html:select property="Cardtype" >
           <html:optionsCollection label="prompt" value="index"
                                   property="Cardtype.displayData" />
    </html:select>

Note: For bandwidth optimization, the Oracle ADF binding layer expects the nonvisible values of the Oracle ADF list binding to be the zero-based `index` number in their `displayData` collection. The Oracle ADF list binding handles translating the underlying list of values (like `IT` for a country code) into index positions (like `86`) on both read and write of the binding value.

In Oracle ADF, the HTML form is tied to the associated action, which is tied to the data form bean. At runtime, the HTML form relies on the data form bean to resolve the properties corresponding to the Oracle ADF binding objects and thereby to display available form attribute values:

1. First, the data form bean asks the Oracle ADF binding container (`BindingContainerActionForm`) whether it has a binding with a name matching the form attribute.

2. Next the Oracle ADF binding container returns the binding if it exists; and, finally, the data form bean populates the HTML form attributes with that binding's value.

Similarly, when the user clicks the Continue button to submit the form with its data, thereby submitting the `placeOrder` event, the Oracle ADF binding container collects the form attribute values that Struts has set on it; then, during the `processUpdateModel()` phase of the lifecycle, the binding container uses those values to update the Oracle ADF binding objects with matching names.

However, if the user submits the form and validation errors in the model layer are thrown, when this page is rendered again, the `<html:errors>` tag will ensure that errors related to the attributes will show up next to the fields.

Note: The Oracle ADF business object that represents a user account in the model layer is declaratively enforcing mandatory attributes, reusing a custom business rule to validate the country and state combination, using a built-in validation rule to enforce uniqueness of the primary key attribute, and validating the correct formatting of email addresses using a custom `Email` datatype. All of the custom error messages are localized to the current browser user's locale (based on language + territory). None of this behavior requires developer-written code to coordinate.

Best Practice Tip: By default, the Oracle ADF data actions bundle all exceptions that occur during the request processing lifecycle and translate them at the end of the request (during the lifecycle's `reportErrors()` phase) to the Struts layer as Struts `ActionError` objects. This means that business validation errors that occur during the processing of the lifecycle neatly appear on the page, wherever you've placed the `<html:errors>` tags. However, a failure to include any `<html:errors>` tag in your page will result in the errors being reported to the Struts layer, but never displayed. This means that even an unexpected error will show up only if you explicitly render the Struts errors using the `<html:errors>` tag. When you work with the JDeveloper Data Control Palette, you will see `<html:errors>` tags in your page, but if you develop your pages in a more manual way, be aware that you must add these tags yourself.

The form includes the standard hidden field that the Oracle ADF controller layer uses to detect whether the user has tried to submit the same form multiple times in rapid succession:

    <input type="hidden" name="<c:out value='${bindings.statetokenid}'/>
           "value="<c:out value='${bindings.statetoken}'/>"/>

The token prevents the data action from processing the same request multiple times should the user page back and forward.

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

## 7.5 Hands-On for Lesson 4

The following hands-on shows how Oracle ADF Business Components supports the use of validation domains to specify an attribute datatype to perform custom validation on the databound attribute.

1. In the Application Navigator, locate toystore.model.datatypes, right-click, and choose New Domain.

2. In the Create Domain wizard, click Next and enter the name `Phone` for the domain name. Leave all other options unchanged and click Finish.

3. In the Application Navigator, right-click the new domain PhoneNumber and choose Go to Domain Class to open the `Phone.java` template you will modify.

4. In the open source for `Phone.java`, replace the stub `validate()` method with the following validation code:

       protected void validate() {
           if (!isEightCharacters()) {
             throw new DataCreationException(ErrorMessages.class,
               ErrorMessages.INVALID_PHONENUMBER, null, null);
           }
         }
         private boolean isEightCharacters() {
           if (mData != null) {
             if (mData.length() != 8) {
               return false;
             } else {
               return true;
             }
           }
           return false;
        }

5. Add the following import statement to the list of imports:

       import oracle.jbo.domain.DataCreationException;

6. Make no other changes to the file and choose File > Save.

7. In the Application Navigator, expand toystore.model.datatypes.common and double-click ErrorMessages.java to add the error message for `INVALID_PHONENUMBER` specified in `Phone.java`.

8. In the open source for `ErrorMessages.java`, add this declaration to the list of constants:

       public static final String INVALID_PHONENUMBER = "20005";

9. To the list of error messages add:

       { INVALID_PHONENUMBER, "Phone number must be eight characters including dash." },

10. In the Application Navigator, expand toystore.model.dataaccess, right-click Accounts, and choose Edit Accounts.

11. In the View Object Editor, select Attributes to display the list of available and selected attributes. Select Phone in the Selected Attributes list and click the Remove arrow button to return it to the Available Attributes list. This step is necessary to remove the dependency of the view object on this attribute before we can apply the new domain type. Click OK to save the changes.

12. In the Application Navigator, expand toystore.model.businessobjects, right-click Account, and choose Edit Account.

13. In the Entity Object Editor, expand Attributes and select Phone from the list.

14. In the Entity Attribute tab, display the Type dropdown list and choose toystore.model.datatypes.common.Phone. If the new datatype is not displayed, you can also select Import Domain from the list to locate the domain. Click OK to save the changes and exit the editor.

15. In the Application Navigator, expand toystore.model.dataaccess, right-click Accounts and choose Edit Accounts.

16. In the View Object Editor, select Attributes to display the list of available and selected attributes. Select Phone in the Available Attributes list and click the Add arrow button to return it to the Selected Attributes list. Click OK to save the changes and exit the editor.

17. Right-click the home.do action and choose Run to launch the application. Click any category in the home page. Select any product link and add it to your cart. Proceed to checkout. When asked to sign in, click the Register as a New User link instead. Complete the form and enter an intentionally short phone number. Then click Submit. The data action redisplays the page with the validation error you created for the phone number field.

Validation domains add business logic to every attribute that uses a validation domain as its type. Domain validation occurs when an object of that domain type is created. The data object can then be passed between the tiers without the need for reconstruction or revalidation. To implement domain-level validation, you create a new domain type and add code to the `validate()` method. Once the domain has been created, you can assign it as the type of an attribute.