Building a Web Store with Struts & BC4J Frameworks

Building a Web Store with Struts & BC4J Frameworks

Steve Muench, BC4J Development Team
Oracle Corporation
September 25, 2003

Abstract

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 Past

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.

Advice for the Future

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.

Rebuilding a Web Storefront with Struts and BC4J

The 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.

Best Practices "JSP Model 2" MVC Web Application Architecture
Figure 1: Best Practices "JSP Model 2" MVC Web Application Architecture

By 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 Setup

These 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.


  1. Download the bc4jtoystore.zip file if you haven't already.
  2. 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.

  3. 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.

  4. 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
  5. Setup two database connections in the JDeveloper IDE corresponding to the two database accounts we created above.

    Define connections in the JDeveloper9i IDE named...

    1. toystore, corresponding to the TOYSTORE user (password TOYSTORE) and
    2. 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 ./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.

  6. 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.

  7. 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.

  8. Run the application in the JDeveloper IDE by running the index.jsp page in the ToyStoreController.jpr project as shown in Figure 2.

    Running the BC4J Toy Store Application Inside the JDeveloper9i IDE
    Figure 2: Running the BC4J Toy Store Application Inside the JDeveloper9i IDE

    Running 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.

    BC4J Toy Store demo Home Page
    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 Demo

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.

Browsing Products and Adding Them to Your Cart

The 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:

  1. Selecting a category name to see the products in that category, or
  2. 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.

Browsing Different Kinds of Items for a Product Type
Figure 4: Browsing Different Kinds of Items for a Product Type

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. 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.

Shopping Cart Display
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 Language

The 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:

  1. Selecting Tools | Internet Options... from the main menu
  2. Clicking the (Languages...) button on the General tab.
  3. Clicking the (Add...) button to add the "Italian (Italy) [it]" language to your list of languages, if it's not already in the list.
  4. 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.
  5. Press (OK) to dismiss the Language Preference dialog, then (OK) again to dismiss the Internet Options dialog.
Changing the Preferred Language in Internet Explorer 6.0
Figure 6: Changing the Preferred Language in Internet Explorer 6.0

After 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.

Shopping Cart With Preferred Browser Language Set to Italian
Figure 7: Shopping Cart With Preferred Browser Language Set to Italian

You can set your browser's preferred language back to English using similar steps to proceed in English again.

Checking Out and Signing In

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 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...


Toy Store Sign-in Page
Figure 8: Toy Store Sign-in Page

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:

  • 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 .

Shipping Information Validation Errors On Form Submission
Figure 9: Shipping Information Validation Errors On Form Submission

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:

  • 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.

Additional Shipping Information Validation Errors
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 Profile

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 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

Validation for New User Accounts In Action
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.

Editing Account Details
Figure 12: Editing Account Details

Experimenting with State Management and Failover Support

The 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:

  1. Add several items to your shopping cart
  2. 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.

    Terminating the OC4J Server to Simulate a Server Failure
    Figure 13: Terminating the OC4J Server to Simulate a Server Failure

  3. 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 Demo

In 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 Projects

Like 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.

Java Package Hierarchy for BC4J Toy Store demo
Figure 14: Java Package Hierarchy for BC4J Toy Store demo

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 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.
Example of Demo Classes that Extend Frameworks
Figure 15: Example of Demo Classes that Extend Frameworks

JDeveloper 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. Deployment.jpr

    This project contains two JDeveloper deployment profiles that automate the deployment of the BC4J Toy Store application to external J2EE application servers.

The BC4JToyStore Workspace in the JDeveloper System Navigator
Figure 16: The BC4JToyStore Workspace in the JDeveloper System Navigator

Advantages of a Model/View/Controller Architecture

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:

  • 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 Components

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 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 Components

Business 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 file
  • ToyStoreService.java - Service interface
  • ToyStoreServiceImpl.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.

ToyStoreService Application Module Component in the ToyStoreModel Project
Figure 17: ToyStoreService Application Module Component in the ToyStoreModel Project

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 BC4J-based JavaBean components in the application.

Exposing Model Data to the View and Controller Layers

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:

  • 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.

Find Named Collections of Data Transfer Objects Using a Model Data Map
Figure 18: Find Named Collections of Data Transfer Objects Using a Model Data Map

Example 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 You

The 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.

Declaratively Define Your Model Data Map Using the Application Module Editor
Figure 19: Declaratively Define Your Model Data Map Using the Application Module Editor

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.

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 Components

Business 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.

Finding the "Business Objects" UML Model in the ToyStoreModel Project
Figure 20: Finding 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 21 shows what you will see when you open the diagram. It's the business object model for the BC4J Toy Store demo.

UML Diagram of the BC4J Toy Store Domain Business Objects
Figure 21: UML Diagram of the BC4J Toy Store Domain Business Objects

For 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 file
  • AccountImpl.java - Entity implementation
  • AccountImplMsgBundle.java - Entity message bundle
Domain Business Objects and Associations in the ToyStoreModel Project
Figure 22: Domain Business Objects and Associations in the ToyStoreModel Project

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.

Supported Approaches for Implementing Business Rules

Figure 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.

Validation Rules Panel in the Entity Object Editor for "Account"
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 NameDeclarative 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:

  1. 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.

  2. 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.

  3. 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.

Method Validation Rule in Use for "Orders" Entity
Figure 24: Method Validation Rule in Use for "Orders" Entity

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.

Example 4: Custom Validation Method to Verify Credit Card Expiration Date
public boolean validateCreditCardExpiration() {    
  if (getCreditcard() != null) {
    ExpirationDate expDate = getExprdate();
    /*
     * If the expiration date is not in the future, then throw an error
     * that the card is expired
     */
    if (expDate != null && !expDate.isFutureDate()) {
      throw new AttrValException(OrdersImplMsgBundle.class,
                                 OrdersImplMsgBundle.EXPIRED_CREDITCARD,
                                 getDefinitionObject().getFullName(),
                                 getDefinitionObject().getAttributeDef(EXPRDATE).getName(),
                                 null,null);
    }
  }
  return true;
}

Using View Object Components to Simplify All Aspects of Data Access

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 BC4J Toy Store demo relies on displaying or editing business information. For example:

  • while browsing, the user sees pages showing store categories, products, and items
  • the Register as a New User page captures new data from the user
  • the Edit Profile page allows the user to update existing account information
  • the Your Cart page shows the items the user has added to their shopping cart.

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:

  1. Write code using the JDBC API to implement the Fast Lane Reader pattern to query database data,
  2. Encapsulate that JDBC code in a Data Access Object to isolate the retrieval method in case the datasource changes later
  3. Write code to implement the Transfer Object pattern. These data transfer objects (or value objects) reflect the structure of the JDBC result rows.
  4. Write code to implement the Value List Handler pattern to iterate over the rows in the JDBC ResultSet and construct instances of row-like data transfer objects holding copies of the queried row data.
  5. Implement the Transfer Object Assembler pattern to aggregate multiple collections of data transfer objects into a single object for further minimizing network round trips.

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:

  • What SQL query is required to retrieve the data I need?
  • What are the attribute names and datatypes of the data transfer object needed to transport a row of data from the result set of this query to the client?

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 BC4J framework provides with its View Object component. Like the other BC4J 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 25 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.

View Object and Its View Object Row "Data Transfer Object"
Figure 25: View Object and Its View Object Row "Data Transfer Object"

