Building a Web Store with Struts & BC4J FrameworksBuilding a Web Store with Struts & BC4J FrameworksSteve Muench, BC4J Development Team Oracle Corporation September 25, 2003Abstract By
exploring the details of a sample application built using two popular
off-the-shelf J2EE frameworks, Jakarta Struts and Oracle Business Components
for Java (BC4J), 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
Oracle9i JDeveloper IDE provides for framework-based
development using Struts and BC4J. Contents Lessons from the Past Advice for the Future Rebuilding a Web Storefront with Struts and BC4J Demo Installation and Setup Quick Tour Through the Demo Browsing Products and Adding Them to Your Cart Trying Out the Demo in Another Language Checking Out and Signing In Register a New User and Editing an Existing User's Profile Experimenting with State Management and Failover Support Dissecting the Demo How the Application is Organized Into Packages and Projects Advantages of a Model/View/Controller Architecture Implementing the Model Layer Using BC4J Framework Components Testing Business Tier Components with JUnit Implementing the Controller Layer with the Jakarta Struts Framework Understanding BC4J/Struts Integration View Layer: JSP Pages and Struts/BC4J Tag Libraries Struts and BC4J Features for Building Multilingual Applications Customizing the Default Framework Behavior Deployment and Packaging Considerations Getting Started on Your Own BC4J/Struts Applications Conclusion
Lessons from the PastThe 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. Advice for the FutureOn 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. Rebuilding a Web Storefront with Struts and BC4JThe BC4J 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:
Jakarta Struts and
Oracle Business
Components for Java (BC4J). Both the Struts and BC4J 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 BC4J Toy Store has the basic architecture illustrated in
Figure 1: - The
model layer represents the business information needed by
the application,
- The controller
layer handles user input, interfaces with the model layer, and picks
the presentation
- The view
layer presents the model data to the
end-user.
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.  Figure 1: Best Practices "JSP Model 2" MVC Web Application ArchitectureBy
dissecting the framework-based implementation of our BC4J Toy Store demo, we'll
learn how BC4J simplifies building all aspects of the model layer, and how the
Struts and BC4J frameworks cooperate to simplify implementing the view and
controller layers. In the process, we'll also see plenty of evidence for how
the Oracle9i JDeveloper 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
Oracle9i JDeveloper. The next section details the steps to
get the demo setup correctly on your system. Demo Installation and SetupThese instructions assume that
you are running one of the following
Oracle9i
JDeveloper releases: - 9.0.3
Production (Build 1035), with JDeveloper patch number 2705796 from
Metalink
applied
- 9.0.3.1 Production (Build
1107)
- 9.0.4 Production, when
available.
We also assume that you have
access to an Oracle database, and privileges to create new user accounts to
setup the sample data.
| NOTE: | BC4J 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, 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. |
- Download the
bc4jtoystore.zip file
if you haven't already. Extract the contents
of the bc4jtoystore.zip file with
the standard JDK jar utility into a convenient directory.
jar xvf bc4jtoystore.zip This will create a directory
bc4jtoystore, and subdirectories. These instructions assume
you've extracted the bc4jtoystore.zip file into the root
directory C:\ thus ending up with a demo "root" directory of
C:\bc4jtoystore.
| 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
Oracle9i JDeveloper, then it comes with a 1.3 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
./bc4jtoystore/setup/CreateToyStoreUsers.sql like
this: cd C:\bc4jtoystore\setup sqlplus /nolog @CreateToyStoreUsers.sql After entering your SYSTEM account's
password, the script will create the TOYSTORE and
TOYSTORE_STATEMGMT user accounts. The
TOYSTORE schema will contain the BC4J Toy Store application
tables, while the TOYSTORE_STATEMGMT schema will be used by
the BC4J state management facility (described later in this whitepaper) to
store pending data across web pages. Create
the application tables for the BC4J Toy Store demo, along with some sample
data. Run the SQL script
./bc4jtoystore/setup/ToyStore.sql like this: sqlplus toystore/toystore @ToyStore.sql Setup two database connections in
the JDeveloper IDE corresponding to the two database accounts we created
above. Define connections in the JDeveloper9i
IDE named... toystore,
corresponding to the TOYSTORE user (password
TOYSTORE) andtoystore_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
./bc4jtoystore/setup directory. To do so, select the
Database category under the
Connections node in the System 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.
Oracle9i JDeveloper 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 under the Oracle9i JDeveloper Extensions
heading on that download page. 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 create a new JUnit regression test
using the items in 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.
Open the
./bc4jtoystore/BC4JToyStore.jws workspace in
JDeveloper9i
| NOTE: | If you are using version
9.0.4 you will be asked to confirm the upgrade of the 9.0.3-format workspace
and projects to the 9.0.4 format. That is expected, and you may
proceed. |
Run the application in the
JDeveloper IDE by running the index.jsp page in the
ToyStoreController.jpr project as shown in
Figure 2.  Figure 2: Running the BC4J Toy Store Application Inside the JDeveloper9i IDERunning the
index.jsp page from inside Oracle9i
JDeveloper will startup the embedded Oracle9iAS Oracle
Containers for J2EE (OC4J) server, launch your default browser, and cause it to
request the URL: http://yourmachine:8988/BC4JToyStore/index.jsp If everything is working correctly, you will see the
home page of the BC4J Toy Store demo, as shown in Figure 3.
 Figure 3: BC4J Toy Store demo Home Page
