Skip Headers

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

Previous | Next |

# 5 Lesson Two: Drilling Down Into the Products

This chapter contains the following sections:

- Section 5.1, "Introduction"

- Section 5.2, "Analyzing the Products Display Page Flow"

- Section 5.3, "Storing JSP Files Under the WEB-INF Directory"

- Section 5.4, "Integrating the Model and Controller Layers"

- Section 5.5, "Laying Out the Query Results Page"

- Section 5.6, "Conditionalizing the Display"

- Section 5.7, "Hands-On for Lesson 2"

## 5.1 Introduction

We began our decomposition of the Oracle ADF Toy Store web store application with its home page. In Lesson 1, we decided users should be able to immediately initiate product searches and begin browsing the catalog without needing to log in or accept application cookies. Now we are ready to create JSP pages to support the product search and category display home page operations. To accomplish this, we will need to perform several tasks:

- Extend the Struts page flow to display products of interest and allow the user to drill down for individual product details.

- Ensure that the business model supports drilling down to display the product details.

- Look at common design patterns for displaying categories, products, and product details.

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

## 5.2 Analyzing the Products Display Page Flow

The page flow from the home page contains two branches: `search` and `showcategory`. As we would expect, separate action forwards correspond to separate JSP pages:

- The `search.jsp` page as the target of the `search` forward must iterate over matching products and display feedback when no product name matches the supplied query string.

- The `category.jsp` page as the target of the `showcategory` forward needs only to iterate over the list of products that match the supplied category `id`.

Following these two pages, we'll want to display the drilldown page where the user can view actual product details before deciding whether to update their cart with a product selection. The new page flow, with the drilldown data page /showproductdetail, is shown in Figure 5-1, "Product Display Page Flow".

Figure 5-1 Product Display 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.

## 5.3 Storing JSP Files Under the WEB-INF Directory

To protect your JSP pages from the user typing a URL to directly access or view any page, store your pages in a subfolder below the web application's `WEB-INF` directory. Based on the servlet specification, `WEB-INF` is not part of the public document tree of the web application. Therefore, no resource within the `WEB-INF` directory, or its subfolders, may be served directly to a client. If you were to save the files under the public tree, at the web application root level, the user could bypass the Struts controller by invoking a JSP directly, which would mean being served pages with no data binding.

Once the JSPs appear below the `WEB-INF` directory, you must use "`WEB-INF`" as part of the URL when referencing the pages. For example, in our Struts configuration file, the action mapping for `showcategory` specifies the `showcategory.jsp` parameter by its complete path:

    <action path="/showcategory" ...attributes not shown....
            parameter="/WEB-INF/jsp/showcategory.jsp" unknown="false">

To take advantage of hiding the JSP pages under `WEB-INF`, you must always invoke your JSP pages with a Struts `.do` action request, even if the action is a very basic JSP. Additionally, be aware when storing your JSP pages below `WEB-INF` that not all web containers support this feature. Be sure to check your specific container.

## 5.4 Integrating the Model and Controller Layers

We already know that different pages will be required to handle the search, category list, and product details, and we know that the query parameters used to find matches are different (product names for searches, and category-unique IDs for category and product links), but underlying all three pages is the need to prepare a query object with the supplied bind parameter and to iterate over the result set. Iterating over the result set is something we'll look at in the page descriptions below. However, in an MVC web application that uses Struts and Oracle ADF to access the model layer, we separate the code needed to prepare the query and create a result set from the presentation layer.

The task of accessing the model layer is initiated in the Oracle ADF data action class, which we described in Lesson 1. Thus, the presentation layer need not have any awareness of how the data is accessed. The data action gets executed like any other Struts action class, except that it provides methods you can use to operate on the model layer. Take a look at these action mappings from the Struts configuration file:

    <action-mappings>
       <action path="/showcategory"
                className="oracle.adf.controller.struts.actions.DataActionMapping"
                type="toystore.controller.strutsactions.ShowCategoryAction"
                name="DataForm" parameter="/WEB-INF/jsp/showcategory.jsp"
                unknown="false">
                <set-property property="modelReference"
                              value="WEB_INF_jsp_showcategoryUIModel"/>
                <forward name="showproduct" path="/showproduct.do" />
       </action>   <action path="/showproduct"
                className="oracle.adf.controller.struts.actions.DataActionMapping"
                type="toystore.controller.strutsactions.ShowProductAction"
                name="DataForm" parameter="/WEB-INF/jsp/showproduct.jsp"
                unknown="false">
                <set-property property="modelReference"
                              value="WEB_INF_jsp_showproductUIModel"/>
                <forward name="addToCart" path="/yourcart.do" />
                <forward name="showProductDetails" path="/showproductdetails.do"/>
        </action>
    ...
        <action path="/showproductdetails"
                className="oracle.adf.controller.struts.actions.DataActionMapping"
                type="toystore.controller.strutsactions.ShowProductDetailsAction"
                name="DataForm" parameter="/WEB-INF/jsp/showproductdetails.jsp"
                unknown="false">
                <set-property property="modelReference"
                value="WEB_INF_jsp_ showproductdetailsUIModel"/>
                     <forward name="addToCart" path="/yourcart.do"/>
         </action>
    ...
    </action-mappings>

