Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3.0)

Part Number B28967-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

12 Using Validation and Conversion

This chapter describes how to add validation and conversion capabilities to your application. It also describes how to handle and display any errors, including those not caused by validation.

This chapter includes the following sections:

12.1 Introduction to Validation and Conversion

ADF Faces input components have built-in validation capabilities. You set validation on a component either by setting the required attribute or by using one of the prebuilt ADF Faces validators. ADF applications also have validation capabilities at the model layer, allowing you to set validation on a binding to an attribute. In addition, you can create your own ADF Faces validators to suit your business needs.

ADF Faces input components also have built-in conversion capabilities, which allow users to enter information as Strings, and which the application can automatically convert to another data type, such as Date. Conversely, data stored as something other than a String can be converted to a String for display and updating.

Many components, such as selectInputDate, automatically provide this capability. Other components, such as inputText, automatically add a built-in ADF Faces or JSF reference implementation converter when you drag and drop from the Data Control Palette an attribute that is of a type for which a converter exists.

When validators or converters fail, associated error messages can be displayed to the user. These messages can be displayed in popup dialogs for client-side validation, or they can be displayed on the page itself next to the component whose validation or conversion failed.

Read this chapter to understand:

12.2 Validation, Conversion, and the Application Lifecycle

Figure 12-1 shows how validation and conversion work in the integrated JSF and ADF lifecycle.

Figure 12-1 Validation and Conversion in the Lifecycle

The phases that handle validation and conversion.

When a form with data is submitted, the browser sends a request value to the server for each UI component whose value attribute is bound. The request value is first stored in an instance of the component in the JSF Apply Request Values phase. If the value requires conversion (for example, if it is displayed as a String but stored as a DateTime object), the data is converted to the correct type. Then, if you set ADF Faces validation for any of the components that hold the data, the value is validated against the defined rules during the Process Validations phase, before the value is applied to the model.

If validation or conversion fails, the lifecycle proceeds to the Render Response phase and a corresponding error message is displayed on the page. If validation and conversion are successful, then the UpdateModel phase starts and the validated and converted values are used to update the model.

At this point, if there are any ADF Model validation rules, the values are validated against those rules in the ADF Validate Model Updates phase. As with ADF Faces validation, if validation fails, the lifecycle proceeds to the Render Response phase. See Section 6.2.3, "What Happens at Runtime: The JSF and ADF Lifecycles" for more information.

When a validation or conversion error occurs, the component (in the case of JSF validation or conversion) or attribute (in the case of ADF Model validation) whose validation or conversion failed places an associated error message in the queue and invalidates itself. The current page is then redisplayed with an error message. Both ADF Faces components and the ADF Model provide a way of declaratively setting these messages. For information about how other errors are handled by an ADF application, see Section 12.8, "Handling and Displaying Exceptions in an ADF Application".

12.3 Adding Validation

You can add validation so that when a user edits or enters data in a field and submits the form, the data is validated against any set rules and conditions. If validation fails, the application displays an error message.

Those rules and conditions can be set at one of the following layers:

12.3.1 How to Add Validation

You set ADF Faces validation on the JSF page and you set ADF Model validation on the page definition file. Message display for both is handled on the JSF page. For more information about displaying messages created by validation errors, see Section 12.7, "Displaying Error Messages".

12.3.1.1 Adding ADF Faces Validation

By default, ADF Faces validation occurs on both the client and server side. Although both syntactic and semantic validation are performed on the client side and server side, the client side performs only a subset of the validation performed by the server side. Client-side validation allows validators to catch and display data without requiring a round-trip to the server.

Note:

If the JavaScript form.submit() function is called on a JSF page, the ADF Faces support for client-side validation is bypassed. ADF Faces provides a submitForm() method that you can use instead, or you could use the autoSubmit attribute on ADF Faces input components.

To set ADF Faces to not run client-side validation, add the <client-validation-disabled> element in adf-faces-config.xml and set it to true.

ADF Faces provides the following types of validation:

  • UI component attributes: ADF Faces input components provide attributes that can be used to validate data. For example, you can supply simple validation using the required attribute on ADF Faces input components to specify whether a value must be supplied. When set to true, the component must have a value. Otherwise the application displays an error message. For more information, see Section 12.3.1.1.1, "Using Validation Attributes".

  • Default ADF Faces validators: The validators supplied by ADF Faces and the JSF reference implementation provide common validation checks, such as validating date ranges and validating the length of entered data. For more information, see Section 12.3.1.1.2, "Using JSF and ADF Faces Validators".

  • Custom ADF Faces validators: You can create your own validators and then select them to be used in conjunction with UI components. For more information, see Section 12.4, "Creating Custom JSF Validation".

12.3.1.1.1 Using Validation Attributes

Many ADF Faces UI components have attributes that provide simple validation. Table 12-1 shows these attributes, along with a description of the validation logic they provide and the UI components that contain them.

Table 12-1 ADF Faces Validation Attributes

Attribute Description Available on

MaxValue

The maximum value allowed for the Date value.

chooseDate

MinValue

The minimum value allowed for the Date value.

chooseDate

Required

When set to true (or set to an EL expression that evaluates to true), the component must have a non-null value or a String value of at least one character.

For table selection components (see Section 7.6, "Enabling Row Selection in a Table"), if the required attribute is set to true, then at least one row in the table must be selected.

All input components, all select components, tableSelectMany, tableSelectOne

MaximumLength

The maximum number of characters that can be entered. Note that this value is independent of the value set for the columns attribute. See also ByteLengthValidator in Table 12-3, "ADF Faces Validators".

inputText


When you use the Data Control Palette to create input components, the required attribute is bound to the mandatory property of its associated binding, as shown in the following EL expression:

<af:inputText required="#{bindings.problemDescription.mandatory}"

The EL expression evaluates to whether or not the attribute on the object to which it is bound can be null. You can choose to keep the expression as is, or you can manually set the required attribute to "true" or "false".

Tip:

The object to which the UI component is bound varies depending on how the input component was created. For example, if a form was created using a method to create a parameter form, then the input components are usually bound to variables, since the attribute values do not yet exist. You need to set the isNotNull property on the variable if you wish to use the default EL expression. If a form was created using a collection returned by a method, then the input component is probably bound to an attribute on an entity object, and you need to set that object's isNotNull property.

