30b. Controller Extension in OAF: How to Add Code in Controller Extension on Button Click Event – Part 2

 

Part 2: OAF Controller Extension — How to Add OAF Code on a Button Click Event

Introduction

This is the second and final part of the guide on Controller Extension in Oracle Application Framework (OAF). In Part 1, we identified the standard controller behind the Expense Reports Home Page, downloaded it, and created the OA Workspace and Project required for the extension.

In this part, we will complete the Controller Extension by:

  1. Creating the Extended Controller (Java class) that inherits from the standard controller

  2. Writing the custom code to track expense report withdrawals

  3. Rebuilding the project to compile the extended class

  4. Uploading the extended controller to the Oracle Java_Top location

  5. Personalizing the page to use the extended controller

  6. Testing the custom logic on the Withdraw button click

Part 1 Reference: https://docodi.blogspot.com/p/30a-controller-extension-in-oaf-how-to.html


Prerequisites

Before proceeding, ensure the following are complete from Part 1:

  • The standard controller (TrackExpenseReportsCO) has been identified

  • The controller has been downloaded from the Oracle server to your JDev environment

  • The OA Workspace and Project have been created with the xx prefix (e.g., xxap)

  • The folder structure matches the standard controller's package path


Test Case Recap

Business Requirement: Whenever a user withdraws an expense report, the system must create a history record in a custom table that tracks:

  • How many times the expense report has been withdrawn

  • The date of each withdrawal

  • The user who performed the withdrawal

We will implement this by extending the standard controller and adding custom code to the Withdraw button click event.


Step 1: Create the Extended Controller

Now we create a new Java class that extends the standard controller. This extended controller will inherit all behavior from the standard controller and allow us to add custom logic.

  1. Open the New Gallery:

    • Right-click on the project XXTrackExpenseReportsCO

    • Select New

  2. Select Java Class:

    • Navigate to General → Java Class

    • Click OK

  3. Configure the Java Class:

    • Class Name: ExpenseReportCOEx (the Ex suffix indicates "Extended")

    • Package: Select the custom package (e.g., oracle.apps.xxap.oie.webui)

  4. Specify the Standard Controller to Extend:

    • Under the Extends option, click the Browse button

    • Navigate to and select the standard controller TrackExpenseReportsCO

    • This is the controller we downloaded in Part 1

Important: The Extends property is what makes this a Controller Extension. The new class inherits all methods and behavior from the standard controller, and we can override or add methods as needed.

  1. Select the Standard Controller Path:

    • Ensure the path matches the standard controller's location:

      text
      oracle/apps/ap/oie/webui
  2. Complete the Class Creation:

    • Review the Java class values

    • Click OK to create the class

Result: The extended Java class ExpenseReportCOEx is now created. We will write our custom code in this class.


Step 2: Write the Custom Code

Open the extended class ExpenseReportCOEx.java and add the following code:

java
package oracle.apps.xxap.oie.webui;

import java.sql.SQLException;

public class ExpenseReportCOEx extends TrackExpenseReportsCO {

    public ExpenseReportCOEx() {
    }

    public void processRequest(OAPageContext paramOAPageContext, OAWebBean paramOAWebBean)
    {
        super.processRequest(paramOAPageContext, paramOAWebBean);
    }