Of course, the BC4J 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 BC4J components have, as shown in Figure 26, the view object component can comprise a number of optional additional files as well:

  1. View Object implementation file (ViewNameImpl.java) used to customize the behavior of the view object component or implement custom methods.
  2. View Object interface (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.
  3. View Row implementation file (ViewNameRowImpl.java) used to customize the behavior of the data transfer object associated with this view object.
  4. View Row interface (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.
  5. View Row message bundle (ViewNameRowImplMsgBundle.java) appears automatically when you define any built-in prompts, tooltips, format masks, etc., for your view object.

For example, toystore.model.dataaccess.Accounts is implemented by the following set of files:

  • Accounts.xml - View Object definition file
  • AccountsImpl.java - View Object implementation
  • AccountsRowImpl.java - View Row implementation
  • AccountsRowImplMsgBundle.java - View Row message bundle
Data Access Objects in the ToyStoreModel Project
Figure 26: Data Access Objects in the ToyStoreModel Project

Looking at the files associated with other view objects in Figure 26 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.

Example 5: Setting View Object Bind Variable Values via a Custom Method
/*
 * Find the ProductList collection of data transfer objects from the 
 * application module's data model map by name.
 */
ProductList productList = (ProductList)appModule.findViewObject("ProductList");
/*
 * Set query bind parameters using custom method that encapsulates
 * WHERE clause handling inside the view object component so that
 * controller and view layers never work with SQL directly.
 */
productList.setProductIdToFind(request.getParameter("id"));

By default, BC4J creates instances of a generic data transfer object (oracle.jbo.Row), but a few clicks in 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:

  1. Encapsulating the data access code
  2. Querying the data using optimized, best-practices JDBC techniques
  3. Constructing instances of the data transfer objects for query result rows
  4. Browsing data a page at a time
  5. Exposing one or more collections of data transfer objects based on the view object's query

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 27 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.

View Object Aggregates a Default RowSet for Ease of Use
Figure 27: View Object Aggregates a Default RowSet for Ease of Use

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 BC4J 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.

How Framework Components Cooperate to Simplify Data Modification

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:

  1. Create a new instance of the appropriate back-end business object
  2. Populate the attributes of the business object based on corresponding data in the data transfer object(s)
  3. Commit the transaction

Steps for updating existing data typically require business service code to:

  1. Lookup the existing instance of the correct back-end business object by primary key, using appropriate attribute value(s) from the data transfer object
  2. Update the attributes of the business object instance based on appropriate, corresponding data in the data transfer objects, ideally only updating the attributes that the client actually changed in case attribute-level auditing is being done inside the business object layer
  3. Commit the transaction

Steps for removing existing data typically involve:

  1. Looking up the existing instance of the correct back-end business object by primary key, using appropriate attribute value(s) from the data transfer object.
  2. Deleting the instance by invoking an appropriate delete method on it.
  3. Committing the transaction

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 unmistakeable 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:

  • Which back-end business objects are related to the data in this data transfer object?
  • How do data transfer object attributes correspond to back-end business object attributes?

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 BC4J 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:

  • Add new DTO instances to any collection
  • Update the attributes of existing DTO instances
  • Remove existing DTO instances from any collection
  • Find a DTO instance in a collection by primary key

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 BC4J 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, BC4J 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 BC4J/Struts integration leverages this bundled exception mode.


NOTE:

While out of scope for this whitepaper, in addition to the tight integration that BC4J provides for working with Struts, it also offers:

  • A companion rich-client data-binding framework named JClient for building Swing GUI's bound to BC4J application module services
  • Automatic exposure of application modules as web services, including built-in view object XML marshalling features for producing and consuming complex business documents

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:

  • directly create, lookup and modify, or lookup and delete entity instances, or
  • update view object row DTO's in a view object's rowset.

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 BC4J 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 28 summarizes in a single picture how the BC4J 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.

How BC4J Framework Components Relate And Which Are Client-Accessible
Figure 28: How BC4J Framework Components Relate And Which Are Client-Accessible

NOTE:

The BC4J transaction object supports the use of J2EE datasources as well as simple JDBC connection URL's for interaction with the database.


Creating Smarter Custom Data Types Using Domains

The Java language comes with a number of basic datatypes like String, Long, Float, etc. The BC4J framework adds a few additional types called "domains" that live in the oracle.jbo.domain.* package. These additional types include:

  • Number
  • Date
  • DBSequence
  • ClobDomain
  • BlobDomain
  • Array

These 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 BC4J framework provides the simple feature of domains to support this use case.

Figure 29 shows the three custom datatypes we've created for the BC4J 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.

Custom DataTypes in the ToyStoreModel Project
Figure 29: Custom DataTypes in the ToyStoreModel Project

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 Business Tier Components with JUnit

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. Oracle9i JDeveloper 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:

  • A JUnit test fixture that encapsulates getting an instance of the desired application module
  • An example JUnit test case class which uses this fixture and asserts that all expected view object instances exist in the application module's model data map.
  • A JUnit Test class that runs all of the test cases (initially just one).

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 30. 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 RunAllTests.java class instead of just running it to use the JDeveloper debugging features to find the problem.


Successful Run of BC4J Toy Store JUnit Test Suite
Figure 30: Successful Run of BC4J Toy Store JUnit Test Suite

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.


Implementing the Controller Layer with the Jakarta Struts Framework

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 Jakarta Struts framework provides a ready-made implementation that handles the job well.

Handling Model Interaction and Page Flow With Struts Actions

Using Struts, you assign logical URL names to the actions your application performs and associate these action names with Java classes containing the action-specific controller logic. For example, an action that allows the user to view the contents of her shopping cart might be named "/yourcart". If the context root name of our J2EE web application were "/BC4JToyStore", then to view the shopping cart using the Struts controller the user might click on a link like this:

http://yourcompany.com/BC4JToyStore/yourcart

An alternative approach is to use links with a conventional suffix like *.action on the action name like this:

http://yourcompany.com/BC4JToyStore/yourcart.action

Of course, you can configure how application URL's map to the Struts controller servlet by making appropriate changes to the standard J2EE web.xml configuration file. By default, the ActionServlet provided by the Struts framework uses the conventional URL suffix of *.do to indicate action URL. So using our example above, the user would click on a link like this to see her shopping cart:

http://yourcompany.com/BC4JToyStore/yourcart.do

Figure 31 illustrates the basic steps that occur each time the user clicks on such a link that represents a Struts action.

  1. The controller receives a page request and routes it to the appropriate action class
  2. The action acquires an instance of a business service, via the business delegate, and invokes a method on it
  3. The business delegate implementation delegates the method call to the business service
  4. The business service uses one or more query components to retrieve business data from SQL queries, and exposes the results as one or more collections of data transfer objects in its "model data map"
  5. The business delegate exposes the "model data map" to the view layer
  6. The controller selects an appropriate view and forwards the request to the view layer for rendering
  7. The view layer finds the collections of data transfer objects from the "model data map" and iterates over them to render the data to the end-user.
Typical MVC Processing Steps with Struts and JSP
Figure 31: Typical MVC Processing Steps with Struts and JSP

NOTE:

We'll explore the mechanics of how the BC4J business delegate implementation works in the Understanding BC4J/Struts Integration section below.


Along with other configuration information, Struts keeps the mapping of action names to action handler classes in the struts-config.xml file. If you peek into the struts-config.xml file under the WEB-INF directory in the ToyStoreController.jpr project, you can see these action mappings for the BC4J Toy Store demo. Searching for /yourcart, you will find the XML tags shown in Example 6.

Example 6: Action Mapping Definition in struts-config.xml Configuration File
<struts-config>
     :
  <action-mappings>
       :
     <!-- Show your shopping cart -->
    <action path="/technology/yourcart" type="toystore.controller.strutsactions.YourCartAction">
      <set-property property="viewobject" value="ShoppingCart"/>
      <set-property property="application" value="ToyStore"/>
      <set-property property="releasemode" value="Stateful"/>
      <forward name="success" path="/technology/WEB-INF/jsp/yourcart.jsp"/>
    </action>
       :
  </action-mappings>
     :
</struts-config>

The file contains one <action> tag for each action in your application. The example above configures the Struts controller servlet to route requests for the /yourcart action to the toystore.controller.strutsaction.YourCartAction class. This action extends the Struts framework's base class org.apache.struts.action.Action and overrides the standard execute() method to add the task-specific controller logic to:

  • process the URL input parameters for item id's and quantities, if present
  • interact if necessary with the ToyStoreService to adjust item quantities
  • access the collection of data transfer objects representing the shopping cart items
  • calculate the total cost of items in the cart and add it as an HTTP request attribute
  • forward the request to the /WEB-INF/jsp/yourcart.jsp page to render the display.

The <forward> elements nested inside the action tag provide logical names like "success" or "requireslogin" for the different pages that the action might conditionally display to the user. When the code in YourCartAction performs the following step:

public class YourCartAction extends Action  {
  public ActionForward execute(ActionMapping mapping, ...etc...) {
    :
    return mapping.findForward("success");
  }
}

this call to findForward("success") on the ActionMapping object returns an ActionForward object encapsulating the details for the forwarding destination named "success". Returning this from the execute() method causes the Struts controller to forward control to the indicated yourcart.jsp page for rendering the shopping cart model data to the user. Since the action can conditionally return different destination targets for the user to see, it should be clear how the actions are in control of the page flow.


NOTE:

We'll explore the effect that the nested <set-property> elements have when we learn more about how BC4J and Struts cooperate in the Understanding BC4J/Struts Integration section below.


Figure 32 is a visual action flow diagram for the key Struts actions in the BC4J Toy Store Demo's controller layer.

BC4J Toy Store demo Struts Action Flow
Figure 32: BC4J Toy Store demo Struts Action Flow

The arrows in the diagram indicate that the user can navigate from the page rendered by the previous action to the indicated target action. For example, on the page rendered by the /home action, the user can type in some search criteria in the What are you looking for? box, and press the icon to submit the search form to the /search action. The caution signs with the return arrow paths indicate actions that can fail due to validation errors, returning the user to the page they submitted to see and correct the errors.

Table 2 gives a description of what each of the actions does.

Table 2: Key Struts Action Mappings in the BC4J Toy Store demo
Action Path NameAction Description
/homeShow the Toy Store home page
/showcategoryAllow paging through the products for a particular category
/showproductAllow paging through the different items for a given product type
/showproductdetailsShow the details of a specific item for sale
/yourcartShow your shopping cart
/searchHandle "search" form submission, and allow paging through search results
/signinShow the "signin" page
/verifysigninHandle form submit for the "signin" page
/registerCreate blank Accounts row, and show the "register new user" form
/createaccountHandle form submit for "register new user" page
/editaccountLookup existing Accounts row, and show the "edit existing account" form
/updateaccountHandle form submit for "edit existing account" page
/reviewcheckoutShow the "review checkout" page
/placeorderCreate new order and forward to confirm shipping info
/confirmshippinginfoShow the "confirm shipping information" page
/finalizeorderHandle form submit for "confirm shipping information" page
/thankyouShow the "thank you" page, showing order tracking number
/revieworderShow the "review order" page
/revieworderxmlShow the "review order" information as XML

Figure 33 shows how the individual Struts actions appear in the JDeveloper navigator. By expanding the struts-config.xml node in the tree, all of the action paths appear. Double-clicking on any action path name navigates you to the corresponding action class. For simple actions that only forward to a JSP page without engaging an action class containing custom logic, double-clicking brings you to the JSP page instead.

Struts Actions in the ToyStoreController Project
Figure 33: Struts Actions in the ToyStoreController Project

NOTE:

The careful reader might rightfully ask, "What is the index.jsp file above doing in the ToyStoreController project? Aren't JSP pages supposed to be in the View layer?" Here's why it's here. It's desireable that the user be able to access the simple URL:

http://localhost:8888/BC4JToyStore

to get to the home page of the web store instead of having to remember to type the home.do at the end of the URL like:

http://localhost:8888/BC4JToyStore/home.do

Attemps to configure home.do as one of the standard "welcome files" in web.xml simply didn't work as expected. So, as a fallback plan, I configured index.jsp as the welcome file, and have a one-line JSP page that does a <jsp:forward page="home.do"/>. Since this JSP page is playing more of a page-flow role than a display role, I thought it made sense in the controller project.


While building your Struts-based applications, while you can work directly with the struts-config.xml file in the JDeveloper code editor, you can be more productive by using the Struts Configuration editor shown in Figure 34. It presents all of the information from the struts-config.xml file in a multi-panel dialog, allowing fast creation, editing, and deletion of all aspects of the Struts configuration without having to remember or to type in the XML syntax directly. The figure shows the Action Mappings panel, after having selected the /yourcart action.

Oracle9i JDeveloper Struts Configuration File Editor
Figure 34: Oracle9i JDeveloper Struts Configuration File Editor

A closer look at the classes in the toystore.controller.strutsaction package reveals our general action implementation strategy:

  • Basic actions extend org.apache.struts.action.Action directly

    These contain only task-specific controller logic and don't share any common code with other Toy Store actions.

  • Actions that support paging through data extend the ToyStoreDisplayDataWithPagingAction action in the toystore.fwk.controller package.

    These are the /search, /showproduct, and /showcategory actions. The base class they extend is a Struts framework customization we've done to share controller logic common to all actions that page through data.

  • Actions that show data entry forms extend EditAction in the oracle.jbo.html.struts11.actions package.

    .

    This is a handy helper-action that the BC4J framework supplies that can handle creating empty rows with default values, looking for an existing row to be edited, etc.

  • Actions that handle submitted form data extend ToyStoreUpdateAction in the toystore.fwk.controller package.

    This is a framework customization of the handy UpdateAction action in the oracle.jbo.html.struts11.actions package supplied by BC4J, to customize how it translates bundled BC4J validation errors into Struts errors for presenting to the user.


NOTE:

We explain more about the customized framework extensions in the Struts Framework Customizations for the Controller Layer section below.


Using Struts Form Beans to Process HTML Form Input

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 /verifysignin action is mapped to the toystore.controller.strutsactions.VerifySignInAction action class. 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
  • Call the Form Bean's input validation method
  • In case of validation error, return the user for input to the "/signin.do" page

Note that loginform is a logical name defined in the <form-beans> section which corresponds to the toystore.controller.strutsformbeans.LoginForm JavaBean class.

Example 7: Action Mapping That Uses a Struts Form Bean with Validation
<struts-config>
  <form-beans>
    <form-bean name="loginform"
               type="toystore.controller.strutsformbeans.LoginForm"/>
      :
  </form-beans>
  <action-mappings>
       :
    <!-- Verify credentials on a login attempt -->
    <action path="/technology/verifysignin" 
            type="toystore.controller.strutsactions.VerifySignInAction"
            input="/technology/signin.do"
            validate="true"
            name="loginform"
            scope="request">
      <set-property property="application" value="ToyStore"/>
      <set-property property="releasemode" value="Stateful"/>
    </action>
       :
  </action-mappings>
</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. The controller will also invoke the standard:

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request)

method on the form bean, which gives the bean a chance to validate the UI input values. Example 8 shows the validate() 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 of the validation checks 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 signin.error.logininvalid are translated into user-readable messages in the appropriate language in the Struts and BC4J Features for Building Multilingual Applications section below.

Example 8: LoginForm Form Bean Validation Method
// Method in the LoginForm form bean class
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
  ActionErrors errors = new ActionErrors();
  if (isNullOrEmpty(getUsername())) {
    errors.add("username", new ActionError("signin.error.usernameblank"));
    return errors;
  }
  if (isNullOrEmpty(getPassword())) {
    errors.add("password", new ActionError("signin.error.passwordblank"));
    return errors;
  }
  BC4JContext context = BC4JContext.getContext(request);
  ToyStoreService ts = (ToyStoreService)context.getApplicationModule();
  if (!ts.validSignon(_username,_password)) {
    errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("signin.error.logininvalid"));      
  }
  return errors;
}

