• No results found

Any new or customized entity object behavior goes here

In document Building-a-Web-Store (Page 101-106)

If you experiment by opening one of the ADF Toy Store demo's UIX pages in the ADF UIX visual editor, you'll experience first-hand how it gives true WYSISYG (what you see is what you get) feedback by showing you what the page will look like using the default look and feel we've selected.

Customizing the Default Framework Behavior

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

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

For example, if the base ADF framework EntityImpl class does not support a feature that you need all of your entity objects to have, not a problem. No need to file an enhancement request with Oracle Corporation and wait until the ADF Development team implements your desired feature for you. Just take the bull by the horns and add the feature yourself by making a framework customization.

Just create a Java class that extends oracle.jbo.server.EntityImpl and add your additional functionality into that class like this:

public class ToyStoreEntityImpl extends oracle.jbo.server.EntityImpl { /*

* Any new or customized entity object behavior goes here */

}

Then, when you create your entity objects for your application, just set up your components to extend from

ToyStoreEntityImpl instead of from the default EntityImpl base class. Figure 67 illustrates how this looks for one of the entity objects in the ADF Toy Store demo like Account which does exactly this.

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

The same opportunity that is available for customizing ADF framework base classes also exists for many aspects of the Struts framework, too. In this section we highlight the ADF framework customizations that were made to support the ADF Toy Store demo. They all live in the FwkExtensions project.

ADF Framework Customizations for the Controller Layer

In the toystore.fwk.controller package we have the ToyStoreDataForwardAction, which extends the base ADF DataForwardAction to make the following customizations:

1. Added two additional DataAction lifecycle methods named initializeModelForPage() and

initializeBindingsForPage() which fire just before and just after the default prepareModel() lifecycle method in the situation when no "postback" events are being handled by the action. By default they do nothing: they live to be overridden by subclasses.

 initializeModelForPage() is useful for calling custom methods to set bind variables in your business service queries before the prepareModel() phase goes about executing the iterators in your binding container.

This setup could be done with a separate DataAction having a separate binding container and a custom method invocation association with it, however that meant that every page requiring bind variable setup would have required an extra action in the page flow model and I wanted to keep it as absolutely simple as possible.

 initializeBindingsForPage() can be used to programmatically modify the values of bindings before the page has a chance to see them.

2. Added a helper method getApplicationModule() to retrieve an ApplicationModule based on its Data Control name.

3. Customized the default way that a "tree" of bundled ADF exceptions get translated into Struts ActionError objects for NOTE:

As with all code in the ADF Toy Store demo, the framework customizations are copiously commented to explain what's going on in the classes, so please look into the code in the FwkExtensions project for more details on what each framework extension is doing.

NOTE: For a jumpstart on understanding the key ADF Business Components framework classes and the most common methods to use and override, please see Most Commonly Used Methods in ADF Business Components [7]

display to the user by overriding the reportErrors() method.

4. Added a boolean method releaseStateless() that can be overridden by a subclassing action to return true in order to indicate that all data controls in use by the current binding container should be released stateless at the end of the request. By default, an ADF ApplicationModule will have its pending state managed by the framework. There's also an alternative approach illustrated in this class with the releaseDataControlStateless() method that subclasses can explicitly call to release a data control by name in stateless mode at the end of the request.

5. Added an evalEL() helper method that subclassing actions can call to evaluate an EL expression.

6. Added an invokeEventAction() helper method that subclassing actions can call in their onEventName event-handler methods to carry out the default declarative before of invoking the action binding whose name matches the name of the event being handled before or after writing other custom code in the event handler method.

7. Added convenience methods to findControlBinding(), getBindingValue(), and setBindingValue(). These methods save a few lines of code requires to find control bindings, and to get/set their value.

In the same toystore.fwk.controller package, we also have the ToyStoreErrorHandler class. This class is installed as a custom error handler by the ToyStoreDataForwardAction's overridden method implementation of the DataAction's handleLifecycle() method. The ADF error handler will be notified any time an exception is thrown by the ADF binding layer.

The one customized feature that we've implemented here is to disable the appending of product codes in the JboException objects (as well as in any nested JboException's they may contain due to the ADF "bundled exception" feature. By calling the JboException's method setAppendCodes(false), when the error messages are displayed as strings they will not include the JBO-NNNNN product code and error number.

Lastly, in the toystore.fwk.controller package we also have the ToyStoreInitModelListener class. This class is referenced in the web.xml file in the ToyStoreViewControllerUIX project's public_html/WEB-INF directory. This class extends the base UIX InitModelListener class and adds a tiny bit of code that converts the Struts ActionError objects into the format that ADF UIX expects for presentation in its <messageBox> component.

ADF Framework Customizations for the Model Layer Business Objects

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

1. Declarative ability to force attribute values to UPPER or lower case.

2. Throwing of a specific custom exception EntityAlreadyExistsException when unique keys are violated Both customizations rely on overriding the framework's setAttributeInternal() method. To perform the declarative case folding, we add some custom code before calling super.setAttributeInternal(). To support the custom exception handling, we write some custom code in a catch block around the call to super.setAttributeInternal().

The private foldCaseOfStringIfCasePropertySet() method illustrates how to check for a custom attribute-level property named "Case" and if provided, behave accordingly based on whether its value is "lower" or "upper". The State and Country attributes of the Account entity, as well as the Shipstate, Billstate, Shipcountry, and Billcountry attributes of the Orders entity have this custom property set to "upper" in their XML metadata.

If you peek in the Account.xml file or the Orders.xml file in the business objects package directory, you'll see that the custom attribute-level properties show up in the XML nested inside the <Attribute> element that they are related to as shown in Example 24.

Example 24: Custom Component and Attribute Properties in XML Descriptor File

<Entity Name="Account" DBObjectName="ACCOUNT" AliasName="Account"

BindingStyle="Oracle" RowClass="toystore.model.businessobjects.AccountImpl"

MsgBundleClass="toystore.model.businessobjects.common.AccountImplMsgBundle">

:

<Attribute Name="State" IsNotNull="true" Precision="2" Type="java.lang.String"

ColumnName="STATE" ColumnType="VARCHAR2" SQLType="VARCHAR" >

<Properties>

Figure 68 shows how to customize the base class for an ADF component like an entity object. On the Java tab of the object editor, there is an (Extends...) button that brings up a Framework Base Classes dialog, where you can set any or all of the relevant base classes to use a custom class. The figure shows the Entity Object Wizard, but the process is similar for View Objects and Application Modules as well.

Figure 68: Setting ADF Framework Base Classes from the Java Panel

ADF Framework Customizations for the Model Layer Data Access Components

In the toystore.fwk.model.dataaccess package, we've implemented the following customizations:

 PropertyFileBasedLookupViewObjectImpl

Customizes the base ViewObjectImpl class to support "fetching" data from a Java *.properties file instead of fetching from a JDBC RowSet resulting from a SQL query. The CountryList, CreditCardList,

ExpirationYearList, and ShippingOptionsList view objects specify this customized class as their base view object class.

 ViewDefHelper NOTE:

I recommend always creating a set of ADF framework extension classes, even if you currently have no particular need to. When you later need to address a new feature that affects all components you have created of a given type, you will be super glad that you listened to this advice. I work with customers who setup multiple layers of framework customization classes for their business components. A first layer is a "company wide" set of classes that extend the base components in oracle.jbo.server.*. For each application project they work on, they create a project-level set of framework customization classes as well. You can set up your preferred ADF Business Components base classes at the IDE level, under the Tools | Preferences... dialog, on the Business Components > Base Classes panel. If you want to override these global settings for a particular project, you can also visit the Business

Components > Base Classes panel on the Project Properties dialog.

Exposes utility methods to simplify creating dynamic view object definitions of many kinds, including ones with updatable, entity-mapped attributes.

ADF Framework Customizations for the Business Service Layer

In the toystore.fwk.model.service package, the ToyStoreDBTransactionImpl class provides a customized

implementation of the framework's oracle.jbo.server.DBTransaction interface. To rewrite the minimum amount of code possible, it extends the oracle.jbo.server.DBTransactionImpl2 class, and overrides the postChanges() method.

This customized postChanges() implementation catches any DMLConstraintException resulting from the posting attempt and throws a JboException with an application-specific error message that it looks up from the from the

toystore.fwk.exceptions.ErrorMessages message bundle based on the database constraint name that has been violated.

The companion ToyStoreDBTransactionFactory class in this package extends the framework oracle.jbo.server.DatabaseTransactionFactory class to return an instance of the customized ToyStoreDBTransactionImpl.

The ADF configuration property named TransactionFactory needs to be set to the fully-qualified class name of the custom DBTransactionFactory class, so we would have:

TransactionFactory=toystore.fwk.model.service.ToyStoreDBTransactionFactory

to use our custom DB transaction implementation.

The ToyStoreApplicationModuleImpl class extends the base ADF Application Module implementation class to add a helper method named getConfigurationProperty(). This illustrates how to retrieve a property from the ADF configuration at runtime, and if not present, falls back to check the value of the same property as a Java System property. This is used by two places in the demo implementation code (ShoppingCartImpl in toystore.model.dataaccess package and

LockAllInventoryItemsHelper in the toystore.model.services package) to pick between alternate implementations based on a value of a configuration property.

Custom Validation Rule as Framework Extension

The toystore.fwk.rules package contains the VerifyStateForCountry class which implements the JbiValidator interface in the oracle.jbo.server.rules package to provide a custom, parameter-driven business rule. Both the Account entity and the Orders entity make declarative use of this reusable validation rule. For example, in the Account.xml file, you'll find the following snippet of XML that records the usage of this custom business rule and captures the parameter values that the generic rule uses to perform its validation. In this case we see that the countryAttributeName parameter is set to

"Country" and the stateAttributeName is set to "State".

<ValidationBean

In the Orders.xml file, you'll find a similar block of XML tags that set the parameter values differently so that the countryAttributeName parameter is set to "Shipcountry" and the stateAttributeName is set to "Shipstate".

The code for the VerifyStateForCountryRule class shows several interesting techniques in use:

1. On-demand creation of the toystore.fwk.rules.dataaccess.StatesForCountry view object

2. Use of a stored function (validate_state_for_country()) to accomplish multi-step database validation in a single round-trip.

3. Reuse of the same view object instance for subsequent executions of the rule

4. Setting of Max Fetch Size to 1 to improve performance of view objects that are known to fetch a single row.

5. Leveraging of the generic concept of attribute groups.

We've added this concept as a framework extension to the demo to improve performance by not re-validating an entity-level validation rules if none of the attributes on which they depend has changed since they were last validated. This idea is explained further in the Implementing Optimized Validation Based on Attribute Groups section below.

6. Caching of "already seen" country/state lookup values

Also, this example illustrates that it's easy to build reusable rules that make use of ADF components like view objects that can be packaged together with the rule into a reusable library like the FwkExtensions.jar that is created by the deployment profile in the FwkExtensions project.

Custom XSQL Action Handler for ADF

The toystore.fwk.xsql.ADFViewObject class implements a custom Oracle XSQL Pages action handler for involving ADF View Objects from the current binding container in XML/XSLT based view-layer rendering.

In document Building-a-Web-Store (Page 101-106)