To use UI component attributes that provide validation:

  1. In the Structure window, select the UI component.

  2. In the Property Inspector, enter a value for the validation attribute. See Table 12-1 for a list of validation attributes you could use.

  3. (Optional) Set the tip attribute to display text that will guide the user to entering correct data (for example, a valid range for numbers). This text will display under the component.

  4. (Optional) If you set the required attribute to true (or if you used an EL expression that can evaluate to true), you can also enter a value for the RequiredMessageDetail attribute. Instead of displaying a default message, ADF Faces will display this message, if validation fails.

    For tables with a selection component set to required, you must place the error message in the summary attribute of the table in order for the error message to display.

    Messages can include optional placeholders (such as {0}, {1}, and so on) for parameters. At runtime, the placeholders are replaced with the appropriate parameter values. The order of parameters is:

    • Component label input value (if present)

    • Minimum value (if present)

    • Maximum value (if present)

    • Pattern value (if present)

    Example 12-1 shows a RequiredMessageDetail attribute that uses parameters.

    Example 12-1 Parameters in a RequiredMessageDetail Attribute

    <af:inputText value="#{bindings.productId.inputValue}"
                  label="Product ID"
                  requiredMessageDetail="You must enter a {0}."
                  required="true"
    </af:inputText>
    

    This message evaluates to You must enter a Product ID.

    For additional help with UI component attributes, in the Property Inspector, right-click the attribute name and choose Help.

12.3.1.1.2 Using JSF and ADF Faces Validators

JSF and ADF Faces validators provide more complex validation routines. Table 12-2 describes the JSF reference implementation validators and Table 12-3 describes the built-in ADF Faces validators.

Table 12-2 JSF Reference Implementation Validators

Validator Tag Name Description

DoubleRangeValidator

f:validateDoubleRange

Validates that a component value is within a specified range. The value must be convertible to floating-point type or a floating-point.

LengthValidator

f:validateLength

Validates that the length of a component value is within a specified range. The value must be of type java.lang.String

LongRangeValidator

f:validateLongRange

Validates that a component value is within a specified range. The value must be any numeric type or String that can be converted to a long


Table 12-3 ADF Faces Validators

Validator Tag Name Description

ByteLengthValidator

af:validateByteLength

Validates the number of bytes in a String when Java encoding is used. For example, six English characters do not use the same byte storage as 6 Japanese characters. You specify the encoding to use as an attribute of the validator.

In cases where the server must limit the number of bytes required to store a string, use this validator instead of specifying the maximumLength attribute on an input component.

DateTimeRangeValidator

af:validateDateTimeRange

Validates that the entered date is within a given range. You specify the range as attributes of the validator.

RegExpValidator

af:validateRegExp

Validates the data using Java regular expression syntax.


Note:

ADF Faces also provides the af:validator tag, which you can use to register a custom validator on a component. For information about using custom validators, see Section 12.4, "Creating Custom JSF Validation".

By default, whenever you drop an attribute from the Data Control Palette as an input text component, JDeveloper automatically adds the af:validator tag to the component, and binds it to the validator property on the associated binding. The binding allows access to ADF Model validation for processing on the client side. For more information, see Section 12.3.2, "What Happens When You Create Input Fields Using the Data Control Palette". For information about ADF Model validation, see Section 12.3.1.2, "Adding ADF Model Validation".

To add ADF Faces validators:

  1. In the Structure window, right-click the component for which you'd like to add a validator.

  2. In the context menu, choose Insert inside <UI component> > ADF Faces Core to insert an ADF Faces validator. (To insert a JSF reference implementation validator, choose Insert inside <UI component> > JSF Core.)

  3. Choose a validator tag (for example, ValidateDateTimeRange).

  4. In the Property Inspector, set values for the attributes, including any messages for validation errors. For additional help, right-click any of the attributes and choose Help.

    ADF Faces lets you customize the detail portion of a validation error message. By setting a value for an XxxMessageDetail attribute, where Xxx is the validation error type (for example, maximumMessageDetail), ADF Faces displays the custom message instead of a default message, if validation fails.

12.3.1.2 Adding ADF Model Validation

Table 12-4 describes the ADF Model validation rules that you can configure for an attribute.

Table 12-4 ADF Model Validation Rules

Validator Rule Name Description

Compare

Compares the attribute's value with a literal value

List

Validates whether or not the value is in or is not in a list of values

Range

Validates whether or not the value is within a range of values

Length

Validates the value's character or byte size against a size and operand (such as greater than or equal to)

Regular Expression

Validates the data using Java regular expression syntax


To create an ADF Model validation rule:

  1. Open the page definition that contains the attribute for which you want to create a rule.

  2. In the Structure window, select the attribute, list, or table binding.

  3. In the Property Inspector, select the Edit Validation Rule link.

  4. In the Validation Rules Editor, select the attribute name and click New.

  5. In the Add Validation Rule dialog, select a validation rule and configure the rule accordingly. For additional help on creating the different types of rules, click Help.

12.3.2 What Happens When You Create Input Fields Using the Data Control Palette

When you use the Data Control Palette to create input text fields (for example, by dropping an attribute from the Data Control Palette as an inputText component), JDeveloper automatically provides ADF Faces validation code on the JSF page by:

  • Adding an af:messages tag as a child of the afh:body tag.

    By default the globalOnly attribute is set to false, and the message and text attributes are not set. For more information, see Section 12.7, "Displaying Error Messages".

  • Binding the required attribute for input fields to the mandatory property of the associated attribute binding, as shown in the following EL expression:

    <af:inputText required="#{bindings.problemDescription.mandatory}"
    

    The expression evaluates to whether or not a null value is allowed based on the attribute of the associated business object. By default, all components whose required attribute evaluates to true will display an asterisk.

  • Adding an af:validator tag as a child of the input component, and binding the tag to the validator property of the associated binding, as shown below:

    <af:inputText value="#{bindings.someAttribute.inputValue}"...>
      <af:validator binding="#{bindings.someAttribute.validator}"/>
    </af:inputText>
    

    The binding allows the JSF lifecycle to access, on the client side, any ADF Model validation that you may have set for the associated attribute. If you don't wish to use ADF Model validation, then you can delete the af:validator tag and insert the validation tag of your choice, or if you don't want to use any validation, you can simply delete the tag. If you do want to use only ADF Model validation, you must keep the tag as is.

    Tip:

    If you delete the af:validator tag and its binding, and want to add ADF Model validation at a later point, you must add the tag back into the code with the binding attribute bound to the associated attribute's validator property.

To create a simple input form for products in the SRDemo application, for example, you might drop the product constructor method from the Data Control Palette as a parameter form. Example 12-2 shows the JSF code created by JDeveloper.

Example 12-2 JSF Code for a Create Product Page

