Extending a Helping HandBy Steve Muench
Add custom features to improve team productivity.
There are two main benefits to framework-based development using Oracle Application Development Framework (Oracle ADF). The first is productivity. You stand on the shoulders of the declaratively configured functionality Oracle ADF provides and focus primarily on implementing features unique to your own company’s requirements. The second is extensibility. When the framework doesn’t offer a feature you need out of the box, or when its default behavior doesn’t work the way you’d hoped, you are free to extend the framework to work as you please. By reading this column, you’ll learn the basics of implementing framework extensions for Oracle ADF business components by studying three simple examples.
To begin, download the starter workspace and ensure that you’re using the studio edition of the Oracle JDeveloper 188.8.131.52.0 (production) release, available as a free download onOracle Technology Network (OTN). Start by extracting the contents of the o39frame.zip file and opening the FrameworksMayJun2009.jws workspace in Oracle JDeveloper. The Model project in the workspace defines an Emp entity object, EmpView view object, and HRModule application module. The FwkExtensions project will contain the extensions you make to the framework.
Before proceeding, adjust the properties of the scott connection in the Application Resources zone of the Application Navigator until you can successfully test a connection to a SCOTT schema. If necessary, use the provided CreateDeptEmpTables.sql script to create the EMP table and EMP_TABLE_SEQ sequence used by the examples.
Setting Up Your Framework Extensions Project
Framework extension classes exploit Java inheritance to add, augment, or override functionality provided by Oracle ADF business components base classes in the oracle.jbo.server package; the starter workspace already contains two such extension classes. To access these classes, expand the FwkExtensions project, the Application Sources folder, and the oramag.model.fwkext package in the Application Navigator. Double-click the CustomEntityImpl class to open it in the code editor. You’ll see that CustomEntityImpl extends the base framework class for entity objects ( EntityImpl ). Now, double-click CustomViewObjectImpl to open it in the code editor. You’ll see that it extends the base framework class for view objects ( ViewObjectImpl ). In the source code for CustomViewObjectImpl , note the red, wavy underline beneath the ViewObjectImpl class name in the extends clause. Place your cursor over the underlined class name, and a tooltip will describe the problem: “Type ‘ViewObjectImpl’ not found.” Expand the import section of the source file, by clicking the small plus sign in the code editor’s left margin. Note that the correct import statement for oracle.jbo.server.ViewObjectImpl is there, but there is still a problem. Hold your mouse over that import line, which itself is wavy underlined, and the tooltip will state, “Import oracle.jbo.server.ViewObjectImpl not found.” These error messages indicate that the project is missing a reference to the library that contains the underlying Oracle ADF business components runtime classes you are trying to extend.
To correct the problem, double-click the FwkExtensions project node in the Application Navigator. When the Project Properties dialog box appears, select the Libraries and Classpath section in the tree at the left. To add the required library, click the Add Library button. When the Add Library dialog box appears, select BC4J Runtime and click OK . Repeat these steps to add a second library, Oracle JDBC , which is also required for a framework extensions project. Then click OK to dismiss the Project Properties dialog box. In the code editor, you should notice immediately that the wavy underlines in both the CustomViewObjectImpl and the CustomEntityObjectImpl tabs disappear, now that the project correctly references the libraries on which it depends.
Next, create a skeleton framework extension class for application modules. Right-click the FwkExtensions project node, and choose New from the context menu. In the New Gallery , select the General category on the left, select the Java Class item in that category on the right, and then click OK . When the Create Java Class dialog box appears, enter the class name CustomApplicationModuleImpl ; leave the default package name, oramag.model.fwkext ; and then click the Browse button next to the Extends field to pick the class that your new class will extend. When the Class Browser dialog box appears, erase any text that may already appear in the Match Class Name field and type the three uppercase letters AMI . The list displays a subset of classes as you type, showing you only those classes whose “CamelCapped” names include the capital letter A , followed by capital M , followed by capital I . (Of course, you could also have typed ApplicationModuleI to shorten the list, but knowing the trick about CamelCapped searching saves you typing.) In the short Matching Classes list, select ApplicationModuleImpl in the oracle.jbo.server package, and then click OK . Finally, uncheck the Generate Default Constructor check box and click OK to create the skeleton framework extension class. Your framework extensions project now has an extension class for entity objects, view objects, and application modules. Although these aren’t the only framework classes you can override, they are the three most common, and the steps for extending and using the other base classes are the same.
Enabling Optional SQL Tracing
The first simple framework extension you create will add a feature to application module components that will enable Oracle Database SQL tracing if a particular Java system property is set in the runtime environment. This will simplify the process of identifying SQL statements your application executes that might be slow, requiring an additional index or a change to the WHERE clause to improve performance. You’ll accomplish this by overriding the prepareSession() method of the application module to add custom code that will run when the application module is first used in a user session.
To begin, ensure that the tab containing the CustomApplicationModuleImpl source code file is active by clicking your cursor somewhere in the code editor. Note that, in addition to the main Oracle JDeveloper IDE toolbar, the code editor has its own toolbar of useful options for working with source code. Pass the cursor over the toolbar buttons, reading the tooltips that appear, until you find the Override Methods button, and then click it. When the Override Methods dialog box appears, note that the Methods list shows you all the methods you can override. Begin typing the initial letters of the method named prepareSession until it scrolls into view, click the check box next to the prepareSession(Session) method, and then click OK to override this method. Note that Oracle JDeveloper includes the appropriate method signature along with the call to super.prepareSession() , which invokes the built-in functionality inherited from the superclass. You can add custom code before or after this line to augment the default functionality of your application modules.
Next, expand the Resources folder in the FwkExtensions project and double-click the ApplicationModuleCode.txt file, which contains the lines of custom code to add. In the editor, select all the lines in this file (Ctrl-A) and copy them to the clipboard with Ctrl-C. Click the tab containing the CustomApplicationModuleImpl source file to activate it in the editor, place the cursor at the end of the super.prepareSession() ; line (after the semicolon), and press Enter to open a blank line below it. Paste the contents of the clipboard into the source file, by pressing Ctrl-V. A few wavy underlines will appear, indicating that import statements are missing. To include the required imports, place your cursor over the first underlined exception name, SQLStmtException, and note that a tooltip displays “Import oracle.jbo.SQLStmtException (Alt-Enter).” Press the Alt-Enter key combination to automatically include the necessary import statement. Then repeat these steps by placing your cursor over the next wavy-underlined exception name, SQLException , and use Alt-Enter to include the import for that exception as well.
Having taken care of the missing import warnings, study the lines of code for a moment. They say that if the Java system property named sqltrace is set to the value true then execute ALTER SESSION SET SQL_TRACE TRUE on (the connection associated with) the current transaction. The try/catch block handles the exception ( SQLStmtException ) that might occur if the current user has not been granted the ALTER SESSION privilege, and it prints out a helpful message letting developers know to ask their DBA for the privilege.
Putting a Framework Extension Class to Work
For a framework extension class to be used at runtime, you need to reference it as the base class of the component that should make use of it. So, next you’ll configure the HRModule application module in the Model project to use the CustomApplicationModuleImpl extension class you just created. Start by configuring the Model project to depend on the FwkExtensions project. This enables the components in the Model project to reference classes that the FwkExtensions project contains. Double-click the Model project in the Application Navigator, and when the Project Properties dialog box appears, select the Dependencies section on the left. Click the pencil icon above the Dependent Projects and Archives list to edit its contents. When the Edit Dependencies dialog box appears, expand the FwkExtensions.jpr node in the Projects list and check the check box next to the Build Output node. This indicates that the FwkExtensions project’s compiled classes should be added to the Model project’s dependencies. Click OK to return to the Project Properties dialog box. While you’re here, because the framework extension class’s functionality depends on the presence of a Java system property named sqltrace , add a Java command-line option to set the value of this property accordingly. Select the Run/Debug/Profile section at the left, ensure that the Default run configuration is selected in the list on the right, and then click Edit. On the Launch Settings panel of the Edit Run Configuration dialog box that appears, find the Java Options field and type -Dsqltrace=true . (The format is a hyphen followed immediately by an uppercase D [it’s case-sensitive!] immediately followed by the param=value for the system parameter you want to set.) Click OK to continue and OK again to dismiss the Project Properties dialog box.
With this project dependency registered, configure the HRModule component to use the framework extension class. In the Application Navigator, in the Model folder, under oramag .model , double-click HRModule to open its editor. Select the Java page of the editor, and click the pencil icon to the right of the Java Classes heading to edit the Java Classes settings. When the Select Java Options dialog box appears, click the Classes Extend button to see the current settings for the classes this component extends. When the Override Base Classes dialog box appears, click the Browse button next to the Object field to pick the new base class for this application module. In the Find Superclass dialog box, click in the Match Class field and begin typing the first letters of the class name CustomApplicationModuleImpl . After the first few letters, your extension class will appear in the list, so select it and then click OK to use this base class, as shown in Figure 1. Click OK to dismiss the Override Base Classes dialog box and then OK again to dismiss the Select Java Options dialog box.
At this point, right-click HRModule in the Application Navigator and choose Run to test it. After clicking Connect in the Select Business Components Configuration dialog box, you should see either a “SQL_TRACE Enabled” message or a “Failed to enable” message in the Oracle JDeveloper log window, depending on whether your DBA has granted the SCOTT database user the ALTER SESSION privilege. Either way, you can see that the framework extension class’s functionality is being exercised, so exit from the business components browser to continue our journey.
Improving View Object Query Logging
Next, you’ll add some code to the framework extension class for view objects to improve the logging of queries so you can better understand which view objects’ queries are executing and how often. Start by double-clicking the ViewObjectCode.txt file (in the FwkExtensions project, in the Resources folder) and copying all the lines it contains to the clipboard. Then click the tab for the CustomViewObjectImpl.java file, click the blank line between the opening and closing braces, and paste the lines (from the ViewObjectCode.txt file) into this class. The first block of code you pasted is an overridden create() method that concatenates an additional comment onto the end of the view object’s optional query optimizer hint when the component is created at runtime. This extra SQL comment includes the name of the view object, such as EmpView1 , and the fully qualified name of its definition, such as oramag.model.EmpView , which can help in analyzing the SQL trace output for your application, revealing which view object produced each SQL statement in the trace. The second code block is an overridden bindParametersForCollection() method that will log a message to the console whenever the view object’s query (or the query for calculating its estimated row count) is executed. This additional diagnostic can help you realize when queries might be executing more frequently than you expected.
Now configure the EmpView view object in the Model project to use this framework extension class. Double-click the EmpView view object to open its editor. As before, select the Java page of the editor and click the pencil icon to edit its Java Classes settings. Click the Classes Extend button and click the Browse button next to the Object field. In the Find Superclass dialog box, click in the Match Class field and begin typing the first letters of the class name CustomViewObjectImpl . After the first few letters, your extension class will appear in the list, so select it and then click OK to use this extension class. Click OK to dismiss the Override Base Classes dialog box and OK again to dismiss the Select Java Options dialog box. Now, if you test the HRModule application module again by running it as you did before, in the Oracle ADF business component browser, when you double-click the EmpView1 view object instance, you should see the following message in the log window:
## Executed [VO=EmpView1, Def=oramag.model.EmpView].
Simplifying Sequence-Valued Primary Keys
As a final framework extension flourish, you’ll implement a way to simplify the population of a sequence-valued primary key at entity-instance-creation time. In the FwkExtensions project, in the Resources folder, double-click the EntityObjectCode.txt file and copy all the lines it contains to the clipboard. Then click the tab for the CustomEntityImpl.java file, click the blank line between the opening and closing braces, and paste the lines into this class. The first block of code you pasted is a helper method named nextVal() that accepts the name of a database sequence and returns the next value from that sequence. The second block of code is an overridden create() method that loops through the attribute definitions for the primary key attribute(s) of the entity object, looking for any attribute that has a custom property named SequenceName defined on it. For each key attribute that has a SequenceName property, the extension code interprets that property’s value as the name of a database sequence, and then uses the helper function above to populate the attribute value with the next value from that sequence.
Now configure the Emp entity object in the Model project to use this framework extension class. Double-click the Emp entity object to open its editor. Using the now-familiar steps, select the Java page of the editor and click the pencil icon to edit its Java Classes settings. Click the Classes Extend button, and use the class browser for the Row field to select the fully qualified name of the CustomEntityImpl class. If the class name doesn’t appear in the list, cancel the dialog box and try again, making sure to click the Browse button next to the Row field and not the Collection or Definition field. Once you’ve selected the desired base class for the entity row, click OK to dismiss the Override Base Classes dialog box and OK again to dismiss the Select Java Options dialog box. Although the application module and view object you configured above used the framework extension class without requiring any custom Java class of their own, note, on the Java panel for the Emp entity object, that it does have a custom EmpImpl Java class. Click the blue hyperlink next to the Entity Object Class label to open the EmpImpl class in the code editor. Note that when you changed the entity row base class above to use CustomEntityImpl , Oracle JDeveloper automatically modified the custom EmpImpl class to extend it. So as we’ve seen, your framework extension classes can easily add functionality, whether or not your Oracle ADF business components require any custom Java code of their own.
The last step is to configure a custom property named SequenceName on the Empno attribute of the Emp entity object. This property acts as a clue to the overridden create( method behavior inherited from the framework extension class that you want the Empno attribute value populated from a sequence. So, click the Emp.xml tab to activate the entity object editor. Select the Attributes page of the editor, select the Empno attribute in the table, and click the green plus-sign toolbar menu in the Custom Properties section header below. When the toolbar menu appears, select Non-translatable Property , because the value of the custom property will not need to change, based on the current user’s locale. Double-click in the Property value cell in the new row of the custom property table that appears. Change the value of this cell to be SequenceName . Double-click the corresponding Value cell, and change its value to be EMP_TABLE_SEQ . Then save all your changes. To test out your new declaratively configured sequence-populated primary key functionality, run the HRModule again (right-click HRModule , and choose Run ) in the business components browser (click Connect in the Select Business Components Configuration dialog box when it appears). Double-click the EmpView1 view object instance, and then click the Insert a new row toolbar button. In the new row that appears, note that the value of the Empno field defaults to the next value in the EMP_TABLE_SEQ sequence, as shown in Figure 2.
With these three examples under your belt, you’re now ready to apply framework extension classes in your next Oracle ADF project. As usual, we’ve had only enough space to explore the simplest examples here, but they should have demonstrated how easy it is to add your own custom, declaratively configurable features to the Oracle ADF framework. In time you’ll come to appreciate the true power of framework extensions, when you begin sharing the generic code with other team members and reusing a common layer of Oracle ADF framework extensions across multiple applications. For more information on how to package your extensions as a reusable library and how your team can configure Oracle JDeveloper to use your framework extension classes by default at design time, see sections 33.1, “Globally Extending ADF Business Components Functionality,” and 33.2, “Creating a Layer of Framework Extensions,” of the Oracle Fusion Developer’s Guide for Oracle ADF 11g.
Steve Muench is a consulting product manager for Oracle JDeveloper and an Oracle ACE. Since 1990 he has developed and supported Oracle tools and XML technologies and continues to evangelize them. Muench coauthored Oracle ADF Developer’s Guide for Forms/4GL Developers (Oracle, 2006), wrote Building Oracle XML Applications (O’Reilly Media, 2000), and shares tips and tricks on OTN and in his “Dive into ADF” blog.