If the form bean validation above fails, then the user is returned to the signin page to try again. If it succeeds, then the VerifySignInAction will get control. It 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.

Using a Command Line Utility to Generate DynaActionForms

In addition to the hand-coded form beans that we saw in the Using Struts Form Beans to Process HTML Form Input section above, Struts also supports a metadata-driven DynaActionForm bean. This allows us to include a <form-bean> definition like the one shown in Example 9 in the struts-config.xml file.

Example 9: Declaratively-Defined Attributes in a Struts DynaActionForm Definition
<struts-config>
  <form-beans>
    :
    <form-bean name="ExampleAccountsForm"
               type="org.apache.struts.action.DynaActionForm">
       <form-property name="Username"  type="java.lang.String"/>
       <form-property name="Password"  type="java.lang.String"/>
       <form-property name="Email"     type="java.lang.String"/>
       <form-property name="Firstname" type="java.lang.String"/>
       <form-property name="Lastname"  type="java.lang.String"/>
       <form-property name="Status"    type="java.lang.String"/>
       <form-property name="Address"   type="java.lang.String"/>
       <form-property name="City"      type="java.lang.String"/>
       <form-property name="State"     type="java.lang.String"/>
       <form-property name="Zip"       type="java.lang.String"/>
       <form-property name="Country"   type="java.lang.String"/>
       <form-property name="Phone"     type="java.lang.String"/>
       <form-property name="Userid"    type="java.lang.String"/>
    </form-bean>
  </form-beans>
     :
</struts-config>

At runtime, the DynaActionForm named ExampleAccountsForm defined above acts like a JavaBean with bean properties defined by the nested <form-property> elements. Using DynaActionForm's you eliminate the need to hand-build Struts form beans in cases when you don't require hand-coded validation logic in the form bean. When using Struts with BC4J, all of your business logic should be encapsulated inside your entity objects for maximum reuse across applications, so there is no pressing need to enforce the same rules in your FormBean's as well. Because of this, DynaActionForms are a perfect fit.


NOTE:

Struts supports a declarative FormBean-level validation feature called Struts Validator. Defining anything but the simplest UI-level value sanity checks, however, sacrifices the reusability of the business rules by other kinds of clients like Swing clients or Web Service clients, as well as potentially causing you to reinforce the same rules in your business objects. By including all relevant business rules in the entity objects that model your business domain, you are poised to reuse the same rules in all applications you build.


The Utilities.jpr project in our BC4JToyStore workspace contains a DynaActionFormGen.java class that implements a handy command-line utility to generate the required XML <form-bean> definition for a DynaActionForm based on any BC4J view object. As we learned above, the view object attribute names are in the view object's XML metadata file. This utility simply applies an appropriate XSLT transformation to transform that view object XML into the corresponding <form-bean> fragment for copying and pasting into the struts-config.xml file.

Using the makeJarFile.deploy deployment profile in the project, you can deploy the appropriate classes, DTD, and XSLT transformation into a DynaActionFormGen.jar archive, and then to use the command line utility you just use the syntax:

java -jar DynaActionFormGen.jar path/to/your/ViewObjectName.xml

NOTE:

The class also serves as a useful example of the best-practice technique for parsing BC4J XML files, caching the DTD for best performance.


Understanding BC4J/Struts Integration

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 View Layer: JSP Pages and Struts/BC4J Tag Libraries section, it also provides some JSP tag libraries to simplify building your view layer. However, Struts 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 BC4J framework is the perfect companion to Struts in this venture.

In addition to the set of cooperating components that BC4J provides to implement your model layer, it also provides specific additional functionality to dovetail with Struts' controller and view layer features. For example, BC4J provides:

  • A customized Struts Request Processor to handle integration with the model layer
  • Struts Actions to automate interaction of Form Beans and BC4J's data transfer objects
  • High-performance Business Delegate implementation that finds and pools business service components
  • Declarative, database-backed state management to simplify transactions spanning pages

The Struts ActionServlet was refactored in the Struts 1.1 release to separate the servlet functionality from the core controller logic that orchestrates how requests are processed. The design goal of this separation, which introduced the new RequestProcessor class in the org.apache.struts.action package, was that developers could easily extend and customize the RequestProcessor without touching the basic servlet. BC4J provides the BC4JRequestProcessor which leverages this new Struts 1.1 extensibility to provide added value for Struts/BC4J developers. It extends the basic Struts RequestProcessor to automate all of the details of working with a BC4J-implemented model layer.

Configuring Struts to Use a BC4J-Powered Model Layer

As with any customized Struts request processor, you configure Struts to use the BC4JRequestProcessor by adding a <controller> tag in the struts-config.xml file like this:

<struts-config>
     :
  <controller processorClass="oracle.jbo.html.struts11.BC4JRequestProcessor"
              locale="true"
              contentType="text/html"/>
     :
</struts-config>

NOTE:

In the BC4J Toy Store demo we've actually configured Struts to use a subclass of BC4JRequestProcessor that we describe in more detail in the Struts and BC4J Features for Building Multilingual Applications section below.


The second step in setting up the BC4J/Struts configuration is to provide two additional initialization parameters for the Struts ActionServlet in web.xml. Example 10 shows the first one. This <init-param> entry defines the BC4JDefinition servlet initialization parameter with value "ToyStoreView". This parameter value is used to automate the model layer interaction based on declarative mapping properties in struts-config.xml.

Example 10: Struts ActionServlet Init Parameter To Identify BC4J Client Project
<web-app>
   <description>web.xml file for the BC4J Toy Store demo application</description>
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>BC4JDefinition</param-name>
      <param-value>ToyStoreView</param-value>
    </init-param>
       :
  </servlet>
    :
</web-app>

Specifically, the value "ToyStoreView" tells the BC4J/Struts integration code to use the BC4J client project file named ToyStoreView.cpx, which it will expect to find as a resource in the ./WEB-INF/classes directory. The client project file is a simple XML file containing one or more named client data model definitions like this:

<Session Name="ToyStore"
         Package="toystore.model.services"
         Configuration="ToyStoreServiceLocal"/>

For the client data model named "ToyStore", this pair of Package and Configuration attribute values is used by the BC4J 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. The ToyStoreServiceLocal configuration contains a property indicating the fully-qualified name of the application module, toystore.model.services.ToyStoreService, that will be used as our business service implementation.


NOTE:

Configurations are a named set of configuration properties that BC4J uses to simplify application deployment. To see or edit the configurations for any application module, after selecting the component in the System 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. In fact, to change a BC4J/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 modifying the value of the Configuration attribute here in the client data model definition's <Session> tag. 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 BC4J design time sets up these multiple configurations for you automatically and gives you productive dialogs to edit the configuration parameters at design time.

In addition to the customizable RequestProcessor, Struts also allows its basic implementation of an ActionMapping to be specialized to allow more intelligent mapping definitions. The BC4J/Struts integration layer does exactly this, providing a BC4JActionMapping class that implements declarative model layer interaction based on nested properties set on your <action> mapping in struts-config.xml. Example 11 illustrates how we configure Struts to use this enhanced action mapping implementation by adding an additional servlet initialization parameter named mapping in web.xml.

Example 11: Configuring Struts to Use the Enhanced BC4JActionMapping
<web-app>
   <description>web.xml file for the BC4J Toy Store demo application</description>
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
       :
    <init-param>
      <param-name>mapping</param-name>
      <param-value>oracle.jbo.html.struts11.BC4JActionMapping</param-value>
    </init-param>
       :
  </servlet>
    :
</web-app>

Consider the nested <set-property> tags in the action mapping that appears in Example 12. The application property of "ToyStore" indicates that this Struts action wants to access the business service whose lookup details are captured in the client data model definition named "ToyStore".

Example 12: Struts Action Mapping Metadata for the /yourcart Action
<!-- Show your shopping cart -->
<action path="/technology/yourcart" type="toystore.controller.strutsactions.YourCartAction">
  <set-property property="application" value="ToyStore"/>
  <set-property property="viewobject"  value="ShoppingCart"/>
  <set-property property="releasemode" value="Stateful"/>
  <forward name="success" path="/technology/WEB-INF/jsp/yourcart.jsp"/>
</action>

The BC4JRequestProcessor works with the BC4JActionMapping class at runtime to access the values of these custom mapping properties so it can use the BC4J business delegate implementation to:

  1. Acquire an instance of the business service component from its component pool
  2. Lookup the business service component if this is the first request for that component
  3. Construct an instance of the request-scoped BC4JContext object

NOTE:

When you use any of the JDeveloper wizards to create BC4J/Struts JSP pages, they insure that web.xml and struts-config.xml exist in your project, creating them if necessary, and automatically configure the basic BC4J/Struts interaction for you. For example, after creating a new Struts Starter Page in the Struts-Based JSP for Business Components category in the New Gallery, you can just start adding your own Struts actions and JSP pages without having to manually create or configure the BC4J/Struts interaction yourself.


Struts actions like our YourCartAction action class in Example 13 use the BC4JContext object to access the declaratively configured business service. All of the business service lookup information is abstracted into XML configuration properties, and the lookup itself is handled by the BC4J-provided business delegate code. Our action code just accesses the business service component instance with a single method call, and the BC4J/Struts integration code in BC4JRequestProcessor has done the rest of the work for us.

Example 13: Struts Actions Use BC4JContext to Access Business Service
public class YourCartAction extends Action {
  public ActionForward execute(ActionMapping mapping, ...) {
    BC4JContext context = BC4JContext.getContext(request);
    ToyStoreService toyStoreSvc = (ToyStoreService)context.getApplicationModule();
    /*
     * Call custom business service methods here
     *
     * You can also find any collection of data transfer objects in the
     * "Model Data Map" by doing toyStoreSvc.findViewObject("string-key-name")
     */
   }
}

As an additional convenience, Example 12 shows that we can include a value for the viewobject property in the action mapping definition like this:

<set-property property="viewobject"  value="ShoppingCart"/>

This causes the BC4JRequestProcessor, after acquiring the application module component instance, to call its findViewObject() method to lookup an initial collection of data transfer objects from the model data map having the string key name of "ShoppingCart" . This allows your action code to access the collection using the getViewObject() method on the BC4JContext, instead of having to call getApplicationModule() and then performing the findViewObject() lookup yourself. Your action can certainly make use of findViewObject() method on the application module service to find any collections it needs access to. This convenience is useful if the action makes primary use of one particular collection of value objects, since it makes this clear in the action mapping metadata.

Setting the viewobject parameter can also be useful if you author generic Struts actions that operate on a view object without knowing its name. BC4J's provided Struts actions in the oracle.jbo.html.struts11.actions package are written generically like this, and assume the presence of this viewobject parameter to function properly.

Another important piece of application infrastructure you get for "free" is BC4J's application module pool. On each request that comes through your Struts controller layer, the BC4JRequestProcessor leverages the application module pool to improve performance and scalability of your application.

The Lifecycle of a Web Page Request Using Struts and BC4J