The `type` attribute informs each of our three actions about which class will be executed on the action request. To view the implementation of the data action, return to the page flow diagram, right-click the /showcategory data page and choose Go to Code. Our action classes, like the one shown in the source editor, allow the application to do some specific work before the Oracle ADF lifecycle prepares the data bindings for the model layer. In our case, we want to create a query object by setting bind variables passed from the invoking JSP page.

We come upon another important design decision at this stage: we can either implement logic to set up the queries on business object data within each data action, or we can invoke service method on our business objects. The latter approach is recommended as a best practice, because it allows the business objects to encapsulate the implementation details of the service, it keeps the controller layer very thin, and it permits the developer to create simple JUnit tests to test the business objects. The data action only implements a custom method `initializeModelForPage()` to retrieve the user-supplied parameters from the `HttpServletRequest`. This custom method, in turn, passes the parameter as an argument to the `prepareToShowProductDetails()` method on our `ToyStoreService` business service interface. The `prepareToShow` `Xxx``()` service methods, in turn, execute the query with the correct binding values before the Struts controller serves the JSP page.

Troubleshooting Tip: Bind variables must be set before the Oracle ADF lifecycle's `prepareModel()` phase is initiated. Otherwise, exception `JBO-27122: SQL error during statement preparation` will be caused, because the query will not be completed. To avoid this error for this common use case (setting bind variables supplied by the user), it is recommended that you override the `prepareModel()` phase of the Oracle ADF lifecycle to include the custom method `initializeModelForPage()`. For an extended discussion of how to customize the Oracle ADF lifecycle for this purpose, see the section "A Page Showing Results of a Query with Bind Variables" in the `Building a Web Store with Apache Struts and Oracle ADF Frameworks` whitepaper.

Best Practice Tip: Business services developers will want to investigate further the benefits of exposing service methods on the application's business objects. By defining these methods outside of Struts and the web container context, it is possible to take advantage of the JUnit testing framework support in JDeveloper (available as a small, separate download, as explained in Section 2.6, "Installing the Oracle JDeveloper JUnit Extension"). One of the wizards available in the Unit Tests (JUnit) category of the New Gallery allows you to create a skeleton Business Components test suite. This wizard allows you to pick an application module component, and a particular configuration that you'd like to use for testing, and then to generate: A JUnit test fixture that encapsulates getting an instance of the desired application module A sample JUnit test case class which uses this fixture and asserts that all expected view object instances exist in the application module's model data map A JUnit test class that runs all of the test cases (initially just one)

The above wizard and the built-in JDeveloper refactoring tools were used to create the Oracle ADF Toy Store application packages containing the test runner, the unit test cases, and the test fixture as separate packages. These packages were created to exercise the `toystore.model.services.ToyStoreService` application module component. In the JDeveloper Application Navigator, expand the Testing project node and the ADFToyStore application node to view the supplied test suite.

## 5.5 Laying Out the Query Results Page

At runtime, the action mapping for the home page action specifies one of two outcomes: a `/search` data page (`search.do`) or a `/showcategory` data page (`showcategories.do`). As has already been described, the data action for each prepares the model before the web page is displayed in the browser.

The task of rendering the databound page is left to the four tag libraries:

- The Struts Bean tag library, whose tags are prefixed by `bean:`

- The Struts HTML tag library, whose tags are prefixed by `html:`

- The JSTL Core tag library, whose tags are prefixed by `c:`

- The Oracle ADF tag library, whose tags are prefixed by `adf:`

Again, we use the Struts Bean tag library `<bean:message>` tag to include translatable text strings into our pages, based on string keys like `category.productid`, `category.productname`, or the scriptlet-derived `<%= request.getParameter("id") %>`, where `id` corresponds to the category name set up by the URL selection in the home page.

We see the `<html:errors>` tag from the Struts HTML tag library, which makes it easy to display any errors that occur during runtime processing at the top of the page in a standard way.

However, the real work of pulling the data from the bindings in the Oracle ADF model layer is done with the aid of the JSTL Core tag library. The HTML table element provides the formatting for the displayed rows of data generated by the JSTL tags. This common presentation layer use case looks like this in the `showcategory.jsp` page:

    <table id="categorydata" border="0" bgcolor="#003399">
       <tr>
         <th><font color="white" size="3"><bean:message key="category.productid"/>
             </font>
         </th>
         <th><font color="white" size="3"><bean:message key="category.productname"/>
             </font>
         </th>
       </tr>
       <c:forEach var="Row" items="${bindings.ProductsInCategory.rangeSet}" >
          <tr bgcolor="#f3f3f3">
             <td><c:out value="${Row.Productid}" /></td>
             <td>
                <a href="<c:url
                     value='showcategory.do?event=showproduct&id=${Row.Productid}'/>">
                   <c:out value="${Row.Name}" />
                </a>
             </td>
          </tr>
       </c:forEach>
    </table>

Specifically, the `<c:forEach>` tag iterates over our data collections and the `<c:out>` tags display values for their attributes in the page. Embedded within the `<c:out>` tags is the first use of tag attribute expressions to access objects in the Oracle ADF data binding context. These expressions begin with a variable "`Row`", which is defined by the evaluated expression `${bindings.ProductsInCategory.rangeset}`. The namespace identifier `bindings` locates the Oracle ADF binding context and its objects, `ProductsInCategory` is the name of the table bindings, and `rangeSet` refers to a property of the table binding which supplies access to the individual rows of the bound business object.

To review, Oracle ADF bindings are lightweight objects that decouple back-end data and front-end UI display:

- An iterator binding lets your pages work with a collection of business objects supplied by the Oracle ADF model layer.

  In the `showcategory.jsp` page, the iterator `ProductsInCategoryIterator` serves this purpose. The iterator binding itself is not referenced in the page, but is part of the definition for the control bindings specified in the `UIModel.xml` file for the page.

- Control bindings provide a standard interface for display components in the page to interact with an iterator's data or to invoke "action" methods for preparing model data and handling events.

  In the `showcategory.jsp` page, we see an Oracle ADF table binding referenced by `bindings.ProductsInCategory`, which defines which attributes to display. The binding container for this JSP page also contains two action bindings [`next` and `previous` (but, as we'll see, these are actually invoked through a JSP page we will include [`pagingControl.jsp`).

The important point to consider is that this common presentation layer use case (iterating over your data and formatting the output using standard tags) is done without scriplet code to manipulate the model layer directly in the JSP page. For example, in the Oracle ADF Toy Store application, using just JSTL tags and HTML elements, we see the same pattern repeated in the `search.jsp`, `showcategory.jsp`, and `showproduct.jsp` pages.

Let's examine another example of how Oracle ADF control bindings access the model layer. Open the `showproductdetails.jsp` page in the JDeveloper source editor. In the source for that page, notice that we do not use the previously described Oracle ADF table binding and `<c:forEach>` loop because the HTML table displays only a single row (information for a single product). Instead, the page source contains a series of `<c:out>` tags and attribute expressions that directly specify the namespace identifier bindings to locate the Oracle ADF binding context and its objects:

    ...
    <c:out value="${bindings.Name}"/>
    ...

The reference following `bindings` in these expressions is the name of the Oracle ADF attribute binding which identifies the bound attribute of the business object. Even without the `<c:forEach>` loop and the `Row` variable assignment previously seen in the source for `showcategory.jsp`, the attribute bindings of `showproductdetails.jsp` can still obtain the attribute value corresponding to the current row of the underlying business object. This works in showproductdetails.jsp because control bindings are always bound to the current row of the business object through an iterator binding. So, retrieving the values on these attribute bindings will automatically retrieve the values on the business object row with the current focus. In this way, coordination between the presentation layer and the model layer is managed for you through the Oracle ADF iterator and control bindings.

In the hands-on for this lesson, we'll see exactly how easy it is to add databound controls to your JSP in JDeveloper.

## 5.6 Conditionalizing the Display

The page `search.jsp` relies on another set of tags from the JSTL Core tag library worth examining: `<c:choose>` , `<c:when>` , and `<c:otherwise>`. The search page uses these tags like an if / then / else statement to conditionalize the display based on whether the search result contains matching products or not. Here is an excerpt from `search.jsp`:

       <c:choose>
         <c:when test="${not empty bindings.FindProducts.rangeSet}">
           <table border="0" bgcolor="#003399" >
              <tr>
                 <!-- displays table header columns here  -->
              </tr>
              <c:forEach var="row" items="${bindings.FindProducts.rangeSet}" >
                 <tr>
                    <!-- displays table body here  -->
                 </tr>
              </c:forEach>
           </table>
          </c:when>
          <c:otherwise>
            <br><br><bean:message key="search.nomatchingproducts"/>
          </c:otherwise>
       </c:choose>

You may notice the `<adf:render>` tag from the Oracle ADF tag library (omitted in the above excerpt), which appears in the third column definition of the product items table. This tag displays and formats attribute data based on language-sensitive format masks that you can define in your Oracle ADF Business Components project.

Troubleshooting Tip: Each Oracle ADF Toy Store web page includes an `<html:errors>` tag at the top. By default, the Oracle ADF data action bundles up all exceptions that occur during the request processing lifecycle and translates 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 we've placed the `<html:errors>` tag(s). However, a failure to include any `<html:errors>` tag in your page will result in errors being reported to the Struts layer, but never displayed. This means that the possible unexpected error will show up only if you explicitly render the Struts errors using the `<html:errors>` tag. When you work with the Data Control Palette in JDeveloper, pages you create will include the `<html:errors>` tag. However, in the event that you create a page entirely in the source code editor, be aware of the need for this tag.

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

## 5.7 Hands-On for Lesson 2

The following hands-on shows how you can easily modify Oracle ADF bindings in your JSP page.

1. In the Application Navigator, expand the Web Content/WEB-INF/jsp folder of the ToyStoreViewController project and double-click the showproduct.jsp file.

2. In the design view of the open file, right-click inside the table cell with the JSTL element for `${Row.Name}` and choose Table > Split Cell.

3. In the Split Cells dialog, enter the value `2` for the number of columns to create and click OK. You should see a new empty cell to the right of the JSTL `${Row.Name}` table cell.

4. Click the Source tab of the visual editor and locate the inserted `<td>&nbsp;</td>` for the new table cell.

5. Replace `&nbsp;` with the JSTL tag `<c:out value="${Row.InStock}"`. Be sure to use the correct case when naming the binding (`InStock`) since binding names are case-sensitive.

6. In the header section of the HTML table element, insert the header for the new column: `<th><font color="white" size="3"><bean:message key="cart.availability"/></font></th>`. The new header should appear after the header defined by the binding `${bindings.ItemsForSale.labels.Name}`.

7. With the `showproduct.jsp` page displayed, open the Structure window and select the UI Model tab to view the list of bindings used by this page. (Note that the current 10.1.2 Toy Store project contains extra unused bindings: Name and Listprice. These two bindings may be safely deleted by choosing Delete from their context menus.)

8. Double-click the ItemsForSale table binding to display the binding editor.

9. In the binding editor, move the attribute InStock from the list of Available Attributes to the list of Display Attributes and click OK.

10. From the Struts page flow diagram, right-click the home.do action and choose Run to launch the application. Click on any category in the home page. The Availability column you added appears in the products in the category page.

The following hands-on shows how you can easily create a new Oracle ADF binding in your JSP page.

1. In the Application Navigator, expand the Web Content/WEB-INF/jsp folder of the ToyStoreViewController project and double-click the showproductdetails.jsp file.

2. In the design view of the open file, right-click inside the table cell with the JSTL element for `${bindings.Name}` and choose Table > Split Cell.

3. In the Split Cells dialog, enter the value `2` for the number of columns to create and click OK. You should see a new empty cell to the right of the JSTL `${bindings.Name}` table cell.

4. With the `showproductdetails.jsp` page displayed, open the Data Control Palette and expand the ToyStoreService data control to display ProductList and ItemsForSale.

5. Locate the Productid attribute node under ItemsForSale and drag the attribute node into the new table cell.

6. Return to the UI Model tab displayed in the page's Structure window and view the new Productid attribute binding.

   Note: As an alternative to using the Data Control Palette, you can also work with the UI Model tab to create bindings (using the right-click context menu) and then reference these bindings using JSTL tags in the page source. However, when you work with the Data Control Palette, the IDE performs these steps for you.

7. Display the source view of the `showproductdetails.jsp` page and examine the new table cell definition: `<c:out value="${bindings.Productid}"/>`.

8. From the Struts page flow diagram, right-click the home.do action and choose Run to launch the application. Click any category in the home page. Select any product link. The product ID you added appears in the products information page.

The next lesson describes available Oracle ADF binding metadata that allow you to customize the behavior of bindings.