    public void processFormRequest(OAPageContext paramOAPageContext, OAWebBean paramOAWebBean)
    {
        Number ReportHeader;
        
        // Check if the Withdraw event was fired
        if ("WithdrawReport".equals(paramOAPageContext.getParameter("event")))
        {
            paramOAPageContext.writeDiagnostics(this, "Clicked on withdraw ", 1);
        }

        // Check if the user confirmed the withdrawal
        if (paramOAPageContext.getParameter("CommitWithdraw") != null)
        {
            paramOAPageContext.writeDiagnostics(this, "Selected Yes to withdraw ", 1);
            
            OAApplicationModule am = (OAApplicationModule)paramOAPageContext.getApplicationModule(paramOAWebBean);
            OADBTransaction txn = am.getOADBTransaction();
            
            // Call the custom PL/SQL procedure to insert the withdrawal history
            String str = "BEGIN xx_insert_withdrawactions.insertrow(P_Header_id => :1); END;";
            OracleCallableStatement Callstmt = (OracleCallableStatement)txn.createCallableStatement(str, 1);
            String Result = "";
            
            try {
                // Get the report header ID from the encrypted parameter
                ReportHeader = new Number(paramOAPageContext.getDecryptedParameter("ReportHeaderId"));
                String RptHdrId = "" + ReportHeader;
                
                Callstmt.setString(1, RptHdrId);
                Callstmt.execute();
                
                paramOAPageContext.writeDiagnostics(this, "xx_insert_withdrawactions proc completed Successfully ", 2);
                Callstmt.close();
            }
            catch (SQLException sqle) {
                paramOAPageContext.writeDiagnostics(this, "Exception in calling sqlstatement  " + sqle, 2);
                try {
                    Callstmt.close();
                } catch (Exception e) {
                    paramOAPageContext.writeDiagnostics(this, "Exception in calling sqlstatement  " + sqle, 2);
                }
            }
        }
        
        // Always call the super method to preserve standard behavior
        super.processFormRequest(paramOAPageContext, paramOAWebBean);
    }
}

Code Explanation

SectionPurpose
public class ExpenseReportCOEx extends TrackExpenseReportsCOExtends the standard controller
processRequest()Overrides the standard processRequest and calls super to preserve behavior
if ("WithdrawReport".equals(...))Detects when the Withdraw button is clicked
if (paramOAPageContext.getParameter("CommitWithdraw") != null)Detects when the user confirms the withdrawal
OAApplicationModule am = ...Retrieves the Application Module
OADBTransaction txn = am.getOADBTransaction();Gets the database transaction object
String str = "BEGIN xx_insert_withdrawactions.insertrow(...)"Defines the PL/SQL call to insert the history record
OracleCallableStatement Callstmt = ...Creates a callable statement for the PL/SQL procedure
paramOAPageContext.getDecryptedParameter("ReportHeaderId")Retrieves the report header ID from the encrypted parameter
Callstmt.setString(1, RptHdrId);Binds the report header ID to the PL/SQL parameter
Callstmt.execute();Executes the PL/SQL procedure
catch (SQLException sqle)Handles any SQL exceptions
super.processFormRequest(...)Critical: Calls the standard controller's processFormRequest to preserve standard behavior

Important: Always call super.processFormRequest() at the end of your custom code. This ensures that the standard functionality (e.g., the actual withdrawal) still executes. Without this, the standard behavior would be lost.


Step 3: Create the Custom PL/SQL Procedure

The custom code calls a PL/SQL procedure xx_insert_withdrawactions.insertrow. You need to create this procedure (or package) in the database.

Example PL/SQL Package:

sql
CREATE OR REPLACE PACKAGE xx_insert_withdrawactions AS
    PROCEDURE insertrow(P_Header_id IN NUMBER);
END xx_insert_withdrawactions;
/

CREATE OR REPLACE PACKAGE BODY xx_insert_withdrawactions AS

    PROCEDURE insertrow(P_Header_id IN NUMBER) IS
        v_count NUMBER;
    BEGIN
        -- Get the current withdrawal count for this report
        SELECT NVL(MAX(withdraw_count), 0) + 1
        INTO v_count
        FROM xx_expense_withdraw_history
        WHERE report_id = P_Header_id;

        -- Insert a new history record
        INSERT INTO xx_expense_withdraw_history (
            history_id,
            report_id,
            withdrawn_by,
            withdrawn_date,
            withdraw_count,
            creation_date,
            created_by
        ) VALUES (
            xx_expense_withdraw_history_s.NEXTVAL,
            P_Header_id,
            fnd_global.user_id,
            SYSDATE,
            v_count,
            SYSDATE,
            fnd_global.user_id
        );

        COMMIT;
    END insertrow;

END xx_insert_withdrawactions;
/

Note: Adjust the table name, sequence name, and column names to match your actual custom table structure.


Step 4: Rebuild the Project

  1. Rebuild the Project:

    • Right-click on the Java class ExpenseReportCOEx

    • Select Rebuild (or right-click on the project and select Rebuild)