<afh:body>
  <af:messages/>
  <h:form>
    <af:panelForm>
      <af:inputText value="#{bindings.productId.inputValue}"
                    label="#{bindings.productId.label}"
                    required="#{bindings.productId.mandatory}"
                    columns="#{bindings.productId.displayWidth}">
        <af:validator binding="#{bindings.productId.validator}"/>
        <f:convertNumber groupingUsed="false"
                         pattern="#{bindings.productId.format}"/>
      </af:inputText>
      <af:inputText value="#{bindings.name.inputValue}"
                    label="#{bindings.name.label}"
                    required="#{bindings.name.mandatory}"
                    columns="#{bindings.name.displayWidth}">
        <af:validator binding="#{bindings.name.validator}"/>
      </af:inputText>
      <af:inputText value="#{bindings.image.inputValue}"
                    label="#{bindings.image.label}"
                    required="#{bindings.image.mandatory}"
                    columns="#{bindings.image.displayWidth}">
        <af:validator binding="#{bindings.image.validator}"/>
      </af:inputText>
      <af:inputText value="#{bindings.description.inputValue}"
                    label="#{bindings.description.label}"
                    required="#{bindings.description.mandatory}"
                    columns="#{bindings.description.displayWidth}">
        <af:validator binding="#{bindings.description.validator}"/>
      </af:inputText>
    </af:panelForm>
    <af:commandButton actionListener="#{bindings.createProducts.execute}"
                      text="createProducts"
                      disabled="#{!bindings.createProducts.enabled}"/>
  </h:form>
</afh:body>

Note that each inputText component's required attribute is bound to the mandatory property of its associated binding. The EL expression evaluates to whether or not the attribute on the object to which it is bound can be null.

When you create an ADF Model validation rule for an attribute, JDeveloper adds the validation rule to the attribute binding, which in turn references the associated validation bean and provides the needed property values for the validation to run. Example 12-3 shows the page definition code created if you add a Length validation rule to the productDescription attribute setting the maximum size for the attribute to 20.

Example 12-3 Page Definition Validation Rule

<attributeValues id="description" IterBinding="variables"
                 ApplyValidation="true">
  <LengthValidationBean xmlns="http://xmlns.oracle.com/adfm/validation"
             OnAttribute="createProducts_description"
             DataType="CHARACTER" CompareType="LESSTHAN"
             ResId="description_Rule_0" Inverse="false"
             CompareLength="20"/>
  <AttrNames>
     <Item Value="createProducts_description"/>
  </AttrNames>
</attributeValues>

12.3.3 What Happens at Runtime

When the user submits the page, the ADF Faces validate() method first checks for a submitted value if the required attribute of a component is true. If the value is null or a zero-length string, the component is invalidated. At this point, what happens depends on whether or not client-side validation is enabled.

If client-side validation is enabled, an error message is placed in the queue. If there are other validators registered on the component, they are not called at all, and the current page is redisplayed with a dialog displaying the error message.

Note:

JSF reference implementation validators are not run on the client side.

In Example 12-2, the image attribute is not required. However, all other columns are required, as set by the mandatory property. This is denoted in the web page by asterisk icons. Figure 12-2 shows the error message displayed if no data is entered for the product ID, and if client-side validation is enabled.

Figure 12-2 Client-Side Error for a Required Value

Error message displays in a popup dialog

If the submitted value is a non-null value or a string value of at least one character, the validation process continues and all validators on the component are called one at a time. Because the af:validator tag on the component is bound to the validator property on the binding, any validation routines set on the model are also accessed and executed at this time.

The process then continues to the next component. If all validations are successful, the Update Model Values phase starts and a local value is used to update the model. If any validation fails, the current page is redisplayed along with the error dialog.

When client-side validation is disabled, all validations are done on the server. First, the ADF Faces validation is performed during the Process Validations phase. If any errors are encountered, the values are invalidated and the associated messages are added to the queue in FacesContext. Once all validation is run on the components, control passes to the model layer, which runs the Validate Model Updates phase. As with the Process Validations phase, if any errors are encountered, the values are invalidated and the associated messages are added to the queue in FacesContext (for information on how errors other than validation or conversion are handled, see Section 12.8, "Handling and Displaying Exceptions in an ADF Application").

The lifecycle then jumps to the Render Response phase and redisplays the current page. ADF Faces automatically displays an error icon next to the label of any input component that generated an error, and it displays the associated messages below the input field. If there is a tip associated with the field, the error message displays below the tip. Figure 12-3 shows a server-side validation error.

Figure 12-3 Server-Side Validation Error

Error message displays below component

12.3.4 What You May Need to Know

You can both set the required attribute and use validators on a component. However, if you set the required attribute to true and the value is null or a zero-length string, the component is invalidated and any other validators registered on the component are not called.

This combination might be an issue if there is a valid case for the component to be empty. For example, if the page contains a Cancel button, the user should be able to click that button and navigate off the page without entering any data. To handle this case, you set the immediate attribute on the Cancel button's component to true. This attribute allows the action to be executed during the Apply Request Values phase, thus bypassing the validation whenever the action is executed.

12.4 Creating Custom JSF Validation

You can add your own validation logic to meet your specific business needs. If you need custom validation logic for a component on a single page, you can create a validation method on the page's backing bean. Creating the validation method on a backing bean is also useful when you need validation to access other fields on the page. For example, if you have separate date fields (month, day, year) and each has its own validator, users will not get an error if they enter February 30, 2005. Instead, a backing bean for the page can contain a validation method that validates the entire date.

If you need to create logic that will be reused by various pages within the application, or if you want the validation to be able to run on the client side, you should create a JSF validator class. You can then create an ADF Faces version, which will allow the validator to run on the client.

12.4.1 How to Create a Backing Bean Validation Method

When you need custom validation for a component on a single page, you can create a method that provides the needed validation on a backing bean.

To add a backing bean validation method:

  1. Insert the component that will require validation into the JSF page.

  2. In the visual editor, double-click the component to launch the Bind Validator Property dialog.

  3. In the Bind Validator Property dialog, enter or select the managed bean that will hold the validation method, or click New to create a new managed bean. Use the default method signature provided or select an existing method if the logic already exists.

    When you click OK in the dialog, JDeveloper adds a skeleton method to the code and opens the bean in the source editor.

  4. Add the needed validation logic. This logic should use javax.faces.validator.ValidatorException to throw the appropriate exceptions and javax.faces.application.FacesMessage to generate the corresponding error messages. For more information about the Validator interface and FacesMessage, see the Javadoc for javax.faces.validator.Validator and javax.faces.application.FacesMessage, or visit http://java.sun.com/.

12.4.2 What Happens When You Create a Backing Bean Validation Method

When you create a validation method, JDeveloper adds a skeleton method to the managed bean you selected. Example 12-4 shows the code JDeveloper generates.

Example 12-4 Managed Bean Code for a Validation Method

public void inputText_validator(FacesContext facesContext, 
                                 UIComponent uiComponent, Object object) {
        // Add event code here...
}

The SREdit page in the SRDemo application uses a validation method to ensure that the new date entered is not earlier than the original date. Example 12-5 shows the validation method on that page's backing bean.

Example 12-5 SREdit Date Validation Method

