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:
Creating the Extended Controller (Java class) that inherits from the standard controller
Writing the custom code to track expense report withdrawals
Rebuilding the project to compile the extended class
Uploading the extended controller to the Oracle
Java_ToplocationPersonalizing the page to use the extended controller
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
xxprefix (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.
Open the New Gallery:
Right-click on the project XXTrackExpenseReportsCO
Select New
Select Java Class:
Navigate to General → Java Class
Click OK
Configure the Java Class:
Class Name:
ExpenseReportCOEx(theExsuffix indicates "Extended")Package: Select the custom package (e.g.,
oracle.apps.xxap.oie.webui)
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.
Select the Standard Controller Path:
Ensure the path matches the standard controller's location:
oracle/apps/ap/oie/webui
Complete the Class Creation:
Review the Java class values
Click OK to create the class
Result: The extended Java class
ExpenseReportCOExis 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:
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
| Section | Purpose |
|---|---|
public class ExpenseReportCOEx extends TrackExpenseReportsCO | Extends 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:
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
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.
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.
Locate the Compiled Class:
In your JDev project, locate the compiled class file
ExpenseReportCOEx.class
Copy the File to Java_Top:
Copy the
ExpenseReportCOEx.classfile to the Oracle serverPlace it in the appropriate directory under
Java_Top:$JAVA_TOP/oracle/apps/xxap/oie/webui/
Note: The
xxapprefix indicates this is a custom (extended) object. The rest of the path matches the standard controller's package structure.
Verify the Upload:
Confirm the file is in the correct location:
$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.
Open the Standard OAF Page:
Navigate to the Expense Reports Home Page in the application
Click the Personalize Link:
Click the Personalize Page link (highlighted at the top of the page)
This opens the Personalization page
Navigate to the Page Header Personalization:
Go to the Update Personalization section under the header of the OAF page
Set the Extended Controller:
Locate the CO (Controller) field
Enter the fully qualified name of your extended controller:
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.
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
Return to the Expense Reports Home Page:
Navigate back to the standard page
Test the Withdraw Button:
Locate an expense report and click the Withdraw button
Confirm the withdrawal when prompted
Verify the Custom Logic:
Check the custom table
XX_EXPENSE_WITHDRAW_HISTORYfor a new recordVerify the record contains:
The correct report ID
The current user ID
The withdrawal date
The withdrawal count
Verification Query:
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:
┌─────────────────────────────────────────────────────────────────┐
│ 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
| Mechanism | Purpose | How It Works |
|---|---|---|
| Controller Extension | Adds custom logic to a standard controller | New class "extends" the standard controller |
| Substitution | Replaces the standard controller with the custom one | Personalization sets the CO field to the custom controller |
| Deployment | Makes the custom controller available at runtime | Upload 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
| Practice | Description |
|---|---|
| Always Call Super | Always call super.processRequest() and super.processFormRequest() |
| Use Diagnostics | Use writeDiagnostics() for debugging and logging |
| Handle Exceptions | Wrap database calls in try-catch blocks |
| Use Bind Variables | Use bind variables in PL/SQL calls to prevent SQL injection |
| Test in Development | Always test Controller Extensions in a development environment |
| Document Personalizations | Keep a record of all personalizations for future reference |
| Verify After Patches | Re-verify extensions after applying Oracle patches |
| Use Version Control | Store extended controller files in a version control system |
Troubleshooting Common Issues
| Issue | Possible Cause | Solution |
|---|---|---|
| Custom code not executing | Personalization not applied | Verify the CO field is set correctly |
| Page breaks after extension | Super method not called | Ensure super.processFormRequest() is called |
| Class not found at runtime | Class not uploaded to Java_Top | Verify the class file is in the correct location |
| PL/SQL procedure fails | Wrong procedure name or parameters | Verify the procedure exists and the parameters match |
| Wrong report ID passed | Decryption issue | Verify the parameter name in getDecryptedParameter |
| Compilation errors | Missing imports | Add imports for OracleCallableStatement, SQLException, etc. |
| Personalization overwritten | Patch applied | Re-apply the personalization after patches |
Required Import Statements
Ensure the following imports are included in your extended controller:
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.OracleCallableStatementimport is specific to the Oracle ADF Business Components layer.
Summary
Completing the Controller Extension involves the following key steps:
Create the Extended Controller: A new Java class that extends the standard
TrackExpenseReportsCOWrite the Custom Code: Add logic to track expense report withdrawals
Create the PL/SQL Procedure: Build the custom procedure that inserts history records
Rebuild the Project: Compile the extended controller
Upload to Java_Top: Copy the compiled class to the server
Personalize the Page: Set the CO field to the extended controller
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 behaviorThe Personalization CO field must contain the fully qualified name of the extended controller
The class file must be uploaded to
Java_Topin the correct package directoryThe 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
Part 1: Controller Extension in OAF — https://docodi.blogspot.com/p/30a-controller-extension-in-oaf-how-to.html
Part 2: Controller Extension in OAF (Continued) — https://docodi.blogspot.com/p/30b-controller-extension-in-oaf-how-to.html
Oracle E-Business Suite Documentation: OA Framework Developer's Guide
Oracle E-Business Suite Documentation: Personalization Guide
Oracle JDeveloper Documentation: Available in the
jdevdocfolderRelated Guides: OAF VO Extension, Creating a Hello World Page in OAF, Creating an LOV in OAF, Creating a Search Page in OAF
No comments:
Post a Comment