8 Testing View Instance Queries

This chapter describes how to interactively test ADF view objects query results using the Oracle ADF Model Tester provided in JDeveloper. This chapter also explains how to use the ADF Business Components API to access view object instances in a test client outside of JDeveloper.

This chapter includes the following sections:

8.1 About View Instance Queries

JDeveloper includes an interactive application module testing tool that you can use to test all aspects of its data model without having to use your application user interface or write a test client program. Running the Oracle ADF Model Tester can often be the quickest way of exercising the data functionality of your business service during development.

Note:

When you want to test an application module programmatically, you can write a test client. For more information, see How to Create a Command-Line Java Test Client.

8.1.1 View Instance Use Cases and Examples

Using the Oracle ADF Model Tester, you can simulate an end user interacting with your application module data model before you have started to build any custom user interface of your own. Even after you have your UI pages constructed, you will come to appreciate using the Oracle ADF Model Tester to assist in diagnosing problems when they arise. You can reproduce the issues in the Oracle ADF Model Tester to discover if the issue lies in the view or controller layers of the application, or is instead a problem in the business service layer application module itself.

8.1.2 Additional Functionality for Testing View Instances

You may find it helpful to understand other Oracle ADF features before you start working with view instances. Following are links to other functionality that may be of interest.

8.2 Creating an Application Module to Test View Instances

Before you can test view objects that you create in your data model project, you must create an application module where you will define instances of the view objects you want to test. The application module is the transactional component that the Oracle ADF Model Tester (or UI client) will use to work with application data. The set of view objects used by an application module defines its data model, in other words, the set of data that a client can display and manipulate through a user interface.

To test the view objects you added to an application module, use the Oracle ADF Model Tester, which is accessible from the Applications window. For details about using the Oracle ADF Model Tester, see Testing View Object Instances Using the Oracle ADF Model Tester.

8.2.1 How to Create the Application Module with Individual View Object Instances

To create an application module that will define instances of individual view objects, use the Create Application Module wizard, which is available in the New Gallery.

Before you begin:

It may be helpful to have an understanding of application modules. For more information, see Creating an Application Module to Test View Instances.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

You will need to complete this task:

To create an application module to test individual view object instances:

  1. In the Applications window, right-click the project in which you want to create the application module and choose New and then Application Module.
  2. In the Create Application Module wizard, in the Name page, provide a package name and an application module name. Click Next.
  3. On the Data Model page, include instances of the view objects you have previously defined and edit the view object instance names to be exactly what you want clients to see. Then click Finish.

    Instead of accepting the default instance name shown in the Data Model page, you can change the instance name to something more meaningful (for example, instead of the default name OrderItems1 you can rename it to AllOrderItems).

8.2.2 How to Create the Application Module with Master-Detail View Object Instances

You can also use the Create Application Module wizard to create a hierarchy of view objects for an application module, based on a master-detail relationship that the view objects represent.

Before you begin:

It may be helpful to have an understanding of application modules. For more information, see Creating an Application Module to Test View Instances.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

You will need to complete this task:

To create an application module based on view object relationships:

  1. In the Applications window, right-click the project in which you want to create the application module and choose New.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then Application Module, and click OK.
  3. In the Create Application Module wizard, select the Data Model node.
  4. In the Available View Objects list, select the instance of the view object that you want to be the actively coordinating master.

    The master view object will appear with a plus sign in the list indicating the available view links for this view object. The view link must exist to define a master-detail hierarchy.

    For example, Figure 8-1 shows PersonsVO selected and renamed AuthenticatedUser in the New View Instance field.

    Figure 8-1 Master View Object Selected

    This image is described in the surrounding text
  5. Shuttle the selected master view object to the Data Model list

    For example, Figure 8-2 shows the newly created master view instance AuthenticatedUser in the Data Model list after you add it to the list.

    Figure 8-2 Master View Instance Created

    This image is described in the surrounding text
  6. In the Data Model list, leave the newly created master view instance selected, so that it appears highlighted. This will be the target of the detail view instance you will add. Then locate and select the detail view object beneath the master view object in the Available View Objects list.

    For example, Figure 8-3 shows the detail OrdersVO indented beneath master PersonsVO with the name OrdersVO via PersonsToOrders. The name identifies the view link PersonsToOrders, which defines the master-detail hierarchy between PersonsVO and OrdersVO. The detail view instance is renamed to MyOrders.

    Figure 8-3 Detail View Object Selected

    This image is described in the surrounding text
  7. To add the detail instance to the previously added master instance, shuttle the detail view object to the Data Model list below the selected master view instance.

    Figure 8-4 shows the newly created detail view instance MyOrders is a detail of the AuthenticatedUser in the data model.

    Figure 8-4 Master View Instance Created

    This image is described in the surrounding text
  8. To add another level of hierarchy, select the newly added detail in the Data Model list, then shuttle over the new detail which itself has a master-detail relationship with the previously added detail instance.

    Your data model can contain as many levels of hierarchy as your view object relationships support. For example, Figure 8-5 shows the Data Model list with instance AuthenticatedUser (renamed for PersonsVO) as the master of MyOrders (renamed for OrdersVO via PersonsToOrders), which in turn is a master for MyOrderItems (renamed from OrderItemsVO via OrdersToOrderItems). The detail view object MyOrderItems is the last level of the hierarchy possible because this view object is itself not a master for another view object.

    Figure 8-5 Master-Detail-Detail Hierarchy Created

    This image is described in the surrounding text

8.3 Testing View Object Instances Using the Oracle ADF Model Tester

Using the Oracle ADF Model Tester, you can simulate an end user interacting with your application module data model before you have started to build any custom user interface of your own. Even after you have your UI pages constructed, you will come to appreciate using the Oracle ADF Model Tester to assist in diagnosing problems when they arise. You can reproduce the issues in the Oracle ADF Model Tester to discover whether the problem lies in the view or controller layers of the application, or whether there is instead a problem in the business service layer application module itself.

8.3.1 How to Run the Oracle ADF Model Tester

To test the view objects you added to an application module, use the Oracle ADF Model Tester, which is accessible from the Applications window. When you run the tester from the Applications window, the default application module configuration is used to connect to the database.

Before you begin:

It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.

You may also find it helpful to understand how to use debugging tools when you run the tester. For more information, see Using the Oracle ADF Model Tester for Testing and Debugging.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

To test view objects using an application module configuration:

  1. In the Applications window, right-click the application module that you want to run and choose Run.

    Alternatively, choose Debug when you want to run the application in the Oracle ADF Model Tester with debugging enabled. JDeveloper opens the debugger process panel in the Log window and the various debugger windows. For example, when debugging using the Oracle ADF Model Tester, you can view status message and exceptions, step in and out of source code, and manage breakpoints.

    For information about receiving diagnostic messages specific to ADF Business Component debugging, see How to Enable ADF Business Components Debug Diagnostics.

  2. In the Oracle ADF Model Tester, to execute a view object, expand the data model tree and double-click the desired view object node.

    Note that the view object instance may already appear executed in the testing session. In this case, the Oracle ADF Model Tester data view page on the right already displays query results for the view object instance. The fields in the Oracle ADF Model Tester data view page of a read-only view object will always appear disabled since the data it represents is not editable. For example, in Figure 8-6, data for the view instance Products appears in the tester. Fields like Product Id, Language, and Category appear disabled because the attributes themselves are not editable.

    Figure 8-6 Testing the Data Model in the Oracle ADF Model Tester

    This image is described in the surrounding text
  3. Right-click a node in the data model tree to display the context menu for that node. For example, on a view object node you can reexecute the query if needed, to remove the view object from the data model tree, and perform other tasks.
  4. Right-click the tab of an open data viewer to display the context menu for that tab, as shown in Figure 8-7. For example, you can close the data viewer or open it in a separate window.

    Figure 8-7 Context Menu for Data Viewer Tabs in the Oracle ADF Model Tester

    This image is described in the surrounding text

8.3.2 How to Run the Oracle ADF Model Tester Using Configurations

To test the view objects you added to an application module, use the Oracle ADF Model Tester, and run it using a specific configuration. The configuration you select will determine the database connection used by the tester.

Before you begin:

It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.

You may also find it helpful to understand how to use debugging tools when you run the tester. For more information, see Using the Oracle ADF Model Tester for Testing and Debugging.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

To test view objects using a specific application module configuration:

  1. In the Applications window, double-click the application module that you want to run.
  2. In the overview editor, click the Configurations navigation tab.
  3. In the Configurations page, select the desired application module configuration from the Default Configuration dropdown list that you want to use to run the Oracle ADF Model Tester.

    By default, an application module has only its default configurations, named AppModuleNameLocal and AppModuleNameShared. For example, Figure 8-8 shows the BackOfficeAppModuleShared configuration is selected to connect to the database.

    Figure 8-8 Configuration Selection in Application Module Overview Editor

    This image is described in the surrounding text
  4. Right-click the selected configuration and choose Run.

    Alternatively, choose Debug when you want to run the application in the Oracle ADF Model Tester with debugging enabled. JDeveloper opens the debugger process panel in the Log window and the various debugger windows. For example, when debugging using the Oracle ADF Model Tester, you can view status message and exceptions, step in and out of source code, and manage breakpoints.

    For information about receiving diagnostic messages specific to ADF Business Component debugging, see How to Enable ADF Business Components Debug Diagnostics.

