By exploring the details of a sample application built using two popular off-the-shelf J2EE frameworks, Apache Struts and Oracle ADF, this paper illustrates how developers can build J2EE-compliant applications with maximum developer productivity using a framework-based approach. In the process, it highlights the full lifecycle support that the Oracle JDeveloper 10g IDE provides for framework-based development using Struts and ADF.
| NOTE: |
This article complements the ADF Data Binding Primer and ADF/Struts Overview by explaining the implementation details of a complete application built using Oracle ADF and Apache Struts. The present article provides an overview of the concepts necessary to understand the web store demo implementation, but please see this other whitepaper for more in-depth information on the underlying features. In addition, the JDeveloper 10g and Oracle ADF Online Documentation, JDeveloper 10g Tutorials, JDeveloper 10g Samples are great resources to be aware of, too. While this document should print fine in Internet Explorer, if you prefer, a zipped, PDF version of this paper is also available. |
The initial release of Sun's Java Pet Store Demo was a watershed event. Thirsty for guidance on implementing real-world J2EE applications, Java developers dove into its cool pools of code like parched creatures of the Kalahari. But after exploring the depths of its refreshing routines, many returned to the surface wondering why application infrastructure code dominated the demo. Obscured by repetitive implementations of J2EE design patterns, the more interesting business functionality of the web storefront was hard to find.
On further analysis, one point was clear to developers: for their own applications they would need to reimplement the same design pattern drudgery. Common sense dictated a framework approach, but developers would have to decide whether to build their own or leverage existing ones. To make a more informed decision, they read books like Core J2EE Patterns: Best Practices and Design Strategies which gave the design patterns names, organized them into functional layers, and explained how a typical J2EE application should use fifteen key patterns together. Another book, EJB Design Patterns: Advanced Patterns, Processes, and Idioms, came with a handy poster in back, detailing twenty-one design pattern tips and diagrams for easy cubicle-wall reference. These and other resources clarified that correctly and efficiently coding all these patterns from scratch would be no trivial task.
While their instincts undoubtedly warned them otherwise, many developers opted anyway for the "do-it-yourself" approach on their first J2EE application projects. A year later, many were still struggling to deliver feature-complete, well-performing applications.
On the opening page of his book Expert One-on-One: J2EE Design and Development (Wrox Press), Rod Johnson offers an observation on this phenomenon:
The return on investment for many J2EE projects is disappointing. Delivered systems are too often slow and unduly complex. Development time is often disproportionate to the complexity of business requirements.
Why? Not so much because of the shortcomings of J2EE as because J2EE is often used badly. This often results from approaches to architecture and development that ignore real world problems. A major contributing factor is the emphasis in many J2EE publications on the J2EE specifications rather than the real world problems people use them to address. Many issues that commonly arise in real applications are simply ignored.
Throughout the rest of his book, Rod debunks many myths about J2EE development and offers pragmatic guidance about which J2EE technologies to use under what circumstances. On page 166, he begins a section on frameworks and how they can help:
Many common problems (beyond those addressed by J2EE application servers) have been solved well by open source or commercial packages and frameworks. In such cases, designing and implementing a proprietary solution may be wasted effort. By adopting an existing solution, we are free to devote all our effort to meeting business requirements.
After commenting that existing frameworks can mean a slightly steeper learning curve, Rod later motivates why this trade-off is worthwhile to gain a strong application infrastructure. On page 395, he clearly explains the benefits:
Using a strong standard infrastructure can deliver better applications, faster. A strong infrastructure makes this possible by achieving the following goals:
- Allowing application code to concentrate on implementing business logic and other application functionality with a minimum of distraction. This reduces time to market by reducing development effort, and reduces costs throughout the project lifecycle by making application code more maintainable (because it is simpler and focused on the problem domain). This is the ultimate goal, which many of the following goals help us to achieve.
- Separating configuration from Java code
- Facilitating the use of OO design by eliminating the need for common compromises.
- Eliminating code duplication, by solving each problem only once. Once we have a good solution for a problem such as a complex API we should always use that solution, in whatever components or classes that encounter the problem
- Concealing the complexity of J2EE APIs. We've already seen this with JDBC; other APIs that are candidate for a higher-level of abstraction include JNDI and EJB access
- Ensuring correct error handling. We saw the importance of this when working with JDBC in Chapter 9.
- Facilitating internationalization if required.
- Enhancing productivity without compromising architectural principles. Without adequate infrastructure, it is tempting to cut corners by adopting quick, hacky solutions that will cause ongoing problems. Appropriate infrastructure should encourage and facilitate the application of sound design principles.
- Achieving consistency between applications within an organization. If all applications use the same infrastructure as well as the same application server and underlying technologies, productivity will be maximized, teamwork more effective, and risk reduced.
- Ensuring that applications are easy to test. Where possible, a framework should allow application code to be tested without deployment on an application server.
Several existing application frameworks provide ready-to-use implementations of the kind of strong application infrastructure that Rod recommends. If you use these frameworks, you won't have to design, code, debug, and maintain your own infrastructure code.
In this whitepaper, we examine two existing J2EE frameworks by studying a working sample application. By patterning the sample application after the "classic" Java Pet Store Demo, we've made it easier for readers familiar with the original demo to compare the developer productivity that a framework-based J2EE development approach can provide.
The ADF Toy Store demo is a simple web storefront application adhering to the Model/View/Controller (MVC) design pattern. It is implemented using two existing J2EE application frameworks: Apache Struts and Oracle Application Development Framework (ADF). Both the Struts and ADF frameworks have been iteratively developed to support the requirements of communities of application developers building real-world applications. Many aspects of their design and implementation echo the pragmatic suggestions that Rod Johnson details throughout his book.
As with all MVC-style web applications, the ADF Toy Store has the basic architecture illustrated in Figure 1:
The model layer consists of one or more business services that expose application functionality and access to model data through a business service interface that is easy to test. These business services, in turn, rely on query components to retrieve that data and on business objects to validate and persist any new or modified data. Code implementing the business delegate design pattern abstracts the details of locating and using the business services. When JavaServer pages are used for the view layer along with a cleanly separated controller layer, many J2EE books refer to the architecture, shown in Figure 1, as a best practices "JSP Model 2" architecture. The number "2" is used because this MVC-based architecture for JSP is an evolution over first-generation JSP-based approaches.

By dissecting the framework-based implementation of our ADF Toy Store demo, we'll learn how ADF simplifies building all aspects of the model layer, and how the Struts and ADF frameworks cooperate to simplify implementing the view and controller layers. In the process, we'll also see plenty of evidence for how Oracle JDeveloper 10g provides a productive environment covering the full development lifecycle for building these kinds of MVC-style business applications.
Before diving into the explanation of the demo, let's make sure you can open and run the demo in JDeveloper 10g. The next section details the steps to get the demo setup correctly on your system.
These instructions assume that you are running Oracle JDeveloper 10g production, version 9.0.5.1 or 9.0.5.2. The demo will not work with earlier versions of JDeveloper.
We also assume that you have access to an Oracle database, and privileges to create new user accounts to setup the sample data.
| NOTE: |
ADF is designed to work with any relational database, and has been tested with Oracle, Oracle Lite, DB2, and SQLServer. The Using BC4J with Foreign Datasources whitepaper covers the details (which are still valid for Oracle ADF as well), but to make the demo explanation easier to follow, herein we've made the simplifying assumption that you're using the Oracle database, version 8.1.7 or later. |
adftoystore.zip file if you
haven't already.
Extract the contents of the
adftoystore.zip file
with the standard JDK jar utility into a convenient
directory.
jar xvf adftoystore.zip
This will create a directory
adftoystore, and subdirectories. These instructions assume
you've extracted the adftoystore.zip file into the root
directory C:\ thus ending up with a demo "root" directory of
C:\adftoystore.
| NOTE: | If the
jar command does not work on your system, double-check that
you have included the JDKHOME/bin
subdirectory in your system path. If you downloaded the full version of Oracle
JDeveloper 10g, then it comes with a 1.4.2 JDK in the
JDEVHOME/jdk directory.
|
Create the TOYSTORE and
TOYSTORE_STATEMGMT user accounts in the database using the
provided SQL script.
Run the SQL script
./adftoystore/DatabaseSetup/CreateToyStoreUsers.sql like
this:
cd C:\adftoystore\DatabaseSetup
sqlplus /nolog @CreateToyStoreUsers.sql
After entering your SYS account's
password, the script will create the TOYSTORE and
TOYSTORE_STATEMGMT user accounts, and then GRANT
EXECUTE ON DBMS_LOCK TO TOYSTORE. The TOYSTORE
schema will contain the ADF Toy Store application tables, while the
TOYSTORE_STATEMGMT schema will be used by the ADF state
management facility (described later in this whitepaper) to store pending data
across web pages. The DBMS_LOCK.SLEEP packaged procedure is
used by the LOCK_ALL_ITEMS_ORDERDED stored procedure
explained in the Example of Accessing Stored Procedures section later in the
paper.
Create the application tables for the ADF Toy Store demo, along with some sample data.
Run the SQL script
./adftoystore/DatabaseSetup/ToyStore.sql like this:
sqlplus toystore/toystore @ToyStore.sql| NOTE: | If you have a version of the Oracle database
prior to Oracle 10g, the command purge
recyclebin at the end of this script will give an error. It's
harmless and you can just ignore it.
|
Setup two database connections in the JDeveloper 10g IDE corresponding to the two database accounts we created above.
Define connections in the JDeveloper 10g IDE named...
toystore, corresponding to the TOYSTORE
user (password TOYSTORE) and
toystore_statemgmt corresponding to the
TOYSTORE_STATEMGMT user (password
TOYSTORE).
| NOTE: | The two connection names are case-sensitive and should be typed in lowercase as shown. |
To save some typing, you can
import these two connections from the supplied
jdev_toystore_connections.xml file in the
./adftoystore/DatabaseSetup directory. To do so, select the
Database category folder in the Connections Navigator and
choose Import Connections... from the right-mouse
menu. Supply the jdev_toystore_connections.xml file name as
the file to import from. After importing the two named connections, you should
test each connection by selecting it, double-clicking to bring up the
Connection Wizard, and visiting the
Test tab. If clicking on the Test
Connection button does not yield a "Success!" message, then correct
the connection details on the Connection tab to work for
the database to which you want to connect. By default, the connections are
defined against a database on your local machine listening on port 1521 with a
SID of ORCL.
Insure that the JUnit Extension for JDeveloper is installed.
JUnit is the defacto standard tool for building regression tests for Java applications. Oracle JDeveloper 10g features native support for creating and running JUnit tests, but this feature is installed as a separately-downloadable IDE extension. You can tell if you already have the JUnit Extension installed by selecting File | New... from the JDeveloper main menu, and verifying that you have a Unit Tests (JUnit) subcategory under the General top-level category in the New Gallery.
If you do not already have the JUnit
extension installed, then download it from
here.
You'll find it along with all the other extensions available for JDeveloper in
the
JDeveloper
Extension Exchange on OTN. To complete the installation of the
extension, first exit from JDeveloper if you are currently running it. With
JDeveloper not running, extract the contents of the
downloaded zip file into the ./jdev/lib/ext subdirectory
under your JDeveloper installation home directory. Then, restart
JDeveloper.
Finally, you should verify that the
junit3.8.1 subdirectory exists in your JDeveloper
installation home. This directory will automatically get created the first time
you run any JUnit wizard from the Unit Tests (JUnit)
category of the New Gallery. However if you don't plan on creating any JUnit
tests yourself yet, you can do the following steps to make sure the directory
gets setup correctly. Assuming your current directory is the JDeveloper
installation home directory...
jar
xvf jdev/lib/ext/junit_addin.jar junit3.8.1.zip
This
extracts the junit3.8.1.zip file from the
junit_addin.jar archive. This zip file contains the
distribution of JUnit with which JDeveloper has been tested.
jar xvf junit3.8.1.zip
This
extracts the contents of the junit3.8.1.zip file into the
JDeveloper installation home
directory.
./adftoystore/ADFToyStore.jws workspace in JDeveloper
10g
Run the application inside the JDeveloper
10g IDE by running the index.jsp page in the
ToyStoreViewController.jpr project as shown in
Figure 2.

| NOTE: | Since
index.jsp is configured as the Default Run
Target on the Runner panel of the project
properties for the ToyStoreViewController project, you can
also simply click the Run icon in the IDE toolbar when this project is active
to run the application, or pick the Run menu item on the
ToyStoreViewController project's right-mouse menu. You can
see the project properties by clicking on it in the Navigator and selecting
Property Properties... from the right-mouse
menu.
|
Running the index.jsp page from
inside JDeveloper will startup the embedded Oracle Application Server 10g
Oracle Containers for J2EE (OC4J) server, launch your default browser, and
cause it to request the URL:
http://yourmachine:8988/ADFToyStore/index.jsp
If everything is working correctly, you will see the home page of the ADF Toy Store demo, as shown in Figure 3.
| NOTE: | If following the steps above didn't produce the above demo home page as expected, see Appendix 1 for a list of known issues and troubleshooting tips. |

| NOTE: |
After exploring the demo using the embedded Oracle Containers for J2EE (OC4J) instance that is built-in to JDeveloper 10g, if you want to install the demo on an external OC4J instance, Oracle Application Server, Tomcat, or other supported server see Deployment and Packaging Considerations |
Before we dive into explaining how the demo was built, let's begin with a quick overview of the end-user functionality of our web storefront application.
The ADF Toy Store is a fictitious online store that sells toys. The products for sale are organized into five categories: Accessories, Games, Party Supplies, Toys, and Models. From the home page, you can browse products in the store in two ways:
If the list contains more than three products, they are presented a page at a time. You can use the Next or Previous links that appear above the item list to browse through the complete list.
Clicking on the name of a product shows you a list of the different product items for sale. For example, clicking on the name of a product like Pinata, you will see a list of the different kinds of piñatas that are available as shown in Figure 4.

To see a detailed description and a picture of any product, just click on its name.
On any page where the
button appears, you can click
on it to add one of those items to your shopping cart.
You can see
what items you have in your shopping cart at any time by clicking on the
button, which shows a page listing the
items and quantities you have selected so far, as shown in
Figure 5.

To adjust the quantities of the items in the
cart, just type over the current value in the Quantity
field for one or more items, and click the
button to see the recalculated
shopping cart total. You can remove an item from your cart either by clicking
the
button, or by adjusting the
item to have a zero quantity.
From the Shopping Cart page, by clicking on the
button you can proceed to the
Review Checkout page. From there, you can review your purchase and if you are
happy with it, press the
button to
continue.
If you have not already signed into the Toy Store as a
registered user, you will be prompted to sign in at this point to continue with
the checkout process. The sign in page looks like what you see in
Figure 6. The user named j2ee is already
registered, with a password of j2ee, so you can provide
these credentials to continue.
| NOTE: |
If instead you want to register as a new user, you can click the Register as New User link. See the next section for details... |

After successfully signing in, you will proceed to the page where you can confirm your shipping and payment details. Here, to see some of the application validation logic that's implemented in the demo, you can try:
ZA for the country USAand pressing the
button. You should see the multiple
validation errors as shown in Figure 7 .

After fixing those errors by entering a valid state abbreviation like CA, and filling out a full 16-digit credit card number, try causing some additional validation errors by:
You
should again see the relevant set of remaining validation errors that need to
be corrected when you press the
button again as shown in Figure 8.

| NOTE: |
When you submit this web page, without having to write any code, the data is automatically communicated to the underlying business objects, which in turn, enforce their declarative business rules. These rules get enforced by your ADF-powered business components as part of normal operation, and work consistently with any kind of user interface technology. Rather than simply presenting the first error that is raised, ADF allows you to easily present the user with a maximal set of errors that have been flagged in a single round-trip, so the user can fix all the problems in one go. |
After correcting these final validation problems and submitting again, your order will be placed, and you'll see the final "Thank You" page, with a reference to your order reference number. Clicking on the hyperlinked order reference number takes you to an order summary page which is implemented using the XML/XSLT-based Oracle XSQL Pages publishing framework instead of JSP Pages, to illustrate that multiple view-rendering technologies are possible.
If you are not currently logged-in as a registered user of the web store,
clicking the
button brings you to the
"Sign In" page as shown in Figure 6. From there, you can
register as a new user by clicking on the Register as a New
User link. This brings you to a form to complete with the necessary
registration details.
This user registration page is another place in our application where it's easy to observe how business rules get enforced by the ADF framework. For example, if while filling out the form you try to:
then when you submit the form, you'll see the full set of errors related to your registration as shown in Figure 9

| NOTE: |
Under
the covers, the business object that represents a user account 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 |
If you are already logged into the site as a
registered user, you will see the
icon in the toolbar. Clicking on it brings you the page where you can edit your
account details as shown in Figure 10. Of course, since we're
working with the same underlying business object representing user accounts
here in this "Update Account" form, the same validation will be enforced as
above.

The demo is built using the internationalization features supported by Struts and ADF Business Components, and it ships with support for three languages: English (the default), Italian, and German. The Struts and ADF frameworks automatically sense the language you want to see based on your browser settings. So, you can see what the demo looks like in Italian by simply setting your browser language preferences appropriately.
In Mozilla Firefox 0.9, you select your preferred languages on the General panel of the Tools | Options... menu by clicking the (Languages) button as shown in Figure 11. You can add "Italian [it]" and then press (Move Up) to move it to the top of the list.

In Internet Explorer, you can do the same thing from the General tab of the Tools | Internet Options... dialog by clicking on the (Languages...) button.
Since Apache Struts caches the browser-user's preferred language at the servlet session level, you will need to close your browser window, and open a new one before you'll see the demo change into Italian. A quick way to relaunch your preferred browser with the right URL for the demo is to find the Target URL in the JDeveloper 10g log window as shown in Figure 12 and click on it.

This will bring up the ADF Toy Store demo again, but this time in Italian. After adding the same items to your shopping cart again, it will look like what you see in Figure 13.

You can set your browser's preferred language back to English using similar steps, and close and relaunch the browser window to proceed in English again.
The last aspect of the demo we'll explore on our tour is a practical example of the ADF framework's state management and failover support for ADF Business Components. The feature sounds complicated, but it's easy to demonstrate. With the ADF Toy Store application running inside the embedded OC4J container that's part of the JDeveloper IDE, try the following:
Without closing your browser window, terminate the OC4J application server to simulate a hardware failure on your application server machine.
To do this, select the View | Run Manager menu option to display the Run Manager. Find the Embedded OC4J Server process in the list, and select it. Finally, choose the Terminate menu option from the right-mouse menu item as shown in Figure 14.

After restarting the application server -- in this example, we've restarted the embedded OC4J application server in JDeveloper -- the browser window you left open in step 2 above will be able to continue where it left off, with all shopping cart items intact.
This failover capability works because the ADF framework
offers automatic database-backed state management for pending data in your
application when using the ADF Business Components. In the ADF Toy Store
application, the pending shopping cart information is not stored in the HTTP
session state the way most applications do. Instead, with a declarative
checkbox on the ShoppingCart component at design time, we
indicate that we'd like this component's pending data to be managed for us. And
the framework takes care of the rest.
At this point we've seen the key functionality in the demo, so it's time to dive in to understand how it has all been built.
In this section we explain the demo in detail, highlighting the interesting details of how:
| NOTE: |
To follow along, we assume
you have followed the instructions in the Demo Installation and Setup section and
have the |
Like all applications built in Java, the ADF Toy Store demo is comprised of a set of classes, organized hierarchically into packages. Figure 15 illustrates the key packages in the demo. We have used the package naming to make it clear how the application classes break down into model, view, and controller layers, as well as to clarify which classes are part of our framework customizations and regression testing suite.

When building applications that leverage existing frameworks, your application-specific classes inherit default functionality from an appropriate framework base class. They inherit core behavior from their superclass, and add application-specific logic and metadata. Typically, the only code needed in your classes is the code that is specific to your application's business functionality. Figure 16 shows some representative examples of classes in the ADF Toy Store demo that inherit their behavior from a framework:
toystore.model.services.ToyStoreService extends
the ADF framework base class
oracle.jbo.server.ApplicationModuleImpl, adding custom
business service methods and an application-specific "data model" of named
collections of data transfer objects (also known as value
objects) exposed to the client.
toystore.model.dataaccess.ProductsInCategory extends the ADF
framework base class oracle.jbo.server.ViewObjectImpl,
adding an application-specific SQL query for products in a particular category
and providing custom methods to encapsulate the setting of its bind
parameters.
toystore.model.businessobjects.Account extends the ADF
framework base class oracle.jbo.server.EntityImpl, adding
application-specific attributes for user accounts and specifying several
business rules that govern an account's validity.
toystore.model.dataaccess.common.ShoppingCartRow extends the
ADF framework base interface oracle.jbo.Row, adding typesafe
access to the application-specific attributes in the row of shopping cart
information.
toystore.controller.strutsformbeans.LoginForm extends the
Struts framework base class
org.apache.struts.action.ActionForm, adding
application-specific bean attributes like Username and
Password that will be entered by the user to sign in to the
Toy Store web site.
toystore.controller.strutsactions.PlaceOrderAction extends
the Struts framework base class
org.apache.struts.action.Action, adding application-specific
controller logic needed before rendering the HTML form to collect shipping
information for the order being placed.
toystore.test.unittests.CreateAnOrderTest extends
the JUnit framework base class junit.framework.TestCase,
adding application-specific testing logic that exercises the
ToyStoreService business service by simulating the creation
of an order after adding items to the shopping
cart.

