Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3)
B25386-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

1.2 Development Process with Oracle ADF and JavaServer Faces

In this section, describes a simple example to acquaint you with the typical development process of building an Oracle ADF application with JavaServer Faces. This information is intended as a high-level overview of the basic workflow of J2EE application building with Oracle ADF.

1.2.1 Overview of the Steps for Building an Application

Our example is based on a highly-simplified version of the Service Request tracking system (the SRDemo sample), the real-world sample application used throughout the remainder of this guide. In the Service Request tracking system, external users log requests for technical assistance with products they've purchased. Internal users try to assist the customers in the area in which they have expertise. This introduction focuses on the basics, and examines a small slice of the system's functionality related to users and their areas of technical expertise.

You'll examine the steps involved in building a simple JSF page like the one you see in Figure 1-5 that allows the end user to browse for users by name, scroll through the results, and for each user found, see their areas of technical expertise.

Figure 1-5 Simple Browse Users Page with Search and Master/Detail Data

Simple browse users page with search and master/detail data

1.2.1.1 Starting by Creating a New Application

The first step in building a new application is to assign it a name and to specify the directory where its source files will be saved. Selecting Application from the JDeveloper New Gallery launches the Create Application dialog shown in Figure 1-6. Here you give the application a name, set a working directory, and provide a package prefix for the classes you'll create in the application. Suppose that you enter a package prefix of oracle.srdemo so that, by default, all of the classes will be created in packages whose names will begin with oracle.srdemo.*. Since you will be building a web application using JSF, EJB, and TopLink, Figure 1-6 shows the corresponding application template selected from the list. This application template is set up to create separate projects named Model and ViewController with appropriate technologies selected to build the respective layers of the application.

Figure 1-6 Creating a New Application Using an Application Template

creating a new application using an application template

1.2.1.2 Building the Business Service in the Model Project

You will typically start by building your business service interface, which by default is done in the project named Model. Your Model project will comprise an EJB 3.0 session bean to function as the service facade, and Java classes that represent the business domain objects you need to work with. The model doesn't need to be functionally complete to proceed on to the subsequent steps of developing the UI, but defining the service interface forces you to think about the data the view layer will need and the information it may need to supply as parameters to your service methods to complete the job. Since you'll want to work with users and areas of expertise, the Java classes named User and ExpertiseArea are created. Each class will contain properties to reflect the data needed to represent users and areas of expertise.

Based on the requirements, suppose the business service needs to support finding users by name. For this purpose, you can use the EJB Session Bean wizard to create a stateless EJB 3.0 session bean using a container-managed transaction. Next you can add a method called findUsersByName() to its local interface that accepts the matching pattern as a parameter called name. To clearly communicate the type of the result and obtain the best compile-time type checking possible, it is best practice to declare the return type of the method to be List<User>, a strongly typed list of User beans. Finally, you can write the method in the SRServiceBean class that implements the service interface. Figure 1-7 shows what the service and its classes look like in the Java class diagram in JDeveloper. You can see that the class also contains the useful findAllUsers() method to return all users if needed.

Figure 1-7 SRService Session Bean Facade and Supporting Domain Classes

SRService Session Bean Façade and supporting domain classes

Because of the clean separation that ADF Model affords between the service and the user interface, the remaining steps to build the page depend only on the service interface, not its implementation. You can begin with a service that returns static test data, but eventually you will want to map the User and ExpertiseArea classes to appropriate tables in your database. This is where Oracle TopLink excels, and JDeveloper's integrated support for configuring your TopLink session and mappings makes quick work of the task. If you already have database tables with similar structure to your classes, Oracle TopLink can "automap" the classes to the tables for you, and then you can adjust the results as needed. If the database tables do not exist yet, you can use JDeveloper's database diagrammer to design them before performing the mapping operation. To implement the findUsersByName() method, you will create a named query as part of the User mapping descriptor and provide the criteria required to retrieve users matching a name supplied as a parameter. At runtime, the Oracle TopLink runtime handles retrieving the results of the parameterized query from the database based on XML-driven object/relational mapping information.

1.2.1.3 Creating a Data Control for Your Service to Enable Data Binding

With the business service in place, you can begin to think about building the user interface. The first step in enabling drag-and-drop data binding for the business service is to create a data control for it. Creating the data control publishes the service interface to the rest of the Oracle ADF Model design time using JSR-227 service and structure descriptions. To create a data control, you just drag the SRServiceBean class onto JDeveloper's Data Control Palette. Figure 1-8 shows the Data Control Palette following this operation. You can see it reflects all of the service methods, any parameters they expect, and the method return types. For the findUsersByName() method, you can see that it expects a name parameter and that its return type contains beans of type User. The nested email, lastName, and expertiseAreas properties of the user are also displayed. Since expertiseAreas is a collection-typed property (of type List<ExpertiseArea>), you also see its nested properties. The Operations folder, shown collapsed in the figure, contains the built-in operations that the ADF Model layer supports on collections like Previous, Next, First, Last, and so on.

