How to Use the Oracle Forms Java API

Inventory, change, and build Oracle Forms 14.1.2 modules from Java with JDAPI, and add what Forms Builder would have added for you.

Finding every date item without a format mask across a hundred forms, or adding a trigger to all of them, is tedious in Forms Builder and trivial for a program. The Oracle Forms Java API, JDAPI, opens forms, menus, libraries, and object libraries, reads and changes every object and property Forms Builder can, creates new ones, and saves the module.

This guide covers JDAPI in Oracle Forms 14.1.2: a program that inventories forms, a program that fixes date masks across an application, how to compile and run them, and what a program must add that Forms Builder adds for you.

Sample Form for This Guide

The examples and screenshots use the sample forms CH34_PERF and CH34_TRIPS from the Oracle Forms code repository on GitHub. Download them, open them in Forms Builder, and connect as CAREWELL to follow along.

FormFileWhat it shows
CH34_PERFforms/ch34/ch34_perf.fmbThe form listed by FormInventory
CH34_TRIPSforms/ch34/ch34_trips.fmbThe form built by the formkit script shown

The forms run against the CareWell Clinic sample schema, which you install first.

JDAPI at a Glance

AspectDetail
Library$ORACLE_HOME/jlib/frmjdapi.jar
ClassesOne per kind of object: FormModule, Block, Item, Trigger, Canvas, LOV, and so on.
PropertiesA getter and setter for each, such as getFormatMask and setFormatMask.
ConstantsIn JdapiTypes, such as JdapiTypes.DATY_DATE_CTID for the data type Date.

Read Modules: a Form Inventory

FormInventory lists the blocks of forms, with their data sources, and counts their items and triggers.

Java program FormInventory.java:

import oracle.forms.jdapi.*;

public class FormInventory {
  public static void main(String[] args) {
    Jdapi.setFailLibraryLoad(false);                 // report, even if a library is missing
    Jdapi.setFailSubclassLoad(false);
    for (String file : args) {
      FormModule form = FormModule.open(file);
      int triggers = count(form.getTriggers());
      System.out.println(form.getName() + "  (" + triggers + " form-level triggers)");
      JdapiIterator blocks = form.getBlocks();
      while (blocks.hasNext()) {
        Block block = (Block) blocks.next();
        int items = 0, itemTriggers = 0;
        JdapiIterator it = block.getItems();
        while (it.hasNext()) {
          itemTriggers += count(((Item) it.next()).getTriggers());
          items++;
        }
        String source = block.getQueryDataSourceName();
        System.out.printf("  %-14s %-14s %3d items %3d triggers%n", block.getName(),
            source == null || source.isEmpty() ? "-" : source, items, count(block.getTriggers()) + itemTriggers);
      }
      form.destroy();
    }
    Jdapi.shutdown();
  }

  static int count(JdapiIterator objects) {
    int n = 0;
    while (objects.hasNext()) { objects.next(); n++; }
    return n;
  }
}

Compile and Run a JDAPI Program

A program is compiled against frmjdapi.jar and runs with the environment of the Forms tools: ORACLE_HOME, and on Linux LD_LIBRARY_PATH with $ORACLE_HOME/lib, because the API uses Forms' native libraries. Set FORMS_API_TK_BYPASS=TRUE where no display is available.

Compile and run:

javac -cp $ORACLE_HOME/jlib/frmjdapi.jar FormInventory.java
java -cp .:$ORACLE_HOME/jlib/frmjdapi.jar FormInventory ch34_perf.fmb

Output:

CH34_PERF  (0 form-level triggers)
  CTL            -                9 items   3 triggers
  APPTS          APPOINTMENTS     7 items   1 triggers

Jdapi.setFailLibraryLoad(false) and setFailSubclassLoad(false) open a module even when an attached library or an object library is not found, which suits reports that must not stop at a missing file.

Change Modules: Fix Date Masks Everywhere

DateMasks gives every date item without a format mask the clinic's format, and saves the forms it changed.

Java program DateMasks.java:

import oracle.forms.jdapi.*;

