Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3) B25386-01 |
|
![]() Previous |
![]() Next |
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.
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.
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.
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.
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.
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.
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.
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.
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.
The basic steps to create this page are:
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".
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.
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.
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.
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.
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.
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.
Note: For complete details on the structure and contents of theDataControls.dcx , DataBindings.cpx , and PageDef.xml metadata files, see Appendix A, "Reference ADF XML Files".
|
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.