Figure 1-8 Data Control Palette Displays Services Declarative Data Binding

Data Control Palette shows services declarative data binding

As you build your application, when you add additional methods on your service interface or change existing ones, simply drag and drop the SRServiceBean class again on to the Data Control Palette and the palette—as well as its underlying data binding metadata—will be refreshed to reflect your latest changes. The data control configuration information resides in an XML file named DataControls.dcx that JDeveloper adds to your Model project when you create the first data control. If you create multiple data controls, the information about the kind of data control they are (for example EJB, JavaBean, XML, Webservice, and so on.) and how to construct them at runtime lives in this file. In addition, JDeveloper creates an XML structure definition file for each data type involved in the service interface in a file whose name matches the name of that data type. For an EJB service interface, this means one structure definition file for the service class itself, and one for each JavaBean that appears as a method return value or method argument in the service interface.

These structure definition files drive the Data Control Palette display and come into play when you leverage the declarative, model-layer features like validators, prompts, tooltips, format masks, and declarative security. Since you defined these features at the model layer in these structure definition files, all your pages that present information related to these types display and validate the information in a consistent way. Figure 1-9 shows all of these files in the Model project in the Application Navigator after the data control for SRServiceBean has been created.

Figure 1-9 Service Classes and Data Control Metadata Files in Model Project

service classes, data control metadata in model project

1.2.1.4 Dragging and Dropping Data to Create a New JSF Page

With the data control created, you can begin doing drag-and-drop data binding to create your page. Since you'll be using ADF Faces components in your page, you first ensure that the project's tag libraries are configured to use them. Double-clicking the ViewController project in the Application Navigator brings up the Project Properties dialog where you can see what libraries are configured on the JSP Tag Libraries page. If the ADF Faces Components and ADF Faces HTML libraries are missing, you can add them from here.

Figure 1-10 Configuring ViewController Project Tag Libraries to Use ADF Faces

configure ViewController project tag libraries for ADF Faces

Next, you use the Create JSF JSP wizard to create a page called browseusers.jspx. You may be more familiar working with JSP pages that have a *.jsp extension, but using a standard XML-based JSP "Document" instead is a best practice for JSF development since it:

  • Simplifies treating your page as a well-formed tree of UI component tags

  • Discourages you from mixing Java code and component tags

  • Allows you to easily parse the page to create documentation or audit reports

When the Create JSF JSP wizard completes, JDeveloper opens the new page in the visual editor. From there, creating the databound page shown in Figure 1-11 is a completely drag-and-drop experience. As you drop elements from the Data Control Palette onto the page, a popup menu appears to show the sensible options for UI elements you can create for that element.

Figure 1-11 Browse Users JSF Page in the Visual Designer

browse users JSF page in the visual designer

The basic steps to create this page are:

  1. Drop a panelHeader component from the ADF Faces Core page of the Component Palette onto the page and set its text attribute in the Property Inspector to "Browse Users and Expertise Areas".

  2. Drop the findUsersByName() method from the Data Control Palette to create an ADF parameter form. This operation creates a panelForm component containing the label, field, and button to collect the value of the name parameter for passing to the method when the button is clicked.

  3. Drop the User return value of the findUsersByName() node from the Data Control Palette to create an ADF read-only form. This operation creates a panelForm component containing the label and fields for the properties of the User bean.

  4. Expand the Operations folder child of the User return value in the Data Control Palette and drop the built-in Previous operation to the page as a command button. Repeat to drop a Next button to the right of it.

  5. Drop the expertiseAreas property nested inside the User return value in the Data Control Palette as an ADF read-only table. Select Enable Sorting in the Edit Table Columns dialog that appears to enable sorting the data by clicking on the column headers.

At any time you can run or debug your page to try out the user interface that you've built.

1.2.1.5 Examining the Binding Metadata Files Involved

The first time you drop a databound component from the Data Control Palette on a page, JDeveloper will create the page definition file for it. Figure 1-12 shows the contents of the browseusersPageDef.xml file in the Structure window. You can see that an action binding named findUsersByName will be created to invoke the service method of the same name. Iterator bindings named findUsersByNameIter and expertiseAreasIterator will be created to handle the collection of User beans returned from the service method and to handle the nested collection of ExpertiseArea beans. Action bindings named Next and Previous will be created to support the buttons that were dropped. And finally, attribute bindings of appropriate names will be created to support the read-only outputText fields and the table.