JDeveloper 10g provides two constructs to organize our work: workspaces and projects. Projects contain a set of files that get compiled (and perhaps deployed) as a unit, and workspaces are a list of projects that go together to comprise a complete application. Theoretically, we can build any application with all of the files in a single project, but typically we organize our work into a number of separate projects to divide up the work into more logical groupings.
As shown in
Figure 17, the ADF Toy Store application is comprised
of a ADFToyStore workspace containing the following six
projects:
ToyStoreModel.jpr
This project contains
the components in the toystore.model.* package tree,
including the main business service
toystore.model.services.ToyStoreService and all the business
object and data access components on which it relies to provide its application
functionality and model data to the client. It also contains the translated
resources (in English, Italian, and German) related to these
components.
ToyStoreViewController.jpr
This project contains
toystore.view package of the translatable text that appears
in all the pages.
struts-config.xml and the source code for all of the
classes in the toystore.controller.* package tree. This
includes the Struts actions that coordinate the interaction between the
business service and the view-layer
pages.
FwkExtensions.jpr
This project contains
the classes in the toystore.fwk.* package tree that extend
the base ADF and Struts framework facilities to augment and/or customize the
default framework behavior. These customizations are not specific to the web
storefront and could be easily reused in another Struts/ADF
application.
Testing.jpr
This project contains the
classes in the toystore.test.* package tree, including a
JUnit regression test suite, test fixture, and unit tests for various aspects
of the ToyStoreService component.
DatabaseSetup.jpr
This project contains the two SQL scripts used to setup the database schema for the demo, as well as a JDeveloper database diagram of the Toy Store database design.
Documentation.jpr
This project contains a
copy of this whitepaper in the readme.html
file.

First generation JSP applications freely mixed code "scriptlets" into the page among the HTML presentation tags. The code for parameter evaluation, data access, business rules enforcement, transaction management, error handling, and page flow was simply typed right into the same JSP file that would also eventually format the data for the end-user to see. Having everything in one file and being able to see compilation errors by refreshing the browser lent an immediacy to development that enticed many developers to follow this approach. However, this hybrid approach more often than not produced pages that were impossible to read. Attempts to alter the look and feel of the pages, unless performed by the original developer, could lead to hours of staring at the file, hunting for the unintended typographical error.
Code scriptlets in JSP pages began to fall out of favor as JSP 1.1's tag libraries allowed many common tasks to be performed using easier-to-read elements and attributes. However, the popularity of tag libraries that performed SQL data access or EJB component interaction directly from the JSP page was still an indication that developers were not correctly separating the presentation layer from the application layer. In these first generation JSP applications, the model, view, and controller layers were hopelessly intertwined.
As these applications evolved, attempts to respond quickly to new business needs requiring an updated look and feel or modified web page flow were greatly complicated by this "heavy page" approach. Developers bitten by the maintenance nightmares of the first-generation approach immediately understood the benefits that the Model, View, and Controller separation has to offer. In a nutshell, with an MVC architecture:
With its advantages now clear, let's begin to look at how our Toy Store demo implements the Model, View, and Controller layers of its architecture.
The model layer is comprised of business services, query components, business objects, and collections of data transfer objects that the business service exposes to the controller and view layers. In this section we'll highlight some examples of these model layer components from the ADF Toy Store demo and briefly explain how they leverage the ADF framework for their implementation.
Before exploring the ADF Toy Store model layer implementation in detail, we should first stop to consider the important choice of whether the model layer will be implemented using:
As illustrated by the two separate sample applications provided by Sun's "J2EE Blueprints" demo team, the approach you choose for your model layer can have a major impact on the application's underlying implementation. The architecture documentation that accompanies the more recent Adventure Builder demo explains:
The Java Pet Store application illustrates how to write a Website application in an EJB-centric manner. The Adventure Builder application illustrates the other option: how to write a Website application in a Web-centric manner. EJB is a key technology in the J2EE platform, but not all J2EE applications need to use it.
The document goes on to explain some of the motivations behind making the choice:
One important design consideration is mapping application modules and functionality to the different tiers and technologies on the J2EE platform. Some choices are obvious, such as having a web tier when a web browser client is required. Other choices may depend on several factors. Issues such as data access and transactional needs, security, portability and modularity of design, lead to deciding how to optimally map the application modules to the client, Web, EJB, and EIS (data storage) tiers. An important question is whether to use an EJB tier. Based on the application's needs, one might choose not to use enterprise beans and the EJB container and tier. The expertise of the development team also affects this decision. For example, a team with strong Web-tier and SQL skills may find it easier to write a Web-only application especially when they are new to the EJB technology and are pressed for time to learn it.
Using the ADF framework, you build your J2EE application using a consistent development approach that is independent of your choice of deployment tier for your model layer. You develop, test, and debug the application using a model layer built from high-performance, well-architected, XML-configured JavaBeans. At any time during the development process, you can choose to deploy your model layer as JavaBeans to the J2EE Web Tier, or as an EJB Session Bean to the EJB tier. Some business requirements that might nudge you in the direction of an EJB tier deployment include the need to:
Since the ADF framework provides an implementation of the best-practices Business Delegate design pattern, your model and view layers are isolated from these deployment details. Even if you change your mind mid-project on your preferred model-layer deployment architecture, none of your application code needs to change. In fact, you can try out both deployment options and pick the one that delivers best performance for your particular application scenario. In other words, using the ADF framework, you don't have to decide up front on an EJB-Centric or Web-Tier-Centric approach, and you can change your mind at any time, without rearchitecting your system.
For the purposes of this demo, we have selected to deploy the ADF Toy Store demo's model layer to the J2EE web tier to keep the demo as easy to follow as possible for the widest audience of Java developers. For the reasons we've just mentioned, redeploying the model layer to the EJB Tier would be a painless step for those wanting to take an EJB-centric approach.
While Oracle ADF supports using virtually any kind of Java class as a business service, the ADF Business Components option we provide gives you the highest level of built-in application-building functionality and developer productivity. Business services built using the ADF business components are called application modules. These service components are:
All of these features can be summarized by saying that ADF-powered service components make the J2EE developer's life a lot simpler. The key ADF framework components that cooperate to provide the business service implementation are:
Our toystore.model.services.ToyStoreService
application module is the heart of our application. It is a JavaBean component
that implements the business service interface shown in
Example 1.
package toystore.model.services.common; |
As shown in Figure 18, the
ToyStoreService component is implemented as a set of files.
The Application Navigator presents the component as a single, logical icon,
while the Structure Window's Sources folder shows the
detailed implementation files comprising that component:
ToyStoreService.xml - Service definition
file
ToyStoreService.java -
Service interfaceToyStoreServiceImpl.java - Service
implementationToyStoreServiceClient.java - Service client
proxy| NOTE: |
There are four ways to navigate to the files that comprise your ADF business components:
|
The client proxy class is created when you've exposed custom methods to be accessed by clients, alongside the custom service interface that the ADF design time creates for your component. The client proxy class implements your custom component interface, and is used automatically at runtime when client layer is deployed in a separate tier from the business service layer or when using Batch Mode.
The application module editor in JDeveloper automatically keeps the XML component definition file and business service interface in sync with the declarative options you set using the editor. For example, the business service interface automatically appears in your project as soon as you mark any custom methods as part of the service interface on the Client Methods panel in the editor. As we'll see in the other sections below, all ADF business components follow this basic pattern for the names of the files that comprise their definition, implementation, and interface.

| NOTE: |
|
If you have a look inside the
ToyStoreServiceImpl.java file, you'll see it contains the
code implementing the business service interface methods, and some
JDeveloper-generated convenience methods to access collections of data transfer
objects. In order to more clearly identify custom code
from JDeveloper-generated code, we've surrounded all of the developer-written,
application-specific code with marking comments like:
//--[Begin Custom Code]--
and
//--[End Custom Code]--
You'll see these same marking comments in all of the ADF-based JavaBean components in the application.
When implementing Model/View/Controller (MVC)
applications by hand, best practice techniques steer
developers to expose model data to the controller and view layers using a
HashMap object. This "model data map" gives the client
layers a single object that represents the entire "data model" needed for the
current application task. Using the model data map, the controller and view
layers can easily find any collections of data transfer objects using a
convenient string key name.
For example, the model data required to display the summary of an order might include:
Figure 19 illustrates what the model data map object would look like for such a task.

Example 2 shows the typical code required to find the collection of data transfer objects for line items from a model data map.
/* |
The model data map discussed above is a necessary feature of any MVC application. Business services implemented as ADF application modules inherit a built-in model data map implementation. The application module cooperates with ADF view object components to allow you to build your model data map declaratively.
You create view object components to encapsulate SQL statements that will produce the required collections of data transfer objects. Then you define your model data map at design-time by adding instances of these view object components to your application module using the appropriate panel of the Application Module Editor shown in Figure 20. The names that appear in the Data Model list on the right are the string key names that you'll use at runtime to find the collection of data transfer objects produced by this view object instance. Of course, you can pick any names you like.

The names
appear in a tree control to illustrate visually any master/detail coordination
that the ADF framework is doing on your behalf among the collections of data
transfer objects. For example, the indentation in
Figure 20 shows that the collection named
Orders will automatically contain only those orders for the
current account data transfer object in the Accounts
collection, and the LineItems collection will contain the
line items for the current order data transfer object.
Figure 21 shows the same
ToyStoreService application module in a visual way using the
UML diagramming support for ADF components in JDeveloper 10g. Each of the named
collections in its data model map appears, and master/detail relationships are
indicated with their cardinality using lines and arrows between the
collections.

Since the application module component implements the model data map for you, at runtime the view or controller layer can lookup a particular collection of data transfer objects by name using the instance of the application module service component that it is working with using syntax as shown in Example 3.
/* |
If you do not want to
work with typesafe collections of data transfer objects, you can opt to work
with ADF's generic collection implementation
(oracle.jbo.RowSet) and generic data transfer object
implementation (oracle.jbo.Row) by writing code like this
instead:
RowSet lineItems = (RowSet)yourAppModule.findViewObject("LineItems");
while (lineItems.hasNext()) {
Row line = lineItems.next();
Long quantity = (Long)line.getAttribute("Quantity");
/* etc. */
}
In addition to the useful
findViewObject() method to access collections of data
transfer objects from the built-in model data map, business services like our
ToyStoreService inherit several other useful methods related
to flexibly working with application data. They are beyond the scope of this
article since we didn't require their use in the ADF Toy Store demo, but
JDeveloper's
Online Help system covers all of the framework API's in its reference
documentation if you are curious for more details.
| NOTE: |
To find
the ADF framework API documentation, with JDeveloper running do the following.
Launch the help system with Help | Help Topics...
and then expand the Reference node to see the
Business Components for Java category. All the JavaDoc is
there. If you prefer to browse the JavaDoc with your own favorite browser, then
expand the |
Business objects built using ADF are called entity objects. Like application module components, your entity objects are JavaBeans that extend a framework base class, are configured from XML metadata, and are created by factories. They cooperate automatically with other ADF framework components to help make application building easier. The distinguishing role of entity objects is to be the software implementation of the domain business entities in your real-world business object model.
Since developers typically use
the Unified Modeling Language (UML) to visualize their business object model,
we can use JDeveloper 10g's UML modeling features for ADF to do just this.
Figure 22 shows where to find the Business Objects UML
model in the ToyStoreModel project.

You can see the UML diagram
named "Business Objects" in the toystore.uml package by
double-clicking on it. Figure 23
shows what you will see when you open the diagram. It's the business object
model for the ADF Toy Store demo. Here, I've set the display options for the
diagram shapes to only show the object attributes to keep things simple, but
you can easily turn on additional options to show methods and other UML
artifacts on the diagram, too.

For each real-world business entity in the application domain, an ADF entity object:
By default, each entity object inherits high-performance, relational-database persistence functionality from the ADF framework, too. Custom persistence schemes can be implemented by overriding one framework method in your domain-specific entity subclass. For example, some ADF framework users are doing this to adapt their entity objects to use an existing PL/SQL package API for updating information in their base tables.
One built-in feature we can notice from the
UML model in Figure 23 is that the
Orders entity object uses a DBSequence
type for its Orderid attribute. By configuring an entity
object to have a datatype of DBSequence, the ADF framework
automatically handles the common case of primary key values assigned from a
database trigger, without having to write code.
Our UML business model in Figure 23 visualizes several other interesting things about the relationships between our entity object components. In particular, it shows:
For
example, Orders are composed of one or more
Lineitem and each Lineitem is associated
with an Item. Each Item is associated
many-to-1 with Supplier, and one-to-one with
Inventory. The arrowheads imply, for example, that code in
Lineitem can call
getItem().getInventory() to access the instance of the
Inventory object that tracks the items quantity in stock. In
fact, if you look at the finalizeOrder() method in the
ToyStoreServiceImpl.java class, you'll see programmatic
association traversal at work as shown in the code snippet below, accessing the
inventory object for the item being ordered on the current order line item, and
setting the inventory quantity on hand to the adjusted quantity.
// Decrement Inventory Quantity for current line item amount
InventoryImpl inv = newLine.getLineitem().getItem().getInventory();
double currentQty = inv.getQty().doubleValue();
double newQuantity = currentQty - (newLine.getQuantity().doubleValue());
inv.setQty(new Number(newQuantity));
As you can see in
Figure 24, which shows the
toystore.model.businessobject package in the JDeveloper
Application Navigator, entity object components like Account
are comprised of a number of constituent files:
Account.xml - Entity definition file
AccountImpl.java - Entity
implementationAccountImplMsgBundle.java - Entity message
bundle
All of the declarative aspects of the
Account entity object definition are kept in the
Account.xml file. This includes attribute definitions,
declarative business rules, and database table/column mapping information. As
we saw with application module components, you never have to hand-modify the
declarative XML yourself. The multi-panel Entity Object Editor in JDeveloper
shows you all of your entity component's settings and lets you easily configure
its declarative behavior. An important thing to notice is that entity objects
do not have a client-accessible interface as application
module components do. No client interface is required since entity objects are
not meant to be directly accessed by the controller or view layers. Entity
objects are private to the model layer by design.
To get an
overall view of the database design that our business object layer maps onto
for its persistence, visit the DatabaseSetup project in the
ADFToyStore workspace, and double-click on the
"Toystore Database Design" diagram under the
toystore.db package in the Application
Sources folder. This will bring up the JDeveloper 10g database
diagrammer showing the ADF Toy Store table design as shown in
Figure 25. With the exception of the
STATES_FOR_COUNTRY table that is used by our State/Country
validation rule, all of the other tables are identical to those used in the
original Java Pet Store Demo.

Figure 26 shows the Validation panel in
the Entity Object Editor for the Account object,
illustrating the object-level and attribute level business rules that we've
defined for this component.

Table 1 shows the declarative business rules that have been
enabled for the Account and Orders entity
objects.
| Component Name | Declarative Business Rule |
|---|---|
Account |
|
Order |
|
Rules like the UniquePKValidationBean,
ListValidationBean, and Method are
supplied with the framework. As we'll see more in detail later in this paper,
the VerifyStateForCountry rule is a declarative rule whose
implementation we've written ourselves. Once a custom rule is written, other
developers can use it declaratively just like any of the supplied rules by
setting specific usage-specific parameter values that will drive the rule
evaluation. In general, validation rules can be set at object-level and
attribute-level with the following three implementation choices:
Use one or more pre-supplied rules
This only requires picking the rule type and setting any properties that
govern its behavior. For example, a RangeValidationRule
might have LowValue and HighValue
parameters that must be supplied to define the range.
Use one or more custom method validation rules
Defining method validation rules causes the framework to evaluate various
validateSomething() methods that
you've written in your entity object's implementation class. These rules are
appropriate for complex validation that don't make sense to generalize into a
custom rule to be reused by other entities.
Using custom business rules
Custom rules are JavaBeans that
implement the JbiValidator interface in the
oracle.jbo.server.rules package. Once defined, they can be
packaged as reusable rule libraries and put to work declaratively by other
developers on the team.
The
validateCreditCardExpiration() method on the
Orders entity object illustrates a custom validation method.
Figure 27 shows the Method validation
rule that we've defined on Orders to engage the
validateCreditCardExpiration() method.