Figure 35 shows all of the "moving parts" together in a single sequence diagram to show the lifecycle of a web page request using the Struts/BC4J frameworks in tandem.

  1. A web request for http://yourserver/yourapp/someaction.do arrives
  2. The Struts ActionServlet forwards the request to the BC4JRequestProcessor
  3. The BC4JRequestProcessor sets up the model layer for this request by:

    • Retrieving the BC4JActionMapping instance corresponding to the "/someaction" action in struts-config.xml
    • Constructing a BC4JContext object holding a reference to the session identifier for the current browser user
    • Using the value of the application property on the action mapping to lookup the pool of appropriate business service components
    • Acquiring an instance of the business service component for use by the current user during the span of this request, holding onto a reference to it in the BC4JContext.
  4. The BC4JRequestProcessor forwards control to the configured action class
  5. The action class accesses the business service interface and its model data map via the BC4JContext to retrieve and/or modify any required model data, then it returns the appropriate ActionForward indicating what view-layer page to "flow" to next.
  6. The BC4JRequestProcessor forwards control to that selected view-layer JSP page
  7. The JSP page accesses and iterates over collection of data transfer objects from the model data map, still available in the request scope by using the BC4JContext, to write out the formatted web page destined to appear in the browser
  8. The BC4JRequestProcessor releases the instance of the business service back to the application module pool
  9. The ActionServlet ends the request, so request-scoped objects like BC4JContext get garbage collected, and the web page appears in the browser
Lifecycle of a Web Page Request Using Struts and BC4J
Figure 35: Lifecycle of a Web Page Request Using Struts and BC4J

The sequence diagram illustrates that the model layer is available in the same, consistent way both for model data manipulation by the controller layer as well as model data iteration and rendering by the view layer. In addition to providing convenience, architecting the solution this way allows BC4J to improve performance by avoiding a lot of the traditional extra copying of model data.

Overview of the BC4J-Supplied Struts Actions Used in the Demo

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 Form Bean for easier processing by your actions. Three very frequent tasks that actions need to perform in service of HTML forms are:

  1. Populating a FormBean with appropriate default values to display a create form
  2. Populating a FormBean with existing row data to display an update form
  3. Updating the model layer from a FormBean containing user-submitted changes

Among the several, handy Struts actions provided by BC4J in the oracle.jbo.html.struts11.actions package, two in particular directly address these three use cases:

  1. The EditAction's create() method populates a FormBean with entity-object-derived default values for a new row in a view object's default rowset
  2. The EditAction's edit() method populates a FormBean with values from an existing row in a view object's rowset
  3. The UpdateAction populates a new or existing row in a view object's default rowset using the FormBean containing user-submitted changes

While it is possible to directly use these generic actions in your Struts action mapping definitions, for clarity, in the BC4J Toy Store demo I chose to implement a specific action class for each action mapping. For example, I implemented a specific RegisterAction class, which extends the base EditAction and calls its create() method like this:

Example 14: Register Action Extends BC4J EditAction
package toystore.controller.strutsactions;
public class RegisterAction extends EditAction  {
  public ActionForward execute(...) {
    /*
     * Use the create() method of the EditAction to prepare a 
     * a new blank row and populate the Struts form bean.
     */
    return super.create(mapping,form,request,response);
  }
}

Having this one-to-one correspondence of action mappings to my own action classes made it easier for me to think about how my application was working, but it's not a required discipline. By extending the BC4J-supplied actions I inherited all the default functionality, but retained the clarity of which classes mapped to which actions.

If you try registering a new user in the BC4J Toy Store site as shown back in Figure 11, you'll notice that the country code is defaulted to the "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 submits the "Register New User" form, the submit button posts the request to the /createaccount.do URL which engages the /createaccount action. The /createaccount action is configured to use the CreateAccountAction class in the ToyStoreController project. This action class extends the default BC4J UpdateAction which carries out the work of populating a new row in the default rowset of the Accounts view object. Figure 36 illustrates this, and helps explain what happens next.

When the BC4J 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 evaluted 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 BC4J framework which is ultimately thrown back to the CreateAccountAction for handling.

If an exception is raised, the addError() method gets called in the UpdateAction to translate the bundled exception (and the nested exceptions it contains) into Struts ActionError objects. If no business rules are violated, the CreateAccountAction causes the transaction to be committed, and the transaction related to the application module component coordinates all "dirty" entity object instances in the cache to persist themselves, and then commits the transaction.


NOTE:

In the Struts Framework Customizations for the Controller Layer section below, we discuss some of the customization we've done to how the BC4J bundled exception is translated into errors to present to the user.


Specifically the steps involved in posting the changes from the browser to the database go like this:

  1. The UpdateAction sets the view object row attributes from corresponding Struts Form Bean properties

    Recall that the view object row is an updateable data transfer object whose structure is based on a view object's SQL query results.

  2. The view row delegates the attribute setter calls to the correct, underlying entity object attributes, causing business rules to be evaluated

    Recall that view object row delegates storage of entity-mapped attributes to underlying instances of entity objects in the entity cache.

  3. On transaction commit, entity objects persist their changes into their underlying tables

    Recall that the application module is a data-savvy service managing the unit of work related to any entity objects that are changed during the transaction.

How New Account Information Gets To the Database
Figure 36: How New Account Information Gets To the Database

NOTE:

The Account and Signon entities are modeled separately, and related by a 1-to-1 association, due to the desire to reuse the existing schema of the "classic" Java Pet Store demo which had these two entities modeled separately.


BC4J provides several other helpful actions for you to use in your applications. Table 3 presents the full list that comes with JDeveloper 9.0.3. Four of the actions extend the base Struts DispatchAction, which allows a generic action containing methods representing multiple "operations" to have the appropriate operation method be invoked based on the value of a URL parameter. There are two ways to use these built-in actions:

  1. You can provide a value for the parameter attribute in the relevant <action> tag in struts-config.xml, indicating the name of the request parameter that will drive selection of the correct "operation" method in the DispatchAction-based action class.
  2. You can build a new action class that extends one of these DispatchAction-based action classes, and explicitly invokes the method it wants on the superclass.

In the BC4J Toy Store Demo, I've adopted the second approach above to keep a one-to-one correspondence between actions and action classes. Of course, both approaches are valid, but I felt that readers trying to learn BC4J and Struts from this whitepaper and demo could follow this second approach more easily due to there being less "parameter magic" involved in understanding the flow of control.

Table 3: BC4J-Supplied Struts Actions
Action ClassDescription
EditActionExtends DispatchAction and contains create(), edit(), and delete() methods to assist in creating a blank/new row, finding an existing row for editing, or deleting an existing row respectively.
NavigationActionExtends LookupDispatchAction and contains next(), previous(), first(), last(), nextSet(), previousSet(), firstSet(), lastSet(), and gotoSet() methods to assist with navigating backwards and forwards through the rows in a rowset.
QueryActionExtends LookupDispatchAction and contains search(), addCriteria(), removeCriteria(), and clearAll() methods to simplify using the built-in Query by Example feature that BC4J view objects support (known as the "ViewCriteria" feature).
TransactionActionExtends DispatchAction and contains commit() and rollback() methods to help manage the transaction if you let the user control when these operations happen.
UpdateActionHandles posting user-submitted form data in a Struts FormBean into the appropriate row of the target BC4J view object.

NOTE:

The source code for the BC4J Struts actions comes with JDeveloper in the ./BC4J/src/bc4jstrutssrc.zip file, in case you're curious to check out the code.


Building Multi-Page Units of Work with Automatic State Management

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:

  1. HTTP Session Objects
  2. Hidden Form Fields
  3. Temporary database "staging tables"

All of these approaches have downsides, though:

  1. For some J2EE application servers, data stored in the HTTP Session cannot be reliably accessed in a "server farm". Even using servers like Oracle9iAS that do support clustering and replication of session state, the more and larger the objects you add to your HTTP Session, the more traffic you generate between servers to keep each other's HTTP Session objects synchronized.
  2. Storing pending data in hidden form fields, in all but the simplest of cases, requires inventing a scheme to reflect the objects' field structure in the names of the hidden fields, and increases the size of every page downloaded.
  3. Since pending data might not yet be complete, it often is impractical to commit it directly into the underlying application tables, which might raise database constraint violations. So, pending data could be stored in some staging tables that allow in-progress work to be saved and loaded later. However, either a special set of tables need to be created for each kind of pending data you need to store, or you need to invent a generic scheme for serializing the pending data, which is not fun.

The BC4J 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 BC4J. This approach has the following benefits:

  • Works on any J2EE-compliant application server
  • Works well in application server farms of any size
  • Has low overhead: only a single database round-trip per request
  • Avoids application server HTTP Session synchronization traffic
  • Doesn't increase the size of web pages delivered to the user
  • Uses the same, generic mechanism for all applications you build

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 BC4J 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 BC4J 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 newer Stateful mode.


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 BC4J 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 the application module pool is sized properly for the load your application needs to handle, then you get excellent performance and scalability.

Since the task of filling the user's shopping cart and buying the items is all part of a logical transaction, in the BC4J Toy Store demo all Struts actions are configured declaratively to use the application module pool in Stateful Mode by setting the nested, action property named releasemode to the value Stateful like this:

<action path="/technology/yourcart" type="toystore.controller.strutsactions.YourCartAction">
  <set-property property="application" value="ToyStore"/>
  <set-property property="releasemode" value="Stateful"/>
  <set-property property="viewobject"  value="ShoppingCart"/>
</action>

To look at one example, let's take the BC4J Toy Store Demo's shopping cart. It is implemented as a BC4J 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 adjustQuantityInCart() 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 have 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 need any more pending state to be managed. 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.

View Layer: JSP Pages and Struts/BC4J Tag Libraries

As Figure 37 shows, the majority of the view layer in the BC4J 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.

Display-Related Resources and Pages in the ToyStoreView Project
Figure 37: Display-Related Resources and Pages in the ToyStoreView Project

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" in the name contain the Italian translations of the same strings.

ToyStoreView.cpx is the BC4J client project file that stores one or more client data model definitions. Clicking on ToyStoreView.cpx in JDeveloper's System Navigator, and looking at its details in the Structure Pane, you can see that our demo only has a single client data model named ToyStore defined. We could create additional client data model definitions using the New Client Data Model... menu pick on the ToyStoreView.cpx, or modify existing client data model definitions by clicking on them in the structure pane and selecting the obvious option from their right-mouse menu.

Accessing and Iterating Over Model Data

Let's start by looking at one of the view layer JSP pages to study how it's built, the showproductdetails.jsp page. Recall from the The Lifecycle of a Web Page Request Using Struts and BC4J section that the BC4JRequestProcessor handles the details of acquiring an instance of your application module, making it available to the controller and view layers, and then releasing it at the end of the request. Using the BC4JContext object that the BC4JRequestProcessor creates at the beginning of the request, both the controller layer code and the view layer pages can access the application module's business service interface and can directly or indirectly access the collections of data transfer objects in its model data map. 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 /showproductdetails action, which ultimately forwards control to the showproductdetails.jsp page for display, is configured like this:

<!-- Show product details for a specific item -->
<action path="/technology/showproductdetails"
        type="toystore.controller.strutsactions.ShowProductDetailsAction">
  <set-property property="viewobject"  value="FindItems"/>
  <set-property property="application" value="ToyStore"/>
  <set-property property="releasemode" value="Stateful"/>
  <forward name="success" path="/technology/WEB-INF/jsp/showproductdetails.jsp"/>