public void assignedDateValidator(FacesContext facesContext, 
                                  UIComponent uiComponent, 
                                  Object newValue) {
    //The new value is passed into us
    Timestamp newAssignedDate = (Timestamp)newValue;
 
    //Get the start date for the SR which is already bound on this screen 
    Timestamp requestDate = 
        (Timestamp)ADFUtils.getBoundAttributeValue(getBindings(), 
                                                   "requestDate");
    // Now compare and raise an error if the rule is broken
    if (newAssignedDate.compareTo(requestDate) < 0) 
        {   
          throw new ValidatorException(JSFUtils.getMessageFromBundle
           ("sredit.error.assignedBeforeStart", FacesMessage.SEVERITY_ERROR));
        }
 
 }

JDeveloper binds the validator attribute of the component to the backing bean's validation method using an EL expression. Example 12-6 shows the code JDeveloper adds to the SREdit page.

Example 12-6 JSF Code for a Custom Validation Method

<af:selectInputDate value="#{bindings.assignedDate.inputValue}"
                    label="#{bindngs.assignedDate.label}"
                    ...
                    validator="#{backing_SREdit.assignedDateValidator}">

Tip:

JDeveloper also adds an af:validator tag that is bound to the validator property of the associated binding. This allows the JSF lifecycle to access any ADF Model validation you may have set for the associated attribute. If you do not set any ADF Model validation, you may remove this binding.

When the form containing the input component is submitted, the method to which the validator attribute is bound is executed.

12.4.3 How to Create a Custom JSF Validator

Creating a custom validator requires writing the business logic for the validation by creating a Validator implementation that contains a method overriding the validate method of the Validator interface, and then registering the custom validator with the application. You can also create a tag for the validator, or you can use the af:validator tag and nest the custom validator as a property of that tag.

You can then create a client-side version of the validator. ADF Faces client-side validation works in the same way that standard validation works on the server, except that JavaScript is used on the client: JavaScript validator objects can throw ValidatorExceptions, and they support the validate() method.

Note:

If the JavaScript form.submit() function is called, the ADF Faces support for client-side validation is bypassed. ADF Faces provides a submitForm() method that you can use instead, or you can use the autoSubmit attribute on ADF Faces input components.

To create a custom JSF validator:

  1. Create a Java class that implements the javax.faces.validator.Validator interface. The implementation must contain a public no-args constructor, a set of accessor methods for any attributes, and a validate method that overrides the validate method of the Validator interface.

    Alternatively, you can implement the javax.faces.FormatValidator interface, which has accessor methods for setting the formatPatterns attribute. This attribute specifies the acceptable patterns for the data entered into the input component. For example, if you want to validate the pattern of a credit card number, you create a formatPatterns attribute for the allowed patterns. The implementation must contain a constructor, a set of accessor methods for any attributes, and a validate method that overrides the validate method on the Validator interface.

    For both interfaces, the validate method takes the FacesContext instance, the UI component, and the data to be validated. For example:

    public void validate(FacesContext facesContext, 
                         UIComponent uiComponent, 
                         Object object) {
    ..
    }
    

    For more information about these classes, refer to the Javadoc or visit http://java.sun.com/.

  2. Add the needed validation logic. This logic should use javax.faces.validator.ValidatorException to throw the appropriate exceptions and javax.faces.application.FacesMessage to generate the corresponding error messages. For more information about the Validator interface and FacesMessage, see the Javadoc for javax.faces.validator.Validator and javax.faces.application.FacesMessage, or visit http://java.sun.com/.

    Note:

    To allow the page author to configure the attributes from the page, you need to create a tag for the validator. See step 5 for more information. If you don't want the attributes configured on the page, then you must configure them in this implementation class.
  3. If your application saves state on the client, make your custom validator implementation implement the Serializable interface, or implement the StateHolder interface, and the saveState(FacesContext) and restoreState(FacesContext, Object) methods of StateHolder. For more information, see the Javadoc for the StateHolder interface of the javax.faces.component package.

  4. Register the validator in the faces-config.xml file.

    • Open the faces-config.xml file and select the Overview tab in the editor window. The faces-config.xml file is located in the <View_Project>/WEB-INF directory.

    • In the window, select Validators and click New. Click Help or press F1 for additional help in registering the validator.

  5. Optionally create a tag for the validator that sets the attributes for the class. You create a tag by adding an entry for the tag in the application's tag library definition file (TLD). To do so:

    • Open or create a TLD for the application. For more information about creating a TLD, visit http://java.sun.com/.

    • Define the validator ID and class as registered in the faces-config.xml file.

    • Define any properties or attributes as registered in that configuration file.

    Note:

    If you do not create a tag for the validator, you must configure any attributes in the Validator implementation.

To create a client-side version of the validator:

  1. Write a JavaScript version of the validator, passing relevant information to a constructor.

  2. Implement the interface oracle.adf.view.faces.validator.ClientValidator, which has two methods. The first method is getClientScript(), which returns an implementation of the JavaScript Validator object. The second method is getClientValidation(), which returns a JavaScript constructor that is used to instantiate an instance of the validator.

For a complete example of how to add client-side validation to a validator implementation, see "Client-Side Converters and Validators" in Development Guidelines for Oracle ADF Faces Applications.

To use a custom validator on a JSF page:

  • To use a custom validator that has a tag on a JSF page, you need to manually nest it inside the component's tag.

    Example 12-7 shows a custom validator nested inside an inputText component. Note that the tag attributes are used to provide the values for the validator's properties that were declared in the faces-config.xml file.

Example 12-7 A Custom Validator Tag on a JSF Page

<h:inputText id="empnumber" required="true">
  <hdemo:emValidator emPatterns="9999|9 9 9 9|9-9-9-9" />
</h:inputText>

To use a custom validator without a custom tag:

To use a custom validator without a custom tag, you must nest the validator's ID (as configured in faces-config.xml file) inside the af:validator tag.

  1. From the Structure window, right-click the input component for which you want to add validation, and choose Insert inside <component> > ADF Faces Core > Validator.

  2. Select the validator's ID from the dropdown list and click OK.

    JDeveloper inserts code on the JSF page that makes the validator ID a property of the validator tag.

Example 12-8 shows the code on a JSF page for a validator using the af:validator tag.

Example 12-8 A Custom Validator Nested Within a Component on a JSF Page

<af:inputText id="empnumber" required="true">
  <af:validator validatorID="emValidator"/>
</af:inputText>

12.4.4 What Happens When You Use a Custom JSF Validator

When you use a custom JSF validator, the application accesses the validator class referenced in either the custom tag or the af:validator tag and executes the validate method. This method accesses the data from the component in the current FacesContext and executes logic against it to determine if it is valid. If the validator has attributes, those attributes are also accessed and used in the validation routine. Like standard validators, if the custom validation fails, associated messages are placed in the message queue in FacesContext.

12.5 Adding Conversion

A web application can store data of many types (such as int, long, date) in the model layer. When viewed in a client browser, however, the user interface has to present the data in a manner that can be read or modified by the user. For example, a date field in a form might represent a java.util.Date object as a text string in the format pattern mm/dd/yyyy. When a user edits a date field and submits the form, the string must be converted back to the type that is required by the application. Then the data is validated against any rules and conditions.

When you create an inputText component by dropping an attribute that is of a type for which there is a converter, JDeveloper automatically adds that converter's tag as a child of the input component. This tag invokes the converter, which will convert the String entered by the user back into the type expected by the object.

The JSF standard converters, which handle conversion between Strings and simple data types, implement the javax.faces.convert.Converter interface. The supplied JSF standard converter classes are:

Table 12-5 shows the converters provided by ADF Faces.

Table 12-5 ADF Faces Converters

Converter Tag Name Description

ColorConverter

af:convertColor

Converts java.lang.String objects to java.awt.Color objects. You specify a set of color patterns as an attribute of the converter.

DateTimeConverter

af:convertDateTime

Converts java.lang.String objects to java.util.Date objects. You specify the pattern and style of the date as attributes of the converter.

NumberConverter

af:convertNumber

Converts java.lang.String objects to java.lang.Number objects. You specify the pattern and type of the number as attributes of the converter.


As with validators, the ADF Faces converters are also run on the client side unless client-side validation is explicitly disabled in the adf-faces-config.xml file.

Note:

JSF reference implementation converters are not run on the client-side

In addition to JavaScript-enabled converters for color, date, and number, ADF Faces also provides JavaScript-enabled converters for input text fields that are bound to any of these Java types:

Unlike the converters listed in Table 12-5, the JavaScript-enabled converters are automatically used whenever needed. They do not have associated tags that can be nested in the component.

12.5.1 How to Use Converters

Whenever you drop an attribute from the Data Control Palette for which there is an ADF Faces converter, JDeveloper automatically adds the converter to the input component. You can also manually insert a converter.

To add ADF Faces converters that have a tag:

  1. In the Structure window, right-click the component for which you'd like to add a converter.

  2. In the context menu, choose Insert inside <UI component> > ADF Faces Core to insert an ADF Faces converter or JSF Core to insert a JSF converter.

  3. Choose a converter tag (for example, ConvertDateTime).

  4. In the Property Inspector, set values for the attributes, including any messages for conversion errors. For additional help, right-click any of the attributes and choose Help.

    ADF Faces lets you customize the detail portion of a conversion error message. By setting a value for an XxxMessageDetail attribute, where Xxx is the conversion error type (for example, convertDateMessageDetail), ADF Faces displays the custom message instead of a default message, if conversion fails.

12.5.2 What Happens When You Create Input Fields Using the Data Control Palette

When you use the Data Control Palette to create input fields that are of a type supported by a converter, JDeveloper automatically provides ADF Faces conversion code on the JSF page by:

  • Adding an af:messages tag as a child of the body tag. By default the globalOnly attribute is set to false, and the message and text attributes are not set. For more information, see Section 12.7, "Displaying Error Messages".

  • Adding a converter tag as a child of the input component.

    By default, the pattern attribute is bound to the format property of the associated binding. The format property determines how the String is formatted. For example, for the convertNumber converter, it might determine whether decimals are used. This binding evaluates to the format property as it is set on the data control itself.

For example, if you drop the prodId attribute from the findAllProducts method as an inputText component, JDeveloper automatically adds the convertNumber converter as a child of the input component, as shown in Example 12-9.

Example 12-9 Converter Tag in a JSF Page

<af:inputText value="#{bindings.productId.inputValue}"
              label="#{bindings.productId.label}"
              required="#{bindings.productId.mandatory}"
              columns="#{bindings.productId.displayWidth}">
  <af:validator binding="#{bindings.productId.validator}"/>
  <f:convertNumber groupingUsed="false"
                   pattern="#{bindings.productId.format}"/>
</af:inputText>

12.5.3 What Happens at Runtime

When the user submits the page containing converters, the ADF Faces validate() method calls the converter's getAsObject() method to convert the string value to the required object type. When there isn't an attached converter and if the component is bound to a bean property in the model, then JSF automatically uses the converter that has the same data type as the bean property. If conversion fails, the submitted value is marked as invalid and JSF adds an error message to a queue that is maintained by FacesContext. If conversion is successful and there are no validators attached to the component, the converted value is stored as a local value that is later used to update the model.

12.6 Creating Custom JSF Converters

You can create your own converters to meet your specific business needs. As with creating custom JSF validators, you can create custom JSF converters that run on the server side, and then also create a JavaScript version that can run on the client side. However, unlike creating custom validators, you can create only converter classes. You cannot add a method to a backing bean to provide conversion.

12.6.1 How to Create a Custom JSF Converter

Creating a custom converter requires writing the business logic for the conversion by creating an implementation of the Converter interface that contains methods overriding the getAsObject and getAsString methods of the Converter interface, and then registering the custom converter with the application. You then use the f:converter tag and nest the custom converter as a property of that tag, or you can use the converter attribute on the input component to bind to that converter.

You can also create a client-side version of the converter. ADF Faces client-side converters work in the same way standard JSF conversion works on the server, except that JavaScript is used on the client: JavaScript converter objects can throw ConverterExceptions, and they support the getAsObject and getAsString methods.

Note:

If the JavaScript form.submit() function is called, the ADF Faces support for client-side conversion is bypassed. ADF Faces provides a submitForm() method that you can use instead, or you can use the autoSubmit attribute on ADF Faces input components.

To create a custom JSF converter:

  1. Create a Java class that implements the javax.faces.converter.Converter interface. The implementation must contain a public no-args constructor, a set of accessor methods for any attributes, and getAsObject and getAsString methods, which override the same methods of the Converter interface.

    The getAsObject method takes the FacesContext instance, the UI component, and the String to be converted to a specified object. For example:

    public Object getAsObject(FacesContext context, 
                            UIComponent component, 
                            java.lang.String value){
    ..
    }
    

    The getAsString method takes the FacesContext instance, the UI component, and the object to be converted to a String. For example:

    public String getAsString(FacesContext context, 
                            UIComponent component, 
                            Object value){
    ..
    }
    

    For more information about these classes, refer to the Javadoc or visit http://java.sun.com/.

  2. Add the needed conversion logic. This logic should use javax.faces.converter.ConverterException to throw the appropriate exceptions and javax.faces.application.FacesMessage to generate the corresponding error messages. For more information about the Converter interface and FacesMessage, see the Javadoc for javax.faces.converter.Converter and javax.faces.application.FacesMessage, or visit http://java.sun.com/.

  3. If your application saves state on the client, make your custom converter implementation implement the Serializable interface, or implement the StateHolder interface, and the saveState(FacesContext) and restoreState(FacesContext, Object) methods of StateHolder. For more information, see the Javadoc for the StateHolder interface of javax.faces.component package.

  4. Register the converter in the faces-config.xml file.

    • Open the faces-config.xml file and select the Overview tab in the editor window. The faces-config.xml file is located in the <View_Project>/WEB-INF directory.

    • In the window, select Converters and click New. Click Help or press F1 for additional help in registering the converter.

To create a client-side version of the converter:

  1. Write a JavaScript version of the converter, passing relevant information to a constructor.

  2. Implement the interface oracle.adf.view.faces.converter.ClientConverter, which has two methods. The first method is getClientScript(), which returns an implementation of the JavaScript Converter object. The second method is getClientConversion(), which returns a JavaScript constructor that is used to instantiate an instance of the converter.

For a complete example of how to add client-side conversion to a converter implementation, see "Client-Side Converters and Validators" in Development Guidelines for Oracle ADF Faces Applications.

To use a custom converter on a JSF page:

  • Bind your converter class to the converter attribute of the input component.

Example 12-10 shows a custom converter referenced by the converter attribute of an inputText component.

Example 12-10 A Custom Converter on a JSF Page

<af:inputText value="#{bindings.name.inputValue}"
              label="#{bindings.name.label}"
              required="#{bindings.name.mandatory}"
              columns="#{bindings.name.displayWidth}"
              converter="srdemo.MyConverter">
</af:inputText>

Note:

If a custom converter is registered in an application under a class for a specific data type, whenever a component's value references a value binding that has the same type as the custom converter object, JSF will automatically use the converter of that class to convert the data. In that case, you don't need to use the converter attribute to register the custom converter on a component, as shown in the following code snippet:
<h:inputText value="#{myBean.myProperty}"/>

where myProperty has the same type as the custom converter.

12.6.2 What Happens When You Use a Custom Converter

When you use a custom converter, the application accesses the converter class referenced in the converter attribute, and executes the getAsObject or getAsString method as appropriate. These methods access the data from the component in the current FacesContext and execute the conversion logic.

12.7 Displaying Error Messages

By default, ADF Faces validation and conversion run on the client side. When a component's data fails validation, an alert dialog displays an error message for the component. You do not need to do any additional work to have client-side error messages display in this way. Figure 12-4 shows the message displayed when data is not entered for an input component that has a required attribute set to true.

Figure 12-4 A Client-Side Error Message

An error message displays in a pop-up dialog.

ADF Faces provides default text for messages displayed when validation or conversion fails. You can replace the default messages with your own messages by setting the text on the xxxMessageDetail attributes of the validator or converter (such as convertDateMessageDetail or notInRangeDetailMessage), or by binding those attributes to a resource bundle using an EL expression. For more information about using resource bundles, see Section 14.4.1, "How to Internationalize an Application".

When you use the Data Control Palette to create input components, JDeveloper inserts the af:messages tag at the top of the page. This tag can display all error messages in the queue for any validation that occurs on the server side, in a box offset by color. If you choose to turn off client-side validation for ADF Faces, those error messages are displayed along with any ADF Model error messages. ADF Model messages are shown first. Messages are shown both within the af:messages tag and with the associated components.

Figure 12-5 shows the error message for an ADF Model validation rule, which states that the description is too long, along with an error message for an ADF Faces component required attribute violation.

Figure 12-5 Displaying Server-Side Error Messages With the ADF Faces Messages Tag

Messages display at the top of the page and below components

12.7.1 How to Display Server-Side Error Messages on a Page

You can display server-side error messages in a box at the top of a page using the af:messages tag. When you drop any item from the Data Control Palette onto a page as an input component, JDeveloper automatically adds this tag for you.

To display error messages in an error box:

  1. In the Structure window, select the af:messages tag.

    This tag is created automatically whenever you drop an input widget from the Data Control Palette. However, if you need to insert the tag, simply insert the following code within the afh:body tag:

    <afh:body>
      <af:messages globalOnly="false" />
      ...
    </afh:body>
    
  2. In the Property Inspector set the following attributes:

    • globalOnly: By default ADF Faces displays global messages (i.e., messages that are not associated with components) followed by individual component messages. If you wish to display only global messages in the box, set this attribute to true. Component messages will continue to display with the associated component.

    • message: The main message text that displays just below the message box title, above the list of individual messages.

    • text: The text that overrides the default title of the message box.

  3. Ensure that client-side validation has been disabled. If you do not disable client-side validation, the alert dialog will display if there are any ADF Faces validation errors, as the server-side validation will not have taken place.

    Tip:

    To disable client-side validation, add the <client-validation-disabled> element in adf-faces-config.xml and set it to true.

12.7.2 What Happens When You Choose to Display Error Messages

When a conversion or validation error occurs on an ADF Faces input component, the component creates a FacesMessage object and adds it to a message queue on the FacesContext instance. During the Render Response phase, the message associated with the validator or converter is displayed using the built-in message display attribute for the ADF Faces input component. This attribute displays the detail error message next to the component. The message is also displayed by the optional af:messages tag, which displays all summary messages in a message box.

12.8 Handling and Displaying Exceptions in an ADF Application

Exceptions thrown by any part of an ADF application are also handled and displayed on the JSF page. By default, all exceptions thrown in the application are caught by the binding container. When an exception is encountered, the binding container routes the exception to the application's active error handler, which by default is the DCErrorHandlerImpl class. The reportException(BindingContainer, Exception) method on this class passes the exception to the binding container to process. The binding container then processes the exception by placing it on the exception list in a cache.If exceptions are encountered on the page during the page lifecycle, (for example, during validation), they are also caught by the binding container and cached, and are additionally added to FacesContext.

During the Prepare Render phase, the ADF lifecycle executes the reportErrors(context) method. This method is implemented differently for each view technology. By default, the reportErrors method on the FacesPageLifecycle class:

You can customize this default framework behavior. For example, you can create a custom error handler for exceptions, or you can change how the lifecycle reports exceptions. You can also customize how a single page handles exceptions.

12.8.1 How to Change Exception Handling

You can change the default exception handling by extending the default error handler, DCErrorHandlerImpl. Doing so also requires that you create a custom ADF lifecycle class that will call the new error handler during the Prepare Model phase.

You can also create a custom ADF lifecycle class to change how the lifecycle reports errors by overriding the reportErrors method.

If you only want to change how exceptions are created for a single page, you can create a lifecycle class just for that page.

To create a custom error handler:

  1. Create a class that extends the DCErrorHandlerImpl class.

  2. In the new class, override the public void reportException(DCBindingContainer, Exception) method.

    Example 12-11 shows the SRDemoErrorHandler Class that the SRDemo application uses to handle errors.

    Example 12-11 SRDemoErrorHandler Class

    public class SRDemoErrorHandler extends DCErrorHandlerImpl{
      /**
       * Constructor for custom error handler.
       * 
       * @param setToThrow should exceptions throw or not
       */
      public SRDemoErrorHandler(boolean setToThrow) {
        super(setToThrow);
      }
      public void reportException(DCBindingContainer bc, Exception ex) {
         //Force JboException's reported to the binding layer to avoid
         //printing out the JBO-XXXXX product prefix and code.
        disableAppendCodes(ex);
        super.reportException(bc, ex);
      }
      
      private void disableAppendCodes(Exception ex) {
        if (ex instanceof JboException) {
          JboException jboEx = (JboException) ex;
          jboEx.setAppendCodes(false);
          Object[] detailExceptions = jboEx.getDetails();
          if ((detailExceptions != null) && (detailExceptions.length > 0)) {
            for (int z = 0, numEx = detailExceptions.length; z < numEx; z++) {
              disableAppendCodes((Exception) detailExceptions[z]);
            }
          }
        }
      }
    }
    
  3. Globally override the error handler. To do this, you must create a custom page lifecycle class that extends FacesPageLifecycle. In this class, you override the public void prepareModel(LifecycleContext) method, which sets the error handler. To have it set the error handler to the custom handler, have the method check whether or not the custom error handler is the current one in the binding context. If it is not, set it to be. (Because by default the ADFBindingFilter always sets the error handler to be DCErrorHandlerImpl, your method must set it back to the custom error handler.) You must then call super.prepareModel.

    Example 12-12 shows the prepareModel method from the frameworkExt.SRDemoPageLifecycle class that extends the FacesPageLifecycle class. Note that the method checks whether or not the error handler is an instance of the SRDemoErrorHandler, and if it is not, it sets it to the new error handler.

    Example 12-12 PrepareModel Method

    public void prepareModel(LifecycleContext ctx) {
        if (!(ctx.getBindingContext().getErrorHandler() instanceof 
              SRDemoErrorHandler)) {
            ctx.getBindingContext().setErrorHandler(new SRDemoErrorHandler(true));
        }
        super.prepareModel(ctx);
    }
    
  4. You now must create a new Phase Listener that will return the custom lifecycle. See the procedure "To create a new phase listener:" later in the section.