8.3.3 How to Test Language Message Bundles and UI Hints

When your application defines alternative languages in your resource message bundles, you can configure the Oracle ADF Model Tester to recognize these languages. In the Oracle ADF Model Tester, you can then display the Locale menu and select among the available language choices.

To specify a default language for the Oracle ADF Model Tester:

  1. In the main menu, choose Tools and then Preferences.
  2. In the Preferences dialog, expand ADF Business Components and select Tester.
  3. In the Oracle ADF Model Tester page, add any locale for which you have created a resource message bundle to the Selected list.

Alternatively, you can configure the default language choice by setting ADF Business Components runtime configuration properties for a specific application module configuration. These runtime properties also determine which language the Oracle ADF Model Tester will display as the default. To set runtime configuration properties, double-click the application module in the Applications window and, in the overview editor, select the Configurations navigation tab. Then, in the Configurations page of the overview editor, click the configuration hyperlink. In the application module configuration overview editor (on the bc4j.xcfg file), select the Properties tab and click Add Property to select the following properties from the Add Property dialog and click OK.

  • jbo.default.country

  • jbo.default.language

Then, in the Properties list enter the desired country code for the country and language. For example, to specify the Italian language, you would enter IT and it for these two properties:

  • jbo.default.country = IT

  • jbo.default.language = it

Testing the language message bundles in the Oracle ADF Model Tester lets you verify that the translations of the UI hints are correctly located. Or, if the message bundle defines date formats for specific attributes, the tool lets you verify that date formats change (like 04/12/2013 to 12/04/2013).

8.3.4 How to Test Entity-Based View Objects Interactively

You test entity-based view objects interactively in the same way as read-only ones. Just add instances of the desired view objects to the data model of some application module, and then test that application module using the Oracle ADF Model Tester.

You'll find the Oracle ADF Model Tester invaluable for quickly testing and debugging your application modules. Table 8-1 gives an overview of the operations that the Oracle ADF Model Tester toolbar buttons perform when you display an entity-based view object.


Table 8-1 Oracle ADF Model Tester Toolbar Buttons

Button Operation Usage
This image is described in the surrounding text

Move to ... row

Changes the current row displayed by the Oracle ADF Model Tester. Moves to the first, previous, next, or last row.

This image is described in the surrounding text

Insert a new row

Creates and inserts a new row.

This image is described in the surrounding text

Delete the current row

Deletes the current row.

This image is described in the surrounding text

Save changes to the database

Posts and commits changes that you made in the ADF Business Components cache.

This image is described in the surrounding text

Discard all changes since last save

Discards changes that you made in the ADF Business Components cache and restores the original values, rolling back any changes posted to the database.

This image is described in the surrounding text

Specify view criteria

Displays the View Criteria dialog that you can use to create and apply view criteria to the master view object instance.

This image is described in the surrounding text

Validate row

Validates the current row by applying validation rules defined for all entity object instances. Disabled unless at least one field is editable.

This image is described in the surrounding text

Edit bind variables

Displays the Bind Variable dialog that you can use to enter values for bind parameters used in the view object query. Disabled unless the view object query uses bind parameters in the query statement.


To test the entity-based view objects you added to an application module, use the Oracle ADF Model Tester, which is accessible from the Applications window.

Before you begin:

It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

To test entity-based view objects using an application module configuration:

  1. In the Applications window, right-click the application module that you want to test and choose Run.
  2. In the Oracle ADF Model Tester, expand the data model tree and double-click the desired entity-based view object node.

    Unlike the fields of a read-only view object, the fields displayed in the data view page will appear enabled, because the data it represents is editable.

  3. To update individual values and perform validation checks on the entered data, use the editable fields.

    In the case of a view instance with referenced entities, you can change the foreign key value and observe that the referenced part changes.

  4. To perform row-level operations, such as navigate rows, create row, remove row, and validate the current row, use the toolbar buttons.

    For further discussion about simulating end-user interaction in the data view page, see How to Simulate End-User Interaction in the Oracle ADF Model Tester.

8.3.5 How to Update the Oracle ADF Model Tester to Display Project Changes

Normally, changes that you make to the data model project will not be picked up automatically by running the Oracle ADF Model Tester. You can, however, force the Oracle ADF Model Tester to reload metadata from the data model project any time you want to synchronize the displayed data model and the data model project. This option is an alternative to quitting the Oracle ADF Model Tester, editing your project, and rerunning the Oracle ADF Model Tester to view the latest changes.

Using the Reload Application option saves time, especially as you work iteratively between the Oracle ADF Model Tester and JDeveloper. For example, while running the Oracle ADF Model Tester you might determine the need to modify the data model with a new view instance or you might find that a view instance is missing an LOV attribute definition. You can return to JDeveloper and use the Business Components overview editors to make the changes that alter the data model metadata. Then, after you recompile the project (a necessary step), you can return to the Oracle ADF Model Tester to reload the updated metadata from the project's class path.

Before you begin:

It may be helpful to have an understanding of the Oracle ADF Model Tester. For more information, see Testing View Object Instances Using the Oracle ADF Model Tester.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

To reload the data model metadata in the running Oracle ADF Model Tester:

  1. In the Applications window, right-click the application module and choose Run.
  2. In the Oracle ADF Model Tester, test the data model and determine any changes you want to make. Do not exit the Oracle ADF Model Tester.
  3. In JDeveloper, make the desired changes and recompile the data model project. (For example, you can right-click the data model project in the Applications window and choose Make to complete the recompile step.)

    Although the metadata changes that you make are not involved in compiling the project, the compile step is necessary to copy the metadata to the class path and to allow the Oracle ADF Model Tester to reload it.

  4. Return to the Oracle ADF Model Tester and click the Reload the Application Metadata button above the data model tree. The Oracle ADF Model Tester closes all open windows.

    Alternatively, you can choose Reload Application from the File menu of the Oracle ADF Model Tester.

  5. Reopen the desired windows and view your changes.

8.3.6 What Happens When You Use the Oracle ADF Model Tester

When you launch the Oracle ADF Model Tester, JDeveloper starts the tool in a separate process and the Oracle ADF Model Tester appears. The tree at the left of the dialog displays all of the view object instances in your application module's data model. After you double-click the desired view object instance, the Oracle ADF Model Tester will display a data view page to inspect the query results. For example, Figure 8-9 shows the view instance Products that has been double-clicked in the expanded tree to display the data for this view instance in the data view page on the right.

The data view page will appear disabled for any read-only view objects you display because the data is not editable. But even for a read-only view object, the tool affords some useful features:

  • You can validate that the UI hints based on the Label Text hint and format masks are defined correctly.

  • You can also scroll through the data using the toolbar buttons.

  • You can enter Query-by-Example criteria to find a particular row whose data you want to inspect. By clicking the Specify View Criteria button in the toolbar, the View Criteria dialog displays the list of available Query-by-Example criteria.

    For example, as shown in Figure 8-9, you can select a view criteria like FindByProductNameCriteria and enter a query criteria like "P%" for a ProductName attribute and click Find to narrow the search to only those products with a name that begins with the letter P.

The Oracle ADF Model Tester becomes even more useful when you create entity-based view objects that allow you to simulate inserting, updating, and deleting rows, as described in How to Test Entity-Based View Objects Interactively.

Figure 8-9 Built-in Query-by-Example Functionality

This image is described in the surrounding text

8.3.7 How to Simulate End-User Interaction in the Oracle ADF Model Tester

When you launch the Oracle ADF Model Tester, the tree at the left of the display shows the hierarchy of the view object instances that the data model of your application module defines. If the data model defines master-detail view instance relationships, the tree will display them as parent and child nodes. A node between the master-detail view instances represent the view link instance that performs the active master-detail coordination as the current row changes in the master. For example, in Figure 8-10 the tree is expanded to show the master-detail relationship between the master Products view instance and the detail WarehouseStockLevels view instance. The selected node, ProductsToWarehouseStockLevels1, is the view link instance that defines the master-detail relationship.

Figure 8-10 Application Module Data Model in the Oracle ADF Model Tester

This image is described in the surrounding text

Double-clicking the view link instance executes the master object and displays the master-detail data in the data view page. For example, in Figure 8-11, double-clicking the ProductsToWarehouseStockLevels1 view link instance in the tree executes the Products master view instance in the top portion of the data view page and the WarehouseStockLevels view instance in the bottom portion of the data view page. Additional context menu items on the view object node allow you to reexecute the query if needed, remove the view object from the data model panel, and perform other tasks.

In the master-detail data view page, you can scroll through the query results. Additionally, because instance of entity-based view objects are fully editable, Instead of displaying disabled UI controls showing read-only data for a read-only view object, the data view page displays editable fields. You are free to experiment with creating, inserting, updating, validating, committing, and rolling back.

Figure 8-11 Master-Detail Data View Page in the Oracle ADF Model Tester

This image is described in the surrounding text

For example, you can view multiple levels of master-detail hierarchies, opening multiple data view pages at the same time. Use the Detach context menu item to open any tab into a separate window and visualize multiple view object's data at the same time.

Using just the master-detail data view page, you can test several functional areas of your application.

8.3.7.1 Testing Master-Detail Coordination