Figure 1-12 Page Definition XML File for browseusers.jsp

page definition XML file for browseusers.jsp.

The very first time you perform Oracle ADF Model data binding in a project, JDeveloper creates one additional XML file called DataBindings.cpx that stores information about the mapping between page names and page definition names and lists the data controls that are in use in the project. Figure 1-13 shows what the DataBindings.cpx file looks like in the Structure window. At runtime, this file is used to create the overall Oracle ADF Model binding context. In addition, page map and page definition information from this file are used to instantiate the binding containers for pages as they are needed by the pages the user visits in your application.

Figure 1-13 Structure of DataBindings.cpx

structure of DataBindings.cpx.

Note:

For complete details on the structure and contents of the DataControls.dcx, DataBindings.cpx, and PageDef.xml metadata files, see Appendix A, "Reference ADF XML Files".

1.2.1.6 Understanding How Components Reference Bindings via EL

As you perform drag-and-drop data binding operations, JDeveloper creates the required ADF Model binding metadata in the page definition and creates the JSF components you've requested. Importantly it also ties the two together by configuring various properties on the components to have EL expression values that reference the bindings. Figure 1-14 summarizes how the components on the page reference the bindings from the page's binding container at runtime.

Figure 1-14 EL Expressions Related Page Components to Bindings

EL expressions related page components to bindings

As a simple example, take the Previous button. When you drop this built-in operation as a button, an action binding named Previous is created in the page definition file, and two properties of the commandButton component are set:

  • actionListener="#{bindings.Previous.execute}"

  • disabled="#{!bindings.Previous.enabled}"

These two EL expressions "wire" the button to invoke the built-in Previous operation and to automatically disable the button when the Previous operation does not make sense, such as when the user has navigated to the first row in the collection.

Studying another example in the page, like the read-only outputText field that displays the user's email, you would see that JDeveloper sets up the following properties on the component to refer to its binding:

  • value="#{bindings.email.inputValue}"

  • label="#{bindings.email.label}"

The combination of these two binding attribute settings tells the component to pull its value from the email binding, and to use the email binding's label property as a display label. Suppose you had configured custom prompts for the User and ExpertiseArea beans in the Model project, the bindings can then expose this information at runtime allowing the prompts to be referenced in a generic way by components on the page.

The drag-and-drop data binding steps above did not account for how the current record display (for example "N of M" ) appeared on the page. Since information about the current range of visible rows, the starting row in the range, and the total number of rows in the collection are useful properties available for reference on the iterator binding, to create this display, just drop three outputText components from the Component Palette and set each's value attribute to an appropriate expression. The first one needs to show the current row number in the range of results from the findUsersByName method, so it is necessary to set its value attribute to an EL expression that references the (zero-based!) rangeStart property on the findUsersByNameIter binding.

#{bindings.findUsersByNameIter.rangeStart + 1}

The second outputText component just needs to show the word "of", so setting its value property to the constant string "of" will suffice. The third outputText component needs to show the total number of rows in the collection. Here, just a reference to an attribute on the iterator binding called estimatedRowCount is needed.

1.2.1.7 Configuring Binding Properties If Needed

Any time you want to see or set properties on bindings in the page definition, you can select Go to Page Definition in the context menu on the page. For example, you would do this to change the number of rows displayed per page for each iterator binding by setting its RangeSize property. In the example shown in Figure 1-14, after visiting the page definition, the Property Inspector was used to set the RangeSize of the findUsersByNameIter iterator binding to 1 and the same property of the expertiseAreasIterator to 2. Setting the RangeSize property for each iterator causes one user and two expertise areas to display at a time on the page.

1.2.1.8 Understanding How Bindings Are Created at Runtime

The final piece of the puzzle to complete your basic understanding of Oracle ADF Model involves knowing how your data controls and declarative bindings are created at runtime based on the XML configuration files you've created. As part of configuring your project for working with Oracle ADF data binding, JDeveloper registers a servlet filter called ADFBindingFilter in the web.xml file of your ViewController project and maps this filter by default to URLs matching the pattern *.jsp and *.jspx.

This ADFBindingFilter servlet filter is responsible for finding your DataBindings.cpx file, based on the information in the web.xml file and creating the ADF binding context. The binding context is a Map that contains all binding containers, data controls, and the mapping of page names to page definition files. You can reference it at any time in your application using the EL expression #{data}. It's also the place where the centralized error handler is registered, and APIs are provided to set a custom error handler if needed (together with numerous other useful APIs).