</action>

It is mapped to the ShowProductDetailsAction action class, which performs the setup necessary to retrieve the appropriate model data. That action contains the following code:

BC4JContext context = BC4JContext.getContext(request);
/*
 * View object instance name "FindItems" is indicated in the Struts Config file
 * for this action, so we can directly get it from the context.
 * Action class for the "/showproductdetails" action
 */
ItemsForSale findItems = (ItemsForSale)context.getViewObject();
findItems.setItemToFind(request.getParameter("id"));
findItems.executeQuery();

As we saw when looking at the controller layer, the static method BC4JContext.getContext() abstracts the details of accessing the context object from the request scope. Notice that since the action definition specified a viewobject property, we can directly access the "preferred" view object instance (named "FindItems") using the getViewObject() method on the BC4JContext. We also cast the view object instance to the custom ItemsForSale interface, to be able to call the setItemToFind() method that encapsulates our bind variable handling. The call to the executeQuery() method on the view object executes the query, and retrieves the data. In the process, data transfer objects of appropriate structure are created for any rows retrieved. The view layer will be able to access these data transfer objects by looking up the "FindItems" collection from the model data map.

After executing the query, the action returns the selected page for showing the user. We see in this line of code that it returns the logical name "success" for the page to render.

return mapping.findForward("success");

Since the logical name of "success" is defined in the <action> definition to correspond to the /WEB-INF/jsp/showproductdetails.jsp page, the Struts controller passes control to this JSP page, whose source is shown in Example 15. The page starts by declaring two tag libraries:

  • The Struts "bean" tag library, whose tags will be prefixed by bean:
  • The BC4J "DataTags" tag library, whose tags will be prefixed by jbo:

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". The BC4J DataTags tag library includes tags that make it very easy to iterate and format the attribute values of the data transfer objects in our model data map. The tags in use in this page are:

  • <jbo:RowsetIterate> to iterate through a collection of data transfer objects in the model data map
  • <jbo:ShowValue> to output the unformatted value of a data transfer object attribute
  • <jbo:RenderValue> to output the formatted value of a data transfer object attribute, using any format mask that may have been specified declaratively for the attribute at design time.

By using the BC4J DataTags for the job they do best, we don't have to write code to retrieve the collection of value objects from the model data map by name. We just set the datasource property to "ToyStore.FindItems" which tells the <jbo:RowsetIterate> tag to lookup the collection of data transfer objects from the model data map named using the string key FindItems. The "ToyStore." prefix corresponds to the name of the client data model that we are using, since theoretically a page could combine data from multiple client data models.


NOTE:

The source code for the BC4J Data Tags tag library and other client-side integration code for web applications comes with JDeveloper in the ./BC4J/src/bc4jhtmlsrc.zip.zip file, in case you're curious to check out the code.


Example 15: The showproductdetails.jsp page
<%@ taglib uri="/technology/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/technology/webapp/DataTags.tld"     prefix="jbo" %>
<html>
  <head>
    <title><bean:message key="details.title"/></title>
  </head>  
  <body>
    <jsp:include page="header.jsp" flush="true"/>
    <jsp:include page="navbar.jsp" flush="true"/>
    <jbo:RowsetIterate datasource="ToyStore.FindItems" changecurrentrow="false">
      <table bgcolor="white" width="100%">
        <tr>
          <td>
            <font size="5" color="#003399">
              <jbo:ShowValue dataitem="Name"/>
            </font>
          </td>
          <td>
            <jbo:RenderValue dataitem="Listprice"/>
          </td>
          <td>
            <jbo:ShowValue dataitem="InStock"/>
          </td>
          <td>
            <a href="yourcart.do?id=<jbo:ShowValue dataitem="Itemid"/>"
               ><img src="<bean:message key='images.buttons.addtocart'/>"
                     border="0"
                     alt="<bean:message key="cart.addItem"/>"></a>
          </td>
        </tr>
        <tr>
          <td colspan="4" valign="center">
            <image align="left" border="0" 
                     src="images/<jbo:ShowValue dataitem='Picture'/>">
            <jbo:ShowValue dataitem="Description"/>
          </td>
        </tr>
      </table>     
    </jbo:RowsetIterate>
  </body>
</html>

The output that appears in the browser is shown in Figure 38.

Show Product Details Page
Figure 38: Show Product Details Page

All of the JSP pages in the BC4J Toy Store demo follow this basic approach for iterating and formatting data. The search.jsp page (used by the /search action) makes use of the Struts "Logic" tag library to conditionally format sections of the page. The SearchAction class (inheriting shared logic from the ToyStoreDisplayDataWithPagingAction class in the FwkExtensions project) sets a number of HTTP Request attributes to indicate details related to the collection being presented. It sets the:

  • NoRowsFound attribute, if "FindProducts" collection is empty
  • CurrentPage attribute, containing the number of the current page
  • TotalPages attribute, containing the total number of pages
  • NextPage attribute, if the current page is not the last page
  • PrevPage attribute, if the current page is not the first page

Once these request attributes have been set (or not) by the SearchAction in the controller layer, the <logic:present> and <logic:notPresent> tags like those in Example 16 can conditionally includes blocks of tags in the page without resorting to the ugliness of embedded Java scriptlets.

Example 16: Using the Struts Logic Tag Library for Conditional Formatting
<%@ taglib uri="/technology/WEB-INF/struts-bean.tld"  prefix="bean"  %>
<%@ taglib uri="/technology/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/technology/webapp/DataTags.tld"      prefix="jbo"   %>
<html>
  <!-- Title tags here -->
  <body>
    <!-- Common header includes here -->
    <logic:present name="NoRowsFound">
        <!-- "Search found no items" message here -->
    </logic:present>
    <logic:notPresent name="NoRowsFound">
      <logic:present name="PrevPage">
        <!-- Previous Page link tags here -->
      </logic:present>
      <!-- Outputs "Page X of Y" in current language -->
      <bean:message key="paging.page"/>
      <bean:write name="CurrentPage"/>
      <bean:message key="paging.of"/>
      <bean:write name="TotalPages"/>    
      <logic:present name="NextPage">
        <!-- Next Page Link tags here -->
      </logic:present>
       <!-- Rest of page here -->
    </logic:notPresent>
  </body>
</html>

A Data Entry Form Using Traditional Form Layout Approach

The editexistingaccount.jsp page (used by the /editaccount action) illustrates an example of a page that formats a data entry form to edit user profile information. The EditAccountAction sets up model layer and populates the Struts FormBean used by this action by:

  1. Creating a oracle.jbo.Key object based on the current logged-in user's username
  2. Looking up an existing row in the Accounts view object using this key
  3. Calling the edit() method on the EditAction superclass to populate the FormBean from the row data.

We use the Struts "HTML" tag library to use the <html:form> tag that has built-in smarts to discover which FormBean is in use by looking at the value of its action attribute. In this case, our tag looks like:

<html:form action="/technology/updateaccount.do" method="post">

The Struts <html:form> tag implementation sees the action attribute value of "/updateaccount.do" and uses it, along with its action mapping information, to determine that the FormBean named "AccountsForm" is the one that should be used to render this form.

Since we're rendering the data entry form for just a single "row" of user profile information, we use the <jbo:Row> tag instead of <jbo:RowsetIterate> since we don't need to iterate the rows.

<jbo:Row datasource="ToyStore.Accounts" action="current" id="row">
  <!-- Other <jbo:XXX> tags in here work in the context of this current row -->
</jbo:Row>

Nested inside this <jbo:Row> tag, we 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.

Example 17 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 <jbo:ShowValue> tag, we can easily just output the value of the field for display. The <bean:message> tags are outputting translatable strings from the ToyStoreResources.properties properties file to display the tooltip and the label for the username.


NOTE:

When they are nested within a Struts <html:form> tag, the BC4J DataTags work with the Struts FormBean data automatically.


Example 17: Showing Read-Only Data Using jbo:ShowValue
<%-- Username field --%>
<tr>
  <th align="right" title="<bean:message key="account.username.tooltip"/>">
    <bean:message key="account.username.label"/>
  </th>
  <td title="<bean:message key="account.username.tooltip"/>">
    <jbo:ShowValue dataitem="Username"/>
  </td>
</tr>

When data needs to be entered or edited, you can use a number of other tags in the BC4J DataTags library to render databound controls. Example 18 shows using the <jbo:InputPassword> tag to show the password field. It 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 rerendered, 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.

Example 18: Use BC4J DataTags to Create Data-Bound Form Controls
<%-- Password field --%>
<tr>
  <th align="right" title="<bean:message key="account.password.tooltip"/>">
    <bean:message key="dataentryform.mandatory"/>
    <bean:message key="account.password.label"/>
  </th>
  <td title="<bean:message key="account.password.tooltip"/>">
    <jbo:InputPassword dataitem="Password"  cols="30"  maxlength="25"/>
  </td>
  <td><html:errors property="Password"/></td>
</tr>

BC4J 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 <jbo:ShowHint> tags shown in Example 19 illustrate how your JSP pages can make use of 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 the control hints if necessary.

Example 19: Accessing BC4J Attribute Control Hints and Metadata
<%-- Firstname field --%>
<tr>
  <th align="right" title="<jbo:ShowHint dataitem="Firstname" hintname='TOOLTIP'/>">
    <%if(def.lookupAttributeDef("Firstname").isMandatory()){%>
      <bean:message key="dataentryform.mandatory"/>
    <%}%>
    <jbo:ShowHint dataitem="Firstname" hintname="LABEL"/>
  </th>
  <td>
    <jbo:InputText dataitem="Firstname"  cols="35"  maxlength="30"/>
  </td>
  <td><html:errors property="Firstname"/></td>
</tr>

BC4J also makes a rich set of component metadata available through its client-accessible interfaces in the oracle.jbo.* package. Each data transfer object exposes runtime metadata as well about all aspects of its individual attributes. In Example 19 we see an example of using this metadata to detect at runtime whether a given attribute, like Firstname in this case, is mandatory or not.

<%if(def.lookupAttributeDef("Firstname").isMandatory()){%>
  <bean:message key="dataentryform.mandatory"/>
<%}%>

How do we get access to this def definition object? The enclosing <jbo:Row> tag defines an id="row" attribute which creates a scriptable variable in the JSP page named row.

<jbo:Row datasource="ToyStore.Accounts" action="current" id="row">

Then a subsequent Java scriptlet in the page can make calls to methods on the oracle.jbo.Row interface representing the current row using this scriptable variable named row. For example, we have the following line in the editexistingaccount.jsp page:

<%  StructureDef def = row.getStructureDef(); %>

This retrieves an instance of the StructureDef interface that exposes metadata about the current "row" data transfer object. We can then call methods on it like lookupAttributeDef() to find metadata for a specific attribute, then access the attribute-specific information like isMandatory(), among many other interesting properties.

Finally, we look at an example of a data-bound form control like a poplist that populates its list of choices from one collection and sets the current selection based on the current value in an attribute of the current row. Example 20 shows how to use the <jbo:InputSelect> tag to do just this. The current value of the list is determined by the value of the Country attribute. The list of valid choices comes from the collection of data transfer objects named "CountryList" in the model data map of the application module referenced in the ToyStore client data model definition. The displaydataitem attribute gives the attribute name in the CountryList collection whose value will be used to display to the user. The displayvaluedataitem attribute gives the attribute name in the same collection whose value will be used as the value to assign to the Country attribute. For example, the list might display "Spain", and the value of that selection assigned to the Country attribute might be "ES".

