27
PRACTICAL JDAPI Harm H Verschuren, AMIS Services BV. Abstract Have you ever done a Forms migration to 9i or 10g WebForms almost totally by hand but wanted to have it performed more in an automated fashion? Or are you trying to improve the quality of your hand built WebForms during development time, by automating QA on the fmb-files and by using quality reports? Or are you trying to automate your post-generation steps after Oracle Designer generates a form? In the recent past, while migrating an application from 6i to 10g, we had to and we used the Java Design-time API (JDAPI) to successfully complete many of these error-prone, time-consuming and often really boring tasks. Many tips and techniques for using the JDAPI in the areas listed above are described and illustrated with demos in this paper. The JDAPI is a Java API for programmatically loading, creating, manipulating, compiling and saving Oracle Forms modules (forms, menus, pl/sql-libraries and object libraries) and it is distributed with Forms as part of Oracle Developer Suite (9i and 10g) and Oracle Application Server. We used the JDAPI to create a toolbox helping us in the topics described above. The toolbox includes tools for both simple and complex search and replace operations including regular expression searches, tools for complex reattaching of attached libraries, tools that add or replace objects and object properties with new ones and generated quality reports. Also, we integrated the Forms Migration Assistant (distributed part of the Oracle Developer Suite) in our Java code resulting in an extended migration tool. Our resulting toolbox almost migrated the whole application by itself. Also, the paper demonstrates how you could combine your own written JDAPI code with the Forms-to-XML Java API to generate quality reports based on XML and easily publish the reports using the Oracle XSQL servlet. Finally the paper describes how you could use the JDAPI in combination with Oracle Designer to automate post-generation actions. JDAPI basics Before I start on the more profound tasks that you can perform with the JDAPI, I will commence with the basics of programming Forms modules with JDAPI using JDeveloper. The Java Design-time API is a Java programming interface for programmatically loading, creating, manipulating and compiling Oracle Forms modules (Forms modules, Menu modules, Object Library modules and PL/SQL Library modules). It is built on top of the Forms C-API and implements the so-called facade pattern (see for example http://c2.com/cgi/wiki?FacadePattern ). JDAPI is a complementary tool to the Forms Builder and can be used to programmatically accomplish almost anything that can be achieved manually and visually using the Forms Builder. www.odtug.com ODTUG 2005

Practical Jd a Pi Paper

Embed Size (px)

Citation preview

Page 1: Practical Jd a Pi Paper

PRACTICAL JDAPIHarm H Verschuren, AMIS Services BV.

AbstractHave you ever done a Forms migration to 9i or 10g WebForms almost totally by hand but wanted to have it performed more in an automated fashion? Or are you trying to improve the quality of your hand built WebForms during development time, by automating QA on the fmb-files and by using quality reports? Or are you trying to automate your post-generation steps after Oracle Designer generates a form? In the recent past, while migrating an application from 6i to 10g, we had to and we used the Java Design-time API (JDAPI) to successfully complete many of these error-prone, time-consuming and often really boring tasks.

Many tips and techniques for using the JDAPI in the areas listed above are described and illustrated with demos in this paper. The JDAPI is a Java API for programmatically loading, creating, manipulating, compiling and saving Oracle Forms modules (forms, menus, pl/sql-libraries and object libraries) and it is distributed with Forms as part of Oracle Developer Suite (9i and 10g) and Oracle Application Server.

We used the JDAPI to create a toolbox helping us in the topics described above. The toolbox includes tools for both simple and complex search and replace operations including regular expression searches, tools for complex reattaching of attached libraries, tools that add or replace objects and object properties with new ones and generated quality reports. Also, we integrated the Forms Migration Assistant (distributed part of the Oracle Developer Suite) in our Java code resulting in an extended migration tool. Our resulting toolbox almost migrated the whole application by itself. Also, the paper demonstrates how you could combine your own written JDAPI code with the Forms-to-XML Java API to generate quality reports based on XML and easily publish the reports using the Oracle XSQL servlet. Finally the paper describes how you could use the JDAPI in combination with Oracle Designer to automate post-generation actions.

JDAPI basicsBefore I start on the more profound tasks that you can perform with the JDAPI, I will commence with the basics of programming Forms modules with JDAPI using JDeveloper. The Java Design-time API is a Java programming interface for programmatically loading, creating, manipulating and compiling Oracle Forms modules (Forms modules, Menu modules, Object Library modules and PL/SQL Library modules). It is built on top of the Forms C-API and implements the so-called facade pattern (see for example http://c2.com/cgi/wiki?FacadePattern). JDAPI is a complementary tool to the Forms Builder and can be used to programmatically accomplish almost anything that can be achieved manually and visually using the Forms Builder.

JDAPI cannot be used to program, interact or control running Forms applications because it is not a run-time API. Also, JDAPI cannot be used with Oracle Reports.

The advantage of using JDAPI is that it is possible to create utilities to perform actions on a large number of forms automatically.

Required software and documentationTo build and eventually run your JDAPI programs you need the following software installed on the machine you will program and run your JDAPI utilities. JDAPI is distributed in a single jar file, located in your Oracle home of your Oracle Development Suite 9i and 10g: $ORACLE_HOME\forms90\java\f90jdapi.jar ($ORACLE_HOME/forms90/java for UNIX/Linux systems). As said earlier, the JDAPI is build on top of the Forms C-API, therefore you must have both Oracle Forms Builder and Oracle Forms Builder installed. This means you cannot use JDAPI without an Forms installation either installed via the Developer Suite or via the Oracle 9i/10g Application Server.

The JDAPI is a Java API therefore you need a Java runtime environment. JDAPI requires at least a JRE or JDK 1.3 installed on the machine on which you program and run your JDAPI utilities. JDeveloper is an easy choice for a Java programming interface, as it is included in the Oracle Developers Suite.

www.odtug.com ODTUG 2005

Page 2: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Because JDAPI works with Java Native Interface (JNI) calls to communicate with the Oracle Forms stack, JDAPI does not impose any restrictions on the operating system. You can build or run your JDAPI utilities on both Windows and UNIX/Linux systems as long as you have a compliant Oracle Forms installed.

For completeness I mention that you will find two more JDAPI related jar files in your $ORACLE_HOME\forms90\java directory. The jar file f90xmltools.jar is the XML-Forms utility. This utility supports conversion of FMB files to an XML format, and vice versa. You can build reporting or even manipulation tools on this XML representation of your Forms. The other jar file, f90upgrade.jar, is the Oracle Forms Migration Assistant utility. Both utilities are built on top of the JDAPI.

The Java-API documentation of the jar files can be downloaded from OTN (UTL: http://otn.oracle.com/forms/help/). The source code is not available from Oracle, but you could - not quite legally - obtain the source code using a decompiler (for example jad – see below) to decompile the jar files. The JDAPI documentation is available at OTN1 or via the Forms Builder On Line Help feature.

Configuring JDAPI in JDeveloperIncorporating JDAPI into JDeveloper consist of some simple tasks:

1. First, you download the JDAPI documentation from the OTN web site and unpack the zip file to a convenient location on your hard disk (in figure 1 I unpacked it in C:\APPS\jar).

2. In the JDeveloper menu, go to Tools – Manage Libraries to add the JDAPI framework as a library. I made it a user library because I use it in a single project (not system wide), but you can make it a system library if you prefer. As shown in figure 1 I named the library Oracle Forms JDAPI.

Figure 1: Manage JDAPI Library into JDeveloper

3. Optionally download a Java decompiler, for instance jad (http://kpdus.tripod.com/jad) and use it to decompile the f90jdapi.jar. The location of the sources is entered in the Source Path field. Enter all paths as required and press OK when finished. Now you have JDAPI incorporated in your JDeveloper IDE.

4. To use JDAPI in your JDeveloper projects, create a new project or open an existing project and open the project properties window. Add the new library called Oracle Forms JDAPI from the available libraries list to the selected libraries list, as shown in figure 2.

1 http://www.oracle.com/technology/documentation/forms/902docs/jdapi9i.zip

www.odtug.com ODTUG 2005

Page 3: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Figure 2: Add Oracle Forms JDAPI to your Project

Now you have incorporated the JDAPI classes, API documentation and the sources into your JDeveloper project and you can start developing tools build on the JDAPI framework.

ArchitectureThe JDAPI is a single java package (oracle.forms.jdapi) containing a number of interfaces and classes representing all Forms objects. Class BaseAPI handles all JNI calls to the C-API Libraries. The JDAPI Session (see next paragraph) is handled through static methods of the Jdapi class. All JDAPI Forms object classes implement the JdapiObject interface through the abstract class BaseFormsObject. Figure 3 illustrates the JDAPI class diagram. The four known module types are each referenced by their concrete classes and one abstract super class, JdapiModule which comes in handy when you are writing generic code. For instance, method getAlerts() is specific for Forms modules and is therefore a member of class FormModule. The generic counterpart would be calling method getJdapiMetaObject()on the JdapiObject.

Object properties represent the child object lists of a given module object, for example, the Items and Triggers owned by a Block, and the respective accessor methods return iterators for the requested child list. JDAPI includes an iterator interface, JdapiIterator, which extends java.util.Iterator, and all its methods.

JDAPI uses constants (static values) to refer to Forms properties and objects: The class JdapiTypes contains a full set of Property Type ID constants (*_PTID) representing Forms object properties, and a set of Object Type ID constants (*_OTID) which represent the Forms Objects types themselves.

www.odtug.com ODTUG 2005

Page 4: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Figure 3: Java Class Diagram JDAPI Framework

JDAPI SessionEvery program you write using JDAPI starts and ends with the JDAPI Session. In essence the JDAPI Session is the memory-area in the JVM used to handle all work. During the JDAPI Session, the underlying Forms API is initialized. By default, this happens by lazy initialization. That means: as soon as the first functional API call is made. The Java code in example 1 shows this. Setting both Jdapi.setFailLibraryLoad() and Jdapi.setFailSubclassLoad() to true will suppress exceptions raised when attached libraries respectively sub classed objects are not found. Next a module is loaded into the JDAPI Session. This is the line that will trigger initialization of the Forms API. When the module is loaded into the Session, any Java class in the JVM can call the static method Jdapi.getModules(),that returns a list of cached modules via a JdapiIterator object.

www.odtug.com ODTUG 2005

Page 5: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Also, each module is uniquely identified by its path on the file system; that is, where it was opened from or last saved to, obtained by calling JdapiModule.getAbsolutePath(). Attempting to reopen an already open module will simply return the open module.

/** * Example1: * This program loads the modules specified through the module names (or wildcard) at the line * 1st command line parameter into the JDAPI Session and writes the module names to the console. */public class Example1 { // generic part public static void main(String[] args) { Jdapi.setFailLibraryLoad()= true; Jdapi.setFailSubclassLoad()= true; try { for (int i=0 ; i < args.length ; i++ ) { JdapiModule.openModule(args[i]); } invoke(); } finally { Jdapi.shutdown(); } } // specific part private static void invoke() { for (JdapiIterator mods=Jdapi.getModules() ; mods.hasNext() ; ) { JdapiModule mod = (JdapiModule)mods.next(); System.out.println("Module name : " + mod.getName()); } }}

Example 1 – MyFirst JDAPI program: opening Forms Modules and printing their names

After the program has completed it is good practice to clean up the JDAPI Session. This is performed by calling Jdapi.shutdown(). This static method will destroy all Forms modules present in the Session by implicitly calling Jdapi.destroyAllModule() and cleans up all Session resources and underlying native objects.

In the example I have separated the specific code into a private method (invoke()) to clarify the fact that you can totally isolate the handling of the JDAPI Session from the tools you write. In fact, as you see later in this paper, the generic code moves to a base class and the invoke method moves to tool-specific subclasses.

Writing generic codeThe next example shows a Java program that iterates though the Items in a Form Block and writes the names to the console. As you can see, the nested for-loops (I could have used while loops all the same) do not make the code very readable/maintainable, leave alone reusable. And it gets more complicated as you also want to iterate over the triggers per item or iterate over the block level triggers and relations per block.

www.odtug.com ODTUG 2005

Page 6: Practical Jd a Pi Paper

Practical JDAPI Verschuren

/** * Example2: * This program loads the modules entered by the module names (or wildcard) at the 1st command line * parameter into the JDAPI Session and iterates over the blocks and items in the blocks, printing * all form-, block- and item names to the console. */public class Example2 { public static void main(String[] args) { try { for (int i=0 ; i < args.length ; i++ ) { JdapiModule.openModule(args[i]); } invoke(); } finally { Jdapi.shutdown(); } } private static void invoke() { for (JdapiIterator modules = Jdapi.getModules() ; modules.hasNext() ; ) { FormModule mod = (FormModule)(JdapiModule)modules.next(); System.out.println("Form module = " + mod.getName()); for (JdapiIterator blocks = mod.getBlocks() ; blocks.hasNext() ; ) { Block blk = (Block)blocks.next(); System.out.println("\tBlock = " + blk.getName()); for (JdapiIterator items = blk.getItems() ; items.hasNext() ; ) { Item itm = (Item)items.next(); System.out.println("\t\tItem = " + itm.getName()); } } } }}

Example 2 – Open one or more FMB files and traverse through all blocks and items

However, JDAPI allows you to write completely generic code for handling objects of different types. As seen in Figure 3, all classes representing Forms objects implement the JdapiObject interface. This interface can be used as the generic starting point. When getting and setting the object properties generically you will need to use the generic accessor, getXxxProperty() and setXxxProperty(), where Xxx equals String, Boolean or Integer. Also you have to deal with metadata and the respective classes (JdapiMetaData, JdapiMetaObject and JdapiMetaProperty).

JdapiMetaData: holds all the metadata for Forms objects. It contains a complete description of the metadata for each Forms object. This class is used to return the properties available for a given Forms object.

JdapiMetaObject: represents the metadata (for example, an Item, Attached Library, Program Unit). It describes the object in terms of its properties. This class can be obtained from JdapiMetaData by calling getJdapiMetaObject().

JdapiMetaProperty: represents the metadata for a given property. It is obtained by iterating over the JdapiMetaObject.

Example 3 shows how you could use the “meta”-classes by recursively traversing a module. The example prints the form-, block- and item name, in a (block.item) fashion, to the console.

www.odtug.com ODTUG 2005

Page 7: Practical Jd a Pi Paper

Practical JDAPI Verschuren

/** * Example 3: * Write all Forms objects to the console by traversing the object tree. */public class Example3 { public static void main(String[] args) { try { for (int i=0 ; i < args.length ; i++ ) { JdapiModule.openModule(args[i]); } invoke(); } finally { Jdapi.shutdown(); } } private static void invoke() { for (JdapiIterator mods=Jdapi.getModules() ; mods.hasNext() ; ) { traverse((JdapiObject)mods.next()); } } /** * Generically traverse the Jdapi object hierarchy. Method travInvoke can be subclassed by * your specific code to process the objects in the hierarchy. * @param jo Object (Form, Library, Menu and ObjectLibrary). */ protected static void traverse(JdapiObject jo) { travInvoke(jo); JdapiIterator childProps = jo.getJdapiMetaObject().getChildObjectMetaProperties(); // iterate through the child object properties while(childProps.hasNext()) { JdapiMetaProperty mprop = (JdapiMetaProperty)childProps.next(); JdapiIterator childObjects = jo.getChildObjectProperty(mprop.getPropertyId()); // won't enter this loop if the object iterator is empty if(childObjects != null) { while (childObjects.hasNext()) { // finally, recurse traverse((JdapiObject)childObjects.next()); } } } } // specific part private static void travInvoke(JdapiObject jo) { if (jo.getTypeId() == JdapiTypes.FORM_MODULE_OTID) { System.out.println("Form module = " + jo.getQualifiedName(false)); } else if (jo.getTypeId() == JdapiTypes.BLOCK_OTID) { System.out.println("\tBlock = " + jo.getQualifiedName(false)); } else if (jo.getTypeId() == JdapiTypes.ITEM_OTID) { System.out.println("\t\tItem = " + jo.getQualifiedName(false)); } }}

Example 3 – Traversing through Forms, Blocks and Items using the Generic JdapiObject interface

Although example 2 and 3 both write form-, block- and item names to the console, example 3 has more code. But that is the price you pay for writing generic code. On the other hand, example 3 is far more reusable as you only have to rewrite a specific implementation for method travInvoke().

Handling PL/SQL Library modulesThe handling of PL/SQL Library modules is somewhat different from dealing with the other module types. Via the JDAPI (or the C-API) it is not possible to compile or save PL/SQL Library modules. The corresponding methods do nothing. With the following work around however, you can compile and save PL/SQL Library modules.

www.odtug.com ODTUG 2005

Page 8: Practical Jd a Pi Paper

Practical JDAPI Verschuren

PL/SQL Library modules are constructed via a pld file. You create a java.lang.StringBuffer object and append to it the two parts that a PL/SQL Library module typically consists of: a list of attached libraries and the program units. The StringBuffer object is written to disk, resulting in the pld file. Using java.lang.Runtime and java.lang.Process the Forms Compiler (ifcmp90.exe on Windows or f90genm.sh on UNIX) is called, as shown by the Java code in Example 4. The command to parse or compile the pld or the pll file respectively is the same command, as you would use to generate modules batch-wise.

/** * Execute an external runtime process (batch command to invoke the forms compiler). * @param command the full command. * @param libfile the library module file name (pll or pld) to process. * @throws java.lang.Exception whenever a failure occures. */ private void doProcess(StringBuffer command, String libfile) { if (logger.isDebugEnabled()) { logger.debug("command = " + command.toString()); } try { Process process = null; Runtime runtime = Runtime.getRuntime(); process = runtime.exec(command.toString()); process.waitFor(); if(process.exitValue() != 0) { logger.warn("Error Forms Compiler failed to save or compile the library " + libfile); } } catch (InterruptedException ie) { String message = "External Compiler runtime error occurs for library " + libfile + "\nThis usually occurs when closing an active compiler process\n"; logger.error(message, ie); } catch (IOException ioe) { logger.error("Cannot perform conversion. Possible reason: No compiler accessible, + Database unreachable", ioe); } }

Example 4 – Workaround for Compiling Forms Libraries from your JDAPI programs

Migration with JDAPIThis chapter discusses how we in our project have migrated an Oracle Forms application with the help of self-written Java tools using the JDAPI framework. The migration process involved migrating from a corporate Client/Server application built in Oracle Forms 6i to an Oracle 10g WebForms application. Originally the application was built in Forms 4.5 and this was showing through the issues with features that are obsoleted in 10g (code form 4.5 that was deprecated in Forms 6i is altogether obsoleted in Forms 10g). The migration involved many tasks that had to be repeated on all modules present. In a big application this becomes a very cumbersome process with a lot of room for oversights, especially if you have to migrate the application by hand. If you can automate these tasks you will both speed up the migration process and have a more robust migration path.

Furthermore, the C/S application interacts with the desktop by using the d2kwutil package, user-exits (host) and OLE2 calls. The C/S application ran on WindowsNT, the WebForms application is deployed on an Oracle iAS 10g application server, running on UNIX (AIX). Figure 5 summarizes the before and after state of the migration path.

Before migration After migration

Oracle Forms Version: 6.5 (6i)

Approx. 550 Forms modules, 20 menu modules, 40 PL/SQL Library modules and 2 Object Library Modules

Version: 9.0.4 (10g)

www.odtug.com ODTUG 2005

Page 9: Practical Jd a Pi Paper

Practical JDAPI Verschuren

(No Oracle Reports)

Desktop integration D2kwutil, HOST, OLE2 Webutil 1.0.5

Application Server None Oracle iAS 10g (9.0.4)

Operating System Windows NT on desktop UNIX AIX on iAS

Windows XP with Internet Explorer 5.5+ on desktop

Figure 5: Migration Transition State

This migration was our first encounter with the Oracle Forms Migration Assistant (OFMA). The OFMA, distributed with Oracle 9i and 10g Development Suite, is a utility that searches for deprecated build-ins and item types and replaces those with the equivalent 10g build-in or raises a message when an insolvable occurrence is found in your module. We started the migration process by using a pilot on a small but representative cross-section of the application (approximately 20 – 30 modules) using the OFMA.

A very neat feature of the OFMA is that you can extend or modify the list of build-ins by editing the file $ORACLE_HOME\forms90\search_replace.properties. For instance, when we migrated to webforms and wanted to use webutil for our desktop integration we extended the file with webutil procedure and function calls. For example, instead of HOST we call WEBUTIL_HOST.NON_BLOCKING.

During the pilot we discovered that there remained a lot of tasks untouched by the OFMA:

Case-sensitivity: because we migrated from a non-case sensitive environment (Windows) to a case-aware environment (UNIX) we had to rename virtually every module file because all file names where mixed-case. Renaming file names meant that build-in calls from within PL/SQL code invalidated, because the parameter formmodule_name in build in CALL_FORM or OPEN_FORM corresponds to the .fmx file name. Also attached libraries could not be found in the UNIX environment and had to be reattached to the parent module.

Add or remove attached PL/SQL Library modules: the existing application used some PL/SQL Library modules that could not be migrated to the web (hint.pll, d2kwutil.pll, ofg4hlp.pll) and had to be removed during the migration process. Other library modules like webutil.pll and enabledisableitem.pll were new attachments. We attached webutil.pll to meet our requirements to integrate with the desktop; enabledisableitem.pll is attached by the OFMA.

Swap deprecated attached PL/SQL Library modules with new versions: after careful testing we found out that the ofg4*.pll library modules could be exchanged with the new ofg*.pll module versions.

Creation of new objects in Forms modules: during the pilot phase we decided that new visual attributes were needed in the WebForms application. also, the WebUtil object group had to be added (subclassed) to the Forms modules that rely on WebUtil functionality.

Modification of object properties: because the application is of some age (originally built in Forms 4.5) some of the object properties needed to be changed for the WebForms application, such as item properties, canvas properties, etc. The modifications were partly needed because the majority of objects were not referenced from a template or object library resulting in an inconsistent overall application look and feel. We wanted to straighten that out.

To automate these tasks we have built a toolbox based on the JDAPI. Also, in order to fully incorporate the OFMA in our toolbox, we built a tool around the Forms Migration Assistant.

Building the migration toolboxThe trigger for building our migration toolbox with JDAPI was the fact that the Oracle Forms Migration Assistant is built upon the JDAPI framework. From the Basic’s section in this paper, you know that building a tool with JDAPI is not too difficult learn. Every tool has the same flow:

1. start by loading modules into the JDAPI Session

2. iterate over the loaded set of modules and do specific tasks with the module and save the result module to disk,

3. release resources from the Session.

www.odtug.com ODTUG 2005

Page 10: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Example 1 most clearly shows this path.

In the following examples I try to isolate the generic flow from the specific tasks by placing the different parts in separate methods. The Java language offers a mechanism for isolating and reusing objects and methods for reuse: inheritance. In our toolbox the generic parts like loading, iterating and releasing are coded in a BaseTool class. Tasks that are specific for a single tool are coded as abstract methods in the BaseTool. The class diagram in figure 6 shows this. Some generic tasks like saving and compiling modules, traversing objects within modules and initialization that are called from all individual tools, are also coded in the BaseTool class. Effectively, new tools only have to extend the class BaseTool and implement the specific tool functionality in the overridden methods invoke and travInvoke. This speeds up the productivity enormously!

Figure 6: Java Class Diagram for our JDAPI based Toolbox

To isolate all variables and parameters used to configure the working of the toolbox, we extracted these into a text file: the toolbox.properties file. This file can be edited externally when needed without having to make changes to the code and recompiling and redeploying the application. In fact, the OFMA works in the same way as you can edit the search_replace.properties file to specify your conversion needs. The toolbox.properties file holds properties for database

www.odtug.com ODTUG 2005

Page 11: Practical Jd a Pi Paper

Practical JDAPI Verschuren

connection, required file name case (upper or lower), file locations, a mechanism for switching on/off distinct tools and conversion properties for the distinct tools. A fragment of the file is shown in Figure 7.

Figure 7: toolbox.properties file

It is good practice to isolate the different tasks or functionalities in separate Java classes for the same reasons as you would isolate different functionality into separate PL/SQL Packages. This is not language specific, but common good programming practice.

The toolbox consists of the functionalities listed below, also visualized in the class diagram in figure 6:

UpgradeAssistant: this is a wrapper around the OFMA. The OFMA runs for a single module and does not allow wild cards for file names. Our tool processes a set of modules and does allow for wild card in file names. Our upgrade-assistant calls the OFMA for every module while iterating over our set of modules. It also merges separate log files generated from the OFMA into one big file.

CopyModules: this tool copies the given set of modules to the out directory specified in the toolbox properties file. It is good practice to have the out-directory point to another directory than your original module files. By doing so you

www.odtug.com ODTUG 2005

Page 12: Practical Jd a Pi Paper

Practical JDAPI Verschuren

have the ability to rerun the toolbox on the same set without overwriting your original modules. The copied modules are converted to the case specified in the toolbox properties file.

ReAttachLibs: this tool reattaches PL/SQL library modules to forms modules, menu modules and PL/SQL library modules. Reattaching is performed in a case sensitive manner, according to the case set in the toolbox properties file. We ran this tool to solve case-related issues when migrating from Windows to UNIX and to solve errors when attached library modules or program units could not be found, even when the module seemed to have the library module properly attached. In most cases, the solution is to reattach the library module without the absolute path.This tool is also able to detach deprecated PL/SQL Library modules and swap attached library modules with new ones. For instance: replace all ofg4*.pll with ofg*.pll.

CaseSwitcher: this tool does a deep copy of file names. It adjusts the case of the module file name to the case set in the properties file, including all occurrences of module names used in build-in commands like CALL_FORM, OPEN_FORM, etc. All module types can be processed.

UpperCase: this tool is a variation of the case-switcher tool. The application we migrated required having lower case file names for menus, object-library modules and PL/SQL library modules, but upper case for form modules. This somewhat strange requirement was enforced because the modules were not called directly via the CALL_FORM build-in, but deviated and returned upper case. The risk of adjusting this (cumbersome) functionality was bigger than adjusting the case of the file names.

Creater: this tool creates new module objects into existing modules by subclassing the parent object from an object library or reference form.

PropSetter: with this tool object properties can be set or altered.

BottomUpCompiler: this tool creates a compile script for the module set for the required operating system. The neat thing about this compiler is that it takes into account dependencies between PL/SQL Library modules, menu modules and forms modules, hence the name bottom-up.Also it has the ability to compile the modules set directly, but that comes in handy only when the toolbox is run on the application server. Normally we ran the toolbox on a developer machine so we could perform some checks (read log files) before the binary files were transferred to the application server and be compiled again. Secondly, the operating system of the developer machine differs from the application server OS, so we had to use two different compile scripts.

In the oldest versions of the toolbox we recognized that we were building a logging framework for debug, error and other log messages of our own and that it took too much of our time. We were reinventing the wheel! There are some very good logging frameworks out there, which do the same as we attempted. You can incorporate these frameworks into your own Java coding. Probably the best-known logging framework is the log4j framework (Apache Foundation, http://jakarta.apache.org/log4j). We use the log4j framework in other projects where web-applications are developed to keep track of errors in the deployed applications. Now we are using it in our toolbox for both gathering debug messages and for reporting purposes. In the next chapter I will describe how you can incorporate log4j in your Java project together with JDAPI for reporting purposes.

Our toolbox certainly does not opush JDAPI to its limits. Based on our experience, the sky is the limit and our toolbox is only the beginning of a skyscraper. You can do virtually anything with the JDAPI framework. Some examples are discussed in the following chapters.

www.odtug.com ODTUG 2005

Page 13: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Generating reports build on JDAPIThe main purpose of our toolbox is automating migration tasks. But during the migration process we recognized that we would like to get an understanding of the quality of the application being migrated. So we developed some tools that just searched for specific properties or objects without making any changes. The search results had to be logged or visualized. Figure 8 shows the class diagram of our reports. We have developed three reports in the same way we developed the migration tools, namely by extending the BaseTool class:

CodeSearcher: this tool can be used to search modules for text (fragments) in PL/SQL code. The tool traverses the modules and when the search finds an occurrence of the search string, it writes a log message to the log file. The search is performed using regular expressions. In order to be flexible in the search criteria, they are not hard-coded but instead listed in the toolbox.properties file.

ListSubclasses: this tool enables searching for objects subclassed from a given parent object. For example, it finds all items that are subclassed from cg$default_item and reports the module, block and item where it occurred.It is also possible to report all objects of a specified object type that are not subclassed or have inappropriately subclassed a parent. Again, for flexibility the objects and required parent objects are listed in the toolbox.properties, not in the code.

ObjectSearcher: this tool searches objects in form modules by looking for the occurrence of a given qualified name. Also, partial matches of the searched object are logged. In this manner searches for subclassed objects can be processed.

Figure 8: Java Class Diagram Reporting Tools

The remainder of this chapter will describe how the log4j framework was used to generate log files and reports. I will describe this using JDeveloper. Incorporating the log4j framework into JDeveloper is done through the same steps as used to incorporate the JDAPI framework into JDeveloper, sse the earlier section Required software and documentation, points 1 through 4.

Log4j is activated in our classes by importing the log4j Logger class, adding a static Logger member and call logger methods. Example 5 shows this. If you compare example 5 with example 1 you will notice example 5 is a copy of example 1, using log4j instead of System.out.println (red lines in example 5).

www.odtug.com ODTUG 2005

Page 14: Practical Jd a Pi Paper

Practical JDAPI Verschuren

import org.apache.log4j.Logger;/** * Example5: * This program loads the modules entered by the module names (or wildcard) at the 1st command line * parameter into the JDAPI Session and writes the module names to the console. */public class Example5 { static Logger logger = Logger.getLogger(Example5.class); public static void main(String[] args) { try { for (int i=0 ; i < args.length ; i++ ) { JdapiModule.openModule(args[i]); } invoke(); } finally { Jdapi.shutdown(); } } private static void invoke() { for (JdapiIterator mods=Jdapi.getModules() ; mods.hasNext() ; ) { JdapiModule mod = (JdapiModule)mods.next(); logger.info("Module name : " + mod.getAbsolutePath()); } }}

Example 5 – Implementing logging using Log4j (Apache logging framework)

Further customization of the log4j framework is done via the log4j.properties file. In this file you define how you will log your messages. It is beyond the scope of this paper to fully describe all properties of the logging mechanism. There is good documentation to be found on the Internet and in bookstores. Figure 9 shows the log4j.properties file used in our toolbox.

www.odtug.com ODTUG 2005

Page 15: Practical Jd a Pi Paper

Practical JDAPI Verschuren

# ###################################################################### File name is defaulted to log4j.properties located on the classpath, # so it is found by the log4j framework.# #####################################################################

# Set root logger level to ALL and its appenders to# stdout (ConsoleAppender; DEBUG),# F1 (RollingFileAppender; ALL) -- not currently in use# and xml (FileAppender w/ XMLLayout; INFO)log4j.rootLogger=ALL, stdout, xml

# stdout is set to be a ConsoleAppender.log4j.appender.stdout=org.apache.log4j.ConsoleAppender# stdout uses SimpleLayoutlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%-5p [%c{1}] - %m%nlog4j.appender.stdout.threshold=DEBUG

# F1 is set to be a FileAppender and F1 clears the log file before each run.# F1 uses a csv format.log4j.appender.F1=org.apache.log4j.FileAppenderlog4j.appender.F1.file=C:/work/jdapi/jdapiTools/public_html/report.csvlog4j.appender.F1.append=falselog4j.appender.F1.layout=org.apache.log4j.PatternLayoutlog4j.appender.F1.layout.ConversionPattern=%c{1};%-5p;%m%n

# xml is set to be a FileAppender, xml uses XMLLayout# and it clears the XML file before each run instead of appending to it.log4j.appender.xml=org.apache.log4j.FileAppenderlog4j.appender.xml.file=C:/work/jdapi/jdapiTools/public_html/report-data.xmllog4j.appender.xml.append=falselog4j.appender.xml.layout=org.apache.log4j.xml.XMLLayoutlog4j.appender.xml.layout.LocationInfo=falselog4j.appender.xml.threshold=INFO

Figure 9: log4j.properties – log4j configuration file for our Reporting Utilities

It shows three output destinations (called appenders) and their properties:

1. stdout, which writes all debug, info and error messages to the console, as System.out.println would do.

2. F1, which writes all debug, info and error messages into a comma-separated values (csv) file C:/work/jdapi/jdapiTools/public_html/report.csv. This logger destination however, is not set active in figure 8, as it is not listed in the line ‘log4j.rootLogger=ALL, stdout, xml’. Loggers are set to active by including them in the log4j.rootLogger entry: log4j.rootLogger=<log level>, <list of active loggers>.

3. xml, which writes info and error messages to xml-file report-data.xml

The csv-file generated by appender F1 could be imported in Microsoft Excel for example for further processing. The XML file offers even more options for further processing, depending on your skills with the XSL-T language to transform the XML document into a dynamic HTML web page, another csv-file, another XML file or even a PDF document (again is the sky the limit).

We make use of both methods to generate reports, depending on the purpose of the reports. For example, if we need a report to get a quick view of what happened during execution of one of our tools, a simple csv-file suffices. If a report is going to be used as more formal, managerial report or as a quality-report, an XML-file is generated and transformed using XSL-T into PDF that can be published on the departmental intranet.

The report-data.xml generated by log4j is not a complete, valid XML file as it lacks a root node (and an XML declaration node). The way to deal with this XML-fragment is to import it into a valid XML document by declaring the fragment as an external doctype entity, as shown in figure 10. Figure 10 shows three XML files in JDeveloper. Report.xml is the parent

www.odtug.com ODTUG 2005

Page 16: Practical Jd a Pi Paper

Practical JDAPI Verschuren

XML document that imports the log messages (called log4j:event) stored in report-data.xml. Report.xsql is also a parent XML document that imports the log messages stored in report-data.xml. Report.xsql is used as input for Oracle’s XSQL Servlet engine.

Figure 10: Building Report.xml from log4j-events

The XSQL Servlet engine is a Java servlet application that runs on the application server. XSQL is typically used to publish XML documents via a web site. The engine parses and optionally transforms XML documents to whatever document type it is transformed into. The application is also distributed with JDeveloper (system libraries: XSQL Runtime and Oracle XML Parser v2) and the 9i/10g Developer Suite, ready to be used.

Apparently the build-in XML parser of both Internet Explorer and Mozilla/Firefox were not able to parse the Report.xml into a complete document. However, server side parsing using the Oracle XSQL Servlet got the job done. Examples of the published XML document are shown in figures 11 and 12. Figure 11 shows the bare log messages (events) after running example 5, without applying a stylesheet.

www.odtug.com ODTUG 2005

Page 17: Practical Jd a Pi Paper

Practical JDAPI Verschuren

Figure 11: Logging events published as bare bone XML document.

Figure 12: Logging events published after transformation through XSL-T stylesheet.

www.odtug.com ODTUG 2005

Page 18: Practical Jd a Pi Paper

Practical JDAPI Verschuren

An example of what you could do with the log messages gathered as XML, figure 12 shows the log messages as a dynamic HTML page after applying an XSL stylesheet. A select list is added via the stylesheet. It allows us to filter the log messages on the log level (all, debug, info, error, fatal).

JDAPI helper for Oracle Designer tasksSo far I have explained how we used the JDAPI in our migration project. This chapter is about looking ahead and discusses other possibilities of what could be possible to achieve using JDAPI.

Reverse engineering of hard to find properties and preferencesSome tasks are hard to achieve using the Designer Module Editor. For instance, finding the right Preferences and Properties to achieve those subtle generation effects, can be a time-consuming and somewhat frustration task. I could imagine that a developer likes to minimize the development-time by keeping the generation simple and setting only some of the (easy to find) preferences and properties. After generating the module and finalizing it using Forms Builder: setting field size, location, font/color, position of stacked canvasses, etc., some of these properties can be reverse engineered into the Designer Repository with help of the JDAPI. Through JDAPI, we can compare the original, completely generated FMB with the FMB that was modified through Forms Builder. On analyzing the differences, we can then make specific changes through the Designer API in the Module Definition. Thus, on subsequent generation of the Form, the manual changes are no longer required.

Post-generation automatedSome – and probably increasingly more – constructs in Oracle WebForms cannot be generated from Oracle Designer. Let’s try to envision a strategy for generating the un-generatable. Using JDAPI in combination with the Forms2XML Utility (f90xmltools.jar) and the XML Diff Utility distributed with the XDK 10g, I could imagine the following scenario. It starts by building a module using the Designer Module Editor. Generate the module and convert this fmb (bare-form.fmb) into the corresponding xml representation using the Forms2XML Utility (bare-form.xml).

Then, finalize the module in Forms Builder and transform this module with the Forms2XML Utility under another filename (post-form.xml). Then, using the XML Diff utility, a delta-XML can be generated from both post-form.xml and bare-form.xml. This delta is an XML representation of all performed post-generation tasks. When this delta-XML is applied to the generated bare-form.xml, the result will be post-form.xml. So in the future, after generation of bare-form, we can again apply the delta to automatically apply post-generation changes. To retrieve the final forms module the XML must be translated back to the fmb file. The XML2Forms Utility does exactly this.

If we upload the delta-XML into the Repository, it is ready to be used for each generation of the module.

Generating a Context Sensitive Help SystemSuppose we have an application that is generated from Oracle Designer. At some stage during prototyping sessions, user acceptance testing or even production usage of the application, the users decide they want a context sensitive help system. In particular, they would like to have little question mark icons behind fields in the Forms that they do not feel comfortable with.

The chief functional analyst will write the text to be displayed. It is your assignment to merge this on-line help system into the generated forms, without breaking the 100% generatability of the application. Using the JDAPI, feeding it for example a CSV -file with FormName, BlockName, FieldName and HelpText, it is relatively straightforward to open the relevant FMB files, locate the items in question and an icon with onMouseClick trigger that opens a window displaying the Help Text.

Introducing the Eastern BunnyYou may have heard of Easter Eggs in software programs. An Easter Egg is a programmer’s joke, hidden in an application; see for example http://www.eeggs.com/faq.html for more background and thousands of examples. An Easter Egg can play a joyful or mournful sound, display the names of the developers that created the application, display a pretty picture, start a game of solitaire or… anything you fancy. Easter Eggs are typically initiated by weird key-combinations, specific data entered or mouse-clicks in special locations.

It might be a fun exercise to build an Easter Egg Hiding Utility, based on the JDAPI, that takes your generated or hand-craft Forms and buries Easter Eggs in them. This might just be the way to learn how to use JDAPI…

www.odtug.com ODTUG 2005

Page 19: Practical Jd a Pi Paper

Practical JDAPI Verschuren

ConclusionI have tried to explain how you can embark on using the JDAPI, the Java Development API for manipulating Oracle WebForms source files. It turns out relatively simple – if you have a few Java programming skills – to open, read and even manipulate these files. The JDAPI makes it possible to for example automate repetitive, costly migration tasks, implement automatic QA processes enforcing quality standards on Form modules, extend the generation capabilities of Oracle Designer’s Forms Generator and last but not least randomly sprinkle easter eggs in your Forms application. Learning how to use the JDAPI opens up many possibilities for eliminating manual – expensive and error-prone - labor in favor of automated – fast, fixed quality – activity.

Thank you very much.

About the authorHarm Verschuren is Senior Developer at AMIS Services BV, a Dutch Oracle and Java specialist. Harm coordinates the Knowledge Center on Server Development at AMIS. He is specialist in many different areas, including WebForms 10g, XML and XSLT. Other professional interests include Java, Linux and the Oracle Database. You can contact Harm at [email protected] or find many of his articles on the AMIS Technology WebLog http://technology.amis.nl/blog.

www.odtug.com ODTUG 2005