Since
this custom method-based validation rule depends on two different attributes
(Creditcard and Exprdate) we implement it
as a validation rule at the business object level, instead of at the
attribute-level. Here we're illustrating how to use code-based validation as an
alternative to declarative business rules like the
VerifyStateForCountryRule that is also associated to this
Orders business object.
The expected signature of validation methods invokeable by the Method business rule is:
public boolean
validateSomeNameYouChoose()
This means that our custom validation methods should return
true if the validation succeeds. For validation failures, we
can throw an oracle.jbo.ValidationException for an
object-level exception, or an oracle.jbo.AttrValException
for an attribute-level exception, accompanied by a custom error message. Of
course, if the framework's generic exception message is adequate, we can just
return false to indicate failure as well.
By
throwing an attribute validation exception, the error is attributed to a
specific attribute instead of to the object as a whole. This attribute name
information is then available to the controller-layer code that handles errors
so it can decide where to present the error to the user.
Example 4 shows what the code for the
validateCreditCardExpiration() method looks like.
public boolean validateCreditCardExpiration() { |
| NOTE: |
See the Business Rules in BC4J (PDF) whitepaper for a systematic discussion of how to classify business rules and implement them with ADF Entity Objects. The article's tips are all valid for Oracle ADF as well. |
One of the most frequent and fundamental tasks that business application developers do is access business information for iteration, presentation, and modification. As simple proof of this fact, we need only note that virtually every page of the ADF Toy Store demo relies on displaying or editing business information. For example:
Unfortunately, in the J2EE world without frameworks, this omnipresent data-access task is fraught with implementation complexity in the name of adhering to J2EE best practices design patterns. Just look at what developers are encouraged to do by most J2EE design pattern books:
ResultSet and construct instances of row-like data transfer
objects holding copies of the queried row data.
Of course, these suggestions are not bad in and of themselves. All of their functionality is interesting and valid. However, their hand-coded implementation implies a ton of uninteresting, infrastructure-level programming tasks that should not be required by application developers. This observation becomes even more painfully clear when we consider that only really interesting things changing in each usage scenario of the above patterns is:
As with many hand-coded J2EE development techniques, there is a better way! Rod Johnson devotes all of chapter 9 in Expert One-on-One: J2EE Design and Development to "Practical Data Access", and he recommends developing a generic JavaBean component that can handle all of these gory details of data access. Specific query components then extends this generic "query bean" to add the usage-specific details like the SQL statement.
This is precisely what the ADF framework provides with its View Object component. Like the other ADF framework components we've seen, your view objects are JavaBeans that extend a framework base class, are configured from XML metadata, and are created by factories. Once configured with a SQL statement you want it to execute, each view object component automatically implements all of the following design patterns for you: Data Access Object, Fast Lane Reader, and Transfer Object. They also cooperate with the application module component to provide an implementation of the Transfer Object Assembler pattern, too.
While defining your view object, in addition to setting up your query (or letting the editor build it for you), you also configure the attribute names and datatypes of the data transfer object that will carry the query's result row data to the client. Figure 28 illustrates this relationship between the view object and its "view object row" data transfer object. When the view object produces collections of data transfer objects after executing a query, each object in the collection is an instance of the view object row class.

Of course, the ADF design time editor defaults the names and datatypes for you based on metadata it obtains from your query statement, but you can change the names and datatypes of the attributes in the view object row as required. The declarative information about the query and the view-specific data transfer object attributes is saved in the view object's XML file.
In addition to
the ViewName.xml file that all
ADF components have, as shown in Figure 29, the view
object component can comprise a number of optional additional files as
well:
ViewNameImpl.java) used to
customize the behavior of the view object component or implement custom
methods.
ViewName.java) appears
automatically when you expose one or more of your custom view object methods on
the Client Methods panel of the View Object
Editor.
ViewNameRowImpl.java) used to
customize the behavior of the data transfer object associated with this view
object.
ViewNameRow.java) appears
automatically when you expose one or more of your custom view row (data
transfer object) methods on the Client Row Methods panel
of the View Object Editor.
ViewNameRowImplMsgBundle.java)
appears automatically when you define any built-in prompts, tooltips, format
masks, etc., for your view object.
Client proxy classes can be generated for view object and/or view rows as well if you have exposed custom methods on either or both of these.
For
example, toystore.model.dataaccess.Accounts is implemented
by the following set of files:
Accounts.xml - View Object definition
file
AccountsImpl.java -
View Object implementationAccountsRowImpl.java - View Row
implementationAccountsRowImplMsgBundle.java - View Row
message bundle
Looking at the
files associated with other view objects in the same package like
ItemsForSale, LineItems, and
ReviewOrder, we see that the additional files only appear as
needed. In the case of ReviewOrder, we didn't need to
customize the view or view row code, and we didn't need a view row message
bundle, so only the ReviewOrder.xml file exists. Everything
necessary for this particular query is captured in metadata. The
LineItems view object illustrates using only code in the
view row class. The ItemsForSale view object is an example
that uses custom code in the ItemsForSaleImpl.java class to
encapsulate bind variable parameter setting, and it exposes these custom
methods through the automatically-maintained
ItemsForSale.java client interface.
Other view
objects in the demo that contain custom methods to encapsulate their bind
variable details are ProductsInCategory,
FindProducts, and ProductList. By
exposing custom view object interfaces, your controller layer can contain code
like Example 5 to set query parameters without being aware
that the query is being performed using SQL.
/* |
By default, ADF creates instances of a
generic data transfer object (oracle.jbo.Row), but by
checking the Expose Accessors to the Client checkbox on
the Java panel of the View Object Editor, you can expose a
typesafe data transfer object interface that lets you access the data using
compile-time-checked accessor methods like getQuantity()
instead of using generic, runtime-evaluated accessor methods like
getAttribute("Quantity").
At runtime, your view object components handle:
While a view object
can be used to produce multiple collections of data
transfer objects, and can iterate each collection using one or more iterators,
in 90% of the cases, developers need to just iterate through a single
collection of results. To cater to this frequent use case, the base
ViewObject framework component implements the
RowSet interface and delegates that interface's methods to
an aggregated instance called the "default rowset". Similarly, all
RowSet objects implement the
RowSetIterator interface, and delegate its methods to an
aggregated instance called the "default iterator".
Figure 30 illustrates how the pieces fit together. This
setup allows developers in most common cases to work with a single view object
instance at runtime, and easily iterate its default collection of data transfer
objects.

Since
view objects encapsulate how data is accessed, it's possible to retrieve data
from datasources other than the normal SQL database query without changing
other parts of your application. As a concrete example of this,
CountryList, CreditCardList,
ExpirationYearList, and
ShippingOptionsList view objects in the ADF Toy Store demo
"query" their data from a standard Java *.properties
file.
View Object queries can automatically be related master/detail by creating additional components called View Links. A view link connects a source and a target view object, and defines (in XML, of course!) which attributes in the source view object row need to correspond to the attributes in the target view object row. By creating view links between view objects, you make it possible for the framework to automate the coordination of the correlated queries on your behalf. You also get the programmatic benefit of being able to call "view link accessor" methods to traverse from one row to its detail collection of correlated rows very easily.
Easily querying and iterating collections of data transfer objects illustrates only part of the view object's value proposition for application developers. View objects cooperate with entity objects and application modules to dramatically simplify creating, updating, and deleting business information as well. In order to appreciate the work this powerful trio is saving us, we'll again study the manual steps that J2EE developers do to modify data when working without a framework.
The controller layer provides data from the end-user to the business service in the form of one or more instances of data transfer objects. The task at hand might involve creating, updating or deleting instances of business objects in the model layer. The steps for creating new data typically require business service code to:
Steps for updating existing data typically require business service code to:
Steps for removing existing data typically involve:
The above steps are those required for the business service to handle a single instance of an incoming a data transfer object from the controller layer. In reality, the incoming data might be a collection of data transfer objects, or a set of collections of one or more different kinds of data transfer objects. In that case, you would combine the steps above with iteration and conditional logic to lookup the correct back-end business object based on the kind of data transfer object coming in.
As with much of the uninteresting application infrastructure code that too many J2EE developers are used to writing themselves, this code is not mind-bendingly difficult to write. However, the unmistakable déjà vu you feel each time you write the code above tells any good programmer that this is a task that can be generalized and driven through metadata to cut down on hand-written code.
The only pieces of information that are unique to each situation are:
Using this information, our implementation of the Value List Handler pattern could be enhanced to support updating the data transfer objects and automatically coordinating them with the back-end business objects.
Luckily, this information is precisely what ADF view objects store in their XML metadata file, along with the other metadata about the SQL query and the attribute names and datatypes of the associated data transfer object. This means that view objects can be related to back-end entity objects in a declarative way, and they store this "attribute map" information for use at runtime to completely automate the coordination between client-modified data transfer objects and the back-end business objects to which they relate. This makes creating, updating, and deleting back-end business data very easy because the controller layer can simply work with the collections of data transfer objects (DTO's) to:
All of the changes made on the data transfer objects in a view object's RowSet collection are delegated automatically back to the appropriate entity objects without user-written code to orchestrate that cooperation. Since the entity objects encapsulate the business rules as we explored in a previous section, this delegation of DTO attribute setting to appropriate entity object attributes causes any relevant business rules to be evaluated. Of course, any failures are thrown as exceptions that can be trapped and presented to the user.
Developers using the ADF framework for their model layer have control over how "eagerly" this delegation between DTO attribute modifications and entity object attribute modifications is performed. For rich-client applications with highly-interactive graphical user interfaces built in Swing, the delegation can be set to be "immediate". This causes errors raised by business rules in the underlying entity objects to be thrown immediately for interactive user feedback as expected.
For web-style applications, the entity-level delegation of changes made to the collections of DTO's can be done in more of a "batch" style. To avoid frustrating the end user by presenting one error at a time to be corrected, ADF offers additional facilities for bundling a maximal set of errors raised by entity-level validation failures. This "bundled exception mode" allows your web application to present all errors to the user in a single round-trip to the browser. The built-in ADF/Struts integration leverages this bundled exception mode.
| NOTE: |
While out of scope for this whitepaper, in addition to the tight integration that ADF provides for working with Struts, it also offers:
|
Recall from above the entity objects representing the domain business objects are not directly accessible to clients. The controller layer can modify entity objects only indirectly in one of these three ways. Two of the three ways involve invoking a business service method. That method's private implementation code can:
Alternatively, the controller layer can directly update any data transfer object "rows" itself, in any collection in the application module's model data map.
In all three cases, any changes effected successfully to entity object instances are held in the entity object cache until the transaction is committed. The application module contains a reference to a transaction object that coordinates the entity object caches during commit or rollback. This coordination insures that any new, modified, or removed entity instances are validated if required, and then persisted appropriately. In effect, the application module component represents the "unit of work" and any entities modified by interaction with the application module business service are automatically "enrolled" in the unit of work, without requiring developer-written code. Of course, when you choose to deploy your application module component as an EJB Session Bean, the ADF transaction object automatically becomes a "slave" to the container-managed transaction so that it participates correctly in transactions that can span multiple session beans.
Figure 31 summarizes in a single picture how the ADF framework components interact. All of the components can be accessed freely by Business Service implementation code inside your application module component. The component interfaces on the left side of the double-dotted line can be used by the view and controller layers as well.

| NOTE: |
The ADF transaction object supports the use of J2EE datasources as well as simple JDBC connection URL's for interaction with the database. |
The Java language comes with a number of basic datatypes like
String, Long, Float,
etc. The ADF framework adds a few additional types called "domains" that live
in the oracle.jbo.domain.* package. These additional types
include:
NumberDateDBSequenceClobDomainBlobDomainArrayThese are designed to improve performance and ease-of-use when working with corresponding types in the Oracle database.
In addition, there are times when you need to create your own custom data types to capture some frequently used concept like an email address or a credit card number that require custom value-level validation whenever they are constructed. The ADF framework provides the simple feature of domains to support this use case.
Figure 32 shows the three custom datatypes we've
created for the ADF Toy Store demo. The
toystore.model.datatypes.CreditCardNumber is a String-based
datatype with construction-time validation that the credit card number has 16
digits. The toystore.model.datatypes.Email domain is another
String-based datatype that checks to make sure its value has
the format of a valid email address.

The
toystore.model.datatypes.ExpirationDate type models
string-based data in the database stored in the format
MM/YY, and internally treats it as a date for comparing the
expiration date with the current date for detecting if a credit card expiration
date is in the future or the past.
Once you've created custom domain types, your entity objects and view objects can use these new types in the same way that they can use any of the built-in domains or the base Java types.
Testing is an important consideration when implementing your J2EE model layer. J2EE experts like Rod Johnson recommend building the business services that support your model layer in a way that allows them to be easily tested outside of the J2EE runtime container environment where they will ultimately be deployed. Being able to run regression tests on business services from the command line simplifies the testing process and allows it to be more easily automated. Simplifying and automating regression tests leads to higher quality software since it encourages engineers to develop more tests and run them more often after making changes to the system.
The open source JUnit testing framework is the defacto standard that most Java development teams use to write and run their regression tests. Oracle JDeveloper 10g features native support for working with the JUnit testing framework (available as a small, separate download explained in the Demo Installation and Setup section above). 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 it generates you:
By using
the above wizard and the built-in JDeveloper refactoring tools, I renamed the
packages containing the test runner, the unit test cases, and the test fixture
into separate packages, and then created several additional tests to exercise
the ToyStoreService application module component.
Running the RunAllTests.java class in the
Testing.jpr project launches the Swing-based JUnit test
runner as shown in Figure 33. This result is what you'll
see if all of the seventeen regression tests pass. If any failures occur, the
progress bar turns red and error messages in the log window alert you to the
failing test.
| NOTE: |
When trying to debug a test failure, just
debug the |

By developing this suite of business service regression tests earlier during the development cycle, I was able to rerun my JUnit test suite after any major new feature additions I made as well as after the many project refactorings that I performed as I continually improved the demonstration. Any problems that my changes might have produced inadvertently were instantly uncovered by the regression tests so that I could easily fix them before moving on to the next task.
| NOTE: |
Running the tests within the IDE requires installing the optional JUnit extension for JDeveloper, available from OTN as a separate download. The installation instructions are in the Demo Installation and Setup section of this document. |
In the MVC
architecture, all of the logic that processes user input, handles interaction
with business services, and decides what page the user should see lives in the
controller layer. The best practice approach for your web application's
controller layer is to implement the
Front
Controller design pattern. Based on the type of incoming request, the
front controller delegates the handling of the request to more task-specific
controller logic. The front controller is typically implemented as a servlet
and configured in web.xml to handle all requests for URL's
related to your application. While you could implement
this controller servlet by hand, the popular
Apache Struts framework
provides a ready-made implementation that handles the job well.
Using Struts, you describe the "page flow" of your web application by:
For example, in a simple web store application, the user
might inspect the contents of her shopping cart by using an action named
/yourcart. Using Struts, actions are executed when the
end-user browses a URL matching the *.do suffix, so the URL
for the /yourcart action might be:
http://yourcompany.com/ADFToyStore/yourcart.do
While looking at her shopping cart, the user might
begin the checkout procedure by clicking on a link to the
/reviewcheckout action, letting her confirm what she is
about to purchase.
A visual representation of these actions and
the links that connect them might look like Figure 34. The
/yourcart and /reviewcheckout actions
show as gear icons, while the JSP pages that represent the view layer are shown
as page icons. The links between the actions, known as "forwards" in Struts
parlance, are shown here as solid lines pointing from the source action to the
target page or action.

The dotted lines represent web page hyperlinks or HTML forms which target the action that their corresponding arrows are pointing to. While the above is a trivial example, you can imagine that a visual diagram of your Struts actions and pages for a more complex application would form an easy-to-understand "page flow" showing where the user can go and how they get can get from one page to another.
As you might imagine, the controller-layer behavior associated with each action is written in an associated Java class. These action classes orchestrate what happens in the web tier in response to each request. Figure 35 illustrates the typical steps that occur each time the user clicks on a link representing a Struts action.

| NOTE: |
We'll explore the mechanics of how the ADF business delegate implementation works in the Understanding ADF/Struts Integration section below. |
Notice back in
Figure 34 how the /yourcart action and
the yourcart.jsp page are in a kind of symbiotic
relationship. The /yourcart action prepares the data to be
displayed by yourcart.jsp, and the
yourcart.jsp page targets any links and submitted HTML forms
back to the /yourcart action so it can handle the
controller-layer details and decide what to do next. When an action and a page
are mutually dependent like this, it's often said that they implement the
"Postback Pattern"; the action prepares the data for the page to render, and
the page posts-back to the action to handle its events.
Figure 36 shows the two different ways that JDeveloper 10g's Struts Page Flow Diagrammer supports implementing the "Postback Pattern" using Struts and ADF. We've already seen the first approach above. You see a discrete icon for both the action and the page, as well as connecting lines in both directions between the two. The alternative is to use the ADF "DataPage" which represents the combination of the action and the page involved in implementing the "Postback Pattern" with a single icon.

As you might expect, using the "DataPage" approach makes for much easier to read page flow diagrams for applications of any non-trivial size. As we'll see shortly, the ADF Toy Store controller layer is implemented using this DataPage approach.
| NOTE: |
The DataPage is a design time abstraction that
simplifies implementing pages that adhere to the Postback Pattern in a
Struts-based MVC architecture. At runtime, a DataPage is just a standard Struts
action and a page to which it forwards control by default, identical to a
blending of the Struts |
Along with other configuration information, Struts keeps
the mapping of action names to action handler classes in the
struts-config.xml file. As shown in the
ToyStoreViewController project in
Figure 37, this configuration file lives in the
standard WEB-INF directory of your application, among the
other elements that comprise your Web Content.

When you double-click on the struts-config.xml file,
by default, JDeveloper 10g shows you a visual page flow diagram. Let's take a
look at what one of these diagrams looks like by studying the page flow of the
ADF Toy Store demo.
Figure 38 shows the page flow diagram for the ADF Toy Store Demo's controller layer. Each DataPage is implemented by the combination of a JSP page and an Struts action that prepares that page's model data to render and handles its post-back events. Since these page/action pairs render as a single icon, the page flow diagram is tidy and easy to understand.

By clicking on the Source tab of the editor, you can
see the XML source code for the Struts action mappings for
the ADF Toy Store demo. Searching for /yourcart, you will
find the XML tags shown in Example 6. Under the
Search main menu, you can use either the
Find... or Incremental Find
Forward
(Ctrl+E) options to
locate it.
<struts-config> |
Among other pieces of configuration
information, the struts-config.xml file contains one
<action> tag for each action in your application. The
<forward> element nested inside Struts action tags provides a
logical name like "reviewcheckout" for the available page
navigation "routes" the user can take from the "/yourcart"
page. In this case, there is only a single forward route that the user can
follow to navigate to review the items for checkout.
The value of
the parameter attribute tells us that our
/yourcart action is related to the
yourcart.jsp page that lives in the
/WEB-INF/jsp directory. We can also see that it is mapped to
the YourCartAction class in the
toystore.controller.strutsaction package. This action
contains the controller logic to handle the "UpdateCart" and
"RemoveItem" events, generated by the user's clicking on
appropriate buttons in the page, by invoking the
adjustQuantitiesInCartAsStringArrays() on the
ToyStoreService to adjust shopping cart quantities. It also
contains a line of code to retrieve the shopping cart total from the business
service for display in the page.
The
YourCartAction class extends the
ToyStoreDataForwardAction class in the
FwkExtensions project. This framework extension class, in
turn, inherits a goodly amount of built-in behavior from the ADF
DataForwardAction, adding a few handy helper methods in the
process that are useful to many of the actions in the Toy Store Demo. The value
of the modelReference property that we see points to some
declarative metadata that ADF uses to simplify data binding.
| NOTE: |
We explore the features of the |
Table 2 gives a description of what each of the actions does.
| Action Path Name | Data Page |
|---|---|
/home |
Toy Store home page |
/showcategory |
Category page, showing products in a category |
/showproduct |
Product page, showing product items available |
/showproductdetails |
Product Details page |
/yourcart |
Shopping Cart page |
/search |
Store-wide Search Results page |
/signin |
Signin page |
/register |
Register New User page |
/editaccount |
Edit Existing Account page |
/reviewcheckout |
Review Checkout page |
/confirmshippinginfo |
Confirm Shipping Information page |
/thankyou |
Thank You page,showing order tracking number |
/revieworder |
Review Order page |
/revieworderxml |
Retrieve "review order" information as XML |
Whenever the
struts-config.xml file is active, the JDeveloper Structure
Window and Property Inspector provide additional design time support. The
Structure Window shows a tree reflecting the internal structure of the
configuration file. You can click on elements in the tree to see or edit their
properties in the Property Inspector. In
Figure 39 you can see all of the ADF Toy
Store Demo's Struts action mappings under the Action
Mappings folder in the tree and the properties of the selected
"/yourcart" action in the Property Inspector.

By selecting an appropriate container node, like Form Beans or Action Mappings for example, you can use the New option in the right-mouse menu to create a new entry of that kind. You can then use the Property Inspector to configure its various properties as you would expect. Alternatively, you can click Edit in the right-mouse menu to edit any of the configuration file items through the Struts Configuration Editor dialog shown in Figure 40. Other right-mouse options certain nodes allow quick navigation to related files. For example, to quickly jump to the code of any of your Struts actions, you can select an action and pick Go to Code on its right-mouse menu.

As we've seen, for working with Struts configuration information you can achieve everything you need to do using a combination of the Struts Page Flow Diagram, the Structure Window, and the Property Inspector. Keep in mind that if you do need (or prefer!) to edit the XML source of the file directory, JDeveloper 10g is happy to oblige and will keep the page flow diagram in sync with your manual edits automatically. The choice is yours.
To complement its features for managing controller-layer action classes, Struts also helps developers process HTML form input in their actions. The Struts controller servlet populates the attributes of a so-called Form Bean with the parameters from the posted HTML form. Then, the developer writing the action class can work with the HTML form values using simple bean getter and setter methods. Since the data entered by the user in an HTML form arrives in string form, and since any errors made to data entered should be redisplayed verbatim so the user can correct them, typically all of the form bean's properties are typed as String.
Example 7 shows that the
/signin action is mapped to the
SignInAction action class in the
toystore.controller.strutsactions package. However,
additional attributes on the <action> tag indicate to the
Struts controller to use the Form Bean named loginform to
encapsulate posted HTML form parameters and to return to the
signin.jsp page in case of any validation errors.
Note that loginform is a logical name defined in the
<form-beans> section which corresponds to the
toystore.controller.strutsformbeans.LoginForm JavaBean
class.
<struts-config> |
With these additional attributes set, at
runtime the struts controller will populate the LoginForm
bean with the values of the username and password HTML form fields from the
signin page when the user clicks to submit the form. Those values are
accessible by the onVerifySignin() event-handler method of
the SignInAction class which will handle the sign-in form's
postback when the user submits the form.
Example 8 shows the onVerifySignin()
method in the form bean. It verifies that neither the username nor password
properties are blank, and then calls the:
public boolean validSignon(String username, String password)
method on the ToyStoreService
business service interface to verify whether the username/password combination
represents a valid webstore user. If any validation check fails, the method
adds a Struts ActionError object to the
ActionErrors collection so the view layer can present
appropriate error messages to the user. We'll dive into more details about how
the error string keys, like those represented by the
INVALIDLOGIN constant, are translated into user-readable
messages in the appropriate language in the Struts and ADF Features for Building Multilingual Applications
section below.
// Method in the SignInAction Struts Action class |
If the login form submission validation above
fails, then the user is returned to the signin page to try
again. If it succeeds, then the code above calls a helper
method to flag the current user as signed in, and then returns the appropriate
page to forward the request to. Since several different actions in the demo can
cause the user to login, this action uses the target
parameter to return the correct "next" page in the flow, based on which action
caused the user to need to log in.
As we've seen above, Struts is a handy framework for implementing the controller layer of your web application. As we'll see in more detail in the Building the View Layer with JSP Pages and JSTL section, Struts also provides some JSP tag libraries to simplify building your view layer. However, it effectively provides virtually no built-in functionality to assist with implementing the model layer, arguably the most important and time-consuming part of your application development. The model layer is where all of your business logic and data access functionality resides. To complete the strong MVC application infrastructure that J2EE developers require, you need a productive approach to building the model layer that integrates nicely with Struts. This section explains how the Oracle ADF framework is the perfect companion to Struts in this venture.
In addition to the business component building-blocks that ADF provides to accelerate model layer development, it also provides specific additional functionality to dovetail with Struts' controller and view layer features. For example, ADF provides:
Of course, JDeveloper 10g provides a productive design time environment for putting all of these feature to work in your own J2EE applications.
| NOTE: |
My companion ADF Data Binding Primer and ADF/Struts Overview whitepaper explains all of Oracle ADF's data-binding and Struts-related features by explaining the implementation of three simple ADF/Struts applications. Here we provide highlights of that information but focus primarily on how we've used those features to implement the ADF Toy Store Demo. |
Oracle ADF includes a "data control" abstraction layer for back-end business services and a binding layer for declaratively connecting front-end user interface controls to data supplied by any data control. These ingredients, based on JSR 227, provide you a consistent and pluggable model layer for your J2EE applications that is on its way to becoming a J2EE standard.
Figure 41 illustrates where the ADF Data Control and ADF Bindings fit into the overall ADF Model, View, Controller, and Business Services architecture.

The key data binding concepts in Oracle ADF are the following:
Data Controls
A data control abstracts the implementation of a business service, allowing the binding layer to access the data from all services in a consistent way.
Iterator Bindings and Control Bindings
Bindings are lightweight objects that decouple back-end data and front-end UI display. An iterator binding provides a consistent way to work with a collection of data objects supplied by a data control. Control bindings provide a standard interface for UI components to interact with an iterator's data or to invoke "action" methods for preparing model data and handling events. Bindings also expose key metadata to simplify building dynamic, multi-lingual user interfaces.
Binding Containers
A binding container is a named group of related iterator and control bindings that you use together for a particular page (or panel) of your application. A binding container is also known as a "UI Model" since it provides the appropriate subset of model data for a specific UI.
Binding Context
The binding context provides the data environment for your application. It contains all of the data controls and binding containers that your application can access.
JDeveloper 10g supports these
data binding concepts at design time in a rich way. Once you've created a
business service, you can expose it as a Data Control and it will appear in the
Data Control palette. Figure 42 shows what our
ToyStoreService data control, based on our
ToyStoreServiceImpl application module component, looks like
in the Data Control Palette. We can see all of the data collections in its
model data map, as well as the custom methods on the ToyStoreService service
interface under the Operations folder.

| NOTE: |
Services built as ADF Application Modules are automatically exposed as
data controls by JDeveloper 10g. By default, the data control based on an
application module named
For
other kinds of business service implementations, there is an additional
Create Data Control... step to perform, and their name can
be changed by editing the |
As you are creating pages with
databound content -- through drag and drop from the Data Control Palette or by
explicit creation -- JDeveloper maintains metadata about your application's
Binding Context. This information describes the data controls your application
is using, the different binding containers that you've created, and the binding
objects they contain. The DataBindings.cpx file is the place
JDeveloper stores this Binding Context metadata. After clicking on this file in
the Navigator, as shown in Figure 43, the Structure
Window displays the Data Controls in use by your application, as well as the
various binding containers (also known as "UI Models") that support the
different pages in your application. Clicking on something in the Structure
Window like the ToyStoreService, the Property Inspector
shows you its properties.

While looking at a given JSP page, you can click on UI Model tab of the Structure Window as shown in Figure 44 to see the details of the binding objects it contains. By clicking on a particular binding object, you can see and edit its properties in the Property Inspector, as well as through the more structured object editor by selecting Edit... on the right-mouse menu.

To simplify using the ADF binding layer in Apache Struts applications, Oracle ADF also provides Struts-specific controller-layer components to seamlessly integrate Struts with the ADF Binding layer. These components include:
ADFBindingFilter that coordinates the use of the business
delegate, business service pooling, and state-management.
BindingContainerActionForm that eliminates the need to
create Struts form beans for each page you build.
DataAction that
implements an easy-to-customize request-handling lifecycle with automatic
support for the ADF data binding functionality, and
DataActionMapping that captures addition declarative
metadata in standard action mapping extension properties used to automate the
data binding.
Rounding off the
JDeveloper 10g IDE support for using ADF and Struts together is the Visual
Struts Page Flow diagrammer and the concept of the ADF "DataPage". The DataPage
simplifies working with a page and a Struts action that want to cooperate to
implement the "Postback Pattern." JDeveloper 10g represents the combination of
a web page and a DataForwardAction that prepares its data
and handled its events into a single node in the Struts diagram. The
DataForwardAction combines the ADF
DataAction's lifecycle features and DispatchAction-like
event-handling with the implicit page forwarding behavior and event-handling of
the basic Struts ForwardAction.
The DataForwardAction inherits event-handling
functionality (from DataAction) that is similar to that
offered by the Struts DispatchAction.
Your HTML forms or hyperlinks can include a special parameter named
event in the request, whose value represents the name of an
event to be "fired" and handled by the DataAction being targeted. So, sending a
parameter event=YourName as a request parameter for form
field will "fire" the YourName event.
For HTML
buttons, whose value attribute serves as
both the user-visible button label and the value of the
control, we support the alternative event-signalling approach of naming the
button event_YourName so that the button can have any
value, and hence any user-visible label, that is
appropriate to the current user's language.
The ADF DataAction supports three key features around the handling of named events.
When an event named YourEvent fires, then...
public void
onYourEvent(DataActionContext ctx) method
in the data action class handling the request, it will be invoked to handle the
event with custom code.
If you have an action
binding in the current binding container named YourEvent, it
will be invoked.
When used in combination with 1, your event-handler code needs to explicitly invoke the default action for the current event by using code like:
if (ctx.getEventActionBinding() != null) {
PageLifecycle p = (PageLifecycle)getPageLifecycle(ctx);
p.invokeActionBinding(ctx,ctx.getEventActionBinding().getName());
}
If you have a Struts forward named
YourEvent, it will be used to determine the next page to
forward control to.
When used in combination with 1, if your
event-handling code invokes ctx.setActionForward(), then
your programmatically set forward takes
precedence.
With these basics fresh in our minds, we can now dive deeper into understanding the ADF/Struts integration and how it's put to work in the ADF Toy Store Demo.
Figure 45 shows all of the "moving parts" together in a single sequence diagram to show the lifecycle of a web page request using the Struts and ADF frameworks in tandem.

http://yourserver/yourapp/some.do arrives
The ADFBindingFilter finds the ADF Binding
Context in the HTTP Session, and if not yet present, initializes it for the
first time.
During Binding Context initialization, the
ADFBindingFilter:
CpxFileName and appends the *.cpx file
extension to its value to determine the name of the
Binding Context metadata file. By default the parameter value will be
"DataBindings", so it will look for a file named
DataBindings.cpx.
ADFBindingFilter invokes the
beginRequest() method on each DataControl in the binding
context. This gives every data control a notification at the start of every
request where they can perform any necessary setup.
beginRequest notification to acquire an instance of the
ApplicationModule from the ApplicationModule pool.
The Struts RequestProcessor forwards control to the
appropriate action class for the "/some" action mapping as
configured in struts-config.xml.
| NOTE: | The
Struts ActionServlet is what initially forwards control to
the RequestProcessor but since
RequestProessor does all the work, we don't bother showing
the ActionServlet in the diagram.
|
If the Struts action class is or extends the ADF
DataAction class, then it goes through a set of processing
steps (known collectively as the request-processing "lifecycle") that
include:
Finding the BindingContainer...
...or initializing it if it's the first time it
has been used. During initialization, the binding objects are created based on
the binding container corresponding *UIModel.xml metadata
file.
prepareModel()
phase
processUpdateModel() phase
invokeCustomMethod() phase, and
findForward() phase.
Each phase of the lifecycle can be overridden by the developer and each method
is passed the DataActionContext object that gives you access
to the BindingContext, the BindingContainer, and all Struts-related elements
like the servlet request and response as well as the action mapping and action
form. The action returns an appropriate Struts ActionForward
indicating which action or page we want to navigate to next.
| NOTE: | If the Struts action class does not extend DataAction, then it must find (and if necessary, initialize) the binding container manually and manage working with the bindings itself. The ADF Data Binding Primer whitepaper has example code illustrating how to do this. |
RequestProcessor forwards control to the action or page
returned by the action as the "next" one to forward control
to.
RequestProcessor returns control to the
ActionServlet which returns control back to the servlet
container.
ADFBindingFilter invokes the endRequest()
method on each DataControl in the binding context. This gives every data
control a notification at the end of every request where they can perform any
necessary resource cleanup.
endRequest
notification to release the instance of the ApplicationModule back to the
ApplicationModule pool.
The sequence diagram illustrates that the binding container is available both for model data manipulation by the controller layer as well as model data iteration and rendering by the view layer.
When you use
JDeveloper to build your ADF-based Struts application, your web application is
automatically configured for you by the design time tools. However, it's useful
to understand what the tool is setting up for you so that you understand how
the setup pieces fit together. Being a J2EE web application, it's
understandable that several of the configuration steps involve the standard
web.xml file. We'll highlight the interesting aspects of the
setup steps in this section.
First, in order to use Struts you need to have the
Struts front-controller servlet named ActionServlet
configured and mapped to a URL pattern. In the ADF Toy Store we're using the
conventional *.do URL pattern to map requests to the
ActionServlet. The relevant portions of the
web.xml file, located under the WEB-INF
directory in the Web Content folder of the
ToyStoreViewController project, look like this:
<web-app>
:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
:
</web-app>
The second step is setting up the
ADFBindingFilter and configuring it to be engaged when
*.jsp or Struts ActionServlet-related
URL's are processed. The relevant portions of the web.xml
file look like this:
<web-app>
:
<filter>
<filter-name>ADFBindingFilter</filter-name>
<filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ADFBindingFilter</filter-name>
<servlet-name>action</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>ADFBindingFilter</filter-name>
<url-pattern>/*.jsp</url-pattern>
</filter-mapping>
:
</web-app>
The ADFBindingFilter needs to know
where it should look for the ADF metadata file that describes the binding
context for your application. It reads a servlet context initialization
parameter named CpxFileName to determine the file name to
look for. We see in the ADF Toy Store's web.xml file, that
this parameter is using the default value of DataBindings.
This means that the ADFBindingFilter will use the
DataBindings.cpx file to read this information.
<!-- web.xml file -->
<web-app>
<description>web.xml file for the ADF Toy Store demo application</description>
<context-param>
<param-name>CpxFileName</param-name>
<param-value>DataBindings</param-value>
</context-param>
:
</web-app>
You can see the DataBindings.cpx file
under the Application Sources folder in the
ToyStoreViewController project. It is an XML file that
describes all of the ADF data controls and binding containers used by the ADF
Toy Store application. Peeking inside it reveals that the ADF Toy Store demo
has just a single ToyStoreService data control. You can also
see the same information in the Structure Window tree display when the
DataBindings.cpx file is active in the navigator or code
editor.
<!-- DataBindings.cpx File -->
<JboProject id="DataBindings" ... >
<Contents>
<DataControl
id="ToyStoreService"
SubType="DCBC4J"
SupportsFindMode="true"
SupportsTransactions="true"
Package="toystore.model.services"
FactoryClass="oracle.adf.model.bc4j.DataControlFactoryImpl"
Configuration="ToyStoreServiceLocal" >
<Parameters >
<Parameter
name="Sync"
value="Immediate" >
</Parameter>
</Parameters>
</DataControl>
:
</Contents>
</JboProject>
Notice that this ADF Business Components-based data
control has properties named Package and
Configuration. The values of these attributes are used by
the ADF implementation of the Business Delegate pattern to lookup an
appropriate instance of the business service whose configuration properties are
defined by the configuration named ToyStoreServiceLocal.
Given the package name, the ADF runtime first looks for a
bc4j.xcfg file in the common subdirectory under the
directory corresponding to the package name. In our example above, it will open
and read the resource named
./toystore/model/services/common/bc4j.xcfg from the runtime
classpath since the package name provided is
toystore.model.services. Inside this file, the
ToyStoreServiceLocal configuration contains a property
indicating the fully-qualified name of the application module that will be used
as our business service implementation.
| NOTE: |
Configurations are a named set of configuration properties that ADF uses to simplify application deployment. To see or edit the configurations for any application module, after selecting the component in the Application Navigator, select the Configurations... option on the right-mouse menu. |
This best-practice abstraction of the business service
lookup lets us easily change model layer deployment strategy without affecting
the rest of our application. As long as you're using ADF Business Components to
build your model layer, then to change an ADF/Struts application from using a
simple JavaBean-based business service to using an application module deployed
as an EJB Session Bean instead, the only change required is changing the value
of the Configuration attribute above in the data control
metadata. This alternative configuration would contain the lookup details for
the EJB session bean, cleanly factored out into XML configuration information
instead of being lost somewhere in your code. Of course, the ADF design time
sets up these multiple configurations for you automatically and gives you
productive dialogs to edit the configuration parameters at design time.
When you build HTML forms to allow the user to enter or modify application data, as we discussed above, the Struts infrastructure abstracts the form data into an instance of a FormBean for easier processing by your actions. Three very frequent tasks that actions need to perform in service of HTML forms are:
Oracle ADF provides two key ingredients that collaborate to make implementing these scenarios easy:
BindingContainerActionForm that eliminates the need to
create separate Struts form beans for each page you build.
DataAction that implements an easy-to-customize
request-handling lifecycle with automatic support for the ADF data binding
functionality
If you use the
/register DataPage to register a new user in the ADF Toy
Store site as shown back in Figure 9, you'll notice that the
country is defaulted to "USA". This defaulting happens automatically because
the Accounts view object being used by this data entry form
is related to the underlying Account entity object, and that
Account entity object has a default value of
"US" defined for its Country attribute.
When the user clicks the (Save Changes)
button on the "Register as a New User" form, the form is submitted back to the
/register.do action. The Struts framework populates the
contents of the form parameters into the FormBean configured for this action
mapping, "DataForm". As we can see in the form-beans section
of struts-config.xml, the "DataForm" form bean is using the
ADF BindingContainerActionForm class.
<form-beans>
<form-bean name="loginform"
type="toystore.controller.strutsformbeans.LoginForm"/>
<form-bean name="DataForm"
type="oracle.adf.controller.struts.forms.BindingContainerActionForm"/>
</form-beans>
This clever form bean is a Struts DynaActionForm that
takes on the properties corresponding to names of the bindings in the current
BindingContainer. The /register action is configured to use
the RegisterAction class in the
ToyStoreViewController project. This action class extends
the default ADF DataForwardAction that applies the data in
the form bean to the appropriate bindings during the "update model" phase of
the its request-processing lifecycle. The bindings turn around and carry out
the out the work of populating the target row in the default rowset of the
Accounts view object. Figure 46
illustrates this, and helps explain what happens next.

| NOTE: |
The
|
Specifically the steps involved in posting the changes from the browser to the database go like this:
RequestProcessor
populates the form bean properties with the values from the submitted form
parameters, and forwards control to the
RegisterAction.
RegisterAction, which inherits its lifecycle handling
functionality from the ADF DataForwardAction, finds its
binding container and updates the model bindings with corresponding values from
the BindingContainerActionForm form bean.
Accounts view object, so
setting the values on the bindings sets these values on the underlying view row
attributes.
event_Save, the RegisterAction invokes
the onSave() event handling method. It inherits this
event-handling functionality from the ADF DataForwardAction.
This method calls the invokeEventAction() helper method to
invoke the action named "Save" in the binding container.
This action binding is bound to the built-in "Commit"
operation on the ToyStoreService data control, so the
transaction is committed.
When the ADF view object row is populated, since the view object was
associated to underlying entity objects at design time, the framework delegates
the attribute setting down to the entity objects. These entity objects contain
business rules that get evaluated and which succeed or fail based on the values
the user has entered. If any business rules are violated, all exceptions are
collected into a single, wrapping "bundled exception" by the ADF framework
which is saved in the DataActionContext so that later when
the ADF DataAction lifecycle method reportErrors() is
called, they may be translated into Struts Action Errors for reporting to the
user.
| NOTE: |
In the Struts Framework Customizations for the Controller Layer section below, we discuss some of the customization we've done to how the ADF bundled exception is translated into errors to present to the user. |
| NOTE: |
In case you're curious to check out the code,
the source for the ADF Struts actions lives in the
|
When your applications need to support an end-user task requiring data entry on many different web pages to complete, you have to invent a scheme for "carrying" the pending data between pages. For example, consider a web application that lets employees file expense reports. The task requires entering many expense line items, potentially requiring multiple pages to complete the task. As the user is entering these details, where do you store the pending expense report data as the end-user enters it bit by bit?
The most common approaches in use today are to store the pending data in:
All of these approaches have downsides, though:
The ADF framework provides a built-in state management facility for your
business services that addresses all of these issues out of the box. Any
application module instance can "snapshot" its pending state to the database
and "reactivate" that state from the database at a future point in time. The
pending state is serialized by generic framework code into a single XML
document, containing only the pending changes, and saved in a single round-trip
to the database in a generic table named PS_TXN, managed by
ADF. This approach has the following benefits:
The
database-backed session management occurs on a different JDBC connection from
the application data access and persistence. Typically you will want to have
the state management table in a separate schema from the application tables.
You can control this by setting the value of the
jbo.server.internal_connection configuration property to
either the name of a JDBC datasource name, or a JDBC URL
connection string. The ADF Toy Store demo sets the value of
jbo.server.internal_connection to use the
jdbc/toystore_statemgmtDS JDBC datasource name so that the
PS_TXN table is created and managed in the
TOYSTORE_STATEMGMT schema, instead of the
TOYSTORE schema where the application tables live.
The ADF application module pool supports three different usage patterns for how application module components are used by incoming browser-user requests. The three modes are called:
Stateless Mode
This retains no business service component state, forcing pending changes to commit/rollback in the current request.
Stateful Mode
This allows pending state to span multiple page requests, using the built-in state management mechanism just described.
Reserved Mode
This dedicates an instance of the application module component to the current browser session until later released in stateless mode, or until the browser session times out.
| NOTE: |
Reserved mode is provided for upward compatibility with JDeveloper 3.2 but is no longer recommended for new applications as it does not offer the same high scalability as the Stateful and Stateless modes. |
When we picked the name "Stateful Mode", we honestly made a really unfortunate choice of terms. We've since come to learn that the word "stateful" conjures up ideas of terrible scalability in web developers' minds. Luckily, it's just the name that's unfortunate; the actual functionality is great. In hindsight, we should have called "Stateful Mode" something like "Managed State with Stateless Performance Mode".
Using "Stateful Mode", you can build web applications with performance very near that of a completely stateless application, but with the programming simplicity of a stateful application. To deliver on this promise, the ADF application module pool implements an algorithm known as "Stateless with Session Affinity". Across multiple page requests in a "stateful mode" page flow, the pool attempts to return the same component instance used by the current session on its most recent request in the current page flow. If this instance is still available in the pool, and it has not been used by another session in the meantime, this scenario gives the highest performance.
If instead the pool needed to hand out that instance to service another browser user session in the meantime, or if the request has come into a different server in the server farm, then the pool grabs any available business service component instance and "reactivates" the current browser user's pending state from the database. This means that if the user had created 5 new rows, updated 2 rows, and deleted 1 row, that these same pending changes would be present after reactivating the state from the database. Also, view object iterator position information is included in the "state snapshot", so that if on the last request your iterator was pointing at the third row in a collection of twenty, your current row will still be row number three after state reactivation.
The executive summary is that if you size the application module pool for the load your application needs to handle, then you get excellent performance and scalability.
Stateful Mode is the ADF default mode of operation. Since the task of filling the user's shopping cart and buying the items is all part of a logical transaction, this default works well for the Toy Store Demo.
To
look at one example, let's take the ADF Toy Store Demo's shopping cart. It is
implemented as an ADF view object named
toystore.model.dataaccess.ShoppingCart in the
ToyStoreModel project, This view object has all
transient attributes and no SQL query. The
YourCartAction Struts action class calls the
ToyStoreService business interface method
adjustQuantitiesInCartAsStringArrays() to add, change, or
remove items from the cart. Since we're using "Stateful Mode", the application
code does not have to worry about how to store the pending shopping cart data.
By visiting the Tuning panel of the View Object editor for
the ShoppingCart view object, we indicate declaratively that we want all
transient attributes of this view object to be passivated by the framework's
state management mechanism. Just checking the checkbox there is the only work
required to leverage this feature.
On each subsequent request, our
actions can access the ShoppingCart view object to work with
its collection of data transfer objects, and programmatically adjust the
contents of the view object's default rowset.
| NOTE: |
To readers who may be familiar with the Oracle Forms product, the view object is being used here like a "non database block" in Oracle Forms. |
The final
action in the demo's page flow, the "/thankyou" action uses
the "Stateless" release mode to indicate that we no longer need any more
pending state to be managed. In order to release in stateless mode, it
overrides the main handleLifecycle() method of the
DataForwardAction and adds a call to the helper method
releaseToyStoreServiceStateless(), which it inherits from
the ToyStoreServiceDataForwardAction:
/* From ToyStoreServiceDataForwardAction */
protected void releaseToyStoreServiceStateless(DataActionContext ctx) {
releaseDataControlStateless(DATACONTROLNAME, ctx);
}
This method turns around and invokes a helper method on
the ToyStoreDataForwardAction, passing in the name of the
data control to release in stateless mode. As you can see in the method below,
it finds this data control and invokes its releaseState()
method, so that all pending statement management info is released at the end of
the request.
/* From ToyStoreDataForwardAction */
protected void releaseDataControlStateless(String dcName,
DataActionContext ctx) {
DCDataControl dc = ctx.getBindingContext().findDataControl(dcName);
if (dc != null) {
dc.resetState();
}
}
Releasing an application module to the pool in stateless mode automatically cleans up any database-backed pending state that was being managed by the framework on its behalf.
As Figure 47 shows, the majority
of the view layer in the ADF Toy Store demo is implemented using JSP pages,
with the exception of one example illustrating how to use an XML/XSLT-based
approach as an alternative. We've consciously placed the pages in
subdirectories of the WEB-INF directory so that users cannot
directly browse the pages. We want to force them to go through our controller
layer for every request, and not be able to bookmark and return to URL's that
short-circuit the controller layer by going straight to the JSP page. Our
controller layer can forward control to pages under
WEB-INF, but they cannot be browsed directly based on rules
laid out in the J2EE specifications. So this is a safe, best-practice approach
for guaranteeing that our controller layer is in total control.

| NOTE: |
The careful reader might rightfully ask, "What is the
Here's why. We want the user be able to access the simple URL:
to get to the home page of the web store instead of
having to remember to type the
Attempts to configure |
The toystore.view package
contains the ToyStoreResources.properties file and the
GlobalErrors.properties file that store translatable strings
used by our view layer pages, for the default language (English). The
additional versions of these two files with the "_it" and
"_de" in the name contain the Italian and German
translations of the same strings, respectively.
DataBindings.cpx is the ADF Binding
Context file that store metadata about data controls and binding
container names. The *UIModel.xml files contain the detailed
binding container metadata, describing the bindings each one contains. Clicking
on DataBindings.cpx in JDeveloper's Application Navigator,
and looking at its details in the Structure Window, you can see that our demo
only has a single data control named ToyStoreService
defined.
Let's start by looking at one of the view layer JSP pages to study how
it's built, the yourcart.jsp page. Recall from the
Lifecycle of a Web Page Request Using Struts and ADF section that the
ADFBindingFilter, DataAction, and
DataControl collaborate to handle the details of acquiring
an instance of your application module, making it available to the controller
and view layers as a data control, and then releasing it at the end of the
request. Using the DataActionContext object that the
DataAction creates at the beginning of each request, your
controller layer code can access:
Once you have the data control in "hand", you can access its data provider and cast it to the application module's business service interface and can directly or indirectly access the collections of data transfer objects in its model data map. Or, by working with the bindings, you can avoid the need directly work with the business service if you choose. Remember that given the roles that the controller layer and the view layer play in the MVC architecture, the controller can modify the model data, whereas the view layer can only iterate over it to present it.
The popular JSP Standard Tag Library (JSTL) provides a set of handy tags that virtually all JSP web page developers need. Using the JSTL tags, your JSP pages can read and write attributes from page, request, session, and application scope, easily iterate over their values, output their values into your page where needed, and conditionalize the rendering of the page. You have always been able to perform these tasks in a JSP page: it's just that with JSTL, you can do them without resorting to pesky JSP scriptlets that make your page harder to manage.
Using the JSTL tags, you identify which
objects and properties you want to work with by using a simple dot notation
called the JSTL Expression Language, known as "EL" for short. To distinguish
themselves from literal values, these EL expressions appear in the attribute
values of JSTL tags surrounded by the ${ and
} like this:
${expression}.
For
example, to refer to the value of an attribute named
bindings, you would use the expression
${bindings}. If the object returned is a JavaBean with
properties of its own, or a
Map
with named members, you can use a dot notation to refer to its members. For
example, if the object named bindings is a map that contains
a member named ShoppingCart, then you can refer to it using
the expression ${bindings.ShoppingCart}.
ADF
binding objects are easy to use with any client technology that can interact
with JavaBean's and the Java Collections Framework, which includes the JSTL tag
library. On each request, the ADF framework's DataAction
makes the current binding container available as an attribute named
bindings and the current Binding Context available as an
attribute named data. You can use EL expressions in JSTL
tags to refer to any information in these contexts.
When working with JSTL tags, in addition to the standard context-sensitive "Code Insight" provided for the tag names and tag attributes, as shown in Figure 48, JDeveloper 10g also provides EL-specific Code Insight for helping you create your EL expressions.

We'll see in the next sections that all the JSP pages in the ADF Toy Store demo make use of JSTL and EL expressions for presenting the data exposed by the ADF binding layer.
| NOTE: |
Click here for handy quick reference to JSTL and its Expression Language. (PDF format) |
Let's start by taking a peek at how the shopping cart page
works. The /yourcart Data Page, which used the related
/WEB-INF/jsp/yourcart.jsp page for its view-layer rendering,
is configured like this:
<action path="yourcart"
className="oracle.adf.controller.struts.actions.DataActionMapping"
type="toystore.controller.strutsactions.YourCartAction"
name="DataForm"
parameter="WEB-INF/jsp/yourcart.jsp"
unknown="false">
<set-property property="modelReference"
value="WEB_INF_jsp_yourcartUIModel"/>
<forward name="reviewcheckout" path="reviewcheckout.do"/>
</action>
It is mapped to the YourCartAction
action class, which performs the setup necessary to retrieve the appropriate
model data. We can see from the value of the modelReference
property above that the binding container name for the
/yourcart DataPage is
WEB_INF_jsp_yourcartUIModel (a name generated for us by the
ADF design time tools). With the yourcart.jsp page active,
clicking on the UI Model tab of the Structure Window shows
the contents of the binding container for the page as shown in
Figure 49. There is one iterator binding
(ShoppingCartIterator), and one range binding
(ShoppingCart).

At
runtime, the default behavior of the DataForwardAction is to
forward control for rendering to its "companion" JSP page. In this case, our
ShowProductDetails action is configured to forward to the
yourcart.jsp page.
The page starts by declaring four tag libraries:
bean:html:c:adf:We're using the
Struts Bean tag library to access the handy <bean:message>
tag, which makes it easy to include translatable text strings into our pages,
based on string keys like "details.title",
"cart.addItem", and
"images.buttons.addtocart".
We're using the
Struts HTML tag library to access the <html:errors> tag, which
makes it easy to displays any errors that occur during runtime processing at
the top of the page in a standard way.
We're using the JSTL Core tag library to iterate our data collections and include values of their attributes in the page. The JSTL tags in use in this page are:
<c:choose>,
<c:when>, and <c:otherwise> which work like
an if/then/else
statement to conditionalize the display based on whether your shopping cart is
empty or not.
<c:choose>
<c:when test="${not empty bindings.ShoppingCart.rangeSet}">
<form action="yourcart.do" method="post">
<!-- etc. -->
</form>
</c:when>
<c:otherwise>
<br><br><bean:message key="cart.empty"/>
</c:otherwise>
</c:choose>
<c:forEach> to
iterate over the items in your shopping cart, and <c:out> to
display attribute values of those items.
<c:forEach var="Row" items="${bindings.ShoppingCart.rangeSet}" >
<tr bgcolor="#f3f3f3">
<td>
<a href="yourcart.do?event=removeItem&id=<c:out value='${Row.Itemid}'/>"
><img src="<bean:message key='images.buttons.removefromcart'/>"
border="0" alt="<bean:message key="cart.removeItem"/>"></a>
</td>
<td><c:out value="${Row.Itemid}"/></td>
<!-- etc. -->
</tr>
</c:forEach>
We're using the ADF tag
library to use the <adf:render> tag that outputs formatted
attribute data based on language-sensitive format masks that you can define in
your business components.
The output that appears in the browser is shown in Figure 50.

Recall that the contents of the shopping cart was kept in transient view
object rows and populated programmatically as the user adds and removes items
from the cart. So, in the /yourcart example from the
previous section, there was no database query involved in rendering the
page.
Next we'll take a look at a page like the
/showproductdetails DataPage that presents queried database
information. The /showproductdetails Data Page, which used
the related showproductdetails.jsp page for its view-layer
rendering, is configured like this:
<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>
It is mapped to the
ShowProductDetailsAction action class, which performs the
setup necessary to retrieve the appropriate model data. That action contains
the code shown in Example 9.
/* From: toystore.controller.strutsactions.ShowProductDetailsAction */ |
We customized the DataAction lifecycle in a
framework extension class to add the additional
initializeModelForPage() method. We override this method in
our actions set bind variable values in our view object's queries
before the DataAction's prepareModel()
lifecycle phase will cause the queries to be executed. This is a common use
case that our additional lifecycle method makes easier to handle.
| NOTE: |
If we don't set the bind variables before the
caused by the underlying database error message:
|
If you look in the
ToyStoreDataForwardAction, you'll see where we've introduced
this new lifecycle method by overriding the existing
prepareModel() method and augmenting its default behavior to
call initializeModelForPage() first if we are not handling
any events:
/* From: toystore.fwk.controller.ToyStoreDataAction */
/**
* Overridden method.
*
* Add two new overrideable methods into the ADF DataAction lifecycle as
* part of the prepareModel() processing to make handling the page
* initialization use case easier.
*/
protected void prepareModel(DataActionContext ctx) throws Exception {
if (!handlingEvents(ctx)) {
initializeModelForPage(ctx);
}
super.prepareModel(ctx);
if (!handlingEvents(ctx)) {
initializeBindingsForPage(ctx);
}
}
The body of the
initializeModelForPage() method back in
Example 9 uses the DataActionContext to
retrieve the id parameter from the
HttpServletRequest and then pass it as an argument to the
prepareToShowProductDetails() method on our
ToyStoreService business service interface. Behind the
tier-independent ToyStoreService interface is the
ToyStoreServiceImpl implementation class, whose
prepareToShowProductDetails() method looks like this.
/* From: toystore.model.services.ToyStoreServiceImpl */
public void prepareToShowProductDetails(String id) {
getFindItems().setItemToFind(id);
getFindItems().executeQuery();
}
It simply encapsulates the setting of the item id to
find, and re-execution of the view object's query. The
getFindItems() method is a view object instance getter
method that the ADF design time tools generate for you when you add a view
object instance named "FindItems" to your application
module's data model. The view layer will be able to access these data transfer
objects representing the view object query's results by using appropriate
control value binding objects. Under the covers, these control value bindings
are related to an iterator binding that knows how to obtain the data from the
"FindItems" collection from the model data map.
Notice again back in Example 9 that we're using the
getToyStoreService() helper method from the
ToyStoreServiceDataForwardAction superclass that returns our
business service's custom service interface named
ToyStoreService.
/* From toystore.controller.strutsactions.ToyStoreServiceDataForwardAction */
protected ToyStoreService getToyStoreService(DataActionContext ctx) {
return (ToyStoreService) getApplicationModule(DATACONTROLNAME, ctx);
}
This code, in turn, calls the
getApplicationModule() helper method from
its superclass:
ToyStoreDataForwardAction. As you can see below, that method
finds the data control by name, then if it is of the expected type for a data
control based on an ADF Application Module, it returns the instance of the
service as an ApplicationModule interface to work
with.
/* From toystore.fwk.controller.ToyStoreDataForwardAction */
protected ApplicationModule getApplicationModule(String dataControlName,
DataActionContext ctx) {
DCDataControl dc = ctx.getBindingContext().findDataControl(dataControlName);
if ((dc != null) && dc instanceof DCJboDataControl) {
return (ApplicationModule) dc.getDataProvider();
}
return null;
}
At runtime, after the
initializeModelForPage() method has set the appropriate bind
variable values, the default behavior of the
DataForwardAction is to forward control for rendering to its
"companion" JSP page. In this case, our ShowProductDetails
action is configured to forward to the
showproductdetails.jsp page that you see in
Example 10. The tag libraries in use are the same
ones as for the yourcart.jsp page. The only difference here
is that the page displays data a single row from
FindItemsIterator, so we don't need a range binding in the
binding container and don't need a <c:forEach> loop. We also
don't have any conditional display logic going on, so this page has no
<c:choose> or <c:if> tags.
<%@page import="org.apache.struts.action.ActionErrors" %> |
The output that appears in the browser is shown in Figure 51.

All of the JSP pages in the ADF Toy Store demo follow this basic approach for iterating and formatting data.
Three different pages in the demo offer the ability to view results a page at a time:
/search - showing results from a storewide
product search
/showcategory - showing the products in a
category
/showproduct -
showing available items of given product type
We would like to create a reusable paging control that displays feedback like 1 - 3 of 18 and conditionally shows Previous and Next links when it makes sense. The pieces of information we need to do the job are:
Each
of these pages includes an appropriate RangeBinding in their respective binding
container. The range binding is an ADF binding object designed for presenting
rows of data in a grid, optionally navigating them a page (or "range") at a
time. You set the size of the range on the related iterator binding to be the
number of rows that you want to appear on a page at a time. If you set the
range size to the value -1, then all rows in the data set
will display at once.
Figure 52
shows the binding container for the /search DataPage, with
its FindProducts range binding, and the underlying
FindProductsIterator iterator binding and the range binding
is related to.

At runtime, the range binding exposes properties that we can access via JSTL expressions to retrieve interesting information about the collection to which its related iterator bound. Some of these properties include:
estimatedRowCount - an
estimate of the rows in the collection
rangeStart - the zero-based row number that appears at
the top the current range of rows
rangeSize - the number of rows to show per
page
Using these three pieces of
information we can calculate all of the bits of information we need.
Example 11 shows the code of our
pagingControl.jsp that you'll find in the
ToyStoreViewController project. You can see that it
leverages the following different JSTL tags, using EL expressions to access the
binding objects it needs:
<c:set> - to set page-local variable values to use later
in the page
<c:choose> - to
perform if/then/else logic without writing scriptlet code
(along
with its nested <c:when> and <c:otherwise>
tags).
<c:out> - to output
the value of an EL-expression
The
Previous and Next links that are
conditionally created are generated to have the
event=Previous and event=Next parameters
in their respective URL's. This will cause the action bindings of the matching
Next and Previous names to be declaratively executed in the
current page's binding container if they exist.
<%@ taglib uri="WEB-INF/struts-bean.tld" prefix="bean"%> |
If you look inside the
search.jsp page's source code, you'll see that it makes use
of this reusable pagingControl.jsp page using
<jsp:include>, passing a couple of parameters needed by the
page with nested <jsp:param> tags. Notice that it uses a
<c:choose> tag that uses the EL not empty
operator to test whether any products have been found or not. If no products
were found, it outputs a specific "No matching products" message instead of
showing an empty table of results.
<c:choose>
<c:when test="${not empty bindings.FindProducts.rangeSet}">
<jsp:include page="pagingControl.jsp">
<jsp:param name="rangeBindingName" value="FindProducts"/>
<jsp:param name="targetPageName" value="search.do"/>
</jsp:include>
<table border="0" bgcolor="#003399">
<!-- etc. -->
</table>
</c:when>
<c:otherwise>
<br><br><bean:message key="search.nomatchingproducts"/>
</c:otherwise>
</c:choose>
The first thing that the
pagingControl.jsp does is take the string-valued
rangeBindingName parameter that we pass in, and lookup the
range binding object having that name using the syntax:
<c:set var="rangeBinding" value="${bindings[param.rangeBindingName]}"/>
Inside the pagingControl.jsp, it can
use EL expressions to refer to properties of that range binding to do its job
in rendering the paging control display. Notice that instead of using the EL
dot notation like ${bindings.SomeRangeBinding}, this
expression uses array-index-style notation using square brackets. This allows
the member name your are trying to access, in this case on the
bindings object, to be provided by another
expression value, instead of providing the name as a
literal.
One detail you might be asking yourself is, "Where do I
set the number of rows per page to display?" Excellent question! Clicking on
the FindProductsIterator in the UI Model tab shown in
Figure 52 and looking at the Property Inspector,
we can see that the Range Size property is set to the
value 3. This means that at runtime, this iterator will
present its results in pages of up to 3 rows at a time. To change to show five
at a time, we would only need to modify this declarative iterator property to
the value 5 instead of changing code. If you need to support
functionality allowing the user to change the number of rows displayed per
page, keep in mind that you can also call setRangeSize() on
your iterator binding.
The
/editaccount DataPage illustrates an example of a page that
displays a data entry form to edit user profile information. It uses a
traditional JSP page layout approach of placing each control to use inside an
HTML form.
The EditAccountAction sets up the model layer in its
initializeModelForPage() method as we've seen above. It
calls the custom service method
prepareToEditAccountInfoFor() on the
ToyStoreService interface, passing in the name of the
current user as an argument.
The implementation of this method in
the ToyStoreServiceImpl class looks what you see in
Example 12. It performs the following three basic
steps:
oracle.jbo.Key object based on the current user's name
passed in
Accounts view object by passing this key to the
findByKey() method on the view object.
/* From: toystore.model.services.ToyStoreServiceImpl */ |
In the corresponding JSP page, named
editexistingaccount.jsp, we use the
<html:form> tag from the Struts HTML tag library to implement
the "Postback Pattern" by having its action post back to the DataPage like
this:
<html:form action="editaccount.do" method="post">
At runtime, the Struts <html:form> tag
sees the action attribute value of
/updateaccount.do and uses it, along with its action mapping
information, to determine that the FormBean named DataForm
is the one that should be used to render this form. The
DataForm form bean is defined in
struts-config.xml to use our ADF
BindingContainerActionForm.
Since we're
rendering the data entry form for just a single "row" of user account
information, we don't need to use the use the JSTL <c:forEach>
in this page and don't need a range binding in our binding container. We simply
format the individual fields in the form, using normal HTML table tags to get
the prompts and controls to line up nicely. As this page shows off several
different techniques in use, we'll try to highlight each of the important ones
in turn.
Figure 53 shows the
binding container for the EditAcount page. Notice that we
have basic attribute bindings for all of the Accounts
attributes except for Country, which is
a list binding (its icon shows a poplist in it). We have two iterator bindings:
AccountsIterator for the main Accounts
information we're editing, and CountryListIterator that will
supply a poplist with the valid country names the user can choose for the
Country attribute.

We
also have an action binding named save that is bound to the
built-in Commit operation on the
ToyStoreService data control.
Example 13 shows the tags in the page that output the HTML table
row containing the prompt and data for the Username
property. Since the username in this application is not updateable once it's
been created, we don't need to render an HTML form control for the data. Using
the <c:out> tag, we can easily just output the value of the
field for display using its corresponding binding object. The
<bean:message> tags are outputting translatable strings from
the default ToyStoreResources.properties properties file to
display the tooltip and the label for the username.
<%-- Username field --%> |
When data needs to be entered or edited, you can use a number
of other tags in the Struts HTML library to render databound controls.
Example 14 shows using the
<html:password> tag to show the Password
property.
<html:password property="Password" size="25" maxlength="30"/>
Recall that the ADF
BindingContainerActionForm presents Struts (and here in
particular, the Struts HTML tag library's tags) with a DynaActionForm bean
having properties that are named after, and "wired to", the bindings in your
current binding container, so when the <html:password> tag
gets and sets the value of the Password property on this
form bean, behind the scenes ADF is coordinating the properties of that form
bean with the corresponding binding objects.
<%-- Password field --%> |
The example also illustrates using the Struts
HTML tag <html:errors> to display any validation errors that
are specific to the Password attribute. Of course, when the
form is first rendered there won't be any validation errors, so this table cell
will be empty. However, if the user submits the form and validation errors in
the model layer are thrown, when this page is rendered again, any errors
related to Password will show up next to the
Password field on the screen. Also, since we know this field
is mandatory, we've included a <bean:write> tag to show the
string corresponding to the key "dataentryform.mandatory" as
a visual marker to the user that the field is required. By default, we render
an asterisk.
ADF entity object and view object
components have a number of built-in features that allow developers to define
control hints like locale-sensitive labels, tooltips, and format masks. The ADF
binding layer exposes this metadata directly on the binding objects for
convenient access by your view layer pages. The <c:out> tags
shown in Example 15 illustrate the EL
expressions for the tooltip and label information that have been associated
with the business object attributes or the view object attributes. If an entity
object has defined a tooltip for one of its attributes named
Firstname, for example, then this tooltip is inherited by
any view objects that include Firstname. Of course, the view
object can also override these control hints if necessary.
<%-- Firstname field --%> |
Each binding object exposes runtime metadata
about the objects to which it is bound that you can access at runtime using EL
expressions. For example, the control value binding for an attribute exposes
information about the underlying attribute in the model layer. In
Example 15 we see an example of using this
metadata to detect at runtime whether a given attribute, like
Firstname, is mandatory or not. Combined with the JSTL tag
<c:if>, we can use this information to conditionally output
the mandatory marker on a required field.
<c:if test="${bindings.Firstname.mandatory}">
<bean:message key="dataentryform.mandatory"/>
</c:if>
One helpful tip to remember is that to get a quick review of all the available properties on a binding object, you can just click on the binding in the UI Model tab of the Structure Window and press the F1 key. The online help topic for the appropriate binding object appears in an IDE window for your reference.
Finally, we look at an example of a data-bound form control like a poplist showing the country where a user resides. As shown in Figure 54 their are two dimensions to the control:
Country binding, reflected by the selection in the list,
and

ADF provides more sophisticated binding objects to handle controls list this that have multiple facets to their data binding requirements. The ADF List Binding caters specifically to poplist-type controls that need to manage both a current bound attribute value, as well as a list of valid choices to present to the user. For hierarchical data, ADF supplies a Tree Binding object that can come in handy on occasion as well.
By clicking on the UI Model
tab of the Structure Window while the
editexistingaccount.jsp is active, you'll see the bindings
we saw back in Figure 53. If you select the
CountryListIterator and look in the Property Inspector,
you'll see that it has a range size of -1. This value
indicates that you want all rows to appear in the list of countries, instead of
only a partial set.
| NOTE: |
The iterator binding range size defaults
to |
Clicking on the
Country binding and selecting
Edit... from the right-mouse menu, you will see the
List Binding Editor shown in Figure 55. It allows you
to see the binding metadata required to support the Country
poplist:
CountryListIteratorAccountsIterator.
Code property from the selected row in the
CountryList will be set into the Country
property on the current row of the target
AccountsIterator.
If
you click on the LOV Display Attributes tab, you can
observe that the Description attribute from the
CountryListIterator is indicated as the value to display to
the user in the list.

Example 16 shows how to use the
<html:select> and <html:optionsCollection> to
leverage this Country list binding to put the poplist onto
our page. The <html:select> is bound to the
Country property of the form bean, which is non other than
our list binding object. The <html:optionsCollection> gets its
data from the nested, List-valued
displayData property of that same Country
binding. The beans in this display data collection 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.
| NOTE: |
For bandwidth optimization, the ADF binding layer expects the
non-visible values of a list binding to be the zero-based index number in their
displayData collection. The ADF list binding handles translating the underlying
|
<%-- Country field --%> |
In contrast to the more "traditional" technique explained above, the Toy
Store demo also includes another page that renders a data entry form in a more
generic, metadata-driven way. Both forms render the same set of controls for
Accounts data, so it's even easier to compare the two
approaches and pick the one that will suit your
applications best.
Example 17 shows the
registernewuser.jsp page (used by the
/register DataPage) which renders the data entry form
allowing users to register on the site for the first time. The
results produced in the browser of this page are nearly
identical to the editexistingaccount.jsp page we looked at
above, but as you can see from the example, the whole form is rendered by the
single <jsp:include page="formControl.jsp"> tag. This tag
works like a reusable component, including the contents of the
formControl.jsp page. The nested
<jsp:param> tags pass three parameters to the reusable
component page:
dataPage
- The name of the current datapage
saveButtonLabelKey - The message bundle key to the
label to display on the (Save) button
saveButtonEvent - The name of the event to
association with the pressing of the (Save)
button.
<%@ taglib uri="WEB-INF/struts-bean.tld" prefix="bean" %> |
So the actual work being done lies in the
formControl.jsp "component" page. The page builds a data
entry form with one data-bound control for each control value binding in the
current binding container.
The page begins with some examples of using the
<c:choose>, <c:if>, and
<c:set> tags to conditionally set up the values of page local
variables named eventName, buttonLabel,
and buttonLabelKey based on whether and which of the
expected input parameters were provided. We'll use these variables later in the
page as part of constructing the (Save) button at the
bottom of the generated form.
<c:choose>
<c:when test="${not empty param.saveButtonEvent}">
<c:set var="eventName" value="${param.saveButtonEvent}"/>
</c:when>
<c:otherwise>
<c:set var="eventName" value="Commit"/>
</c:otherwise>
</c:choose>
<c:if test="${not empty param.saveButtonLabel}">
<c:set var="buttonLabel" value="${param.saveButtonLabel}"/>
</c:if>
<c:if test="${not empty param.saveButtonLabelKey}">
<c:set var="buttonLabelKey" value="${param.saveButtonLabelKey}"/>
</c:if>
The formControl.jsp page goes on to
use <html:errors> tag as part of a "global errors" section of
the input form, where any errors that are not attribute-specific will show
up:
<center>
<table border="0">
<tr>
<td><html:errors bundle="GlobalErrors"
property="<%= ActionErrors.GLOBAL_ERROR %>"/></td>
</tr>
</table>
</center>
Next the form uses the value of the
dataPage parameter passed in by the
jsp:include as part of opening the
<html:form> tag. Notice that since we cannot use EL
expressions directly in the <html:form> tag's action
attribute, we use <c:set> to first set a page local variable
named name with the EL-expression value we want, then we use a JSP scriptlet to
pass the value of this name variable to the action
attribute:
<c:set var="name" value="${param.dataPage}.do"/>
<html:form action='<%= pageContext.getAttribute("name")%>'>
<!-- etc. -->
</html:form>
The form includes the standard hidden field that the 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}'/>"/>
Next we begin the loop that will create
an HTML form field for each control value binding in the binding container.
Inside the <table> tag, we have the following
<c:forEach> iteration:
<c:forEach var="curBinding" items="${bindings.ctrlBindingList}">
<% JUControlBinding cb =
(JUControlBinding)pageContext.getAttribute("curBinding");
if (cb instanceof JUCtrlValueBinding &&
!(cb instanceof JUCtrlRangeBinding) &&
!(cb instanceof JUCtrlHierNodeBinding)) { %>
<!-- Build control for current control value binding in here -->
<% } %>
</c:forEach>
The <c:forEach> loop iterates over the
list of control value bindings from the binding container. Since this list
might include control action bindings, we need to skip
over those when rendering the input controls. Since we want to keep things
simple, we'll also skip over the RangBindings and TreeBindings, too. The EL
expression language doesn't have a built-in instanceof
operator, so we're using a JSP scriptlet to use a regular Java-language
if statement to perform the combination of
instanceof checks.
| NOTE: |
We could have decided
to generically render a set of buttons for any of the action bindings found in
the binding container, which would be of type
|
Since we specified the
var="curBinding" attribute on the
<c:forEach> tag, inside the loop we can refer to this
curBinding loop variable to access the current control value
binding as part of our generic form input control generation. Notice how we're
making use of the binding properties in our EL expressions like
tooltip, mandatory, and
label to access this metadata from the current control
binding.
<c:forEach var="curBinding" items="${bindings.ctrlBindingList}">
<% JUControlBinding cb =
(JUControlBinding)pageContext.getAttribute("curBinding");
if (cb instanceof JUCtrlValueBinding &&
!(cb instanceof JUCtrlRangeBinding) &&
!(cb instanceof JUCtrlHierNodeBinding)) { %>
<tr>
<th align="right" title="<c:out value='${curBinding.tooltip}'/>">
<c:if test="${curBinding.mandatory}">* </c:if>
<c:out value="${curBinding.label}"/>
</th>
<td>
<c:set var="name" value="bindings.${curBinding.name}"/>
<adf:inputrender model='<%= pageContext.getAttribute("name")%>'/>
</td>
<c:set var="name" value="${curBinding.name}"/>
<td>
<html:errors property='<%= pageContext.getAttribute("name") %>'/>
</td>
</tr>
<% } %>
</c:forEach>
To actually render the HTML form control, we use the
<adf:inputrender> tag which is setup to render an appropriate
tag based on the datatype of the current binding's attribute value. As we'll
see, we can also use some additional attribute metadata to customize the way
that the <adf:inputrender> tag does its job. We repeat our
trick of using <c:set> to set a local page variable named
name to the concatenation of the string
"bindings." with the name of the current binding, which is
what the <adf:inputrender> tag wants to "see" as the value of
its model attribute.
Finally, we use the
<html:errors> tag to show any attribute-level validation
errors that might occur next to the control to which they are relevant. We
again use the <c:set> trick to get the value of the
<html:errors> tag's property attribute to
be the name of the current binding.
Last, but not least, we use a
<c:choose> to put the appropriately-labeled
(Save) button at the bottom of the form. We're based on
whether the user specified a button label or a button label key, we either use
the literal label string, or employ the bean:message tag to lookup the label
key for us. We're using our page local variable eventName
that we setup at the top of the page to fill-in the right name for the button
to generate that event when clicked by the user.
<c:choose>
<c:when test="${not empty buttonLabel}">
<input name="event_<c:out value="${eventName}"/>" type="submit"
value='<c:out value="${buttonLabel}"/>'/>
</c:when>
<c:when test="${not empty buttonLabelKey}">
<input name="event_<c:out value="${eventName}"/>" type="submit"
value='<bean:message name="buttonLabelKey"/>'>
</c:when>
<c:otherwise>
<input name="event_<c:out value="${eventName}"/>" type="submit"
value='Submit'/>
</c:otherwise>
</c:choose>
Each ADF Business Component supports setting custom properties that can be read at runtime and be used to drive metadata-driven behavior. For ADF entity objects and view objects which have attributes, you can also set custom properties on individual attributes as well.
To edit attribute
properties, expand the Attributes heading in the respective object editor, and
click on the name of the attribute whose properties you want to modify. Then,
click on the Attribute Properties tab on the editor panel
on the right. Figure 56 shows what this looks
like for editing the custom attribute properties of the
Country attribute of the Accounts view
object.
The <adf:inputrender> tag implementation
consults the value of the EditRenderer attribute property to
see if the attribute has specified a custom renderer. If none is specified, a
default heuristic is used to pick an appropriate control.

In this
example, we've specified the class name
toystore.fwk.view.ListBindingPoplistRenderer which
implements a customized poplist renderer for ADF list bindings. This custom
field renderer extends the default
oracle.jdeveloper.html.StaticPickList renderer to populate
some of its properties based on information it can retrieve from the list
binding object. The source code for the custom renderer (from the
FwkExtensions project in the demo) is shown in
Example 18. You can see that the code accesses the
JUControlBinding object from the datasource, and after
checking that its a JUCtrlListBinding, calls the
getDisplayData() method on the list binding to access the
list display data. In order to populate the String[]
variables for the labels and the values, it iterates over the display data
collection and adds the prompt attribute from each bean in
the collection to the label array. Since the ADF binding layer will expect the
value coming back from the page to be the numerical row number, we populate the
values array by converting the loop variable z to a string
on each iteration. The net effect is that when our generic
formControl.jsp "component" page renders an HTML form for
the bindings in the current binding container, the Country
binding will render as a data-bound poplist populated from the display data
collection configured of value objects in the model data map named
"CountryList".
package toystore.fwk.view; |
If you adopt a generic data form rendering technique like this in your applications, you can more easily insure that all data entry forms in your application look and act similarly since they are all rendered by the same generic code.
Above we've seen the use of translatable strings in a few example JSP pages, as well as references to translatable attribute labels, tooltips, and format masks. In this section we'll quickly cover the features provided by the Struts and ADF frameworks for building applications that need to support user interfaces in multiple languages.
Struts provides a basic facility for using translated messages stored in
standard Java *.properties files. There is a default message
resource file, but you can define secondary message resources as well. The ADF
Toy Store demo uses:
The default Struts resource message file
In
./ToyStoreView/src/toystore/view/ToyStoreResources.properties
A secondary resource message file for global errors identified
by the key "GlobalErrors"
In
./ToyStoreView/src/toystore/view/GlobalErrors.properties
You make Struts aware of the names of your message
resource files in struts-config.xml as shown in
Example 19. The
<message-resources> element with no key
attribute defines the location of the default message resource. Secondary
message resources identify their key by specifying a value for the
key attribute.
<struts-config> |
The messages are stored in a properties file
that pairs a string key with a text message. For example, two lines that appear
in ToyStoreResources.properties are:
:
cart.addItem=Add Item to Your Shopping Cart
cart.removeItem=Remove Item from Your Cart
:
You provide translations of the messages in the message
resource file by creating a properties file in the same directory with the same
name, except with the locale suffix appended to it. For example, the Italian
translations of the messages in ToyStoreResources.properties
are in the ToyStoreResources_it.properties file in the same
directory. The translated message resource files contain the same string
key as the default language, but with the translated
version of the default language's message string. For example, the above two
messages in English look like this in the Italian version of the message
resource file:
:
cart.addItem=Aggiungi al carrello
cart.removeItem=Togli dal carrello
:
If there are messages that do not need to be translated, there is no need to repeat them in the translated message resource files. If a message cannot be found in the locale-specific message bundle, the one from the default message resource file will be used as a fallback.
| NOTE: |
If you want
to provide messages that are sensitive both to the language
and the country components of the locale, you can use a
suffix that includes both like this:
|
As
we've seen in the sections above, you include a message resource string in a
JSP page using the Struts "Bean" tag library's <bean:write>
tag, providing the string key to lookup like this:
<bean:write key="cart.addItem"/>
At runtime, the Struts <bean:write> tag
attempts to return the string for the most specifically matching message
resource file that is available. For example, if the user's locale is Swiss
German (de_CH) then it will:
ToyStoreResources_de_CH.properties file if it
exists, then
ToyStoreResources_de.properties file if it exists,
then
Struts
infers the locale of the current browser user by looking at HTTP request header
properties that browsers send with each request, indicating an ordered list of
the user's preferred languages. Specifically, on each request through the
Struts RequestProcessor, the default implementation of
processLocale() method checks to see if the HTTP Session
attribute whose name is defined by the Action.LOCALE_KEY
constant is present. If the session attribute exists, processing continues. If
it does not exist, Struts infers the locale and saves it in that same session
attribute. The net result is that the browser user's language is determined
once per session by Struts.
ADF entity objects and view objects can have an optional, associated Java message bundle. This component-specific message bundle stores locale-sensitive values of attribute-level control hints like label, tooltip, format mask, and others, as well as component-specific exception messages. The ADF design time wizards automatically handle the maintenance of the component-specific message bundle Java class for the default language. You are responsible for creating translated message bundles with a locale-specific suffix on the end of the name.
For example, for an entity object named
toystore.model.businessobjects.Account, the message bundle
will be named AccountImplMsgBundle.java and will look like
Example 20. Notice that for a component in package
toystore.model.businessobjects, the message bundles (as well
as the custom client interfaces) are created automatically in the
toystore.model.businessobjects.common
subpackage. This package naming scheme emphasizes the fact that message bundle
classes and client interfaces are common to both the
client tier and the server tier. All of the *Impl.java
classes and XML files in the toystore.model.* packages are
not shared by both tiers. Their use is restricted to the server tier. Allowing
the client or web tiers work only with interfaces (and a
minimal number of classes like message bundles) is a best-practice technique
that the ADF design time automatically encourages through consistent naming
patterns and built-in deployment packaging support.
package toystore.model.businessobjects.common; |
In the Toy Store Demo, we've also added by hand an extra entity-specific exception message to the two-dimensional array of strings like this:
static final Object[][] sMessageStrings = {
:
{ErrorMessages.ENTITY_ALREADY_EXISTS,
"Another user has already chosen this name. Please try another."},
:
}
Just as the order of the string keys in the Struts
message resource properties files was not meaningful, the order here of the
{String,String}
elements in the Object array is not meaningful
either.
To create a translated version of this resource bundle,
for example for Italian, you would create the
AccountImplMsgBundle_it class in the
toystore.model.businessobjects.common
package. As a shortcut, you can:
AccountImplMsgBundle.java to
AccountImplMsgBundle_it.javaRename the class declaration in the new
AccountImplMsgBundle_it.java and make it extend the default
language message bundle class, changing:
public class AccountImplMsgBundle extends JboResourceBundle
to:
public class AccountImplMsgBundle_it extends AccountImplMsgBundle
Rename the default constructor in
the new AccountImplMsgBundle_it.java file from:
public AccountImplMsgBundle(){}
to:
public AccountImplMsgBundle_it(){}
This will give you a translated message bundle like Example 21
package toystore.model.businessobjects.common; |
The message bundle classes extend the
oracle.jbo.common.JboResourceBundle framework base class so
the messages can inherit the message "merging" functionality. The merging
occurs when your message bundle's getContents() method
invokes super.getMergedArray(). This allows the translated
bundles to only include messages that need translating while other messages are
inherited from the superclass bundle. To insure that the message merging works
correctly, translated resource bundles should also include this overridden
getContents() method.
View objects also support
custom message bundles for storing locale-sensitive values of control hints.
For a view object named
some.pkg.ViewObjectName, the
custom message bundle will be named
some.pkg.common.ViewObjectNameRowImplMsgBundle.java.
Table 3 lists the components in the ADF Toy Store demo
that have associated custom message bundles.
| toystore.model.* Component | Contains |
|---|---|
*.businessobjects.Account |
|
*.businessobjects.Item |
Indicates format mask for the
Listprice attribute and the default number formatter class
to use.
|
*.businessobjects.Orders |
Contains custom validation error message thrown by the
validateCreditCardExpiration() validation
method.
|
*.businessobjects.Signon |
|
*.dataaccess.ShoppingCart |
Indicates format mask for the
Listprice and ExtendedTotal attributes
and the default number formatter class to use.
|
*.dataaccess.Accounts |
Control hints to hide Status and
Userid attributes.
|
Since the business service supporting the model layer could be deployed
remotely from the web tier that is accessing it, the model layer tracks its own
per-user-session notion of the current user's locale. The
ADFBindingFilter also performs a similar preferred language
inference to automatically set the current language context on the ADF binding
context. Each ADF data control then picks up its locale off that binding
context.
Oracle XSQL Pages is a flexible publishing platform for XML-based information that comes with the free Oracle XML Developers Kit for Java. You create server-side "datapage" templates out of XML documents mixed with "action handler" tags which can assist in building all or parts of your XML-based datapage. It comes with a number of built-in action handlers for pulling XML content from SQL queries, stored procedures, web services, and other sources, and easily combining them with XSLT transformations to produce any kind data format required by the requesting user. The most typical formats are HTML, XML, or Text, but it also supports producing,PDF (when used in combination with Apache FOP), SVG, WML, and others.
When one of the built-in action handlers doesn't fit the bill, you can write your own custom action handlers to assist the publishing engine in getting XML data or performing other programmatic tasks as part of the XSQL page template processing.
Since Oracle ADF business components feature automatic, bidirectional support for working with XML messages, we can easily conjure up a custom XSQL action handler to allow an Oracle XSQL Page to include XML data from an ADF view object. Example 22 shows the code required to accomplish this.
package toystore.fwk.xsql; |
| NOTE: |
In this release, the only kinds of data
collections that support XML reading and writing are those created using ADF
View Objects. Accordingly, the code tests to make sure that the data control
related to the iterator whose name you specify is ADF Business Components-based
Data Control ( |
This custom action handler can then be used inside an XSQL page template using the syntax:
<xsql:action handler="toystore.fwk.xsql.ADFViewObject" iterator="YourIteratorName"/>
We'll see a couple of interesting uses of this handler in the next couple of sections.
Let's say we wanted to allow a user to request a review of their order in XML format. This might be used as part of some workflow automation process so that the order that has been placed can be audited automatically. Generally when two programs need to exchange XML, they first agree on an XML Schema that describes the structure of the XML to be exchanged, and then at runtime they exchange documents that comply with that schema.
Using the JDeveloper 10g XML Schema Designer, I designed the "Toystore Orders" XML Schema that you see in Figure 57. This allowed me to create the XML schema with very little knowledge of the low-level details of XML Schema itself.
Each of the rounded boxes represents an XML element in the schema.
Attributes, like the id attribute of an Order, appear nested within their
element's box. When an element needs to contain a sequence of subelements, I
dragged and dropped a sequence connector
from the component palette onto the
diagram. I defined some custom types like PersonType and
ItemType which then I can reuse in other parts of the schema
as I've done with the OrderedBy element (reusing
PersonType) and the Line element (reusing
ItemType).

To serve up an XML datagram for my order review that complies with this XML schema, I did the following:
/revieworderxml DataPage in the Struts Page Flow
Diagram
revieworderAsXML.xsql as the name of the
view-layer page.
/revieworderxml DataPage in the Struts Page Flow Diagram and
then click on the UI Model tab of the Structure Pane to see <No
Bindings>ReviewOrder data collection in the ToyStoreService data
control.
Added an
<xsql:action> tag to the XSQL page template to use the custom
action handler we created in the previous section, indicating the name of the
iterator I want to use like this:
<Page xmlns:xsql="urn:oracle-xsql">
<xsql:action handler="toystore.fwk.xsql.ADFViewObject"
iterator="ReviewOrderIterator"/>
</Page>
The underlying
ReviewOrder view object component has a query that is
defined to use a bind variable. We need to set the bind variable using an
overridden initializeModelForPage() method as we've done in
previous examples. Therefore, I customized the DataForwardAction class for the
/revieworderxml to use the
ReviewOrderAction class that you see in the demo. It looks
like this:
package toystore.controller.strutsactions;
import oracle.adf.controller.struts.actions.DataActionContext;
/**
* ADF DataAction for the "/revieworderxml"
* and "/revieworder" action mapping (data page).
*
* @author Steve Muench
*/
public class ReviewOrderAction extends ToyStoreServiceDataForwardAction {
/**
* Model initialization logic for this page.
*/
protected void initializeModelForPage(DataActionContext ctx) {
String id = ctx.getHttpServletRequest().getParameter("id");
getToyStoreService(ctx).prepareToShowReviewOrder(id);
}
/**
* Illustrate using an alternative mechanism, implemented in the
* base ToyStoreServiceDataForwardAction class, to release all
* data controls in use by the current binding container in
* stateless mode.
*/
protected boolean releaseStateless() {
return true;
}
}
If we were to access the
/revieworderxml datapage at this stage using a URL
like:
http://localhost:8988/ADFToyStore/revieworderxml.do?id=1001
we would see results like that you see in Example 23, reflecting the canonical XML format produced by the ADF view object.
<Page> |
To transform this into the format expected by our XML Schema "contract", we need to create an XSLT stylesheet like the one shown in Example 24 which transforms the above canonical XML format into the "Toystore Orders" XML Schema-compliant syntax.
<xsl:transform version="1.0" |
Finally, we need to augment our XSQL page
template to engage this XSLT stylesheet to transform the XSQL datapage before
returning to the client. This entails adding one extra line to the top of the
revieworderAsXML.xsql page that looks like this:
<?xml-stylesheet type="text/xsl" href="revieworderASXML.xsl"?>
With that additional line in the template, then requesting the XML order review produces the schema-compliant datagram like this:
<Order id="1001" xsi:schemaLocation="urn:oracle-toystore-order schemas/Order.xsd"
xmlns="urn:oracle-toystore-order"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OrderDate>2004-06-15</OrderDate>
<OrderTotal>19.98</OrderTotal>
<OrderedBy>
<GivenName>Jay</GivenName>
<FamilyName>Tooey</FamilyName>
</OrderedBy>
<Lines>
<Line id="1">
<ItemId>EST-27</ItemId>
<Description>White Dice</Description>
<Quantity>1</Quantity>
<UnitPrice>3.99</UnitPrice>
<LineTotal>3.99</LineTotal>
</Line>
<Line id="2">
<ItemId>EST-18</ItemId>
<Description>Apollo-13 Rocket</Description>
<Quantity>1</Quantity>
<UnitPrice>15.99</UnitPrice>
<LineTotal>15.99</LineTotal>
</Line>
</Lines>
</Order>
After placing an order at the ADF Toy Store web site the user sees a link
that they can bookmark showing their order review. We've designed this
/revieworder page as a DataPage related to an XSQL template
similar to the above /revieworderxml action. Since the model
layer setup was identical to the XML-flavored order review above, we are
reusing the same ReviewOrderAction class that we studied
above. Rather than double-clicking on the /revieworder
DataPage in the Struts Page Flow diagram to create a new action class, instead
I just used the Property Inspector to set the value of its
type property to
toystore.controller.strutsactions.ReviewOrderAction, the
same class we used above.
The revieworder.xsql
template is the same as the one used above, with one exception: the name of the
XSLT stylesheet is different.
<?xml-stylesheet type="text/xsl" href="revieworder.xsl"?>
<Page xmlns:xsql="urn:oracle-xsql">
<xsql:set-stylesheet-param name="orderId" value="{@id}"/>
<xsql:action handler="toystore.fwk.xsql.ADFViewObject"
iterator="ReviewOrderIterator"/>
</Page>
With this in place, clicking on the link from the ADF
Toy Store "Thank You" page after placing an order will show you the
XML/XSLT-based Order Review that you see in Figure 58.
You can study the revieworder.xsl stylesheet in the
./WEB-INF/xsql directory to see how a stylesheet that
produces HTML is just as simple as using it to transform XML into a different
XML format. Remember that the XML document that it is transforming is the
canonical XML produced by the ADF view object that we saw back in
Example 23.

Two of the biggest benefits of framework-based J2EE development are:
For example, if the base ADF
framework EntityImpl class does not support a feature that
you need all of your entity objects to have, not a
problem. No need to file an enhancement request with Oracle Corporation and
wait until the ADF Development team implements your desired feature for you.
Just take the bull by the horns and add the feature yourself by making a
framework customization.
Just create a Java class that extends
oracle.jbo.server.EntityImpl and add your additional
functionality into that class like this:
public class ToyStoreEntityImpl extends oracle.jbo.server.EntityImpl {
/*
* Any new or customized entity object behavior goes here
*/
}
Then, when you create your entity objects for your
application, just set up your components to extend from
ToyStoreEntityImpl instead of from the default
EntityImpl base class.
Figure 59 illustrates how this looks for one of
the entity objects in the ADF Toy Store demo like Account
which does exactly this.

The same opportunity that is available for customizing ADF framework base
classes also exists for many aspects of the Struts framework, too. In this
section we highlight the ADF framework customizations that were made to support
the ADF Toy Store demo. They all live in the FwkExtensions
project.
| NOTE: |
As with all code in the ADF Toy Store demo, the
framework customizations are copiously commented to explain what's going on in
the classes, so please look into the code in the
|
In the
toystore.fwk.controller package we have the
ToyStoreDataForwardAction, which extends the base ADF
DataForwardAction to make the following
customizations:
Added two additional
DataAction lifecycle methods named initializeModelForPage()
and initializeBindingsForPage() which fire just before and
just after the default prepareModel() lifecycle method in
the situation when no "postback" events are being handled by the action. By
default they do nothing: they live to be overridden by subclasses.
initializeModelForPage() is
useful for calling custom methods to set bind variables in your business
service queries before the prepareModel() phase goes about
executing the iterators in your binding container. This setup could be done
with a separate DataAction having a separate binding container and a custom
method invocation association with it, however that meant that every page
requiring bind variable setup would have required an extra action in the page
flow model and I wanted to keep it as absolutely simple as
possible.
initializeBindingsForPage() can be used to
programmatically modify the values of bindings before the page has a chance to
see them.
getApplicationModule() to retrieve an
ApplicationModule based on its DataControl name.
ActionError objects for display to
the user by overriding the reportErrors()
method.
releaseStateless() that can be overridden by a subclassing
action to return true in order to indicate that all data controls in use by the
current binding container should be released stateless at the end of the
request. By default, an ADF ApplicationModule will have its pending state
managed by the framework. There's also an alternative approach illustrated in
this class with the releaseDataControlStateless() method
that subclasses can explicitly call to release a data control by name in
stateless mode at the end of the request.
evalEL() helper method that subclassing
actions can call to evaluate an EL expression.
invokeEventAction() helper method that
subclassing actions can call in their onEventName event-handler methods to
carry out the default declarative before of invoking the action binding whose
name matches the name of the event being handled before or after writing other
custom code in the event handler
method.
In the
toystore.fwk.model.businessobjects package, we created the
ToyStoreEntityImpl class that extends the base ADF entity
implementation base class to add the following features:
UPPER or lower case.
EntityAlreadyExistsException when unique keys are
violated
Both customizations rely on
overriding the framework's setAttributeInternal() method. To
perform the declarative case folding, we add some custom code before calling
super.setAttributeInternal(). To support the custom
exception handling, we write some custom code in a catch
block around the call to
super.setAttributeInternal().
The private
foldCaseOfStringIfCasePropertySet() method illustrates how
to check for a custom attribute-level property named "Case"
and if provided, behave accordingly based on whether its value is
"lower" or "upper". The
State and Country attributes of the
Account entity, as well as the Shipstate,
Billstate, Shipcountry, and
Billcountry attributes of the Orders
entity have this custom property set to "upper" in their XML
metadata.
If you peek in the Account.xml file
or the Orders.xml file in the business objects package
directory, you'll see that the custom attribute-level properties show up in the
XML nested inside the <Attribute> element that they are
related to as shown in Example 25.
<Entity Name="Account" DBObjectName="ACCOUNT" AliasName="Account" |
Figure 60 shows how to customize the base class for an ADF component like an entity object. On the Java tab of the object editor, there is an (Extends...) button that brings up a Framework Base Classes dialog, where you can set any or all of the relevant base classes to use a custom class. The figure shows the Entity Object Wizard, but the process is similar for View Objects and Application Modules as well.

| NOTE: |
I recommend always creating a set of ADF framework extension classes,
even if you currently have no particular need to. When you later need to
address a new feature that affects all components you have created of a given
type, you will be super glad that you listened to this advice. I work with
customers who setup multiple layers of framework customization classes for
their business components. A first layer is a "company wide" set of classes
that extend the base components in |
In the
toystore.fwk.model.dataaccess package, we've implemented the
following customizations:
PropertyFileBasedLookupViewObjectImpl
Customizes the base ViewObjectImpl class to support
"fetching" data from a Java *.properties file instead of
fetching from a JDBC RowSet resulting from a SQL query. The
CountryList, CreditCardList,
ExpirationYearList, and
ShippingOptionsList view objects specify this customized
class as their base view object class.
ViewDefHelper
Exposes utility methods to simplify creating dynamic view object definitions of many kinds, including ones with updatable, entity-mapped attributes.
In the
toystore.fwk.model.service package, the
ToyStoreDBTransactionImpl class provides a customized
implementation of the framework's
oracle.jbo.server.DBTransaction interface. To rewrite the
minimum amount of code possible, it extends the
oracle.jbo.server.DBTransactionImpl2 class, and overrides
the postChanges() method. This customized
postChanges() implementation catches any
DMLConstraintException resulting from the posting attempt
and throws a JboException with an application-specific error
message that it looks up from the from the
toystore.fwk.util.ErrorMessages message bundle based on the
database constraint name that has been violated.
The companion
ToyStoreDBTransactionFactory class in this package extends
the framework oracle.jbo.server.DatabaseTransactionFactory
class to return an instance of the customized
ToyStoreDBTransactionImpl.
The ADF configuration
property named TransactionFactory needs to be set to the
fully-qualified class name of the custom
DBTransactionFactory class, so we would have:
TransactionFactory=toystore.fwk.model.service.ToyStoreDBTransactionFactory
to use our custom DB transaction implementation.
The ToyStoreApplicationModuleImpl class extends the
base ADF Application Module implementation class to add a helper method named
getConfigurationProperty(). This illustrates how to retrieve
a property from the ADF configuration at runtime, and if not present, falls
back to check the value of the same property as a Java System property. This is
used by two places in the demo implementation code
(ShoppingCartImpl in
toystore.model.dataaccess package and
LockAllInventoryItemsHelper in the
toystore.model.services package) to pick between alternate
implementations based on a value of a configuration property.
The
toystore.fwk.rules package contains the
VerifyStateForCountry class which implements the
JbiValidator interface in the
oracle.jbo.server.rules package to provide a custom,
parameter-driven business rule. Both the Account entity and
the Orders entity make declarative use of this reusable
validation rule. For example, in the Account.xml file,
you'll find the following snippet of XML that records the usage of this custom
business rule and captures the parameter values that the generic rule uses to
perform its validation. In this case we see that the
countryAttributeName parameter is set to
"Country" and the stateAttributeName is
set to "State".
<ValidationBean
OperandType="LITERAL"
Name="VerifyStateForCountryRule"
BeanClass="toystore.fwk.rules.VerifyStateForCountryRule" >
<NamedData
NDName="countryAttributeName"
NDType="java.lang.String"
NDValue="Country" >
</NamedData>
<NamedData
NDName="stateAttributeName"
NDType="java.lang.String"
NDValue="State" >
</NamedData>
</ValidationBean>
In the Orders.xml file, you'll find a
similar block of XML tags that set the parameter values differently so that the
countryAttributeName parameter is set to
"Shipcountry" and the stateAttributeName
is set to "Shipstate".
The code for the
VerifyStateForCountry shows several interesting techniques
in use:
toystore.fwk.rules.dataaccess.StatesForCountry view
object
Also, this example illustrates that it's easy to build reusable rules
that make use of ADF components like view objects that can be packaged together
with the rule into a reusable library like the
FwkExtensions.jar that is created by the deployment profile
in the FwkExtensions project.
The
toystore.fwk.xsql.ADFViewObject class implements a custom
Oracle XSQL Pages action handler for involving ADF View Objects from the
current binding container in XML/XSLT based view-layer rendering.
In this section we'll study a few miscellaneous points of interest that we haven't already covered about the demo's implementation.
ADF View Object components have two
similarly-named properties, FetchSize and
MaxFetchSize, that are important to understand for
performance tuning reasons. Both of them can be set either declaratively in the
View Object Editor on the Tuning panel, or
programmatically at runtime with appropriate API's.
The
FetchSize property determines the number of rows at a time
that will be fetched from the database. For example, if a view object's query
identifies 200 rows in the result, and its FetchSize is set
to 50, it will make (200/50=4) four round-trips to the
database to retrieve those 200 rows, fifty at a time. If the
FetchSize were set to 1, which is the
default, then the view object would make two hundred
(200/1=200) round-trips to the database to fetch all those rows.
Sizing the FetchSize too large
can mean using more memory than necessary while fetching if you don't fill up
the JDBC row buffers each time. So unless there were very few attributes in
each row, we probably wouldn't want to set the value to 200 or larger. Setting
the FetchSize too small, or leaving at the default of
1 when that is not appropriate, can mean many additional
round-trips to the database server to retrieve your view object's query
results.
It is easy to understand why you should consider the
value of FetchSize on each of your view objects to see
whether the default of 1 is appropriate. If it's not, set a
reasonable value as a function of the amount of rows you expect to retrieve in
that view object's query. The ADF Toy Store Demo view objects that fetch
mutiple rows like FindProducts,
ItemsForSale, LineItems,
ProductList, ProductsInCategory, and
ReviewLineItems in the
toystore.model.dataaccess package all set
FetchSize to 10.
| NOTE: |
Did I
mention that the default For view objects that plan to fetch and
display more than one row at a time, always consider setting the value to
something more appropriate! As a rule of thumb, if you are displaying data from
a view object |
Next let's look at the other parameter
MaxFetchSize whose name often makes customers confuse it
with the FetchSize we've just discussed.
The MaxFetchSize property sets an upper
limit on the number of rows that view object will attempt to fetch
from the database. The default value is -1 which means to
not apply any maximum limit. In other words, the default is to fetch all rows
from the query. If the above view object's query identifies those same 200 rows
in the result, but its MaxFetchSize is set to
10, then it will only fetch the first 10 of those 200 rows.
| NOTE: |
By default the |
The value of the
FetchSize property is orthogonal to the
FetchSize property we saw in the last section. With
MaxFetchSize set to 10 and FetchSize set
to 50, for example, when querying our 200-row example view object its first 10
rows will be returned in a single round-trip to the database, because the
FetchSize of 50 is enough to accommodate
those first 10 rows. As you would expect, with a FetchSize
of 1 (the default!), the first 10 rows would be fetched in ten round-trips to
the database.
In practice, a non-default
MaxFetchSize value is used to indicate:
MaxFetchSize=1)
MaxFetchSize=0).
Indicating a MaxFetchSize of 1 when
you expect a maximum of one row is good for performance because it prevents the
view object from fetching again to see if there are more rows in the result or
not. Indicating a MaxFetchSize of zero causes the view object to never execute
its query. Rows can be created in that view object or found via the combination
of findByKey() plus setCurrentRow(), but
they won't be fetched via its normal query.
The ADF Toy Store demo
uses the Accounts view object in both of these ways. The
code in that view object's findAccountByUsernamePassword()
method sets the MaxFetchSize to 1 before performing the
query using the username and password to lookup, then it sets it back to
zero.
// From: toystore.model.dataaccess.AccountsImpl
public boolean findAccountByUsernamePassword(String username, String password) {
/*
* We're expecting either zero or 1 row here, so indicate that
* by setting the max fetch size to 1.
*/
setMaxFetchSize(1);
setWhereClause("username = :0 and password = :1");
setWhereClauseParam(0, username);
setWhereClauseParam(1, password);
executeQuery();
boolean found = first() != null;
setWhereClause(null);
setWhereClauseParams(null);
setMaxFetchSize(0);
return found;
}
The prepareToEditAccountInfoFor() and
prepareToCreateNewAccount() methods set the
MaxFetchSize to zero before finding a row by key and before
creating a new row, respectively.
For good measure, I also wrote
code in the application module's prepareSession() method to
force the MaxFetchSize to zero on
Accounts.
// From toystore.model.services.ToyStoreServiceImpl
protected void prepareSession(Session session) {
super.prepareSession(session);
getAccounts().setMaxFetchSize(0);
}
This make sure that even if we don't get the same
application module instance from the pool on each request, that this setting
stays at the default value what we want. The
prepareSession() method is always called by the framework
each time an ApplicationModule instance is used from the
pool, so it's a good place to write code like this.
With the ADF Bindings and Data Controls layers in place, you are free to choose your front-end client technology and your back-end business service implementation. However, it's important to note that this consistency does not imply that all data controls are reduced to have the lowest common denominator functionality of a generic Java Bean. You work with the iterators, bindings, and data controls in a consistent way, but you also can take advantage of unique features offered by a particular data control provider.
For
example, Figure 61 illustrates that the data control based on
an ADF application module has a Sync property that the other
data controls do not. In JDeveloper 10g, the value of Sync
defaults to "Batch" to use the new batch mode.