Example 20: Using jbo:InputSelect to Render a Data-Bound PopList
<%-- Country field --%>
<tr>
  <th align="right" title="<jbo:ShowHint dataitem="Country" hintname='TOOLTIP'/>">
    <%if(def.lookupAttributeDef("Country").isMandatory()){%>
      <bean:message key="dataentryform.mandatory"/>
    <%}%>
    <jbo:ShowHint dataitem="Country" hintname="LABEL"/>
</th>
  <td>
    <jbo:InputSelect dataitem="Country"
                     displaydatasource="ToyStore.CountryList"
                     displaydataitem="Description"
                     displayvaluedataitem="Code"/>
  </td>
  <td><html:errors property="Country"/></td>
</tr>

Rendering Data Entry Forms in a More Generic Way Using Metadata

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 21 shows the registernewuser.jsp page (used by the /register action) which shows 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 <jbo:DataEdit> tag. This tag works like a reusable component, including the contents of the page indicated in its relativeUrlPath attribute, similar to a <jsp:include>. If this attribute is not provided, then the default name of the included "component" page is DataEditComponent.jsp. In this case, we've given a specific name of a component page /WEB-INF/jsp/lib/dataentryform.jsp whose name and location (in a ./lib subdirectory) help convey the idea that this particular JSP page is being used like a reusable component in a library.

Example 21: Register New User Page
<%@ taglib uri="/technology/webapp/DataTags.tld"  prefix="jbo" %>
<%@ taglib uri="/technology/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
  <head>
    <title><bean:message key="registernewuser.title"/></title>
  </head>
  <body>
    <jsp:include page="header.jsp" flush="true"/>
    <jsp:include page="navbar.jsp" flush="true"/>  
    <h2><bean:message key="registernewuser.header"/></h2>
    <jbo:DataEdit relativeUrlPath="/technology/WEB-INF/jsp/lib/dataentryform.jsp"
                  datasource="ToyStore.Accounts"
                  targetURL="/technology/createaccount.do"/>
  </body>
</html>

So the actual work being done lies in the dataentryform.jsp "library" page shown in Example 22. The page builds a data entry form with one data-bound control for each attribute in the view object's result "row". The <jbo:DataEdit> tag provides a value for its datasource attribute that represents the name of the collection of data transfer objects in the model data map to use. In this example, it provides the value of "ToyStore.Accounts".

The dataentryform.jsp library page reads the value of this datasource tag attribute as if it were a request parameter using:

<% String ds = request.getParameter("datasource"); %>

Then it uses this ds variable later in the page to pass the value to the <jbo:AttributeIterate> tag:

<jbo:AttributeIterate id="def" datasource="<%= ds %>">

The <jbo:AttributeIterate> tag allows us to iterate over attribute metadata in a generic way. Inside this attribute iteration loop, the page makes use of the <jbo:ShowHint> tag to show the appropriate labels and tooltips for each attribute based on the BC4J component attribute-level control hints. We also access the def scriptable variable created by the <jbo:AttributeIterate> tag in order to conditionally include the mandatory field symbol for fields that are mandatory, and to configure the <html:errors>property attribute value so that any error messages are shown next to the control whose value is in error.


NOTE:

While we tried to avoid use of embedded Java scriptlets in our "normal" JSP pages, when coding JSP pages that generate pages from metadata, it's often unavoidable to use small scriptlets. At least their use is limited to a single, generic page in the "library", allowing other pages to stay scriptlet-free.


Example 22: Reusable "Library" Page Renders Data Entry Form from Metadata
<%@page import="org.apache.struts.action.ActionErrors" %>
<%@ taglib uri="/technology/webapp/DataTags.tld" prefix="jbo" %>
<%@ taglib uri="/technology/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/technology/WEB-INF/struts-bean.tld" prefix="bean" %>
<% String ds = request.getParameter("datasource"); %>
<center>
  <table border="0">
    <tr>
      <td>
        <html:errors bundle="GlobalErrors"
                     property="<%= ActionErrors.GLOBAL_ERROR %>"/>
      </td>
    </tr>
  </table>
</center>
<%--
 | Build a form with an editable field for each of the attributes of the row
 +--%>
 <center>
  <html:form action='<%=request.getParameter("targetURL")%>' method="post">
    <table border="0">
     <%-- Iterate over all of the attributes in the view object --%>
      <jbo:AttributeIterate id="def" datasource="<%= ds %>">
        <tr>
          <th align="right" title="<jbo:ShowHint hintname='TOOLTIP'/>">
            <%-- If the attribute is required, mark with an asterisk --%>
            <%if(def.isMandatory()){%><bean:message key="dataentryform.mandatory"/><%}%>
            <jbo:ShowHint hintname="LABEL"/>:
          </th>
          <td align="left" title="<jbo:ShowHint hintname='TOOLTIP'/>">
            <jbo:InputRender datasource="<%= ds %>"/>
          </td>
          <td>
            <html:errors property="<%= def.getName() %>"/>
          </td>
        </tr>
      </jbo:AttributeIterate>      
    </table>
    <%-- 
     | The following four hidden parameters are required for the
     | BC4J EditAction and UpdateAction Struts actions.
     +--%>
    <html:hidden property="jboEvent" />
    <html:hidden property="jboEventVo" />
    <html:hidden property="jboRowKey" />
    <html:hidden property="amId" />
    <html:submit><bean:message key="dataentryform.register"/></html:submit>
  </html:form>
</center>

Another new tag used in this generic page is the <jbo:InputRender> tag. This tag renders an appropriate data-bound control for the current attribute based on additional control hints that can be set on the BC4J entity object attribute and/or view object attribute level. Each BC4J component supports setting custom properties that can be read at runtime and be used to drive metadata-driven behavior. For BC4J entity objects and view objects which have attributes, you can also set custom properties on individual attributes as well.

To edit attribute properties, you can click on the entity object or view object in the System Navigator, then select the desired attribute in the Structure Pane, and pick Edit DesiredAttributeName... from the right-mouse context menu. Figure 39 shows the Attribute Wizard that will appear if you do this. In particular, this figure shows the custom attribute-level properties for the Country attribute in the Accounts view object that is part of the ToyStoreModel project in the demo. The <jbo: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.

Custom Attribute Properties for Country Attribute in Accounts View Object
Figure 39: Custom Attribute Properties for Country Attribute in Accounts View Object

In this example, we've specified the class name toystore.fwk.view.VOBasedPoplistRenderer which implements a customized poplist renderer for the Country attribute. This custom field renderer extends the default oracle.jdeveloper.html.PickList renderer to drive some of its databinding properties off the values of additional custom properties like ListViewObject, DisplayAttribute, and DataAttribute, also visible in Figure 39. The source code for the custom renderer (from the FwkExtensions project in the demo) is shown in Example 23. You can see the code accesses the AttributeDef interface for the current attribute being rendered (using the inherited getAttributeDef() method), and then calls getProperty() on it three times to evaluate whether the current attribute defines the ListViewObject, DisplayAttribute, and DataAttribute properties. Then it uses these values to initialize the name of the view object instance to use, the attribute name to display, and the attribute to use as the value of the poplist. The net effect is that when our generic dataentryform.jsp "library component" page renders an HTML form for a row in the Accounts view object, the Country attribute will render as a data-bound poplist populated from the collection of value objects in the model data map named "CountryList".

Example 23: Custom Field Renderer Used by jbo:InputRenderer
package toystore.fwk.view;
import oracle.jdeveloper.html.PickList;
import oracle.jbo.Row;
import oracle.jbo.AttributeDef;
/**
 * Extends the oracle.jdeveloper.html.PickList renderer to drive its key
 * definitional properties from custom BC4J attribute properties:
 *
 *   ListViewObject   : Name of the VO instance to use for picklist
 *   DisplayAttribute : Name of the attribute in the VO to display in list
 *   DataAttribute    : Name of the attribute to use for the data value of list
 */
public class VOBasedPoplistRenderer extends PickList {
  /**
   * Overrides renderToString() in PickList
   */
  public String renderToString(Row row) {
    AttributeDef  aDef = getAttributeDef();
    String        voInstanceName   = (String)aDef.getProperty("ListViewObject");
    String        displayAttribute = (String)aDef.getProperty("DisplayAttribute");
    String        dataAttribute    = (String)aDef.getProperty("DataAttribute");
    setDisplayAttributes(displayAttribute);
    setDataAttributes(dataAttribute);
    setDataSourceInfo(ds.getApplicationModule(), voInstanceName);
    return super.renderToString(row);
  }
}

If you adopt a generic data form rendering technique like this in your applications, you see that 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.

Struts and BC4J Features for Building Multilingual Applications

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 BC4J framework for building applications that need to support user interfaces in multiple languages.

Struts Message Resource File Support

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 BC4J 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 24. 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.

Example 24: Configuring Default and Secondary Struts Message Resource Files
<struts-config>
    :
  <!--
   | This entry tells Struts where to find the default application
   | resources properties file
   +-->
  <message-resources parameter="toystore.view.ToyStoreResources"/>

  <!--
   | Resource used to render globals errors with <html:errors>
   +-->
  <message-resources key="GlobalErrors" parameter="toystore.view.GlobalErrors"/>
</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: ToyStoreResources_de_CH.properties. This would be used when a user has set their locale to use the German language (de) for the country of Switerzland (CH).


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"/>

The Struts <bean:write> tag implementation 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:

  1. Return the string from ToyStoreResources_de_CH.properties file if it exists, then
  2. Try finding it in the ToyStoreResources_de.properties file if it exists, then
  3. Return the string matching the key from the default message resource file

Struts infers the locale of the current browser user by looking at HTTP request header properties that browsers send with each request, indicating the users preferred languages. Specifically, on each request through the Struts RequestProcessor, the default implementation of the method:

protected void processLocale(HttpServletRequest req, HttpServletResponse resp)

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.