When you click the navigation buttons on the Oracle ADF Model Tester toolbar, you can see that the rows for the current master view object are correctly coordinated. For example, Figure 8-11 shows a master-detail hierarchy with products and warehouses. If you click the Next Row button in the master panel, the master panel will display the next product (identified by a product ID) and the detail panel will update to display the list of warehouses and quantities available for the product.

8.3.7.2 Testing UI Hints

The entity-based view object attributes inherit their UI hints from those on the underlying entity object attribute. The prompts displayed in the data view page help you see whether you have correctly defined a user-friendly label text UI hint for each attribute. For details on setting up the hint on your entity object, see Defining UI Hints for View Objects.

8.3.7.3 Testing Business Domain Layer Validation

Depending on the validation rules you have defined, you can try entering invalid values to trigger and verify validation exceptions. For example, when you have defined a range validation rule, enter a value outside the range and see an error similar to:

(oracle.jbo.AttrSetValException) Valid product codes are between 100 and 999

Click the rollback button in the toolbar to revert data to the previous state.

8.3.7.4 Testing View Objects That Reference Entity Usages

By scrolling through the data — or using the Specify View Criteria button in the Oracle ADF Model Tester toolbar to search — you can verify whether you have correctly altered the WHERE clause in an entity-based view object's query to use an outer join. The rows should appear as expected.

You also can try changing a primary key attribute of a master view object. This will allow you to verify that the corresponding reference information is automatically updated to reflect the new primary key value.

Use the Oracle ADF Model Tester to verify that UI hints defined at the view object level override the ones it would normally inherit from the underlying entity object. If you notice that several attributes share the same label text, you can edit the UI hint for the desired attributes at the view object level. For example, you can set the Label Text hint to Member Since for the RegisteredDate attribute and Provisioned? for the ProvisionedFlag attribute.

8.3.7.5 Testing Row Creation and Default Value Generation

When displaying an entity-based view object, click the Create Row button in the Oracle ADF Model Tester toolbar for the view object instance to create a new blank row. Any fields that have a declarative default value will appear with that value in the blank row. If the DBSequence-valued attribute is used, a temporary value will appear in the new row. After entering all the required fields, click the Commit button to commit the transaction. The actual, trigger-assigned primary key should appear in the field after successful commit.

8.3.7.6 Testing That New Detail Rows Have Correct Foreign Keys

If you click Create Row in the Oracle ADF Model Tester toolbar to try adding a new row to an existing detail entity-based view object instance, you'll notice that the view link automatically ensures that the foreign key attribute value in the new row is set to the value of the current master view instance row.

8.3.8 How to Test Multiuser Scenarios in the Oracle ADF Model Tester

When view objects and entity objects cooperate at runtime, two exceptions can occur when you run the application in a multiuser environment. To anticipate these exceptions, you can simulate a multiuser environment for testing purposes using the Oracle ADF Model Tester. For example, when the application displays edit forms for view object queries, what is the expected behavior when two users attempt to modify the same attribute in their forms?