To customize how the lifecycle reports errors:

  1. Create a custom page lifecycle class that extends FacesPageLifecycle.

  2. Override the public void reportErrors(PageLifecycleContext) method to customize the display of error messages.

    Example 12-13 shows the reportErrors method and associated methods in the frameworkExt.SRDemoPageLifecycle class that extends the FacesPageLifecycle class to change how the errors are reported.

    Example 12-13 ReportErrors Method in the SRDemoPageLifecycle Class

    public void reportErrors(PageLifecycleContext ctx) {
        DCBindingContainer bc = (DCBindingContainer)ctx.getBindingContainer();
        if (bc != null) {
            List<Exception> exceptions = bc.getExceptionsList();
            if (exceptions != null) {
                Locale userLocale = 
                    ctx.getBindingContext().getLocaleContext().getLocale();
                /*
                 * Iterate over the top-level exceptions in the exceptions list and
                 * call addError() to add each one to the Faces errors list
                 * in an appropriate way.
                 */
                for (Exception exception: exceptions) {
                    try {
                        translateExceptionToFacesErrors(exception, userLocale, 
                                                        bc);
                    } catch (KnowErrorStopException stop) {
                        FacesContext fctx = FacesContext.getCurrentInstance();
                        fctx.addMessage(null, 
                                        JSFUtils.getMessageFromBundle
                                        (stop.getMessage(),
                                         FacesMessage.SEVERITY_FATAL));
                        break;
                    }
                }
            }
        }
    }
     
    protected void translateExceptionToFacesErrors(Exception ex, Locale locale, 
                                                       BindingContainer bc) throws
                                                  KnowErrorStopException {
        List globalErrors = new ArrayList();
        Map attributeErrors = new HashMap();
        processException(ex, globalErrors, attributeErrors, null, locale);
        int numGlob = globalErrors.size();
        int numAttr = attributeErrors.size();
        if (numGlob > 0) {
            for (int z = 0; z < numGlob; z++) {
                String msg = (String)globalErrors.get(z);
                if (msg != null) {
                    JSFUtils.addFacesErrorMessage(msg);
                }
            }
        }
        if (numAttr > 0) {
            Iterator i = attributeErrors.keySet().iterator();
            while (i.hasNext()) {
                String attrNameKey = (String)i.next();
                /*
                 * Only add the error to show to the user if it was related
                 * to a field they can see on the screen. We accomplish this
                 * by checking whether there is a control binding in the current
                 * binding container by the same name as the attribute with
                 * the related exception that was reported.
                 */
                ControlBinding cb = 
                    ADFUtils.findControlBinding(bc, attrNameKey);
                if (cb != null) {
                    String msg = (String)attributeErrors.get(attrNameKey);
                    if (cb instanceof JUCtrlAttrsBinding) {
                        attrNameKey = ((JUCtrlAttrsBinding)cb).getLabel();
                    }
                    JSFUtils.addFacesErrorMessage(attrNameKey, msg);
                }
            }
        }
    }
     
    /**
    * Populate the list of global errors and attribute errors by
    * processing the exception passed in, and recursively processing
    * the detail exceptions wrapped inside of any oracle.jbo.JboException
    * instances.
    *
    * If the error is an attribute-level validation error, we can tell
    * because it should be an instanceof oracle.jbo.AttrValException
    * For each attribute-level error, we retrieve the name of the attribute
    * in error by calling an appropriate getter method on the exception
    * object which exposes this information to us. Since attribute-level
    * errors could be wrapping other more specific attribute-level errors
    * that were the real cause (especially due to Bundled Exception Mode).
    * We continue to recurse the detail exceptions and we only consider
    * relevant to report the exception that is the most deeply nested, since
    * it will have the most specific error message for the user. If multiple
    * exceptions are reported for the same attribute, we simplify the error
    * reporting by only reporting the first one and ignoring the others.
    * An example of this might be that the user has provided a key value
    * that is a duplicate of an existing value, but also since the attribute
    * set failed due to that reason, a subsequent check for mandatory attribute
    * ALSO raised an error about the attribute's still being null.
    *
    * If it's not an attribute-level error, we consider it a global error
    * and report each one.
    *
    * @param ex the exception to be analyzed
    * @param globalErrs list of global errors to populate
    * @param attrErrs map of attrib-level errors to populate, keyed by attr name
    * @param attrName attribute name of wrapping exception (if any)
    * @param locale the user's preferred locale as determined by the browser
    */
    private void processException(Exception ex, List globalErrs, Map attrErrs, 
                                  String attrName, 
                                  Locale locale) throws KnowErrorStopException {
        /*
         * Process the exceptions
         * Start with some special cases that are known bad situations where we
         * need to format some useful advice rather than just parroting the
         * exception text
         */
        if (ex instanceof EJBException) {
            String msg = ex.getLocalizedMessage();
            if (msg == null) {
                msg = firstLineOfStackTrace(ex, true);
            }
            Exception causeEx = ((EJBException)ex).getCausedByException();
            if (causeEx instanceof TopLinkException) {
                int toplinkErrorCode = 
                    ((TopLinkException)causeEx).getErrorCode();
                switch (toplinkErrorCode) {
                case 7060:
                    {
                        throw new KnowErrorStopException("srdemo.topLinkError.7060");
                    }
                case 4002:
                    {
                        throw new KnowErrorStopException("srdemo.topLinkError.4002");
                    }
                }
            }
            globalErrs.add(msg);
        } else if (ex instanceof AdapterException){
            AdapterException causeEx = ((AdapterException)ex);
            
            int err = Integer.parseInt( causeEx.getErrorCode());
            switch (err){
                case 40010:{
                    throw new KnowErrorStopException("srdemo.urlDCError.40010");
                }
                case 29000:{
                    throw new KnowErrorStopException("srdemo.urlDCError.29000");
                }
                default:{
                    throw new KnowErrorStopException("srdemo.urlDCError.other");
                }
            }
            
        } else if (!(ex instanceof JboException)) {
            String msg = ex.getLocalizedMessage();
            if (msg == null) {
                msg = firstLineOfStackTrace(ex, true);
            }
            globalErrs.add(msg);
            /*
           * If this was an unexpected error, print out stack trace
           */
            reportUnexpectedException(ex);
            return;
        }
        if (ex instanceof AttrValException) {
            AttrValException ave = (AttrValException)ex;
            attrName = ave.getAttrName();
            Object obj = attrErrs.get(attrName);
            /*
             * If we haven't already recorded an error for this attribute
             * and if it's a leaf detail, then log it as the first error for
             * this attribute. If there are details, then we'll recurse
             * into the details below. But, in the meantime we've recorded
             * What attribute had the validation error in the attrName
             */
            Object[] details = ave.getDetails();
            if (obj != null) {
                /*
                 * We've already logged an attribute-validation error for this
                 * attribute, so ignore subsequent attribute-level errors
                 * for the same attribute. Note that this is not ignoring
                 * NESTED errors of an attribute-level exception, just the
                 * second and subsequent PEER errors of the first attribute-level
                 * error. This means the user might receive errors on several
                 * different attributes, but for each attribute we're choosing
                 * to tell them about just the first problem to fix.
                 */
                return;
            } else {
                /*
                 * If there aren't any further, nested details, then log first error
                 */
                if ((details == null) || (details.length == 0)) {
                    attrErrs.put(attrName, ave.getLocalizedMessage(locale));
                }
            }
        }
        JboException jboex = (JboException)ex;
        /*
         * It is a JboException so recurse into the exception tree
         */
        Object[] details = jboex.getDetails();
        /*
         * We only want to report Errors for the "leaf" exceptions
         * So if there are details, then don't add an errors to the lists
         */
        if ((details != null) && (details.length > 0)) {
            for (int j = 0, count = details.length; j < count; j++) {
                processException((Exception)details[j], globalErrs, attrErrs, 
                                 attrName, locale);
            }
        } else {
            /*
             * Add a new Error to the collection
             */
            if (attrName == null) {
                String errorCode = jboex.getErrorCode();
                globalErrs.add(jboex.getLocalizedMessage(locale));
            } else {
                attrErrs.put(attrName, jboex.getLocalizedMessage(locale));
            }
            if (!(jboex instanceof ValidationException)) {
                reportUnexpectedException(jboex);
            }
        }
    }
    /**
    * Prints the stack trace for an unexpected exception to standard out.
    *
    * @param ex The unexpected exception to report.
    */
    protected void reportUnexpectedException(Exception ex) {
        ex.printStackTrace();
    }
    /**
    * Picks off the exception name and the first line of information
    * from the stack trace about where the exception occurred and
    * returns that as a single string.
    */
    private String firstLineOfStackTrace(Exception ex, boolean logToError) {
       if (logToError) {
            ex.printStackTrace(System.err);
        }
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        ex.printStackTrace(pw);
        LineNumberReader lnr = 
            new LineNumberReader(new StringReader(sw.toString()));
        try {
            String lineOne = lnr.readLine();
            String lineTwo = lnr.readLine();
            return lineOne + " " + lineTwo;
        } catch (IOException e) {
            return null;
        }
    }
    
  3. You now must create a new phase listener that will return the custom lifecycle.

To create a new phase listener:

  1. Extend the ADFPhaseListener class.

  2. Override the protected PageLifecycle createPageLifecycle () method to return a new custom lifecycle.

    Example 12-14 shows the createPageLifecycle method in the frameworkExt.SRDemoADFPhaseListener class.

    Example 12-14 CreatePageLifecycle Method in SRDemoADFPhaseListener

    public class SRDemoADFPhaseListener extends ADFPhaseListener {
      protected PageLifecycle createPageLifecycle() {
        return new SRDemoPageLifecycle();
      }
    }
    
  3. Register the phase listener in the faces-config.xml file.

    • Open the faces-config.xml file and select the Overview tab in the editor window. The faces-config.xml file is located in the <View_Project>/WEB-INF directory.

    • In the window, select Life Cycle and click New. Click Help or press F1 for additional help in registering the converter.

To override exception handling for a single page:

  1. Create a custom page lifecycle class that extends the FacesPageLifecycle class.

  2. Override the public void reportErrors(PageLifecycleContext) method to customize the display of error messages. For an example of overriding this method, see the procedure "To customize how the lifecycle reports errors:" earlier in this section.

  3. Open the page definition for the page. In the Structure window, select the page definition node. In the Property Inspector, enter the new class as the value for the ControllerClass attribute.

12.8.2 What Happens When You Change the Default Error Handling

When you create your own error handler, the application uses that class instead of the DCErrorHandler class. Because you created and registered a new lifecycle, that lifecycle is used for the application. This new lifecycle instantiates your custom error handler.

When an error is subsequently encountered, the binding container routes the error to the custom error handler. The reportException(BindingContainer, Exception) method then executes.

If you've overridden the reportErrors method in the custom lifecycle class, then during the Prepare Render phase, the lifecycle executes the new reportErrors(context) method.