As an example of how we can extend frameworks (we'll see a lot more in Customizing the Default Framework Behavior below) , the toystore.fwk.controller.ToyStoreRequestProcessor class in the FwkExtensions project in the demo extends the BC4JRequestProcessor and overrides the processLocale() method (which it inherits from the base Struts RequestProcessor) to null out the Action.LOCALE_KEY session attribute each time, just before calling the superclass' implementation of processLocale() like this:

protected void processLocale(HttpServletRequest request, HttpServletResponse response) {
  HttpSession session = request.getSession();
  session.setAttribute(Action.LOCALE_KEY, null);
  super.processLocale(request, response);
}

This simple change causes Struts to re-evaluate the current session's locale on each request so that a user could toggle their preferred language in their browser during a session and have the application adapt on the next click. In practice, your application might not require this on-the-fly language changing, in which case the default Struts locale determination behavior of once-per-session will be adequate. An alternative approach might be to show little flag icons indicating the languages your application supports. Your Struts actions related to the links on these flag buttons could then just explicitly set the HTTP Session variable Action.LOCALE_KEY to the desired locale.

Custom BC4J Component Message Bundles

BC4J 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 BC4J design time wizards automatically manage 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 25. 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 "sub"-package. 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 BC4J design time automatically encourages through consistent naming patterns and built-in deployment packaging support.

Example 25: Default Version of the Account Entity Object's Resource Bundle
package toystore.model.businessobjects.common;
import oracle.jbo.common.JboResourceBundle;
import oracle.jbo.*;
import toystore.fwk.exceptions.ErrorMessages;
//  ---------------------------------------------------------------
//  ---    File generated by Oracle Business Components for Java.
//  ---------------------------------------------------------------
public class AccountImplMsgBundle extends JboResourceBundle  {
  public AccountImplMsgBundle() {}
  /**
   * @return an array of key-value pairs.
   */
  public Object[][] getContents() {
    return super.getMergedArray(sMessageStrings, super.getContents());
  }
  static final Object[][] sMessageStrings = {
    {"Country_Rule_0", "Invalid country code"},
    {"Addr1_LABEL", "Street Address"},
    {"Addr1_TOOLTIP", "Enter your street address"},
    /* etc. */
    {"Zip_LABEL", "Postal Code"},
    {"Zip_TOOLTIP", "Enter your postal code"}
  };
}

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.

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:

  1. Copy AccountImplMsgBundle.java to AccountImplMsgBundle_it.java
  2. Rename 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
  3. Rename the default constructor in the new AccountImplMsgBundle_it.java file from:

    public AccountImplMsgBundle(){}

    to:

    public AccountImplMsgBundle_it(){}
  4. Edit the user-visible strings to be in Italian.

This will give you a translated message bundle like Example 26

Example 26: Italian Version of the Account Entity Object's Resource Bundle
package toystore.model.businessobjects.common;
import oracle.jbo.common.JboResourceBundle;
import toystore.fwk.exceptions.ErrorMessages;
/**
 * Italian translations of Account entity object control hints
 * Traduzioni italiane dei control hint dell'entity object Account
 */
public class AccountImplMsgBundle_it extends AccountImplMsgBundle  {
  public AccountImplMsgBundle_it() {}
  public Object[][] getContents() {
    return super.getMergedArray(sMessageStrings, super.getContents());
  }
  static final Object[][] sMessageStrings = {
    {"Country_Rule_0", "Codice di paese inesistente"},
    {ErrorMessages.ENTITY_ALREADY_EXISTS,
    "Un altro utente ha già scelto questo nome. Prova un altro."},  
    {"Addr1_LABEL", "Indirizzo"},
    {"Addr1_TOOLTIP", "Inserisci il tuo indirizzo"},
    /* ecc. */
    {"Zip_LABEL", "CAP"},
    {"Zip_TOOLTIP", "Inserisci il tuo codice di avviamento postale"}
  };
}

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 4 lists the components in the BC4J Toy Store demo that have associated custom message bundles.

Table 4: BC4J Components With a Custom Message Bundle
toystore.model.* Component Contains
*.businessobjects.Account
  1. Default labels and tooltips for Account attributes
  2. Account-specific error message for the generic EntityAlreadyExists custom exception in the toystore.fwk.model.businessobjects package
  3. Custom error message for the ListValidationBean rule attached to the Country attribute which checks if the country code is one of the value codes from the toystore.model.dataaccess.CountryList view object's default rowset.
*.businessobjects.ItemIndicates format mask for the Listprice attribute and the default number formatter class to use.
*.businessobjects.OrdersContains custom validation error message thrown by the validateCreditCardExpiration() validation method.
*.businessobjects.Signon
  1. Default labels and tooltips for Signon attributes
  2. Signon-specific error message for the generic EntityAlreadyExists custom exception in the toystore.fwk.model.businessobjects package
*.dataaccess.ShoppingCartIndicates format mask for the Listprice and ExtendedTotal attributes and the default number formatter class to use.
*.dataaccess.AccountsControl 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. You can get or set the current locale using methods on the oracle.jbo.Session interface, which you can access by calling getSession() on any instance of an application module. The extended BC4JRequestProcessor complements the Struts default locale detection by including code that calls:

am.getSession().setLocale(locale);

to set the correct user locale for the current user session in the application module.

Customizing the Default Framework Behavior

Two of the biggest benefits of framework-based J2EE development are:

  1. Your application components stand on the shoulders of the base framework functionality
  2. When you need to make application-wide changes, you can extend the base framework

For example, if the base BC4J 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 BC4J 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 40 illustrates how this looks for one of the entity objects in the BC4J Toy Store demo like Account which does exactly this.

Application Components Can Extend a Customized Framework Base Class
Figure 40: Application Components Can Extend a Customized Framework Base Class

The same opportunity that is available for customizing BC4J framework base classes also exists for many aspects of the Struts framework, too. In this section we highlight the various Struts and BC4J framework customizations that were made to support the BC4J Toy Store demo. All of these framework customizations live in the FwkExtensions project in the BC4JToyStore workspace.


NOTE:

As with all code in the BC4J 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 FwkExtensions project for more details on what each framework extension is doing.


Struts Framework Customizations for the Controller Layer

In the toystore.fwk.controller package we made the following controller layer customizations:

  • ToyStoreDisplayDataWithPagingAction

    This action centralizes the common processing shared by all Struts actions in the demo that support paging through query results a set at a time. The /search, /showcategory, and /showproduct action classes extend this base class.

  • ToyStoreEditAction

    This action extends the basic BC4J EditAction to add functionality that allows looking up an existing row by explicitly provided key value, instead of relying on the implicit processing of the jboKey request parameter.

  • ToyStoreUpdateAction

    This action extends the basic BC4J UpdateAction to customize the way that a "tree" of bundled BC4J exceptions get translated into Struts ActionError objects for display to the user.

  • ToyStoreRequestProcessor

    This class extends the BC4JRequestProcessor (which, in turn, extends the base Struts RequestProcessor) to override the processLocale() method to allow the user to toggle the preferred language on the fly. The default implementation only sets the user's preferred locale once per session.

BC4J Framework Customizations for the Model Layer Business Objects

In the toystore.fwk.model.businessobjects package, we created the ToyStoreEntityImpl class that extends the base BC4J entity implementation base class to add the following features:

  1. Declarative ability to force attribute values to UPPER or lower case.
  2. Throwing of a specific custom exception 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 27.

Example 27: Custom Component and Attribute Properties in XML Descriptor File
<Entity Name="Account" DBObjectName="ACCOUNT" AliasName="Account"
        BindingStyle="Oracle" RowClass="toystore.model.businessobjects.AccountImpl"
        MsgBundleClass="toystore.model.businessobjects.common.AccountImplMsgBundle">
   :
   <Attribute Name="State" IsNotNull="true" Precision="2" Type="java.lang.String"
      ColumnName="STATE" ColumnType="VARCHAR2" SQLType="VARCHAR" >
      <Properties>
         <Property Name ="Case" Value ="Upper" />
      </Properties>
   </Attribute>
    :
</Entity>

Figure 41 shows how to customize the base class for a BC4J 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.

Setting BC4J Framework Base Classes from the Java Panel
Figure 41: Setting BC4J Framework Base Classes from the Java Panel

BC4J Framework Customizations for the Model Layer Data Access Components

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.

  • ToyStoreViewRowImpl

    Works around Bug#2698389 where a field marked as Updateable_While_New renders incorrectly in a Struts-based form after an attempted create fails with errors. This bug is fixed in BC4J 9.0.3.3 and 9.0.4.

BC4J Framework Customizations for the Business Service Layer

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 JboException resulting from the posting attempt,
  • Checks whether this exception represents an Oracle database constraint violation,
  • If it does, then it 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 BC4J 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.

Custom Validation Rule as Framework Extension

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:

  1. On-demand creation of the toystore.fwk.rules.dataaccess.StatesForCountry view object
  2. Reuse of the same view object instance for subsequent executions of the rule
  3. Setting of Max Fetch Size to 1 to improve performance of view objects that are known to fetch a single row.

Also, this example illustrates that it's easy to build reusable rules that make use of BC4J 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.

Deployment and Packaging Considerations

JDeveloper supports the ability to create deployment profiles that encapsulate all the details required to create archives of various kinds for deployment. This section describes the deployment profiles in use in the BC4J Toy Store Demo.

The ToyStoreModel project contains a BC4J deployment profile named ToyStoreModel.bcdeploy. This was created using by selecting the project in the System Navigator, and selecting the Create Business Deployment Profiles... right-mouse menu on the context menu. In the wizard that appears, we selected the Simple Archive Files option. By expanding the node in the navigator, you can see that a BC4J deployment profile contains two "children" profiles which are defined to package up the compiled artifacts like...

  • Custom component interfaces and message bundles from the *.common subpackages of the project into the ToyStoreModelCSCommon.jar file
  • Component implementation files (*Impl.java) and XML definition files, into the ToyStoreModelCSMT.jar file

For a client like our Struts web controller layer that is using the BC4J model layer in "local mode" as a set of JavaBeans, both of these JAR files must be in the 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.


NOTE:

When deploying application modules with exposed custom methods on their business interface as EJB Session Beans, BC4J will generate some additional remote client stubs. By default, they will get generated in a separate project in your workspace with a name that makes it obvious what they are for. While you don't need to understand what code gets generated in those remote client stubs, you do have to include those client stub classes in your client tier along with the BC4J generic application client jar file. Failure to do so will result in ClassCastException errors at runtime when the BC4J business delegate implementation attempts to make use of the client stubs automatically on your behalf.


Each of the other projects contains a deployment profile that deploys its classes into an archive in the:

./bc4jtoystore/webroot/WEB-INF/lib

directory. Then, as shown in Figure 42 the BC4JToyStore.deploy profile ties all of the other JAR files together to produce a WAR file and an EAR file for deploying to Oracle9iAS or an external application server.

Settings for the BC4JToyStore.deploy Profile
Figure 42: Settings for the BC4JToyStore.deploy Profile

For completeness, Table 5 lists all the deployment profiles in use in the BC4J Toy Store Demo.

Table 5: Deployment Profiles in the BC4J Toy Store Demo
Project NameDeployment ProfileJAR File(s) Produced
Deployment.jprBC4JToyStore.deploy
  • BC4JToyStore.ear
  • BC4JToyStore.war
ToyStoreModel.jprToyStoreModel.bcdeploy
  • ToyStoreModelCSMT.jar
  • ToyStoreModelCSCommon.jar
ToyStoreView.jprToyStoreModel.bcdeployToyStoreViewResources.jar
Deployment.jprToyStoreController.deployToyStoreController.jar
FwkExtensions.jprFwkExtensions.deployToyStoreFwkExtensions.jar
Utilities.jprmakeJarFile.deployDynaActionFormGen.jar

Table 6 shows the additional supporting JAR files that are required for Struts/BC4J support.

Table 6: Overview of Additional JAR Files Support the Toy Store Demo
JAR FilesDescription
  • bc4jstruts.jar
  • datatags.jar
BC4J/Struts support and BC4J DataTags tag library implementation
  • struts.jar
  • commons-beanutils.jar
  • commons-collections.jar
  • commons-digester.jar
  • commons-fileupload.jar
  • commons-logging.jar
  • commons-services.jar
  • commons-validator.jar
Struts framework runtime libraries

If you are deploying onto a J2EE application server other than Oracle, there are "How To" documents on the OTN JDeveloper HowTo Page that will lead you through the steps. These documents provide a list of the base BC4J framework libraries that are required to install on your non-Oracle application server. They are installed by default in Oracle9iAS.

Getting Started on Your Own BC4J/Struts Applications

In the sections above, we analyzed all the key aspects of the BC4J 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 several Oracle9i JDeveloper features for simplifying the development of our model, view, and controller-layer components like:

  • An editor to easily work with all of the configuration info in struts-config.xml
  • Integrated UML modeling for your BC4J components, with two-way synchronization
  • Wizards and editors to create, configure, and test your BC4J model components
  • Ability to run your Struts actions, JSP pages, and BC4J components using the embedded Oracle Containers for J2EE (OC4J) container without going through a lengthy deploy step each time.

When you start building new applications with Struts, BC4J, and JSP, JDeveloper's New Gallery offers additional wizards that assist in getting started on the right foot. After creating your new workspace, and the multiple projects to contain your model, view, and controller layers, you can expand the Business Tier category in the New Gallery and select the Business Components subcategory. Double-click the Business Components Package icon to launch the wizard that lets you create BC4J entity objects from existing database tables. You can optionally have the wizard create initial data model components like view objects and an application module as well. This starts you off quickly with a fully working model layer that you can then incrementally tailor using component editors to:

  • Add business rules to your entity objects
  • Modify your view object components to change columns included, WHERE and ORDER BY clauses, or database tuning options
  • Add custom service methods to your application module as needed

Of course, if you don't have existing tables you can start by creating a new UML diagram, and iteratively designing your business objects (and the associations between them) from scratch, right on the model. After changing any default mapping information for your new entity objects, right-clicking on a package and selecting Create Database Objects... allows you to generate the DDL statements to create the underlying database tables.

With an initial model layer up and working, you can proceed to create your view pages and Struts controller actions that will bring the web user interfaces to life. Back in the New Gallery, under the Web Tier category, you'll find both the Struts and the Struts-Based JSP for Business Components subcategories. These contain icons that launch wizards to assist in in Struts-enabling any project [Starter Application] and helping to create a:

  • New action mapping and associated action class [Action]
  • New form bean class [ActionForm]
  • New BC4J client data model [Business Components Client Data Model]
  • New action mapping, action class, and JSP page ready to work with a BC4J model layer [Struts-Starter Page]
  • Complete web application with query, page-by-page browsing, create, update, delete features based on one or more view object instances in a BC4J model data map [Complete Struts-Based JSP Application]

Once you've setup the basic action classes and JSP pages, you can use the JSP editor and Java code editors to evolve the functionality of the view and controller layers respectively. All of the Struts and BC4J DataTag tag libraries are available on the Component Palette, so you just pick what tag you need and the tag editors guide you through filling out any required parameters. If you prefer working directly in your JSP code, there is context-senstive "Tag Insight" assistance that puts tag library element and attribute help right where you need it while you're typing.

At any time, you can right-mouse on one of your Struts actions in the System Navigator and select Run (or Debug) to run (or debug) your application using the embedded J2EE container without having to go through any deployment step. Of course, other JDeveloper features like integrated performance profiling, CodeCoach code improvement suggestions, and integrated source control management, among many others, work great on your Struts/BC4J applications, too!

Conclusion

By exploring the details of this BC4J Toy Store application built using Oracle BC4J and Jakarta 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 BC4J 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 Oracle9i JDeveloper IDE provides excellent support for putting both Struts and BC4J to use in real application scenarios. Over 800 developers inside Oracle Corporation alone are using the BC4J 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 BC4J as well, as evidenced by the enthusiastic questions we get every day on the Oracle Technet JDeveloper Discussion Forum -- a forum you can use as well as you experiment with Struts and BC4J.

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:

This paper is a companion to the Simplifying J2EE Development with BC4J paper published in 2002.


Appendix 1: Installing on the Standalone OC4J Container

The Deployment.jpr project in the BC4JToyStore.jws workspace contains a web application deployment profile named BC4JToyStore.deploy. After setting up a "Standalone OC4J" Application Server connection definition, you can select Deploy to YourOC4JConnectionName... to deploy the application to standalone OC4J.


NOTE:

The simplest way to run a standalone OC4J container that is pre-configured for running BC4J-powered applications is to startup the OC4J container that ships with JDeveloper itself in standalone mode. To do this, assuming the current directory is your JDeveloper installation home, you can do:

$ cd j2ee\home
$ java -jar oc4j.jar

This will startup OC4J in standalone mode, with its web listener on port 8888.

If you want to install the Toy Store demo on a standalone OC4J 9.0.3 container that you downloaded separately from OTN, then this will not have BC4J pre-installed (to keep the download size to a minimum). You will need to run the bc4j2oc4j.bat script in the JDEV_HOME/BC4J/bin directory to automate the installation of the BC4J runtime libraries into the proper locations of your standalone OC4J installation.

For a full Oracle9iAS 9.0.3 installation, BC4J is pre-installed automatically. It's just the standalone OC4J minimum download zip file that leaves it out.


After installing the demo on the standalone OC4J container, you can run the demo by pointing your browser at the URL:

http://yourmachine:8888/BC4JToyStore/

Appendix 2: Known Issues

  • 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 BC4J 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 43. The access this preference page, select Tools | Preferences IDE menu option.

    Changing the IDE Preference for Embedded OC4J Host Name
    Figure 43: Changing the IDE Preference for Embedded OC4J Host Name
  • 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'))
      )
    )
  • If you are using Oracle9i JDeveloper Release 9.0.3 Production (Build 1035) with BC4J patch number 2705796 from Metalink applied, but not the 9.0.3.1 Production maintenace release, you will receive a NullPointerException if you add an item to your shopping cart, remove it from the cart, then add the same item back again. This is due to Bug 2740780 (NullPointerException Re-Adding Removed Row From Rowset With Transient Attrs) which is fixed in 9.0.3.1 and 9.0.4 releases.