Note: Rebuilding compiles the Java class and ensures there are no syntax errors. Fix any compilation errors before proceeding.

  1. Verify Compilation:

    • Check the compilation output for any errors or warnings

    • Ensure the class compiles successfully


Step 5: Upload the Extended Controller to Java_Top

Now we deploy the extended controller to the Oracle server's Java_Top location.

  1. Locate the Compiled Class:

    • In your JDev project, locate the compiled class file ExpenseReportCOEx.class

  2. Copy the File to Java_Top:

    • Copy the ExpenseReportCOEx.class file to the Oracle server

    • Place it in the appropriate directory under Java_Top:

      text
      $JAVA_TOP/oracle/apps/xxap/oie/webui/

Note: The xxap prefix indicates this is a custom (extended) object. The rest of the path matches the standard controller's package structure.

  1. Verify the Upload:

    • Confirm the file is in the correct location:

      text
      $JAVA_TOP/oracle/apps/xxap/oie/webui/ExpenseReportCOEx.class

Step 6: Personalize the Page to Use the Extended Controller

Now we use Personalization to tell the OAF framework to use the extended controller instead of the standard one.

  1. Open the Standard OAF Page:

    • Navigate to the Expense Reports Home Page in the application

  2. Click the Personalize Link:

    • Click the Personalize Page link (highlighted at the top of the page)

    • This opens the Personalization page

  3. Navigate to the Page Header Personalization:

    • Go to the Update Personalization section under the header of the OAF page

  4. Set the Extended Controller:

    • Locate the CO (Controller) field

    • Enter the fully qualified name of your extended controller:

      text
      oracle.apps.xxap.oie.webui.ExpenseReportCOEx

Important: The controller path must match the package and class name of your extended controller exactly. Any mismatch will cause the personalization to fail.

  1. Apply the Personalization:

    • Click Apply to save the personalization

Result: The OAF framework will now use your extended controller (ExpenseReportCOEx) instead of the standard controller (TrackExpenseReportsCO) for this page.


Step 7: Test the Controller Extension

  1. Return to the Expense Reports Home Page:

    • Navigate back to the standard page

  2. Test the Withdraw Button:

    • Locate an expense report and click the Withdraw button

    • Confirm the withdrawal when prompted

  3. Verify the Custom Logic:

    • Check the custom table XX_EXPENSE_WITHDRAW_HISTORY for a new record

    • Verify the record contains:

      • The correct report ID

      • The current user ID

      • The withdrawal date

      • The withdrawal count

Verification Query:

sql
SELECT history_id, report_id, withdrawn_by, withdrawn_date, withdraw_count
FROM xx_expense_withdraw_history
ORDER BY creation_date DESC;

Result: If the custom record appears in the table, the Controller Extension is working correctly.


How Controller Extension Works at Runtime

The following illustrates the complete runtime flow:

text
┌─────────────────────────────────────────────────────────────────┐
│                    STANDARD OAF PAGE                            │
│  1. Page loads with the standard controller                     │
│  2. Personalization redirects to the extended controller        │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                  EXTENDED CONTROLLER                            │
│  1. ExpenseReportCOEx extends TrackExpenseReportsCO             │
│  2. Inherits all standard behavior                              │
│  3. Adds custom logic for the Withdraw button                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    USER CLICKS WITHDRAW                         │
│  1. processFormRequest is invoked                               │
│  2. Detects "CommitWithdraw" parameter                          │
│  3. Retrieves the report header ID                              │
│  4. Calls the custom PL/SQL procedure                           │
│  5. Inserts a record into the custom history table              │
│  6. Calls super.processFormRequest() for standard behavior      │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    DATABASE                                     │
│  1. Custom record inserted into XX_EXPENSE_WITHDRAW_HISTORY     │
│  2. Standard withdrawal process also executed                   │
│  3. User sees confirmation message                              │
└─────────────────────────────────────────────────────────────────┘

Understanding the Three Key Mechanisms

MechanismPurposeHow It Works
Controller ExtensionAdds custom logic to a standard controllerNew class "extends" the standard controller
SubstitutionReplaces the standard controller with the custom onePersonalization sets the CO field to the custom controller
DeploymentMakes the custom controller available at runtimeUpload the compiled class to Java_Top

Key Insight: Controller Extension allows you to intercept and enhance standard page behavior. When a user clicks the Withdraw button, your custom code runs before the standard code (since you call super.processFormRequest() at the end), allowing you to add auditing, validation, or business logic without modifying Oracle's seeded code.


Best Practices

PracticeDescription
Always Call SuperAlways call super.processRequest() and super.processFormRequest()
Use DiagnosticsUse writeDiagnostics() for debugging and logging
Handle ExceptionsWrap database calls in try-catch blocks
Use Bind VariablesUse bind variables in PL/SQL calls to prevent SQL injection
Test in DevelopmentAlways test Controller Extensions in a development environment
Document PersonalizationsKeep a record of all personalizations for future reference
Verify After PatchesRe-verify extensions after applying Oracle patches
Use Version ControlStore extended controller files in a version control system

Troubleshooting Common Issues

IssuePossible CauseSolution
Custom code not executingPersonalization not appliedVerify the CO field is set correctly
Page breaks after extensionSuper method not calledEnsure super.processFormRequest() is called
Class not found at runtimeClass not uploaded to Java_TopVerify the class file is in the correct location
PL/SQL procedure failsWrong procedure name or parametersVerify the procedure exists and the parameters match
Wrong report ID passedDecryption issueVerify the parameter name in getDecryptedParameter
Compilation errorsMissing importsAdd imports for OracleCallableStatement, SQLException, etc.
Personalization overwrittenPatch appliedRe-apply the personalization after patches

Required Import Statements

Ensure the following imports are included in your extended controller:

java
import oracle.apps.fnd.framework.OAApplicationModule;
import oracle.apps.fnd.framework.OAException;
import oracle.apps.fnd.framework.server.OADBTransaction;
import oracle.apps.fnd.framework.webui.OAPageContext;
import oracle.apps.fnd.framework.webui.OAControllerImpl;
import oracle.apps.fnd.framework.webui.beans.OAWebBean;
import oracle.jbo.server.OracleCallableStatement;
import java.sql.SQLException;
import oracle.apps.fnd.framework.OAViewObject;
import oracle.apps.fnd.framework.OARow;

Note: The oracle.jbo.server.OracleCallableStatement import is specific to the Oracle ADF Business Components layer.


Summary

Completing the Controller Extension involves the following key steps:

  1. Create the Extended Controller: A new Java class that extends the standard TrackExpenseReportsCO

  2. Write the Custom Code: Add logic to track expense report withdrawals

  3. Create the PL/SQL Procedure: Build the custom procedure that inserts history records

  4. Rebuild the Project: Compile the extended controller

  5. Upload to Java_Top: Copy the compiled class to the server

  6. Personalize the Page: Set the CO field to the extended controller

  7. Test: Verify the custom logic executes on the Withdraw button click

Why Controller Extension Matters

Controller Extension is one of the most powerful customization techniques in Oracle E-Business Suite development because it:

  • Preserves Oracle Code: No modifications to seeded Oracle objects

  • Survives Upgrades: Customizations remain intact after patches and upgrades

  • Follows Standards: Uses Oracle's official extension mechanisms

  • Is Maintainable: Clear separation between standard and custom code

  • Enables Auditing: Allows you to track user actions on standard pages

  • Enforces Business Rules: Lets you add validations and controls to standard processes

Key Points to Remember

  • The super.processFormRequest() call is mandatory at the end of your custom code to preserve standard behavior

  • The Personalization CO field must contain the fully qualified name of the extended controller

  • The class file must be uploaded to Java_Top in the correct package directory

  • The PL/SQL procedure must exist in the database before the extension can call it

  • Diagnostics should be used liberally for debugging and logging

  • Exception handling is essential to prevent page crashes when database errors occur

By mastering Controller Extension, you gain the ability to enhance standard Oracle pages with custom logic, auditing, and business rules—without compromising the integrity of the Oracle E-Business Suite environment.


References

No comments:

Post a Comment