| NOTE: | After exploring the demo using the embedded Oracle Containers for
J2EE (OC4J) instance that is built-in to Oracle9i
JDeveloper, if you want to install the demo on an external OC4J instance, see
Appendix 1 |
| NOTE: | If following the
steps above didn't produce the above demo home page as expected, see
Appendix 2 for a list of known issues and troubleshooting
tips. |
| NOTE: | To try installing the demo on a non-Oracle J2EE
web container like Tomcat 4.1.24, see the additional instructions in
Appendix 3. |
Quick Tour Through the DemoBefore 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. Browsing Products and Adding Them to Your CartThe BC4J 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: - Selecting a category name to see the products in that category,
or
- Using the What are you looking
for? search box in the banner to find products by name, regardless
of what category they belong to.
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 Piñata, you will see a list of the different
kinds of piñatas that are available as shown in
Figure 4.  Figure 4: Browsing Different Kinds of Items for a Product TypeTo 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. If you already have some
of that item in your cart, doing this will add an
additional one of that item. 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.  Figure 5: Shopping Cart Display 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. Trying Out the Demo in Another LanguageThe demo is built using the
internationalization features supported by Struts and BC4J, and it ships with
support for three languages: English (the default), Italian, and German. The
Struts and BC4J 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
Internet Explorer, you can do this by: - Selecting Tools | Internet
Options... from the main menu
- Clicking the (Languages...) button on the
General tab.
- Clicking the
(Add...) button to add the "Italian (Italy) [it]" language
to your list of languages, if it's not already in the list.
- Selecting the "Italian (Italy) [it]" language in the
Language list and clicking the (Move
Up) button until it is at the top of the list as shown in
Figure 6.
- Press
(OK) to dismiss the Language Preference dialog, then
(OK) again to dismiss the Internet Options
dialog.
 Figure 6: Changing the Preferred Language in Internet Explorer 6.0After
setting Italian to be your preferred browser language, clicking again on the
icon will now show your shopping cart in
Italian, as shown in Figure 7.  Figure 7: Shopping Cart With Preferred Browser Language Set to ItalianYou can set your
browser's preferred language back to English using similar steps to proceed in
English again. Checking Out and Signing InFrom 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 8. 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 to do fill out your details. See the next section for
details... |
 Figure 8: Toy Store Sign-in PageAfter 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: - Entering an invalid