Appendix 3: Installing on Apache Tomcat 4.1.24

Several customers have asked how to install the BC4J Toy Store Demo under Apache Tomcat 4.1.24. Here are the steps:

  1. Create the BC4JToyStore.war web archive file

    After opening the BC4JToyStore.jws workspace in JDeveloper 9.0.3, expand the Deployment.jpr project, and select the BC4JToyStore.deploy deployment profile. Select Deploy to WAR file from the right-mouse context menu.

  2. Deploy the BC4JToyStore.war file just produced to Tomcat by copying it from the ./bc4jtoystore/deploy directory, to the TOMCAT_HOME/webapps directory.
  3. Install the required supporting libraries for BC4J web applications.

    Copy the JAR files listed in Table 7 from JDEVHOME to TOMCAT_HOME/common/lib.

    Table 7: Supporting JAR files for BC4J-based Web Applications
    JAR File NameDescription
    ./bc4j/lib/bc4jmt.jarBC4J Core Middle Tier Libraries
    ./bc4j/lib/bc4jct.jarBC4J Client Libraries
    ./bc4j/lib/bc4j_jclient_common.jarBC4J Common Client Data Model Support
    ./bc4j/lib/bc4jhtml.jarBC4J Web Application Support
    ./bc4j/jlib/bc4jdatum.jarBC4J Base Domain Support
    ./bc4j/lib/bc4jdomorcl.jarBC4J Oracle Domains
    ./bc4j/lib/bc4jimdomains.jarBC4J Oracle Intermedia Domains
    ./ord/jlib/ordhttp.jarOracle Intermedia HTTP Upload Support
    ./ord/jlib/ordim.jarOracle Intermedia
    ./sqlj/lib/runtime12.jarSQLJ Runtime (Used by Intermedia)
    ./jlib/jdev-cm.jarJDeveloper Connection Manager
    ./jdev/lib/jdev-rt.jarJDeveloper Runtime Event Instrumentation
    ./jdbc/lib/classes12.jarOracle JDBC Driver
    ./jdbc/lib/nls_charset12.jarOracle JDBC Driver NLS Support
    ./lib/xmlparserv2.jarOracle XML Parser
    ./lib/oraclexsql.jarOracle XSQL Pages Templating Engine
    ./rdbms/jlib/xsu12.jarOracle XML SQL Utility (Used by XSQL)
  4. Configure the Tomcat-specific setup for the two JDBC datasources required

    The BC4J Toy Store demo's web.xml file contains the following two resource reference definitions for JDBC datsources 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 "/BC4JToyStore" web application context, edit the TOMCAT_HOME/conf/server.xml file, and find the place inside the appropriate <Host> element, where you can paste the additional BC4J 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 BC4J 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 BC4J Toy Store tables.

            <!--
             | BEGIN BC4J Toy Store Tomcat DataSource Config Section
             +-->
            <Context path="/technology/BC4JToyStore" docBase="BC4JToyStore">

              <Logger className="org.apache.catalina.logger.FileLogger"
                      prefix="localhost_BC4JToyStore." 
                      suffix=".log"
                      timestamp="true"/>
                <!--
                 | Using the oracle.jdbc.pool.OracleDataSourceFactory factory class
                 | the valid values for the 'type' attribute below are:
                 | 
                 |    1. oracle.jdbc.pool.OracleDataSource
                 |    2. oracle.jdbc.pool.OracleConnectionPoolDataSource 
                 |    3. oracle.jdbc.xa.client.OracleXADataSource
                 |    4. oracle.jdbc.pool.OracleConnectionCacheImpl
                 |    5. oracle.jdbc.pool.OracleOCIConnectionPool
                 |
                 | If you specify either "oracle.jdbc.pool.OracleConnectionCacheImpl"
                 | or "oracle.jdbc.pool.OracleOCIConnectionPool" you can supply
                 | additional children <parameter> entries of the corresponding 
                 | <ResourceParams> element below to set the values of a number
                 | of additional configuration parameters.
                 | 
                 | For "oracle.jdbc.pool.OracleConnectionCacheImpl", additional  
                 | optional parameters are:
                 |        
                 |        minLimit
                 |        maxLimit
                 |        cacheScheme
                 |
                 | For "oracle.jdbc.pool.OracleOCIConnectionPool", additional
                 | optional parameters are:
                 |
                 |        connpool_min_limit
                 |        connpool_max_limit
                 |        connpool_increment
                 |        connpool_active_size
                 |        connpool_pool_size
                 |        connpool_timeout
                 |        connpool_nowait
                 |        transactions_distributed
                 | 
                 | Consult the Oracle JDBC documentation for more info on what
                 | the the values of these parameters mean and how they can be
                 | used to tune the connection pool implementation.
                 |
                 +-->

                <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>
                  <!--
                   | You might need to change the "@machine:port:SID" part of the JDBC URL below
                   | to match your target database. The value below is for a default Oracle 
                   | database running on the same machine as Tomcat.
                   +-->
                  <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>
                  <!--
                   | You might need to change the "@machine:port:SID" part of the JDBC URL below
                   | to match your target database. The value below is for a default Oracle 
                   | database running on the same machine as Tomcat.
                   +-->
                  <parameter>
                    <name>url</name>
                    <value>jdbc:oracle:thin:toystore_statemgmt/toystore@127.0.0.1:1521:ORCL</value>
                  </parameter>
                </ResourceParams>
           </Context>
            <!--
             | END BC4J Toy Store Tomcat DataSource Config Section
             +-->
  5. Test the JDBC Datasource Configuration

    The BC4J Toy Store Demo comes with a testing servlet in the ToyStoreController.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/BC4JToyStore/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/BC4JToyStore

to run the BC4J Toy Store demo on Tomcat.

References

  1. Expert One-on-One: J2EE Design and Development (Johnson) from Wrox
  2. EJB Design Patterns: Advanced Patterns, Processes, and Idioms (Marinescu) from John Wiley & Sons
  3. Core J2EE Patterns: Best Practices and Design Strategies (Alur, Crupi, Malks) from Prentice Hall
E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy