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

Previous
Previous
Next
Next
 

7 Lesson Four: Processing the Shopping Cart Order

This chapter contains the following sections:

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:

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

This image shows the page flow described in lesson 4.

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:

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.