public class DateMasks {
  public static void main(String[] args) {
    for (String file : args) {
      FormModule form = FormModule.open(file);
      int changed = 0;
      JdapiIterator blocks = form.getBlocks();
      while (blocks.hasNext()) {
        Block block = (Block) blocks.next();
        JdapiIterator items = block.getItems();
        while (items.hasNext()) {
          Item item = (Item) items.next();
          boolean isDate = item.getDataType() == JdapiTypes.DATY_DATE_CTID;
          String mask = item.getFormatMask();
          if (isDate && (mask == null || mask.isEmpty())) {
            item.setFormatMask("DD-MON-YYYY");
            System.out.println("  " + block.getName() + "." + item.getName());
            changed++;
          }
        }
      }
      if (changed > 0) form.save(file);           // compile the form afterwards
      System.out.println(file + ": " + changed + " items changed");
      form.destroy();
    }
    Jdapi.shutdown();
  }
}

Run on copies of the sample forms, it found eight forms with such items.

Output:

  PATIENTS.BIRTH_DATE
/tmp/app/ch03_patients.fmb: 1 items changed
  INVOICES.INVOICE_DATE
  PAYMENTS.PAID_ON
/tmp/app/ch08_invoices.fmb: 2 items changed
...

The saved forms are compiled like any other. A program that changes hundreds of forms should run on copies, compare the XML before and after, and compile everything before the changes replace the originals; see how to automate Oracle Forms builds for the compile script and XML comparison.

What Forms Builder Adds That Programs Must Add

The Java API sets exactly what the program sets. Forms Builder does more when it creates objects, and a program must do it itself:

AreaWhat to do
RelationsForms Builder writes the triggers and program units that coordinate a master and its details; a relation created with the API has none. Write ON-POPULATE-DETAILS, ON-CHECK-DELETE-MASTER, ON-CLEAR-DETAILS, and the procedures QUERY_MASTER_DETAILS, CLEAR_ALL_MASTER_DETAILS, and CHECK_PACKAGE_FAILURE yourself.
UnitsSizes and positions use the form's coordinate system, points by default, but font sizes are in hundredths of a point: 900 for 9 points.
Wizard defaultsThe Data Block and Layout Wizards set prompts, sizes, frames, and scroll bars; the API creates an item with the Property Palette's defaults, nothing more.
LibrariesAttach them in lowercase, as Forms Builder does.

The coordination code Forms Builder generates is shown in how to create a master-detail form in Oracle Forms.

Build Forms with Scripts

Every sample form in this series was built by a program: a Python script, run by the Jython interpreter that the Oracle home includes, calling the Java API through a helper module called formkit. The script of the trips form is typical.

A formkit script:

from formkit import *
f = form('CH34_TRIPS', 'CareWell Clinic')
main(f, 'Requests to the Forms Server', 420, 230)
c = block(f, 'CTL')
for i in range(1, 9):
    item(c, 'F%d' % i, 'Item %d' % i, 70, 10 + 22 * (i - 1), 120, edge='start')
s = item(c, 'START', 'Start Clock', 230, 10, 90, 20, kind='button')
trigger(s, 'WHEN-BUTTON-PRESSED', file='ch34/start-clock.pls')
save(f)

formkit is in the tools folder of the Oracle Forms code repository on GitHub, with the scripts in its specs folder. Built this way, a form can be rebuilt at any time from its script and code files, which are also the text version control compares, and a change to all forms is a change to one function.

Conclusion

The Oracle Forms Java API opens, reads, changes, creates, and saves modules through one class per object type, with getters, setters, and JdapiTypes constants. Compile programs against frmjdapi.jar, run them with the Forms environment and FORMS_API_TK_BYPASS=TRUE on servers, and run changes on copies before compiling everything. Remember what Forms Builder adds that a program must add itself: relation code, wizard defaults, lowercase library names, and font sizes in hundredths of a point.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE and software veteran with 25+ years of experience, passionate about AI and IT innovation.

guest

0 Comments
Oldest
Newest Most Voted
00