state abbreviation of
ZA for the country
USA - Entering a credit card
number that is not comprised of 16 digits
and pressing the button. You
should see the multiple validation errors as shown in Figure 9
.  Figure 9: Shipping Information Validation Errors On Form SubmissionAfter
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: - Entering a date in the past for
the expiration date of your credit card
- Blanking out a required field like Last
Name.
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 10.  Figure 10: Additional Shipping Information Validation Errors
| 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 the
BC4J framework 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, BC4J 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. Register a New User and Editing an Existing User's ProfileIf 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 8. 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 BC4J framework. For example, if while filling out the form you try
to: - Enter a user name that has already been
chosen by another user
- Forget to provide a
password
- Enter an email address that is not
properly formed
then when you submit the
form, you'll see the full set of errors related to your registration as shown
in Figure 11  Figure 11: Validation for New User Accounts In Action
| 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 Email datatype. All of the
custom error messages are localized to the current browser user's locale (i.e.
language + territory). None of this behavior requires developer-written code to
coordinate. |
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 12. 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.  Figure 12: Editing Account DetailsExperimenting with State Management and Failover SupportThe last aspect of the demo we'll explore on our tour is a practical
example of the BC4J framework's state management and failover support. The
feature sounds complicated, but it's easy to demonstrate. With the BC4J Toy
Store application running inside the embedded OC4J container that's part of the
JDeveloper IDE, try the following: - Add
several items to your shopping cart
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 13.
 Figure 13: Terminating the OC4J Server to Simulate a Server Failure- Re-run the BC4J Toy Store demo as we did in Figure 2.
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. The same failover capability would work if you simulated a database
crash by stopping and starting the database. This failover
capability works because the BC4J framework offers automatic database-backed
state management for pending data in your application. In the BC4J 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. Dissecting the DemoIn this section we explain the demo in detail, highlighting
the interesting details of how: - The
demo is architected into Model, View, and Controller layers
- The Model layer uses BC4J's business
service, data access, and business
object components
- The business
services can be tested using JUnit
- The
Controller layer uses Struts actions to coordinate application
flow
- The View layer uses Struts and BC4J tag
libraries to simplify building the web UI
- The BC4J features for seamless Struts integration work
- The features of Struts and BC4J are used to deliver a
multilingual application
- The default framework
behavior can be customized fit your needs.
| NOTE: | To follow along, we assume you have followed the instructions in
the Demo Installation and Setup section and have the
BC4JToyStore.jws workspace open in the JDeveloper IDE, and
your default browser open to the BC4J Toy Store home page as shown in
Figure 3. |
How the Application is Organized Into Packages and ProjectsLike all applications built
in Java, the BC4J Toy Store demo is comprised of a set of classes, organized
hierarchically into packages. Figure 14 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 regression testing suite and
utilities.  Figure 14: Java Package Hierarchy for BC4J Toy Store demoWhen 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 15 shows some representative
examples of classes in the BC4J Toy Store demo that inherit their behavior from
a framework: - The main business service
component
toystore.model.services.ToyStoreService extends
the BC4J 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. - An
example query component
toystore.model.dataaccess.ProductsInCategory extends the
BC4J 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. - An example business object
toystore.model.businessobjects.Account extends the BC4J
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. - The data transfer object
toystore.model.dataaccess.common.ShoppingCartRow extends the
BC4J framework base interface oracle.jbo.Row, adding
typesafe access to the application-specific attributes in the row of shopping
cart information. - The form bean
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. - The action
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. - The
test case
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.
 Figure 15: Example of Demo Classes that Extend FrameworksJDeveloper 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 16, the BC4J Toy Store application is comprised