To understand the expected behavior, open two instances of the Oracle ADF Model Tester on the application module to simulate two users editing the same view object attribute. Keep both instances open and perform the following two tests to demonstrate how multiuser exceptions can arise:

  • In one instance of the Oracle ADF Model Tester, modify an attribute of an existing view object and tab out of the field. Then, in the other tester instance, try to modify the same view object attribute in some way. You'll see that the second user gets the oracle.jbo.AlreadyLockedException.

    You can then change the value of jbo.locking.mode to be pessimistic on the Properties page of the Oracle ADF Model Tester Connect dialog and try repeating the test (the default mode is set to optimistic). You'll see the error occurs for the second user immediately after changing the value instead of after committing the change.

  • In one instance of the Oracle ADF Model Tester, modify an attribute of an existing view object and tab out of the field. Then, in the other tester instance, retrieve (but don't modify) the same view object attribute. Back in the first window, commit the change. If the second user then tries to modify that same attribute, you'll see that the second user gets the oracle.jbo.RowInconsistentException. The row has been modified and committed by another user since the second user retrieved the row into the entity cache.

8.3.9 How to Customize Configuration Options Before Running the Tester

Using the overview editor for the application module, you can select a predefined configuration to run the tool using that named set of runtime configuration properties. The Configurations page of the overview editor also lets you open the overview editor for application module configurations (defined in the bc4j.xcfg file) to specify properties of the configuration before running the tester.

To display the application module configuration overview editor, double-click the application module in the Applications window and, in the overview editor, select the Configurations navigation tab. Then, in the Configurations page of the overview editor, click the configuration hyperlink. In the application module configuration overview editor, select the Properties tab and click Add Property to select the desired property from the Add Property dialog and click OK.

For example, you could alter the default language for the UI hints by editing the configuration and choosing the Properties tab to add and then set the following two properties with the desired country code (in this case, IT for Italy):

  • jbo.default.country = IT

  • jbo.default.language = it

8.3.10 How to Enable ADF Business Components Debug Diagnostics

When launching the Oracle ADF Model Tester, if you have configured diagnostic logging for the oracle.jbo logger, JDeveloper will direct ADF Business Components debug diagnostics messages to the JDeveloper Log window. Figure 8-12 shows the oracle.jbo logger set to a log level of FINEST to enable ADF Business Components debug diagnostics.

Figure 8-12 Configuring a Logger for ADF Business Components Debugging

This image is described in the surrounding text

The oracle.jbo logger can be configured either before running the application or while the application is running in Integrated WebLogic Server. The logging will begin without the need to restart the server. You do not need to run the application in debug mode to log diagnostic messages.

With the oracle.jbo logger configured, the next time you run the Oracle ADF Model Tester and double-click the view object, you'll see detailed diagnostic output in the Log window, as shown in the following example. Configuring the oracle.jbo logger with a log level FINEST will allow you to visualize everything the ADF Business Components framework components are doing for your application.

  :
[355] Oracle SQLBuilder: Registered driver: oracle.jdbc.OracleDriver
[356] Creating a new pool resource
[357] **** DBTransactionImpl establishNewConnection
[358] Successfully logged in
[359] JDBCDriverVersion: xx.xx.x.x-Production
[360] DatabaseProductName: Oracle
[361] DBTransactionImpl initTransaction
[362] Replacing: null with: StoreServiceAM_AddressesPageDef
[363] Replacing: null with: StoreServiceAM_MostPopularProductsByCategoriesPageDef
...
[537] Orders ViewRowSetImpl.execute caused params to be "un"changed
[538] Column count: 41
[539] ViewObject: Orders Created new QUERY statement
[540] Orders>#q computed SQLStmtBufLen: 952, actual=865, storing=895
[541] SELECT OrderEO.ORDER_ID, OrderEO.ORDER_DATE, OrderEO.ORDER_SHIPPED_DATE,
       FROM ORDERS OrderEO ORDER BY OrderEO.ORDER_DATE desc
[542] Bind params for ViewObject: Orders

For backward compatibility, it remains possible to enable ADF Business Components debug diagnostics on the data model project using the Java system property jbo.debugoutput=console. To set the property, open the Run/Debug/Profile page in the Project Properties dialog for your data model project. Click Edit to edit the chosen run configuration, and add -Djbo.debugoutput=console to the Java Options field in the page. Other legal values for this property are silent (the default, if not specified) and file. If you choose the file option, diagnostics are written to the system temp directory.

Other legal values for the -Djbo.debugoutput system property are silent (the default, if not specified) and file. If you enter the file option, diagnostics are written to the system temp directory.

8.3.11 What Happens at Runtime: How View Objects and Entity Objects Cooperate

On their own, view objects and entity objects simplify two important jobs that every enterprise application developer needs to do:

  • Work with SQL query results

  • Modify and validate rows in database tables

Entity-based view objects can query any selection of data that you want the end user to be able to view and modify. Any data the end user is allowed to change will be validated and saved by your reusable business domain layer. The key ingredients you provide as the developer are the ones that only you can know:

  • You decide what business logic should be enforced in your business domain layer

  • You decide what queries describe the data you need to put on the screen

These are the things that make your application unique. The built-in functionality of your entity-based view objects handles the rest of the implementation details.

Note:

Understanding row keys and what role the entity cache plays in the transaction are important concepts that help to clarify the nature of the entity-based view objects. These two concepts are addressed in ViewObject Interface Methods for Working with the View Object's Default RowSet.

8.3.11.1 What Happens at Runtime: After a View Object Executes Its Query

After adding an instance of an entity-based view object to the application module's data model, you can see what happens at runtime when you execute the query. Like a read-only view object, an entity-based view object sends its SQL query straight to the database using the standard Java Database Connectivity (JDBC) API, and the database produces a result set. In contrast to its read-only counterpart, however, as the entity-based view object retrieves each row of the database result set, it partitions the row attributes based on which entity usage they relate to. This partitioning occurs by creating an entity object row of the appropriate type for each of the view object's entity usages, populating them with the relevant attributes retrieved by the query, and storing each of these entity rows in its respective entity cache. Then, rather than storing duplicate copies of the data, the view row simply points at the entity row parts that comprise it.

Figure 8-13 illustrates how the entity cache partitions the result set attributes of two entity-based view objects. In this example, the highlighted row in the database result set is partitioned into an Order entity row with primary key 112 and a CustomerInfo entity row with primary key 301.

As described in The Role of the Entity Cache in the Transaction, the entity row that is brought into the cache using findByPrimaryKey() contains all attributes of the entity object. In contrast, an entity row created by partitioning rows from the entity-based view object's query result contains values only for attributes that appear in the query. It does not include the complete set of attributes. This partially populated entity row represents an important runtime performance optimization.

Since the ratio of rows retrieved to rows modified in a typical enterprise application is very high, you can save memory by bringing only the attributes into memory that you need to display instead of bringing all attributes into memory all the time.

Figure 8-13 Entity Cache Partitions View Rows into Entity Rows

This image is described in the surrounding text

By partitioning queried data this way into its underlying entity row constituent parts, the first benefit you gain is that all of the rows that include some data queried will display a consistent result when changes are made in the current transaction. In other words, if one view object allows the PaymentType attribute of customer 301 to be modified, then all rows in any entity-based view object showing the PaymentType attribute for customer 301 will update instantly to reflect the change. Since the data related to customer 301 is stored exactly once in the CustomerInfo entity cache in the entity row with primary key 301, any view row that has queried the order's PaymentType attribute is just pointing at this single entity row.

Luckily, these implementation details are completely hidden from a client working with the rows in a view object's row set. The client works with a view row, getting and setting the attributes, and is unaware of how those attributes might be related to entity rows behind the scenes.

8.3.11.2 What Happens at Runtime: After a View Row Attribute Is Modified

When a user attempts to update the attribute of a view row, a series of steps occur to automatically coordinate this view row attribute modification with the underlying entity row. These steps ensure that a validation rule defined on the entity-mapped attribute will be triggered before the value is changed.

Figure 8-14 illustrates the basic steps that occur at runtime when the user attempts to update an entity-mapped attribute. In this example, the modified attribute Status is mapped to an entity usage where a validation rule is defined.

  1. The user attempts to set the Status attribute to the value Ship.

  2. Since Status is an entity-mapped attribute from the Order entity usage, the view row delegates the attribute set to the appropriate underlying entity row in the Order entity cache having primary key 112.

  3. Any attribute-level validation rules on the Status attribute of the Order entity object are evaluated and the modification attempt will fail if any rule does not succeed.

    Assume that some validation rule for the Status attribute programmatically references the ShipDate attribute (for example, to enforce a business rule that an Order cannot be shipped the same day it is placed). The ShipDate was not one of the Order attributes retrieved by the query, so it is not present in the partially populated entity row in the Order entity cache.

  4. To ensure that business rules can always reference all attributes of the entity object, the entity object detects this situation and "faults-in" the entire set of Order entity object attributes for the entity row being modified using the primary key (which must be present for each entity usage that participates in the view object).

  5. After the attribute-level validations all succeed, the entity object attempts to acquire a lock on the row in the ORDERS table before allowing the first attribute to be modified.

  6. If the row can be locked, the attempt to set the Status attribute in the row succeeds and the value is changed in the entity row.

Note:

The jbo.locking.mode configuration property controls how rows are locked. The default value is optimistic. Typically, Fusion web applications will use the default setting optimistic, so that rows aren't locked until transaction commit time. In pessimistic locking mode, the row must be lockable before any change is allowed to it in the entity cache.

Figure 8-14 View Row Attribute Updates Delegate to the Entity

This image is described in the surrounding text

8.3.11.3 What Happens at Runtime: After a Foreign Key Attribute is Changed

When a user attempts to update a foreign key attribute, a series of steps occur to automatically coordinate this view row attribute modification with the underlying entity row. These steps ensure that a validation rule defined on the foreign key, entity-mapped attribute will be triggered before the value is changed. They also ensure that the view row for the changed foreign key attribute reflects the correct attributes of all referenced entity objects.

Figure 8-15 illustrates the basic steps that occur at runtime when the user attempts to update a foreign key, entity-mapped attribute. In this example, the modified attribute CustomerInfoId is mapped to an entity usage Order where the attribute is associated with another entity object CustomerInfo.

  1. The user attempts to set the CustomerInfoId attribute to the value 300.

  2. Since CustomerInfoId is an entity-mapped attribute from the Order entity usage, the view row delegates the attribute set to the appropriate underlying entity row in the Order entity cache, which has primary key 112.

  3. Any attribute-level validation rules on the CustomerInfoId attribute of the Order entity object are evaluated and the modification attempt will fail if any rule does not succeed.

  4. The row is already locked, so the attempt to set the CustomerInfoId attribute in the row succeeds and the value is changed in the entity row.

  5. Since the CustomerInfoId attribute on the Order entity usage is associated with the CustomerInfo entity object, this change of foreign key value causes the view row to replace its current entity row part for customer 301 with the entity row corresponding to the new CustomerInfoId = 300. This effectively makes the view row for order 112 point to the entity row for 300, so the value of the PaymentType in the view row updates to reflect the correct reference information for this newly assigned customer.

Figure 8-15 After Updating a Foreign Key, View Row Points to a New Entity

This image is described in the surrounding text

8.3.11.4 What Happens at Runtime: After a Transaction is Committed

Suppose the user is satisfied with the changes, and commits the transaction. As shown in Figure 8-16, there are two basic steps:

  1. The Transaction object validates any invalid entity rows in its pending changes list.

  2. The entity rows in the pending changes list are saved to the database.

The figure depicts a loop in Step 1 before the act of validating one modified entity object might programmatically affect changes to other entity objects. Once the transaction has processed its list of invalid entities on the pending changes list, if the list has entities, the transaction will complete another pass. It will attempt up to ten passes through the list. If by that point there are still invalid entity rows, it will throw an exception because this typically means you have an error in your business logic that needs to be investigated.

Figure 8-16 Committing the Transaction Validates Invalid Entities, Then Saves Them

This image is described in the surrounding text

8.3.11.5 What Happens at Runtime: After a View Object Requeries Data

When you reexecute a view object's query, by default the view rows in its current row set are "forgotten" in preparation for reading in a fresh result set. This view object operation does not directly affect the entity cache, however. The view object then sends the SQL to the database and the process begins again to retrieve the database result set rows and partition them into entity row parts.

Note:

Typically when the view object requeries data, you expect it to retrieve the latest database information. If instead you want to avoid a database roundtrip by restricting your view object to querying only over existing entity rows in the cache, or over existing rows already in the view object's row set, see Performing In-Memory Sorting and Filtering of Row Sets.

8.3.11.5.1 How Unmodified Attributes are Handled During Requery

As part of the entity row partitioning process during a requery, if an attribute on the entity row is unmodified, then its value in the entity cache is updated to reflect the newly queried value.

8.3.11.5.2 How Modified Attributes are Handled During Requery

However, if the value of an entity row attribute has been modified in the current transaction, then during a requery the entity row partitioning process does not refresh its value. Uncommitted changes in the current transaction are left intact so the end-user's logical unit of work is preserved. As with any entity attribute value, these pending modifications continue to be consistently displayed in any entity-based view object rows that reference the modified entity rows.

Note:

End-user row inserts and deletes are also managed by the entity cache, which permits new rows to appear and deleted rows to be skipped during requerying. For more information about new row behavior, see Maintaining New Row Consistency Between View Objects Based on the Same Entity.

For example, Figure 8-17 illustrates the scenario where a user "drills down" to a different page that uses the Orders view object instance to retrieve all details about order 112 and that this happens in the context of the current transaction's pending changes. That view object has two entity usages: a primary Orders usage and a reference usage for CustomerInfo. When its query result is partitioned into entity rows, it ends up pointing at the same Order entity row that the previous OrderInfo view row had modified. This means the end user will correctly see the pending change, that the order is assigned to sking in this transaction.

Figure 8-17 Entity Cache Merges Sets of Entity Attributes from Different View Objects

This image is described in the surrounding text
8.3.11.5.3 How Overlapping Subsets of Attributes are Handled During Requery

Two different view objects can retrieve two different subsets of reference information and the results are merged whether or not they have matching sets of attributes. For example, Figure 8-17 also illustrates the situation, where the Orders view object queries the user's Email, while the OrderInfo view object queried the user's PaymentOption. The figure shows what happens at runtime: if while partitioning the retrieved row, the entity row part contains a different set of attributes than does the partially populated entity row that is already in the cache, the attributes get "merged". The result is a partially populated entity row in the cache with the union of the overlapping subsets of user attributes. In contrast, for jchen (user 302), who wasn't in the cache already, the resulting new entity row contains only the Email attribute, but not the PaymentOption.

8.3.12 What You May Need to Know About Optimizing View Object Runtime Performance

The view object provides tuning parameters that let you control how SQL is executed and how data is fetched from the database. These tuning parameters play a significant role in the runtime performance of the view object. If the fetch options are not tuned correctly for the application, then your view object may fetch an excessive amount of data and may make too many roundtrips to the database.

You can use the Tuning section of the General page of the overview editor to configure the fetch options shown in Table 8-2.


Table 8-2 Parameters to Tune View Object Performance

Fetch Tuning Parameters Usage

Fetch Mode

The default fetch option is the All Rows option, which will be retrieved As Needed (FetchMode="FETCH_AS_NEEDED") or All at Once (FetchMode="FETCH_ALL"), depending on which option is desired. The As Needed option ensures that an executeQuery() operation on the view object initially retrieves only as many rows as necessary to fill the first page of a display, whose number of rows is set based on the view object's range size.

Fetch Size

In conjunction with the Fetch Mode option, the in Batches of field controls the number of records fetched at one time from the database (FetchSize in the view object XML). The default value is 1, which will give poor performance unless only one row will be fetched. The suggested configuration is to set this value to n+3 where n is the number of rows to be displayed in the user interface. For more information about how this parameter may affect memory requirements, see Consider Whether Fetching One Row at a Time is Appropriate.

Max Fetch Size

The default max fetch size for a view object is -1, which means that there is no limit to the number of rows the view object can fetch. In cases where the result set should contain only n rows of data, the option Only up to row number should be selected and set to n. The developer can alternatively call setMaxFetchSize(n) to set this programmatically or manually add the parameter MaxFetchSize to the view object XML.

For view objects whose WHERE clause expects to retrieve a single row, set the option At Most One Row. This way the view object knows you don't expect any more rows and it will skip its normal test for that situation.

As mentioned earlier, setting a maximum fetch size of 0 (zero) makes the view object insert-only. In this case, no select query will be issued, so no rows will be fetched.

When you want to specify a global threshold for all view object queries in the application, you can configure the Row Fetch Limit property in the adf-config.xml file. Setting this property means you can avoid changing the Max Fetch Size for individual query operations. If you do specify a fetch limit for individual view objects, the Row Fetch Limit setting will be ignored in those cases. For more details about Row Fetch Limit, see Using Fetch Size to Limit the Maximum Number of Records Fetched for a View Object.

Forward-only Mode

If a data set will only be traversed going forward, then forward-only mode can help performance when iterating through the data set. This can be configured by programmatically calling setForwardOnly(true) on the view object. Setting forward-only will also prevent caching previous sets of rows as the data set is traversed.


When you tune view objects, you should also consider these issues:

  • Large data sets: View objects provide a mechanism to page through large data sets such that a user can jump to a specific page in the results. This is configured by calling setRangeSize(n) followed by setAccessMode(RowSet.RANGE_PAGING) on the view object where n is the number of rows contained within one page. When the user navigates to a specific page in the data set, the application can call scrollToRangePage(P) on the view object to navigate to page P. Range paging fetches and caches only the current page of rows in the view object row cache at the cost of another query execution to retrieve each page of data. Range paging is not appropriate where it is beneficial to have all fetched rows in the view object row cache (for example, when the application needs to read all rows in a dataset for an LOV or page back and forth in records of a small data set.

  • Spillover: There is a facility to use the data source as "virtual memory" when the JVM container runs out of memory. By default, this is disabled and can be turned on as a last resort by setting jbo.use.pers.coll=true. Enabling spillover can have a large performance impact.

  • SQL platform: If the generic SQL92 SQL platform is used to connect to generic SQL92-compliant databases, then some view object tuning options will not function correctly. The parameter that choosing the generic SQL92 SQL platform affects the most is the fetch size. When SQL92 SQL platform is used, the fetch size defaults to 10 rows regardless of what is configured for the view object. You can set the SQL platform when you define the database connection or you can define it as global project setting in the adf-config.xml file. By default, the SQL platform will be Oracle. To manually override the SQL platform, you can also pass the parameter -Djbo.SQLBuilder="SQL92" to the JVM upon startup.

Additionally, you have some options to tune the view objects' associated SQL for better database performance:

  • Bind variables: If the query associated with the view object contains values that may change from execution to execution, use bind variables. Using bind variables in the query allows the query to reexecute without needing to reparse the query on the database. You can add bind variables to the view object in the Query page of the overview editor for the view object. For more information, see Working with Bind Variables.

  • Query optimizer hints: The view object can pass hints to the database to influence which execution plan to use for the associated query. The optimizer hints can be specified in the Retrieve from the Database group box in the Tuning section of the overview editor for the view object. For information about optimizer hints, see Specify a Query Optimizer Hint if Necessary.

8.4 Testing View Object Instances Programmatically

When you are ready to test a working application module containing at least one view object instance, you can build a simple test client program to illustrate the basics of working programmatically with the data in the contained view object instances.

From the point of view of a client accessing your application module's data model, the API's to work with a read-only view object and an entity-based view object are identical. The key functional difference is that entity-based view objects allow the data in a view object to be fully updatable. The application module that contains the entity-based view objects defines the unit of work and manages the transaction. This section presents test client programs that work with the SummitADF_Examples workspace to illustrate:

  • Iterating master-detail-detail hierarchy

  • Finding a row and updating a foreign key value

  • Creating a new order

  • Retrieving the row key identifying a row

8.4.1 ViewObject Interface Methods for Working with the View Object's Default RowSet

The ViewObject interface in the oracle.jbo package provides the methods to easily perform any data-retrieval task. Some of these methods used in the example include:

  • executeQuery(), to execute the view object's query and populate its row set of results

  • setWhereClause(), to add a dynamic predicate at runtime to narrow a search

  • setNamedWhereClauseParam(), to set the value of a named bind variable

  • hasNext(), to test whether the row set iterator has reached the last row of results

  • next(), to advance the row set iterator to the next row in the row set

  • getEstimatedRowCount(), to count the number of rows a view object's query would return

Typically, when you work with a view object, you will work with only a single row set of results at a time. To simplify this overwhelmingly common use case, as shown in Figure 8-18, the view object contains a default RowSet, which, in turn, contains a default RowSetIterator. The default RowSetIterator allows you to call all of the data-retrieval methods directly on the ViewObject component itself, knowing that they will apply automatically to its default row set.

Figure 8-18 ViewObject Contains a Default RowSet and RowSetIterator

This image is described in the surrounding text

Note:

Defining Polymorphic View Objects presents situations when you might want a single view object to produce multiple distinct row sets of results. You can also find scenarios for creating multiple distinct row set iterators for a row set. Most of the time, however, you'll need only a single iterator.

The phrase "working with the rows in a view object," when used in this guide more precisely means working with the rows in the view object's default row set. Similarly, the phrase "iterate over the rows in a view object," more precisely means you will use the default row set iterator of the view object's default row set to loop over its rows.

8.4.1.1 The Role of the Key Object in a View Row or Entity Row

When you work with view rows you use the Row interface in the oracle.jbo package. As shown in Figure 8-19, the interface contains a method called getKey() that you can use to access the Key object that identifies any row. Notice that the Entity interface in the oracle.jbo.server package extends the Row interface. This relationship provides a concrete explanation of why the term entity row is so appropriate. Even though an entity row supports additional features for encapsulating business logic and handling database access, you can still treat any entity row as a Row.

An entity-based view object delegates the task of finding rows by key to its underlying entity row parts.

Recall that both view rows and entity rows support either single-attribute or multiattribute keys, so the Key object related to any given Row will encapsulate all of the attributes that comprise its key. Once you have a Key object, you can use the findByKey() method on any row set to find a row based on its Key object. When you use the findByKey() method to find a view row by key, the view row proceeds to use the entity definition's findByPrimaryKey() method to find each entity row contributing attributes to the view row key.

In the case of a read-only view object with no underlying entity row to which to delegate this task, the view object implementation automatically enables the manageRowsByKey flag when at least one primary key attribute is detected. This ensures that the findByKey() method is successful in the case of read-only view objects. If the manageRowsByKey flag is not enabled, then UI operations like setting the current row with the key, which depend on the findByKey() method, would not work.

Figure 8-19 Any View Row or Entity Row Supports Retrieving Its Identifying Key

This image is described in the surrounding text

Note:

When you define an entity-based view object, by default the primary key attributes for all of its entity usages are marked with their Key Attribute property set to true. In any nonupdatable reference entity usages, you should disable the Key Attribute property for the key attributes. Since view object attributes related to the primary keys of updatable entity usages must be part of the composite view row key, their Key Attribute property cannot be disabled.

8.4.1.2 The Role of the Entity Cache in the Transaction

An application module is a transactional container for a logical unit of work. At runtime, it acquires a database connection using information from the named configuration you supply, and it delegates transaction management to a companion Transaction object. Since a logical unit of work may involve finding and modifying multiple entity rows of different types, the Transaction object provides an entity cache as a "work area" to hold entity rows involved in the current user's transaction. Each entity cache contains rows of a single entity type, so a transaction involving two or more entity objects holds the working copies of those entity rows in separate caches.

By using an entity object's related entity definition, you can write code in an application module to find and modify existing entity rows. As shown in Figure 8-20, by calling findByPrimaryKey() on the entity definition for the Order entity object, you can retrieve the row with that key. If it is not already in the entity cache, the entity object executes a query to retrieve it from the database. This query selects all of the entity object's persistent attributes from its underlying table, and finds the row using an appropriate WHERE clause against the column corresponding to the entity object's primary key attribute. Subsequent attempts to find the same entity row by key during the same transaction will find it in the cache, preventing the need for a trip to the database. In a given entity cache, entity rows are indexed by their primary key. This makes finding an entity row in the cache a fast operation.

When you access related entity rows using association accessor methods, they are also retrieved from the entity cache. If related entity rows are not in the cache, then they are retrieved from the database. Finally, the entity cache is also the place where new entity rows wait to be saved. In other words, when you use the createInstance2() method on the entity definition to create a new entity row, it is added to the entity cache.

Figure 8-20 Entity Cache Stores Entity Rows During the Transaction

This image is described in the surrounding text

When an entity row is created, modified, or removed, it is automatically enrolled in the transaction's list of pending changes. When you call commit() on the Transaction object, it processes its pending changes list, validating new or modified entity rows that might still be invalid. When the entity rows in the pending list are all valid, the Transaction issues a database SAVEPOINT and coordinates saving the entity rows to the database. If all goes successfully, it issues the final database COMMIT statement. If anything fails, the Transaction performs a ROLLBACK TO SAVEPOINT to allow the user to fix the error and try again.

The Transaction object used by an application module represents the working set of entity rows for a single end-user transaction. By design, it is not a shared, global cache. The database engine itself is an extremely efficient shared, global cache for multiple, simultaneous users. Rather than attempting to duplicate all the work of fine-tuning that has gone into the database's shared, global cache functionality, ADF Business Components consciously embraces it. To refresh a single entity object's data from the database at any time, you can call its refresh() method. You can setClearCacheOnCommit() or setClearCacheOnRollback() on the Transaction object to control whether entity caches are cleared at commit or rollback. The defaults are false and true, respectively. The Transaction object also provides a clearEntityCache() method you can use to programmatically clear entity rows of a given entity type (or all types). When you clear an entity cache, you allow entity rows of that type to be retrieved from the database fresh the next time they are either found by primary key or retrieved by an entity-based view object.

8.4.2 How to Create a Command-Line Java Test Client

To the create a test client program, use the Create Java Class wizard, which is accessible from the New Gallery.

8.4.2.1 Generating a Test Client with Skeleton Code

When you use the Create Java Class wizard to create the test client program, JDeveloper will open your program file in the source editor and allow you to add code from a predefined code template to complete the test client.

Before you begin:

It may be helpful to have an understanding of programmatic testing. For more information, see Testing View Object Instances Programmatically.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

To generate a skeleton Java test client:

  1. In the Applications window, right-click the project in which you want to create the test client and choose New and then Java Class.
  2. In the Create Java Class dialog, enter a class name and a package name and ensure that the Extends field shows java.lang.Object.
  3. In Optional Attributes, deselect Constructors from Superclass and select Main Method.
  4. Click OK.

    The .java file opens in the source editor to show the skeleton code, as shown in the following example. In this example, TestClient is the class name and oracle.summit.model.test is the package name.

package oracle.summit.model.test;

public class TestClient {
  public static void main(String[] args) {
     TestClient testClient = new TestClient();
  }
}

8.4.2.2 Modifying the Skeleton Code to Create the Test Client

After you generate skeleton code for the test client, you can proceed to edit the file using the skeleton code template you created.

Before you begin:

It may be helpful to have an understanding of programmatic testing. For more information, see Testing View Object Instances Programmatically.

You may also find it helpful to understand functionality that can be added using other Oracle ADF features. For more information, see Additional Functionality for Testing View Instances.

You will need to complete this task:

To insert the skeleton code:

  1. Place the cursor on a blank line inside the body of the main() method and type the name of your test client class and then press Ctrl + Enter.
  2. Type the characters bc4jclient and then press Ctrl + Enter.
  3. Adjust the values of the amDef andconfig variables to reflect the names of the application module definition and the configuration that you want to use, respectively.

    For the example below, the changed lines look like this:

    String amDef = "oracle.summit.model.viewobjects.AppModule";
    String config = "AppModuleLocal";
    
  4. Finally, change the view object instance name in the call to findViewObject() to the one you want to work with. Specify the name exactly as it appears in the Data Model tree on the Data Model page of the overview editor for the application module.

    the changed line looks like this:

    ViewObject customerList = am.findViewObject("SCustomerView1");
    

Your skeleton test client for your application module should contain source code like what you see in the following example.

Note:

The examples throughout Working Programmatically with an Application Module's Client Interface expand this test client sample code to illustrate calling custom application module service methods, too.

package oracle.summit.model.viewobjects;

import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;

public class TestClient {

  public static void main(String[] args) {
    String  amDef = "oracle.summit.model.viewobjects.AppModule";
    String  config = "AppModuleLocal";
    ApplicationModule am =
                      Configuration.createRootApplicationModule(amDef,config);
    // 1. Find the Customer view object instance.
    ViewObject customerList = am.findViewObject("SCustomerView1");
    // Work with your appmodule and view object here
    Configuration.releaseRootApplicationModule(am,true);
  }
}

The method call createRootApplicationModule() allows test clients to create an application module instance where the ADF Model layer is unavailable. Normally, in the Fusion web applications your code finds the view object on the application module through ADF binding objects. For more information about how to work with the ADF Model layer, see How to Access an Application Module Client Interface in a Fusion Web Application.

Replace // Work with your appmodule and view object here , with code that will execute the view objects you want to test. For example, to execute the view object's query, display the number of rows it will return, and loop over the result to fetch the data and print it out to the console, you can adapt the code shown in the following example for your data model project components.

    // 2. Execute the query
    customerList.executeQuery();
    // 3. Iterate over the resulting rows
    while (customerList.hasNext()) {
        Row customer = customerList.next();
        // 4. Print the customer's name
        System.out.println("Customer: " + customer.getAttribute("Name"));
        // 5. Get related rowset of Orders using view link accessor attribute
        RowSet orders = (RowSet)customer.getAttribute("SOrdView");
        // 6. Iterate over the Orders rows
        while (orders.hasNext()) {
            Row order = orders.next();
            // 7. Print out some order attribute values
              System.out.println(" ["+order.getAttribute("CustomerId")+"] "+
                                    order.getAttribute("Id")+": "+
                                    order.getAttribute("Total"));
              if(!order.getAttribute("OrderFilled").equals("Y")) {
                  // 8. Get related rowset of OrderItems
                  RowSet items = (RowSet)order.getAttribute("SItemView");
                  // 9. Iterate over the OrderItems rows
                  while (items.hasNext()) {
                     Row item = items.next();
                     // 10. Print out some order items attributes
                     System.out.println("  "+item.getAttribute("ItemId")+": "+
                                          item.getAttribute("Price"));
                }
            }
        }
    }

The first line calls the executeQuery() method to execute the view object's query. This produces a row set of zero or more rows that you can loop over using a while statement that iterates until the view object's hasNext() method returns false. Inside the loop, the code puts the current Row in a variable named customer, then invokes the getAttribute() method twice on that current Row object to get the value of the Name and Orders attributes to print order information to the console. A second while statement performs the same task for the line items of the order.

8.4.3 What Happens When You Run a Test Client Program

The call to createRootApplicationModule() on the Configuration object returns an instance of the application module to work with. As you might have noticed in the debug diagnostic output, the ADF Business Components runtime classes load XML documents as necessary to instantiate the application module and the instance of the view object component that you've defined in its data model at design time. The findViewObject() method on the application module finds a view object instance by name from the application module's data model. After the loop described in Modifying the Skeleton Code to Create the Test Client, the test client executes releaseRootApplicationModule() on the Configuration object. This signals that you're done using the application module and it allows the framework to clean up resources, like the database connection that was used by the application module.

8.4.4 What You May Need to Know About Running a Test Client

The createRootApplicationModule() and releaseRootApplicationModule() methods are very useful for command-line access to application module components. However, you typically won't need to write these two lines of code in the context of an ADF-based web application. The ADF Model layer cooperates automatically with the ADF business services layer to acquire and release ADF application module components for you in those scenarios. For more information about how to work with the ADF Model layer, see How to Access an Application Module Client Interface in a Fusion Web Application.

8.4.5 How to Count the Number of Rows in a Row Set

The getEstimatedRowCount() method is used on a RowSet to determine how many rows it contains:

long numOrders = orders.getEstimatedRowCount();

The implementation of the getEstimatedRowCount() initially issues a SELECT COUNT(*) query to calculate the number of rows that the query will return. The query is formulated by "wrapping" your view object's entire query in a statement like:

SELECT COUNT(*) FROM ( ... your view object's SQL query here ... )

The SELECT COUNT(*) query allows you to access the count of rows for a view object without necessarily retrieving all the rows themselves. This approach permits an important optimization for working with queries that return a large number of rows, or for testing how many rows a query would return before proceeding to work with the results of the query.

Once the estimated row count is calculated, subsequent calls to the method do not reexecute the COUNT(*) query. The value is cached until the next time the view object's query is executed, since the fresh query result set returned from the database could potentially contain more, fewer, or different rows compared with the last time the query was run. The estimated row count is automatically adjusted to account for pending changes in the current transaction, adding the number of relevant new rows and subtracting the number of removed rows from the count returned.

You can also override getEstimatedRowCount() to perform a custom count query that suits your application's needs.

8.4.6 How to Access a Detail Collection Using the View Link Accessor

Once you've retrieved the RowSet of detail rows using a view link accessor, as described in Programmatically Accessing a Detail Collection Using the View Link Accessor, you can loop over the rows it contains using the same pattern used by the view object's row set of results, as shown in the following example.

while (orders.hasNext()) {
  Row curOrder = orders.next();
  System.out.println("--> (" + curOrder.getAttribute("Id") + ") " + 
                     curOrder.getAttribute("Total"));
}

The following example shows the main() method sets a dynamic WHERE clause to restrict the CustomerView view object instance to show only customers whose credit_rating_id has the value 4. Additionally, the executeAndShowResults() method accesses the view link accessor attribute (SOrdView) and prints out the order Id and Total attribute for each customer.

To access the a detail collection using a view link accessor, follow these basic steps (as illustrated in the following example):

  1. Find the master view object instance.

  2. Execute the query.

  3. Iterate over the master view object rows.

  4. Get the related row set of the detail view object using the view link accessor attribute.

  5. Iterate over the detail view object rows.

  6. Optionally, do something with the detail row set attributes.

Performance Tip

If the code you write to loop over the rows does not need to display them, then you can call the closeRowSet() method on the row set when you're done. This technique will make memory use more efficient. The next time you access the row set, its query will be reexecuted.

package oracle.summit.model.viewobjects;

import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;

public class TestClient2 {
    public static void main(String[] args) {
            String        amDef = "oracle.summit.model.viewobjects.AppModule";
            String config = "AppModuleLocal";
            ApplicationModule am = 
                Configuration.createRootApplicationModule(amDef, config);
            ViewObject vo = am.findViewObject("SCustomerView1");
          // Add an extra where clause with a new named bind variable
            vo.setWhereClause("credit_rating_id = :theCreditRating");
            vo.defineNamedWhereClauseParam("theCreditRating", null, null);
            vo.setNamedWhereClauseParam("theCreditRating", "4");
          // Show results when :theCreditRating = '4'
          executeAndShowResults(vo);
          Configuration.releaseRootApplicationModule(am, true);
        }
        private static void executeAndShowResults(ViewObject vo) {
          System.out.println("---");
          vo.executeQuery();
          while (vo.hasNext()) {
            Row curCustomer = vo.next();
            // Access the row set of details using the view link accessor attr.
            RowSet orders = (RowSet)curCustomer.getAttribute("SOrdView");
            long numOrders = orders.getEstimatedRowCount();
            System.out.println(curCustomer.getAttribute("Id") + " " + 
                               curCustomer.getAttribute("Name")+" ["+
                               numOrders+" orders]");
            while (orders.hasNext()) {
              Row curOrder = orders.next();
              System.out.println("--> (" + curOrder.getAttribute("Id") + ") " + 
                                 curOrder.getAttribute("Total"));
            }
          }
        }
}

Running TestClient2.java produces output in the Log window, as shown in the following example. Each customer is listed, and for each customer that has some orders, the order ID and total appears beneath their name.

---
202 Simms Athletics [11 orders]
--> (98) 595
--> (113) 4990
--> (114) 567
--> (115) 866.7
--> (116) 3661.24
--> (117) 17489
212 Hamada Sport [22 orders]
--> (108) 149570
--> (196) 761
--> (197) 516.75
...

If you run TestClient2.java with debug diagnostics enabled, you will see the SQL queries that the view object performed. The view link WHERE clause predicate is used to automatically perform the filtering of the detail service request rows for the current row in the CustomerView view object.

8.4.7 How to Iterate Over a Master-Detail-Detail Hierarchy

To iterate over a master-detail with an additional level of nesting, follow these basic steps (as illustrated in the following example):

  1. Find the master view object instance.

  2. Executes the query.

  3. Iterate over the resulting rows.

  4. Optionally, do something with the attributes of the master row set.

  5. Get the related row set of the detail view object using the view link accessor attribute.

  6. Iterate over the detail row set rows.

  7. Optionally, do something with the attributes of the detail row set.

  8. Get the related row set of the second detail view object using the view link accessor attribute.

  9. Iterates over the second detail row set rows.

  10. Optionally, do something with the second detail row set attributes.

Other than having one additional level of nesting, the following example uses the same API's used in the TestClient program that was iterating over master-detail read-only view objects in How to Access a Detail Collection Using the View Link Accessor.

If you use JDeveloper's Refactor > Duplicate functionality on an existing TestClient.java class, you can quickly "clone" it to create a TestClient2.java class. For example, the TestClient.java class described in How to Access a Detail Collection Using the View Link Accessor is suited to this technique.

package oracle.summit.model.viewobjects;

import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;

public class TestClient {
    public static void main(String[] args) {
        TestClient testClient = new TestClient();
        String amDef = "oracle.summit.model.viewobjects.AppModule";
        String config = "AppModuleLocal";
        ApplicationModule am = 
                          Configuration.createRootApplicationModule(amDef,config);
        // Work with your appmodule and view object here
        // 1. Find the Customer view object instance.
        ViewObject customerList = am.findViewObject("SCustomerView1");
        // 2. Execute the query
          customerList.executeQuery();
          // 3. Iterate over the resulting rows
          while (customerList.hasNext()) {
              Row customer = customerList.next();
              // 4. Print the person's email
              System.out.println("Customer: " + customer.getAttribute("Name"));
              // 5. Get related rowset of Orders using view link accessor attr.
              RowSet orders = (RowSet)customer.getAttribute("SOrdView");
              // 6. Iterate over the Orders rows
              while (orders.hasNext()) {
                  Row order = orders.next();
                  // 7. Print out some order attribute values
                    System.out.println(" ["+order.getAttribute("CustomerId")+"] "+
                                          order.getAttribute("Id")+": "+
                                          order.getAttribute("Total"));
                    if(order.getAttribute("OrderFilled").equals("Y")) {
                        // 8. Get related rowset of Items
                        RowSet items = (RowSet)order.getAttribute("SItemView");
                        // 9. Iterate over the Items rows
                        while (items.hasNext()) {
                           Row item = items.next();
                           // 10. Print out some order items attributes
                           System.out.println("  "+
                                                item.getAttribute("ItemId")+": $"+
                                                item.getAttribute("Price"));
                      }
                  }
              }
          }
    }
}

Running the program produces the output shown in the following example.

Customer: Unisports
 [201] 97: 84000
  210: $9
  211: $1500
Customer: Simms Athletics
 [202] 98: 595
  212: $85
 [202] 113: 4990
  328: $9
 [202] 114: 567
  329: $28
  330: $40.95
  331: $25
...

8.4.8 How to Find a Row and Update a Foreign Key Value

To find a row and update a foreign key value, follow these basic steps (as illustrated in the following example):

  1. Find the view object instance.

  2. Construct a Key object to look up the row for the view instance.

  3. Use findByKey() to find the row.

  4. Optionally, do something with the row's attribute.

The following example shows the main() method finds and updates a foreign key value to find a row of the Orders view object instance. The sample then prints out the existing value of the OrderStatusCode attribute before changing the value on the row.

package oracle.summit.model.viewobjects;
 
import java.text.ParseException;
import java.text.SimpleDateFormat;
 
import java.util.Calendar;
import java.util.Date;
 
import oracle.jbo.ApplicationModule;
import oracle.jbo.JboException;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
 
public class TestClient3 {
    public TestClient3() {
        super();
    }
 
    public static void main(String[] args) {
        String amDef = "oracle.summit.model.viewobjects.AppModule";
        String config = "AppModuleLocal";
        ApplicationModule am = 
                         Configuration.createRootApplicationModule(amDef, config);
        // 1. Find the Orders view object instance
        ViewObject vo = am.findViewObject("SOrdView1");
        // 2. Construct a new Key to find Order # 100
        Key orderKey = new Key(new Object[] { 100 });
        // 3. Find the row matching this key
        Row[] ordersFound = vo.findByKey(orderKey, 1);
        if (ordersFound != null && ordersFound.length > 0) {
            Row order = ordersFound[0];
            // 4. Print some order information
            Date dateOrdered = (Date) order.getAttribute("DateOrdered");
            String orderDate = dateOrdered.toString();
            System.out.println("Order Date is: " + orderDate);
 
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
            Calendar c = Calendar.getInstance();
            try {
                c.setTime(sdf.parse(orderDate));
            } catch (ParseException e) {
            }
            c.add(Calendar.DAY_OF_MONTH, -2);  
            // // number of days to add 
            orderDate = sdf.format(c.getTime());  
            System.out.println(orderDate);
            try {
                // 5. Try setting the ship date to an illegal value
                order.setAttribute("DateShipped", orderDate);
                am.getTransaction().commit();
            } catch (JboException ex) {
                System.out.println("ERROR: " + ex.getMessage());
            }
            // 6. Set the ship date to a legal value
            order.setAttribute("DateShipped", orderDate);
            // 7. Show the value of the ship date was updated successfully
            System.out.println("Ship date is: " +
                                             order.getAttribute("DateShipped"));
            // 8. Show the current value of the customer for this order
            System.out.println("Customer: " + order.getAttribute("CustomerId"));
            // 9. Reassign the order to customer # 210
            order.setAttribute("CustomerId", 210);
            // 10. Show the value of the reference information now
            System.out.println("Customer: " + order.getAttribute("CustomerId"));
            // 11. Rollback the transaction
            am.getTransaction().rollback();
            System.out.println("Transaction canceled");
        }
        Configuration.releaseRootApplicationModule(am, true);
    }
 
}

Running this example produces the output shown in the following example.

Order Date is: 2012-08-29
2012-08-27
ERROR: JBO-oracle.summit.model.viewobjects.SOrd_Rule_0: 
       You cannot have a shipping date that is before the order date
Ship date is: 2012-08-27
Customer: 204
Customer: 210
Transaction canceled

8.4.9 How to Create a New Row for a View Object Instance

To create a new view row instance, follow these basic steps (as illustrated in the following example):

  1. Find the view object instance.

  2. Create a new row and insert it into the row set.

  3. Set the values of the required attributes in the new row.

  4. Commit the transaction.

The following example shows the main() method finds the OrdView view object instance and inserts a new row into the row set. Because the OrdView view object is entity-based, the CreatedBy attribute derives its value from the mapped entity object attribute. The sample then sets values for the remaining attributes before committing the transaction.

package oracle.summit.model.viewobjects;

import java.util.Date;

import oracle.jbo.ApplicationModule;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
import oracle.jbo.domain.DBSequence;

public class TestClient4 {
 
  public static void main(String[] args) {
        String amDef = "oracle.summit.model.viewobjects.AppModule";
        String config = "AppModuleLocal";
        ApplicationModule am = 
                         Configuration.createRootApplicationModule(amDef, config);

        // 1. Find the Orders view object instance.
        ViewObject orders = am.findViewObject("SOrdView1");

        // 2. Create a new row and insert it into the row set
        Row newOrder = orders.createRow();
        orders.insertRow(newOrder);
        // Show the entity object-related defaulting for DateOrdered attribute
        System.out.println("DateOrdered defaults to: " + 
                                    newOrder.getAttribute("DateOrdered"));

        // 3. Set values for some of the required attributes        
        newOrder.setAttribute("CustomerId", 201);
        newOrder.setAttribute("SalesRepId", 12);
        newOrder.setAttribute("PaymentTypeId", 2);
        newOrder.setAttribute("OrderFilled", "N");

        // 4. Commit the transaction
        am.getTransaction().commit();

        // 5. Retrieve and display the trigger-assigned order id
        DBSequence id = (DBSequence)newOrder.getAttribute("Id");
        System.out.println("Thanks, reference number is " +
                                                  id.getSequenceNumber());
        Configuration.releaseRootApplicationModule(am, true);
  }
}

Running this example produces the results shown in the following example.

DateOrdered defaults to: 2013-3-11
Thanks, reference number is 6

8.4.10 How to Retrieve the Row Key Identifying a Row

To retrieve a row key to identify a row, follow these basic steps (as illustrated in the following example):

  1. Find the view object instance.

  2. Construct a key using a supplied value.

  3. Find the row with this key.

  4. Optionally, do something with the key of the row.

The following example shows the main() method finds the OrdView view object instance and constructs a row key to find an order number. The findByKey() method find the OrdView rows with the specified key. The sample then displays the key of the row, accesses the row set using the ItemView view link accessor, and iterates over the rows to display the key of each ItemView row.

package oracle.summit.model.viewobjects;
 
import oracle.jbo.ApplicationModule;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;
 
public class TestClient5 {
    public static void main(String[] args) {
        String amDef = "oracle.summit.model.viewobjects.AppModule";
        String config = "AppModuleLocal";
        ApplicationModule am = 
                         Configuration.createRootApplicationModule(amDef, config);
        // 1. Find the Orders view object instance
        ViewObject vo = am.findViewObject("SOrdView1");
        // 2. Construct a key to find order number 100
        Key orderKey = new Key(new Object[] { 100 });
        // 3. Find the Orders row with the key
        Row[] ordersFound = vo.findByKey(orderKey, 1);
        if (ordersFound != null && ordersFound.length > 0) {
             Row order = ordersFound[0];
             // 4. Displays the key of the Orders row
             showKeyFor(order);
             // 5. Accesses row set of Orders using OrderItems view link accessor
             RowSet items = (RowSet)order.getAttribute("SItemView");
             // 6. Iterates over the Items row
             while (items.hasNext()) {
                 Row itemRow = items.next();
                 // 4. Displays the key of each Items row
                 showKeyFor(itemRow);
             }
        }
        Configuration.releaseRootApplicationModule(am, true);
    }
 
    private static void showKeyFor(Row r) {
       // get the key for the row passed in
       Key k = r.getKey();
       // format the key as "(val1,val2)"
       String keyAttrs = formatKeyAttributeNamesAndValues(k);
       // get the serialized string format of the key, too
       String keyStringFmt = r.getKey().toStringFormat(false);
       System.out.println("Key " + keyAttrs + " has string format " + 
                             keyStringFmt);
    }

    // Build up "(val1,val2)" string for key attributes
    private static String formatKeyAttributeNamesAndValues(Key k) {
       StringBuffer sb = new StringBuffer("(");
       int attrsInKey = k.getAttributeCount();
       for (int i = 0; i < attrsInKey; i++) {
           if (i > 0)
               sb.append(",");
               sb.append(k.getAttributeValues()[i]);
       }
       sb.append(")");
       return sb.toString();
    }
}

Running the example produces the results shown in the following example. Notice that the serialized string format of a key is a hexadecimal number that includes information in a single string that represents all the attributes in a key.

Key (100) has string format 000100000003313030
Key (100,156) has string format 000200000003C2020200000002C102
Key (100,157) has string format 000200000003C2020200000002C102
...

8.4.11 What You May Need to Know About Using Partial Keys with findByKey()

View objects based on multiple entity usages support the ability to find view rows by specifying a partially populated key. A partial key is a multi-attribute Key object with some of its attributes set to null. However, there are strict rules about what kinds of partial keys can be used to perform the findByKey().

If a view object is based on n entity usages, where n > 1, then the view row key is by default comprised of all of the primary key attributes from all of the participating entity usages. Only the ones from the first entity object are required to participate in the view row key, but by default all of them do.

If you allow the key attributes from some secondary entity usages to remain as key attributes at the view row level, then you should leave all of the attributes that form the primary key for that entity object as part of the view row key. Assuming you have left the one or more key attributes in the view row for m of the n entity usages, where (m <= n), then you can use findByKey() to find rows based on any subset of these m entity usages. Each entity usage for which you provide values in the Key object, requires that you must provide non-null values for all of the attributes in that entity's primary key.

You have to follow this rule because when a view object is based on at least one or more entity usages, its findByKey() method finds rows by delegating to the findByPrimaryKey() method on the entity definition corresponding to the first entity usage whose attributes in the view row key are non-null. The entity definition's findByPrimaryKey() method requires all key attributes for any given entity object to be non-null in order to find the entity row in the cache.

As a concrete example, imagine that you have a OrderInfoVO view object with a OrderEO entity object as its primary entity usage, and an AddressEO entity as secondary reference entity usage. Furthermore, assume that you leave the Key Attribute property of both of the following view row attributes set to true:

  • OrderId — primary key for the OrderEO entity

  • AddressId — primary key for the AddressEO entity

The view row key will therefore be the (OrderId, AddressId) combination. When you do a findByKey(), you can provide a Key object that provides:

  • A completely specified key for the underlying OrderEO entity

    Key k = new Key(new Object[]{new Number(200), null});
    
  • A completely specified key for the underlying AddressEO entity

    Key k = new Key(new Object[]{null, new Number(118)}); 
    
  • A completely specified key for both entities

    Key k = new Key(new Object[]{new Number(200), new Number(118)});
    

When a valid partial key is specified, the findByKey() method can return multiple rows as a result, treating the missing entity usage attributes in the Key object as a wildcard.

8.4.12 How to Authenticate Test Users in the Test Client

If you have enabled ADF Security for your application and provisioned the jazn-data.xml file with test users, you will need to include method calls to authenticate a user before you run the test client. To authenticate a user in the test client, follow these basic steps (as illustrated in the following exxample):

  1. Create the authentication service.

  2. Pass in the login credentials for a test user defined in the jazn-data.xml file.

  3. If authentication succeeds, then test the application module.

  4. Log the user out.

For details about how to run the Configure ADF Security wizard to enable ADF Security and how to create test users in JDeveloper's identity store, see ADF Security Process Overview.

package oracle.summit.model.test;
import oracle.jbo.ApplicationModule;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.ViewObject;
import oracle.jbo.client.Configuration;

import oracle.adf.share.security.AuthenticationService;
import oracle.adf.share.security.authentication.AuthenticationServiceUtil;
import oracle.adf.share.ADFContext;
import oracle.adf.share.security.SecurityContext;
import oracle.adf.share.security.SecurityEnv;

import javax.security.auth.Subject;

public class TestAuthenticationClient {
    public static void main(String[] args) {
        String amDef = "test.TestAuthModule";
        String config = "TestAuthModuleLocal";

        // 1. Create authentication service.
        AuthenticationService authService =
                            AuthenticationServiceUtil.getAuthenticationService();
        try
           {
              // 2. Pass in user id and password defined in local identity store.
              authService.login("tester1", "welcome1");
           }
           catch (Exception e)
           {

           }

        // Uncomment to output authentication status
        // String userName = 
                       ADFContext.getCurrent().getSecurityContext().getUserName();

        // System.out.println("*** userName : " + userName);
        // Subject subject = 
                       ADFContext.getCurrent().getSecurityContext().getSubject();
        // System.out.println("Subject : " + subject);

        // 3. Test application module if authentication succeeds.
        if (ADFContext.getCurrent().getSecurityContext().isAuthenticated()) {
           ApplicationModule am = 
                       Configuration.createRootApplicationModule(amDef,config);
           ViewObject vo = am.findViewObject("TestView");
           // Work with your appmodule and view object here
           Configuration.releaseRootApplicationModule(am,true);

           // 4. Log out test user.
           authService.logout();

           // Uncomment to report logout success
           // boolean isAuthenticated = 
                   ADFContext.getCurrent().getSecurityContext().isAuthenticated();
           // System.out.println("*** isAuthenticated : " + isAuthenticated);
        }

    }
}