Batch mode is a network roundtrip-reduction feature that, as its name implies, batches up data-related operations in order to perform them in more coarse-grained chunks.
The client layer works with its application module and its view
objects to perform all of the setup operations on any view objects whose data
is required by the current task at hand. These operations are batched up using
a batch-mode specific client-side ApplicationModule
implementation class, which ADF provides for you behind the normal
ApplicationModule interface when Sync
Mode is set to "Batch". Your application then
makes a call to the following method to perform all of the data operations and
retrieve all the data that you are expecting from the datasources in a single
network round-trip:
yourBindingContainer.refreshControl();
When you call refreshControl() on the
binding container, it calls the sync() method on the data
provider of each data control in use in that binding container. In the case of
an ADF Application Module running in batch mode, this data provider is the
client-side ApplicationModule object. This sync() operation
causes the pending operations and any data changes in the client-side cache to
be sent to the server-side business service and get executed. Any data changes
that are made by components in the middle-tier layer, either by executing
queries or programmatically modifying data, will be returned back to the client
cache in the same round-trip.
Eventually this
batched mode of operation will allow improved application scalability even when
client layer and business service are co-located in the same J2EE web
container, by minimizing the span of time that any given client makes use of an
application module from the pool. In our 9.0.5 release of JDeveloper 10g, when
running co-located like this, our Immediate mode still gives
better performance.
We are focusing a lot of research and development effort on making batch mode the most scalable choice in the future, although we are arriving at that goal in phases. Even if you plan to deploy your client layer (like your Struts action classes) and business service layer in the same web container -- where reducing network traffic would not be a worry you have in mind -- batch mode can still provide value for you during development time.
When you run and test your ADF application in
batch mode, it will guarantee to insure the best practices approach of working
exclusively with component
interfaces on the client layer, and never with the
underlying implementation classes. The guarantees comes from the
ClassCastException errors that you will get in batch mode at
runtime if your client-layer coding has gotten "sloppy" and contains downcasts
to the business service tier's *Impl classes
(ViewObjectImpl, ViewRowImpl,
EntityImpl, or ApplicationModuleImpl, or
your own classes extending these).
Maintaining this best practice approach insures that you can easily redeploy your application into physically separate client and server layer at some later point in time if you decide you need to. It also insures that you can take advantage of clever new optimizations that the ADF team implements in batch mode for scalability in future releases. This explains why we've made Batch mode the default for JDeveloper 10g.
For now, my recommendation for web-based
applications like the ADF Toy Store demo is to use Sync Mode of
Batch during development and testing, but to switch to use a
Sync Mode of Immediate before performing final tests and
deploying in production. Changing the Sync Mode is as simple as flipping the
property as shown in Figure 61.
A final important
point about Batch mode is that the Oracle ADF DataAction tries to shield you
from having to remember to sync your batch-mode application module. In fact,
the default DataAction page handling lifecycle automatically performs the
refreshControl() operation on your binding container at the
appropriate times. Once during the prepareModel() phase at
the beginning of the lifecycle handling, and once at the end of the request in
the refreshModel() phase. This is designed on the assumption
that it will be the view layer to be the first to access the data returned by
the business service, so the batch mode sync operation occurs at the end of the
data action lifecycle, before the Struts RequestProcessor
forwards control to the page. If you need to iterate the data retrieved from
the business service within the Struts action itself, then you'll need to need
to make an extra call to refreshModel() just before your
action code that needs to iterate the data. Failure to do so might result in an
error like:
JBO-25048: Operation YYYYY is invalid for a
working set view object
or other
InvalidOperException type errors.
In versions of ADF beyond 9.0.5 that are currently in development, we've already made the automatic batch mode sync behavior even smarter to further avoid network round trips in cases where we can detect that there is no need to go over the wire. We've also further improved the ability for the DataControl to checkout the application module instance from the pool for an even shorter amount of time.
| NOTE: |
If you run into trouble using Batch mode, you can
always set the value of the |
In the
ADF Toy Store Demo, I've taken a very service-centric approach to my
application business logic. As a best practice, any logic which manipulates the
business objects or governs setup of the queries over that business object
data, I've put into service methods on my ToyStoreService
component. This keeps my controller layer very thin and encapsulates the
implementation details of my service to the maximum.
The other approach would have been to leave much of the data model manipulation code inside my struts actions. I opted against this approach because the service-centric approach made the application functionality much easier to test outside of a web environment. It also just made sense to me to try and accomplish as much as possible over inside the service implementation. Besides the obvious benefit of the additional encapsulation, this approach will maximize how my application benefits in the future to improvements in the ADF batch mode.
While running stress tests on the ADF Toy
Store, I discovered a potential issue when different web users completed their
orders for similar items at almost exactly the same time. The problem would
arise in the stress test if the random orders being placed by the load testing
tool contained intersecting sets of item id's. In a very narrow window of time,
it sometimes occurred that a second user would receive an error that one of the
inventory items was locked by another user when the
ToyStoreServiceImpl class'
finalizeOrder() method was decrementing the inventory
quantities for the items ordered.
I decided to implement a
solution to pre-lock the inventory item rows for all items
in the cart in an "all or nothing" fashion, so that in the rare case where
users might be simultaneously ordering the same items, those orders would never
clash. This approach seemed more scalable to me than simply marking the
finalizeOrder() method as synchronized, because that would
serialize all orders, even when they contained no overlapping items.
The helper method that is called by finalizeOrder() is
a static method on the LockAllInventoryItemsHelper class
that you'll find inside the
ToyStoreServiceImpl.java file
toystore.model.services package. It looks like this:
static void lockAllInventoryItemsInCart(ToyStoreServiceImpl am) {
if (useViewObjectObjectForLockingAllItems(am)) {
ensureLockAllInventoryItemsViewObjectExists(am);
lockAllItemsInArrayUsingViewObject(am);
}
else {
lockAllItemsInArrayUsingStoredProcedure(am);
}
}
As you can see in the code, for educational purposes, I
implemented the "lock all or nothing" solution in two different ways: firstly
as a stored procedure, and secondly as Java code in the application module that
uses a View Object to perform a similar task. In the
useViewObjectObjectForLockingAllItems() method you can see
that it uses the getConfigurationProperty() method that we
added in our Application Module framework customization class to read the value
of the toystore.lockinventoryitems configuration property.
Valid values are ViewObject and
StoredProcedure (the default). In both implementations, if
an order that is about to be finalized cannot lock all of its items, it will
wait for a second, and then try again.
The method that does the call to the stored procedure is the following one:
private static void lockAllItemsInArrayUsingStoredProcedure(
ToyStoreServiceImpl am) {
PreparedStatement ps = null;
try {
ps = am.getDBTransaction().createPreparedStatement(STMT, 0);
ps.setObject(1, arrayOfItemIdsInShoppingCart(am));
ps.execute();
}
catch (SQLException s) {
throw new AlreadyLockedException(s);
}
finally {
if (ps != null) {
try {
ps.close();
}
catch (SQLException s) {}
}
}
}
Using the DBTransaction object that it obtains from the
application module, it creates a JDBC PreparedStatement to
invoke the block of PL/SQL code stored in the private constant variable named
STMT, and looks like this:
begin lock_all_items_ordered(?); end;
It makes sure to close the prepared statement at the end
of the block. It's interesting to note that the bind variable that it passes is
an array typed bind variable that is returned by the
arrayOfItemIdsInShoppingCart() method. At runtime this array
will contain the item id's of all the items being ordered in the shopping cart.
The lock_all_items_ordered procedure uses this array-valued bind variable in a
SQL statement to lock all or none of the rows in a single try:
-- from lock_all_items_ordered
CURSOR c(cp_items table_of_itemid) IS
SELECT null
FROM inventory
WHERE itemid IN (
SELECT *
FROM TABLE(CAST(cp_items AS table_of_itemid))
) FOR UPDATE NOWAIT;
The array parameter is defined to be of type
table_of_itemid which is an Oracle object type defined as a TABLE OF
VARCHAR(10). By combining the TABLE() operator and
the CAST(ArrayExpr AS
ArrayType) syntax, we can use a nested
SELECT statement to select over the array element values as if it were a
table.
| NOTE: |
The |
If
you try setting the ToyStoreService's configuration property named
toystore.lockinventoryitems to the value
ViewObject, then our "lock all or nothing" helper method
will use a View Object based implementation instead of the stored procedure
based approach from above. You can explore the code of the
lockAllItemsInArrayUsingViewObject() method to see how it's
implemented.
I initially tried to create a design-time View Object
to support the SELECT FOR UPDATE NOWAIT query that this
implementation needs to perform, but I ran into trouble because the design time
verification of the correctness of my query's SQL syntax kept failing due to
the FOR UPDATE NOWAIT being in there. Not a problem! It gave
me an excuse to come up with a helper class named
ViewDefHelper in the
toystore.fwk.model.dataaccess package (part of the
FwkExtensions project) to simplify the runtime creation of
view objects.
I wanted to avoid the usual runtime overhead
associated with ApplicationModule's handy
createViewObjectFromQueryStmt() method, which is forced to
perform a round-trip to the database to "describe" the select list of the query
to calculate the appropriate datatype of each view object attribute. So I
dynamically create the ViewDefImpl view object definition
class in ensureLockAllInventoryItemsViewObjectExists() and
then proceed to use some ViewDefImpl member methods along
with some helper methods in ViewDefHelper to create the view
object along with the metadata about its attributes to avoid the runtime
describe of the query. Notice that since the View definition object is shared
by all components running in the VM, we use the synchronized modifier to make
sure to avoid multi-threading issues for the definition creation and
registering. Once the view object definition is resolved and registered, then I
can use it to create a view object instance based on this
definition. Since we always first try to find the view object instance before
going about creating it, the same view object instance will get used over and
over (with only the array-valued bind variable changing on each
execution).
While developing the generic
formControl.jsp page that we studied in the
Rendering Data Entry Forms in a More Generic Way Using Metadata section above, I ran into a small problem. While
entering her personal details in the Register New User
form, if the user made a mistake like entering an invalid email address, the
invalid value was getting reverted to the (blank) value it had
before instead of continuing to show the value the user
needed to correct. I diagnosed the issue to be a bug with the default
HTMLFieldRendererImpl class in the
oracle.jdeveloper.html package, which was incorrectly
reading the attribute value to display in the form field from the underlying
model-layer row instead of reading it from the binding object. It's the binding
object that will cache an invalid value until it is successfully able to be set
on the underlying model object, so by reading the value from the binding
object, we would get the behavior we were expecting.
Until the bug
(#3703925) gets fixed in a future JDeveloper release, I worked around the
problem by creating a custom renderer that reads the value from the binding
object instead of from the row's attribute directly. My
ControlBindingTextFieldRenderer looks like what you see in
Example 26. It extends the default
TextField renderer and overrides its
getHTMLValue() method to change the way it works when we're
dealing with a BindingContainer-based datasource. If we're
working with a BindingContainer datasource, we access the control binding for
the current field being rendered (which has already been setup for us by the
superclass) and get the value of the binding by calling its
getInputValue() method. If we're not using a
BindingContainer, then we return the value of what the
superclass would have done before.
package toystore.fwk.view; |
To illustrate another technique for perform
more global overrides of the field renderer classes that the
<adf:inputrender> tag will use, I added the helper method you
see in Example 27 to my
RegisterAction, which is the
DataForwardAction that supports the
/register DataPage to register new users.
We are able to set the default edit renderer class for any given Java type by setting a HTTP request attribute whose name looks like:
JavaTypeWithDotsConvertedToUnderscores_EditRenderer
to the fully-qualified name of the edit renderer class it should use.
Accordingly, in order to have my
ControlBindingTextFieldRenderer used as the default edit
renderer class for edit controls rendered for attributes of type
java.lang.String and
toystore.model.datatypes.common.Email, I've set the values
of the request attributes named:
java_lang_String_EditRenderertoystore_model_datatypes_common_Email_EditRenderer to the fully-qualified name of the
ControlBindingTextFieldRenderer class.
| NOTE: |
Similar overridability is available for display rendering using the
|
//From: toystore.controller.strutsactions.RegisterAction |
I call the helper method from an overridden
handleLifecycle() method in my
RegisterAction like this:
// From: toystore.controller.strutsactions.RegisterAction
protected void handleLifecycle(DataActionContext ctx) throws Exception {
setupDefaultFieldRenderers(ctx);
super.handleLifecycle(ctx);
}
The handleLifecycle() method is the
root method to override in a DataAction to perform custom code before the
built-in lifecycle starts or after it is done. Once Bug 3703925 gets fixed,
we'll be able to comment out this workaround code. Luckily, since we were using
a framework, working around problems we encounter is typically just as easy as
adding new behavior. Both involve customizing a framework base class and
getting our customized class to be used instead of the default framework
implementation class.
The
fillInCartItemDetails() method in the
ShoppingCartImpl view object class illustrates two different
approaches for looking up information using the ADF business components. As in
the example above, the implementation that is used at runtime depends on the
value of a configuration property, in this case named
toystore.shoppingcartlookup. When that parameter has the
value "ViewObject", it uses an approach that makes use of an
instance of the ShoppingCartItemLookup view object. On the
other hand, if the parameter has the value "EntityObject",
it uses an approach that finds the inventory item entity object instance by
primary key from the entity cache.
Often your data
will contain single-character "flag" fields. In the ADF Toy Store Demo, our
example is the "In Stock" field that tells whether an item is in stock or not.
In the ItemsForSale view object (in the
toystore.model.dataaccess package) you can see that the
expert-mode query uses a DECODE() statement to return a
string like "Back Ordered" or "In Stock" depending on the value of the
INVENTORY.QTY, the quantity currently in the inventory:
SELECT Item.ITEMID,
ITEM.ATTR1||' '||PRODUCT.NAME AS NAME,
Item.LISTPRICE,
Item.PRODUCTID,
DECODE(INVENTORY.QTY,NULL,'Back Ordered',
0,'Back Ordered',
'In Stock')
AS IN_STOCK,
/* etc */
On the other hand, the ShoppingCart
view object contains a simple transient field named InStock
which takes on the values of either Y or N to indicate if the item is in stock
or not. The value of this is determined by the
fillInCartItemDetails() method we discussed in the previous
section. When the yourcart.jsp page goes to display the
InStock information, rather than showing the "raw" Y or N
value, it uses the Y or N as part of the string key name for a translatable
string in the Struts message resource file. If first uses the JSTL
<c:set> tag to set a page-local 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.
<td>
<%--
| NOTE: Here we are using the value of the "InStock"
| ---- attribute, which will be "Y" or "N", as part
| of the key to lookup a translated value to
| show to the user like "In Stock" or "Back Ordered"
+--%>
<c:set var="inStockMsgKey" value="cart.instock.${Row.InStock}"/>
<bean:message name="inStockMsgKey"/>
</td>You might have noticed that every one of our web pages
includes an <html:errors> tag at the top. By default, the ADF
DataAction 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 the
errors being reported to the Struts layer, but never displayed. This means that
even some kind of unexpected error will only show up if you explicitly render
the Struts errors using the <html:errors> tag. The JDeveloper
10g Data Control Palette does its best to insure you always have an
<html:errors> tag in your page, but in case you develop your
pages in a more manual way, forewarned is forearmed.
JDeveloper 10g supports the ability to create deployment profiles that encapsulate all the details required to build and package Java archive files of various kinds for deployment. It also supports popular the build and packaging tool Apache Ant. The ADF Toy Store Demo contains examples of both approaches. This section describes the deployment profiles and Ant build file in use in the ADF Toy Store Demo.
As shown in Figure 62 the
ADFToyStore.deploy is a WAR deployment profile in the
ToyStoreViewController project that depends on all of the
other deployment profiles. It packages up the JAR files produced by these
other deployment profiles to produce a WAR file and an EAR
file for deploying to Oracle Application Server or an external application
server that has been configured with the ADF Runtime Installer for running
ADF-based applications.

The
ToyStoreModel project contains an ADF business components
deployment profile named ToyStoreModel.bcdeploy. This was
created by selecting the ToyStoreService application module
in the Application Navigator, and selecting Business Components
Deployment... on the right-mouse menu. In the wizard that
appears, we selected the Simple Archive Files option. By
expanding the node in the navigator, you can see that an ADF deployment profile
contains two "children" profiles which are defined to package up the compiled
artifacts like...
*.common subpackages
of the project into the ToyStoreModelCSCommon.jar
file
*Impl.java) and XML definition files, into the
ToyStoreModelCSMT.jar file
For a client like our Struts web controller layer that is using the ADF
model layer in the same web container as a set of simple JavaBeans, both of
these JAR files must be in the web container's classpath. For a remote client
like a Swing application -- or a web-tier accessing a remotely-deployed
application module deployed as an EJB Session Bean -- only the
ToyStoreModelCSCommon.jar is required in the client tier. In
that scenario, the ToyStoreModelCSMT.jar would be required
in the class path of the EJB Session Bean supporting the "server tier" of that
distributed application.
For completeness, Table 4 lists all the deployment profiles in use in the ADF Toy Store Demo.
| Project Name | Deployment Profile | JAR File(s) Produced |
|---|---|---|
ToyStoreViewController.jpr |
ADFToyStore.deploy |
|
ToyStoreModel.jpr |
ToyStoreModel.bcdeploy |
|
ToyStoreViewController.jpr |
ViewController.deploy |
|
FwkExtensions.jpr |
FwkExtensions.deploy |
|
Table 5 shows the additional supporting JAR files that are required for Struts/ADF support.
| JAR Files | Description |
|---|---|
|
ADF/Struts support and ADF tag library implementation |
|
Struts framework runtime libraries |
|
Oracle XSQL Servlet runtime |
Using the
ADFToyStore.deploy deployment profile, deploying and
redeploying are a single step after having first performed
two prerequisite steps:
Defined an application server connection for your target application server
You can do this in the JDeveloper 10g Connections Navigator by right-clicking on the Application Server folder and picking New... from the right-mouse menu.
In this section we'll cover the basics to deploy the application using the IDE.
| NOTE: |
If you are deploying to an Oracle Application Server 10g (v9.0.4) server, it comes pre-installed with a JDeveloper 9.0.4 version of the BC4J framework (precursor to Oracle ADF). To run an application built using JDeveloper 10g (v9.0.5) you need to use the ADF Runtime Installer to install the more up-to-date JDeveloper 10g (v9.0.5) ADF framework version to that server. |
If you are deploying an Oracle ADF application built with JDeveloper 10g to any of the following servers, we offer automatic framework installation facilities:
To install the latest version of the ADF framework on one of the above servers, select Tools > ADF Runtime Installer > YourServerType. The wizard will ask for a root directory for your server installation. Once you provide that, the wizard will configure your server for you after you confirm the operation in the next step. It will produce a log file detailing all of the file copy and configuration file modification that it performed (as well as reporting any errors it ran into), and open that log file for your viewing in an IDE editor window.
To deploy the ADF Toy
Store demo to one of these predefined application server connections, just
click on the ADFToyStore.deploy node in the Application
Navigator and select Deploy To >
YourServerName in the right-mouse
menu.
If are running a server other than Oracle Application Server or Standalone OC4J, you will need to manually setup the JDBC datasource mappings for the two datasources used in the Toy Store demo. Appendix 2 provides an example of those instructions for Tomcat servers.
| NOTE: |
If you are deploying your ADF-based application onto a J2EE application server other than the ones that JDeveloper 10g supports in the IDE, there are "How To" documents on the OTN JDeveloper HowTo Page and the OTN JDeveloper HowTo Archive Page that will lead you through the manual steps. These documents provide a list of the base ADF framework libraries that are required to install on your application server. In the future, the standardization of J2EE deployment across J2EE-compliant servers with JSR-88, and JDeveloper's support of it, will greatly simplify this situation. |
Apache Ant is the defacto standard for
Java build tools. The ADF Toy Store Demo comes with a standard Ant
build.xml file that you can use to build the demo from the
command line. Table 6 gives an overview of all the Ant
build targets and what they do.
| Build Target Name | Description |
|---|---|
all |
Build
and assemble the ADFToyStore.ear file
(Default)
|
clean |
Remove the ./build and ./deploy directories |
allclean |
Clean,
then build and assemble the ADFToyStore.ear
file
|
init |
Create the ./build and ./deploy directories |
fwk-extensions |
Build the framework extensions to produce
ToyStoreFwkExtensions.jar |
model-layer |
Build the model layer to produce
ToyStoreModelCSCommon.jar and
ToyStoreModelCSMT.jar |
view-controller |
Build the view and controller layers to produce
ToyStoreViewController.jar |
war |
Assemble the ADFToyStore.war
file
|
ear |
Assemble the ADFToyStore.ear
file
|
deploy |
Deploy ADFToyStore.ear file to OC4J
Standalone
|
deployclean |
Clean, then deploy ADFToyStore.ear
file
|
Since JDeveloper 10g also supports Ant, you can build any of your Ant build targets right from the Application Navigator by clicking on the right-mouse menu as shown in Figure 63.

To build the
ADFToyStore.ear file, pick the ear build
target.
To build the ADFToyStore.war file from,
pick the war build target.
When you use Ant from
within the JDeveloper 10g IDE, it sets the jdev.home
parameter to the home directory where the currently-running version of
JDeveloper 10g lives. This way you can make references in your Ant build script
to files that are relative to the JDeveloper installation home. The ADF Toy
Store's build.xml file does this. This means, that when
trying to build from the command line, if you try to perform a build target
like this:
$ ant ear
You'll get a handy warning message telling you:
Buildfile: build.xml
init:
BUILD FAILED
C:\adftoystore\build.xml:52: Need to set jdev.home property to JDev home dir!
So, you'll need to run the build like this instead,
passing a value for jdev.home on the ant command
line:
$ ant -Djdev.home=C:\jdev\9051 ear
Where here C:\jdev\9051 is an example
of what you would replace with your JDeveloper installation home
directory.
I have setup Ant build targets named
deploy and deployclean which will try to
perform the OC4J Standalone installation from within the build script. You'll
likely need to edit the properties in the section of build.xml that looks like
this to get the deploy-from-Ant to work, supplying values that make sense for
your environment.
<!--
| Local OC4J Connection Information for "deploy" task
+-->
<property name="oc4j.admin.jar" value="${jdev.home}/j2ee/home/admin.jar"/>
<property name="oc4j.url" value="ormi://localhost/"/>
<property name="oc4j.username" value="admin"/>
<property name="oc4j.password" value="welcome"/>
<property name="j2ee.app.name" value="ADFToyStore"/>
<property name="j2ee.web.app.name" value="ADFToyStore"/>
<property name="oc4j.web.site" value="http-web-site"/>
<property name="j2ee.context.root" value="ADFToyStore"/>
These parameters are used in the deploy task as
arguments passed to the command-line deployment utility
admin.jar.
In the sections above, we analyzed all the key aspects of the ADF Toy Store demo to better understand each of the "moving parts" in a typical J2EE web application adhering to an MVC architecture. Along the way, we noted numerous Oracle JDeveloper 10g features for simplifying the development of our model, view, and controller-layer components. For example, JDeveloper 10g offers integrated support for:
struts-config.xmlTo get more step-by-step assistance in beginning to use Oracle ADF in your own J2EE application development, the Oracle by Example Series for Oracle JDeveloper 10g (on our OTN web site) offers a number of step-by-step tutorials that can help you get started. After going through the Installing the Sample Schemas and Establishing a Database Connection setup steps, try any or all of the following for some additional "practice" with using ADF, JSP, and Struts:
While this article focused exclusively on using ADF with JSP pages, the following two tutorials illustrates two alternative client technologies that we support for your application building:
| NOTE: |
For developers who may not have time to learn enough about Struts to feel comfortable using it on a project, Oracle ADF can also be used with a more traditional "Model 1" JSP approach which does not use an explicit controller layer. The Creating a JSP Application with ADF Using a Model 1 Architecture tutorial provides an overview of using ADF in this way. |
By exploring the details of this ADF Toy Store application built using Oracle ADF and Apache Struts, we've seen how you can build J2EE-compliant applications by standing on the shoulders of existing J2EE frameworks. By doing so, we've seen that the code that we actually write in minimal, and when required, is code that is focused directly on the business application problem at hand. Both Struts and ADF make extensive use of XML-based configuration information for their framework components, which further simplifies development by driving a lot of framework behavior from this metadata instead of using a heavy code generation approach.
Along the way, we've seen that the Oracle JDeveloper 10g IDE provides excellent support for putting both Struts and ADF to use in real application scenarios. Over 800 developers inside Oracle Corporation alone are using the ADF framework daily for building their self-service web applications as part of the Oracle e-Business Suite. Hundreds of external customers are using JDeveloper and ADF as well, as evidenced by the enthusiastic questions we get every day on the Oracle Technology Network JDeveloper Discussion Forum -- a forum you can use as well as you experiment with Struts and ADF.
Hopefully after discovering the power of framework-based J2EE development, you will be asking yourself, "How could I think of building my next application any other way?"
| NOTE: |
If you would like to follow tips and tricks about using Oracle technology more effectively, including on using JDeveloper 10g and Oracle ADF, please see the RSS Feeds and Blogs on Oracle Technology Network for a number of useful links. My own "Dive into BC4J and ADF" web log is in the list there, too. Since these web logs (known
popularly as "blogs") syndicate their content using the XML-based
RSS
format, you can use any RSS news reader program to aggregate the stories that
appear on these sites. Instead of having to remember to visit each site that
you want to follow, you subscribe to their RSS feed and your RSS news reader
periodically checks your feeds for new entries and brings the news to you to
read on your own schedule. RSS news feeds are typically identified on a site by
an |
If you are you are using DHCP to get an
automatically-assigned IP address, sometimes when JDeveloper launches your
default browser and starts the embedded OC4J, you may see an HTTP error saying
the ADF Toy Store home page does not exist. Typically this can be corrected by
experimenting with different options for how the Embedded OC4J server refers to
the local machine in the URL. One option that always works for me is setting
the preference to use localhost as your machine name in the
URL as shown in Figure 64. The access this preference
page, select Tools | Embedded OC4J Server
Preferences... menu option, and visit the
Startup category.

If you see an error like the following (additional details removed for
clarity) when you click on one of the categories to browse items in the store,
this means that the database connection has failed. Check that the database is
running and if it is, double-check the connection details that you have
provided in the two JDeveloper named connections (toystore
and toystore_statemgmt) in the setup instructions.
500 Internal Server Error
JBO-30003: The application pool (ToyStoreServiceLocal) failed to checkout an application
module due to the following exception:
oracle.jbo.DMLException: JBO-26061: Error while opening JDBC connection.
:
## Detail 0 ##
:
java.sql.SQLException: Io exception: Connection refused
(DESCRIPTION=(TMP=)(VSNNUM=153092608)(ERR=12500)
(ERROR_STACK=
(ERROR=(CODE=12500)(EMFI=4))
(ERROR=(CODE=12560)(EMFI=4))
(ERROR=(CODE=530)(EMFI=4))
(ERROR=(BUF='32-bit Windows Error: 2: No such file or directory'))
)
)
In JDeveloper 10g, there is automatic support for
installing the ADF Runtime on Tomcat, as well defining Application Server
connections to a Tomcat server and deploying to it. In fact, with a Tomcat-type
application server connection defined, you can just click right-mouse
Deploy to option on the
ADFToyStore.deploy deployment profile in the
ToyStoreViewController project to carry out the
deployment.
However, there is one manual step left to do regarding
the configuration of the two JDBC datasources that the Toy Store application
uses: jdbc/toystoreDS and
jdbc/toystore_statemgmtDS. Similar manual steps would be
required to map the logical JDBC datasource names to physical JDBC connections
with any other J2EE server as well. For the record, this section explains how
to configure these two datasources on the Tomcat server.
The ADF
Toy Store demo's web.xml file contains the following two
resource reference definitions for JDBC datasources named
jdbc/toystoreDS and
jdbc/toystore_statemgmtDS:
<resource-ref>
<res-ref-name>jdbc/toystoreDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<res-ref-name>jdbc/toystore_statemgmtDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
This is the J2EE container-independent resource
definition which is always paired with container-specific setup information for
these resources. To provide the Tomcat-specific datasource configuration
details for the "/ADFToyStore" web application context, edit
the file, and
find the place inside the appropriate TOMCAT_HOME/conf/server.xml<Host> element, where
you can paste the additional ADF Toy Store <Context>
definition. It will look like this:
<Server>
<Service>
<Engine>
<Host>
:
<Context>
<!-- Existing other <Context> entries -->
</Context>
:
<!--
| Paste in Entire <Context> element show below right here
+-->
</Host>
</Engine>
</Service>
</Server>
Paste in the following ADF Toy Store
<Context> entry at the indicated location. You will need to
edit the value of the JDBC url parameter to have the
appropriate @machine:port:sid
string to match the database where you have created the
TOYSTORE and TOYSTORE_STATEMGMT users and
installed the ADF Toy Store tables.
<!--
| BEGIN ADF Toy Store Tomcat DataSource Config Section
| Change JDBC URL info to match your database!!
+-->
<Context path="ADFToyStore" docBase="ADFToyStore">
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="localhost_ADFToyStore."
suffix=".log"
timestamp="true"/>
<Resource name="jdbc/toystoreDS"
auth="Container"
type="oracle.jdbc.pool.OracleConnectionPoolDataSource"/>
<Resource name="jdbc/toystore_statemgmtDS"
auth="Container"
type="oracle.jdbc.pool.OracleConnectionPoolDataSource"/>
<ResourceParams name="jdbc/toystoreDS">
<parameter>
<name>factory</name>
<value>oracle.jdbc.pool.OracleDataSourceFactory</value>
</parameter>
<parameter>
<name>url</name>
<value>jdbc:oracle:thin:toystore/toystore@127.0.0.1:1521:ORCL</value>
</parameter>
</ResourceParams>
<ResourceParams name="jdbc/toystore_statemgmtDS">
<parameter>
<name>factory</name>
<value>oracle.jdbc.pool.OracleDataSourceFactory</value>
</parameter>
<parameter>
<name>url</name>
<value>jdbc:oracle:thin:toystore_statemgmt/toystore@127.0.0.1:1521:ORCL</value>
</parameter>
</ResourceParams>
</Context>
<!--
| END ADF Toy Store Tomcat DataSource Config Section
+-->
The ADF Toy Store Demo comes with a testing servlet in
the ToyStoreViewController.jpr project named
toystore.controller.TestJDBCDatasources. The web.xml file is
pre-configured to map this servlet to the path
"/testjdbcdatasources". So, by pointing your browser at the
URL:
http://yourtomcatmachine:yourport/ADFToyStore/testjdbcdatasources
You will see the following returned in the browser if the two required datasources are properly configured:
Successfully looked-up 'java:comp/env/jdbc/toystoreDS' DataSource
Successfully issued a ROLLBACK command using connection from this datasource
Successfully looked-up 'java:comp/env/jdbc/toystore_statemgmtDS' DataSource
Successfully issued a ROLLBACK command using connection from this datasource
After you have performed these steps, you should be able to point your browser at:
http://yourtomcatmachine:yourport/ADFToyStore
to run the ADF Toy Store demo on Tomcat.