of a BC4JToyStore workspace containing the following seven
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. ToyStoreView.jpr
This project contains
the JSP pages that comprise the user interface of the web store, and the
view-layer resource files in the toystore.view package of
the translatable text that appears in all the pages. ToyStoreController.jpr
This
project contains the Struts configuration file
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 BC4J 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/BC4J
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. Utilities.jpr
This project
contains an example command-line utility, in the
toystore.util package, that automates a frequent development
task for Struts developers, the automatic creation of XML-based
DynaActionForm definitions. Deployment.jpr
This project contains two
JDeveloper deployment profiles that automate the deployment of the BC4J Toy
Store application to external J2EE application servers.
 Figure 16: The BC4JToyStore Workspace in the JDeveloper System NavigatorAdvantages of a Model/View/Controller ArchitectureFirst
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: - Application look and feel
can change without affecting core application logic
- Page flow and error handling are centralized and removed from individual
pages
- Simpler-looking web pages can be
understood and modified by less technical team
members
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. Implementing the Model Layer Using BC4J Framework ComponentsThe 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 BC4J Toy Store
demo and briefly explain how they leverage the BC4J framework for their
implementation. Considering Model Layer Approaches: EJB-Centric or Web-Tier-Centric?Before exploring the BC4J 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: - EJB Technology deployed to the J2EE
EJB Tier, or
- JavaBeans Technology deployed to
the J2EE Web Tier.
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 BC4J 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: - Coordinate BC4J-powered services
with other Session Beans in the same transaction
- Leverage method-level security on your BC4J-backed
services.
Since the BC4J 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 BC4J 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 BC4J 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. Implementing Business Services with BC4J Application Module ComponentsBusiness services built using the BC4J framework are called
application modules. These service components are: - Cleanly architected with a client-side business
service interface and server-tier
implementation
- Efficiently implemented as
JavaBeans, but deployable as EJB Session Beans as necessary, with support for
container-managed transactions
- Automatically
configured at runtime from XML metadata and created through framework-supplied
factories
- Easily used by clients through
BC4J's implementation of the Business Delegate design
pattern
- Cleverly designed to expose "active"
collections of updateable data transfer objects that interact with your
business objects without code
All of
these features can be summarized by saying that BC4J-powered service components
make the J2EE developer's life a lot simpler. The key BC4J framework components
that cooperate to provide the business service implementation are: - Application Modules to build
transactional business services
- View Objects and View Links to
build collections of updateable data transfer objects based on SQL
queries
- Entity Objects
and Associations to encapsulate business rules and
persistence details of domain business objects and express the relationships
between them
- Domains -
to build custom datatypes, where necessary
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. Example 1: ToyStoreService Business Interface
package toystore.model.services.common; public interface ToyStoreService extends oracle.jbo.ApplicationModule { boolean validSignon(String username, String password); String finalizeOrder(); boolean adjustQuantityInCart(String[] itemid, long[] qty); boolean isCartEmpty(); long currentQuantityInCart(String itemid); } |
As shown in Figure 17, the
ToyStoreService component is implemented as a set of
files: ToyStoreService.xml - Service definition
fileToyStoreService.java -
Service interfaceToyStoreServiceImpl.java - Service
implementation
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 BC4J components
follow this basic pattern for the names of the files that comprise their
definition, implementation, and interface.  Figure 17: ToyStoreService Application Module Component in the ToyStoreModel ProjectIf 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
BC4J-based JavaBean components in the application. Exposing Model Data to the View and Controller LayersWhen 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: - Account information for the customer placing the
order
- Order information
- Order line item information to show the items and quantities
the customer purchases
- Shipping option
information to drive a poplist of delivery
choices
Figure 18
illustrates what the model data map object would look like for such a task.
 Figure 18: Find Named Collections of Data Transfer Objects Using a Model Data MapExample 2 shows the typical code required to find the
collection of data transfer objects for line items from a model data
map. Example 2: Finding a Collection of Data Transfer Objects from a Model Data Map
/* * Find the collection of line item data transfer objects from the * model data map using string key "LineItems" */ Collection lineItems = (Collection)modelDataMap.get("LineItems"); /* * Iterate over the LineItem data transfer objects in the collection */ Iterator iter = lineItems.iterator(); while (iter.hasNext()) { LineItem line = (LineItem)iter.next(); // Work with the line item values using getter/setter methods Long quantity = line.getQuantity(); // etc. } |
BC4J Application Modules Implement Your Model Data Map For YouThe model data map discussed
above is a necessary feature of any MVC application. Business services
implemented as BC4J application modules inherit a built-in model data map
implementation. The application module cooperates with BC4J 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 19.
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 BC4J framework is doing on
your behalf among the collections of data transfer objects. For example, the
indentation in Figure 19 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 19: Declaratively Define Your Model Data Map Using the Application Module EditorSince
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. Example 3: Finding a Collection of Data Transfer Objects Using an Application Module
/* * Find the collection of line item data transfer objects from the * model data map implemented by the application module component * using string key "LineItems". */ LineItems lineItems = (LineItems)yourAppModule.findViewObject("LineItems"); /* * Iterate over the LineItem data transfer objects in the collection */ while (lineItems.hasNext()) { LineItemRow line = (LineItemRow)lineItems.next(); // Work with the line item values using getter/setter methods Long quantity = line.getQuantity(); // etc. } |
If you do not want to
work with typesafe collections of data transfer objects, you can opt to work
with BC4J'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 BC4J Toy Store demo, but the
Online Help system in JDeveloper covers all of the framework API's in its
reference documentation if you are curious for more details.
| NOTE: | To find the BC4J 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 BC4J category. All the JavaDoc is there. If you
prefer to browse the javadoc with your own favorite browser, then expand the
bc4j*doc.jar files in the
JDEVHOME/jdev/doc/ohj directory
into a convenient directory and go for it! |
Implementing Domain Business Objects Using BC4J Entity Object ComponentsBusiness objects built using the BC4J framework 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 BC4J 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 Modelling Language (UML) to visualize their business object
model, Figure 20 shows how we can use the UML modeling
features in JDeveloper to do just this.  Figure 20: Finding the "Business Objects" UML Model in the ToyStoreModel ProjectYou
can see the UML diagram named "Business Objects" in the
toystore.uml package by double-clicking on it.
Figure 21 shows what you will see
when you open the diagram. It's the business object model for the BC4J Toy
Store demo.  Figure 21: UML Diagram of the BC4J Toy Store Domain Business ObjectsFor each real-world business entity in the application domain, a BC4J
entity object: - Defines the names and
datatypes of the attributes required to model its business
data
- Declares how it is associated to and/or
composed of other entities in the model
- Encapsulates the business rules governing the entity and any composed
entities
- Handles the persistence of changes
made to business objects
By default,
each entity object inherits high-performance, relational-database persistence
functionality from the BC4J framework, but custom persistence schemes can be
implemented by overriding one framework method in your domain-specific entity
subclass. For example, some BC4J 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 21 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 BC4J 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 21 visualizes
several other interesting things about the relationships between our entity
object components. In particular, it shows: - The associations and compositions between entities
- The cardinality of the association (one-to-one, one-to-many,
etc.)
- The programmatic navigation possible
between entities (the arrowheads)
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 22, which shows the
toystore.model.businessobject package in the JDeveloper
System Navigator, entity object components like Account are
comprised of a number of constituent files: Account.xml - Entity definition fileAccountImpl.java - Entity
implementationAccountImplMsgBundle.java - Entity message
bundle
 Figure 22: Domain Business Objects and Associations in the ToyStoreModel ProjectAll
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.
Supported Approaches for Implementing Business RulesFigure 23 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.  Figure 23: Validation Rules Panel in the Entity Object Editor for "Account"Table 1 shows the declarative business rules that have been
enabled for the Account and Orders entity
objects. Table 1: Examples of Declarative Business Rules In Use By Demo Business Objects
| Component Name | Declarative
Business Rule |
|---|
Account | UniquePKValidationBean checks
that the new username entered doesn't conflict with one already in
use.VerifyStateForCountry
checks that the state/province code is valid for the country code provided for
the account's home address.ListValidationBean checks that the
Country attribute value is a country code from the
toystore.model.dataaccess.CountryList view object's default
rowset.
| Order | VerifyStateForCountry checks
that the state/province code is valid for the country code provided for the
order's shipping address.- Method validation
rule
validateCreditCardExpiration() that raises an exception
if the credit card number provided for the order is
expired.
|
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 24 shows the Method validation
rule that we've defined on Orders to engage the
validateCreditCardExpiration() method.  Figure 24: Method Validation Rule in Use for "Orders" EntitySince
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 |