When the page request is received the application invokes both the JSF lifecycle and the ADF lifecycle. Specifically, during execution of the ADF lifecycle execution, another object, the ADFPhaseListener, lazily instantiates the bindings in a binding container and data controls the first time they are needed to service a page request. The ADFPhaseListener references the information in the page map on the binding context to know which binding container to use for which page; it also references information in the DataControls.dcx file to know what data control factory to use. On each request, it ensures that the binding container of the current page being requested is available for reference via EL using the expression #{bindings}. Figure 1-15 summarizes the relationships between these metadata files.

Figure 1-15 How ADF Binding Metadata Is Used at Runtime

how ADF binding metadata is used at runtime

Once the binding container is set up for a given page, the ADFPhaseListener integrates the JSF page handling lifecycle with the bindings. It coordinates the per-page execution of the iterators and service methods based on information in the appropriate page definition file. The iterators and method invocation bindings are known as "executable" bindings for this reason.

1.2.2 Making the Display More Data-Driven

After you have a basic page working, you will likely notice some aspects that you'd like to make more sophisticated. For example, you can use the properties of ADF bindings to hide or show groups of components or to toggle between alternative sets of components.

1.2.2.1 Hiding and Showing Groups of Components Based on Binding Properties

If the application user enters a last name in the browseusers.jspx page that matches a single user, it doesn't look very nice to show disabled Next and Previous navigation buttons and a "1 of 1" record counter. Instead, you might want a result like what you see in Figure 1-16, where these components disappear when only a single row is returned.

Figure 1-16 Hiding Panel with Navigation Buttons When Not Relevant

hiding panel with navigation buttons when not relevant

Luckily, this is easy to accomplish. You start by organizing the navigation buttons and the record counter display into a containing panel component like panelHorizontal. After creating the panel to contain them, you can drag and drop in the visual editor, or drag and drop in the Structure window to place the existing controls inside another container. Then, to hide or show all the components in the panel, you just need to set the value of the panel's rendered attribute to a data-driven EL expression.

Recall that the number of rows in an iterator binding's collection can be obtained using its estimatedRowCount property. Figure 1-17 shows the EL picker dialog that appears when you select the panelHorizontal component, click in the Property Inspector on its rendered attribute, and click the ... button. If you expand the bindings for the current page you will see the findUsersByNameIter iterator binding. You can then expand it further to see the most common properties that developers reference in EL. By picking estimatedRowCount and clicking the > button, you can then change the expression to a boolean expression by introducing a comparison operator to compare the row count to see if it is greater than one. When you set such an expression, the panel will be rendered at runtime only when there are two or more rows in the result.

Figure 1-17 Setting a Panel's Rendered Attribute Based on Binding Properties

setting panel rendered attribute based on binding properties

1.2.2.2 Toggling Between Alternative Sets of Components Based on Binding Properties

Consider another situation in the sample page. When no rows are returned, by default the read-only form would display its prompts next to empty space where the data values would normally be, and the table of experience areas would display the column headings and a blank row containing the words "No rows yet". To add a little more polish to the application, you might decide to display something different when no rows are returned in the iterator binding's result collection. For example, you might simply display a "No matches. Try again" message as shown in Figure 1-18.

Figure 1-18 Alternative Display If Search Produces Empty Collection

alternative display if search produces empty collection

JSF provides a basic feature called a "facet" that allows a UI component to contain one or more named, logical groups of other components that become rendered in a specific way at runtime. ADF Faces supplies a handy switcher component that can evaluate an EL expression in its FacetName attribute to determine which of its facets becomes rendered at runtime. Using this component effectively lets you switch between any groups of components in a dynamic and declarative way. If you group the components that present the user information and experience area table into a panel, then you can use the switcher component to switch between showing that panel and a simple message depending on the number of rows returned.

Figure 1-19 shows the Structure window for the browseusers.jsp page reflecting the hierarchical containership of JSF components after the switcher component is introduced. First, you would set up two JSF facets and give them meaningful names like found and notfound. Then you can organize the existing components into the appropriate facet using drag and drop in the Structure window. In the found facet, you want a panel containing all of the components that show the user and experience area information. In the notfound facet, you want just an outputText component that displays the "No matches. Try again" message.

Figure 1-19 Structure Window View of browseusers.jsp

structure window view of browseusers.jsp.

By setting the facetName attribute of switcher to the EL expression, the found facet will be used when the row count is greater than zero, and the notfound facet will be used when the row count equals zero:

#{bindings.findUsersByNameIter.estimatedRowCount > 0 ?'found':'notfound'}

The combination of Oracle ADF declarative bindings, ADF Faces components, and EL expressions demonstrates another situation that previously required tedious, repetitive coding which now can be handled with ease.

This concludes the introduction to building J2EE applications with Oracle ADF. The rest of this guide describes the details of building a real-world sample application using Oracle ADF, EJB, and JSF.